Skip to content
On this page

面向原版数据包开发者

其实我自己最早也是原版数据包开发者。正是因为我受够了数据包开发里的繁琐流程和各种限制,才做出了 Katton。我希望保留数据包热重载的优点,同时获得接近模组级别的能力。所以如果你也是数据包开发者,不用担心,Katton 的体验会非常熟悉。

Katton 提供了一系列函数来封装原版命令的能力。即使你完全不了解 Fabric 模组开发,也依然可以用这些函数实现你在数据包里能做到的大部分事情。

命令

命令相关函数都位于 top.katton.api.dpcaller 包下。你可以在 API 文档 查阅这些函数。它们的设计尽量贴近原版命令。例如你在数据包里会写 scoreboard players set test myscore 1,在 Katton 中对应写法如下:

kotlin
import top.katton.api.dpcaller.getObjective
import top.katton.api.dpcaller.getOrCreateObjective
import top.katton.api.dpcaller.setScore

@ServerScriptEntrypoint
fun scoreboardExampleMain(){
    //get the scoreboard objective
    val obj = getOrCreateObjective("myscore")
    //set the score of "test" to 100
    setScore("test", obj, 100)
}

实体访问

在数据包里,我们通常通过目标选择器指定要操作的实体。在 Katton 里同样可以这样做。我们提供了 EntitySelectorBuilder 类来帮助你构建目标选择器。

kotlin
import net.minecraft.world.effect.MobEffects
import net.minecraft.world.entity.EntityType
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.phys.Vec3
import top.katton.api.dpcaller.addEffect
import top.katton.api.requireServer
import top.katton.util.EntitySelectorBuilder

@ServerScriptEntrypoint
fun targetSelectorExample(){
    // Build a target selector
    val selector = EntitySelectorBuilder.allEntities()  //@e
        .type(EntityType.CREEPER)   //type = creeper
        .tag("qwq", false)  //tag = qwq
        .distanceBelow(16.0)    //distance = ..16
        .create()
    // So we get a target selector like this: @e[type=creeper,tag=qwq,distance=..16]

    // And then we need to build a command execution source, which is required by the selector to get the world and the position for distance calculation
    val source = requireServer().createCommandSourceStack()
        .withLevel(requireServer().overworld()) // Set the dimension for the command source
        .withPosition(Vec3(50.0, 70.0, 50.0)) // Set the position for distance calculation

    // Get entities selected by the selector
    val entities = selector.findEntities(source)

    // Once you get the references of the entities, you access those entities at anywhere in your code, and do whatever you want with them
    for (entity in entities) {
        if(entity is LivingEntity){
            // add some effect to the entity
            addEffect(entity,
                MobEffects.GLOWING, // effect
                200,    // effect duration in **ticks**
                0,      // effect amplifier level
                false,  // show particles
                false   // show icon
            )
        }
    }
}

NBT

NBT 也是数据包里非常重要的一部分。在 Katton 中,你可以使用 getEntityNbtgetBlockNbtgetStorageNbt 分别读取实体、方块和存储中的 NBT 数据。

调用数据包函数

想继续调用你已经写好的 mcfunction?完全可以。使用 runFunction 就能调用数据包里的任意 mcfunction。

#tick#load 函数

在数据包中,我们会通过带 #load 标签的函数在加载时执行逻辑,通过带 #tick 标签的函数在每个 tick 执行逻辑。在 Katton 中,这些场景被更强的事件监听机制替代。你可以使用 ServerEvent.onStartServerTick 在每个 tick 执行逻辑,使用 ServerEvent.onServerStarted 在服务器启动后执行逻辑。更多信息请参考事件页面。

kotlin
@ServerScriptEntrypoint
fun main() {
    // Executed when an entity is loaded
    ServerEntityEvent.onAfterEntityLoad += load@
    fun(arg: EntityLoadArg) {
        val (entity, _) = arg
        if (entity !is Arrow) return
        //if a player shoots an arrow, check the bow's data
        val owner = entity.owner
        if (owner is ServerPlayer) {
            onArrowShot(owner, entity)
        }
    }
    
    // Executed every server tick
    onStartServerTick += tick@
    fun(_) {
        // check if a tnt arrow has hit the ground and make it explode
        processTNTArrow()
    }
}

val tntArrow = HashSet<Arrow>()

fun onArrowShot(player: ServerPlayer, arrow: Arrow) {
    tell(player, Component.empty() + "The weapon on your hand is: " + player[Weapon.MainHand]?.itemName)
    //this arrow is shot by a tnt bow, make it explode
    if (player.mainHandItem.nbt["tnt"](false)) {
        tntArrow.add(arrow)
    }
}

fun processTNTArrow() {
    val iterator = tntArrow.iterator()
    while (iterator.hasNext()) {
        val arrow = iterator.next()

        // Check if the arrow has hit the ground by checking its NBT data. 
        if (getEntityNbt(arrow).getBooleanOr("inGround", false)) {
            // Make the arrow explode
            // This method is from vanilla code
            arrow.level().explode(
                arrow,
                arrow.damageSources().explosion(arrow, arrow.owner),
                null,
                arrow.position(),
                16.0f,
                false,
                Level.ExplosionInteraction.TNT
            )
            iterator.remove()
            // Remove the arrow entity after explosion
            arrow.kill(arrow.level() as ServerLevel)
        }
    }
}

Katton 命令

Katton 还内置了一组脚本管理命令。对数据包开发者最实用的几个:

  • /katton reload — 重载所有脚本(类似 /reload,但是专门为脚本设计,还带进度条!)
  • /katton registry — 查看你的脚本都注册了啥
  • /katton status — 检查 Katton 是否正常运行

完整列表请看 命令 页面。