--- front: https://mc.res.netease.com/pc/zt/20201109161633/mc-dev/assets/img/wps13.a2434b82.jpg hard: 进阶 time: 20分钟 --- # 游戏外功能 ## 运营指令 运营指令:接收外部http请求,处理游戏相关逻辑,比如给某个玩家发物品,公告等。 master是运营执行入口,开发者可以根据需要,把请求分发到lobby/game/service。下面给AwesomeGame新增一个运营指令,功能是打印指定玩家信息。 ### 获取玩家数据运营指令 由于lobby是异步定时存档的,因此mysql数据可能不是最新的。这里实现方案是: * 玩家在lobby,从对应lobby的内存中拉取玩家数据。 * 玩家不在lobby,选择任意一个可用lobby,从db中读取玩家数据。 处理过程如下所示: ![img](./images/wps13.jpg) master主要接受请求然后转发,核心代码如下所示: ```python class AwesomeMaster(MasterSystem): def __init__(self, namespace, systemName): MasterSystem.__init__(self, namespace, systemName) # 注册gm指令 masterHttp.RegisterMasterHttp('/get-user-info', self, self.OnGetUserInfo) self.DefineEvent('GetUserInfoRequestEvent') self.ListenForEvent( 'Minecraft', 'AwesomeLobby', 'GetUserInfoResponseEvent', self, self.OnGetUserInfoResponse ) def OnGetUserInfo(self, client_id, request_body): ''' 获取gm指令 ''' import ujson as json request = json.loads(request_body) uid = request['uid'] redis_key_player = "online_user_%d" % uid #获取玩家在线状态 redisPool.AsyncHgetall( redis_key_player, lambda record:self._GetUserInfoCb(client_id, uid, record) ) def _GetUserInfoCb(self, client_id, uid, record): ''' 回调函数。获取目标lobby,向lobby请求在线人数。 ''' serverid = None serverlistConf = masterConf.netgameConf['serverlist'] if record: #若玩家在game中,则随机从一个lobby获取在线人数。 serverid = record.get('serverid', None) tmpServerConf = masterConf.serverListMap.get(serverid, None) if not tmpServerConf or tmpServerConf['type'] != 'lobby': serverid = None if not serverid: for serverConf in serverlistConf: #服务器可用且是lobby if serverConf['type'] == 'lobby' \ and serverManager.IsValidServer(serverConf['serverid']): serverid = serverConf['serverid'] break if not serverid: response = self.makeFailResponse(master_http.HTTP_CODE_FAIL, 'no valid lobby.') masterHttp.SendHttpResponse(client_id, response) return request_data = {'uid' : uid, 'client_id' : client_id} self.NotifyToServerNode(serverid, 'GetUserInfoRequestEvent', request_data) def OnGetUserInfoResponse(self, args): ''' 接受玩家数据,返回http请求。 ''' client_id = args['client_id'] entity = args['user_info'] response = self.makeResponse(master_http.HTTP_CODE_SUCCESS, '', entity) masterHttp.SendHttpResponse(client_id, response) ``` Lobby主要获取玩家数据,核心代码如下所示: ```python class AwesomeServer(ServerSystem): def __init__(self, namespace, systemName): ServerSystem.__init__(self, namespace, systemName) self.ListenForEvent( modConfig.Minecraft, modConfig.MasterSystemName, modConfig.GetUserInfoRequestEvent, self, self.OnGetUserInfoRequest) def OnGetUserInfoRequest(self, args): ''' 获取玩家数据。 ''' uid = args['uid'] client_id = args['client_id'] player_data = self.mPlayerMap.get(uid, None) if not player_data: if self.mDBType == DbType.Mongo: self.mMongoMgr.QueryPlayerData( uid, uid, lambda data: self._OnGetUserInfoRequestCb(client_id, data)) elif self.mDBType == DbType.Mysql: self.mMysqlMgr.QueryPlayerData( uid, uid, lambda data: self._OnGetUserInfoRequestCb(client_id, data)) else: self._GetUserInfoResponse(client_id, player_data.toSaveDict()) def _OnGetUserInfoRequestCb(self, client_id, record): ''' 回调函数,处理db操作结果,把玩家数据告知master。 ''' if record: player_data = playerData.PlayerData() player_data.initPlayer(-1, record) self._GetUserInfoResponse(client_id, player_data.toSaveDict()) else: self._GetUserInfoResponse(client_id, {}) def _GetUserInfoResponse(self, client_id, player_info): ''' 玩家数据告知master。 ''' response_data = {'client_id' : client_id, 'user_info' : player_info} self.NotifyToMaster('GetUserInfoResponseEvent', response_data) ``` ### 验证 登录到开发机,然后给master发送curl请求,即可获取结果,如下图示: img ## 官方运营指令 查看“服务器MOD SDK”中【运营指令】部分,里面介绍了常用的指令,比如禁言、踢人等。 ## 总结 - 运营指令的实现通常分为两个步骤: - master接受响应指令,将指令请求转发到其他服务器; - lobby/game/serivce实现指令功能。 - 官方实现了常见的运营指令,具体可以查看“服务器MOD SDK”中【运营指令】部分。