7月31日同步更新
0
mcguide/27-手机网络游戏/课程0:基岩版网络游戏概述/README.md
Normal file
BIN
mcguide/27-手机网络游戏/课程0:基岩版网络游戏概述/images/0_0.png
Normal file
|
After Width: | Height: | Size: 152 KiB |
BIN
mcguide/27-手机网络游戏/课程0:基岩版网络游戏概述/images/0_1.png
Normal file
|
After Width: | Height: | Size: 2.0 MiB |
BIN
mcguide/27-手机网络游戏/课程0:基岩版网络游戏概述/images/0_2.png
Normal file
|
After Width: | Height: | Size: 722 KiB |
BIN
mcguide/27-手机网络游戏/课程0:基岩版网络游戏概述/images/0_3.png
Normal file
|
After Width: | Height: | Size: 649 KiB |
BIN
mcguide/27-手机网络游戏/课程0:基岩版网络游戏概述/images/0_4.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
mcguide/27-手机网络游戏/课程0:基岩版网络游戏概述/images/0_5.png
Normal file
|
After Width: | Height: | Size: 330 KiB |
41
mcguide/27-手机网络游戏/课程0:基岩版网络游戏概述/第1节:基岩版网络游戏概述与工具指引.md
Normal file
@@ -0,0 +1,41 @@
|
||||
---
|
||||
front:
|
||||
hard: 入门
|
||||
time: 5分钟
|
||||
---
|
||||
|
||||
# 基岩版网络游戏概述与工具指引
|
||||
|
||||
## 基岩版网络游戏概述
|
||||
|
||||
<img src="./images/0_0.png" alt="图层 2" style="zoom:155%;" />
|
||||
|
||||
基岩版网络游戏是指开发者通过 **官方提供** 的服务器资源进行玩法搭建的多人游戏服务。在《我的世界》手机版主页右下方有显目的入口位置,用户可以通过入口找到处于 **上线阶段** 的在线多人服务器。 **网络游戏解除了本地联机的多种限制,让玩家与玩家间可以不受时间和地区因素干扰,遇到更多兴趣相投的玩家好友。**
|
||||
|
||||
<img src="./images/0_1.png" alt="微信图片_20220425162806" style="zoom:155%;" />
|
||||
|
||||
|
||||
|
||||
## 基岩版网络游戏工具指引
|
||||
|
||||
**《我的世界》基岩版网络游戏大厅** 当前 **仅支持** 接入 **网络游戏开服工具(Apollo)** 架设的网络游戏。框架内置于 **《我的世界》开发工作台** ,集成负载均衡、集群管理、运营指令、多号协作的功能,同时支持**完整的原版内容**与**附加包自定义功能** ,为制作我的世界服务器创意玩法带来更多可能。在根据下方流程入驻成功后,开发者将会获得一台用于上线开发的 **测试开发机(小小云)** 。拥有属于自己的第一台小小云仅仅是是开始,官方也为方便更多人了解工具框架提供了诸多支持:
|
||||
|
||||
|
||||
|
||||
### 一站式教学与活动
|
||||
|
||||
为了方便更多人了解工具框架,官方邀请明星导师为广大开发者提供[一站式图文教学](../课程1:成为Apollo服主及相关准备/第1节:Apollo介绍.html),并常态化举办《我的世界》线上短期·手游网络服插件开发实战班与《我的世界》手游网络服插件征集活动,以激发社区活力,促进生态健康。
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
### 官方与社区插件共建
|
||||
|
||||
《我的世界》开发工作台上内置了 **长期维护** 的 **官方插件库** 和 **稳定可靠** 的 **第三方插件库(由插件开发者提供)**。官方会定期线上调研时下基岩版网络游戏开发者需求榜单。**同样官方也诚挚邀请有志愿的插件开发者与我们共建手游版网络游戏的插件生态**。官方将为协助制作网络游戏开服工具插件的开发者提供**资金支持**、**免费的测试开发机**以及**专业团队的开发辅导**,让服主拥有一个更加灵活、便捷的开服条件!
|
||||
|
||||

|
||||
|
||||

|
||||
84
mcguide/27-手机网络游戏/课程10:使用Spigot开服/10-支持基岩版客户端的Java版网络游戏概述.md
Normal file
@@ -0,0 +1,84 @@
|
||||
---
|
||||
front:
|
||||
hard: 入门
|
||||
time: 60分钟
|
||||
---
|
||||
|
||||
# 支持基岩版客户端的Java版网络游戏概述
|
||||
|
||||
## 目的
|
||||
|
||||
- 本文档旨在阐述通过Apollo工具,搭建并部署Java版网络游戏的流程
|
||||
|
||||
- 玩家能够通过基岩版手机客户端进入Java版网络游戏中进行游玩
|
||||
|
||||
- 开发者可以使用基岩版已有的模组SDK和附加包Json制作客户端的玩法逻辑和表现,需要的技术与中国版基岩版组件开发相似。部分功能可能无法生效
|
||||
|
||||
## 前置知识
|
||||
|
||||
### 名词介绍
|
||||
- Spigot、Spigot服
|
||||
|
||||
> Spigot是一个开源的、高效的Java版MC服务器,并且提供了丰富的api可以编写插件。他是由第三方提供的,并非官方提供,是由官方的minecraft server反编译后,打进去一些patch后而来
|
||||
|
||||
- BC、BungeeCord、BC服
|
||||
|
||||
> BungeeCord(后面简称BC)是一个代理,提供了切服、数据包转发、插件编写、BC指令等等
|
||||
|
||||
- Geyser、Geyser服、协议服
|
||||
|
||||
> Geyser是第三方开源的MC协议转换工具,它能把MC-Bedrock版本协议转换为MC-java版本协议,也就是说通过Geyser,我们能实现基岩版客户端进入Java版服务器游玩的效果
|
||||
|
||||
- Master服、Proxy服
|
||||
|
||||
> 见[名词解释](../课程2:Apollo基础知识/第2节:Apollo框架.md)
|
||||
|
||||
## 框架示例和讲解
|
||||
|
||||

