For Vanilla Datapack Developers
In fact, I am originally a vanilla datapack developer. It was precisely because I grew tired of the tediousness and various limitations of datapacks that I developed Katton. I wanted to retain the advantage of datapack hot-reloading while achieving powerful functionality comparable to that of mods. So if you're also a datapack developer, don't worry, Katton will definitely give you a home-like experience.
Katton provides a series of functions that wrap all the functionalities of the vanilla commands. Even if you know nothing about Fabric modding, you can still use these functions to implement everything you can within the datapack.
Commands
All command functions are located in the top.katton.api.dpcaller package. You can find the documentation for these functions in the API documentation. These functions are designed to be as close to the original commands as possible. For example, you may use scoreboard players set test myscore 1 in datapack, and in Katton you can achieve the same effect with the following code:
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)
}Entity Access
In datapacks, we always need to use target selectors to specify the entities we want to operate on. In Katton, you can also use target selectors to get the corresponding entities. We provide EntitySelectorBuilder class to help you build target selectors.
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 is another important part of datapacks. In Katton, you can use getEntityNbt, getBlockNbt, and getStorageNbt functions to get the NBT data of entities, blocks, and storage respectively.
Call Datapack Functions
Want to continue calling the mcfunction you've already written? No problem! You can use the runFunction function to call any mcfunction in your datapack.
#tick and #load Functions
In datapacks, we will use functions with #load tag to execute some code when the datapack is loaded, and use functions with #tick tag to execute some code every tick. In Katton, we replace these tags with more powerful event listeners. You can use ServerEvent.onStartServerTick event to execute code every tick, and use ServerEvent.onServerStarted event to execute code when the server starts or stops. Check out the Events page for more details.
@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 Commands
Katton also ships with built-in commands for script management. The most useful ones for datapack developers:
/katton reload— reload all scripts (like/reloadbut for scripts specifically, with a progress bar!)/katton registry— see what your scripts have registered/katton status— check if Katton is running properly
Check the Commands page for the full list.
