2.6
This commit is contained in:
242
docs/mconline/60-我的世界创造营教程/网络游戏开服教程/1-服务器与客户端的通信/0-Spigot插件编写.md
Normal file
242
docs/mconline/60-我的世界创造营教程/网络游戏开服教程/1-服务器与客户端的通信/0-Spigot插件编写.md
Normal file
@@ -0,0 +1,242 @@
|
||||
# Spigot插件编写
|
||||
|
||||
<iframe src="https://cc.163.com/act/m/daily/iframeplayer/?id=64818bb8c31a9c0f360dc5b2" width="800" height="600" allow="fullscreen"/>
|
||||
|
||||
## 语言基础
|
||||
|
||||
编写Spigot插件需要一定的Java语言基础,并在后续开发中会使用到Maven或Gradle来构建Spigot插件,在此仅提供部分链接供参考,需要开发者自行学习。
|
||||
|
||||
- [Java](https://www.runoob.com/java/java-tutorial.html)
|
||||
- [Maven](https://maven.apache.org/)
|
||||
- [Gradle](https://gradle.org/)
|
||||
|
||||
在掌握Java语言基础后,需要继续学习Spigot、BC的API并进行插件开发。同样也需要开发者自行进行学习。
|
||||
|
||||
- [Spigot插件开发教程](https://www.spigotmc.org/wiki/spigot-plugin-development/)
|
||||
- [BC插件开发教程](https://www.spigotmc.org/wiki/bungeecord-plugin-development/)
|
||||
|
||||
## 开始编写Spigot插件
|
||||
|
||||
### 创建项目
|
||||
|
||||
> 本教程会使用IntelliJ IDEA来进行插件开发,并使用Minecraft Development插件来快速创建项目。
|
||||
>
|
||||
> 如果没有安装的可以提前进行安装
|
||||
>
|
||||
> 
|
||||
|
||||
首先点击创建项目,找到Minecraft分类,选择Spigot插件。
|
||||
|
||||

|
||||
|
||||
接着自行填写GroupId和ArtifactId,点击下一步后,进行如下更改:
|
||||
|
||||
1. 选择Minecraft Version为1.12.2
|
||||
2. 填写Depend为SpigotMaster
|
||||
|
||||

|
||||
|
||||
点击下一步后,选择目录和目录名进行项目创建。
|
||||
|
||||
### 添加依赖
|
||||
|
||||
在下载SpigotMaster插件后,复制文件路径。将下方指令中的path-to-jar替换为路径,x.x.x替换为版本号。
|
||||
|
||||
SpigotMaster插件和之前安装到服务器里的是同一个,可以点[我](https://mc.163.com/dev/mcmanual/mc-dev/mcguide/27-手机网络游戏/课程10:使用Spigot开服/99-下载内容.html?catalog=1)查看下载方式。
|
||||
|
||||
```
|
||||
mvn install:install-file -Dfile=path-to-jar -DgroupId=com.neteasemc -DartifactId=SpigotMaster -Dversion=x.x.x-SNAPSHOT -Dpackaging=jar
|
||||
```
|
||||
|
||||
在IDEA的Maven指令中执行
|
||||
|
||||

|
||||
|
||||
执行成功后,会看到BUILD SUCCESS的输出。
|
||||
|
||||

|
||||
|
||||
接下来在pom.xml中,配置dependency添加依赖。
|
||||
|
||||
```
|
||||
<dependency>
|
||||
<groupId>com.neteasemc</groupId>
|
||||
<artifactId>SpigotMaster</artifactId>
|
||||
<version>1.3.0-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
其中版本处需要替换为自己安装的版本。添加完成后dependency如下。
|
||||
|
||||

|
||||
|
||||
### 代码编写
|
||||
|
||||
完成了依赖的添加,就可以使用SpigotMaster来和中国版基岩版客户端进行通信了。
|
||||
|
||||
在SpigotMaster的接口中,涉及到与客户端通信的方法主要有两个。
|
||||
|
||||
- listenForEvent
|
||||
- notifyToClient
|
||||
|
||||
使用SpigotMaster实例下的这两个方法将可以实现绝大多数通信的需求。
|
||||
|
||||
其他方法可以参考:[SpigotMaster文档](https://mc.163.com/dev/mcmanual/mc-dev/mcguide/27-手机网络游戏/课程10:使用Spigot开服/81-SpigotMasterAPI文档.html?catalog=1)
|
||||
|
||||
SpigotMaster的实例可以通过下方代码获取。
|
||||
|
||||
```java
|
||||
public void onEnable() {
|
||||
SpigotMaster spigotMaster = (SpigotMaster) Bukkit.getPluginManager().getPlugin("SpigotMaster");
|
||||
}
|
||||
```
|
||||
|
||||
#### listenForEvent
|
||||
|
||||
使用该方法可以监听客户端发送来的事件,需要提供参数:
|
||||
|
||||
- `namespace` - 来源客户端系统的namespace
|
||||
- `system` - 来源客户端系统的systemName
|
||||
- `event` - 事件名
|
||||
- `handler` - 回调函数
|
||||
|
||||
在这里我们先暂时编写一个简单的监听事件的函数。
|
||||
|
||||
```java
|
||||
package me.zhanshi123.apollo2example;
|
||||
|
||||
import com.neteasemc.spigotmaster.SpigotMaster;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
public final class Apollo2Example extends JavaPlugin {
|
||||
private static Apollo2Example instance;
|
||||
|
||||
public static Apollo2Example getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
private SpigotMaster spigotMaster;
|
||||
|
||||
public SpigotMaster getSpigotMaster() {
|
||||
return spigotMaster;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
instance=this;
|
||||
spigotMaster = (SpigotMaster) Bukkit.getPluginManager().getPlugin("SpigotMaster");
|
||||
spigotMaster.listenForEvent("testMod", "testModBeh", "TestEvent", (player, map) -> {
|
||||
getLogger().info(player.getName());
|
||||
map.forEach((key, value) -> getLogger().info("k: " + key + "v: " + value));
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
// Plugin shutdown logic
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
调用这个方法,将会注册一个命名空间为testMod,系统名为testModBeh的TestEvent事件,并在收到数据时打印发送的玩家名,和事件的信息字典。
|
||||
|
||||
Java的listenForEvent的回调函数,会在Python端调用NotifyToServer时触发。对应的代码如下:
|
||||
|
||||
```python
|
||||
self.NotifyToServer("TestEvent", {"data": "测试数据"})
|
||||
```
|
||||
|
||||
在**ClientSystem**内调用**NotifyToServer**方法,传递事件和对应参数。完整的Python代码见下一节。
|
||||
|
||||
#### notifyToClient
|
||||
|
||||
该方法可以主动给客户端发送事件,需要提供参数:
|
||||
|
||||
- `player` - 接收事件的玩家
|
||||
- `namespace` - 在客户端系统使用ListenForEvent监听的namespace
|
||||
- `system` - 在客户端系统使用ListenForEvent监听的systemName
|
||||
- `event` - 事件名
|
||||
- `data` - 事件参数。注意,要使用-2指代本地玩家的entityId。
|
||||
|
||||
我们同样编写一个指令,用来后续测试Python端是否能正常收到消息
|
||||
|
||||
首先在plugin.yml中注册指令
|
||||
|
||||
```yaml
|
||||
commands:
|
||||
apollotest:
|
||||
```
|
||||
|
||||
接下来新建一个类,实现CommandExecutor接口,在玩家执行指令时调用SpigotMaster实例并notifyToClient
|
||||
|
||||
```java
|
||||
package me.zhanshi123.apollo2example;
|
||||
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class Commands implements CommandExecutor {
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
if (!(sender instanceof Player)) {
|
||||
return true;
|
||||
}
|
||||
Player player = (Player) sender;
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("msg", "这是一条来自Java服的消息");
|
||||
Apollo2Example.getInstance().getSpigotMaster()
|
||||
.notifyToClient(player, "testMod", "testModDev", "TestServerEvent", data);
|
||||
player.sendMessage("notifyToClient已执行");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
在这里,我们将作为testMod这个命名空间的testModDev的系统,发送事件,事件名为TestServerEvent,数据为一个Map,它会在客户端被读取为Python的字典类型进行处理,其中的数据也会被转换为Python类型:
|
||||
|
||||
| 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端的notifyToClient发送的数据,需要调用**ClientSystem**的**ListenForEvent**方法,对应的代码如下:
|
||||
|
||||
这样就会在收到事件时,打印参数。完整的Python代码见下一节。
|
||||
|
||||
```python
|
||||
def __init__(self, namespace, systemName):
|
||||
ClientSystem.__init__(self, namespace, systemName)
|
||||
self.ListenForEvent(ModConst.ModName, ModConst.ServerSystemName, "TestServerEvent", self, self.OnServerEvent)
|
||||
|
||||
def OnServerEvent(self, args):
|
||||
print "OnServerEvent", args
|
||||
|
||||
def Destroy(self):
|
||||
self.UnListenForEvent(ModConst.ModName, ModConst.ServerSystemName, "TestServerEvent", self, self.OnServerEvent)
|
||||
```
|
||||
|
||||
接下来在onEnable方法中进行注册。
|
||||
|
||||
```java
|
||||
Bukkit.getPluginCommand("apollotest").setExecutor(new Commands());
|
||||
```
|
||||
|
||||
随后执行`mvn package`即可对插件进行打包,随后可以上传至小小云对应Spigot端的plugins文件夹。
|
||||
|
||||
然后重启服务器,即可让插件加载。
|
||||
188
docs/mconline/60-我的世界创造营教程/网络游戏开服教程/1-服务器与客户端的通信/1-客户端模组编写.md
Normal file
188
docs/mconline/60-我的世界创造营教程/网络游戏开服教程/1-服务器与客户端的通信/1-客户端模组编写.md
Normal file
@@ -0,0 +1,188 @@
|
||||
# 客户端模组编写
|
||||
|
||||
本节将主要介绍如何制作客户端模组与Java服插件进行通信。
|
||||
|
||||
## 语言基础
|
||||
|
||||
编写中国版基岩版客户端模组需要掌握Python2.7、模组SDK。
|
||||
|
||||
该部分较为基础,需要开发者自行安装并学习。推荐提前安装[补全库](https://mc.163.com/dev/mcmanual/mc-dev/mcguide/20-%E7%8E%A9%E6%B3%95%E5%BC%80%E5%8F%91/13-%E6%A8%A1%E7%BB%84SDK%E7%BC%96%E7%A8%8B/2-Python%E8%84%9A%E6%9C%AC%E5%BC%80%E5%8F%91/0-%E8%84%9A%E6%9C%AC%E5%BC%80%E5%8F%91%E5%85%A5%E9%97%A8.html?key=%E8%A1%A5%E5%85%A8&docindex=1&type=0#%E5%AE%89%E8%A3%85mod-sdk%E8%A1%A5%E5%85%A8%E5%BA%93)。
|
||||
|
||||
- [Python2.7](https://www.python.org/downloads/release/python-2718/)
|
||||
- [模组SDK](https://mc.163.com/dev/mcmanual/mc-dev/mcguide/20-%E7%8E%A9%E6%B3%95%E5%BC%80%E5%8F%91/13-%E6%A8%A1%E7%BB%84SDK%E7%BC%96%E7%A8%8B/1-Mod%E5%BC%80%E5%8F%91%E7%AE%80%E4%BB%8B/1-Mod%E7%AE%80%E4%BB%8B.html?catalog=1)
|
||||
|
||||
## 项目创建
|
||||
|
||||
在开始代码编写之前,首先需要创建项目。
|
||||
|
||||
切换到插件标签页,点击新建插件。
|
||||
|
||||

|
||||
|
||||
然后我们按照团队名,插件名称来填写信息,并且勾选大厅服/游戏服。
|
||||
|
||||
因为我们在之前已经在Spigot插件中定义好了插件的命名空间和系统名,所以我们这里按照下方截图填写,方便后面直接和Java服通信。
|
||||
|
||||

|
||||
|
||||
创建完成后,对插件右键,打开目录。就可以看到插件的目录结构。
|
||||
|
||||
- behavior_packs - 行为包目录
|
||||
- developer_mods - 在开服工具2.0中无用
|
||||
- resource_packs - 资源包目录
|
||||
- worlds - 存档(在开服工具2.0中仅用来配置行为包和资源包)
|
||||
|
||||
在这里,我们主要需要编写的地方就是**行为包目录、资源包目录**。
|
||||
|
||||
- **行为包**主要用来存放客户端模组的代码、物品定义、实体定义等等。
|
||||
- **资源包**主要用来存放客户端模组的美术资源,文本资源等等。
|
||||
|
||||
因为developer_mods在开服工具2.0中没有用途,所以我们可以打开文件夹,将截图所示内容删除。
|
||||
|
||||

|
||||
|
||||
完成删除后,我们可以将整个testMod文件夹剪切到服务器配置中的Mod目录文件夹。
|
||||
|
||||

|
||||
|
||||
接下来,打开装有Python插件的IDEA或者PyCharm,对客户端模组进行脚本编辑。
|
||||
|
||||
在File->Open中复制文件路径,打开这个模组文件夹。
|
||||
|
||||

|
||||
|
||||
然后对`testModBehavior`右键,将其标记为`Sources Root`,这样补全库才能正常工作。
|
||||
|
||||

|
||||
|
||||
接下来,我们可以打开`modConst.py`,在这里可以看到这个模组的一些常量。
|
||||
|
||||
- `ModName` 代表 模组命名空间
|
||||
- `ClientSystemName` 代表 模组客户端系统名
|
||||
|
||||
可以回顾一下Java服插件中的命名空间和客户端系统名,可以看到这它们是一一对应的。
|
||||
|
||||
**只有在服务器和客户端通信时使用相同命名空间和系统名,通信数据才会被成功处理。**
|
||||
|
||||
```python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# 整个Mod的一些绑定配置
|
||||
ModVersion = "1.0.0"
|
||||
ModName = "testMod"
|
||||
ClientSystemName = "testModBeh"
|
||||
ClientSystemClsPath = "testModScript.modClientSystem.ModClientSystem"
|
||||
ServerSystemName = "testModDev"
|
||||
ServerSystemClsPath = "testModScript.modServerSystem.ModServerSystem"
|
||||
|
||||
# 引擎事件
|
||||
UiInitFinishedEvent = "UiInitFinished"
|
||||
```
|
||||
|
||||
接下来打开`modClientSystem.py`
|
||||
|
||||
```python
|
||||
import client.extraClientApi as clientApi
|
||||
```
|
||||
|
||||
将文件顶部的代码修改为,方便正常使用补全库。
|
||||
|
||||
```python
|
||||
import mod.client.extraClientApi as clientApi
|
||||
```
|
||||
|
||||
## 代码编写
|
||||
|
||||
功能需求:
|
||||
|
||||
- 在玩家客户端UI初始化完成时,向服务器发送TestEvent事件,参数任意。
|
||||
- 监听服务器TestServerEvent,并打印信息到控制台。
|
||||
|
||||
会用到以下两个函数:
|
||||
|
||||
- [NotifyToServer](https://mc.163.com/dev/mcmanual/mc-dev/mcdocs/1-ModAPI/%E6%8E%A5%E5%8F%A3/%E9%80%9A%E7%94%A8/%E4%BA%8B%E4%BB%B6.html?key=NotifyToServer&docindex=1&type=0)
|
||||
- [ListenForEvent](https://mc.163.com/dev/mcmanual/mc-dev/mcdocs/1-ModAPI/%E6%8E%A5%E5%8F%A3/%E9%80%9A%E7%94%A8/%E4%BA%8B%E4%BB%B6.html?key=ListenForEvent&docindex=5&type=0)
|
||||
|
||||
除此之外,还有更多的事件相关的接口,可以参考[官方文档](https://mc.163.com/dev/mcmanual/mc-dev/mcdocs/1-ModAPI/%E6%8E%A5%E5%8F%A3/%E9%80%9A%E7%94%A8/%E4%BA%8B%E4%BB%B6.html?catalog=1)。
|
||||
|
||||
### 向服务器发送TestEvent事件
|
||||
|
||||
在创建项目后的模板中,已经生成了监听UI初始化完成的事件,我们可以直接在这个事件的回调函数中向服务器通信。
|
||||
|
||||
直接使用NotifyToServer函数即可。发送的数据是一个Python字典。
|
||||
|
||||
```python
|
||||
# UI加载完成
|
||||
def OnUiInitFinished(self, args):
|
||||
logger.info("%s OnUiInitFinished", ModConst.ClientSystemName)
|
||||
self.NotifyToServer("TestEvent", {"data": "测试数据"})
|
||||
```
|
||||
|
||||
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> |
|
||||
|
||||
### 监听服务器TestServerEvent事件
|
||||
|
||||
我们可以在客户端系统初始化时,监听这个事件并注册回调函数。在Destroy时注销监听。
|
||||
|
||||
```python
|
||||
def __init__(self, namespace, systemName):
|
||||
ClientSystem.__init__(self, namespace, systemName)
|
||||
self.mUIMgr = uiMgr.UIMgr()
|
||||
|
||||
self.ListenForEvent(clientApi.GetEngineNamespace(), clientApi.GetEngineSystemName(), ModConst.UiInitFinishedEvent, self, self.OnUiInitFinished)
|
||||
self.ListenForEvent(ModConst.ModName, ModConst.ServerSystemName, "TestServerEvent", self, self.OnServerEvent)
|
||||
|
||||
def OnServerEvent(self, args):
|
||||
print "OnServerEvent", args
|
||||
|
||||
def Destroy(self):
|
||||
self.UnListenForEvent(ModConst.ModName, ModConst.ServerSystemName, "TestServerEvent", self, self.OnServerEvent)
|
||||
self.UnListenForEvent(clientApi.GetEngineNamespace(), clientApi.GetEngineSystemName(), ModConst.UiInitFinishedEvent, self, self.OnUiInitFinished)
|
||||
if self.mUIMgr:
|
||||
self.mUIMgr.Destroy()
|
||||
```
|
||||
|
||||
在这里,我们监听的命名空间,引用了ModConst中的ModName,对应Spigot插件中的命名空间`testMod`。还引用了ModConst中的ServerSystemName,对应Spigot插件中的系统名`testModDev`。因此这个监听函数将会正常监听来自服务器的信息。
|
||||
|
||||
## 部署测试
|
||||
|
||||
至此我们完成了客户端与服务端之间双端通信的最基础的实现。接下来将客户端模组进行部署,进入服务器测试。
|
||||
|
||||
找到服务器配置->游戏配置->协议服,勾选testMod。进行部署。
|
||||
|
||||

|
||||
|
||||
随后点击启动测试,进入游戏。并输入指令/apollotest
|
||||
|
||||
可以看到服务器控制台正常输出
|
||||
|
||||

|
||||
|
||||
客户端控制台也正常输出。
|
||||
|
||||

|
||||
|
||||
Python命令行执行:
|
||||
|
||||
```python
|
||||
"OnServerEvent {'msg': '\xe8\xbf\x99\xe6\x98\xaf\xe4\xb8\x80\xe6\x9d\xa1\xe6\x9d\xa5\xe8\x87\xaaJava\xe6\x9c\x8d\xe7\x9a\x84\xe6\xb6\x88\xe6\x81\xaf'}".decode("utf-8")
|
||||
```
|
||||
|
||||
```python
|
||||
u"OnServerEvent {'msg': '\u8fd9\u662f\u4e00\u6761\u6765\u81eaJava\u670d\u7684\u6d88\u606f'}"
|
||||
```
|
||||
|
||||
消息经过utf8解码,是我们传输的消息
|
||||
|
||||
159
docs/mconline/60-我的世界创造营教程/网络游戏开服教程/1-服务器与客户端的通信/2-作业.md
Normal file
159
docs/mconline/60-我的世界创造营教程/网络游戏开服教程/1-服务器与客户端的通信/2-作业.md
Normal file
@@ -0,0 +1,159 @@
|
||||
# 作业
|
||||
|
||||
<iframe src="https://cc.163.com/act/m/daily/iframeplayer/?id=64818c70c31a9c0f360dc5c0" width="800" height="600" allow="fullscreen"/>
|
||||
|
||||
学习了服务端与客户端之间的基本通信方法之后,我们可以尝试来完成一个使用模组SDK来实现的全息字的功能。
|
||||
|
||||
## 要求
|
||||
|
||||
在玩家加入游戏之后,在玩家客户端的指定位置生成一个文字面板(全息字)。
|
||||
|
||||
整个通信流程应该如下图所示:
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
客户端->>服务端: 客户端加载完毕
|
||||
服务端-->>客户端: 生成文字面板的参数
|
||||
客户端->>服务端: 生成文字面板,返回生成是否成功
|
||||
```
|
||||
|
||||
> **为什么需要在客户端加载完毕的时候主动通知服务端,而不是直接监听服务端的PlayerJoinEvent?**
|
||||
>
|
||||
> 因为在PlayerJoinEvent触发的时候,基岩版客户端可能还没有完全加载完毕。
|
||||
>
|
||||
> 在这个时候给客户端发送事件,有可能客户端模组还没有初始化完成,无法处理请求。
|
||||
|
||||
## 实现过程
|
||||
|
||||
### Spigot插件
|
||||
|
||||
首先新建Spigot项目,操作步骤和之前一致。这里新建了一个名为TutorialHologram的项目,并配置pom.xml,添加SpigotMaster插件的maven依赖。
|
||||
|
||||
在开始编写插件之前,我们预先定义好,客户端的命名空间为`testHologram`,因此,根据[开发规范](https://mc.163.com/dev/mcmanual/mc-dev/mcguide/27-%E6%89%8B%E6%9C%BA%E7%BD%91%E7%BB%9C%E6%B8%B8%E6%88%8F/%E8%AF%BE%E7%A8%8B5%EF%BC%9A%E6%8F%92%E4%BB%B6%E6%95%99%E5%AD%A6/%E7%AC%AC1%E8%8A%82%EF%BC%9A%E5%AE%98%E7%BD%91%E6%8F%92%E4%BB%B6%E8%A7%84%E8%8C%83.html?catalog=1),客户端系统名应为`testHologramBeh`,服务端系统名应为`testHologramDev`。
|
||||
|
||||
- 客户端通知加载完毕的事件定义为`ClientLoadFinishEvent`
|
||||
- 传输生成文字面板的参数的事件定义为`HologramParameterEvent`
|
||||
- 返回生成结果的事件定义为`HologramGeneratedEvent`
|
||||
|
||||
推荐将这部分命名空间和事件名定义为常量。
|
||||
|
||||
> 教程为了方便将所有代码都写入主类,在实际开发过程中,不推荐将所有代码写入一个类中!!
|
||||
|
||||
```java
|
||||
private final String NAMESPACE = "testHologram";
|
||||
private final String CLIENT_SYSTEM_NAME = "testHologramBeh";
|
||||
private final String SERVER_SYSTEM_NAME = "testHologramDev";
|
||||
private final String CLIENT_LOAD_FINISH_EVENT = "ClientLoadFinishEvent";
|
||||
private final String HOLOGRAM_PARAMETER_EVENT = "HologramParameterEvent";
|
||||
private final String HOLOGRAM_GENERATED_EVENT = "HologramGeneratedEvent";
|
||||
```
|
||||
|
||||
首先,生成一个文字面板,需要提供这个文字面板的坐标,文本的信息,我们在作业中可以简单地硬编码坐标的位置和文本的内容。例如坐标为`(0,100,0)`,内容为`这是一个文字面板`。
|
||||
|
||||
在监听ClientLoadFinishEvent后,发送文字面板参数信息。同时监听HologramGeneratedEvent,输出结果。
|
||||
|
||||
```java
|
||||
@Override
|
||||
public void onEnable() {
|
||||
spigotMaster = (SpigotMaster) Bukkit.getPluginManager().getPlugin("SpigotMaster");
|
||||
spigotMaster.listenForEvent(NAMESPACE, CLIENT_SYSTEM_NAME, CLIENT_LOAD_FINISH_EVENT, (player, map) -> {
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("x", 0);
|
||||
data.put("y", 100);
|
||||
data.put("z", 0);
|
||||
data.put("text", "这是一个文字面板");
|
||||
spigotMaster.notifyToClient(player, NAMESPACE, SERVER_SYSTEM_NAME, HOLOGRAM_PARAMETER_EVENT, data);
|
||||
});
|
||||
spigotMaster.listenForEvent(NAMESPACE, CLIENT_SYSTEM_NAME, HOLOGRAM_GENERATED_EVENT, (player, map) -> {
|
||||
boolean success = (boolean) map.get("suc");
|
||||
getLogger().info("生成全息字 " + player.getName() + " " + success);
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
在HologramGeneratedEvent中,我们可以监听来自客户端的事件,并从中获取suc的值,来判断生成是否成功,可以继续拓展插件的功能。
|
||||
|
||||
这样我们的Spigot插件部分的代码就编写完成了,可以构建后装入服务器。
|
||||
|
||||
### 客户端模组
|
||||
|
||||
和之前的操作一样,新建一个插件,团队名称填写test,模组名填写hologram,勾选游戏服和大厅服。
|
||||
|
||||
生成完成后打开插件文件夹,删除`developer_mods`文件夹里的内容,并将整个插件文件夹复制到部署设置的模组目录中。
|
||||
|
||||
#### 文字面板的生成
|
||||
|
||||
文字面板的API文档 [点我](https://mc.163.com/dev/mcmanual/mc-dev/mcdocs/1-ModAPI/%E6%8E%A5%E5%8F%A3/%E7%89%B9%E6%95%88/%E6%96%87%E5%AD%97%E9%9D%A2%E6%9D%BF.html?catalog=1)
|
||||
|
||||
通过文档的查阅,我们需要先后
|
||||
|
||||
1. 创建文字面板
|
||||
2. 设置文字面板的位置
|
||||
3. 如有需要的话 返回服务器文字面板的ID
|
||||
|
||||
#### 常量定义
|
||||
|
||||
之前在Spigot服插件编写的过程中已经定义了一些事件名常量,方便起见,我们也需要在客户端模组的常量文件中定义相同的常量。
|
||||
|
||||
`hologramConst.py`文件中定义下列变量
|
||||
|
||||
```python
|
||||
ClientLoadFinishEvent = "ClientLoadFinishEvent"
|
||||
HologramParameterEvent = "HologramParameterEvent"
|
||||
HologramGeneratedEvent = "HologramGeneratedEvent"
|
||||
```
|
||||
|
||||
#### 功能实现
|
||||
|
||||
首先在OnUiInitFinished函数中,向服务端系统发送事件。
|
||||
|
||||
```python
|
||||
# UI加载完成
|
||||
def OnUiInitFinished(self, args):
|
||||
logger.info("%s OnUiInitFinished", HologramConst.ClientSystemName)
|
||||
self.NotifyToServer(HologramConst.ClientLoadFinishEvent, {})
|
||||
```
|
||||
|
||||
接下来监听来自服务端系统的`HologramParameterEvent`事件,并设置回调函数。获取坐标,创建文字面板。
|
||||
|
||||
部分代码如下
|
||||
|
||||
```python
|
||||
def __init__(self, namespace, systemName):
|
||||
ClientSystem.__init__(self, namespace, systemName)
|
||||
self.mUIMgr = uiMgr.UIMgr()
|
||||
self.mTextBoardComp = clientApi.GetEngineCompFactory().CreateTextBoard(clientApi.GetLevelId())
|
||||
|
||||
self.ListenForEvent(HologramConst.ModName, HologramConst.ServerSystemName, HologramConst.HologramParameterEvent, self, self.OnHologramParameter)
|
||||
self.ListenForEvent(clientApi.GetEngineNamespace(), clientApi.GetEngineSystemName(), HologramConst.UiInitFinishedEvent, self, self.OnUiInitFinished)
|
||||
|
||||
def OnHologramParameter(self, args):
|
||||
x = args["x"]
|
||||
y = args["y"]
|
||||
z = args["z"]
|
||||
text = args["text"]
|
||||
boardId = self.mTextBoardComp.CreateTextBoardInWorld(text, (1, 1, 1, 1), (0.5, 0.5, 0.5, 0.1), True)
|
||||
if not boardId:
|
||||
self.NotifyToServer(HologramConst.HologramGeneratedEvent, {"suc": False})
|
||||
return
|
||||
self.mTextBoardComp.SetBoardPos(boardId, (x, y, z))
|
||||
self.NotifyToServer(HologramConst.HologramGeneratedEvent, {"suc": True, "boardId": boardId})
|
||||
|
||||
def Destroy(self):
|
||||
self.UnListenForEvent(HologramConst.ModName, HologramConst.ServerSystemName, HologramConst.HologramParameterEvent, self, self.OnHologramParameter)
|
||||
self.UnListenForEvent(clientApi.GetEngineNamespace(), clientApi.GetEngineSystemName(), HologramConst.UiInitFinishedEvent, self, self.OnUiInitFinished)
|
||||
if self.mUIMgr:
|
||||
self.mUIMgr.Destroy()
|
||||
```
|
||||
|
||||
OnHologramParameter函数,会解析来自服务端的数据,在指定xyz坐标创建文字面板,如果失败,返回信息中suc是False。如果成功,suc为True,并附带文字面板的id。
|
||||
|
||||
## 部署测试
|
||||
|
||||
前往服务器配置,协议服,勾选刚刚编写的testHologram模组,重新部署后进入游戏进行测试。
|
||||
|
||||

|
||||
|
||||
进入游戏后,传送到坐标(0,100,0)附近,可以看到我们生成的文字面板。
|
||||
|
||||

|
||||
BIN
docs/mconline/60-我的世界创造营教程/网络游戏开服教程/1-服务器与客户端的通信/images/01.png
LFS
Normal file
BIN
docs/mconline/60-我的世界创造营教程/网络游戏开服教程/1-服务器与客户端的通信/images/01.png
LFS
Normal file
Binary file not shown.
BIN
docs/mconline/60-我的世界创造营教程/网络游戏开服教程/1-服务器与客户端的通信/images/02.png
LFS
Normal file
BIN
docs/mconline/60-我的世界创造营教程/网络游戏开服教程/1-服务器与客户端的通信/images/02.png
LFS
Normal file
Binary file not shown.
BIN
docs/mconline/60-我的世界创造营教程/网络游戏开服教程/1-服务器与客户端的通信/images/03.png
LFS
Normal file
BIN
docs/mconline/60-我的世界创造营教程/网络游戏开服教程/1-服务器与客户端的通信/images/03.png
LFS
Normal file
Binary file not shown.
BIN
docs/mconline/60-我的世界创造营教程/网络游戏开服教程/1-服务器与客户端的通信/images/04.png
LFS
Normal file
BIN
docs/mconline/60-我的世界创造营教程/网络游戏开服教程/1-服务器与客户端的通信/images/04.png
LFS
Normal file
Binary file not shown.
BIN
docs/mconline/60-我的世界创造营教程/网络游戏开服教程/1-服务器与客户端的通信/images/05.png
LFS
Normal file
BIN
docs/mconline/60-我的世界创造营教程/网络游戏开服教程/1-服务器与客户端的通信/images/05.png
LFS
Normal file
Binary file not shown.
BIN
docs/mconline/60-我的世界创造营教程/网络游戏开服教程/1-服务器与客户端的通信/images/06.png
LFS
Normal file
BIN
docs/mconline/60-我的世界创造营教程/网络游戏开服教程/1-服务器与客户端的通信/images/06.png
LFS
Normal file
Binary file not shown.
BIN
docs/mconline/60-我的世界创造营教程/网络游戏开服教程/1-服务器与客户端的通信/images/07.png
LFS
Normal file
BIN
docs/mconline/60-我的世界创造营教程/网络游戏开服教程/1-服务器与客户端的通信/images/07.png
LFS
Normal file
Binary file not shown.
BIN
docs/mconline/60-我的世界创造营教程/网络游戏开服教程/1-服务器与客户端的通信/images/08.png
LFS
Normal file
BIN
docs/mconline/60-我的世界创造营教程/网络游戏开服教程/1-服务器与客户端的通信/images/08.png
LFS
Normal file
Binary file not shown.
BIN
docs/mconline/60-我的世界创造营教程/网络游戏开服教程/1-服务器与客户端的通信/images/09.png
LFS
Normal file
BIN
docs/mconline/60-我的世界创造营教程/网络游戏开服教程/1-服务器与客户端的通信/images/09.png
LFS
Normal file
Binary file not shown.
BIN
docs/mconline/60-我的世界创造营教程/网络游戏开服教程/1-服务器与客户端的通信/images/10.png
LFS
Normal file
BIN
docs/mconline/60-我的世界创造营教程/网络游戏开服教程/1-服务器与客户端的通信/images/10.png
LFS
Normal file
Binary file not shown.
BIN
docs/mconline/60-我的世界创造营教程/网络游戏开服教程/1-服务器与客户端的通信/images/11.png
LFS
Normal file
BIN
docs/mconline/60-我的世界创造营教程/网络游戏开服教程/1-服务器与客户端的通信/images/11.png
LFS
Normal file
Binary file not shown.
BIN
docs/mconline/60-我的世界创造营教程/网络游戏开服教程/1-服务器与客户端的通信/images/12.png
LFS
Normal file
BIN
docs/mconline/60-我的世界创造营教程/网络游戏开服教程/1-服务器与客户端的通信/images/12.png
LFS
Normal file
Binary file not shown.
BIN
docs/mconline/60-我的世界创造营教程/网络游戏开服教程/1-服务器与客户端的通信/images/13.png
LFS
Normal file
BIN
docs/mconline/60-我的世界创造营教程/网络游戏开服教程/1-服务器与客户端的通信/images/13.png
LFS
Normal file
Binary file not shown.
BIN
docs/mconline/60-我的世界创造营教程/网络游戏开服教程/1-服务器与客户端的通信/images/14.png
LFS
Normal file
BIN
docs/mconline/60-我的世界创造营教程/网络游戏开服教程/1-服务器与客户端的通信/images/14.png
LFS
Normal file
Binary file not shown.
BIN
docs/mconline/60-我的世界创造营教程/网络游戏开服教程/1-服务器与客户端的通信/images/15.png
LFS
Normal file
BIN
docs/mconline/60-我的世界创造营教程/网络游戏开服教程/1-服务器与客户端的通信/images/15.png
LFS
Normal file
Binary file not shown.
BIN
docs/mconline/60-我的世界创造营教程/网络游戏开服教程/1-服务器与客户端的通信/images/16.png
LFS
Normal file
BIN
docs/mconline/60-我的世界创造营教程/网络游戏开服教程/1-服务器与客户端的通信/images/16.png
LFS
Normal file
Binary file not shown.
BIN
docs/mconline/60-我的世界创造营教程/网络游戏开服教程/1-服务器与客户端的通信/images/17.png
LFS
Normal file
BIN
docs/mconline/60-我的世界创造营教程/网络游戏开服教程/1-服务器与客户端的通信/images/17.png
LFS
Normal file
Binary file not shown.
Reference in New Issue
Block a user