feat:上传mcguide-开发指南部份
This commit is contained in:
@@ -0,0 +1,199 @@
|
||||
---
|
||||
front:
|
||||
hard: 入门
|
||||
time: 60分钟
|
||||
---
|
||||
|
||||
# 自定义物品Demo详解
|
||||
|
||||
## 前置知识
|
||||
|
||||
[Spigot服自定义物品原理简介](../22-Spigot服自定义物品原理简介.md)
|
||||
|
||||
### DEMO详解
|
||||
|
||||
[示例Demo](../99-下载内容.html#示例demo)中的CustomItemDemo包含了客户端mod及spigot插件。
|
||||
|
||||
Demo实现了11个不同的自定义物品:
|
||||
- 剑 (customitem:item_1)
|
||||
|
||||
当鼠标右键方块时,方块所在位置发生半径为1.5格的爆炸
|
||||
- 斧 (customitem:item_2)
|
||||
|
||||
当鼠标右键方块时,方块所在位置出现落雷效果,并随机生成物品,必定掉落**铲**
|
||||
- 铲 (customitem:item_3)
|
||||
|
||||
当鼠标右键方块时,方块所在位置出现落雷效果,并随机生成物品,必定掉落**剩余自定义物品**
|
||||
- 锄 (customitem:item_4)
|
||||
|
||||
自定义物品的耐久度, 2/60(剩余耐久/总耐久)
|
||||
- 弓 (customitem:item_5)
|
||||
|
||||
自定义弓箭,无特殊效果
|
||||
- 稿 (customitem:item_11)
|
||||
|
||||
自定义弓箭,无特殊效果
|
||||
- 胸甲 (customitem:item_6)
|
||||
|
||||
自定义胸甲,无特殊效果
|
||||
- 腿甲 (customitem:item_7)
|
||||
|
||||
自定义护腿,无特殊效果
|
||||
- 鞋甲 (customitem:item_8)
|
||||
|
||||
自定义鞋子,无特殊效果
|
||||
- 头甲 (customitem:item_9)
|
||||
|
||||
自定义头盔,无特殊效果
|
||||
- 盾甲 (customitem:item_10)
|
||||
|
||||
自定义盾牌,无特殊效果
|
||||
|
||||
### 开发流程
|
||||
|
||||
#### 客户端Mod编写
|
||||
|
||||
##### 目的
|
||||
为了让Geyser能加载新增的自定义物品,我们需要编写客户端Mod
|
||||
|
||||
##### 流程
|
||||
|
||||
- 在behavior文件夹中新建**netease_item_beh**目录,如下图所示
|
||||
|
||||

|
||||
|
||||
- 在**netease_item_beh**目录下新增11个物品Json,其中"java_identifier"字段用于标识自定义物品的原生Java换皮物品。样例如下:
|
||||
|
||||

|
||||
|
||||
- 在**resource**文件夹中新建**netease_item_res**、**texts**、**textures**目录,如下图所示
|
||||
|
||||

|
||||
|
||||
这三个目录的作用分别如下:
|
||||
- **netease_item_res**用于存放自定义物品客户端贴图表现Json
|
||||
- **texts**用于存放自定义内容的中文命名
|
||||
- **textures**用于存放自定义内容的贴图文件
|
||||
|
||||
- 在**netease_item_res**目录下新增三个物品Json,样例如下:
|
||||
|
||||
样例中,我们通过**minecraft:icon**这个**Component**为物品设置贴图
|
||||
|
||||

|
||||
|
||||
- 在**texts**目录下新增**zh_CN.lang**文件,设置自定义物品的名称,样例如下:
|
||||
|
||||

|
||||
|
||||
- 在**textures**目录下新增**item_texture.json**文件,设置物品贴图对应的贴图路径,样例如下:
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
- 至此,客户端Mod中自定义物品完成
|
||||
|
||||
#### Spigot插件编写
|
||||
|
||||
##### 目的
|
||||
为了让自定义物品能有不同的效果、功能,我们还需要编写Spigot插件,实现不同物品的不同效果逻辑
|
||||
|
||||
##### 流程
|
||||
|
||||
1. 安装SpigotMaster插件
|
||||
|
||||
详见[下载内容](../99-下载内容.md)
|
||||
|
||||
2. 如App.java所示,实例ServerOriginalListener监听了Spigot原生事件
|
||||
|
||||
通过下述接口实现
|
||||
```
|
||||
getServer().getPluginManager().registerEvents(new ServerOriginalListener(), this);
|
||||
```
|
||||
|
||||
3. 实例**ServerOriginalListener**监听了Spigot原生的事件,**ServerOriginalListener**监听方法如图:
|
||||
示例中,**ServerOriginalListener**一共监听了两个事件,如下:
|
||||
- 玩家加入事件(PlayerJoinEvent), 效果为玩家加入游戏时,给玩家自动发放两个物品
|
||||
- 玩家交互事件(PlayerInteractEvent),效果为玩家和方块右键交互时,根据不同自定义物品,触发不同的效果
|
||||
|
||||

|
||||
|
||||
4. 创建物品
|
||||
- 通过Spigot提供的Api,创建物品ItemStack
|
||||
```
|
||||
@Param material 物品material(自定义物品的material通过下一步获取)
|
||||
@Param amount 物品数量
|
||||
ItemStack customItem1 = new ItemStack(material, amount);
|
||||
```
|
||||
- 在创建ItemStack时,自定义物品的Material必须与"java_identifier"字段一致
|
||||
|
||||
- 同时,通过下述接口,为物品设置基岩版中对应的Identifier
|
||||
**PS: 这一步为必须步骤,缺少这一步时,会导致客户端无法正常生成自定义物品**
|
||||
```
|
||||
@Param itemStack 通过Spigot接口生成的ItemStack
|
||||
@Param itemIdentifier 自定义物品Identifier,需要和客户端Mod中定义一致
|
||||
SpigotMaster.setCustomItemIdentifier(itemStack, itenIdentifier)
|
||||
```
|
||||
- 根据需要,再进一步修改ItemStack的其他属性,如样例中的lore
|
||||
|
||||
5. 使用物品
|
||||
- 通过事件参数获取ItemStack:
|
||||
```
|
||||
ItemStack item = event.getItem();
|
||||
```
|
||||
- 通过接口获取物品的Identifier:
|
||||
```
|
||||
@Param itemStack 通过Spigot接口生成的ItemStack
|
||||
@Return String itemIdentifier,非自定义物品返回null
|
||||
SpigotMaster.getCustomItemIdentifier(itemStack)
|
||||
```
|
||||
根据获取到的Identifier实现不同的逻辑,如图所示
|
||||
|
||||

|
||||
|
||||
6.运行mvn clean install,会在插件target下生成插件.jar,把生成的jar放置于Spigot服的plugin文件夹下
|
||||
|
||||
7.最终实现的效果如下:
|
||||
- 玩家进入游戏即可获取两个自定义物品
|
||||
|
||||

|
||||
|
||||
- 使用剑时发生爆炸
|
||||
- 使用斧时有打雷效果,并生成物品,其中物品必掉落铲
|
||||
- 使用铲时有打雷效果,并掉落剩余所有自定义物品
|
||||
|
||||
### Q&A
|
||||
|
||||
- **SpigotMaster插件**简要API文档
|
||||
|
||||
[SpigotMaster插件API文档](../81-SpigotMasterAPI文档.md)
|
||||
|
||||
|
||||
### **需要注意的是**
|
||||
- 由于目前自定义物品为Java换皮物品,因此字段工具挖掘速度、挖掘等级需要和Java物品对应,不然会出现方块破坏速率不一致导致的卡方块现象。
|
||||
```
|
||||
"netease:weapon":{
|
||||
"type":"shovel",
|
||||
"level":0,
|
||||
"speed":2
|
||||
}
|
||||
```
|
||||
|
||||
具体对应关系如下:
|
||||
|
||||
| 键 | 类型 | 默认值 | 解释 |
|
||||
| ------------- | ---- | ------ | ------------------------------------------------------------ |
|
||||
| type | str | | 武器/工具的类型,目前支持类型有:<br>sword:剑<br>shovel:铲<br>pickaxe:镐<br>hatchet:斧<br>hoe:锄头 |
|
||||
| level | int | | level为0:当速度为2对应木板,否则对应金锭 <br>level为1:对应石头<br>level为2:对应铁锭<br>level为3:对应钻石<br>level大于3:无法使用铁砧修复 |
|
||||
| speed | int | 0 | 对采集工具生效,表示挖掘方块时的基础速度<br>木头:2<br>石头:4<br>铁:6<br>钻石:8<br>金:12 |
|
||||
|
||||
|
||||
样例中,为木头工具换皮,因此为level:0, speed:2
|
||||
|
||||
- 同理,盔甲字段,需要和Java物品对应
|
||||
```
|
||||
"netease:armor":{
|
||||
"armor_slot":2
|
||||
}
|
||||
```
|
||||
盔甲槽位,详见<a href="../../../../mcdocs/1-ModAPI/枚举值/ArmorSlotType.html" rel="noopenner"> ArmorSlotType </a>
|
||||
@@ -0,0 +1,65 @@
|
||||
---
|
||||
front:
|
||||
hard: 入门
|
||||
time: 30分钟
|
||||
---
|
||||
|
||||
|
||||
# PyRpcDemo详解
|
||||
|
||||
## 前置知识
|
||||
|
||||
[Spigot服与客户端python通信原理简介](../21-Spigot服与客户端python通信原理简介.md)
|
||||
|
||||
## DEMO详解
|
||||
|
||||
[示例Demo](../99-下载内容.html#示例demo)中的PyRpcDemo包含了客户端mod及spigot插件。
|
||||
|
||||
进入游戏后会在右侧显示3个按钮
|
||||
|
||||
* 点击“打开窗口”会弹出一个UI,再点击“获取随机数”会从spigot获取一个0-9的随机数并显示在ui上。点击x关闭
|
||||
* 点击“绑定特效”会通知当前world内所有玩家,给发起玩家替换模型并挂接一个特效
|
||||
|
||||
* 点击”广播消息“会在spigot内所有玩家的聊天栏显示一条消息
|
||||
|
||||
|
||||
|
||||
### 客户端部分
|
||||
|
||||
1. 在客户端初始化时注册UiInitFinished事件,并在UiInitFinished事件中创建三个按钮的ui,注册后续将要使用的弹出窗口ui
|
||||
|
||||
注册两个自定义事件:
|
||||
|
||||
- bindEffect:给entityId参数对应实体更换模型以及创建特效
|
||||
- showMsg:在本地显示聊天栏消息
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
2. 在为三个按钮注册回调函数
|
||||
|
||||
”打开窗口“按钮抬起时弹出随机数的ui
|
||||
|
||||
”绑定特效“和”广播消息“按钮抬起时给spigot发送自定义消息
|
||||
|
||||

|
||||
|
||||
3. 在随机数的ui创建时监听获取随机数的回调事件,将参数中的值显示到label控件上
|
||||
|
||||
注册按钮回调:”获取随机数“按钮抬起时向spigot发送一个自定义消息,关闭按钮抬起时弹出界面
|
||||
|
||||
界面销毁时反监听获取随机数的回调事件
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
### spigot部分
|
||||
|
||||
- 初始化时注册自定义事件,分别为:
|
||||
- requestRandom:给玩家返回随机数获取回调
|
||||
- requestBindEffect:给本人返回-2的entityId,给world内其他人返回spigot的entityId
|
||||
- requestMsg:给spigot内所有人返回消息
|
||||
|
||||

|
||||
128
mcguide/27-网络游戏/课程10:使用Spigot开服/30-Spigot服Demo详解/3-商城Demo详解.md
Normal file
128
mcguide/27-网络游戏/课程10:使用Spigot开服/30-Spigot服Demo详解/3-商城Demo详解.md
Normal file
@@ -0,0 +1,128 @@
|
||||
---
|
||||
front:
|
||||
hard: 入门
|
||||
time: 30分钟
|
||||
---
|
||||
|
||||
# 商城Demo详解
|
||||
|
||||
Demo给出了基于目前SpigotMaster提供的接口接入商场的基础方案
|
||||
|
||||
- 提供了四个命令
|
||||
- openShop
|
||||
|
||||
打开玩家商城界面
|
||||
- closeShop
|
||||
|
||||
关闭玩家商城界面
|
||||
- showOne tips
|
||||
|
||||
弹出冒泡提示
|
||||
- showTwo tipHead tipsTail
|
||||
|
||||
弹出拼接后冒泡提示
|
||||
|
||||
|
||||
- 客户端部分很简单,只需简单地注册客户端即可
|
||||
|
||||

|
||||
|
||||
- Spigot插件部分主要包含两个接口的调用
|
||||
|
||||
- spigotMaster.getPlayerOrderList(player, callback)
|
||||
该接口用于获取指定玩家尚未发货的所有订单
|
||||
|
||||
- spigotMaster.finPlayerOrder(player, callback)
|
||||
该接口用于通知网易服务器标记指定订单已发货
|
||||
|
||||
值得注意的是,目前两个接口采用http异步请求,意味着调用**finPlayerOrder**后,订单不一定已经标记完成。
|
||||
|
||||
如果出现已经通知过网易服务器完成发货的订单,请调用**finPlayerOrder**再次通知网易服务器
|
||||
|
||||
# 插件逻辑流程
|
||||
|
||||
- 通过enableCustomShopEntry接口决定是否使用自定义的入口,参数为false时,开启官方商城入口。参数为true时,官方商城入口不显示,由开发者自行决定入口ui逻辑
|
||||
|
||||
注意,该接口为首次初始化调用即可,重复调用多次无效
|
||||
|
||||
- 首先是调用listenForSpigotMasterEvent接口,监听SpigotMaster抛出的事件
|
||||
分别为:玩家购买物品成功事件、玩家催促发货事件
|
||||

|
||||
|
||||
- 收到两个事件其中之一时,调用getPlayerOrderList接口,请求获取玩家订单
|
||||

|
||||
|
||||
# 重要提醒:
|
||||
- 如果mc游戏已经对订单发货,但**getPlayerOrderList**仍然返回了该条订单,请不要重复发货。请mc游戏服务必再次调用接口**finPlayerOrder**,再次通知网易服务器修改订单状态
|
||||
- 强烈建议mc服务器对订单发货进行排队处理,避免并发时发生重复发货的情况
|
||||
- 返回的订单列表数量上限为100,如果玩家未发货订单超过100,则超出的订单下次请求返回
|
||||
|
||||
# getPlayerOrderList返回Json样例
|
||||
```
|
||||
{
|
||||
// code为0表示成功返回,否则失败,错误码参考错误码列表
|
||||
"code": 0,
|
||||
"message": "正常返回",
|
||||
"details": "",
|
||||
"entities": [
|
||||
{
|
||||
// item_id:商品id,仅记录用
|
||||
"item_id": 90027446413343740,
|
||||
|
||||
// uuid:玩家的唯一编号
|
||||
"uuid": "8a0886b5-eeb5-41f0-b517-f65691a2ce3b",
|
||||
|
||||
// item_num:玩家购买的道具数量
|
||||
"item_num":1,
|
||||
|
||||
// orderid:订单编号
|
||||
"orderid":1234,
|
||||
|
||||
// cmd:实现指令(商品唯一标识,请以此判断玩家购买的是什么商品,空字符串为官方预留,服务器自己设置的cmd内容是不允许为空字符串的)
|
||||
"cmd":"test",
|
||||
|
||||
// buy_time:购买时间戳
|
||||
"buy_time":1230782400,
|
||||
|
||||
// group:道具分类
|
||||
"group" : 1
|
||||
|
||||
// type:道具类型(保留,一般默认为空字符串,官方活动奖励会用到)
|
||||
// extra:额外数据(保留,一般默认为空字典,官方活动奖励会用到)
|
||||
|
||||
},
|
||||
{
|
||||
"item_id": 90027446413343740,
|
||||
"uuid": "8a0886b5-eeb5-41f0-b517-f65691a2ce3b",
|
||||
"item_num":1,
|
||||
"orderid":1234,
|
||||
"cmd":"test",
|
||||
"buy_time":1230782400,
|
||||
"group" : 1
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
# finPlayerOrder返回Json样例
|
||||
```
|
||||
{
|
||||
// code为0表示成功返回,否则失败,错误码参考错误码列表
|
||||
"code": 0,
|
||||
"message": "正常返回",
|
||||
"details": "",
|
||||
"entities": []
|
||||
}
|
||||
```
|
||||
|
||||
# 错误码列表
|
||||
|
||||
- 0:成功返回
|
||||
- 4:参数为空
|
||||
- 12:参数错误
|
||||
- 18:数据库内部错误
|
||||
- 52:订单不存在
|
||||
- 53:签名错误
|
||||
- 54:订单状态错误,原因是该订单未扣费或mc游戏中已经发过货
|
||||
- 55:玩家未购买该网络游戏
|
||||
- 56:秘钥未分配
|
||||
@@ -0,0 +1,37 @@
|
||||
---
|
||||
front:
|
||||
hard: 入门
|
||||
time: 40分钟
|
||||
---
|
||||
|
||||
# 自定义生物模型详解
|
||||
|
||||
[示例Demo](../99-下载内容.html#示例demo)中的包含CustomHumanModelDemo和CustomPigModelDemo。其中
|
||||
|
||||
修改玩家模型的demo为:CustomHumanModelDemo
|
||||
- 修改玩家模型
|
||||
|
||||
修改原版猪生物模型的demo为:CustomPigModelClientMod
|
||||
由于修改猪为存客户端逻辑,因此不需要Spigot插件即可
|
||||
- 50%几率生成黄皮肤猪(修改原生生物模型)
|
||||
|
||||
|
||||
## 修改玩家模型流程
|
||||
- 自定义模型以及json详细参数详见:
|
||||
[自定义生物](../../../20-玩法开发/15-自定义游戏内容/3-自定义生物/01-自定义基础生物.md)
|
||||
|
||||
- 具体生效逻辑为:
|
||||

|
||||
|
||||
- 最终效果图如下:
|
||||

|
||||
|
||||
## 修改原生猪模型流程
|
||||
- 具体生效逻辑为:
|
||||
- 初始化molang变量
|
||||
- 当生成生物时,根据生物entityid创建molang变量,最终赋值
|
||||
|
||||

|
||||
- 赋值后,由于自定义猪的render_controller中根据猪molang值判断,若符合条件,则为变色猪
|
||||
- 最终效果如下:
|
||||

|
||||
@@ -0,0 +1,198 @@
|
||||
---
|
||||
front:
|
||||
hard: 入门
|
||||
time: 40分钟
|
||||
---
|
||||
|
||||
# 自定义实体Demo详解
|
||||
|
||||
## 概要
|
||||
[示例Demo](../99-下载内容.html#示例demo)中的CustomEntityDemo包含客户端Mod及Java插件,其中:
|
||||
|
||||
- 客户端Mod新增了三种生物
|
||||
- netease:squirrel
|
||||
- 自定义松鼠,spigot实体根据命令,可对应不同生物
|
||||
- netease:robot
|
||||
- json配置碰撞盒大小为3x3
|
||||
- 自定义机器人,spigot实体固定对应狼
|
||||
- netease:spider
|
||||
- json配置碰撞盒大小为2*3
|
||||
- 客户端继承马
|
||||
|
||||
- Java插件新增了一个指令/addEntity,简单展示了三种新增实体的方式
|
||||
- 仅客户端换皮
|
||||
- 命令
|
||||
- /addEntity WOLF(spigot生物identifier) netease:squirrel(客户端生物identifier)
|
||||
- 效果
|
||||
- 生成一只客户端表现为松鼠,实际行为为狼的换皮生物
|
||||
|
||||
- 使用mythicmob插件进行生物换皮
|
||||
- 命令
|
||||
- /addEntity AngrySludge
|
||||
- /addEntity StaticallyChargedSheep
|
||||
- /addEntity SkeletalMinion
|
||||
- /addEntity SkeletonKing
|
||||
- 效果
|
||||
- 生成客户端为机器人,实际为MythicMob史莱姆
|
||||
- 生成客户端为机器人,实际为MythicMob闪电羊
|
||||
- 生成客户端为松鼠,实际为MythicMob骷髅兵
|
||||
- 生成客户端为机器人,实际为MythicMob骷髅王,骷髅王被攻击后会发送信息,并且有概率会召唤骷髅兵
|
||||
|
||||
- 继承原生生物类进行换皮
|
||||
- 命令
|
||||
- /addEntity
|
||||
- 效果
|
||||
- 生成一只客户端表现为机器人,实际行为为狼的生物,修改攻击力为20,同时覆写了攻击函数,实现攻击时产生随机特效
|
||||
- 生成一只客户端表现为蜘蛛,实际行为为马的生物,同时覆写骑乘函数,实现骑乘两人
|
||||
|
||||
## 客户端开发流程
|
||||
- 自定义松鼠
|
||||
1. 参照[自定义生物](../../../20-玩法开发/15-自定义游戏内容/3-自定义生物/01-自定义基础生物.md),如实体定义、动画controller、动画、客户端实体定义、骨骼模型、贴图等
|
||||

|
||||
|
||||
2. 需要注意的是animation_controllers中使用molong语法,获取mod.is_moving变量,当收到rpc调用时,修改相关值,让松鼠在被攻击时播放移动动作
|
||||

|
||||
|
||||
3. 客户端注册molong值增加rpc调用监听,当收到事件时,修改松鼠的molong值
|
||||

|
||||
|
||||
- 自定义机器人
|
||||
1. 类似松鼠,增加各项文件,其中特别设置碰撞盒大小
|
||||

|
||||
2. 机器人的animation_controllers使用微软原生molang变量,只要机器人浮空,即可触发浮空的动作
|
||||

|
||||
3. 机器人的 animation_controllers使用Mod自定义molang变量,只要plugin发送rpc包,即可触发机器人放技能动作
|
||||

|
||||
|
||||
强烈建议阅读:
|
||||
- 关于机器人的模型来源、动画制作、molong语法详细解读
|
||||
|
||||
[机器人Demo](https://learn.microsoft.com/zh-cn/minecraft/creator/documents/entitymodelingandanimation)
|
||||
|
||||
- 自定义蜘蛛
|
||||
1. 类似松鼠,增加各项文件,同时增加 **minecraft:rideable** 组件、**minecraft:can_power_jump** 组件,支持骑乘和蓄力跳跃、客户端继承马实体 **"runtime_identifier" : "minecraft:horse"**
|
||||

|
||||
|
||||
至此客户端Mod开发结束
|
||||
|
||||
## Java插件开发流程
|
||||
|
||||
Demo中的Java插件演示了三种换皮方式,其具体的适用范围、实际效果我们一一讲述。
|
||||
|
||||
### 仅客户端换皮
|
||||
|
||||
- 应用场景:
|
||||
|
||||
- 只支持修改生物的模型、动画、碰撞盒大小
|
||||
|
||||
- 不支持修改生物的行为逻辑,如攻击、生命、敌对行为等等
|
||||
|
||||
- 命令格式为:
|
||||
```
|
||||
/addEntity WOLF netease:robot
|
||||
```
|
||||
|
||||
- 具体代码示例如下:
|
||||
```
|
||||
spigotIdentifier = "WOLF";
|
||||
clientIdentifier = "netease:robot";
|
||||
EntityType spigotType = EntityType.valueOf(spigotIdentifier);
|
||||
Entity mob = spigotMaster.spawnEntity(player, player.getLocation(), spigotType, clientIdentifier);
|
||||
```
|
||||
|
||||
- 上述代码的最终效果如下,生成一个本质上为狼,但是模型是机器人的实体
|
||||

|
||||
|
||||
### 使用mythicmob插件进行生物换皮
|
||||
|
||||
Demo中使用的MythicMob插件为**4.13.0**版本
|
||||
|
||||
- 应用场景:
|
||||
|
||||
- MythicMob插件提供了强大的自定义生物功能,包括生物的技能、血量、体积等等
|
||||
|
||||
- 不同版本的MythicMob需要根据各自版本的事件、API自行适配
|
||||
|
||||
- 插件开启时,监听MythicMob插件的生物生成事件
|
||||
|
||||

|
||||
|
||||
- 同时新增Mechanic,通过配置,达到玩家触发技能时,发送rpc包
|
||||
|
||||
下图演示的是新增的yml配置
|
||||

|
||||

|
||||
|
||||
下图演示的是代码逻辑实现
|
||||

|
||||

|
||||
|
||||
- 命令格式为:
|
||||
```
|
||||
/addEntity SkeletonKing
|
||||
```
|
||||
|
||||
- 生成对应生物时,设置生物对应的客户端identifier,这里提供两种设置方案
|
||||
- 第一种适用于固定identifier,Mob自动对应到固定的identifier,如:简单讲就是 **mythicMob:mobType**(固定前缀 + MobType首字母缩写)
|
||||
- SkeletonKing -> mythicMob:skeletonKing
|
||||
- SkeletalMinion -> mythicMob:skeletalMinion
|
||||
```
|
||||
String typeStr = event.getMob().getMobType();
|
||||
spigotMaster.setMythicMobIdentifier(event.getEntity(), typeStr);
|
||||
```
|
||||
|
||||
- 第二种适用于想自定义对应identifier,相同Mob可对应不同identifier,如下代码
|
||||
```
|
||||
// 把MythicMob插件的骷髅王样例映射为客户端的机器人
|
||||
if(typeStr.equals("SkeletonKing")) {
|
||||
spigotMaster.setCustomEntityIdentifier(event.getEntity(), "netease:robot");
|
||||
}
|
||||
// 把MythicMob插件的骷髅小兵样例映射为客户端的松鼠
|
||||
else if(typeStr.equals("SkeletalMinion")){
|
||||
spigotMaster.setCustomEntityIdentifier(event.getEntity(), "netease:squirrel");
|
||||
}
|
||||
```
|
||||
|
||||
- 最终效果如下, MythicMob插件官方给的骷髅王例子的具体效果有
|
||||
- 被攻击后,发送消息
|
||||
- 被攻击有一定概率会召唤骷髅小兵,召唤技能的动作为右手平挥
|
||||

|
||||
|
||||
### 继承原生生物类进行换皮
|
||||
|
||||
- 应用场景:
|
||||
|
||||
- 要求十分了解生物属性、逻辑,能阅读混淆后逻辑,并自行继承改造相应逻辑
|
||||
|
||||
- 可改造范围十分广泛,行为逻辑定制灵活性很高
|
||||
|
||||
- 命令格式为:
|
||||
|
||||
1、目前demo样例中,展示了如何继承并修改原生狼的攻击函数,在客户端以机器人的模型表现出来
|
||||
|
||||
2、展示了客户端模型为蜘蛛的马
|
||||
|
||||
```
|
||||
/addEntity
|
||||
```
|
||||
|
||||
- 插件开启时,反射修改Spigot核心的实体定义,并把狼的实体映射为自定义的Robot类
|
||||
|
||||
**这里需要注意的是,目前1.12.2版本需要Viaversion,因此注册时,需要采用已用的EntityId,如图中的EntityType.valueOf("WOLF").getTypeId()**
|
||||
|
||||
**如果只是单纯地取最大值递增,最终Viaversion会拦截下相关实体创建,导致实体生成失败**
|
||||

|
||||
|
||||
- Robot类中,修改狼的攻击里为20, 并重载狼地攻击函数,随机产生攻击效果
|
||||
|
||||

|
||||
|
||||
- MySpider类中,修改马的骑乘判断函数、骑乘函数
|
||||
|
||||

|
||||
|
||||
|
||||
- 最终效果如下:
|
||||

|
||||
|
||||

|
||||
@@ -0,0 +1,373 @@
|
||||
---
|
||||
front:
|
||||
hard: 入门
|
||||
time: 40分钟
|
||||
---
|
||||
|
||||
# ServerFormDemo详解
|
||||
|
||||
## 概要
|
||||
Geyser的服务端FormUI基于Cumulus,允许开发者直接通过服务端接口生成客户端UI,不需要编写客户端Mod。
|
||||
|
||||
基础的用法和 [Geyser官方文档](https://wiki.geysermc.org/geyser/forms/) 中演示的相差不大,只是发送逻辑需要通过SpigotMaster调用,而不是Floodgate插件
|
||||
|
||||
|
||||
## 准备工作
|
||||
在编写FormUI逻辑前,需要先添加依赖,具体如下图所示:
|
||||
|
||||
```
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>nexus</id>
|
||||
<name>Lumine Releases</name>
|
||||
<url>https://mvn.lumine.io/repository/maven-public/</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>opencollab-repo</id>
|
||||
<url>https://repo.opencollab.dev/maven-releases</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.geysermc.cumulus</groupId>
|
||||
<artifactId>cumulus</artifactId>
|
||||
<version>1.1.1</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||

|
||||
|
||||
|
||||
## Java插件开发流程
|
||||
|
||||
Demo中的Java插件演示了三种FormUI及FormUI响应处理逻辑,其具体的适用范围、实际效果我们一一讲述。
|
||||
|
||||
### ModalForm
|
||||
|
||||
- 应用场景:
|
||||
|
||||
- ModalForm是最简单的表单形式,但可定制程度较低
|
||||
|
||||
- 仅支持标题、内容和两个按钮
|
||||
|
||||
- 命令格式为:
|
||||
```
|
||||
/showForm 1
|
||||
```
|
||||
|
||||
- 具体代码示例如下:
|
||||
```
|
||||
ModalForm.Builder builder = ModalForm.builder()
|
||||
.title("Title")
|
||||
.content("Content")
|
||||
.button1("Button 1")
|
||||
.button2("Button 2")
|
||||
.validResultHandler((response) -> {
|
||||
if (response.clickedButtonId() == 0){
|
||||
System.out.println("点击了Button 1");
|
||||
}
|
||||
else{
|
||||
System.out.println("点击了Button 2");
|
||||
}
|
||||
});
|
||||
spigotMaster.sendForm(player, builder);
|
||||
```
|
||||
|
||||
- 最终效果如下:
|
||||
|
||||

|
||||
|
||||
### SimpleForm
|
||||
|
||||
- 应用场景:
|
||||
|
||||
- SimpleForm比ModalForm稍显复杂,但是可定制程度也更高,支持带图片按钮
|
||||
|
||||
- 仅支持标题、内容和不限数量的按钮
|
||||
|
||||
- 命令格式为:
|
||||
```
|
||||
/showForm 2
|
||||
```
|
||||
|
||||
- 具体代码示例如下:
|
||||
```
|
||||
SimpleForm simpleForm = SimpleForm.builder()
|
||||
.title("Title")
|
||||
.content("Content")
|
||||
.button("Button without an image", FormImage.Type.PATH, "")
|
||||
.button("Button with URL image", FormImage.Type.URL, "https://github.com/GeyserMC.png?size=200")
|
||||
.button("Button with path image", FormImage.Type.PATH, "textures/map/map_background.png")
|
||||
.build();
|
||||
spigotMaster.sendForm(player, simpleForm);
|
||||
```
|
||||
|
||||
- 最终效果如下:
|
||||
|
||||

|
||||
|
||||
### CustomForm
|
||||
|
||||
- 应用场景:
|
||||
|
||||
- 最为复杂,但是可定制程度最高
|
||||
|
||||
- 支持标题、内容、标签列表、滑块、输入等等
|
||||
|
||||
- 命令格式为:
|
||||
|
||||
```
|
||||
/showForm 3
|
||||
```
|
||||
|
||||
- 具体代码示例如下:
|
||||
```
|
||||
CustomForm customForm = CustomForm.builder()
|
||||
.title("Title")
|
||||
.dropdown("Text", "Option 1", "Option 2")
|
||||
.input("Input", "placeholder")
|
||||
.toggle("Toggle")
|
||||
.slider("Text", 0, 10, 1, 5)
|
||||
.validResultHandler(
|
||||
(response) -> {
|
||||
System.out.println("option:" + response.asDropdown(0));
|
||||
System.out.println("input:" + response.asInput(1));
|
||||
System.out.println("toggle:" + response.asToggle(2));
|
||||
System.out.println("input1:" + response.asInput(3));
|
||||
System.out.println("slider:" + response.asSlider(4));
|
||||
}
|
||||
)
|
||||
.build();
|
||||
spigotMaster.sendForm(player, customForm);
|
||||
```
|
||||
|
||||
- 最终效果如下:
|
||||
|
||||

|
||||
|
||||
### Form表单响应处理函数
|
||||
|
||||
- 命令格式为:
|
||||
|
||||
```
|
||||
/showForm 4
|
||||
```
|
||||
|
||||
- 具体代码示例如下:
|
||||
```
|
||||
CustomForm customForm1 = CustomForm.builder()
|
||||
.title("geyser.auth.login.form.details.title")
|
||||
.label("geyser.auth.login.form.details.desc")
|
||||
.input("geyser.auth.login.form.details.email", "account@geysermc.org", "")
|
||||
.input("geyser.auth.login.form.details.pass", "123456", "")
|
||||
.closedOrInvalidResultHandler(
|
||||
() -> System.out.println("关闭窗口")
|
||||
)
|
||||
.validResultHandler(
|
||||
(response) -> System.out.println("有效输入:" + response.getResponses())
|
||||
)
|
||||
.build();
|
||||
```
|
||||
|
||||
## 进一步扩展
|
||||
|
||||
### 上述方案的局限性
|
||||
|
||||
从上面几种form的效果我们可以看到,在不修改客户端Json配置的情况下,FormUI的布局、贴图等表现效果十分受限,排序只能从头列到尾,控件效果也只有单调的一种
|
||||
|
||||
### 扩展方案
|
||||
|
||||
我们可以通过修改客户端JSON配置文件,来让FormUI的样式更丰富,同时不需要编写额外的客户端Python代码。
|
||||
|
||||
在阅读下述内容前,默认开发者已阅读过 [客户端ui开发相关知识](../../../18-界面与交互/2-从零开始创建UI.md)
|
||||
|
||||
同时默认开发者已阅读过 [客户端基础ui知识](https://wiki.bedrock.dev/json-ui/json-ui-intro.html)
|
||||
|
||||
### 样例效果
|
||||
|
||||
- 命令格式为:
|
||||
|
||||
```
|
||||
/showForm 5
|
||||
```
|
||||
|
||||

|
||||
|
||||
### 修改流程
|
||||
|
||||
- 复制一份引擎中的server_form.json(具体位于开发包/data/resource_packs/vanilla/ui/下),到自定义mod中
|
||||

|
||||
|
||||
- 首先我们在中间插入一份自定义控件**apollo_demo_switch_long_form**,**注意代码中注释**
|
||||
```
|
||||
"apollo_demo_switch_long_form" : {
|
||||
"type" : "panel",
|
||||
"size" : [
|
||||
"100%",
|
||||
"100%"
|
||||
],
|
||||
"layer": 1,
|
||||
"anchor_from": "top_left",
|
||||
"anchor_to": "top_left",
|
||||
// 首先定义标志位变量,用于标题的判断
|
||||
"$flag_grid" : "§g",
|
||||
"controls": [
|
||||
{
|
||||
"long_form@long_form" : {
|
||||
"enabled" : false,
|
||||
"visible" : false,
|
||||
// 通过绑定,我们可以判断标题,当标题不以**§g**开头时,显示原生long_form
|
||||
"bindings" : [
|
||||
{
|
||||
"binding_type" : "global",
|
||||
"binding_condition" : "none",
|
||||
"binding_name" : "#title_text",
|
||||
"binding_name_override" : "#title_text"
|
||||
},
|
||||
{
|
||||
"source_property_name" : "(((#title_text - $flag_grid) = #title_text))",
|
||||
"binding_type" : "view",
|
||||
"target_property_name" : "#visible"
|
||||
},
|
||||
{
|
||||
"source_property_name" : "(((#title_text - $flag_grid) = #title_text))",
|
||||
"binding_type" : "view",
|
||||
"target_property_name" : "#enabled"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"grid_long_form@grid_long_form" : {
|
||||
"enabled" : false,
|
||||
"visible" : false,
|
||||
// 通过绑定,我们可以判断标题,当标题以**§g**开头时,显示我们自定义的grid_long_form
|
||||
"bindings" : [
|
||||
{
|
||||
"binding_type" : "global",
|
||||
"binding_condition" : "none",
|
||||
"binding_name" : "#title_text",
|
||||
"binding_name_override" : "#title_text"
|
||||
},
|
||||
{
|
||||
"source_property_name" : "(not ((#title_text - $flag_grid) = #title_text))",
|
||||
"binding_type" : "view",
|
||||
"target_property_name" : "#visible"
|
||||
},
|
||||
{
|
||||
"source_property_name" : "(not ((#title_text - $flag_grid) = #title_text))",
|
||||
"binding_type" : "view",
|
||||
"target_property_name" : "#enabled"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
```
|
||||
|
||||
|
||||
- 定义五个自定义控件
|
||||
- grid_long_form@server_form.long_form
|
||||
|
||||
继承自原生server_form.long_form
|
||||
- grid_long_form_panel
|
||||
|
||||
为内容面板增加了背景贴图
|
||||
```
|
||||
{
|
||||
"bg" : {
|
||||
"type" : "image",
|
||||
"size": [
|
||||
"100%",
|
||||
"100%"
|
||||
],
|
||||
"layer" : 1,
|
||||
"fill" : true,
|
||||
"nine_slice_buttom" : 4,
|
||||
"nine_slice_left" : 3,
|
||||
"nine_slice_right" : 4,
|
||||
"nine_slice_top" : 4,
|
||||
"nineslice_size" : [ 3,3,4,4 ],
|
||||
"is_new_nine_slice" : false,
|
||||
"texture" : "textures/ui/grid_bg"
|
||||
}
|
||||
},
|
||||
```
|
||||
|
||||
- grid_long_form_scrolling_content@server_form.long_form_scrolling_content
|
||||
|
||||
继承自原生**server_form.long_form_scrolling_content**的滚动面板,内容填充为**grid_long_form_dynamic_buttons_panel@server_form.grid_long_form_dynamic_buttons_panel**
|
||||
|
||||
- grid_long_form_dynamic_buttons_panel
|
||||
|
||||
自定义的grid面板,用于生成动态按钮,从配置中可以见到,我们定义该面板由6行2列组成,每项控件模板为**server_form.grid_dynamic_button**
|
||||
```
|
||||
|
||||
"grid_dimensions" : [2, 6],
|
||||
"grid_item_template": "server_form.grid_dynamic_button"
|
||||
```
|
||||
|
||||
|
||||
- grid_dynamic_button
|
||||
自定义的按钮面板,除了原生按钮外,还增加了按钮的背景贴图
|
||||
```
|
||||
|
||||
{
|
||||
"bg" : {
|
||||
"type" : "image",
|
||||
"size": [
|
||||
32,32
|
||||
],
|
||||
"layer" : 4,
|
||||
"texture" : "#form_button_texture",
|
||||
// 通过绑定,我们把Form中的icon参数传给image的texture
|
||||
// 详细到代码即为:.button("测试按钮7", FormImage.Type.PATH, "textures/ui/btn.png")中的第三个参数
|
||||
"bindings": [
|
||||
{
|
||||
"binding_name": "#form_button_texture",
|
||||
"binding_name_override": "#texture",
|
||||
"binding_type": "collection",
|
||||
"binding_collection_name": "form_buttons"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"form_button@common_buttons.light_text_button": {
|
||||
"$pressed_button_name": "button.form_button_click",
|
||||
"anchor_from": "top_left",
|
||||
"anchor_to": "top_left",
|
||||
"size": [
|
||||
"100%",
|
||||
32
|
||||
],
|
||||
// 此处把按钮的三种贴图都设为空,因为我们不希望原生按钮的贴图显示出来
|
||||
"$default_button_texture" : "",
|
||||
"$hover_button_texture" : "",
|
||||
"$$pressed_button_texture" : "",
|
||||
"$button_text": "#form_button_text",
|
||||
"$button_text_binding_type": "collection",
|
||||
"$button_text_grid_collection_name": "form_buttons",
|
||||
"$button_text_max_size": [
|
||||
"100%",
|
||||
20
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
- 最后,我们把原生的long_form的定义改为我们自定义的控件**@server_form.apollo_demo_switch_long_form**
|
||||
|
||||
long_form对应的是SimpleForm,custom_form对应的是CustomForm
|
||||

|
||||
|
||||
- Spigot中我们构造如下的Form,把Title设置为**§g**开头
|
||||

|
||||
|
||||
- 至此,扩展FormUI结束,不同样式的FormUI我们增加不同的标题前缀
|
||||
|
||||
## 鸣谢
|
||||
|
||||
感谢 **布吉岛(妖猫)** 团队对扩展FormUI功能的支持
|
||||
@@ -0,0 +1,103 @@
|
||||
---
|
||||
front:
|
||||
hard: 入门
|
||||
time: 40分钟
|
||||
---
|
||||
|
||||
# 自定义方块Demo详解
|
||||
|
||||
## 概要
|
||||
[示例Demo](../99-下载内容.html#示例demo)中的CustomBlockDemo包含客户端Mod
|
||||
|
||||
客户端Mod自定义了四种方块:
|
||||
|
||||
- custom:custom_block_squirrel
|
||||
|
||||
使用了自定义松鼠模型的方块
|
||||
|
||||
- custom:my_block1
|
||||
|
||||
破坏时间为10s的自定义方块
|
||||
|
||||
- custom:my_block2
|
||||
|
||||
自发光方块,发光强度为1.0
|
||||
|
||||
- custom:my_block3
|
||||
|
||||
四面向方块,根据玩家摆放方向不同而不同
|
||||
|
||||
## 开发流程
|
||||
- 自定义方块1-3
|
||||
1. 参照[自定义方块实体概述](../../../20-玩法开发/15-自定义游戏内容/2-自定义方块/0-自定义方块概述.md) 、[自定义方块Json组件](../../../20-玩法开发/15-自定义游戏内容/2-自定义方块/1-JSON组件.md),增加对应方块定义、Json组件
|
||||

|
||||
|
||||
2. 使用原生Spigot命令,获取带SkullOwner的头颅
|
||||
```
|
||||
/give @s minecraft:skull 64 3 {SkullOwner: { "Name" : "geyser_custom_block_custom:my_block1"}}
|
||||
/give @s minecraft:skull 64 3 {SkullOwner: { "Name" : "geyser_custom_block_custom:my_block2"}}
|
||||
/give @s minecraft:skull 64 3 {SkullOwner: { "Name" : "geyser_custom_block_custom:my_block3"}}
|
||||
```
|
||||
|
||||
3. 最终效果如下:
|
||||

|
||||
|
||||
- 自定义松鼠方块
|
||||
1. 参照[自定义方块实体外观](../../../20-玩法开发/15-自定义游戏内容/2-自定义方块/4.1-自定义方块实体外观.md),定义方块实体、动画controller、动画、骨骼模型、贴图等
|
||||

|
||||
|
||||
2. 需要注意的是,基于头颅换皮的情况下,方块本身即带有服务端方块实体。若加上**netease:block_entity**字段,则同时会生成客户端方块实体;不加**netease:block_entity**字段则无客户端方块实体。
|
||||
```
|
||||
"netease:block_entity": {
|
||||
"tick": true, // 无效字段,可以不填
|
||||
"movable": false // 无效字段,可以不填
|
||||
},
|
||||
```
|
||||
|
||||
3. 使用Spigot原生命令
|
||||
```
|
||||
/give @s minecraft:skull 64 3 {SkullOwner: { "Name" : "geyser_custom_block_custom:custom_block_squirrel"}}
|
||||
```
|
||||
|
||||
4. 最终效果如下:
|
||||

|
||||
|
||||
## 目前支持组件详解
|
||||
|
||||
1. Q: **netease:aabb** 组件在使用上有没有限制?
|
||||
|
||||
A: 由于方块实际上为头颅换皮,因此目前**netease:aabb**设置值应大于头颅的aabb,否则会出现服务端客户端不一致的挖掘表现,头颅aabb具体为
|
||||
```json
|
||||
{
|
||||
"netease:aabb": {
|
||||
"collision": {
|
||||
"min": [0.25, 0.0, 0.25],
|
||||
"max": [0.75, 0.5, 0.75]
|
||||
},
|
||||
"clip": {
|
||||
"min": [0.25, 0.0, 0.25],
|
||||
"max": [0.75, 0.5, 0.75]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
PS:另外需要注意的是,在apollo2.0中**collision**和**clip**均为必须字段,缺少时Geyser会报错!
|
||||
|
||||
2. Q: **netease:face_directional** 组件在使用上有没有限制?
|
||||
|
||||
A: 由于头颅方块并没有细致到上下朝向的区分,因此目前组件只支持四方向类型,即只支持 **type: "direction"**
|
||||
|
||||
3. Q:**netease:block_entity**使用时有什么需要注意的地方?
|
||||
|
||||
A:对于Apollo2.0来说,服务端本身就自带了Skull方块实体,而只需要有这个组件即可保证客户端同时生成方块实体。
|
||||
|
||||
由于子字段中的 **tick** 、 **moveable** 为服务端逻辑,在Apollo2.0中为无效字段,需要自行通过Spigot插件的方式,修改Skull方块实体进行兼容。
|
||||
|
||||
**tick** 、 **moveable** 为无效字段可以理解成不配置相应字段也不会出现问题
|
||||
|
||||
4. Q:**minecraft:destory_time**使用时有什么需要注意的地方吗?
|
||||
|
||||
A:目前**destory_time** 可以设置方块所需的挖掘时间,由于服务端仍然是头颅 **destory_time**设置小于原生头颅时,会出现挖掘纹理和挖掘时间不一致问题。
|
||||
|
||||
因此,不建议**destory_time**值小于 **1.5**
|
||||
Reference in New Issue
Block a user