|
||||
|
||||
如框架图所示,为了尽可能地兼容不同服主Spigot服框架,我们把整体的Java版网络游戏分为两部分:
|
||||
|
||||
- 负载/协议转换部分(上半部分)
|
||||
|
||||
- Java服部分(下半部分)
|
||||
|
||||
接着阐述每个部分的具体作用
|
||||
|
||||
- 负载/协议转换部分
|
||||
- 这一部分包括了Master服、Proxy服、Geyser服
|
||||
- Master和Proxy服作用和基岩版网络游戏一致
|
||||
- 由于基岩版和Java版的游戏数据协议包格式并不相同,因此,Geyser服则扮演着协议转换者的角色,它负责把基岩版的协议转换为Java版的协议
|
||||
|
||||
- Java服部分
|
||||
- 这一部分为Java游戏服,它的作用相当于基岩版网络游戏中,Lobby服和Game服的结合体,是存储玩家实际游玩数据以及世界数据的服务器
|
||||
- 它主要由两部分组成,BC服和Spigot服
|
||||
- 架构图中的DB则指额外数据库(可选)、其他则是指用于进一步管理BC、Spigot或者其他更多功能的第三方应用程序(可选)
|
||||
|
||||
为了可扩展地连接起上下两部分,框架中,我们会把Java服视为黑盒,并为BC服提供名为**BungeeMaster**的插件,这个插件安装在BC服上,负责建立起BC服和Master服的通信连接。
|
||||
|
||||
## 常见问题
|
||||
|
||||
Q:如何申请使用Spigot开服
|
||||
|
||||
A:在开发者平台提交入驻申请,选择作品管理-上架与资源管理-网络游戏-基岩版开服工具游戏-入驻申请,使用框架勾选spigot,认真填写入驻申请。
|
||||
|
||||
Q:服务端可以使用Spigot插件么?
|
||||
|
||||
A:理论上可以使用来自Spigot插件市场的插件,目前已有一批广泛使用的插件经过了测试。
|
||||
|
||||
Q:我需要使用哪个版本的Spigot?是否支持Paper-Spigot?
|
||||
|
||||
A:推荐使用1.12.2版本的spigot,其他版本不保证可以运行且官方不做技术支持。
|
||||
|
||||
Q:是否支持手机和电脑同服?
|
||||
|
||||
A:目前仅支持手机端,如果您需要双端同服,可以选择使用Apollo进行开服。
|
||||
|
||||
Q:使用的客户端是什么?支持forge么?
|
||||
|
||||
A:使用的客户端是中国版的《我的世界》基岩版,目前是1.18;很遗憾,基岩版客户端不支持forge,推荐使用官方推出的模组SDK替代。
|
||||
|
||||
Q:我是否可以将Spigot PC服移植到PE?
|
||||
|
||||
A:可以,我们欢迎您这么做。但是对于客户端逻辑来说,您需要重写逻辑。
|
||||
119
mcguide/27-手机网络游戏/课程10:使用Spigot开服/11-部署服务器.md
Normal file
@@ -0,0 +1,119 @@
|
||||
---
|
||||
front:
|
||||
hard: 入门
|
||||
time: 60分钟
|
||||
---
|
||||
|
||||
# 部署服务器
|
||||
|
||||
## 准备阶段
|
||||
|
||||
在进行Java版网络游戏搭建和部署流程前,首先需要注册成为我的世界开发者,并完成入驻申请,申请开发阶段服务器。具体步骤,请详细阅读如下文档:
|
||||
|
||||
1. [开发准备阶段概述](../课程1:成为Apollo服主及相关准备/第2节:开发准备阶段概述.md)
|
||||
|
||||
2. [入驻申请](../课程1:成为Apollo服主及相关准备/第3节:入驻申请.md)
|
||||
|
||||
3. [申请开发阶段服务器](../课程1:成为Apollo服主及相关准备/第4节:申请开发阶段服务器.md)
|
||||
|
||||
4. [使用数据库前端连接数据库](../课程1:成为Apollo服主及相关准备/第5节(拓展):使用数据库前端连接数据库.md)
|
||||
|
||||
5. [连接开发机](../课程1:成为Apollo服主及相关准备/第5节:连接开发机.md)
|
||||
|
||||
## 特别提示
|
||||
|
||||
1. 拿到机器后,要先访问添加白名单,之后才可以正常登入
|
||||
2. spigot机器的java环境对应的操作命令,分别是**java8**和**java18**,其中运行Spigot请使用java8(具体原因详见[常见问题合集](./90-常见问题合集.md))
|
||||
|
||||
## JAVA服部署流程
|
||||
- 在准备好开发机后,连接上开发机
|
||||
|
||||
- 通过服主各自的Java服框架,部署各自的Java服(必须存在BC服)
|
||||
|
||||
- 经过初步的调研,确定目前使用的Spigot版本为1.12.2,因此为了兼容基岩版1.18客户端,需要在**Spigot服**使用**ViaVersion**插件
|
||||
|
||||
- 下载[**ViaVersion插件**](https://www.spigotmc.org/resources/viaversion.19254/),并放置于 **Spigot服目录/plugins/** 目录下。
|
||||
**注:由于Viaversion转换1.20.5版本头颅(模型)会过滤掉非法昵称的信息,导致自定义方块和中文昵称的头颅(模型)出错,因此无法直接使用官网版本。可选择MCS内容库中的“ViaVersion-4.10.2”资源或自行修改。**
|
||||
|
||||
- 下载[**SpigotMaster插件**](./99-下载内容.md#SpigotMaster插件),并放置于 **Spigot服目录/plugins/** 目录下。
|
||||
|
||||
- 下载[**BungeeMaster插件**](./99-下载内容.md#bungeemaster插件),并放置于 **BC服目录/plugins/** 目录下。
|
||||
|
||||
- 在 **BC服目录/plugins/** 下,新建文件夹 **BungeeMaster**,并在 **BungeeMaster** 文件夹下新建文件 **config.yml**(此为BungeeMaster插件配置)
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
- 然后填入如下内容,其中
|
||||
- server_id 取值区间在[12000, 14000)内,并且保持单次部署唯一即可
|
||||
|
||||
- master_port端口范围要求[29000,31000)
|
||||
- **master_port**是指BC服监听Master服连接所用端口,请注意与下述用于原生游戏的**query_port**区分,以免造成端口冲突
|
||||
|
||||
- 填写完毕后,请记下相关参数,后面需要在studio中填写
|
||||
|
||||

|
||||
|
||||
- 打开**BC服目录/config.yml**文件(此为bc服配置),配置bc服监听的端口,端口范围要求[29000,31000)
|
||||
|
||||
- 记下端口参数,后面需要在studio中填写
|
||||
- 注意,此处的**query_port**和**host中冒号后面的端口值**,请保持一致
|
||||
- 该处的端口,均为**BC服**用于监听来自**Geyser**服相关连接的端口
|
||||
|
||||

|
||||
|
||||
## 负载/协议转换部分部署流程
|
||||
|
||||
- 打开MCStudio,选择基岩版服务器,并选择右上角新建选项,打开后,选择空白Spigot服
|
||||
|
||||

|
||||
|
||||
- 选择更多后,点击服务器配置,开始配置
|
||||
|
||||

|
||||
|
||||
- 其中,控制服的配置不再赘述,和基岩版网络服务器相似
|
||||
|
||||
- 代理服和协议服实际上是同一个进程,所以配置合在一起说明:
|
||||
|
||||
- 每个proxy内部会启动一个geyser,因此只需要调整proxy的数量,不需要配置geyser的数量。
|
||||
- proxy单进程内存:暂时未被使用
|
||||
- geyser单服最大人数:建议为200~300人,正式上线后根据proxy与geyser进程的负载情况再进行调整。0表示无上限,但正式服强烈建议设置最大人数,避免整服卡死。
|
||||
- geyser单进程内存:指geyser的java部分的最大可用堆内存。0表示使用默认值,开发与审核阶段默认为1G,正式阶段默认为6G。一般来说6G内存至少可以承载500人。
|
||||
|
||||

|
||||
|
||||
- BC服的配置则根据Java服部署中,BC服的数量和配置来决定。如,根据上个步骤截图数据,则最终配置为:
|
||||
|
||||

|
||||
- 如图所示,BC服端口即为**query_port**
|
||||
- 控制服端口即为**master_port**
|
||||
|
||||
- 配置完成后,点击部署
|
||||
|
||||
- 查看日志,若部署成功,则可以通过工具箱打开ModPC开发包,进入游戏开始测试
|
||||
|
||||

|
||||
|
||||
## 部署多个代理服与BC
|
||||
|
||||
- 在代理服的配置界面填写数量即可部署多个代理服(协议服与代理服为一对一的关系,所以这个数量也是协议服的数量),点击增加代理服则可以在不同机器上部署代理服。
|
||||
|
||||
- 如果需要部署多个BC:
|
||||
|
||||
1. 在机器上部署好多个BC(可以分布在不同机器上),按上面的指引分别配置好server_id,master_port,query_port。注意这三个字段,不同bc之间不能有重复。
|
||||
2. 在studio的BC服配置界面,点击增加BC服,然后填写每个BC的ip,server_id,master_port,query_port。
|
||||
3. 重新部署,即可与多个BC组网,master会平均分配玩家到各个BC。
|
||||
|
||||
## Proxy与Geyser服的性能参数
|
||||
|
||||
一个proxy可以支撑300个玩家的跑图及连续登录,并且维持流畅运行,此时geyser建议分配4G的内存上限,整个进程最多会占用5G内存,以及5个核心的cpu。
|
||||
|
||||
|
||||
## 常见部署问题合集
|
||||
|
||||
[常见部署问题合集](./90-常见问题合集.md)
|
||||
|
||||
84
mcguide/27-手机网络游戏/课程10:使用Spigot开服/12-Spigot服务器开发规范.md
Normal file
@@ -0,0 +1,84 @@
|
||||
---
|
||||
front:
|
||||
hard: 入门
|
||||
time: 60分钟
|
||||
---
|
||||
|
||||
# Spigot服务器开发规范
|
||||
|
||||
## Spigot服部署规范
|
||||
|
||||
- 支持spigot官方BuildTools编译出的1.12.2版本,其他版本不保证可以运行且官方不做技术支持。
|
||||
|
||||
[官方BuildTools wiki](https://www.spigotmc.org/wiki/buildtools/)
|
||||
|
||||
- **<span style="font-size: 25px; color: red;">BungeeCord服需要部署在以下路径</span>**:`~/bc/*/BungeeCord.jar`
|
||||
|
||||
例如:`~/bc/bc1/BungeeCord.jar`
|
||||
|
||||
- **<span style="font-size: 25px; color: red;">Spigot服需要部署在以下路径</span>**:`~/spigot/*/spigot-1.12.2.jar`
|
||||
|
||||
例如:`~/spigot/lobby1/spigot-1.12.2.jar`
|
||||
|
||||
## Spigot服 spigot.yml参数规范
|
||||
|
||||
- bungeecord
|
||||
|
||||
```
|
||||
必须值:true
|
||||
```
|
||||
|
||||
为了接收从bc发过来的玩家uuid。注意2023.9.19前上线的网络服非必须。更多信息请参考[玩家uuid数据存储说明](./13-1-【必读】玩家uuid数据存储说明.md)
|
||||
|
||||
## Spigot服 server.properties参数规范
|
||||
|
||||
- view-distance
|
||||
|
||||
```
|
||||
建议值: 4-6
|
||||
```
|
||||
决定服务端发送的区块信息数据,对服务器性能与流量消耗影响巨大,建议调小。
|
||||
|
||||
- online-mode
|
||||
```
|
||||
必须值: false
|
||||
```
|
||||
|
||||
决定Spigot服是否进行正版验证。在Apollo中登录认证由proxy处理,spigot不需要再次验证。
|
||||
|
||||
- allow-flight
|
||||
```
|
||||
必须值: true
|
||||
```
|
||||
决定Spigot是否进行飞行检测。由于目前基岩版登录Java服务器需要经过Geyser服翻译协议,在这过程中,玩家移动相关包体不能完美翻译,若这个字段设为false,存在玩家移动被服务器当作飞行封禁的可能性。
|
||||
|
||||
- network-compression-threshold
|
||||
|
||||
```
|
||||
必须值:-1
|
||||
```
|
||||
|
||||
决定spigot与bc通信协议的压缩等级。因为spigot与bc在同一个内网,流量不需要成本,因此不压缩网络协议节省性能
|
||||
|
||||
|
||||
## BungeeCord服 config.yml参数规范
|
||||
- connection_throttle_limit
|
||||
```
|
||||
建议值: 100
|
||||
```
|
||||
BC服允许来自同一个ip同时登录的玩家数。因为apollo中玩家登录都来自协议服,所以需要调大这个值。
|
||||
|
||||
- online-mode
|
||||
```
|
||||
必须值: false
|
||||
```
|
||||
|
||||
决定BC服是否进行正版验证,在Apollo中登录认证由proxy处理,bc不需要再次验证。
|
||||
|
||||
- network_compression_threshold
|
||||
|
||||
```
|
||||
必须值:-1
|
||||
```
|
||||
|
||||
决定bc与协议服通信协议的压缩等级。因为bc与协议服在同一个内网,流量不需要成本,因此不压缩网络协议节省性能
|
||||
64
mcguide/27-手机网络游戏/课程10:使用Spigot开服/13-1-【必读】玩家uuid数据存储说明.md
Normal file
@@ -0,0 +1,64 @@
|
||||
# 【必读】玩家uuid数据存储说明
|
||||
|
||||
## 前情概要
|
||||
|
||||
在2023.9.19日前,玩家uuid由java侧根据玩家昵称生成。
|
||||
|
||||
而地图存档、很多插件的数据库都使用这个uuid,这样会导致玩家在启动器改名后,进入服务器会丢失所有数据。
|
||||
|
||||
## 调整
|
||||
|
||||
为解决上述问题,在2023.9.19日更新后,支持geyser获取玩家网易账号的uuid,并传递给bc。
|
||||
|
||||
同时添加了开关以兼容已上线服务器。开关为开时,使用网易账号uuid;开关为关时,依然由java侧根据昵称生成uuid。
|
||||
|
||||
1. 对于每个网络服,先判定绑定的[游戏ID](../课程9:服务器上线/第1节:平台发布.md#获取游戏id)。若游戏ID于2023.9.19日前成功上线,开关为关;若该日期前还未上线,则开关为开
|
||||
|
||||
2. 没有绑定游戏ID的网络服,如果是在2023.9.19日前创建的,开关为关;在该日期后创建的,开关为开
|
||||
3. “复制网络服”的开关不会继承
|
||||
|
||||
## 注意事项
|
||||
|
||||
<font color="red">**uuid的更改会导致玩家数据丢失或错乱,请仔细阅读以下注意事项**</font>
|
||||
|
||||
<font color="red">**注意您可能同时拥有多个服务器,符合多个情况,请一并阅读并注意区分**</font>
|
||||
|
||||
### 2023.9.19前已上线的网络服
|
||||
|
||||
您所有已创建的网络服都会保持使用java侧生成玩家uuid,可以放心更新引擎或者BungeeMaster插件。
|
||||
|
||||
留意`spigot.yml`中的`bungeecord`字段,建议不要修改该字段,除非您确定这不会对玩家uuid产生影响。
|
||||
|
||||
如果您希望转为新版的uuid获取方式,请评估存档、数据库的数据迁移工作并联系官方支持。
|
||||
|
||||
要注意后续新建或者复制出来的网络服,都会使用网易uuid,除非绑定了上线的游戏ID。
|
||||
|
||||
### 2023.9.19前入驻但未上线的网络服
|
||||
|
||||
您的网络服上线后将会使用网易账号uuid。
|
||||
|
||||
但是,所有已经创建的网络服都还是使用java侧生成玩家uuid。
|
||||
|
||||
为了保持开发阶段与上线阶段的环境一致性,**务必**做以下操作:
|
||||
|
||||
1. 给所有已创建的网络服绑定游戏ID,或者使用studio复制一个,然后弃用旧的网络服。但留意这会导致测试账号的数据清空,并且存档及数据库中会有旧的玩家数据残留。
|
||||
2. 使用2.8.0.java.release20230907或以上版本的引擎。
|
||||
3. 将所有BungeeMaster插件升级到1.0.3以上。
|
||||
4. 将所有`spigot.yml`的`bungeecord`字段配置为`true`。
|
||||
|
||||
后续新建或复制的网络服,也**必须**满足上述2到4。
|
||||
|
||||
### 2023.9.19后入驻的网络服
|
||||
|
||||
您的网络服开发,审核,上线所有阶段都会使用网易账号uuid。
|
||||
|
||||
**必须**使用2.8.0.java.release20230907或以后版本的引擎。
|
||||
|
||||
**必须**使用1.0.3或以上版本的BungeeMaster插件。
|
||||
|
||||
**必须**将`spigot.yml`的`bungeecord`字段配置为`true`。
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
50
mcguide/27-手机网络游戏/课程10:使用Spigot开服/13-其他部署时可配置字段.md
Normal file
@@ -0,0 +1,50 @@
|
||||
# 其他部署时可配置字段
|
||||
|
||||
## deploy.json
|
||||
|
||||
对studio的服务器点更多->配置目录->打开配置文件目录,在目录下找到deploy.json。我们可以在该json的“common”字段中 **自行** 添加一些额外的配置字段。
|
||||
|
||||

|
||||
|
||||
### command_suggestions
|
||||
|
||||
bool类型。
|
||||
|
||||
设置是否在客户端开启命令补全,若开启,那么在命令很多时,客户端在聊天栏输入斜杠后会卡顿。默认为true。
|
||||
|
||||
### geyser_survival_place_dist
|
||||
|
||||
int类型。
|
||||
|
||||
由于基岩版与java版的可放置距离不同,geyser会在玩家放置方块时对距离进行校验,距离大于该值会放置失败。该值为生存模式玩家的校验距离,默认为6。
|
||||
|
||||
PE基岩版生存模式的放置距离为7,所以将该字段配置为7时,可以满足基岩版的最远放置距离。但是配置时需要同步修改spigot,对于1.12.2版本,需要修改`PlayerConnection.java`下的`SURVIVAL_PLACE_DISTANCE_SQUARED`
|
||||
|
||||
### geyser_creative_place_dist
|
||||
|
||||
int类型。
|
||||
|
||||
与geyser_survival_place_dist类似,但该配置对应创造模式下的放置距离,默认为7。
|
||||
|
||||
PE基岩版创造模式的放置距离为12,配置时需要同步修改spigot,对于1.12.2版本,需要修改`PlayerConnection.java`下的`CREATIVE_PLACE_DISTANCE_SQUARED`
|
||||
|
||||
### geyser_max_place_dist
|
||||
|
||||
float类型。
|
||||
|
||||
生存模式与创造模式的放置距离都会受此限制,默认为8.0。
|
||||
|
||||
如果只需兼容生存模式,则不需要改动。如果还需兼容创造模式,则需要设置为12.0或以上。
|
||||
|
||||
配置时需要同步修改spigot,对于1.12.2版本,需要修改`PlayerConnection.java`下方法`a(PacketPlayInUseItem packetplayinuseitem)`中的`64.0D`,将其修改为配置的距离的平方。
|
||||
|
||||
### geyser_max_destroy_dist
|
||||
|
||||
float类型。
|
||||
|
||||
由于基岩版与java版的可破坏距离不同,geyser会在玩家破坏方块时对距离进行校验,距离大于该值会破坏失败。生存模式及创造模式的玩家都受此距离限制,默认为6.0。
|
||||
|
||||
PE基岩版生存模式的放置距离为7(创造模式下为12),所以将该字段配置为7.0时,可以满足生存模式的最远放置距离。但是配置时需要同步修改spigot,对于1.12.2版本,需要修改`PlayerConnection.java`下方法`a(PacketPlayInBlockDig packetplayinblockdig)`,`case START_DESTROY_BLOCK`分支下的`36.0D`,将其修改为配置的距离的平方。
|
||||
|
||||
**如果只是想兼容pe基岩版生存模式的交互距离,则把geyser_survival_place_dist设为7,把geyser_max_destroy_dist设为7.0即可。**
|
||||
|
||||
42
mcguide/27-手机网络游戏/课程10:使用Spigot开服/20-Spigot插件结构与部署.md
Normal file
@@ -0,0 +1,42 @@
|
||||
---
|
||||
front:
|
||||
hard: 入门
|
||||
time: 60分钟
|
||||
---
|
||||
|
||||
# Spigot插件结构与部署
|
||||
|
||||
### Spigot插件目录结构样例
|
||||
|
||||

|
||||
|
||||
listen_plugin
|
||||
main
|
||||
java\com\netease
|
||||
command
|
||||
ServerCommand.java
|
||||
ToClientCommand.java
|
||||
App.java
|
||||
ClientListener.java
|
||||
GlobalVar.java
|
||||
ServerOriginalListen.java
|
||||
resouces
|
||||
plugin.yml
|
||||
|
||||
| 文件/文件夹 | 解释 |
|
||||
| ------------------------- | ------------------------------------------------------------ |
|
||||
| java\com\netease | 插件逻辑代码目录 |
|
||||
| command | 指令处理类目录 |
|
||||
| App.java | 插件入口 |
|
||||
| ClientListener.java | 客户端事件监听处理函数 |
|
||||
| GlobalVar | 通用数据储存类 |
|
||||
| ServerOriginalListen | Spigot原生事件监听处理函数 |
|
||||
| resouces | 插件资源版本信息 |
|
||||
| plugin.yml | mod资源版本信息 |
|
||||
|
||||
## Spigot插件加载流程
|
||||
- 如图所示,把mvn clean install编译出的xxx.jar放入Spigot的Plugin文件夹
|
||||

|
||||
|
||||
- Spigot加载插件后,会有输出,具体命名由plugin.yml决定
|
||||

|
||||
138
mcguide/27-手机网络游戏/课程10:使用Spigot开服/21-Spigot服与客户端python通信原理简介.md
Normal file
@@ -0,0 +1,138 @@
|
||||
# Spigot服与客户端python通信
|
||||
|
||||
## 使用方法
|
||||
|
||||
1. spigot服需要安装SpigotMaster插件,插件api文档见[SpigotMasterAPI文档](./81-SpigotMasterAPI文档.html)
|
||||
|
||||
2. 客户端到spigot
|
||||
|
||||
- 在spigot使用spigotMaster.listenForEvent监听事件。
|
||||
|
||||
- 在客户端使用<a href="../../../mcdocs/1-ModAPI/接口/通用/事件.html#notifytoserver" rel="noopenner">NotifyToServer</a>发送事件
|
||||
|
||||
3. spigot到客户端
|
||||
|
||||
- 在客户端使用<a href="../../../mcdocs/1-ModAPI/接口/通用/事件.html#listenforevent" rel="noopenner">ListenForEvent</a>监听事件
|
||||
|
||||
- 在spigot使用spigotMaster.notifyToClient或其他多播接口发送事件
|
||||
|
||||
示例:
|
||||
|
||||
- spigot侧
|
||||
|
||||
```java
|
||||
public void onEnable() {
|
||||
SpigotMaster spigotMaster = (SpigotMaster) Bukkit.getPluginManager().getPlugin("SpigotMaster");
|
||||
if (spigotMaster != null){
|
||||
// 监听事件,然后原封不动发回去
|
||||
spigotMaster.listenForEvent("MyMod", "MySystemClient", "clientEvent", new PyRpcHandler() {
|
||||
@Override
|
||||
public void onEvent(Player player, Map<String, Object> map) {
|
||||
spigotMaster.notifyToClient(player, "MyMod", "MySystemServer", "serverEvent", map);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- python侧
|
||||
|
||||
```python
|
||||
# modMain.py
|
||||
@Mod.InitClient()
|
||||
def InitClient(self):
|
||||
clientApi.RegisterSystem("MyMod", "MySystemClient", client_system_class_path)
|
||||
|
||||
# clientSystem
|
||||
class MySystemClient(ClientSystem):
|
||||
def __init__(self, namespace, systemName):
|
||||
ClientSystem.__init__(self, namespace, systemName)
|
||||
# 注册事件,在回调函数中打印参数
|
||||
self.ListenForEvent("MyMod", "MySystemServer", "serverEvent", self, self.onEvent)
|
||||
# 给spigot发一个事件
|
||||
self.NotifyToServer("clientEvent", {'a': 1})
|
||||
|
||||
def onEvent(self, data):
|
||||
# 可以在客户端日志中看到onEvent {"a": 1}
|
||||
print 'onEvent', data
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 事件支持的参数类型及映射关系
|
||||
|
||||
| Java类型 | Python类型 |
|
||||
| ------------------------ | ---------- |
|
||||
| null | None |
|
||||
| boolean | bool |
|
||||
| int | int |
|
||||
| long | long |
|
||||
| BigInteger(2^63到2^64-1) | long |
|
||||
| float | float |
|
||||
| double | float |
|
||||
| String | str |
|
||||
| List\<Object\> | list |
|
||||
| Map<String, Object> | dict |
|
||||
|
||||
### Python发送给Java
|
||||
|
||||
| Python类型 | Java类型 |
|
||||
| ---------------------------------------- | ---------- |
|
||||
| None | null |
|
||||
| bool | Boolean |
|
||||
| int/long(-2^31到2^31-1) | Integer |
|
||||
| int/long(-2^63到-2^31-1,2^31到2^63-1) | Long |
|
||||
| int/long(2^63到2^64-1) | BigInteger |
|
||||
| float | Double |
|
||||
| str | String |
|
||||
| list | List\<Object\> |
|
||||
| dict(key必须为str) | Map<String, Object> |
|
||||
|
||||
|
||||
|
||||
## 关于entityId的注意事项
|
||||
|
||||
- 客户端侧的**非玩家实体**的entityId与spigot侧org.bukkit.entity.Entity.getEntityId()获取的实体id相同
|
||||
|
||||
- 请注意spigot获取的实体id类型为**int**,而客户端modsdk接口需要的实体id类型为**str**
|
||||
|
||||
- 但客户端侧会存在一些负数的实体id,会geyser做协议转换时生成的虚拟实体,在spigot侧没有对应的实体
|
||||
|
||||
- 在**每个客户端**视角来看,**本地玩家的entityId永远为-2**,其他玩家的entityId与spigot侧getEntityId相同,也就是说:
|
||||
|
||||
- 客户端使用GetLocalPlayerId永远返回-2。如果将他发给spigot,那spigot是不能直接根据这个id获取到玩家的,需要做一些特殊处理
|
||||
|
||||
```java
|
||||
spigotMaster.listenForEvent("MyMod", "MySystemClient", "clientEvent", new PyRpcHandler() {
|
||||
@Override
|
||||
public void onEvent(Player player, Map<String, Object> map) {
|
||||
Player eventPlayer;
|
||||
String entityId = (String) map.get("entityId");
|
||||
if (entityId.equals("-2")) {
|
||||
eventPlayer = player;
|
||||
}
|
||||
else {
|
||||
// 将entityId转成int然后去获取对应player
|
||||
}
|
||||
// 处理eventPlayer的逻辑
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
- 如果在spigot侧使用getEntityId的返回值发给该玩家,那玩家客户端无法根据这个id获取到本地玩家,需要做一些特殊处理
|
||||
|
||||
```java
|
||||
int entityId;
|
||||
if (sendPlayer == player) {
|
||||
entityId = -2;
|
||||
}
|
||||
else {
|
||||
entityId = player.getEntityId()
|
||||
}
|
||||
map.put("entityId", entityId)
|
||||
spigotMaster.notifyToClient(sendPlayer, "MyMod", "MySystemServer", "serverEvent", map);
|
||||
```
|
||||
|
||||
## Demo详解
|
||||
|
||||
详见文档[Python通信Demo详解](./30-Spigot服Demo详解/2-Python通信Demo详解.md)
|
||||
53
mcguide/27-手机网络游戏/课程10:使用Spigot开服/21-客户端Mod结构与部署.md
Normal file
@@ -0,0 +1,53 @@
|
||||
---
|
||||
front:
|
||||
hard: 入门
|
||||
time: 60分钟
|
||||
---
|
||||
|
||||
# 客户端Mod结构与部署
|
||||
|
||||
## 客户端Mod的目录结构样例
|
||||
|
||||
我们以demoMod为示例介绍游戏服Mod目录结构:
|
||||
|
||||
demoMod
|
||||
behavior_packs
|
||||
behavior_pack_geyser_demo_mod
|
||||
geyserDemoMod
|
||||
geyserDemoModScript
|
||||
modClient
|
||||
modCommon
|
||||
modMain.py
|
||||
__init__.py
|
||||
entities
|
||||
manifest.json
|
||||
resource_packs
|
||||
resource_pack_geyser_demo_mod
|
||||
manifest.json
|
||||
|
||||
| 文件/文件夹 | 解释 |
|
||||
| ------------------------- | ------------------------------------------------------------ |
|
||||
| demoMod | 游戏服Mod根目录 |
|
||||
| behavior_packs | 存放客户端行为包,可以包含多个行为包 |
|
||||
| behavior_pack_geyser_demo_mod | 行为包 |
|
||||
| geyserDemoMod | python脚本的根目录,该目录的脚本会被加到python运行环境下,可以从该路径开始import脚本文件,例如from geyserDemoMod import modMain |
|
||||
| modClient | pytho客户端行为逻辑,包含GeyserDemoModClientSystem|
|
||||
| modCommon | 通用数据,包含Modname、自定义事件等定义 |
|
||||
| resource_packs | 存放客户端资源,可以包含多个资源包 |
|
||||
| resource_pack_geyser_demo_mod | 资源包 |
|
||||
| pack_manifest.json | mod资源版本信息 |
|
||||
|
||||
更多模组SDK内容详见:
|
||||
<a href="../../20-玩法开发/13-模组SDK编程/2-Python脚本开发/0-脚本开发入门.html#modmain-py是什么" target="_blank">mod开发简介</a>
|
||||
|
||||
## 客户端Mod上传加载流程
|
||||
- 如图所示
|
||||
- mod目录结构
|
||||

|
||||
- studio配置
|
||||

|
||||
|
||||
|
||||
- Geyser读取客户端Mod后,会有如下输出
|
||||

|
||||
|
||||
56
mcguide/27-手机网络游戏/课程10:使用Spigot开服/22-Spigot服自定义物品原理简介.md
Normal file
@@ -0,0 +1,56 @@
|
||||
---
|
||||
front:
|
||||
hard: 入门
|
||||
time: 60分钟
|
||||
---
|
||||
|
||||
# Spigot自定义物品
|
||||
|
||||
## 简要原理
|
||||
|
||||
- 基于目前Spigot服相关接口、自定义物品流程,Spigot服的自定义物品实际上是原生Java物品的换皮物品,客户端Mod利用字段**java_identifier**来标识
|
||||
|
||||
- Geyser服加载客户端Mod后,根据**java_identifier**字段记录自定义物品与原生Java物品的映射,并在使用物品时,调用原生物品相关的逻辑
|
||||
|
||||
- 对于Spigot服来说,自定义物品实际上还是原生Java物品,不过相比于Java原生物品,由自定义物品转换而来的物品会拥有额外数据用以标识
|
||||
|
||||
## 简要开发流程
|
||||
|
||||
- 对于客户端Mod来说,参照文档[自定义物品](../../20-玩法开发/15-自定义游戏内容/1-自定义物品/1-自定义基础物品.md),自定义客户端物品,与基岩版区别在于,额外加上**java_identifier**字段,用于表示何种Java物品换皮
|
||||
- 对于Spigot插件来说,根据Spigot相关接口,构造ItemStack物品,并实现相关逻辑
|
||||
- 详细步骤可参考Demo
|
||||
|
||||
## Demo详解
|
||||
|
||||
详见文档[自定义物品Demo详解](./30-Spigot服Demo详解/1-自定义物品Demo详解.md)
|
||||
|
||||
## Q&A
|
||||
|
||||
- 目前Mod由Geyser进行分发、预加载,因此通过相同Geyser连接的玩家加载的Mod都相同,加载到的自定义物品也相同
|
||||
|
||||
- 目前自定义物品实际为Java原生物品换皮,因此,当**java_identifier**字段与Spigot服实际创建的物品不一致时,物品表现会存在问题
|
||||
|
||||
- "java_identifier"字段取值为Java版物品的命名空间ID, 如自定义物品为木剑换皮,则填 "java_identifier" : "wooden_sword"
|
||||
|
||||
更多物品命名空间ID详见[官方WiKi](https://zh.minecraft.wiki/w/%E7%89%A9%E5%93%81)
|
||||
|
||||
|
||||
- 由于配置中的**Components**有一部分为双端逻辑,因此此类**Components**的逻辑需要由Spigot插件实现,直接在Json中配置无法生效或效果异常。目前已知不可用**Component**如下:
|
||||
|
||||
- 基岩版自定义物品中用于物品防火的组件
|
||||
```
|
||||
设置物品是否防火
|
||||
"netease:fire_resistant"{ "value" : true}
|
||||
```
|
||||
|
||||
- 基岩版自定义物品中用于物品是否可做燃料的组件
|
||||
```
|
||||
设置物品是否可作为燃料
|
||||
"netease:fuel" { "value" : true}
|
||||
```
|
||||
|
||||
- 基岩版自定义物品中用于物品的使用间隔
|
||||
```
|
||||
设置物品使用间隔
|
||||
"netease:cooldown" : { "duration" : 5}
|
||||
```
|
||||
72
mcguide/27-手机网络游戏/课程10:使用Spigot开服/23-Spigot服自定义实体原理简介.md
Normal file
@@ -0,0 +1,72 @@
|
||||
---
|
||||
front:
|
||||
hard: 入门
|
||||
time: 60分钟
|
||||
---
|
||||
|
||||
# Spigot自定义实体
|
||||
|
||||
## 简要原理
|
||||
|
||||
目前Spigot服自定义实体基于实体metadata实现,换皮/继承创建实体的同时写入自定义实体identifier
|
||||
- 客户端identifier和服务端identifier无须一一对应,只需要在创建生物时确定即可
|
||||
|
||||
## 简要开发流程
|
||||
|
||||
- 开发流程如下:
|
||||
1. 编写客户端Mod,制作生物的模型、贴图、动画、客户端逻辑等
|
||||
2. 编写Spigot插件,制作生物运行逻辑、属性、生成规则等
|
||||
|
||||
- 对于客户端Mod来说,请参照文档 [自定义生物](../../20-玩法开发/15-自定义游戏内容/3-自定义生物/01-自定义基础生物.md) 制作生物的模型、贴图、动画等
|
||||
- 与纯基岩版的自定义生物区别在于,绝大部分Components、ComponentGroups、events均需要通过Java插件实现,无法通过Json配置生效
|
||||
- 客户端Mod的主要作用为告知客户端当生成对应Identifier生物时,渲染的模型、贴图、动画、碰撞体等
|
||||
|
||||
- 对于Spigot插件来说,目前支持了三种自定义生物方式
|
||||
- 简单换皮指定生物,并通过Spigot接口,修改生物属性,如血量等
|
||||
- 兼容MythicMob插件,继承MythicMob生物类
|
||||
- (Demo展示了4.13.0版本MythicMob的兼容方式)
|
||||
- 其他版本的MythicMob请根据各个版本的接口进行兼容
|
||||
- 反射注册类生物,并创建新生物类实例,覆写继承类方法,如攻击等,最后加入到世界中
|
||||
- 详细步骤可参考Demo
|
||||
|
||||
## Demo详解
|
||||
|
||||
详见文档[自定义实体Demo详解](./30-Spigot服Demo详解/5-自定义实体Demo详解.md)
|
||||
|
||||
## Q&A
|
||||
|
||||
- 由于配置中的**components**、**component_groups**、**events**均为服务端逻辑,因此均需Spigot插件实现,直接在Json中配置无法生效或效果异常。目前已知可用的**components**如下:
|
||||
|
||||
- 自定义生物components
|
||||
```
|
||||
// 设置生物碰撞盒大小
|
||||
"minecraft:collision_box": {
|
||||
"width": 3,
|
||||
"height": 3
|
||||
}
|
||||
|
||||
// 设置生物可骑乘、骑乘位置,偏移、蹲伏可交互等
|
||||
"minecraft:rideable": {
|
||||
"priority": 0,
|
||||
"seat_count": 2,
|
||||
"crouching_skip_interact": true,
|
||||
"family_types": [
|
||||
"player"
|
||||
],
|
||||
|
||||
"seats": [
|
||||
{
|
||||
"position": [ 0.0, 1, 0.0 ]
|
||||
},
|
||||
{
|
||||
"position": [ 0.0, 1, -1.0 ]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
// 设置骑乘生物是否可以蓄力跳跃,有则可跳跃,没有则不可跳跃
|
||||
"minecraft:can_power_jump" : {}
|
||||
```
|
||||
|
||||
- 1.12版本的Spigot由于需要使用Viaversion,因此在通过反射注册新生物时,需要注意生物的type必须复用已有的生物,或者同时为Viaversion注册,不然会出现生物无法生成的问题。
|
||||
|
||||
54
mcguide/27-手机网络游戏/课程10:使用Spigot开服/24-Spigot服自定义方块原理简介.md
Normal file
@@ -0,0 +1,54 @@
|
||||
---
|
||||
front:
|
||||
hard: 入门
|
||||
time: 60分钟
|
||||
---
|
||||
|
||||
# Spigot自定义方块原理简介
|
||||
|
||||
## 简要原理
|
||||
|
||||
目前Spigot服自定义方块实际上为头颅换皮
|
||||
|
||||
当头颅的 **SkullOwner** 由形如 **geyser_custom_block_xxx(自定义方块identifier)** 组成时,Geyser会把对应头颅方块转换成客户端自定义方块
|
||||
|
||||
如:
|
||||
- SkullOwner : "geyser_custom_block_custom:my_block1" 的头颅
|
||||
最终会转成客户端自定义方块 **custom:my_block1**
|
||||
- SkullOwner : "geyser_custom_block_custom:my_block2" 的头颅
|
||||
最终会转成客户端自定义方块 **custom:my_block2**
|
||||
- SkullOwner : "geyser_custom_block_custom:my_block3" 的头颅
|
||||
最终会转成客户端自定义方块 **custom:my_block3**
|
||||
- SkullOwner : "geyser_custom_block_custom:custom_block_squirrel" 的头颅
|
||||
最终会转成客户端自定义方块 **custom:custom_block_squirrel**
|
||||
## 简要开发流程
|
||||
|
||||
- 开发流程如下:
|
||||
1. 编写客户端Mod,配置方块Json、客户端逻辑等
|
||||
2. 编写Spigot插件,制作基于头颅实体的自定义逻辑等
|
||||
|
||||
- 对于客户端Mod来说,请参照文档 [自定义生物](../../20-玩法开发/15-自定义游戏内容/2-自定义方块/0-自定义方块概述.md) 制作自定义方块
|
||||
|
||||
- 对于Spigot插件来说,仅需给对应Skull方块设置上给定要求的SkullOwner即可,如:
|
||||
- 简单发放物品的情况:
|
||||
```
|
||||
/give @s minecraft:skull 64 3 {SkullOwner: { "Name" : "geyser_custom_block_custom:my_block3"}}
|
||||
```
|
||||
- 设置方块的情况:
|
||||
```
|
||||
Location loc = new Location(world, x, y, z);
|
||||
Block block = loc.getBlock();
|
||||
block.setType(Material.SKULL);
|
||||
Skull skull = (Skull) block.getState();
|
||||
String owner = "geyser_custom_block_custom:my_block3";
|
||||
skull.setOwningPlayer(Bukkit.getOfflinePlayer(owner));
|
||||
skull.update();
|
||||
```
|
||||
|
||||
## Demo详解
|
||||
|
||||
详见文档[自定义实体Demo详解](./30-Spigot服Demo详解/7-自定义方块Demo详解.md)
|
||||
|
||||
## 鸣谢
|
||||
|
||||
感谢 **布吉岛(妖猫)** 团队对自定义方块功能的支持
|
||||
@@ -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
@@ -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**
|
||||
504
mcguide/27-手机网络游戏/课程10:使用Spigot开服/81-SpigotMasterAPI文档.md
Normal file
@@ -0,0 +1,504 @@
|
||||
# SpigotMaster文档
|
||||
|
||||
## `public class SpigotMaster extends JavaPlugin`
|
||||
|
||||
SpigotMaster
|
||||
|
||||
* **Package:**
|
||||
com.neteasemc.spigotmaster;
|
||||
* **Example:**
|
||||
```
|
||||
import com.neteasemc.spigotmaster.SpigotMaster;
|
||||
SpigotMaster spigotMaster = (SpigotMaster)Bukkit.getPluginManager().getPlugin("SpigotMaster");
|
||||
```
|
||||
|
||||
|
||||
## `public Material getCustomItemMaterial(String customItemIdentifier)`
|
||||
|
||||
该接口已废弃
|
||||
|
||||
* **Deprecated**
|
||||
|
||||
## `public String getCustomItemIdentifier(ItemStack spigotItemStack)`
|
||||
|
||||
获取自定义物品名的identifier
|
||||
|
||||
* **Parameters:**
|
||||
`spigotItemStack` — itemStack
|
||||
* **Returns:**
|
||||
String 自定义物品identifier
|
||||
|
||||
## `public ItemStack setCustomItemIdentifier(ItemStack spigotItemStack, String customIdentifier)`
|
||||
|
||||
设置自定义物品的identifier
|
||||
|
||||
* **Parameters:**
|
||||
* `spigotItemStack` — itemStack
|
||||
* `customIdentifier` — 自定义物品identifier
|
||||
|
||||
* **Returns:**
|
||||
设置后的itemStack
|
||||
|
||||
## `public ItemStack setItemLayer(ItemStack itemStack, int layer, String texture)`
|
||||
|
||||
设置物品的叠加贴图。与ModSDK的SetItemLayer用法一致
|
||||
|
||||
* **Parameters:**
|
||||
* `itemStack` — itemStack
|
||||
* `layer` — 贴图的层级。可以为-2,-1,1,2,3。负数层级显示在物品下方,正数的层级显示在物品上方。层级大的显示在层级小的上方。
|
||||
* `texture` — 贴图的名字,对应资源包item_texture.json中的key。
|
||||
|
||||
* **Returns:**
|
||||
设置后的itemStack(没设置成功会返回itemStack参数本身)
|
||||
|
||||
## `public String getItemLayer(ItemStack itemStack, int layer)`
|
||||
|
||||
获取物品的叠加贴图。与ModSDK的GetItemLayer用法一致
|
||||
|
||||
* **Parameters:**
|
||||
* `itemStack` — itemStack
|
||||
* `layer` — 贴图的层级。可以为-2,-1,1,2,3。
|
||||
|
||||
* **Returns:**
|
||||
贴图的名字,不存在返回null
|
||||
|
||||
## `public ItemStack removeItemLayer(ItemStack itemStack, int layer)`
|
||||
|
||||
移除物品的叠加贴图。与ModSDK的RemoveItemLayer用法一致
|
||||
|
||||
* **Parameters:**
|
||||
* `itemStack` — itemStack
|
||||
* `layer` — 贴图的层级。可以为-2,-1,1,2,3。
|
||||
|
||||
* **Returns:**
|
||||
移除后的itemStack(没移除会返回itemStack参数本身)
|
||||
|
||||
## `public void listenForSpigotMasterEvent(SpigotMasterEvent event, PyRpcHandler handler)`
|
||||
|
||||
监听spigot master的自定义事件
|
||||
|
||||
* **Parameters:**
|
||||
* `event` — SpigotMasterEvent的枚举值
|
||||
* `handler` — 回调函数
|
||||
|
||||
|
||||
## `public void listenForEvent(String namespace, String system, String event, PyRpcHandler handler)`
|
||||
|
||||
注册客户端事件
|
||||
|
||||
* **Parameters:**
|
||||
* `namespace` — 来源客户端系统的namespace
|
||||
* `system` — 来源客户端系统的systemName
|
||||
* `event` — 事件名
|
||||
* `handler` — 回调函数
|
||||
|
||||
|
||||
## `public void notifyToClient(Player player, String namespace, String system, String event, Map<String, Object> data)`
|
||||
|
||||
给指定玩家发送服务端事件
|
||||
|
||||
* **Parameters:**
|
||||
* `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对于不同玩家来说都指代本机玩家,而非某个固定的实体,所以不要在多播中发送这种信息。
|
||||
|
||||
* **Parameters:**
|
||||
* `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对于不同玩家来说都指代本机玩家,而非某个固定的实体,所以不要在多播中发送这种信息。
|
||||
|
||||
* **Parameters:**
|
||||
* `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对于不同玩家来说都指代本机玩家,而非某个固定的实体,所以不要在多播中发送这种信息。
|
||||
|
||||
* **Parameters:**
|
||||
* `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对于不同玩家来说都指代本机玩家,而非某个固定的实体,所以不要在多播中发送这种信息。
|
||||
|
||||
* **Parameters:**
|
||||
* `except` — 发送事件时排除掉这个玩家,可以为null表示不排除
|
||||
* `namespace` — 在客户端系统使用ListenForEvent监听的namespace
|
||||
* `system` — 在客户端系统使用ListenForEvent监听的systemName
|
||||
* `event` — 事件名
|
||||
* `data` — 事件参数
|
||||
|
||||
|
||||
## `public void enableCustomShopEntry(boolean useCustomShop)`
|
||||
|
||||
开启商城插件入口
|
||||
|
||||
* **Parameters:**
|
||||
`useCustomShop` — 是否使用自定义商城入口,为false时,则使用官方商城入口
|
||||
* **Since:**
|
||||
1.2.3
|
||||
|
||||
## `public void openShop(Player player)`
|
||||
|
||||
打开指定玩家商城界面 注意:该接口需要使用商城插件,并调用接口开启商城插件
|
||||
|
||||
* **Parameters:**
|
||||
`player` — 玩家
|
||||
* **Since:**
|
||||
1.2.3
|
||||
|
||||
## `public void closeShop(Player player)`
|
||||
|
||||
关闭指定玩家商城界面 注意:该接口需要使用商城插件,并调用接口开启商城插件
|
||||
|
||||
* **Parameters:**
|
||||
`player` — 玩家
|
||||
* **Since:**
|
||||
1.2.3
|
||||
|
||||
## `public void showHintOne(Player player, @Nullable String text)`
|
||||
|
||||
推送冒泡提示给指定玩家 注意:该接口需要使用商城插件,并调用接口开启商城插件
|
||||
|
||||
* **Parameters:**
|
||||
* `player` — 指定玩家
|
||||
* `text` — 提示字符串
|
||||
|
||||
* **Since:**
|
||||
1.2.3
|
||||
|
||||
## `public void showHintTwo(Player player, String head, @Nullable String tail)`
|
||||
|
||||
推送 拼接后的头尾两串冒泡提示 给指定玩家 注意:该接口需要使用商城插件,并调用接口开启商城插件
|
||||
|
||||
* **Parameters:**
|
||||
* `player` — 指定玩家
|
||||
* `head` — 提示字符串首
|
||||
* `tail` — 提示字符串尾
|
||||
|
||||
* **Since:**
|
||||
1.2.3
|
||||
|
||||
## `public void getPlayerOrderList(Player player, FutureCallback<Map<String, Object>> callback)`
|
||||
|
||||
获取玩家未发货订单列表
|
||||
|
||||
* **Parameters:**
|
||||
* `player` — 指定玩家
|
||||
* `callback` — FutureCallBack回调函数
|
||||
|
||||
* **Since:**
|
||||
1.2.3
|
||||
* **Example:**
|
||||
回调参数为Map<String,Object>, 目前值为
|
||||
| Key | Value | 解释 |
|
||||
| -----------------| ---------------| ---- |
|
||||
| player | Player | 玩家 |
|
||||
| json_result | 订单json数据 | 玩家 |
|
||||
|
||||
|
||||
## `public void finPlayerOrder(Player player, List<String> orderList, FutureCallback<Map<String, Object>> callback)`
|
||||
|
||||
通知网易服务器完成指定玩家订单
|
||||
|
||||
* **Parameters:**
|
||||
* `player` — 指定玩家
|
||||
* `orderList` — 订单id列表
|
||||
* `callback` — FutureCallBack回调函数
|
||||
|
||||
* **Since:**
|
||||
1.2.3
|
||||
* **Example:**
|
||||
回调参数为Map<String,Object>, 目前值为
|
||||
| Key | Value | 解释 |
|
||||
| -----------------| ---------------| ---- |
|
||||
| player | Player | 玩家 |
|
||||
| json_result | 订单json数据 | 玩家 |
|
||||
|
||||
|
||||
## `public void enableClientChatExtension(Player player)`
|
||||
|
||||
开启官方聊天扩展功能,需要在CLIENT_LOAD_ADDON_FINISH事件中调用
|
||||
|
||||
* **Parameters:**
|
||||
`player` — 指定玩家
|
||||
* **Since:**
|
||||
1.2.4
|
||||
* **Example:**
|
||||
```
|
||||
SpigotMaster.listenForSpigotMasterEvent(CLIENT_LOAD_ADDON_FINISH, new PyRpcHandler() {
|
||||
|
||||
@Override
|
||||
public void onEvent(Player player, Map<String, Object> data) {
|
||||
SpigotMaster.enableClientChatExtension(player);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
## `public void disableClientChatExtension(Player player)`
|
||||
|
||||
关闭官方聊天扩展功能,需要在CLIENT_LOAD_ADDON_FINISH事件中调用
|
||||
|
||||
* **Parameters:**
|
||||
`player` — 指定玩家
|
||||
* **Since:**
|
||||
1.2.4
|
||||
* **Example:**
|
||||
```
|
||||
SpigotMaster.listenForSpigotMasterEvent(CLIENT_LOAD_ADDON_FINISH, new PyRpcHandler() {
|
||||
|
||||
@Override
|
||||
public void onEvent(Player player, Map<String, Object> data) {
|
||||
SpigotMaster.disableClientChatExtension(player);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
## `public String getCustomEntityIdentifier(Entity entity)`
|
||||
|
||||
获取自定义实体的identifier
|
||||
|
||||
* **Parameters:**
|
||||
`entity` — 需要设置的entity实例
|
||||
* **Returns:**
|
||||
String 自定义物品identifier
|
||||
* **Since:**
|
||||
1.3.1
|
||||
|
||||
## `public Entity spawnEntity(Player player, Location loc, EntityType entityType, String neteaseClientIdentifier)`
|
||||
|
||||
生成自定义生物
|
||||
|
||||
* **Parameters:**
|
||||
* `player` — 玩家
|
||||
* `loc` — 生成位置
|
||||
* `entityType` — 原版生物类型
|
||||
* `neteaseClientIdentifier` — 自定义生物identifier
|
||||
|
||||
* **Returns:**
|
||||
Entity 自定义生物实例
|
||||
* **Since:**
|
||||
1.3.1
|
||||
|
||||
## `public void spawnEntity(Player player, Entity entity, String neteaseClientIdentifier)`
|
||||
|
||||
转换成自定义生物
|
||||
|
||||
* **Parameters:**
|
||||
* `player` — 玩家
|
||||
* `entity` — 生物实例
|
||||
* `neteaseClientIdentifier` — 自定义生物identifier
|
||||
|
||||
* **Returns:**
|
||||
Entity 自定义生物实例
|
||||
* **Since:**
|
||||
1.3.1
|
||||
|
||||
## `public Entity spawnEntity(World world, Location loc, EntityType entityType, String neteaseClientIdentifier)`
|
||||
|
||||
生成自定义生物
|
||||
|
||||
* **Parameters:**
|
||||
* `world` — world实例
|
||||
* `loc` — 生成位置
|
||||
* `entityType` — 原版生物类型
|
||||
* `neteaseClientIdentifier` — 自定义生物identifier
|
||||
|
||||
* **Returns:**
|
||||
Entity 自定义生物实例
|
||||
* **Since:**
|
||||
1.3.8
|
||||
|
||||
## `public void spawnEntity(World player, Entity entity, String neteaseClientIdentifier)`
|
||||
|
||||
转换成自定义生物
|
||||
|
||||
* **Parameters:**
|
||||
* `world` — 玩家
|
||||
* `entity` — 生物实例
|
||||
* `neteaseClientIdentifier` — 自定义生物identifier
|
||||
|
||||
* **Returns:**
|
||||
Entity 自定义生物实例
|
||||
* **Since:**
|
||||
1.3.8
|
||||
|
||||
## `public Entity setCustomEntityIdentifier(Entity entity, String customIdentifier)`
|
||||
|
||||
设置自定义实体的identifier
|
||||
|
||||
* **Parameters:**
|
||||
* `entity` — 需要设置的entity实例
|
||||
* `customIdentifier` — 自定义实体identifier
|
||||
|
||||
* **Returns:**
|
||||
设置后的entity
|
||||
* **Since:**
|
||||
1.3.1
|
||||
|
||||
## `public boolean sendForm(Player player, Form form)`
|
||||
|
||||
发送formui给指定玩家
|
||||
|
||||
* **Parameters:**
|
||||
* `player` — 指定玩家
|
||||
* `form` — 表单类实例
|
||||
|
||||
* **Since:**
|
||||
1.3.2
|
||||
|
||||
## `public boolean sendForm(Player player, FormBuilder<?, ?, ?> formBuilder)`
|
||||
|
||||
发送formui给指定玩家
|
||||
|
||||
* **Parameters:**
|
||||
* `player` — 指定玩家
|
||||
* `formBuilder` — 表单builder类实例
|
||||
|
||||
* **Since:**
|
||||
1.3.2
|
||||
|
||||
## `public void setEntitySize(Entity entity, float height, float width)`
|
||||
|
||||
设置自定义实体的碰撞盒大小
|
||||
|
||||
* **Parameters:**
|
||||
* `entity` — 需要设置的entity实例
|
||||
* `height` — 实体高度
|
||||
* `width` — 实体宽度
|
||||
|
||||
* **Since:**
|
||||
1.3.3
|
||||
|
||||
## `public void setMythicMobIdentifier(Entity entity, String mobType)`
|
||||
|
||||
设置mythicmob的identifier
|
||||
|
||||
如: SkeletonKing 会自动转为客户端的 mythicmob:skeletonKing
|
||||
|
||||
SkeletalMinion 会自动转为客户端的 mythicmob:skeletalMinion
|
||||
|
||||
即为:mythicmob:xxx xxx为首字母小写的mythicmob的identifier
|
||||
|
||||
* **Parameters:**
|
||||
* `entity` — 需要设置的entity实例
|
||||
* `mobType` — mythicmob的identifier
|
||||
|
||||
* **Since:**
|
||||
1.3.3
|
||||
|
||||
## `public void setEntityScale(Entity entity, float scale)`
|
||||
|
||||
设置自定义实体的scale
|
||||
|
||||
注意,由于Java并不提供修改Scale接口,因此该接口仅为客户端暂时生效接口
|
||||
|
||||
玩家重登;掉线重连;后续进入的玩家都只能看到默认的scale,不能看到通过接口修改后的scale!
|
||||
|
||||
请留意该接口的使用时机!
|
||||
|
||||
* **Parameters:**
|
||||
* `entity` — 需要设置的entity实例
|
||||
* `scale` — scale
|
||||
|
||||
* **Since:**
|
||||
1.3.5
|
||||
|
||||
## `public void sendBedrockPacket(int packetId, byte[] packetData)`
|
||||
|
||||
给所有玩家广播基岩版原生数据包,
|
||||
|
||||
注:网易基岩版客户端数据包协议库基于国际版MC进行修改,不符合当前BE客户端的协议版本(v503 MCBE版本1.18.30)的包体数据会导致客户端异常,包括但不限于断开连接,闪退以及游戏无响应
|
||||
|
||||
目前建议版本为v503
|
||||
|
||||
* **Parameters:**
|
||||
* `packetId` — 数据包id
|
||||
* `packetData` — 数据包数据
|
||||
|
||||
* **Since:**
|
||||
1.3.7
|
||||
* **Example:**
|
||||
```
|
||||
//修改客户端时间
|
||||
org.cloudburstmc.protocol.bedrock.packet.TimePacket timePacket = new TimePacket();
|
||||
ByteBuf setTimeByteBuf = ByteBufAllocator.DEFAULT.heapBuffer(128);
|
||||
timePacket.setTime(20000);
|
||||
setTimeByteBuf = ByteBufAllocator.DEFAULT.heapBuffer(128);
|
||||
org.cloudburstmc.protocol.bedrock.codec.v503.Bedrock_v503.CODEC.tryEncode(Bedrock_v503.CODEC.createHelper(), setTimeByteBuf, timePacket);
|
||||
int setTimeDataLen = setTimeByteBuf.readableBytes();
|
||||
byte[] setTimeData = new byte[setTimeDataLen];
|
||||
setTimeByteBuf.readBytes(setTimeData, 0, setTimeDataLen);
|
||||
setTimeByteBuf.release();
|
||||
spigotMaster.sendBedrockPacket(setTimePacketId, setTimeData);
|
||||
```
|
||||
|
||||
|
||||
## `public void sendBedrockPacketToPlayer(int packetId, byte[] packetData, Player player)`
|
||||
|
||||
给指定玩家广播基岩版原生数据包
|
||||
|
||||
注:网易基岩版客户端数据包协议库基于国际版MC进行修改,不符合当前BE客户端的协议版本(v503 MCBE版本1.18.30)的包体数据会导致客户端异常,包括但不限于断开连接,闪退以及游戏无响应
|
||||
|
||||
目前建议版本为v503
|
||||
|
||||
* **Parameters:**
|
||||
* `packetId` — 数据包id
|
||||
* `packetData` — 数据包数据数组
|
||||
* `player` — 发送的玩家
|
||||
|
||||
* **Since:**
|
||||
1.3.7
|
||||
|
||||
## `public void setEntityGravity(int entityId, float gravity)`
|
||||
|
||||
设置实体重力系数
|
||||
|
||||
注:需要注意设置实体的时机,如果通过监听entityspawnevent修改则无法生效,因为此时客户端还未同步到相关实体!
|
||||
|
||||
* **Parameters:**
|
||||
* `entityId` — 需要设置的entityId
|
||||
* `gravity` — 重力系数,如果为0则应用世界重力
|
||||
|
||||
* **Since:**
|
||||
1.3.9
|
||||
|
||||
## `public String getItemIconPath(ItemStack item)`
|
||||
|
||||
获取物品贴图路径(只能获取到原生物品的贴图)
|
||||
|
||||
注: 由于大部分方块由四方向(side/front/back/top)共四张贴图组成,因此,该接口只会默认返回side方向贴图,而formui只能支持单独一张贴图,因此对于方块而言,单纯使用该接口获取贴图后显示,效果并不佳,建议自行绘制贴图
|
||||
|
||||
* **Parameters:**
|
||||
`item` — 需要获取的ItemStack
|
||||
* **Since:**
|
||||
1.3.10
|
||||
13
mcguide/27-手机网络游戏/课程10:使用Spigot开服/82-SpigotMasterEvent文档.md
Normal file
@@ -0,0 +1,13 @@
|
||||
# SpigotMasterEvent文档
|
||||
|
||||
## `PLAYER_URGE_SHIP("player_urge_ship"),`
|
||||
|
||||
玩家催发货事件
|
||||
|
||||
## `PLAYER_BUY_ITEM_SUCCESS("player_buy_item_success"),`
|
||||
|
||||
玩家购买成功事件
|
||||
|
||||
## `CLIENT_LOAD_ADDON_FINISH("client_load_addon_finish")`
|
||||
|
||||
玩家客户端加载Mod完成事件
|
||||
83
mcguide/27-手机网络游戏/课程10:使用Spigot开服/90-常见问题合集.md
Normal file
@@ -0,0 +1,83 @@
|
||||
---
|
||||
front:
|
||||
hard: 入门
|
||||
time: 60分钟
|
||||
---
|
||||
|
||||
# 常见问题合集
|
||||
## 1、开发机上是否有对外开放的tcp端口?
|
||||
目前开发机上没有对外开放的tcp端口,只有[10000,20000]的udp端口允许外部访问
|
||||
|
||||
## 2、能否支持连接相同Geyser的玩家加载不同的Mod?
|
||||
|
||||
目前Mod经过Geyser加载并分发,因此当前版本中,通过同一个Geyser连接的玩家,加载的Mod都相同,如果需要实现不同bc或者spigot加载不同的Mod效果,可通过配置不同的geyser连接不同的bc实现
|
||||
|
||||
## 3、开发机上Java命令怎么调用?
|
||||
|
||||
由于目前Geyser和Spigot使用的java版本不同,因此开发机上存在两个版本的java,对应的命令分别是 **java18** 以及 **java8**,因此,在启动Spigot时,请使用命令视情况自行选择java版本
|
||||

|
||||
|
||||
## 4、Geyser服日志怎么查看?
|
||||
使用ssh登录服务器后,查看对应网络id下的geyser目录,可以查看geyser的日志
|
||||
|
||||

|
||||
|
||||
## 5、部署测试时,遇到玩家连接不上问题怎么排查?
|
||||
部署测试时,遇到玩家连接不上的问题请根据如下步骤排查
|
||||
### (1) 首先确定下调试标识
|
||||
- 部署时,开启了如下标识
|
||||
|
||||

|
||||
|
||||
> 则在bc服和master未成功连接时,登录会有如下提示
|
||||
|
||||

|
||||
|
||||
- 当对应标识未开启时,提示如下:
|
||||
|
||||

|
||||
|
||||
### (2) 排查Geyser与Master、Proxy服是否正常连接
|
||||
|
||||
|
||||
- 当geyser与proxy及master正常连接时,Geyser服会输出如下日志
|
||||
|
||||

|
||||
|
||||
- 当玩家正常连接上proxy及geyser时,会输出如下日志
|
||||

|
||||
|
||||
- 当玩家登录时没有上述输出,证明Geyser和Master、Proxy连接异常,请对照文档检查配置
|
||||
|
||||
[Apollo2.0简述](./10-支持基岩版客户端的Java版网络游戏概述.md)
|
||||
|
||||
### (3) 排查BC服及BungeeMaster插件是否正常加载
|
||||
|
||||
- 当确定Geyser服连接正常时,请查看BC服日志,排查BungeeMaster插件是否正常加载以及master服是否正常连接上,正常连接会有如下日志打印:
|
||||
|
||||

|
||||
- 当遇到没有上述输出时,证明Geyser服、Master服、Bungee服组网失败。请根据文档排查以下几种常见原因
|
||||
[Apollo2.0简述](./10-支持基岩版客户端的Java版网络游戏概述.md)
|
||||
- 首先检查BC服的**config.yml**配置中,**master_port**是否和Studio配置的一致
|
||||
|
||||
- 再检查Studio配置的BC服端口是否为BC的**config.yml**中的**query_port(红框)**,请不要配置成**servers**里具体**Spigot服(绿框)**监听的端口
|
||||

|
||||
|
||||
- 若一致,请通过netstat命令排查端口是否冲突,被多个应用监听
|
||||
|
||||
- 若上述结果都显示正常,请联系官方,提供网络id、机器ip以供排查
|
||||
|
||||
### (4) 排查Spigot服是否正常连接
|
||||
|
||||
- 当出现如下截图信息时,一般是Spigot无法连接到
|
||||

|
||||
|
||||
- 请检查BC服与Spigot服配置以及日志,确定BC服能正常连接上Spigot服
|
||||
|
||||
## 6、SpigotMaster是否必须加载?
|
||||
目前Spigotmaster是强制要求加载,虽然不加载不会报错,但是会有部分功能无法支持。如皮肤防作弊、聊天插件控制、商城接口等。因此强烈要求必须加载SpigotMaster
|
||||
**另外目前SpigotMaster不支持reload指令,使用后会不正常,建议不使用Spigot等端的reload指令**
|
||||
|
||||
## 7、Geyser的config.tpl如果有需要该如何修改?
|
||||
目前暂不支持直接修改config.tpl,如果有需要,目前deploy.json文件提供了几个参数,用于修改config.tpl中的常用参数。详见[其他部署时可配置字段](./13-其他部署时可配置字段.md)
|
||||
|
||||
70
mcguide/27-手机网络游戏/课程10:使用Spigot开服/99-下载内容.md
Normal file
@@ -0,0 +1,70 @@
|
||||
---
|
||||
front:
|
||||
hard: 入门
|
||||
time: 分钟
|
||||
---
|
||||
|
||||
# 下载内容
|
||||
|
||||
下载方式如下:
|
||||
|
||||

|
||||
|
||||
下载后导入解压到本地,获取.jar文件:
|
||||
|
||||

|
||||
|
||||
## BungeeMaster插件
|
||||
|
||||
bc服插件,必须,用于bc服组网
|
||||
|
||||
|
||||
## SpigotMaster插件
|
||||
|
||||
spigot服插件,必须,用于客户端python通信,自定义物品
|
||||
|
||||
|
||||
### 使用方式
|
||||
|
||||
下载 **SpigotMaster** 的jar包,然后执行以下指令,**path-to-jar**替换为jar路径
|
||||
|
||||
```shell
|
||||
mvn install:install-file -Dfile=path-to-jar
|
||||
-DgroupId=com.neteasemc
|
||||
-DartifactId=SpigotMaster
|
||||
-Dversion=x.x.x-SNAPSHOT
|
||||
-Dpackaging=jar
|
||||
```
|
||||
|
||||
- 插件编写时
|
||||
|
||||
1. 在pom.xml中添加依赖
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>com.neteasemc</groupId>
|
||||
<artifactId>SpigotMaster</artifactId>
|
||||
<version>x.x.x-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
2. 在plugin.yml中添加依赖
|
||||
|
||||
```yml
|
||||
depend:
|
||||
- SpigotMaster
|
||||
```
|
||||
|
||||
## 示例Demo
|
||||
|
||||
包含以下内容:
|
||||
1. CustomItemDemo:客户端新自定义物品示例
|
||||
2. CustomPigModelDemo:客户端为随机的猪更改颜色的示例
|
||||
3. CustomHumanModelDemo: 客户端为更改玩家模型的示例
|
||||
4. PyRpcDemo:客户端python通信示例
|
||||
5. ShopDemo:商业化插件示例
|
||||
6. CustomEntityDemo:自定义实体示例
|
||||
7. FormUIDemo:ServerFormUI示例
|
||||
8. CustomBlockDemo: 自定义方块示例
|
||||
9. InventoryToFormUIDemo:原生箱子UI转FormUI示例
|
||||
0
mcguide/27-手机网络游戏/课程10:使用Spigot开服/README.md
Normal file
|
After Width: | Height: | Size: 398 KiB |
|
After Width: | Height: | Size: 181 KiB |
|
After Width: | Height: | Size: 214 KiB |
BIN
mcguide/27-手机网络游戏/课程10:使用Spigot开服/res/bc01.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
mcguide/27-手机网络游戏/课程10:使用Spigot开服/res/bc02.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
mcguide/27-手机网络游戏/课程10:使用Spigot开服/res/bc03.png
Normal file
|
After Width: | Height: | Size: 7.5 KiB |
BIN
mcguide/27-手机网络游戏/课程10:使用Spigot开服/res/bc04.png
Normal file
|
After Width: | Height: | Size: 6.6 KiB |
BIN
mcguide/27-手机网络游戏/课程10:使用Spigot开服/res/bc05.png
Normal file
|
After Width: | Height: | Size: 58 KiB |
BIN
mcguide/27-手机网络游戏/课程10:使用Spigot开服/res/deployjson0.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
mcguide/27-手机网络游戏/课程10:使用Spigot开服/res/dl.png
Normal file
|
After Width: | Height: | Size: 114 KiB |
BIN
mcguide/27-手机网络游戏/课程10:使用Spigot开服/res/dl1.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
mcguide/27-手机网络游戏/课程10:使用Spigot开服/res/frame.png
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
mcguide/27-手机网络游戏/课程10:使用Spigot开服/res/quest1.png
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
mcguide/27-手机网络游戏/课程10:使用Spigot开服/res/quest10.png
Normal file
|
After Width: | Height: | Size: 78 KiB |
BIN
mcguide/27-手机网络游戏/课程10:使用Spigot开服/res/quest2.png
Normal file
|
After Width: | Height: | Size: 74 KiB |
BIN
mcguide/27-手机网络游戏/课程10:使用Spigot开服/res/quest3.png
Normal file
|
After Width: | Height: | Size: 68 KiB |
BIN
mcguide/27-手机网络游戏/课程10:使用Spigot开服/res/quest4.png
Normal file
|
After Width: | Height: | Size: 172 KiB |
BIN
mcguide/27-手机网络游戏/课程10:使用Spigot开服/res/quest5.png
Normal file
|
After Width: | Height: | Size: 6.5 KiB |
BIN
mcguide/27-手机网络游戏/课程10:使用Spigot开服/res/quest6.png
Normal file
|
After Width: | Height: | Size: 34 KiB |
BIN
mcguide/27-手机网络游戏/课程10:使用Spigot开服/res/quest7.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
mcguide/27-手机网络游戏/课程10:使用Spigot开服/res/quest8.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
mcguide/27-手机网络游戏/课程10:使用Spigot开服/res/quest9.png
Normal file
|
After Width: | Height: | Size: 205 KiB |
BIN
mcguide/27-手机网络游戏/课程10:使用Spigot开服/res/spigotCustomBlock/1.png
Normal file
|
After Width: | Height: | Size: 55 KiB |
BIN
mcguide/27-手机网络游戏/课程10:使用Spigot开服/res/spigotCustomBlock/2.png
Normal file
|
After Width: | Height: | Size: 52 KiB |
BIN
mcguide/27-手机网络游戏/课程10:使用Spigot开服/res/spigotCustomEntity/17.png
Normal file
|
After Width: | Height: | Size: 283 KiB |
BIN
mcguide/27-手机网络游戏/课程10:使用Spigot开服/res/spigotCustomEntity/18.png
Normal file
|
After Width: | Height: | Size: 159 KiB |
BIN
mcguide/27-手机网络游戏/课程10:使用Spigot开服/res/spigotCustomEntity/20.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
mcguide/27-手机网络游戏/课程10:使用Spigot开服/res/spigotCustomEntity/21.png
Normal file
|
After Width: | Height: | Size: 84 KiB |
BIN
mcguide/27-手机网络游戏/课程10:使用Spigot开服/res/spigotCustomEntity/22.png
Normal file
|
After Width: | Height: | Size: 63 KiB |
BIN
mcguide/27-手机网络游戏/课程10:使用Spigot开服/res/spigotCustomEntity/23.png
Normal file
|
After Width: | Height: | Size: 71 KiB |
BIN
mcguide/27-手机网络游戏/课程10:使用Spigot开服/res/spigotCustomEntity/24.png
Normal file
|
After Width: | Height: | Size: 335 KiB |
BIN
mcguide/27-手机网络游戏/课程10:使用Spigot开服/res/spigotCustomEntity/25.png
Normal file
|
After Width: | Height: | Size: 337 KiB |
BIN
mcguide/27-手机网络游戏/课程10:使用Spigot开服/res/spigotCustomEntity/26.png
Normal file
|
After Width: | Height: | Size: 257 KiB |
BIN
mcguide/27-手机网络游戏/课程10:使用Spigot开服/res/spigotCustomEntity/27.png
Normal file
|
After Width: | Height: | Size: 559 KiB |
BIN
mcguide/27-手机网络游戏/课程10:使用Spigot开服/res/spigotCustomEntity/28.png
Normal file
|
After Width: | Height: | Size: 487 KiB |
BIN
mcguide/27-手机网络游戏/课程10:使用Spigot开服/res/spigotCustomEntity/4.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
mcguide/27-手机网络游戏/课程10:使用Spigot开服/res/spigotCustomEntity/5.png
Normal file
|
After Width: | Height: | Size: 87 KiB |
BIN
mcguide/27-手机网络游戏/课程10:使用Spigot开服/res/spigotCustomEntity/6.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
mcguide/27-手机网络游戏/课程10:使用Spigot开服/res/spigotCustomEntity/7.png
Normal file
|
After Width: | Height: | Size: 55 KiB |
BIN
mcguide/27-手机网络游戏/课程10:使用Spigot开服/res/spigotCustomEntity/8.png
Normal file
|
After Width: | Height: | Size: 212 KiB |
BIN
mcguide/27-手机网络游戏/课程10:使用Spigot开服/res/spigotCustomEntity/9.png
Normal file
|
After Width: | Height: | Size: 661 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 189 KiB |
|
After Width: | Height: | Size: 52 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 8.9 KiB |
|
After Width: | Height: | Size: 124 KiB |
|
After Width: | Height: | Size: 225 KiB |
|
After Width: | Height: | Size: 174 KiB |
BIN
mcguide/27-手机网络游戏/课程10:使用Spigot开服/res/spigotForm/1.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
mcguide/27-手机网络游戏/课程10:使用Spigot开服/res/spigotForm/2.png
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
mcguide/27-手机网络游戏/课程10:使用Spigot开服/res/spigotForm/3.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
mcguide/27-手机网络游戏/课程10:使用Spigot开服/res/spigotForm/4.png
Normal file
|
After Width: | Height: | Size: 164 KiB |
BIN
mcguide/27-手机网络游戏/课程10:使用Spigot开服/res/spigotForm/5.png
Normal file
|
After Width: | Height: | Size: 517 KiB |
BIN
mcguide/27-手机网络游戏/课程10:使用Spigot开服/res/spigotForm/6.png
Normal file
|
After Width: | Height: | Size: 188 KiB |
BIN
mcguide/27-手机网络游戏/课程10:使用Spigot开服/res/spigotForm/7.png
Normal file
|
After Width: | Height: | Size: 340 KiB |
BIN
mcguide/27-手机网络游戏/课程10:使用Spigot开服/res/spigotForm/8.png
Normal file
|
After Width: | Height: | Size: 647 KiB |
BIN
mcguide/27-手机网络游戏/课程10:使用Spigot开服/res/spigotPlugin/plugin1.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
mcguide/27-手机网络游戏/课程10:使用Spigot开服/res/spigotPlugin/plugin10.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
mcguide/27-手机网络游戏/课程10:使用Spigot开服/res/spigotPlugin/plugin11.png
Normal file
|
After Width: | Height: | Size: 45 KiB |
BIN
mcguide/27-手机网络游戏/课程10:使用Spigot开服/res/spigotPlugin/plugin12.png
Normal file
|
After Width: | Height: | Size: 140 KiB |
BIN
mcguide/27-手机网络游戏/课程10:使用Spigot开服/res/spigotPlugin/plugin13.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
mcguide/27-手机网络游戏/课程10:使用Spigot开服/res/spigotPlugin/plugin14.png
Normal file
|
After Width: | Height: | Size: 887 KiB |
BIN
mcguide/27-手机网络游戏/课程10:使用Spigot开服/res/spigotPlugin/plugin16.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
mcguide/27-手机网络游戏/课程10:使用Spigot开服/res/spigotPlugin/plugin17.png
Normal file
|
After Width: | Height: | Size: 69 KiB |
BIN
mcguide/27-手机网络游戏/课程10:使用Spigot开服/res/spigotPlugin/plugin18.png
Normal file
|
After Width: | Height: | Size: 61 KiB |
BIN
mcguide/27-手机网络游戏/课程10:使用Spigot开服/res/spigotPlugin/plugin19.png
Normal file
|
After Width: | Height: | Size: 73 KiB |