创建房间何时被触发

上一章已经提到,这里再回顾下:

  1. 客户端连接RoomServer
  2. 客户端发送MsgLogin
  3. RoomServer的PlayerTask::ParseMsg函数被触发,执行 PlayerTask::LoginVerify 函数
  4. PlayerTask::LoginVerify函数内最后,调用 RoomMgr.AddPlayer(this)

这时就触发,创建房间 或 加入房间 流程

项目中房间管理代码分析 - RoomMgr.go

RoomMgr.go,就是对房间进行管理,类似 PlayerTaskMgr.go的功能

  • 创建房间

    func (this *RoomMgr) AddPlayer(player def.IPlayerTask) bool {
      room := this.getRoomById(player.UData().RoomId)
      switch player.UData().Model {
      case common.UserModelJoin:
          if room == nil {
              player.RetErrorMsg(common.ErrorCodeRoom)
              glog.Error("[房间] 加入的房间不存在 ", player.UData().Id, ",", player.UData().Account, ",", player.UData().RoomId)
              return false
          }
          glog.Info("[房间] 加入模式 [", room.RoomType(), ",", room.id, "],[", player.UData().Id, ",", player.UData().Account, "]")
      case common.UserModelTeam:
          if room == nil {
              room = this.NewRoom(player.UData().SceneId, common.RoomTypeTeam, player.UData().RoomId, player, 0)
          }
          glog.Info("[房间] 组队模式 [", room.RoomType(), ",", room.id, "],[", player.UData().Id, ",", player.UData().Account, "],[", player.UData().TeamId, ",", player.UData().TeamName, "],", room.GetPlayerNum())
      case common.UserModelQuick:
          if room == nil {
              room = this.NewRoom(player.UData().SceneId, common.RoomTypeQuick, player.UData().RoomId, player, 0)
          }
          glog.Info("[房间] 闪电模式 [", room.RoomType(), ",", room.id, "],[", player.UData().Id, ",", player.UData().Account, "],[", player.UData().TeamId, ",", player.UData().TeamName, "],", room.GetPlayerNum())
      default:
          if room == nil {
              room = this.NewRoom(player.UData().SceneId, common.RoomTypeQuick, player.UData().RoomId, player, 0)
          }
          glog.Info("[房间] 闪电模式 [", room.RoomType(), ",", room.id, "],[", player.UData().Id, ",", player.UData().Account, "],[", player.UData().TeamId, ",", player.UData().TeamName, "],", room.GetPlayerNum())
      }
    
      if !room.IsClosed() {
          player.SetRoom(room)
          room.IncPlayerNum(player)
          room.chan_AddPlayer <- player
          if !room.iscustom {
              rcenterclient.GetMe().UpdateRoom(room.RoomType(), room.id, room.GetPlayerNum(), common.UserOnline, false, 0, 0)
          }
      }
    
      glog.Info("[房间] 分配成功 [", room.RoomType(), ",", room.id, "],", player.ID(), ",", player.UData().Account, ",", player.UData().Model, ",", player.UData().IsCustom)
      return true
    }
    

    在判断没有房间时,会调用 NewRoom 函数 来说创建房间,最后到进入房间流程

  • 加入房间

    room.chan_AddPlayer <- player
    

    通过go chan通知房间主循环有玩家加入房间,触发加入房间逻辑

  • 销毁房间

    func (this *RoomMgr) timeAction() {
    var prooms []*Room
    nowTime := time.Now().Unix()
    
    this.mutex.RLock()
    rms := make([]*Room, 0)
    for _, room := range this.rooms {
        rms = append(rms, room)
    }
    this.mutex.RUnlock()
    
    this.frame++
    for _, room := range rms {
        if !room.CheckInited() {
            continue
        }
        channum := make(chan []int, 1)
        room.chan_GetPlayerNum <- channum
        num := <-channum
        playernum := num[1]
        allpalyernum := num[0] + num[1]
        if room.endTime > nowTime && playernum != 0 {
            continue
        }
        if this.frame%50 == 0 && room.RoomType() == common.RoomTypeQuick {
            if allpalyernum != 0 && room.endTime > nowTime {
                continue
            }
        }
        prooms = append(prooms, room)
    }
    for _, room := range prooms {
        this.RemoveRoom(room.id)
        if !room.iscustom {
            rcenterclient.GetMe().RemoveRoom(room.RoomType(), room.id)
        }
        rcenterclient.GetMe().UpdateServer()
        room.Control(ROOM_CONTROL_STOP)
        glog.Info("[房间]删除房间 deleteroom [", room.RoomType(), ",", room.id, "],", this.GetNum(),
            ",time:", nowTime-room.endTime, " now:", nowTime, " end:", room.endTime)
    }
    }
    

    定期检查房间是否结束,调用 room.Control(ROOM_CONTROL_STOP),关闭房间。

    room.Control(ROOM_CONTROL_STOP) 的内部实现,也是通过go chan通知房间主循环,触发房间关闭逻辑

MsgEndRoom

房间关闭后,会广播MsgEndRoom消息,给客户端。

项目中房间主循环代码分析 - Room.go

从 NewRoom -> room.Start() -> go this.Loop()。

Room::Loop 房间主循环,非常重要,房间主循环按照规定的频率,计算一局游戏的每一帧,并按规定的频率将结果反馈给客户端。

这里着重看下,房间主循环:

func (this *Room) Loop() {
    var (
        timeTicker = time.NewTicker(time.Millisecond * def.FrameTimeMS)
        tloop      uint64
    )

    defer func() {
        this.Stop()
        timeTicker.Stop()
        if err := recover(); err != nil {
            glog.Error("[异常] 房间线程出错 [", this.roomType, ",", this.id, "] ", err, "\n", string(debug.Stack()))
        }
    }()

    this.ResetFrame()

    for {
        select {
        case <-timeTicker.C:
            this.IncreaseFrame()
            this.Scene.Render()
            //100ms
            if tloop%def.FrameCountBy100MS == 0 {
                this.Render5()
            }
            //400ms
            if tloop%(def.FrameCountBy100MS*4) == 0 {
                this.SendTeamMemPos()
            }
            //1s
            if tloop%(def.FrameCountBy100MS*10) == 0 {
                this.TimeAction()
            }
            if tloop%(def.FrameCountBy100MS*40) == 0 {
                this.PushTopList(nil)
            }
            tloop += 1
        case op := <-this.chan_PlayerCmd:
            if !this.IsClosed() {
                player, ok := this.Players[op.playerID]
                if ok {
                    player.OnRecvPlayerCmd(op.playerID, op.cmd, op.data, op.flag)
                } else {
                    glog.Info("chan_PlayerCmd:no player,", op.playerID, " cmd:", op.cmd)
                }
            }
        case player := <-this.chan_AddPlayer:
            this.AddPlayer(player)
        case player := <-this.chan_RemovePlayer:
            this.RemovePlayer(player)
        case playerId := <-this.chan_RemovePlayerById:
            this.RemovePlayerByIdDetail(playerId)
        case ctrl := <-this.chan_Control:
            switch ctrl {
            case ROOM_CONTROL_END:
            case ROOM_CONTROL_STOP:
                glog.Info("[ctrl]", this.name, " ctrl: ", ctrl)
                this.destory()
            }
            return
        case c := <-this.chan_GetPlayerNum:
            rn := int(this.GetRobotNum())
            pn := len(this.Players) - rn
            c <- []int{rn, pn}
        }
    }
}

另外,上面的加入房间、销毁房间,最终都是通过go chan进入房间主循环。这里涉及到一个Go协程的编程模型。(下节内容)

results matching ""

    No results matching ""