package main import ( "fmt" "mongo.games.com/goserver/core/logger" "time" "mongo.games.com/game/common" "mongo.games.com/game/model" "mongo.games.com/goserver/core/basic" "mongo.games.com/goserver/core/module" "mongo.games.com/goserver/core/netlib" "mongo.games.com/goserver/core/task" ) const ( LoginStateLogin int = iota // 登录中 LoginStateLoginFinish // 登录完成 LoginStateLogout // 登出中 LoginStateLogoutFinish // 登出完成 ) var LoginStateMgrSington = &LoginStateMgr{ statesByName: make(map[string]*AccLoginState), statesByAccId: make(map[string]*AccLoginState), statesByPlayer: make(map[string]*AccLoginState), statesBySid: make(map[int64]*SessionState), } // SessionState 客户端连接状态 type SessionState struct { userName string // 玩家唯一标识 sid int64 // 连接唯一标识 state int // 登录状态 gateSess *netlib.Session // 客户端所在gatesrv als *AccLoginState // 账号状态 clog *model.ClientLoginInfo // 登录日志 startLoginTs int64 // 登录时间 } type AccLoginState struct { acc *model.Account lss map[int64]*SessionState // sid lastLoginTs int64 //最后登录时间 lastLogoutTs int64 //最后登出时间(只是用来判断清理内存缓存) } // LoginStateMgr 玩家登录信息 // 保存登录中和已经登录的玩家账号信息 // 玩家登出或断开连接只会清除连接信息,短时间内重复登录会使用之前的账号缓存数据不会频繁访问数据库 // 玩家长时间离开才会清除缓存信息 // 注意:玩家登录次数,登录时间,登出时间记录并没有准确同步到数据库 type LoginStateMgr struct { statesByName map[string]*AccLoginState // key:玩家唯一标识,玩家登录状态 statesByAccId map[string]*AccLoginState // key:accountid,所有人登录状态 statesByPlayer map[string]*AccLoginState // key:accountid,真人登录状态 statesBySid map[int64]*SessionState // key:连接唯一标识,连接状态 } func UserKey(userName, platform string, tagkey int32) string { return fmt.Sprintf("%v_%v_%v", userName, platform, tagkey) } func (this *LoginStateMgr) IsLogining(userName, platform string, tagkey int32) bool { userName = UserKey(userName, platform, tagkey) if v, exist := this.statesByName[userName]; exist { if len(v.lss) == 0 { return false } for _, ls := range v.lss { if ls.state == LoginStateLogin { return true } } } return false } func (this *LoginStateMgr) IsLoginFinish(userName, platform string, tagkey int32) bool { userName = UserKey(userName, platform, tagkey) if v, exist := this.statesByName[userName]; exist { if len(v.lss) == 0 { return false } for _, ls := range v.lss { if ls.state == LoginStateLoginFinish { return true } } } return false } func (this *LoginStateMgr) GetLoginStateByName(userName string) *AccLoginState { if v, exist := this.statesByName[userName]; exist { return v } return nil } func (this *LoginStateMgr) GetLoginStateByAccId(accId string) *AccLoginState { if v, exist := this.statesByAccId[accId]; exist { return v } return nil } func (this *LoginStateMgr) GetLoginStateByTelAndPlatform(tel, platform string) *AccLoginState { for _, als := range this.statesByPlayer { if als != nil && als.acc != nil { if als.acc.Tel == tel && als.acc.Platform == platform { return als } } } return nil } // GetLoginStateBySid 连接是否登录中 func (this *LoginStateMgr) GetLoginStateBySid(sid int64) *SessionState { if v, exist := this.statesBySid[sid]; exist { return v } return nil } // StartLogin 开始登录 func (this *LoginStateMgr) StartLogin(userName, platform string, sid int64, s *netlib.Session, clog *model.ClientLoginInfo, tagkey int32) bool { //注意此处的坑,这个地方要增加平台id userName = UserKey(userName, platform, tagkey) ts := time.Now().Unix() if als, exist := this.statesByName[userName]; !exist { als = &AccLoginState{ lss: make(map[int64]*SessionState), lastLoginTs: ts, } ls := &SessionState{ userName: userName, sid: sid, gateSess: s, state: LoginStateLogin, als: als, startLoginTs: ts, clog: clog, } this.statesByName[userName] = als als.lss[sid] = ls this.statesBySid[sid] = ls return true } else { ls := &SessionState{ userName: userName, sid: sid, gateSess: s, state: LoginStateLogin, als: als, startLoginTs: ts, clog: clog, } als.lss[sid] = ls this.statesBySid[sid] = ls if als.acc != nil { return false } return true } } // LoginFinish 玩家登录完成 func (this *LoginStateMgr) LoginFinish(userName, platform string, sid int64, acc *model.Account, tagkey int32) (oldState map[int64]*SessionState) { userName = UserKey(userName, platform, tagkey) if v, exist := this.statesByName[userName]; exist { v.acc = acc v.lastLoginTs = time.Now().Unix() if acc != nil { this.statesByAccId[acc.AccountId.Hex()] = v if acc.Platform != common.Platform_Rob { this.statesByPlayer[acc.AccountId.Hex()] = v } } if len(v.lss) > 0 { oldState = make(map[int64]*SessionState) for k, v := range v.lss { if k != sid { oldState[k] = v } } } } if v, exist := this.statesBySid[sid]; exist { v.state = LoginStateLoginFinish } return } // Logout 删除连接状态 // 连接断开,正常登出 func (this *LoginStateMgr) Logout(s *SessionState) { if s != nil { delete(this.statesBySid, s.sid) //缓存下,避免对DB造成冲击 s.state = LoginStateLogoutFinish s.als.lastLogoutTs = time.Now().Unix() if s.als != nil && s.als.acc != nil { s.als.acc.LastLogoutTime = time.Now() } delete(s.als.lss, s.sid) } } // LogoutBySid 登录失败,删除连接状态 func (this *LoginStateMgr) LogoutBySid(sid int64) { if s, exist := this.statesBySid[sid]; exist { this.Logout(s) } } func (this *LoginStateMgr) LogoutByAccount(accId string) { if als, exist := this.statesByAccId[accId]; exist { if als != nil { for _, s := range als.lss { this.Logout(s) } } } } // LogoutAllBySession gatesrv掉线,则上面的所有玩家掉线 func (this *LoginStateMgr) LogoutAllBySession(session *netlib.Session) { for sid, s := range this.statesBySid { if s.gateSess == session { this.Logout(s) p := PlayerMgrSington.GetPlayer(sid) if p != nil { p.DropLine() } } } } // DelAccountByAccid 特殊处理,负责从statesByName删除,为了处理账号的删除问题 // 目前的一个使用功能,机器人账号会被删除,但是这里是删除真人账号数据? func (this *LoginStateMgr) DelAccountByAccid(accid string) { for name, s := range this.statesByPlayer { if s != nil && s.acc != nil && s.acc.AccountId.Hex() == accid { this.DeleteAccount(name, s) } } } func (this *LoginStateMgr) DeleteAccount(name string, s *AccLoginState) { if s != nil && s.acc != nil { //注意此处的坑,这个地方要增加平台id,为了和上面的匹配相同 userName := UserKey(s.acc.UserName, s.acc.Platform, s.acc.TagKey) delete(this.statesByName, userName) userName = UserKey(s.acc.Tel, s.acc.Platform, s.acc.TagKey) delete(this.statesByName, userName) acc := s.acc if acc != nil { delete(this.statesByAccId, acc.AccountId.Hex()) } delete(this.statesByPlayer, name) } } func (this *LoginStateMgr) ModuleName() string { return "LoginStateMgr" } func (this *LoginStateMgr) Init() { // 预加载机器人数据,是为了让机器人登录时不再访问数据库吗? if model.GameParamData.PreLoadRobotCount > 0 { task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} { tsBeg := time.Now() accounts := model.GetRobotAccounts(model.GameParamData.PreLoadRobotCount) tsEnd := time.Now() logger.Logger.Tracef("GetRobotAccounts take:%v total:%v", tsEnd.Sub(tsBeg), len(accounts)) return accounts }), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) { if accounts, ok := data.([]model.Account); ok { if accounts != nil { ts := time.Now().Add(time.Hour).Unix() // 这里加了一小时,延迟数据清理? for i := 0; i < len(accounts); i++ { userName := UserKey(accounts[i].UserName, accounts[i].Platform, accounts[i].TagKey) if _, exist := this.statesByName[userName]; !exist { als := &AccLoginState{ acc: &accounts[i], lss: make(map[int64]*SessionState), lastLoginTs: ts, lastLogoutTs: ts, } this.statesByName[userName] = als this.statesByAccId[accounts[i].AccountId.Hex()] = als } } } } }), "GetAllRobotAccounts").Start() } PlayerMgrSington.LoadRobots() } func (this *LoginStateMgr) Update() { // 定时清理已经登出的账号数据 curTs := time.Now().Unix() var delAcc []*AccLoginState for _, s := range this.statesByPlayer { if s == nil { continue } if len(s.lss) == 0 && curTs-s.lastLogoutTs > int64(model.GameParamData.LoginStateCacheSec) { if s.acc != nil { delAcc = append(delAcc, s) } } } for _, s := range delAcc { this.DeleteAccount(s.acc.AccountId.Hex(), s) } } func (this *LoginStateMgr) Shutdown() { for _, s := range this.statesByName { if s.acc != nil && s.acc.Platform != common.Platform_Rob { model.LogoutAccount(s.acc) // 更新登录次数和登出时间 } } module.UnregisteModule(this) } func init() { module.RegisteModule(LoginStateMgrSington, time.Minute, 0) }