游戏编程研究院

服务器编程(状态)

2018-12-16  本文已影响1人  Godan
传统模式:蓝色-判断 绿色-消息处理 红色-错误处理

图中上方的3个判断,从左到右逐步叠加,至最后“坐下人数是否满足开始条件”时,需要先判断“是否登录”、“是否在房间”。如果采用公共判断函数,也最好将每个判断独立出来,并在每个消息处理函数里叠加使用。

通常多个消息处理需要同样的条件判断。我们可以把这些共有的判断封装到一些函数里。

// 公共判断函数

// 是否登录
function isLogin(){}
// 是否在房间
function isInRoom(){}
// 坐下人数是否满足开始条件
function isCanStart(){}

当实现上图底部的消息请求处理函数时,会用到这些公共判断函数。如果,某个消息请求处理函数漏掉其中一个判断,就有可能造就利用它的外挂,并且这样的疏忽在编程阶段难以体现出来,而利用严格完备的单元测试和代码review,来检查这样的漏洞势必花费大量的精力。

// 假如获取房间里玩家信息消息请求处理函数
// 漏掉isInRoom()是否在房间的判断
// 那么就可以制作一种外挂获得每个房间里当前玩家信息

// 更糟糕的是,游戏时没有检查玩家是否在房间中
// 外挂模拟的客户端直接加入到游戏中,成为
// 其他玩家无法感知的幽灵玩家
// 外挂模拟的玩家,由于其数据的不完备
// 可能让服务器异常,破坏结算的一致性

看来需要一些更好的手段,最好在开发时就规避这样的风险,像红外对射传感器般,在有人阻断对射时立刻发出报警。

状态模式:紫色-状态 蓝色-判断 绿色-消息处理 红色-错误处理

状态模式中,增加状态的概念(紫色),希望明确的区分每个请求消息属于哪个状态,并且需要满足哪个必备条件(状态右上方的判断)。

每个状态有一个必备条件,支持处理一种或多种消息请求。

// 状态需要实现的方法

// 每个状态拥有独一无二的名称或ID
function name() {}
// 每个状态都有一个必备条件
function isEnough(playerData)  {}

// 每个状态有一个或多个消息请求处理函数
function handleXXXMsg(msg) {}

// 注册消息请求处理函数到某个状态
states.register(stateN, handleXXXMsg)
// ...

你必须先实现状态必备条件isEnough(playerData)方法,并给它安排一个恰当的顺序,否则消息处理就会失败,这样在开发周期中问题就必备条件检测问题立刻暴露出来。

将这个4个状态按顺序存放,玩家的每次请求,都一次拿状态的必备条件去检测,一旦请求满足某个条件后,就认为玩家属于这个条件对应的状态(叠加的判断)。然后尝试分发到这个状态支持的消息请求处理(分隔消息处理),分发失败则丢弃。

// 自动更新玩家状态,处理消息请求前或后执行
function autoChangePlayerState(player) {
  var find = false
  for (n = 0; n < states.length; n++) {
    if (states[n].isEnough(player.data)) {
      player.setState(state)
      find = true
      break
    }
  }
  if (!find) {
    console.error("没有为玩家", player.id, "指定正确的状态")
  }
}

// 处理客户端请求
function onClientRequest(player, msg) {
  autoChangePlayerState(player)
  player.currentState().process(msg)
}

// 处理请求,玩家的成员方法
function process(msg) {
  handle = this.handlers[msg.name]
  if (!handle) {
    console.log("无法处理消息", msg.name)
    return
  }

  // 调用处理函数
  handle(msg)
}
上一篇 下一篇

猜你喜欢

热点阅读