game_sync/worldsrv/loginstatemgr.go

338 lines
9.6 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 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.GetOnlinePlayer(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)
}