| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338 |
- package user
- import (
- "encoding/binary"
- cherryApp "f1-game/internal/cherry_app"
- "f1-game/internal/code"
- "f1-game/internal/component/rate"
- "f1-game/internal/constant"
- "f1-game/internal/data"
- "f1-game/internal/extend/utils"
- nameActor "f1-game/internal/name/actor"
- nameRemote "f1-game/internal/name/remote"
- nameRoute "f1-game/internal/name/route"
- "f1-game/internal/pb"
- "f1-game/internal/sessions"
- "f1-game/nodes/gate/cache"
- "slices"
- "time"
- cstring "github.com/cherry-game/cherry/extend/string"
- ctime "github.com/cherry-game/cherry/extend/time"
- cfacade "github.com/cherry-game/cherry/facade"
- clog "github.com/cherry-game/cherry/logger"
- cconnector "github.com/cherry-game/cherry/net/connector"
- "github.com/cherry-game/cherry/net/parser/pomelo"
- pmessage "github.com/cherry-game/cherry/net/parser/pomelo/message"
- pomeloPacket "github.com/cherry-game/cherry/net/parser/pomelo/packet"
- cproto "github.com/cherry-game/cherry/net/proto"
- cprofile "github.com/cherry-game/cherry/profile"
- "go.uber.org/zap/zapcore"
- )
- var (
- upa *userParserActor
- )
- type (
- // 网关专用用户解析器
- userParserActor struct {
- *pomelo.Actor
- nodeRouteMap map[string]routeFunc // 节点路由函数映射
- nodeSyncMap map[string]SyncFunc // 节点数据同步函数映射
- duplicateLoginCode []byte // 已序列化好的重复登录响应数据
- notLoginCode []byte // 已序列化好的未登录响应数据
- kickCode []byte // 已序列化好的踢人响应数据
- }
- // 节点路由函数
- routeFunc func(pomeloAgent *pomelo.Agent, session *cproto.Session, route *pmessage.Route, msg *pmessage.Message)
- // 节点数据同步函数
- SyncFunc func(actor cfacade.IActor, session *cproto.Session, isBind bool)
- )
- func NewUserActorParser() *pomelo.Actor {
- upa = &userParserActor{
- Actor: pomelo.NewActor(nameActor.GateUser),
- nodeRouteMap: make(map[string]routeFunc),
- nodeSyncMap: make(map[string]SyncFunc),
- }
- upa.init()
- return upa.Actor
- }
- func (p *userParserActor) init() {
- // 当actor OnInit()时,增加hook进2个远程调用接口
- p.SetOnInitFunc(func() {
- p.Remote().Register(nameRemote.User_PlayerPush, p.playerPush) // 玩家消息推送
- p.Remote().Register(nameRemote.User_PlayerKick, p.playerKick) // 玩家踢掉
- p.initNodeKick()
- })
- // 初始化状态码
- p.initStatusCode()
- // 初始心跳设置
- p.initHeartBeat()
- // 初始用户新连接处理函数
- p.initNewAgent()
- // 设置数据包处理函数
- p.SetOnDataRoute(p.onDataRoute)
- // 初始化节点路由函数
- p.initNodeRoute()
- // 初始化节点数据同步函数
- p.initNodeSync()
- // 添加网络连接器
- //p.AddConnector(cconnector.NewWS(p.app.Address()))
- p.AddConnector(cconnector.NewTCP(cherryApp.App().Address()))
- }
- func (p *userParserActor) initHeartBeat() {
- // 心跳超时时间,过该时间未往网关发送心跳则网关主动断开连接
- heartBeatTimeout := cherryApp.App().Settings().GetDuration("heartbeat", 30) * time.Second
- p.SetHeartbeat(heartBeatTimeout)
- // 自定义设置心跳包处理函数
- p.SetOnPacket(pomeloPacket.Heartbeat, p.onHeartbeat)
- }
- func (p *userParserActor) initNewAgent() {
- // 有新连接时触发创建pomelo.Agent实例
- p.SetOnNewAgent(func(newAgent *pomelo.Agent) {
- // 为每个新连接创建child actor agent
- childAgent := newChildAgent(newAgent)
- _, err := p.Child().Create(childAgent.AliasID(), childAgent)
- if err != nil {
- clog.Warnf("Create agent fail. err = %v", err)
- }
- })
- }
- func (p *userParserActor) initNodeRoute() {
- p.nodeRouteMap[constant.NodeGame] = gameNodeRoute
- p.nodeRouteMap[constant.NodeMap] = mapNodeRoute
- }
- func (p *userParserActor) initNodeSync() {
- p.nodeSyncMap[sessions.GameNodeID] = sync2Game
- p.nodeSyncMap[sessions.MapNodeID] = sync2Map
- }
- func (p *userParserActor) initStatusCode() {
- p.duplicateLoginCode, _ = cherryApp.App().Serializer().Marshal(&cproto.I32{
- Value: code.PlayerDuplicateLogin,
- })
- p.notLoginCode, _ = cherryApp.App().Serializer().Marshal(&cproto.I32{
- Value: code.PlayerNotLogin,
- })
- }
- // 监听节点移除事件,当接收到(game/map)节点移除的消息时踢掉当前gate的相关session
- func (p *userParserActor) initNodeKick() {
- p.App().Discovery().OnRemoveMember(func(member cfacade.IMember) {
- if member.GetNodeType() == constant.NodeGame {
- clog.Infof("Kick Sessions with [NodeType = %v, NodeID = %v]", member.GetNodeType(), member.GetNodeID())
- p.playerKick(&pb.GateKick{
- Type: pb.GateKick_GameNode,
- StrValue: member.GetNodeID(),
- Reason: p.kickCode,
- Closed: true,
- })
- }
- if member.GetNodeType() == constant.NodeMap {
- clog.Infof("Kick Sessions with [NodeType = %v, NodeID = %v]", member.GetNodeType(), member.GetNodeID())
- p.playerKick(&pb.GateKick{
- Type: pb.GateKick_MapNode,
- StrValue: member.GetNodeID(),
- Reason: p.kickCode,
- Closed: true,
- })
- }
- })
- }
- func (p *userParserActor) onHeartbeat(pomeloAgent *pomelo.Agent, _ *pomeloPacket.Packet) {
- var (
- now = ctime.Now().ToMillisecond()
- nowBytes = utils.Int64ToBytesFixed(now, binary.BigEndian)
- )
- pomeloAgent.SendPacket(pomeloPacket.Heartbeat, nowBytes)
- if clog.Enable(zapcore.DebugLevel) {
- clog.Debugf("[heartbeat] sid = %s, now = %d", pomeloAgent.SID(), now)
- }
- }
- func (p *userParserActor) playerPush(req *pb.GatePusher) {
- switch req.Type {
- case pb.GatePusher_Player:
- for _, playerID := range req.I64List {
- if agent, found := cache.GetPlayerAgent(playerID); found {
- if agent != nil {
- agent.Push(req.Route, req.Data)
- }
- }
- }
- case pb.GatePusher_GameNode:
- if gameMap, found := cache.GetGameAgentsMap(req.StrValue); found {
- gameMap.Range(func(_ int64, agent *pomelo.Agent) bool {
- if agent != nil {
- agent.Push(req.Route, req.Data)
- }
- return true
- })
- }
- case pb.GatePusher_Map:
- if mapMap, found := cache.GetMapAgentsMap(req.StrValue); found {
- mapMap.Range(func(_ int64, agent *pomelo.Agent) bool {
- if agent != nil {
- agent.Push(req.Route, req.Data)
- }
- return true
- })
- }
- }
- }
- func (p *userParserActor) playerKick(req *pb.GateKick) {
- // 检测是否过滤当前节点
- if len(req.FilterNodeIDS) > 0 && slices.Contains(req.FilterNodeIDS, cherryApp.App().NodeID()) {
- return
- }
- switch req.Type {
- case pb.GateKick_Player:
- for _, playerID := range req.I64List {
- if agent, found := cache.GetPlayerAgent(playerID); found {
- if agent != nil {
- agent.Kick(req.Reason, req.Closed)
- }
- }
- }
- case pb.GateKick_GameNode:
- if gameMap, found := cache.GetGameAgentsMap(req.StrValue); found {
- gameMap.Range(func(_ int64, agent *pomelo.Agent) bool {
- if agent != nil {
- agent.Kick(req.Reason, req.Closed)
- }
- return true
- })
- }
- case pb.GateKick_UID:
- for _, uid := range req.I64List {
- if agent, found := pomelo.GetAgentWithUID(uid); found {
- if agent != nil {
- agent.Kick(req.Reason, req.Closed)
- }
- }
- }
- case pb.GateKick_MapNode:
- if mapMap, found := cache.GetMapAgentsMap(req.StrValue); found {
- mapMap.Range(func(_ int64, agent *pomelo.Agent) bool {
- if agent != nil {
- agent.Kick(req.Reason, req.Closed)
- }
- return true
- })
- }
- }
- }
- // onDataRoute 数据路由规则
- //
- // 登录逻辑:
- // 1.(建立连接)客户端建立连接,服务端对应创建一个agent用于处理玩家消息,actorID == sid
- // 2.(用户登录)客户端进行帐号登录验证,通过uid绑定当前sid
- // 3.(角色登录)客户端通过'beforeLoginRoutes'中的协议完成角色登录
- func (p *userParserActor) onDataRoute(pomeloAgent *pomelo.Agent, route *pmessage.Route, msg *pmessage.Message) {
- if clog.PrintLevel(zapcore.DebugLevel) {
- clog.Debugf("[sid = %s,uid = %d] Request Data Message. [mid = %d, route = %s]",
- pomeloAgent.SID(),
- pomeloAgent.UID(),
- msg.ID,
- msg.Route,
- )
- }
- session := pomelo.BuildSession(pomeloAgent, msg)
- var errCode int32
- // 如果不是debug模式,则检查路由规则
- if !cprofile.Debug() {
- errCode = data.ProtoRoute.Contain(msg.Route)
- if code.IsFail(errCode) {
- pomeloAgent.ResponseCode(session, errCode, true)
- return
- }
- }
- // rate check,有uid时则优先uid进行限速检查
- if session.Uid > 0 {
- errCode = rate.Check(cstring.ToString(session.Uid))
- } else {
- errCode = rate.Check(session.Ip)
- }
- if code.IsFail(errCode) {
- pomeloAgent.ResponseCode(session, errCode, true)
- return
- }
- if !session.IsBind() && msg.Route != nameRoute.GateUser_Login {
- pomeloAgent.Kick(p.notLoginCode, true)
- return
- }
- if pomeloAgent.NodeType() == route.NodeType() {
- targetPath := cfacade.NewChildPath(pomeloAgent.NodeID(), route.HandleName(), session.Sid)
- pomelo.LocalDataRoute(pomeloAgent, session, route, msg, targetPath)
- return
- }
- // uid未绑定则踢掉
- if !session.IsBind() {
- pomeloAgent.Kick(p.notLoginCode, true)
- return
- }
- // 如果不是首条路由,则需要验证playerID
- if msg.Route != nameRoute.GameLogin_Enter {
- playerID := session.GetString(sessions.PlayerID)
- if playerID == "" {
- pomeloAgent.Kick(p.notLoginCode, true)
- return
- }
- }
- // 找到路由函数并执行
- if routeFn, found := p.GetNodeRoute(route.NodeType()); found {
- routeFn(pomeloAgent, session, route, msg)
- }
- }
- func (p *userParserActor) GetNodeRoute(nodeType string) (routeFunc, bool) {
- route, found := p.nodeRouteMap[nodeType]
- return route, found
- }
- func (p *userParserActor) GetSyncParser(nodeID string) (SyncFunc, bool) {
- parser, found := p.nodeSyncMap[nodeID]
- return parser, found
- }
|