package model import ( "math/rand" "rocommon" "rocommon/service" "rocommon/util" "roserver/baseserver/model" "roserver/baseserver/set" "roserver/serverproto" ) type RoleManager struct { rocommon.UpdateModule onlineNum int channelRoleList map[uint64]RoleOuter //[channelid,Role] uuidRoleList map[uint64]RoleOuter //[uuid,role] offlineRoleList map[uint64]RoleOuter //[uuid,role] openRoleList map[string]RoleOuter //[openid,rol] updateTimer util.ServerTimer //主循环定时器 lastResetTime uint64 playerFifoList set.Interface //最近在线玩家列表 FIFO noticeInfo *serverproto.SSWebGMNoticeNtf //走马灯公告 tw *util.TimeWheel allGameOnlineNum int32 //所有game服务器当前在线的玩家数量 } func NewRoleManager() *RoleManager { rm := &RoleManager{ channelRoleList: make(map[uint64]RoleOuter), uuidRoleList: make(map[uint64]RoleOuter), offlineRoleList: make(map[uint64]RoleOuter), openRoleList: make(map[string]RoleOuter), lastResetTime: util.GetTimeMilliseconds(), } //rm.updateTimer = util.NewDurationTimer(util.GetCurrentTime(), 500) rm.updateTimer = util.NewDurationTimer(util.GetCurrentTime(), 2000) rm.playerFifoList = set.New(set.NonThreadSafe) rm.tw = util.NewTimeWheel(100, 32) rm.tw.Callback = rm.UpdateWithTimeTimeWheelTask return rm } var updateCount = 0 var ResetCount = 0 func (this *RoleManager) Update(ms uint64) { if !this.updateTimer.IsStart() || !this.updateTimer.IsExpired(ms) { return } //注意不能5点后停服,5点后启服(不会触发隔天重置) dailyResetHour5 := model.IsDailyResetHour5(this.lastResetTime) if dailyResetHour5 { rand.Seed(int64(ms)) } //dailyReset := model.IsDailyReset(this.lastResetTime) //if dailyReset { // rand.Seed(int64(util.GetTimeMilliseconds())) //} //已经离线的玩家处理 for key, offRole := range this.offlineRoleList { //offRole.Update(ms) if offRole.IsLogout(ms) { offRole.Logout() delete(this.offlineRoleList, key) delete(this.openRoleList, offRole.GetOpenId()) util.DebugF("Role delete offline=%v uid=%v", key, offRole.GetUUid()) } } this.onlineNum = 0 ResetCount = 0 for key, chRole := range this.channelRoleList { switch chRole.GetState() { case ROLE_STATE_OFFLINE: chRole.Update(ms) delete(this.channelRoleList, key) delete(this.uuidRoleList, chRole.GetUUid()) //放到离线处理中 this.addOfflineRole(chRole.GetUUid(), chRole) util.DebugF("Role delete ROLE_STATE_OFFLINE clid=%v uid=%v", key, chRole.GetUUid()) case ROLE_STATE_ZOMBIE: //需要做删除操作 delete(this.channelRoleList, key) delete(this.uuidRoleList, chRole.GetUUid()) delete(this.offlineRoleList, chRole.GetUUid()) delete(this.openRoleList, chRole.GetOpenId()) chRole.Logout() util.DebugF("Role delete ROLE_STATE_ZOMBIE clid=%v uid=%v", key, chRole.GetUUid()) case ROLE_STATE_ONLINE: chRole.Update(ms) this.onlineNum++ //每日重置 if dailyResetHour5 { //设置reset标记,在每个玩家自身的update中处理 //chRole.DailyReset(true) chRole.SetUpdateDailyReset(ms, true) //ResetCount++ } } } updateCount++ //if updateCount > 50 { // updateCount = 0 // //util.InfoF("RoleManager update onlineNum=%v offlineNum=%v", this.onlineNum, len(this.offlineRoleList)) //} if ResetCount > 0 { util.InfoF("RoleManager dailyreset online=%v resetnum=%v", this.onlineNum, ResetCount) } //12表示1分钟(60表示5分钟) if this.onlineNum > 0 && updateCount >= 60 { updateCount = 0 model.ElasticPutLogInfo(&model.ElasticLogST{ LogType: "RoleOnlineNum", LogDesc: "RoleOnlineNum", Param1: this.onlineNum, }) tmpNode := service.GetServiceConfig().Node //netease log nLog := &NeteaseLogOnlineRoleNum{ Server: service.GetServiceConfig().Node.Zone*100 + service.GetServiceConfig().Node.Id, Online: this.onlineNum, OnLineTime: uint64(util.GetTimeSeconds()), } nLog.Log(nil) //添加到mysql log logData := &serverproto.SSRoleLogData{ Type: int32(serverproto.MysqlLogType_LType_OnlineNum), ParamList: []int32{int32(tmpNode.Zone), int32(tmpNode.Id), int32(this.onlineNum)}, //zoneid,subid,onlinenum } mysqlOnlineNumNtf := &serverproto.SSRoleLogNtf{} mysqlOnlineNumNtf.LogList = append(mysqlOnlineNumNtf.LogList, logData) SendDb(mysqlOnlineNumNtf) } if dailyResetHour5 { this.lastResetTime = ms } } func (this *RoleManager) UpdateWithTimeWheel(ms uint64) { this.tw.Update(ms) } //task tick func (this *RoleManager) UpdateWithTimeTimeWheelTask(twTask *util.TWTask, ms uint64) { chRole, ok := this.uuidRoleList[twTask.Uid] if !ok { chRole, ok = this.offlineRoleList[twTask.Uid] if !ok { util.WarnF("uid=%v UpdateWithTimeTimeWheelTask save but role data not find", twTask.Uid) return } } chRole.Save() if chRole.GetState() == ROLE_STATE_ONLINE || chRole.GetState() == ROLE_STATE_OFFLINE { chRole.Save() } } //bool表示给定的cid是否已经存在role, bool是否已经存在 func (this *RoleManager) AddRole(cid model.ClientID, openId string, selectZone int32) (RoleOuter, bool, bool) { role, ok := this.channelRoleList[cid.SessID] if ok { util.WarnF("channel role exist id=", cid) return role, true, false } util.WarnF("AddRole openid=%v", openId) bHas := false role, ok = this.openRoleList[openId] if !ok { role = NewRole(cid) } else { bHas = true if role.GetSelectZone() != selectZone { delete(this.channelRoleList, role.CliID().SessID) delete(this.uuidRoleList, role.GetUUid()) delete(this.openRoleList, openId) role.SwitchState(ROLE_STATE_ZOMBIE, nil) role = NewRole(cid) bHas = false } } this.channelRoleList[cid.SessID] = role this.openRoleList[openId] = role return role, false, bHas } func (this *RoleManager) AddRoleObj(role RoleOuter) bool { //if _, ok := this.channelRoleList[role.CliID().SessID]; ok { // return false //} //if _, ok := this.uuidRoleList[role.GetUUid()]; ok { // return false //} if role.GetUUid() == 0 { util.InfoF("AddRoleObj uid=0") } this.channelRoleList[role.CliID().SessID] = role this.uuidRoleList[role.GetUUid()] = role return true } func (this *RoleManager) RemoveRoleObj(role RoleOuter) bool { delete(this.channelRoleList, role.CliID().SessID) delete(this.uuidRoleList, role.GetUUid()) return true } func (this *RoleManager) RemoveRoleObjByOpenId(openId string) { delete(this.openRoleList, openId) } //添加到uuid角色列表中 func (this *RoleManager) Resolve(role *Role) { if role.GetUUid() == 0 { util.InfoF("AddRoleObj uid=0") } if _, ok := this.uuidRoleList[role.GetUUid()]; ok { //log.Println("channel role exist id:", role.GetUUid()) return } this.uuidRoleList[role.GetUUid()] = role } func (this *RoleManager) GetRole(cid model.ClientID) RoleOuter { if role, ok := this.channelRoleList[cid.SessID]; ok { return role } else { if role, ok := this.uuidRoleList[cid.SessID]; ok { return role } } util.InfoF("role not found id=%v", cid) return nil } func (this *RoleManager) GetRoleOrKick(cid model.ClientID, ev rocommon.ProcEvent) RoleOuter { role := this.GetRole(cid) if role == nil { ev.Session().Send(&serverproto.SSUserKickNtf{ Error: int32(serverproto.ErrorCode_ERROR_OK), ClientId: cid.SessID, }) } else if ev.SeqId() > 0 { //序列号处理 msgInfo := rocommon.MessageInfoByMsg(ev.Msg()) role.ReqAckConfirm(uint32(msgInfo.ID), ev.SeqId()) } return role } func (this *RoleManager) GetRoleFromChannel(id uint64) RoleOuter { if role, ok := this.channelRoleList[id]; ok { return role } return nil } func (this *RoleManager) RemoveRoleFromChannel(id uint64) bool { delete(this.channelRoleList, id) return true } func (this *RoleManager) RemoveOfflineRole(id uint64) bool { delete(this.offlineRoleList, id) return true } func (this *RoleManager) GetRoleFromUUid(id uint64) RoleOuter { if role, ok := this.uuidRoleList[id]; ok { return role } util.InfoF("role not found uid=%v", id) return nil } func (this *RoleManager) GetRoleByOpenId(openId string) RoleOuter { if role, ok := this.openRoleList[openId]; ok { return role } return nil } func (this *RoleManager) GetRoleUUIdList() map[uint64]RoleOuter { return this.uuidRoleList } func (this *RoleManager) GetRoleFromOffline(id uint64) RoleOuter { if role, ok := this.offlineRoleList[id]; ok { return role } return nil } func (this *RoleManager) GetRoleOfflineList() map[uint64]RoleOuter { return this.offlineRoleList } func (this *RoleManager) AddFIFOList(uid uint64) { if this.playerFifoList.Size() >= 200 { this.playerFifoList.Pop() } if !this.playerFifoList.Has(uid) { this.playerFifoList.Add(uid) } } func (this *RoleManager) GetFIFOList(idx, num int32, role *Role, exceptUidList map[uint64]struct{}) (bool, []uint64) { var retUidList []uint64 if role == nil { return false, retUidList } if idx <= 0 { idx = 1 } var retSetList = set.New(set.NonThreadSafe) var count int32 = 0 for _, data := range this.playerFifoList.List() { count++ tmpUid := data.(uint64) if tmpUid <= 0 { continue } if count >= idx && num > 0 { if role.GetRoleSocial().isInSubList(tmpUid) || tmpUid == role.GetUUid() { continue } if _, ok := exceptUidList[tmpUid]; ok { continue } num-- retSetList.Add(data) retUidList = append(retUidList, tmpUid) if num <= 0 { break } } } if num > 0 { for _, data := range this.playerFifoList.List() { tmpUid := data.(uint64) if tmpUid <= 0 { continue } if role.GetRoleSocial().isInSubList(tmpUid) || tmpUid == role.GetUUid() { continue } if _, ok := exceptUidList[tmpUid]; ok { continue } //表示之前已经遍历过,不需要继续 if retSetList.Has(data) { break } num-- retUidList = append(retUidList, tmpUid) if num <= 0 { break } } return false, retUidList } return true, retUidList } //[sessionId]clientuse channelid //[serviceID] 所在的网关节点 func (this *RoleManager) KickFromGate(sessionId uint64, serviceID string) { if tmpRole, ok := this.channelRoleList[sessionId]; ok { if tmpRole.CliID().SessID == sessionId && tmpRole.GetState() == ROLE_STATE_ONLINE { util.InfoF("uid=%v KickFromGate sessionId=%v", tmpRole.GetUUid(), sessionId) tmpRole.SwitchState(ROLE_STATE_OFFLINE, nil) } } else { //服务器重启,客户端没有关闭的情况下会有该错误 //发送CSLoginReq请求到达比断开连接到达要快 util.ErrorF("KickFromGate sessionId=%v err=not find", sessionId) } } func (this *RoleManager) addOfflineRole(uid uint64, role RoleOuter) bool { if _, ok := this.offlineRoleList[uid]; ok { return false } //清空玩家对应的gate相关信息 role.(*Role).SetCliIDSessionID(0) this.offlineRoleList[uid] = role return true } func (this *RoleManager) SendMsg2OnlinePlayer(msg interface{}, fromUid uint64) { var clientIdLIstMap = map[string][]uint64{} //var clientIdList []uint64 for _, data := range this.uuidRoleList { if data.GetState() != ROLE_STATE_ONLINE { continue } if fromUid != 0 && data.(*Role).GetRoleSocial().IsInBlackList(fromUid) { continue } serviceId := data.CliID().ServiceID clientIdLIstMap[serviceId] = append(clientIdLIstMap[serviceId], data.CliID().SessID) if len(clientIdLIstMap[serviceId]) > 20 { ReplayGateList(msg, clientIdLIstMap[serviceId], serviceId, true) clientIdLIstMap[serviceId] = clientIdLIstMap[serviceId][:0] } } for sid, val := range clientIdLIstMap { if len(val) > 0 { ReplayGateList(msg, val, sid, true) } } } func (this *RoleManager) SetGMNoticeInfo(info *serverproto.SSWebGMNoticeNtf) { this.noticeInfo = info this.noticeInfo.NoticeTime = uint64(util.GetTimeMilliseconds()) scMsg := &serverproto.SCWebGMNoticeNtf{ NoticeInfo: this.noticeInfo.NoticeInfo, } this.SendMsg2OnlinePlayer(scMsg, 0) } func (this *RoleManager) NoticeInfoNtf(role *Role) { if this.noticeInfo == nil { return } //持续一小时 if this.noticeInfo.NoticeTime+uint64(60*60*1000) > util.GetTimeMilliseconds() { scMsg := &serverproto.SCWebGMNoticeNtf{ NoticeInfo: this.noticeInfo.NoticeInfo, } role.ReplayGate(scMsg, true) } } func (this *RoleManager) ServerMaintain(state int32) { for _, data := range this.uuidRoleList { if data.GetState() != ROLE_STATE_ONLINE { continue } data.KickWithSave(int32(serverproto.ErrorCode_ERROR_SERVER_MAINTAIN)) } } //gate端口后,game上对应role的处理 func (this *RoleManager) GateCloseKickRole(serviceId string) { for _, chRole := range this.channelRoleList { if chRole.GetState() == ROLE_STATE_ONLINE && chRole.CliID().ServiceID == serviceId { chRole.SwitchState(ROLE_STATE_OFFLINE, nil) util.DebugF("GateCloseKickRole role=%v %v", chRole.GetUUid(), chRole.GetOpenId()) } } } //social通知game当前在线的玩家数量 func (this *RoleManager) PlayerOnlineNumFromSocial(onlineNum int32) { this.allGameOnlineNum = onlineNum } func (this *RoleManager) IsGameOnlineNumLimit() bool { if this.allGameOnlineNum >= 6000 { return true } return false } func (this *RoleManager) SendRushReward(rushType, rushRound int32) { for _, chRole := range this.channelRoleList { if chRole.GetState() != ROLE_STATE_ONLINE { continue } if rushType == model.Rush_Type_Tower { chRole.(*Role).GetRoleTower().GetFightCountMailReward(rushRound) } else if rushType == model.Rush_Type_Arena { chRole.(*Role).GetRoleArena().GetFightCountMailReward(rushRound) } else if rushType == model.Rush_Type_Map { chRole.(*Role).GetRoleBattle().GetFightCountMailReward(rushRound) } else if rushType == model.Rush_Type_Pet { chRole.(*Role).GetRolePet().GetFightCountMailReward(rushRound) } else if rushType == model.Rush_Type_Skill { chRole.(*Role).GetRoleRush().GetFightCountMailReward(rushRound) } } } func (this *RoleManager) GuildBattleBeginNtf(inBattle bool) { ntfMsg := &serverproto.SCGuildBattleStageNtf{ InGuildBattle: inBattle, } for _, chRole := range this.channelRoleList { if chRole.GetState() != ROLE_STATE_ONLINE { continue } chRole.(*Role).ReplayGate(ntfMsg, true) } } //文件热加载通知操作(调用该方法时文件已经重新加载,转换部分需要这边重新处理) func (this *RoleManager) ServerReloadConfigNtf(cfgList []string) { //转换 for _, cfgData := range cfgList { if cfgData == "CombinedServiceCfg" { model.ConvertCombinedServer() this.CombineServerNtf() } } } func (this *RoleManager) CombineServerNtf() { ntfMsg := &serverproto.SCCombineServerOnlineNtf{ CombineTime: 0, } data, ok := model.ConvertCombineServerData[1] for _, chRole := range this.channelRoleList { if chRole.GetState() != ROLE_STATE_ONLINE { continue } if ok { zoneId := chRole.GetSelectZone() for _, server := range data.ServerList { if server.Key <= zoneId && zoneId <= server.Value { ntfMsg.CombineTime = data.CombineTime break } } } chRole.ReplayGate(ntfMsg, true) } }