2.6
This commit is contained in:
246
docs/mconline/30-网络服插件教程/3-插件知识进阶/2-服务器通信.md
Normal file
246
docs/mconline/30-网络服插件教程/3-插件知识进阶/2-服务器通信.md
Normal file
@@ -0,0 +1,246 @@
|
||||
# 服务器通信
|
||||
|
||||
服务器之间的信息交换是开发服务器非常重要的一个环节,本节将对部分通信相关的接口进行说明。
|
||||
|
||||
本节所使用的所有接口都可以在开发者文档上找到具体说明。
|
||||
|
||||
地址: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发送玩家的uid,Service收到请求后,查询玩家今天是否登陆过,并返回数据给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=True,args= {'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>查看。
|
||||
|
||||
Reference in New Issue
Block a user