package mapPlayer import ( capp "f1-game/internal/cherry_app" "f1-game/internal/code" "f1-game/internal/component/rate" "f1-game/internal/enum" "f1-game/internal/event" nameLocal "f1-game/internal/name/local" nameRemote "f1-game/internal/name/remote" "f1-game/internal/pb" "f1-game/internal/types" mapCall "f1-game/nodes/map/internal/call" "f1-game/nodes/map/internal/db" mapFacade "f1-game/nodes/map/internal/facade" ctime "github.com/cherry-game/cherry/extend/time" cfacade "github.com/cherry-game/cherry/facade" cherryActor "github.com/cherry-game/cherry/net/actor" ) // actorPlayer 每个玩家一个PlayerActor type actorPlayer struct { capp.ActorLogger playerID int64 // 当前玩家id isOnline bool // 玩家是否在线 now *ctime.CherryTime // 玩家当前时间 funcLimiter rate.FuncLimiter // 函数访问限速 funcErrCode int32 // 函数访问的错误码 // 战斗处理器 battleHandlers map[enum.TileBattleType]func(*pb.BattleReq, *pb.BattleRsp) } func newActorPlayer(playerID int64) actorPlayer { now := ctime.Now() return actorPlayer{ ActorLogger: capp.NewLoggerPrefix("playerID", playerID), playerID: playerID, now: &now, funcLimiter: rate.NewFuncLimiter(30, 3, code.NodeRateLimiter), funcErrCode: code.NodeRateLimiter, } } func (p *actorPlayer) OnInit() { p.initRateLimits() p.initRemote() p.initPlayerLeague() p.initMap() p.initFacility() p.initPlayerTeam() p.initConscript() p.initQuest() p.initBattle() p.initAsset() p.initHero() p.initAssemble() p.initShop() p.initExchange() p.initShow() p.initPlayerStats() p.initSignIn() p.initSeason() p.initEvent() } func (p *actorPlayer) initRemote() { p.Remote().Register(nameRemote.MapPlayer_OnSessionClosed, p.onSessionClosed) p.Remote().Register(nameRemote.MapPlayer_OnCheckExit, p.onCheckExit) p.Remote().Register(nameRemote.MapPlayer_SyncData, p.syncData) p.Remote().Register(nameRemote.MapPlayer_SyncVip, p.syncVipAttrs) } func (p *actorPlayer) OnStop() { p.Info("[actorPlayer] OnStop() playerID = %d", p.playerID) p.Timer().RemoveAll() p.ActorLogger.Destory() } func (p *actorPlayer) OnLocalReceived(m *cfacade.Message) (bool, bool) { // 玩家未登录时,第一条消息必须是 map.player.login if !p.isOnline && m.FuncName != nameLocal.MapPlayer_Login { p.ResponseCode(m.Session, code.PlayerNotLogin) return false, false } p.funcErrCode = p.funcLimiter.Check(m.BuildTime, m.FuncName) if code.IsFail(p.funcErrCode) { p.ResponseCode(m.Session, p.funcErrCode) return false, false } return false, true } func (p *actorPlayer) OnRemoteReceived(m *cfacade.Message) (bool, bool) { if p.playerID == 0 { p.Warn("[OnRemoteReceive] player not found: Source = %s, Target = %s, FuncName = %s", m.Source, m.Target, m.FuncName) return false, false } // 如果找不到玩家表,直接返回错误 if db.GetMapPlayerTable(p.playerID) == nil { p.Warn("[OnRemoteReceive] player not found: Source = %s, Target = %s, FuncName = %s", m.Source, m.Target, m.FuncName) return false, false } return false, true } func (p *actorPlayer) updateNowTime() { now := ctime.Now() p.now = &now } func (p *actorPlayer) initRateLimits() { p.funcLimiter.Add(nameLocal.MapPlayer_March, 1000, 10, code.OperationTooFast) p.funcLimiter.Add(nameLocal.MapPlayer_MarchStop, 1000, 10, code.OperationTooFast) p.funcLimiter.Add(nameLocal.MapPlayer_Retreat, 1000, 10, code.OperationTooFast) p.funcLimiter.Add(nameLocal.MapPlayer_Assemble, 1000, 10, code.OperationTooFast) p.funcLimiter.Add(nameLocal.MapPlayer_AssembleJoin, 1000, 10, code.OperationTooFast) p.funcLimiter.Add(nameLocal.MapPlayer_FindPath, 1000, 10, code.OperationTooFast) p.funcLimiter.Add(nameLocal.MapPlayer_MapObjectPosition, 1000, 10, code.OperationTooFast) } func (p *actorPlayer) EventRegister(name string, fn cherryActor.IEventFunc) { p.Event().Register(name, fn, p.playerID) } // 同步玩家数据 func (p *actorPlayer) syncData(req *pb.SyncMapPlayerRequest) { p.Debug("sync player data: req = %+v", req) playerTable := db.GetMapPlayerTable(p.playerID) // 同步必要的数据 playerTable.SyncData(req) // 抛事件 p.PostEvent(event.NewMapWatcherSync(p.playerID, &event.MapWatcherData{ PlayerID: p.playerID, PlayerName: req.PlayerName, IconID: req.IconID, FrameID: req.FrameID, })) } // 同步vip属性 func (p *actorPlayer) syncVipAttrs(req *pb.SyncMapPlayerVip) { p.Debug("sync player vip attrs: req = %+v", req) playerTable := db.GetMapPlayerTable(p.playerID) vipAttrs := types.NewAttrs() for _, attr := range req.Attrs.List { vipAttrs.Set(attr.Key, attr.Value) } playerTable.SyncVip(req.Level, vipAttrs, req.ExpireTime) // 抛事件 p.PostEvent(event.NewVipActive(p.playerID, vipAttrs, req.ExpireTime)) } func (p *actorPlayer) onSessionClosed() { p.Info("[onLogout] playerID = %d", p.playerID) // 不在线则忽略 if !p.isOnline { return } // 必需设置为 false,如果玩家被顶号,actor 不会被销毁,不设置会导致玩家无法登录 p.isOnline = false //移除当前玩家所有定时器 p.Timer().RemoveAll() if playerTable := db.GetMapPlayerTable(p.playerID); playerTable != nil { if playerTable.ObjectID > 0 { mapCall.AOI.Leave(playerTable.ObjectID, enum.ObjectType_Player) } mapFacade.ServiceOnLogout(playerTable) playerTable.Save2Queue() } p.Info("[onLogout] finished! playerID = %d", p.playerID) } // 父actor检测到当前子actor很久收到消息,确认是否要退出 func (p *actorPlayer) onCheckExit() { // 不在线则销毁当前actor if !p.isOnline { p.Exit() } }