# NukkitMaster文档

# 准备阶段

在部署Nukkit服务器之前,您需要阅读一下NukkitMaster的API来进行基本的ModSDK通信与商业化内容接入。
NukkitMaster需要安装在Nukkit-MOT服务端中。

需要注意: NukkitMaster是基于Nukkit-MOT分支进行开发的。Nukkit官方服务端版本无法兼容。

如果您需要使用其他分支的Nukkit,您可以参考NukkitMOT源码仓库修改的内容而自行修改服务端以兼容中国版的协议。
通信内容方面,您可以自行反编译NukkitMaster来兼容您的其他分支的Nukkit服务端。

NukkitMOT分支开源地址: https://github.com/MemoriesOfTime/Nukkit-MOT

# 插件配置

# 服务器id(开发者平台中的资源数字id)
game_id: ""
# 正式服务器key(开发者平台中的签名信息)
game_key: ""
# 测试服务器key(开发者平台中的签名信息)
test_game_key: ""

# 是否是测试服
test_server: false

# 是否使用自定义商城(false表示使用官方提供的商城功能)
custom_shop: false


# 订单服务器地址(一般不用填,保持""即可)
shop_server_url: ""
# web服务器地址(一般不用填,保持""即可)
web_server_url: ""

NukkitMaster插件会在 plugins/NukkitMaster 下生成 config.yml ,需要将服务器的相关数据进行配置,订单API才能生效。
其中gameidrawkeytest rawkey是必须要填写的。
test_server需要根据服务器部署情况进行修改,这个值会影响NukkitMaster插件使用的是正式服url还是测试服url
custom_shop商业化流程 (opens new window) 中 use custom shop 功能同理
shop_server_urlweb_server_url为预留配置,目前不需要修改,默认即可

# API

# public void enableCustomShopEntry(boolean useCustomShop)

开启商城插件的入口,该功能已经在NukkitMaster中集成,可修改NukkitMaster的config.yml文件。
参数: useCustomShop —— 是否使用自定义商城入口,为false时,则使用官方商城入口

# public void openShop(Player player)

打开指定玩家商城界面 注意:该接口需要使用商城插件,并修改config.yml的custom_shop为true。
参数: player —— 玩家

# public void closeShop(Player player)

关闭指定玩家商城界面 注意:该接口需要使用商城插件,并修改config.yml的custom_shop为true。
参数: player —— 玩家

# public void getPlayerOrderList(Player player, FutureCallback<Map<String, Object>> callback)

获取玩家未发货订单列表 参数:
player —— 玩家
callback —— FutureCallBack 回调函数

例子:
回调参数为Map<String,Object>, 目前值为

Key Value
json_result 订单json数据对象
player Player玩家对象

# public void finPlayerOrder(Player player, List<String> orderList, FutureCallback<Map<String, Object>> callback)

获取玩家未发货订单列表 参数:
player —— 玩家
orderList —— 订单id列表
callback —— FutureCallBack 回调函数

# public void listenForNukkitMasterEvent(SpigotMasterEvent event, PyRpcHandler handler)

监听spigot master的自定义事件 参数:
event — SpigotMasterEvent的枚举值 handler — 回调函数

# public void listenForEvent(String namespace, String system, String event, PyRpcHandler handler)

注册客户端事件 参数:
namespace — 来源客户端系统的namespace system — 来源客户端系统的systemName event — 事件名 handler — 回调函数

# public void notifyToClient(Player player, String namespace, String system, String event, Map<String, Object> data)

给指定玩家发送服务端事件 参数:
player — 接收事件的玩家 namespace — 在客户端系统使用ListenForEvent监听的namespace system — 在客户端系统使用ListenForEvent监听的systemName event — 事件名 data — 事件参数。注意,要使用-2指代本地玩家的entityId。

# public void notifyToMultiClients(List<Player> players, String namespace, String system, String event, Map<String, Object> data)

给多个玩家发送服务端事件。 因为-2的entityId对于不同玩家来说都指代本机玩家,而非某个固定的实体,所以不要在多播中发送这种信息。 参数:
players — 接收事件的玩家列表 namespace — 在客户端系统使用ListenForEvent监听的namespace system — 在客户端系统使用ListenForEvent监听的systemName event — 事件名 data — 事件参数

# public void notifyToClientsNearby(@Nullable Player except, Location loc, double dist, String namespace, String system, String event, Map<String, Object> data)

给某个位置附近一定半径内的所有玩家发送服务端事件。 因为-2的entityId对于不同玩家来说都指代本机玩家,而非某个固定的实体,所以不要在多播中发送这种信息。 参数:
except — 发送事件时排除掉这个玩家,可以为null表示不排除 loc — 圆心位置 dist — 半径 namespace — 在客户端系统使用ListenForEvent监听的namespace system — 在客户端系统使用ListenForEvent监听的systemName event — 事件名 data — 事件参数

# public void broadcastToAllClient(@Nullable Player except, World world, String namespace, String system, String event, Map<String, Object> data)

