Files
netease-bedrock-wiki/mconline/30-网络服插件教程/3-插件知识进阶/2-服务器通信.md
2025-08-25 18:36:29 +08:00

247 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 服务器通信
服务器之间的信息交换是开发服务器非常重要的一个环节,本节将对部分通信相关的接口进行说明。
本节所使用的所有接口都可以在开发者文档上找到具体说明。
地址http://mc.163.com/dev/mcmanual/mc-dev/mcdocs/2-Apollo/4-SDK/8-%E6%9C%8D%E5%8A%A1%E5%99%A8%E9%80%9A%E4%BF%A1.html
### 举例说明
#### 需求
制作一个每日签到系统
#### 通信流程
- 玩家登陆后Game/Lobby向Service发送玩家的uidService收到请求后查询玩家今天是否登陆过并返回数据给Game/Lobby。
- Game/Lobby收到数据后向Client发送某个打开UI的事件数据作为参数一同发送。
- Client收到打开UI的事件解析参数并调用ScreenNode创建UI显示到玩家客户端上。
- 玩家点击UI上的签到按钮Client向Game/Lobby发送签到事件。
- Game/Lobby收到签到事件向Service发送玩家签到事件。
- Service收到请求确认玩家当天没签到过并更新签到状态并回应Game/Lobby签到是否成功。
- Game/Lobby收到会用后调用回调根据参数判断签到是否成功成功的话给玩家发送签到奖励。
## 客户端UI
客户端UI相关的示例代码将会截取neteaseDaily插件的部分代码进行举例可以自行下载并在插件中找到对应代码进行学习。
客户端首先需要在初始化时使用下方代码监听Game/Lobby发来的打开界面请求。
然后将事件参数提供给ScreenNode做出相关解析。
dailyClientSystem.py
```python
# -*- coding: utf-8 -*-
import neteaseDailyScript.dailyConst as dailyConst
import client.extraClientApi as clientApi
ClientSystem = clientApi.GetClientSystemCls()
class DailyClientSystem(ClientSystem):
"""
该mod的客户端类
与服务端类通信
显示每日登录奖励的状态
"""
def __init__(self, namespace, systemName):
ClientSystem.__init__(self, namespace, systemName)
self.mPlayerId = clientApi.GetLocalPlayerId()
self.mDailyUINode = None
self.ListenForEvent(clientApi.GetEngineNamespace(), clientApi.GetEngineSystemName(), dailyConst.UiInitFinishedEvent, self, self.OnUiInitFinished)
self.ListenForEvent(dailyConst.ModName, dailyConst.ServerSystemName, dailyConst.DisplayDailyRewardEvent, self, self.OnDisplayDailyReward) # 服务端调用接口打开每日登录奖励界面
self.ListenForEvent(dailyConst.ModName, dailyConst.ServerSystemName, dailyConst.PlayerRecvEvent, self, self.OnPlayerRecv) # 玩家正常领取奖励后更新每日登录奖励界面
def Destroy(self):
self.mDailyUINode = None
self.UnListenForEvent(clientApi.GetEngineNamespace(), clientApi.GetEngineSystemName(), dailyConst.UiInitFinishedEvent, self, self.OnUiInitFinished)
self.UnListenForEvent(dailyConst.ModName, dailyConst.ServerSystemName, dailyConst.DisplayDailyRewardEvent, self, self.OnDisplayDailyReward)
self.UnListenForEvent(dailyConst.ModName, dailyConst.ServerSystemName, dailyConst.PlayerRecvEvent, self, self.OnPlayerRecv)
def OnUiInitFinished(self, *args):
# 注册UI 详细解释参照《UI API》
clientApi.RegisterUI(dailyConst.ModName, dailyConst.dailyUIName, dailyConst.dailyUIClsPath, dailyConst.dailyUIScreenDef)
# 创建UI 详细解释参照《UI API》
clientApi.CreateUI(dailyConst.ModName, dailyConst.dailyUIName, {"isHud": 1})
self.mDailyUINode = clientApi.GetUI(dailyConst.ModName, dailyConst.dailyUIName)
if self.mDailyUINode:
self.mDailyUINode.InitScreen()
else:
print '==== %s ====' % 'create UI: %s failed' % dailyConst.dailyUIScreenDef
def OnDisplayDailyReward(self, data):
print 'OnDisplayDailyReward', data
if self.mDailyUINode:
self.mDailyUINode.open(data)
#从服务器收到打开客户端UI的请求调用ScreenNode并传输数据data打开界面
def OnPlayerRecv(self, data):
print 'OnPlayerRecv', data
if self.mDailyUINode:
self.mDailyUINode.refresh(data)
```
在UI逻辑代码中编写点击领取按钮的相关逻辑代码。下方的代码就是确定领取的最关键的代码将请求发送到服务器。
可以看到其实最主要还是使用了ClientSystem#NotifyToServer函数,来传输请求到服务器。
dailyClientUI.py
```python
def recv(self):
print 'recv', self.m_valid_date
clientApi.GetSystem(dailyConst.ModName, dailyConst.ClientSystemName).NotifyToServer(
dailyConst.PlayerRecvEvent,
{'playerId': clientApi.GetLocalPlayerId(), 'valid': self.m_valid_date} # 传递服务端发送下来的日期过去验证,表示领取的是同一天的每日登录奖励
)
```
## 客户端和Game/Lobby通信
这个通信接口和开发Mod所使用的接口是同一个读者应该都对这个接口不陌生。
使用这个接口,客户端和服务器之间可以双向发送消息,具体使用方式如下。
```python
#服务端给客户端发送消息的示例
#client mod
class testClient(ClientSystem):
def __init__(self,namespace,systemName):
ClientSystem.__init__(self, namespace, systemName)
self.ListenForEvent('serverNamespace', 'serverSystem', 'PlayerJoinOKEvent', self, self.OnPlayerJoinOK)
def OnPlayerJoinOK(self, args):
#args的结果为{'uid':123, 'name':'nickname'}
print 'OnPlayerJoinOK', args
#game/lobby mod
class testServer(ServerSystem):
def __init__(self, namespace, systemName):
ServerSystem.__init__(self, namespace, systemName)
def testNotifyClient(self):
player = {}
player['uid'] = 123
player['name'] = 'nickname'
playerId = '456'
self.NotifyToClient(playerId, "PlayerJoinOKEvent", player)
```
```python
#客户端给服务端发送消息的示例
#client mod
class testClient(ClientSystem):
def __init__(self,namespace,systemName):
ClientSystem.__init__(self, namespace, systemName)
def testNotifyServer(self):
player = {}
player['uid'] = 123
player['name'] = 'nickname'
self.NotifyToServer("PlayerJoinEvent", data)
#game/lobby mod
class testServer(ServerSystem):
def __init__(self, namespace, systemName):
ServerSystem.__init__(self, namespace, systemName)
self.ListenForEvent('clientNamespace', 'clientSystem', 'PlayerJoinEvent', self, self.OnPlayerJoin)
def OnPlayerJoin(self, args):
#args结果为{'uid':123, 'name':'nickname'}
print 'OnPlayerJoin', args
```
## Service和Service/Master通信
此类通信通常用于不同服务器节点之间的数据传输,具体使用方法如下。
```python
#service同master通信示例.service同service通信与此类似这里不重复了
#service mod
class testService(ServiceSystem):
def __init__(self,namespace,systemName):
ServiceSystem.__init__(self, namespace, systemName)
#注册service方法注意一个事件只能注册一次否则后面监听函数会覆盖前面监听函数
self.RegisterRpcMethodForMod('PlayerJoinOKEvent', self.OnPlayerJoinOK)
def OnPlayerJoinOK(self, serverId, callbackId, args):
#args的结果为{'uid':123, 'name':'nickname'}
print 'OnPlayerJoinOK', args
response = {}
response['result'] = 1
self.ResponseToServer(serverId, callbackId, response)
#master mod
class masterServer(MasterSystem):
def __init__(self, namespace, systemName):
MasterSystem.__init__(self, namespace, systemName)
def OnCallback(self, suc, args):
#若成功suc=Trueargs= {'result' : 1}
#若超时则suc为False
if not suc:
print 'OnCallback timeout'
return
print 'OnCallback success', args
def testNotifyService(self):
player = {}
player['uid'] = 123
player['name'] = 'nickname'
self.RequestToServiceMod("idv_service", "PlayerJoinOKEvent", data, self.OnCallback, 2)
```
在上方的示例代码中调用了3个相关的接口下面将解释这3个接口的具体使用方法。
### RegisterRpcMethodForMod
service接口用来监听master发来的请求并将请求参数带入回调函数。
如上方代码所示service在收到**PlayerJoinOKEvent**后,将会触发**OnPlayerJoinOK**函数。
### ResponseToServer
service接口用来回应master的通信请求。
在上方代码中service服务器收到了请求进行处理后会将操作成功的结果再次发送回master服务器同时调用master的回调函数**OnCallback**。
如果请求没有在2秒钟内完成则会判定超时。
超时秒数可以在master的函数中进行配置可根据自己的需要自行调整。
### RequestToServiceMod
master接口用来发送数据到指定的service服务器。
其中第一个参数为module需要提前在service的mod.json中配置好module_names。
其他参数均没有什么需要特别注意的地方,参考官方文档,即可查询相关参数的具体含义。链接在本页顶部。
## Service和Lobby/Game通信
**Service和Lobby/Game**之间的通信方式,基本与**Service和Service/Master通信**一致。都是使用的```RegisterRpcMethodForMod```、```ResponseToServer```、```RequestToServiceMod```上方提到的这3个接口。demo也基本一致下方代码供参考。
```python
#service同game/lobby通信示例
#service mod
class testService(ServiceSystem):
def __init__(self,namespace,systemName):
ServiceSystem.__init__(self, namespace, systemName)
self.RegisterRpcMethodForMod("idv_service", 'PlayerJoinOKEvent', self.OnPlayerJoinOK)
def OnPlayerJoinOK(self, serverId, callbackId, args):
#args的结果为{'uid':123, 'name':'nickname'}
print 'OnPlayerJoinOK', args
#lobby mod
class lobbyServer(ServerSystem):
def __init__(self, namespace, systemName):
ServerSystem.__init__(self, namespace, systemName)
def testNotifyService(self):
player = {}
player['uid'] = 123
player['name'] = 'nickname'
self.RequestToServiceMod("idv_service", "PlayerJoinOKEvent", args)
```
除此之外Service和Lobby/Game之间还有更多接口可供调用。
具体可以点击<a href="../../../mcdocs/2-Apollo/4-SDK/8-服务器通信.html#service和game/lobby通信" rel="noopenner"> 这里 </a>查看。