package room import ( "cs" "f5" "github.com/golang/protobuf/proto" "main/common" "main/constant" "mt" "q5" "sort" ) type room struct { cs.MsgHandlerImpl roomId string roomIdx int64 curSortIdx int32 entry q5.ListHead config struct { mapId int32 zoneId int32 nodeId int32 passwd string maxTeamNum int32 } owner *member roomState int32 startTime int64 startReason int32 disbandTime int64 disbandReason int32 teamUuidHash map[string]*team teamIdHash map[int32]*team obTeamUuidHash map[string]*team obTeamIdHash map[int32]*team members map[string]*member startTimer *f5.TimerWp delayDeleteTimer *f5.TimerWp attacher *f5.TimerAttacher gameStartNotifyMsg *cs.SMRoomGameStartNotify } func (this *room) init(roomId string, roomIdx int64, owner common.Player, msg *cs.CMCreateRoom) { this.roomId = roomId this.roomIdx = roomIdx this.config.mapId = msg.GetMapId() this.config.zoneId = msg.GetZoneId() this.config.nodeId = msg.GetNodeId() this.config.passwd = msg.GetPasswd() this.config.maxTeamNum = constant.ROOM_MAX_TEAM_NUM this.owner = newMember(this, owner) this.addMember(this.owner) this.addTeam(newTeam(this, this.genTeamId(), msg.GetTeamUuid(), this.owner)) } func (this *room) unInit() { for len(this.teamUuidHash) > 0 { for _, t := range this.teamUuidHash { t.unInit() break } } this.attacher.ClearTimers() this.entry.DelInit() delete(_roomMgr.idHash, this.roomId) } func (this *room) addMember(m *member) { this.members[m.hum.GetAccountId()] = m m.hum.SetRoom(this) } func (this *room) addTeam(t *team) { t.sortIdx = this.genSortIdx() this.teamUuidHash[t.teamUuid] = t this.teamIdHash[t.teamId] = t } func (this *room) removeTeam(t *team) { delete(this.teamUuidHash, t.teamUuid) delete(this.teamIdHash, t.teamId) } func (this *room) addObTeam(t *team) { t.obSortIdx = this.genSortIdx() this.obTeamUuidHash[t.teamUuid] = t this.obTeamIdHash[t.teamId] = t } func (this *room) removeObTeam(t *team) { delete(this.obTeamUuidHash, t.teamUuid) delete(this.obTeamIdHash, t.teamId) } func (this *room) genSortIdx() int32 { this.curSortIdx += 1 return this.curSortIdx; } func (this *room) getMember(accountId string) *member { m, ok := this.members[accountId] if ok { return m } return nil } func (this *room) getTeamByUuid(teamUuid string) *team { t, ok := this.teamUuidHash[teamUuid] if ok { return t } return nil } func (this *room) getTeamById(teamId int32) *team { t, ok := this.teamIdHash[teamId] if ok { return t } return nil } func (this *room) getObTeamByUuid(teamUuid string) *team { t, ok := this.obTeamUuidHash[teamUuid] if ok { return t } return nil } func (this *room) getObTeamById(teamId int32) *team { t, ok := this.obTeamIdHash[teamId] if ok { return t } return nil } func (this *room) getTeamNum() int32 { return int32(len(this.teamUuidHash)) } func (this *room) getObTeamNum() int32 { return int32(len(this.obTeamUuidHash)) } func (this *room) canJoin(member common.Player, msg *cs.CMJoinRoom) bool { if this.started() { return false } if member.GetRoom() != nil { return false } if this.getMember(member.GetAccountId()) != nil { return false } if this.config.passwd != msg.GetPasswd() { return false } t := this.getTeamByUuid(msg.GetTeamUuid()) if t == nil { t = this.getObTeamByUuid(msg.GetTeamUuid()) } if t != nil { if t.isFull() { return false } else { return true } } if msg.GetIsObserver() != 0 { if this.isObTeamFull() { return false } } else { if this.isTeamFull() { return false } } return true } func (this *room) isTeamFull() bool { return this.getTeamNum() >= this.config.maxTeamNum } func (this *room) isObTeamFull() bool { return this.getObTeamNum() >= this.config.maxTeamNum } func (this *room) join(hum common.Player, msg *cs.CMJoinRoom) bool { if !this.canJoin(hum, msg) { return false } t := this.getTeamByUuid(msg.GetTeamUuid()) if t == nil { t = this.getObTeamByUuid(msg.GetTeamUuid()) } if t != nil { if t.isFull() { return false } m := newMember(this, hum) this.addMember(m) this.autoStartCountdown() return true } m := newMember(this, hum) this.addMember(m) t = newTeam(this, this.genTeamId(), msg.GetTeamUuid(), m) t.addMember(m) if msg.GetIsObserver() != 0 { this.addObTeam(t) } else { this.addTeam(t) this.autoStartCountdown() } return true } func (this *room) autoStartCountdown() { if this.getTeamNum() != this.config.maxTeamNum { return } var secondsRemaining int32 = 15 timer := f5.GetTimer() timer.SetInterval(1000, func(e int32, args *q5.Args) { if e == q5.TIMER_EXEC_EVENT { if this.getTeamNum() != this.config.maxTeamNum { timer.DeleteRunningTimer() return } if secondsRemaining > 0 { this.AutoStartGameCountdownNotify(secondsRemaining) } if secondsRemaining == 0 { this.notifyGameStart() timer.DeleteRunningTimer() return } secondsRemaining-- } }) } func (this *room) fillMFRoom(hum common.Player, pb *cs.MFRoom) { pb.RoomId = proto.String(this.roomId) pb.MapId = proto.Int32(this.config.mapId) pb.ZoneId = proto.Int32(this.config.zoneId) if this.config.passwd != "" { pb.HasPasswd = proto.Int32(1) } pb.PlayerNum = proto.Int32(int32(len(this.members))) pb.PlayerMaxNum = proto.Int32(constant.ROOM_MAX_PLAYER_NUM) pb.TeamNum = proto.Int32(int32(len(this.teamUuidHash))) pb.TeamMaxNum = proto.Int32(constant.ROOM_MAX_TEAM_NUM) m := this.getMember(hum.GetAccountId()) if m != nil { pb.MyTeamId = proto.Int32(m.team.teamId) } pb.Owner = new(cs.MFMember) this.owner.fillMFMember(pb.Owner) } func (this *room) fillMFCurrentRoom(hum common.Player, pb *cs.MFCurrentRoom) { pb.RoomId = proto.String(this.roomId) pb.MapId = proto.Int32(this.config.mapId) pb.ZoneId = proto.Int32(this.config.zoneId) if this.config.passwd != "" { pb.HasPasswd = proto.Int32(1) } pb.PlayerNum = proto.Int32(int32(len(this.members))) pb.PlayerMaxNum = proto.Int32(constant.ROOM_MAX_PLAYER_NUM) pb.TeamNum = proto.Int32(int32(len(this.teamUuidHash))) pb.TeamMaxNum = proto.Int32(constant.ROOM_MAX_TEAM_NUM) m := this.getMember(hum.GetAccountId()) if m != nil { pb.MyTeamId = proto.Int32(m.team.teamId) } pb.Owner = new(cs.MFMember) this.owner.fillMFMember(pb.Owner) q5.NewSlice(&pb.TeamList, 0, 10) for _, t := range this.teamUuidHash { pbT := &cs.MFTeam{} t.fillMFTeam(pbT) pbT.SortIdx = proto.Int32(t.sortIdx) q5.AppendSlice(&pb.TeamList, pbT) } sort.Slice(pb.TeamList, func(i, j int) bool { return *pb.TeamList[i].SortIdx < *pb.TeamList[j].SortIdx }) for _, t := range this.obTeamUuidHash { pbT := &cs.MFTeam{} t.fillMFTeam(pbT) pbT.SortIdx = proto.Int32(t.obSortIdx) q5.AppendSlice(&pb.TeamList, pbT) } sort.Slice(pb.ObserverTeamList, func(i, j int) bool { return *pb.ObserverTeamList[i].SortIdx < *pb.ObserverTeamList[j].SortIdx }) } func (this *room) OnPlayerOffline(hum common.Player) { } func (this *room) OnPlayerOnline(hum common.Player) { } func (this *room) CMDisbandRoom(hdr *f5.MsgHdr, msg *cs.CMDisbandRoom) { hum := hdr.Context.(common.Player) rspMsg := &cs.SMDisbandRoom{} if !this.isOwner(hum) { rspMsg.Errcode = proto.Int32(1) rspMsg.Errmsg = proto.String("insufficient permissions") hum.SendMsg(rspMsg) return } if this.roomState == ROOM_INIT_STATE { this.doDisband(ROOM_DISBAND_1_REASON) } hum.SendMsg(rspMsg) } func (this *room) CMLeaveRoom(hdr *f5.MsgHdr, msg *cs.CMLeaveRoom) { hum := hdr.Context.(common.Player) m := this.getMember(hum.GetAccountId()) if m != nil && this.roomState == ROOM_INIT_STATE { var removeMemberAccountId string if this.isOwner(hum) { nextOwner := this.getNextOwner() if nextOwner == nil { this.doDisband(ROOM_DISBAND_NO_NEXT_OWNER_REASON) return } else { removeMemberAccountId = this.owner.hum.GetAccountId() this.owner = nextOwner } } else { removeMemberAccountId = hum.GetAccountId() } notifyMsg := &cs.SMRoomLeaveNotify{} q5.NewSlice(¬ifyMsg.AccountIds, 0, 1) q5.AppendSlice(¬ifyMsg.AccountIds, m.hum.GetAccountId()) this.broadcastMsg(notifyMsg) this.notifyRoomInfo(hum) this.removeMember(removeMemberAccountId) } } func (this *room) CMModifyRoom(hdr *f5.MsgHdr, msg *cs.CMModifyRoom) { hum := hdr.Context.(common.Player) rspMsg := cs.SMModifyRoom{} if this.isOwner(hum) && this.roomState == ROOM_INIT_STATE { if msg.MapId != nil { this.config.mapId = msg.GetMapId() } if msg.Passwd != nil { this.config.passwd = msg.GetPasswd() } } hum.SendMsg(&rspMsg) this.notifyRoomInfo(hum) } func (this *room) CMStartGame(hdr *f5.MsgHdr, msg *cs.CMStartGame) { hum := hdr.Context.(common.Player) rspMsg := &cs.SMStartGame{} if this.started() { hum.SendMsg(rspMsg) return } if this.canStart() { this.doStart(1) } hum.SendMsg(rspMsg) } func (this *room) CMSetPrepare(hdr *f5.MsgHdr, msg *cs.CMSetPrepare) { hum := hdr.Context.(common.Player) m := this.getMember(hum.GetAccountId()) if m != nil && m != this.owner && this.roomState == ROOM_INIT_STATE { m.state = MEMBER_READY_STATE notifyMsg := &cs.SMRoomMemberChangeNotify{} notifyMsg.Member = new(cs.MFMember) m.fillMFMember(notifyMsg.Member) this.broadcastMsg(notifyMsg) } } func (this *room) CMKickoutTeam(hdr *f5.MsgHdr, msg *cs.CMKickoutTeam) { hum := hdr.Context.(common.Player) m := this.getMember(hum.GetAccountId()) t := this.getTeamByUuid(msg.GetTeamUuid()) if m == this.owner && t != nil && t != m.team && this.roomState == ROOM_INIT_STATE { membersPtr := make([]*member, 0, t.members.Size()) t.members.ForEach( func(data interface{}) bool { if mPtr, ok := data.(*member); ok { membersPtr = append(membersPtr, mPtr) } return true }) notifyMsg := &cs.SMRoomKickoutNotify{} for _, m2 := range membersPtr { notifyMsg.AccountIds = append(notifyMsg.AccountIds, m2.hum.GetAccountId()) } this.broadcastMsg(notifyMsg) t.unInit() this.notifyRoomInfo(hum) } } func (this *room) CMKickoutMember(hdr *f5.MsgHdr, msg *cs.CMKickoutMember) { hum := hdr.Context.(common.Player) m := this.getMember(hum.GetAccountId()) target := this.getMember(msg.GetTargetId()) if m == this.owner && target != nil && this.roomState == ROOM_INIT_STATE { notifyMsg := &cs.SMRoomKickoutNotify{} notifyMsg.AccountIds = append(notifyMsg.AccountIds, msg.GetTargetId()) this.broadcastMsg(notifyMsg) this.removeMember(target.hum.GetAccountId()) this.notifyRoomInfo(hum) } } func (this *room) CMCloseNotify(hdr *f5.MsgHdr, msg *cs.CMCloseNotify) { hum := hdr.Context.(common.Player) m := this.getMember(hum.GetAccountId()) if m != nil { m.closeGameStartNotify = true } } func (this *room) broadcastMsg(msg proto.Message) { for _, m := range this.members { if m.hum.GetRoom() == this { m.hum.SendMsg(msg) } } } func (this *room) autoStart() { if this.roomState == ROOM_INIT_STATE { if this.canStart() { this.doStart(0) } else { this.doDisband(ROOM_DISBAND_DEFAULT_REASON) } } } func (this *room) started() bool { return this.roomState == ROOM_STARTED_STATE } func (this *room) doDisband(reason int32) { this.roomState = ROOM_DISBAND_STATE this.disbandTime = f5.GetApp().GetNowSeconds() this.disbandReason = reason notifyMsg := &cs.SMRoomDisbandNotify{} notifyMsg.RoomId = proto.String(this.roomId) notifyMsg.Reason = proto.Int32(reason) for _, m := range this.members { if m.hum.GetRoom() == this { m.hum.SendMsg(notifyMsg) } } this.delayDeleteRoom() } func (this *room) doStart(reason int32) { this.roomState = ROOM_STARTED_STATE this.startTime = f5.GetApp().GetNowSeconds() this.startReason = ROOM_AUTO_START_TYPE f5.GetTimer().SetIntervalExWp( 1000, func(e int32, args *q5.Args) { if e == q5.TIMER_EXEC_EVENT { if f5.GetApp().GetNowSeconds()-this.startTime < int64(mt.Table.Config.Get().GetGameStartNotifyTime()) { this.notifyGameStart() } } }, this.attacher) this.delayDeleteRoom() } func (this *room) viewable() bool { return this.roomState == ROOM_INIT_STATE } func (this *room) GetRoomState() int32 { return this.roomState } func (this *room) AutoStartGameCountdownNotify(seconds int32) { notifyMsg := &cs.SMAutoStartGameCountdownNotify{} notifyMsg.Seconds = proto.Int32(seconds) this.broadcastMsg(notifyMsg) } func (this *room) notifyGameStart() { if this.gameStartNotifyMsg == nil { this.genGameStartNotifyMsg() } for _, m := range this.members { // m.state == MEMBER_READY_STATE && if m.hum.GetRoom() == this && !m.closeGameStartNotify { m.hum.SendMsg(this.gameStartNotifyMsg) } } } func (this *room) genGameStartNotifyMsg() { if this.gameStartNotifyMsg != nil { return } this.gameStartNotifyMsg = &cs.SMRoomGameStartNotify{} this.gameStartNotifyMsg.ZoneId = proto.Int32(this.config.zoneId) this.gameStartNotifyMsg.NodeId = proto.Int32(this.config.nodeId) this.gameStartNotifyMsg.MapId = proto.Int32(this.config.mapId) this.gameStartNotifyMsg.TeamUuid = proto.String(q5.ToString(this.roomIdx)) startInfo := struct { ZoneId int32 `json:"zone_id"` NodeId int32 `json:"node_id"` RoomUuid string `json:"room_uuid"` StartTime int32 `json:"start_time"` TeamList []struct { TeamUuid string `json:"team_uuid"` Members []struct { AccountId string `json:"account_id"` } `json:"members"` } `json:"team_list"` ObList []struct { AccountId string `json:"account_id"` } `json:"ob_list"` } { ZoneId: this.config.zoneId, NodeId: this.config.nodeId, RoomUuid: q5.ToString(this.roomIdx), StartTime: int32(this.startTime), } q5.NewSlice(&startInfo.TeamList, 0, 10) for _, t := range this.teamUuidHash { //if t.hasAlreadMember() { { ele := q5.NewSliceElement(&startInfo.TeamList) ele.TeamUuid = t.teamUuid q5.NewSlice(&ele.Members, 0, 4) t.members.ForEach( func(data interface{}) bool { m := data.(*member) //if m.state == MEMBER_READY_STATE { ele2 := q5.NewSliceElement(&ele.Members) ele2.AccountId = m.hum.GetAccountId() //} return true }) } } q5.NewSlice(&startInfo.ObList, 0, 10) for _, t := range this.obTeamUuidHash { t.members.ForEach( func(data interface{}) bool { m := data.(*member) ele := q5.NewSliceElement(&startInfo.ObList) ele.AccountId = m.hum.GetAccountId() return true }) } this.gameStartNotifyMsg.CustomRoomPayload = proto.String( q5.Md5Str(q5.EncodeJson(&startInfo) + "520d8eAbB(8cf1^#$^&!@d833a42c820432PDAFE^^)") + "|" + q5.EncodeJson(&startInfo)) } func (this *room) canStart() bool { return true alreadyNum := 0 for _, t := range this.teamUuidHash { if t.hasAlreadMember() { alreadyNum++ } } return alreadyNum >= ROOM_MIN_START_TEAM_NUM } func (this *room) isOwner(hum common.Player) bool { return this.owner.hum.GetAccountId() == hum.GetAccountId() } func (this *room) getNextOwner() *member { var minT *member var maxT *member for i := int32(1); i <= this.config.maxTeamNum; i++ { if i != this.owner.team.teamId { if t, ok := this.teamIdHash[i]; ok { ownerCandidate := t.getOwnerCandidate() if ownerCandidate != nil { if i < this.owner.team.teamId && minT == nil { minT = ownerCandidate } if i > this.owner.team.teamId && maxT == nil { maxT = ownerCandidate } } } } } if maxT != nil { return maxT } else { return minT } } func (this *room) genTeamId() int32 { teamId := int32(-1) for i := int32(1); i <= this.config.maxTeamNum; i++ { if _, ok := this.teamIdHash[i]; !ok { teamId = i break } } if teamId < 1 { panic("room.genTeamId error") } return teamId } func (this *room) removeMember(accountId string) { m := this.getMember(accountId) if m != nil { t := m.team m.unInit() if t.getMemberNum() <= 0 { t.unInit() } } } func (this *room) notifyRoomInfo(hum common.Player) { notifyMsg := &cs.SMRoomChangeNotify{} notifyMsg.Room = new(cs.MFRoom) this.fillMFRoom(hum, notifyMsg.Room) this.broadcastMsg(notifyMsg) } func (this *room) delayDeleteRoom() { if this.delayDeleteTimer != nil { panic("errror dealyDeleteRoom") } this.delayDeleteTimer = f5.GetTimer().SetTimeoutExWp( 1000*mt.Table.Config.Get().GetDelayDeleteTime(), func(e int32, args *q5.Args) { if e == q5.TIMER_EXEC_EVENT { this.unInit() } }, this.attacher) } func newRoom() *room { r := new(room) r.curSortIdx = 1 r.teamUuidHash = map[string]*team{} r.teamIdHash = map[int32]*team{} r.obTeamUuidHash = map[string]*team{} r.obTeamIdHash = map[int32]*team{} r.members = map[string]*member{} r.entry.Init(r) r.attacher = f5.GetTimer().NewTimerAttacher() r.startTimer = f5.GetTimer().SetTimeoutExWp( 1000*mt.Table.Config.Get().GetAutoStartTime(), func(e int32, args *q5.Args) { if e == q5.TIMER_EXEC_EVENT { r.autoStart() } }, r.attacher) return r }