From 6276db64e0261469b63aadfba54f70ecf538b345 Mon Sep 17 00:00:00 2001 From: kwiilh Date: Fri, 17 Oct 2025 14:52:29 +0800 Subject: [PATCH] =?UTF-8?q?=E5=90=8C=E6=AD=A5=E6=9C=80=E6=96=B0=E6=96=87?= =?UTF-8?q?=E6=A1=A310m17d?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 + .../15-自定义游戏内容/9-自定义指令.md | 4 +- .../20-玩法开发/18-性能优化/代码进阶优化.md | 34 ++- .../课程11:使用Nukkit开服/0-Nukkit开服教程.md | 103 +++++++ .../课程11:使用Nukkit开服/1-NukkitMasterAPI文档.md | 277 ++++++++++++++++++ .../课程11:使用Nukkit开服/README.md | 0 .../课程11:使用Nukkit开服/images/img.png | Bin 0 -> 6507 bytes mcguide/35-上架与入驻/课程11.1-整合包概览.md | 174 +++++++++++ 8 files changed, 576 insertions(+), 18 deletions(-) create mode 100644 mcguide/27-手机网络游戏/课程11:使用Nukkit开服/0-Nukkit开服教程.md create mode 100644 mcguide/27-手机网络游戏/课程11:使用Nukkit开服/1-NukkitMasterAPI文档.md create mode 100644 mcguide/27-手机网络游戏/课程11:使用Nukkit开服/README.md create mode 100644 mcguide/27-手机网络游戏/课程11:使用Nukkit开服/images/img.png create mode 100644 mcguide/35-上架与入驻/课程11.1-整合包概览.md diff --git a/.gitignore b/.gitignore index dba7df0..8550474 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,5 @@ package-lock.json package.json .DS_Store .idea +mcguide/30-测试/video/airperf.mp4 +mcguide/30-测试/video/tracy.mp4 diff --git a/mcguide/20-玩法开发/15-自定义游戏内容/9-自定义指令.md b/mcguide/20-玩法开发/15-自定义游戏内容/9-自定义指令.md index 2a2956f..074b405 100644 --- a/mcguide/20-玩法开发/15-自定义游戏内容/9-自定义指令.md +++ b/mcguide/20-玩法开发/15-自定义游戏内容/9-自定义指令.md @@ -57,7 +57,7 @@ selection: true |format_version|str||格式版本,请填写0.0.1| |name|str||指令名称,例如gamemode| |description|str||指令描述,也支持在语言文件(例如zh_CN.lang)中定义| - |permission_level|str|game_directors|权限等级,可选game_directors、admin、host、owner、any,具体含义如下
game_directors:任何操作员都可以运行此命令,包括命令方块
admin:任何操作员都可以运行此命令,但命令方块不能
host:任何服务器主机都可以运行此命令
owner:只有专用服务器可以运行此命令
any:任何人都可以运行此命令| + |permission_level|str|权限等级,可选game_directors、admin、host、owner、any,具体含义如下
game_directors:任何操作员都可以运行此命令,包括命令方块
admin:任何操作员都可以运行此命令,但命令方块不能
host:任何服务器主机都可以运行此命令
owner:只有专用服务器可以运行此命令
any:任何人都可以运行此命令| - args是一个包含object的列表,定义指令的参数。每个object都代表一个参数,object的顺序决定指令参数的输入顺序,每个object的参数说明如下 @@ -124,4 +124,4 @@ selection: true /explode ~~8~ 3 false 让发送对象爆炸,爆炸范围为3,破坏方块,产生火焰 /explode @s 3 true true -``` +``` \ No newline at end of file diff --git a/mcguide/20-玩法开发/18-性能优化/代码进阶优化.md b/mcguide/20-玩法开发/18-性能优化/代码进阶优化.md index 82fded9..a065349 100644 --- a/mcguide/20-玩法开发/18-性能优化/代码进阶优化.md +++ b/mcguide/20-玩法开发/18-性能优化/代码进阶优化.md @@ -92,30 +92,32 @@ playerId = clientApi.GetLocalPlayerId() ClientComp.CreateQueryVariable(levelId).Register("query.mod.ysm_is_create_flying", 0) ClientComp.CreateQueryVariable(levelId).Register("query.mod.ysm_is_elytra_flying", 0) -# key为需要监听的attr名称,value为需要设置的对应Molang变量名称 -queryDict = { - "playerIsFlying": "query.mod.ysm_is_flying", - "playerIsElytraFlying": "query.mod.ysm_is_elytra_flying" -} - -# 根据queryDict自动配置监听,简化代码 -def CreateAttrCallBack(bindQuery): - def _eventFuckCallBack(args): - ClientComp.CreateQueryVariable(args["entityId"]).Set(bindQuery, args["newValue"]) - return _eventFuckCallBack - class PlayerActionClientSystem(ClientSystem): def __init__(self, namespace, systemName): ClientSystem.__init__(self, namespace, systemName) # 注册本地玩家的属性值变化回调函数 - for attr, query in queryDict.items(): - ClientComp.CreateModAttr(playerId).RegisterUpdateFunc(attr, CreateAttrCallBack(query)) + modAttr = ClientComp.CreateModAttr(playerId) + modAttr.RegisterUpdateFunc("playerIsCreateFlying", self.PlayerCreateFlyStateChanged) + modAttr.RegisterUpdateFunc("playerIsElytraFlying", self.PlayerElytraFlyStateChanged) def OnAddPlayerAOIClient(self, args): """玩家加入游戏或进入视野时触发,注册属性值变化回调""" pId = args["playerId"] - for attr, query in queryDict.items(): - ClientComp.CreateModAttr(pId).RegisterUpdateFunc(attr, CreateAttrCallBack(query)) + modAttr = ClientComp.CreateModAttr(pId) + modAttr.RegisterUpdateFunc("playerIsCreateFlying", self.PlayerCreateFlyStateChanged) + modAttr.RegisterUpdateFunc("playerIsElytraFlying", self.PlayerElytraFlyStateChanged) + + def PlayerCreateFlyStateChanged(self, args): + """创造飞行状态变化回调""" + molangVar = args["newValue"] # 属性变化后的值 + pId = args["entityId"] # 属性变化对应的实体Id + ClientComp.CreateQueryVariable(pId).Set("query.mod.ysm_is_create_flying", molangVar) + + def PlayerElytraFlyStateChanged(self, args): + """鞘翅飞行状态变化回调""" + molangVar = args["newValue"] # 属性变化后的值 + pId = args["entityId"] # 属性变化对应的实体Id + ClientComp.CreateQueryVariable(pId).Set("query.mod.ysm_is_elytra_flying", molangVar) ``` ### 工作流程说明 diff --git a/mcguide/27-手机网络游戏/课程11:使用Nukkit开服/0-Nukkit开服教程.md b/mcguide/27-手机网络游戏/课程11:使用Nukkit开服/0-Nukkit开服教程.md new file mode 100644 index 0000000..9f254fb --- /dev/null +++ b/mcguide/27-手机网络游戏/课程11:使用Nukkit开服/0-Nukkit开服教程.md @@ -0,0 +1,103 @@ +--- +front: +hard: 入门 +time: 60分钟 +--- + +# Nukkit部署教程 + +## 前言 +本文默认认为你了解过Nukkit 或者 通过Waterdogpe + Nukkit搭建过第三方服务器。 + +## 准备阶段 +1. 准备Java版本,建议Java版本为Java17及以上版本 +2. 准备NukkitMOT分支,NukkitMOT原作者已将中国版打包到github主分支内 https://github.com/MemoriesOfTime/Nukkit-MOT +> 目前仅支持接入Nukkit-MOT,其他分支可自行通过参考NukkitMOT源码进行对接 +3. 准备 WaterDogPE 代理服务端。 + + +## WaterDogPE +### config.yml 的配置 + +需要修改以下参数,其他配置根据情况自行修改 +```yaml + +# .... + +netease_client: true # 启用 netease 客户端的支持 +online_mode: true +# 开发测试阶段,需要改为false,否则会提示需要 minecraft验证 +# 发布阶段时,需要改为true,之后只能用网易手机客户端连接才能进入 +listener: + # ..... + + host: 0.0.0.0:19132 # waterdogpe 代理端的IP和端口 。官方提供的机器19132端口可能会被占用,根据实际情况更改 + priorites: # 按顺序配置,第一个是玩家进入时默认在的服务器。 一般为大厅服 + - lobby1 + + # ..... +servers: # 子服务器的连接ID配置,如大厅服、游戏服等等。如子服务器不在同一台机器,则需要写具体的ip。 + lobby1: + address: 127.0.0.1:19133 + public_address: play.myserver.com:19133 + server_type: bedrock + + +permissions: # 权限配置,一般用于配置管理员的权限,用于测试、调试。仅通过名字识别,建议发布阶段去掉,改为用插件实现相关调试功能 + 玩家id: + - waterdog.player.transfer + - waterdog.player.list + - waterdog.command.server + +``` + +### 装载客户端模组 + +当通过waterdogpe代理后,需要将客户端模组存放在waterdogpe 根目录下的 packs 文件夹中,不要存放在NukkitMOT根目录 +> 除非你仅通过NukkitMOT单端开服 否则waterdogpe 会自动将nukkit模组加载数据包拦截 + +> 但如果关闭waterdogpe的资源包功能,Nukkit数据包依然会被waterdogpe拦截而无法正常进入游戏 + +确保 config.yml 已启用资源包 +```yaml +enable_packs: true # 必须打开 +overwrite_client_packs: true # 按需 +force_server_packs: true # 按需 +``` + +### 模组打包格式 + +waterdogpe 仅支持 zip和mcpack包,不支持文件夹。 +可以将行为包、资源包都存放在packs目录中。 +目前仅支持 manifest.json ,不支持 pack_manifest.json + +zip打包格式为: 压缩包一级目录下就是 manifest.json,不要额外套一层文件夹 +![图](./images/img.png) + +## Nukkit-MOT + +nukkit社区的插件 +https://cloudburstmc.org/resources/categories/nukkit-plugins.1/ + +### server.properties 配置 + +为了确保Nukkit-MOT正常接入中国版,您需要调整一些配置 +```properties +xbox-auth=off +netease-client-support=on +only-allow-netease-client=on +``` + +### 模组装载 + +如果使用 waterdogpe 代理,则需要将模组放在 waterdogpe 目录,而非Nukkit目录 +如果仅适用Nukkit,则将行为包合资源包都放在Nukkit目录下有关 netease的文件里 + +### NukkitMaster插件 + +NukkitMaster相当于Spigot服的SpigotMaster,内置封装了PyRPC、订单接口、消息收发等功能,API和使用方法和SpigotMaster一样。 + + +### 自定义物品、方块和实体 + +Nukkit-MOT本身已经支持自定义物品、方块、实体的功能,可以参考[官方文档](https://www.nukkit-mot.com/zh/docs/tutorial-extras/custom/custom_item) diff --git a/mcguide/27-手机网络游戏/课程11:使用Nukkit开服/1-NukkitMasterAPI文档.md b/mcguide/27-手机网络游戏/课程11:使用Nukkit开服/1-NukkitMasterAPI文档.md new file mode 100644 index 0000000..2a2872f --- /dev/null +++ b/mcguide/27-手机网络游戏/课程11:使用Nukkit开服/1-NukkitMasterAPI文档.md @@ -0,0 +1,277 @@ +--- +front: +hard: 入门 +time: 60分钟 +--- + +# NukkitMaster文档 + +## 准备阶段 + +在部署Nukkit服务器之前,您需要阅读一下NukkitMaster的API来进行基本的ModSDK通信与商业化内容接入。 +NukkitMaster需要安装在Nukkit-MOT服务端中。 + +> 需要注意: NukkitMaster是基于Nukkit-MOT分支进行开发的。Nukkit官方服务端版本无法兼容。 + +> 如果您需要使用其他分支的Nukkit,您可以自行反编译NukkitMaster + +> NukkitMOT分支开源地址: https://github.com/MemoriesOfTime/Nukkit-MOT + +## 插件配置 + +```yaml +# 服务器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才能生效。 +其中`gameid`、`rawkey`、`test rawkey`是必须要填写的。 +`test_server`需要根据服务器部署情况进行修改,这个值会影响NukkitMaster插件使用的是正式服url还是测试服url +`custom_shop` 和 [商业化流程](https://mc.163.com/dev/mcmanual/mc-dev/mcguide/27-%E6%89%8B%E6%9C%BA%E7%BD%91%E7%BB%9C%E6%B8%B8%E6%88%8F/%E8%AF%BE%E7%A8%8B9%EF%BC%9A%E6%9C%8D%E5%8A%A1%E5%99%A8%E4%B8%8A%E7%BA%BF/%E7%AC%AC3%E8%8A%82%EF%BC%9A%E5%95%86%E4%B8%9A%E5%8C%96%E6%93%8D%E4%BD%9C.html?key=use_custom_shop&docindex=1&type=0) 中 use custom shop 功能同理 +`shop_server_url`、`web_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> callback)` +获取玩家未发货订单列表 +参数: +`player` —— 玩家 +`callback` —— FutureCallBack 回调函数 + +例子: +回调参数为Map, 目前值为 + +| Key | Value | +|-------------|------------| +| json_result | 订单json数据对象 | +| player | Player玩家对象 | + +### `public void finPlayerOrder(Player player, List orderList, FutureCallback> 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 data)` + +给指定玩家发送服务端事件 +参数: +`player` — 接收事件的玩家 +`namespace` — 在客户端系统使用ListenForEvent监听的namespace +`system` — 在客户端系统使用ListenForEvent监听的systemName +`event` — 事件名 +`data` — 事件参数。注意,要使用-2指代本地玩家的entityId。 + + +### `public void notifyToMultiClients(List players, String namespace, String system, String event, Map 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 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 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 data)` + +给服务器内的所有玩家发送服务端事件。 因为-2的entityId对于不同玩家来说都指代本机玩家,而非某个固定的实体,所以不要在多播中发送这种信息。 +参数: +`except` — 发送事件时排除掉这个玩家,可以为null表示不排除 +`namespace` — 在客户端系统使用ListenForEvent监听的namespace +`system` — 在客户端系统使用ListenForEvent监听的systemName +`event` — 事件名 +`data` — 事件参数 + + +## API的使用例子 + +可以参考Spigot开服的商店DEMO: [商城Demo详解](https://mc.163.com/dev/mcmanual/mc-dev/mcguide/27-%E6%89%8B%E6%9C%BA%E7%BD%91%E7%BB%9C%E6%B8%B8%E6%88%8F/%E8%AF%BE%E7%A8%8B10%EF%BC%9A%E4%BD%BF%E7%94%A8Spigot%E5%BC%80%E6%9C%8D/30-Spigot%E6%9C%8DDemo%E8%AF%A6%E8%A7%A3/3-%E5%95%86%E5%9F%8EDemo%E8%AF%A6%E8%A7%A3.html) +仅仅只是将其中spigotMaster更换为nukkitMaster,其他逻辑基本相同 + +### 获取nukkitMaster对象 +```java +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"); + } +} +``` + +### 监听玩家购买商品事件、玩家催发货事件和发货逻辑 +```java + public void ListenShop() { + PyRpcHandler shipItemHandler = new PyRpcHandler() { + @Override + public void onEvent(Player player, Map data) { + tryShipItem(player); + } + }; + // 玩家催发货或者玩家购买物品成功事件进行发货检查 + nukkitMaster.listenForNukkitMasterEvent(NukkitMasterEvent.PLAYER_BUY_ITEM_SUCCESS, shipItemHandler); + nukkitMaster.listenForNukkitMasterEvent(NukkitMasterEvent.PLAYER_URGE_SHIP, shipItemHandler); + } + + // 获取玩家订单,并且尝试发货 + public void tryShipItem(Player player){ + FutureCallback> cbHandler = new FutureCallback>() { + @Override + public void completed(Map result) { + JSONObject jsonRes = (JSONObject)result.get("json_result"); + Player requestPlayer = (Player)result.get("player"); + + JSONArray entities = (JSONArray)jsonRes.get("entities"); + // 这里进行entites的订单内容发放 + List 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 finOrderList){ + FutureCallback> cbHandler = new FutureCallback>() { + + @Override + public void completed(Map 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完成事件 diff --git a/mcguide/27-手机网络游戏/课程11:使用Nukkit开服/README.md b/mcguide/27-手机网络游戏/课程11:使用Nukkit开服/README.md new file mode 100644 index 0000000..e69de29 diff --git a/mcguide/27-手机网络游戏/课程11:使用Nukkit开服/images/img.png b/mcguide/27-手机网络游戏/课程11:使用Nukkit开服/images/img.png new file mode 100644 index 0000000000000000000000000000000000000000..1d4c7ac6ffefa6f683eb32b7bb26bf88c8454bbf GIT binary patch literal 6507 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 KRNgCF1^o|5tW;D0 literal 0 HcmV?d00001 diff --git a/mcguide/35-上架与入驻/课程11.1-整合包概览.md b/mcguide/35-上架与入驻/课程11.1-整合包概览.md new file mode 100644 index 0000000..dc9ec37 --- /dev/null +++ b/mcguide/35-上架与入驻/课程11.1-整合包概览.md @@ -0,0 +1,174 @@ +--- +front: +hard: 入门 +time: 15分钟 +selection: true +--- + +# 整合包概览 + +> 文档版本:2023.2.21, **首次更新整合包的使用说明** 。 + +《我的世界》整合包是一种面向玩家的新型购物模式,它能将多个上线模组捆绑在一起进行打折销售,为开发者的专一粉丝提供附加价值与更多惠益。针对常见情况,有两种普遍的使用场景: + +- 结合 主包 搭配 DLC 的模式销售系列模组。 +- 根据模组题材进行分类,帮助粉丝玩家更短时间找到喜欢的内容。 + +原合集功能将支持一键同步合集模组至整合包,后续开发者也无法创建新的合集,详情请查看[一键同步合集模组至整合包](./课程11.2-一键同步合集模组至整合包.html)。 **资源中心** - **开发者主页** 的合集分区将替换为整合包分区。 **且整合包当前仅支持《我的世界》手机版。** + +《我的世界》开发者平台将在 **2月21日** 上线整合包功能,玩家可在 **3月23日** 后下载整合包。 + +期待各位开发者挖掘整合包的更多整合,使用上的建议与反馈可以通过[问题与反馈中心](https://mcdev.webapp.163.com/#/feedbackModal?target=browser)与我们取得联系。《我的世界》开发者平台也会提供独立的整合包流量位,敬请期待。 + + + +## 创建整合包 + +进入 《我的世界》开发者平台,点击 **作品管理** - **上架与资源管理**,接着点击 **模组** - **手机版** - **整合包**。 + +![image-20230216150314989](./images/bundle_8.png) + + + +点击 **创建整合包** ,可先设置 **整合包名称** 、 **整合包类型** 、 **基础折扣** 与 **限时折扣** 。 + +- 整合包类型:分为 **普通整合包** 、 **纯地图整合包** 。 + - **普通整合包** :可以选择任一类型的模组。 + - **纯地图整合包** :只能选择 **地图模组** 。 +- 整合包名称:将作为整合包的名称进行展示。 +- 基础折扣:必填,整合包上线后的默认折扣。 +- 限时折扣:选填,可设置整合包在特定时间期间的折扣。整合包内的基础折扣与限时折扣同时生效时, **优先使用限时折扣** 。 + +![image-20230216163112834](./images/bundle_12.png) + + + + **纯地图整合包** 最低折扣为50%, **普通整合包** 有价格限制,最低折扣需按模组原价折后价**500钻石**设置。整合包类型将根据包内模组类型情况进行自动选择。 + +模组购买价格将按照**模组本身的折扣**以及**整合包限时折扣**进行叠加,若整合包限时折扣不存在时,将由整合包基础折扣代替。 + +折扣的运作模式可参考下图: + +![bundle_example_0](./images/bundle_0.png) + + + +接着在资源区域添加与整合包关联的模组。其中,整合包最多关联20个,最少关联3个 **有过审记录的模组资源** 。 **单个模组资源可加入的整合包次数不限** 。 + +![image-20230216162103730](./images/bundle_2.png) + + + +添加模组资源后,必须设置其中一个模组为核心模组。核心模组默认在第一顺位展示。开发者可在每次关联模组时,通过添加顺序管理新增的模组展示顺序,未来也会提供直接调整非核心模组的展示顺序功能,敬请期待。 + +![image-20230216161858452](./images/bundle_6.png) + + + +接着填充 **详情信息** 、**编辑图片** 和 **上传视频** 区域的相关内容,这些信息内容将会展示在整合包详情页中。也可直接点击同步按钮同步核心资源的资源描述,减少人工操作的时间。 + +![image-20230216162211163](./images/bundle_5.png) + +![image-20230216142214820](./images/bundle_3.png) + +![image-20230216142226019](./images/bundle_4.png) + + + +与编辑模组上传信息不同,整合包的 **更新纪要** 为必填内容。设置之后会在玩家的订阅功能中同步生成资源卡片,并会在手机消息通知中同步收到提醒。 + +![image-20230216141926921](./images/bundle_1.png) + + + +## 自测、提审与更新整合包 + +进入 《我的世界》开发者平台,点击 **作品管理** - **上架与资源管理**,接着点击 **模组** - **手机版** - **整合包**。 + +![image-20230216145005670](./images/bundle_8.png) + + + +对已创建好的整合包,可在《我的世界》2.6整包Beta更新后,点击自测按钮进行 **自测** ,在手机版测试启动器中预览对应的整合包。 + +点击 **提交审核** 按钮对整合包提交审核,点击后需进行二次确认才可提审,同样支持在二次确认弹窗中补充提审说明。 + +![image-20230216143926571](./images/bundle_9.png) + +点击 **编辑** 按钮可重新进入整合包编辑页面,编辑更新后记得保存。 + +![image-20230216144831521](./images/bundle_7.png) + + + +## 下架整合包 + +在单个整合包详情页中,找到 **上架设置** 。编辑 **上架设置** ,勾选 **弱下架** ,并提供 **弱下架原因** 。 + +![image-20230216161426665](./images/bundle_11.png) + + + +最后重新提审,等待审核通过后即可下架整合包。 + + + +## 整合包FAQ + +### 整合包的展示位置在哪里? + +答:整合包会在 **关联的单个模组详情页** 下进行展示,也可在开发者主页下的 **整合包分区** 中找到。 + + + +### 整合包的折扣后价格是如何计算得出的? + +答:价格是所有关联整合包的模组当前总价与整合包折扣相乘后的折扣价格, **折扣后的价格出现小数点时,将向下取整到整数钻石** 。例如其中一个模组原价20钻石,使用模组折扣5折,并添入基础折扣为88折的整合包,则该模组最终在整合包的价格向下取整至8钻石。其余细则具体可查看 [创建整合包](#创建整合包)的折扣运作模式。 + + + +### 整合包的评分与评论系统来源是哪里? + +答:整合包的评分与评论系统将直接使用核心资源的评分与评论。 + + + +### 整合包支持加入购物车、心愿单与索要转赠功能吗? + +答:整合包暂不支持搭配购物车、心愿单与索要转赠功能使用。 + + + +### 是否可以将整合包加入折扣特卖或作品活动? + +答:目前尚不支持开发者登记整合包至[折扣特卖或作品活动](../40-活动、推广与收益/课程08-对作品进行推广与活动.html?catalog=1#活动参与)。 + + + +### 是否可以使用整合包的参与手机版推广位申请与竞拍? + +答:目前尚不支持在[推广位申请与竞拍功能](../40-活动、推广与收益/课程16-推广位申请与竞拍试运行指南.html?catalog=1#申请手机版轮播图推广位)内选择整合包,请期待后续更新。 + + + +### 整合包的审核流程是什么形式? + +答:与模组审核流程一致,开发者需要点击提审进入审核队列,并通过开发者邮箱与短信获得审核结果。 + + + +### 已购模组在整合包内会如何展示? + +答:已购模组会在整合包内的模组列表显示已购标识,同理,未购模组也会显示未购买标识。 + + + +### 如何更换/新增/取消关联核心模组与非核心模组? + +答: **在整合包首次上架前,开发者可以随时更换/新增与取消关联相应的核心模组与非核心模组。** 但在上架后,就无法更换/取消之前的核心模组或非核心模组,只允许关联其他新的模组资源为非核心模组。 + + + +### 核心模组或其他关联模组下架时,整合包会如何处理? + +答:核心模组下架后,整合包将会消失,请开发者注意。其他关联模组下架时,整合包会清除对应模组信息。 \ No newline at end of file