给某个world内的所有玩家发送服务端事件。 因为-2的entityId对于不同玩家来说都指代本机玩家,而非某个固定的实体,所以不要在多播中发送这种信息。 参数:
except — 发送事件时排除掉这个玩家,可以为null表示不排除 world — 所在world namespace — 在客户端系统使用ListenForEvent监听的namespace system — 在客户端系统使用ListenForEvent监听的systemName event — 事件名 data — 事件参数

# public void broadcastToAllClient(@Nullable Player except, String namespace, String system, String event, Map<String, Object> data)

给服务器内的所有玩家发送服务端事件。 因为-2的entityId对于不同玩家来说都指代本机玩家,而非某个固定的实体,所以不要在多播中发送这种信息。 参数:
except — 发送事件时排除掉这个玩家,可以为null表示不排除 namespace — 在客户端系统使用ListenForEvent监听的namespace system — 在客户端系统使用ListenForEvent监听的systemName event — 事件名 data — 事件参数

# API的使用例子

可以参考Spigot开服的商店DEMO: 商城Demo详解 (opens new window)
仅仅只是将其中spigotMaster更换为nukkitMaster,其他逻辑基本相同

# 获取nukkitMaster对象

import com.neteasemc.nukkitmaster.NukkitMaster;
public final class App extends PluginBase {
    NukkitMaster nukkitMaster;

    @Override
    public void onEnable() {
		// 可以直接获取instance
        nukkitMaster = NukkitMaster.getInstance();
		// 或者通过插件名字获取
		nukkitMaster = (NukkitMaster)getServer().getPluginManager().getPlugin("NukkitMaster");
    }
}

# 监听玩家购买商品事件、玩家催发货事件和发货逻辑

    public void ListenShop() {
        PyRpcHandler shipItemHandler = new PyRpcHandler() {
            @Override
            public void onEvent(Player player, Map<String, Object> data) {
                tryShipItem(player);
            }
        };
        // 玩家催发货或者玩家购买物品成功事件进行发货检查
        nukkitMaster.listenForNukkitMasterEvent(NukkitMasterEvent.PLAYER_BUY_ITEM_SUCCESS, shipItemHandler);
        nukkitMaster.listenForNukkitMasterEvent(NukkitMasterEvent.PLAYER_URGE_SHIP, shipItemHandler);
    }

    // 获取玩家订单,并且尝试发货
    public void tryShipItem(Player player){
        FutureCallback<Map<String, Object>> cbHandler = new FutureCallback<Map<String, Object>>() {
            @Override
            public void completed(Map<String, Object> result) {
                JSONObject jsonRes = (JSONObject)result.get("json_result");
                Player requestPlayer = (Player)result.get("player");

                JSONArray entities = (JSONArray)jsonRes.get("entities");
                // 这里进行entites的订单内容发放
                List<String> finOrderIds = new ArrayList<>();
                for(int i = 0; i < entities.size(); ++i){
                    JSONObject order = (JSONObject)entities.get(i);
                    // 取出订单id,判断是否已经发放过,比如说通过本地的数据库等 
                    String orderId = order.getAsString("orderid");

                    // 对于还未发放的订单, 根据order的cmd字段进行对应的奖励逻辑发放
                    // 如:shipItemToPlayer(requestPlayer);
                    // 发放完之后记录数据库
                    // 如:saveOrder(requestPlayer, orderId);

                    // 最后通知网易服务器订单已完成
                    finOrderIds.add(orderId);
                }
                finPlayerOrder(requestPlayer, finOrderIds);
            }

            @Override
            public void failed(Exception ex) {
                // 失败原因
                getLogger().info(ex.toString());
            }

            @Override
            public void cancelled() {
                getLogger().info("取消请求玩家订单");
            }
        };
        nukkitMaster.getPlayerOrderList(player, cbHandler);
    }
    

    // 通知网易服务器订单完成 
    public void finPlayerOrder(Player player, List<String> finOrderList){
        FutureCallback<Map<String, Object>> cbHandler = new FutureCallback<Map<String, Object>>() {

            @Override
            public void completed(Map<String, Object> result) {
                JSONObject jsonRes = (JSONObject)result.get("json_result");
                Player requestPlayer = (Player)result.get("player");
                getLogger().info("玩家:" + requestPlayer.getDisplayName() + " 订单已完成:" + jsonRes);
            }

            @Override
            public void failed(Exception ex) {
                getLogger().info(ex.toString());
            }

            @Override
            public void cancelled() {
                getLogger().info("取消通知玩家订单完成");
            }
            
        };
        nukkitMaster.finPlayerOrder(player, finOrderList, cbHandler);
    }


# NukkitMaster Event事件

# PLAYER_URGE_SHIP("player_urge_ship")

玩家催发货事件

# PLAYER_BUY_ITEM_SUCCESS("player_buy_item_success")

玩家购买成功事件

# CLIENT_LOAD_ADDON_FINISH("client_load_addon_finish")

玩家客户端加载Mod完成事件