615 lines
16 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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服务器当前在线的玩家数量
//客户端自己约定对应位的使用
funcDisableFlag uint64 //功能开启枚举(位上非0表示不开启)
funcDisableFlagRefreshTime uint64
}
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) AddOffline(id uint64, role RoleOuter) {
this.offlineRoleList[id] = role
}
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,
}
bFind := false
zoneId := int32(0)
for _, chRole := range this.channelRoleList {
if chRole.GetState() != ROLE_STATE_ONLINE {
continue
}
if zoneId == 0 {
zoneId = chRole.GetSelectZone()
if zoneId == 0 {
continue
}
}
//bFind 从合服列表中没找到,则继续找
if bFind == false {
bRet, combineTime := this.GetCombineServerTime(zoneId)
if bRet == true {
bFind = bRet
ntfMsg.CombineTime = combineTime
}
}
chRole.ReplayGate(ntfMsg, true)
}
}
func (this *RoleManager) GetCombineServerTime(zoneId int32) (bool, uint64) {
for _, data := range model.ConvertCombineServerData {
for _, server := range data.ServerList {
if server.Key <= zoneId && zoneId <= server.Value {
return true, data.CombineTime
}
}
}
return false, 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)
}
*/
// 来自webgm的功能屏蔽通知
func (this *RoleManager) FuncDisableFlagNtf(flag uint64, force bool) {
if force {
this.funcDisableFlag = flag
for _, chRole := range this.channelRoleList {
if chRole.GetState() != ROLE_STATE_ONLINE {
continue
}
chRole.(*Role).OnPlayerFuncDisableFlag(flag)
}
} else if this.funcDisableFlag != flag {
this.funcDisableFlag = flag
for _, chRole := range this.channelRoleList {
if chRole.GetState() != ROLE_STATE_ONLINE {
continue
}
chRole.(*Role).OnPlayerFuncDisableFlag(flag)
}
}
}