本篇教程由作者设定未经允许禁止转载。

前言

KubeJS能干什么?如果能,该怎么实现?怎么使用更好的代码来实现?本文试图解答这些问题,并将会提供许多常用技巧及案例供大家参考。


Minecraft ForgeKubeJSArchitecturyRhinoProbeJS
1.19.243.3.51902.6.2-build.426.3.491902.2.2-build.2805.3.2



使用Visual Studio Code 和ProbeJS

按照Visual Studio CodeProbeJS 安装之后,右键你的版本文件夹,通过code打开,可以看见类似如下的界面:



打开logs文件夹,把里面的文件长按拖动到右下角KubeJS魔改思路-第1张图片

3个文件都这样,以后再查看日志就更加方便了KubeJS魔改思路-第2张图片

编写代码时,可以看到ProbeJS提供的提示,按下Ctrl+空格可以再次唤起提示KubeJS魔改思路-第3张图片

使用KubeJS时的思路

当你想用KubeJS做某些事情时,你先要知道它该从哪里开始,一般是使用某个事件

事件的组成

事件被分为几个对象,每个对象中都有许多方法

PlayerEvents的方法:
decorateChat,chestOpened,advancement,chat,chestClosed,loggedIn,loggedOut,inventoryClosed,inventoryChanged,inventoryOpened,tick,respawned

在Wudji的教程或官方wiki中可以看到事件列表。当发生这些事件时可以开始执行代码。

KubeJS魔改思路-第4张图片

使用ProbeJS可以直接看到事件的详细信息。

KubeJS魔改思路-第5张图片

extra说明这个事件可以添加一个额外的参数,在这个实体受伤事件中表示受伤的实体的类型,如果不填也可以。@at表示事件是发生在服务端还是客户端或者都有。@cancellable说明这个事件可以被取消,如果没有就说明不能取消。

案例:

  EntityEvents.hurt((event) => {
    event.cancel();//当实体受伤时,取消事件
  });

此时任何实体受伤都不会受到伤害,因为事件被取消了。若改为:

  EntityEvents.hurt('minecraft:pig',(event) => {
    event.cancel();//当minecraft:pig受伤时,取消事件
  });

则只有minecraft:pig受伤时才会取消事件。它相当于简化了以下代码:

  EntityEvents.hurt((event) => {
    if (event.entity.type == "minecraft:pig") {
      //当minecraft:pig受伤时
      event.cancel(); //取消事件
    }
  });

某些事件是无法取消的,例如:

ServerEvents.loaded

loaded(handler: (event: Internal.ServerEventJS) => void): void

@at — server

event.cancel()同时还具有return的作用,它会直接退出函数。如果只使用return,那么只会退出函数而不会取消事件,例如:

 EntityEvents.hurt((event) => {
    if (event.entity.type != "minecraft:pig") {
      //当不是minecraft:pig受伤时
      return;//退出函数,后续代码不会执行
    }
    event.cancel(); //取消事件
  });

回调函数

某些回调函数可以开始执行代码,例如注册一个物品时,当它被吃掉会运行一些代码:

StartupEvents.registry('item',event=>{
  event.create('kubejs:test_food').maxStackSize(16).food(food=>{
    food.hunger(5);
    food.eaten(eaten=>{
      eaten.player.tell('吃吃吃');
    })
  })
})

food.eaten()需要以一个回调函数,你可以在这个参数中写需要的代码。

如果想要热更新其中的部分代码,可以按照如下方式改写:

StartupEvents.registry("item", (event) => {
  event
    .create("kubejs:test_food")
    .maxStackSize(16)
    .food((food) => {
      food.hunger(5);
      food.saturation(1.5);
      food.eaten((eaten) => {
        global["test_food_eaten"](eaten);
      });
    });
});
global["test_food_eaten"] = (eaten) => {
  eaten.player.tell("吃1吃");
};

为了在global函数中获得自动补全,你可以使用TypeScript也可以使用JSdoc。不管使用哪种,都需要获得这个回调函数的参数的类型。将鼠标放在eaten上,然后复制如下内容:

KubeJS魔改思路-第6张图片如果使用JSdoc,那么先在函数声明的上方(允许缩进,空格和空行)输入/* ,接下来按下Tab自动补全:

KubeJS魔改思路-第7张图片粘贴刚才复制的内容,之后下方的eaten参数就会被认为是Internal.FoodEatenEventJS而获得自动补全。

/**
 *
 * @param {Internal.FoodEatenEventJS} eaten
 */

global["test_food_eaten"] = (eaten) => {
  eaten.player.tell("吃1吃");
};

当你第一次使用global后还需要重启游戏一次让global被加载到,在之后就只需要使用如下指令即可

/kubejs reload startup_scripts

获取和修改属性

我们总是需要获取和修改一些属性。获取属性可以使用get 或is 开头的方法:

let p = event.getPlayer();//获取事件的玩家
let f = p.isOnFire();//获取玩家是否着火

不过,如果这些方法没有参数,那么可以去掉get 或is 同时把首字母大写改成小写,直接获取属性:

let p = event.player;
let f = p.onFire;//只能更改首字母
let s = p.getHeldItem('main_hand');//需要参数不能简写

同理,修改属性(如果方法只有1个参数):

p.setHealth(10);//使用set 开头的方法修改属性
let h = p.health;//去掉set 并更改首字母,获取health 属性
h = 10;//直接赋值

以上代码可以使用解构赋值来优化:

let p = event.player;
let l = event.level;
const{player,level}=event;//除了变量名不同,没有区别
const{health}=player;//再解构一次
const{player:{health}}=event;//直接得到health
health = 10;//直接赋值

在之后的代码中都会使用解构赋值,以上使用单个字母作为变量名仅供演示。

一些JavaScript特性

使用JavaScript的特性可以优化代码,本节所有内容都会附带菜鸟教程廖雪峰的官方网站的链接,希望你先学了JavaScript再来学KubeJS。

Array(数组)

菜鸟教程 廖雪峰的官方网站

在游戏中,可以输入以下命令获得一个物品的数组:

/kubejs hotbar
/kubejs inventory
#分别是快捷栏和物品栏

点击聊天栏复制,可能类似如下:

['minecraft:white_wool', 'minecraft:oak_wood', 'minecraft:oak_stairs', 'minecraft:jungle_planks']

接下来就可以到代码中去使用它:

ServerEvents.recipes((event) => {
  let arr = [
    "minecraft:white_wool",
    "minecraft:oak_wood",
    "minecraft:oak_stairs",
    "minecraft:jungle_planks",
  ];
  arr.forEach((f) => {
    event.remove({ output: `${f}` });//批量移除物品配方
  });
});

除了自己获得数组,你还可以使用Item.list获得,它是所有出现在创造模式物品栏中的物品的数组。

  //在日志中打印出所有可附魔的物品
  Item.list
    .filter((f) => {
      return f.enchantable;
    })
    .forEach((f) => {
      console.log(f.toItemString());
    });
    //可能的输出的一部分:
    //[15:17:48] [INFO] example.js#26: Item.of('minecraft:netherite_pickaxe', '{Damage:0}')


String(字符串)

菜鸟教程廖雪峰的官方网站

字符串一般用于表示物品方块实体等,也可用于聊天事件中。

"命名空间 ID"可以表示1个物品,"10x 命名空间 ID"可以表示10个物品。

如果你想要更加方便的测试,可以试试:

PlayerEvents.chat((event) => {
  const { player, message, level, server } = event;
  if (message != "ts") {//如果不是ts就退出函数
    return;
  }
//当你在聊天栏输入ts时,将会执行代码
  Item.list
    .filter((f) => {
      return f.enchantable;
    })
    .forEach((f) => {
      console.log(f.toItemString());
    });
});

一些小技巧和实例

以下是一些KubeJS的实例。

快速刷新

一次性刷新所有KubeJS可以刷新的脚本,并且比原版的/reload更快

  PlayerEvents.chat((event) => {
    const { player, message, level } = event;

    const command = [
      "client_scripts",
      "config",
      "lang",
      "server_scripts",
      "startup_scripts",
      "textures",
    ];
    if (message == "re") {
      command.forEach((c) => level.runCommandSilent(`kubejs reload ${c}`));
      player.tell(Text.of("Reloaded All Scripts!").green());
    }
  });