From 6fd3d6a349ccabb2a99ad397e422b410096e0fa7 Mon Sep 17 00:00:00 2001 From: flswld Date: Thu, 29 Dec 2022 22:42:57 +0800 Subject: [PATCH] =?UTF-8?q?=E7=8E=A9=E5=AE=B6=E7=AE=A1=E7=90=86=E6=A8=A1?= =?UTF-8?q?=E5=9D=97=E6=8E=A5=E5=85=A5redis=EF=BC=8C=E5=B7=B2=E5=AE=8C?= =?UTF-8?q?=E6=95=B4=E5=AE=9E=E7=8E=B0=E5=A5=BD=E5=8F=8B=E3=80=81=E5=A4=9A?= =?UTF-8?q?=E4=BA=BA=E4=B8=96=E7=95=8C=E7=9A=84=E8=B7=A8=E6=9C=8D=E4=BA=A4?= =?UTF-8?q?=E4=BA=92=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cmd/gs/application.toml | 4 + common/config/config.go | 6 + common/mq/net_msg.go | 83 ++-- dispatch/dao/dao.go | 16 +- gate/net/kcp_connect_manager.go | 12 +- go.mod | 7 +- go.sum | 14 +- gs/dao/dao.go | 34 +- gs/dao/{player_dao.go => player_mongo.go} | 0 gs/dao/player_redis.go | 53 +++ gs/game/command_manager.go | 2 +- gs/game/game_manager.go | 12 +- gs/game/local_event_manager.go | 33 +- gs/game/route_manager.go | 26 +- gs/game/user_chat.go | 107 ++++-- gs/game/user_common_handler.go | 58 +-- gs/game/user_login.go | 31 +- gs/game/user_manager.go | 307 +++++++++++---- gs/game/user_multiplayer.go | 261 ++++++++++--- gs/game/user_scene.go | 19 +- gs/game/user_social.go | 358 ++++++++++++++---- gs/game/user_team.go | 4 + .../{user_video_player.go => video_player.go} | 0 gs/game/world_manager.go | 8 +- pkg/logger/logger.go | 46 +-- 25 files changed, 1094 insertions(+), 407 deletions(-) rename gs/dao/{player_dao.go => player_mongo.go} (100%) create mode 100644 gs/dao/player_redis.go rename gs/game/{user_video_player.go => video_player.go} (100%) diff --git a/cmd/gs/application.toml b/cmd/gs/application.toml index 05ae0d18..e2543298 100644 --- a/cmd/gs/application.toml +++ b/cmd/gs/application.toml @@ -13,5 +13,9 @@ max_size = 10485760 [database] url = "mongodb://mongo:27017" +[redis] +addr = "redis:6379" +password = "" + [mq] nats_url = "nats://nats:4222" diff --git a/common/config/config.go b/common/config/config.go index 021a6323..b8006762 100644 --- a/common/config/config.go +++ b/common/config/config.go @@ -13,6 +13,7 @@ type Config struct { HttpPort int32 `toml:"http_port"` Logger Logger `toml:"logger"` Database Database `toml:"database"` + Redis Redis `toml:"redis"` Hk4e Hk4e `toml:"hk4e"` MQ MQ `toml:"mq"` } @@ -30,6 +31,11 @@ type Database struct { Url string `toml:"url"` } +type Redis struct { + Addr string `toml:"addr"` + Password string `toml:"password"` +} + // Hk4e 原神相关 type Hk4e struct { KcpPort int32 `toml:"kcp_port"` diff --git a/common/mq/net_msg.go b/common/mq/net_msg.go index 4afd810f..641a3df4 100644 --- a/common/mq/net_msg.go +++ b/common/mq/net_msg.go @@ -3,10 +3,10 @@ package mq import pb "google.golang.org/protobuf/proto" const ( - MsgTypeGame = iota - MsgTypeFight - MsgTypeConnCtrl - MsgTypeServer + MsgTypeGame = iota // 来自客户端的游戏消息 + MsgTypeFight // 战斗服务器消息 + MsgTypeConnCtrl // GATE客户端连接信息消息 + MsgTypeServer // 服务器之间转发的消息 ) type NetMsg struct { @@ -22,8 +22,7 @@ type NetMsg struct { } const ( - NormalMsg = iota - UserOfflineNotify + NormalMsg = iota // 正常的游戏消息 ) type GameMsg struct { @@ -35,9 +34,10 @@ type GameMsg struct { } const ( - ClientRttNotify = iota - ClientTimeNotify - KickPlayerNotify + ClientRttNotify = iota // 客户端网络时延上报 + ClientTimeNotify // 客户端本地时间上报 + KickPlayerNotify // 通知GATE剔除玩家 + UserOfflineNotify // 玩家离线通知GS ) type ConnCtrlMsg struct { @@ -49,10 +49,10 @@ type ConnCtrlMsg struct { } const ( - AddFightRoutine = iota - DelFightRoutine - FightRoutineAddEntity - FightRoutineDelEntity + AddFightRoutine = iota // 添加战斗实例 + DelFightRoutine // 删除战斗实例 + FightRoutineAddEntity // 战斗实例添加实体 + FightRoutineDelEntity // 战斗实例删除实体 ) type FightMsg struct { @@ -65,20 +65,27 @@ type FightMsg struct { } const ( - ServerAppidBindNotify = iota - ServerUserOnlineStateChangeNotify - ServerGetUserBaseInfoReq - ServerGetUserBaseInfoRsp - ServerUserGsChangeNotify + ServerAppidBindNotify = iota // 玩家连接绑定的各个服务器appid通知 + ServerUserOnlineStateChangeNotify // 广播玩家上线和离线状态以及所在GS的appid + ServerUserBaseInfoReq // 跨服玩家基础数据请求 + ServerUserBaseInfoRsp // 跨服玩家基础数据响应 + ServerUserGsChangeNotify // 跨服玩家迁移通知 + ServerUserMpReq // 跨服多人世界相关请求 + ServerUserMpRsp // 跨服多人世界相关响应 + ServerChatMsgNotify // 跨服玩家聊天消息通知 + ServerAddFriendNotify // 跨服添加好友通知 ) type ServerMsg struct { - FightServerAppId string `msgpack:"FightServerAppId"` - UserId uint32 `msgpack:"UserId"` - IsOnline bool `msgpack:"IsOnline"` - UserBaseInfo *UserBaseInfo `msgpack:"UserBaseInfo"` - GameServerAppId string `msgpack:"GameServerAppId"` - JoinHostUserId uint32 `msgpack:"JoinHostUserId"` + FightServerAppId string `msgpack:"FightServerAppId"` + UserId uint32 `msgpack:"UserId"` + IsOnline bool `msgpack:"IsOnline"` + UserBaseInfo *UserBaseInfo `msgpack:"UserBaseInfo"` + GameServerAppId string `msgpack:"GameServerAppId"` + JoinHostUserId uint32 `msgpack:"JoinHostUserId"` + UserMpInfo *UserMpInfo `msgpack:"UserMpInfo"` + ChatMsgInfo *ChatMsgInfo `msgpack:"ChatMsgInfo"` + AddFriendInfo *AddFriendInfo `msgpack:"AddFriendInfo"` } type OriginInfo struct { @@ -96,4 +103,32 @@ type UserBaseInfo struct { Signature string `msgpack:"Signature"` HeadImageId uint32 `msgpack:"HeadImageId"` WorldPlayerNum uint32 `msgpack:"WorldPlayerNum"` + WorldLevel uint32 `msgpack:"WorldLevel"` + Birthday []uint8 `msgpack:"Birthday"` +} + +type UserMpInfo struct { + OriginInfo *OriginInfo `msgpack:"OriginInfo"` + HostUserId uint32 `msgpack:"HostUserId"` + ApplyUserId uint32 `msgpack:"ApplyUserId"` + ApplyPlayerOnlineInfo *UserBaseInfo `msgpack:"ApplyPlayerOnlineInfo"` + ApplyOk bool `msgpack:"ApplyOk"` + Agreed bool `msgpack:"Agreed"` + HostNickname string `msgpack:"HostNickname"` +} + +type ChatMsgInfo struct { + Time uint32 `msgpack:"Time"` + ToUid uint32 `msgpack:"ToUid"` + Uid uint32 `msgpack:"Uid"` + IsRead bool `msgpack:"IsRead"` + MsgType uint8 `msgpack:"MsgType"` + Text string `msgpack:"Text"` + Icon uint32 `msgpack:"Icon"` +} + +type AddFriendInfo struct { + OriginInfo *OriginInfo `msgpack:"OriginInfo"` + TargetUserId uint32 `msgpack:"TargetUserId"` + ApplyPlayerOnlineInfo *UserBaseInfo `msgpack:"ApplyPlayerOnlineInfo"` } diff --git a/dispatch/dao/dao.go b/dispatch/dao/dao.go index b292802b..5de09cd8 100644 --- a/dispatch/dao/dao.go +++ b/dispatch/dao/dao.go @@ -8,28 +8,34 @@ import ( "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" + "go.mongodb.org/mongo-driver/mongo/readpref" ) type Dao struct { - client *mongo.Client - db *mongo.Database + mongo *mongo.Client + db *mongo.Database } func NewDao() (r *Dao) { r = new(Dao) - clientOptions := options.Client().ApplyURI(config.CONF.Database.Url) + clientOptions := options.Client().ApplyURI(config.CONF.Database.Url).SetMinPoolSize(10).SetMaxPoolSize(100) client, err := mongo.Connect(context.TODO(), clientOptions) if err != nil { logger.Error("mongo connect error: %v", err) return nil } - r.client = client + err = client.Ping(context.TODO(), readpref.Primary()) + if err != nil { + logger.Error("mongo ping error: %v", err) + return nil + } + r.mongo = client r.db = client.Database("dispatch_hk4e") return r } func (d *Dao) CloseDao() { - err := d.client.Disconnect(context.TODO()) + err := d.mongo.Disconnect(context.TODO()) if err != nil { logger.Error("mongo close error: %v", err) } diff --git a/gate/net/kcp_connect_manager.go b/gate/net/kcp_connect_manager.go index 3c60c2e6..88b00c1a 100644 --- a/gate/net/kcp_connect_manager.go +++ b/gate/net/kcp_connect_manager.go @@ -361,14 +361,14 @@ func (k *KcpConnectManager) closeKcpConn(session *Session, enetType uint32) { EventId: KcpConnCloseNotify, } // 通知GS玩家下线 - gameMsg := new(mq.GameMsg) - gameMsg.UserId = session.userId + connCtrlMsg := new(mq.ConnCtrlMsg) + connCtrlMsg.UserId = session.userId k.messageQueue.SendToGs(session.gsServerAppId, &mq.NetMsg{ - MsgType: mq.MsgTypeGame, - EventId: mq.UserOfflineNotify, - GameMsg: gameMsg, + MsgType: mq.MsgTypeConnCtrl, + EventId: mq.UserOfflineNotify, + ConnCtrlMsg: connCtrlMsg, }) - logger.Info("send to gs user offline, ConvId: %v, UserId: %v", convId, gameMsg.UserId) + logger.Info("send to gs user offline, ConvId: %v, UserId: %v", convId, connCtrlMsg.UserId) k.destroySessionChan <- session } diff --git a/go.mod b/go.mod index c2572395..b1f44b10 100644 --- a/go.mod +++ b/go.mod @@ -50,8 +50,13 @@ require github.com/byebyebruce/natsrpc v0.5.5-0.20221125150611-56cd29a4e335 // cobra require github.com/spf13/cobra v1.6.1 +// redis +require github.com/go-redis/redis/v8 v8.11.5 + require ( + github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/gin-contrib/sse v0.1.0 // indirect github.com/go-playground/locales v0.13.0 // indirect github.com/go-playground/universal-translator v0.17.0 // indirect @@ -83,6 +88,6 @@ require ( golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e // indirect golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec // indirect golang.org/x/text v0.3.6 // indirect - gopkg.in/yaml.v2 v2.2.8 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 079b6ea9..1d12c77e 100644 --- a/go.sum +++ b/go.sum @@ -6,15 +6,20 @@ github.com/arl/statsviz v0.5.1/go.mod h1:zDnjgRblGm1Dyd7J5YlbH7gM1/+HRC+SfkhZhQb github.com/byebyebruce/natsrpc v0.5.5-0.20221125150611-56cd29a4e335 h1:V5qahA5kDL/TBnlwvYjemR5du/uQ7q75qkBBlTc4rXI= github.com/byebyebruce/natsrpc v0.5.5-0.20221125150611-56cd29a4e335/go.mod h1:w61gLVOQWr/Tq/1wxSOMLxDPbH66rEo8jEHMh7j3qjo= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= +github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14= @@ -27,6 +32,8 @@ github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD87 github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY= github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= +github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= +github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/golang-jwt/jwt/v4 v4.4.0 h1:EmVIxB5jzbllGIjiCV5JG4VylbK3KE400tLGLI1cdfU= @@ -96,6 +103,9 @@ github.com/nats-io/nkeys v0.3.0 h1:cgM5tL53EvYRU+2YLXIK0G2mJtK12Ft9oeooSZMA2G8= github.com/nats-io/nkeys v0.3.0/go.mod h1:gvUNGjVcM2IPr5rCsRsC6Wb3Hr2CQAm08dsxtV6A5y4= github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -212,9 +222,11 @@ google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/gs/dao/dao.go b/gs/dao/dao.go index e077eb44..48e962fe 100644 --- a/gs/dao/dao.go +++ b/gs/dao/dao.go @@ -6,31 +6,55 @@ import ( "hk4e/common/config" "hk4e/pkg/logger" + "github.com/go-redis/redis/v8" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" + "go.mongodb.org/mongo-driver/mongo/readpref" ) type Dao struct { - client *mongo.Client - db *mongo.Database + mongo *mongo.Client + db *mongo.Database + redis *redis.Client } func NewDao() (r *Dao, err error) { r = new(Dao) - clientOptions := options.Client().ApplyURI(config.CONF.Database.Url) + clientOptions := options.Client().ApplyURI(config.CONF.Database.Url).SetMinPoolSize(1).SetMaxPoolSize(10) client, err := mongo.Connect(context.TODO(), clientOptions) if err != nil { logger.Error("mongo connect error: %v", err) return nil, err } - r.client = client + err = client.Ping(context.TODO(), readpref.Primary()) + if err != nil { + logger.Error("mongo ping error: %v", err) + return nil, err + } + r.mongo = client r.db = client.Database("gs_hk4e") + r.redis = redis.NewClient(&redis.Options{ + Addr: config.CONF.Redis.Addr, + Password: config.CONF.Redis.Password, + DB: 0, + PoolSize: 10, + MinIdleConns: 1, + }) + err = r.redis.Ping(context.TODO()).Err() + if err != nil { + logger.Error("redis ping error: %v", err) + return nil, err + } return r, nil } func (d *Dao) CloseDao() { - err := d.client.Disconnect(context.TODO()) + err := d.mongo.Disconnect(context.TODO()) if err != nil { logger.Error("mongo close error: %v", err) } + err = d.redis.Close() + if err != nil { + logger.Error("redis close error: %v", err) + } } diff --git a/gs/dao/player_dao.go b/gs/dao/player_mongo.go similarity index 100% rename from gs/dao/player_dao.go rename to gs/dao/player_mongo.go diff --git a/gs/dao/player_redis.go b/gs/dao/player_redis.go new file mode 100644 index 00000000..966d0def --- /dev/null +++ b/gs/dao/player_redis.go @@ -0,0 +1,53 @@ +package dao + +import ( + "context" + "strconv" + "time" + + "hk4e/gs/model" + "hk4e/pkg/logger" + + "github.com/vmihailenco/msgpack/v5" +) + +const RedisPlayerKeyPrefix = "HK4E" + +func (d *Dao) GetRedisPlayerKey(userId uint32) string { + return RedisPlayerKeyPrefix + ":USER:" + strconv.Itoa(int(userId)) +} + +func (d *Dao) GetRedisPlayer(userId uint32) *model.Player { + playerData, err := d.redis.Get(context.TODO(), d.GetRedisPlayerKey(userId)).Result() + if err != nil { + logger.Error("get player from redis error: %v", err) + return nil + } + player := new(model.Player) + err = msgpack.Unmarshal([]byte(playerData), player) + if err != nil { + logger.Error("unmarshal player error: %v", err) + return nil + } + return player +} + +func (d *Dao) SetRedisPlayer(player *model.Player) { + playerData, err := msgpack.Marshal(player) + if err != nil { + logger.Error("marshal player error: %v", err) + return + } + err = d.redis.Set(context.TODO(), d.GetRedisPlayerKey(player.PlayerID), playerData, time.Hour*24*30).Err() + if err != nil { + logger.Error("set player from redis error: %v", err) + return + } +} + +func (d *Dao) SetRedisPlayerList(playerList []*model.Player) { + // TODO 换成redis批量命令执行 + for _, player := range playerList { + d.SetRedisPlayer(player) + } +} diff --git a/gs/game/command_manager.go b/gs/game/command_manager.go index 5c48bc12..95f6aedf 100644 --- a/gs/game/command_manager.go +++ b/gs/game/command_manager.go @@ -225,7 +225,7 @@ func (c *CommandManager) SendMessage(executor any, msg string, param ...any) { case *model.Player: // 玩家类型 player := executor.(*model.Player) - GAME_MANAGER.SendPrivateChat(c.system, player, fmt.Sprintf(msg, param...)) + GAME_MANAGER.SendPrivateChat(c.system, player.PlayerID, fmt.Sprintf(msg, param...)) case string: // GM接口等 // str := executor.(string) diff --git a/gs/game/game_manager.go b/gs/game/game_manager.go index 0cee4e54..29b21a95 100644 --- a/gs/game/game_manager.go +++ b/gs/game/game_manager.go @@ -26,12 +26,12 @@ var USER_MANAGER *UserManager = nil var WORLD_MANAGER *WorldManager = nil var TICK_MANAGER *TickManager = nil var COMMAND_MANAGER *CommandManager = nil +var MESSAGE_QUEUE *mq.MessageQueue var SELF *model.Player type GameManager struct { dao *dao.Dao - messageQueue *mq.MessageQueue snowflake *alg.SnowflakeWorker clientCmdProtoMap *client_proto.ClientCmdProtoMap clientCmdProtoMapRefValue reflect.Value @@ -40,7 +40,7 @@ type GameManager struct { func NewGameManager(dao *dao.Dao, messageQueue *mq.MessageQueue, gsId uint32) (r *GameManager) { r = new(GameManager) r.dao = dao - r.messageQueue = messageQueue + MESSAGE_QUEUE = messageQueue r.snowflake = alg.NewSnowflakeWorker(int64(gsId)) if appConfig.CONF.Hk4e.ClientProtoProxyEnable { r.clientCmdProtoMap = client_proto.NewClientCmdProtoMap() @@ -115,7 +115,7 @@ func (g *GameManager) gameMainLoop() { commandCost = 0 } select { - case netMsg := <-g.messageQueue.GetNetMsg(): + case netMsg := <-MESSAGE_QUEUE.GetNetMsg(): // 接收客户端消息 start := time.Now().UnixNano() ROUTE_MANAGER.RouteHandle(netMsg) @@ -171,7 +171,7 @@ func (g *GameManager) SendMsgEx(cmdId uint16, userId uint32, clientSeq uint32, g ClientSeq: clientSeq, PayloadMessage: payloadMsg, } - g.messageQueue.SendToGate(gateAppId, &mq.NetMsg{ + MESSAGE_QUEUE.SendToGate(gateAppId, &mq.NetMsg{ MsgType: mq.MsgTypeGame, EventId: mq.NormalMsg, GameMsg: gameMsg, @@ -203,7 +203,7 @@ func (g *GameManager) SendMsg(cmdId uint16, userId uint32, clientSeq uint32, pay return } gameMsg.PayloadMessageData = payloadMessageData - g.messageQueue.SendToGate(player.GateAppId, &mq.NetMsg{ + MESSAGE_QUEUE.SendToGate(player.GateAppId, &mq.NetMsg{ MsgType: mq.MsgTypeGame, EventId: mq.NormalMsg, GameMsg: gameMsg, @@ -271,7 +271,7 @@ func (g *GameManager) DisconnectPlayer(userId uint32, reason uint32) { if player == nil { return } - g.messageQueue.SendToGate(player.GateAppId, &mq.NetMsg{ + MESSAGE_QUEUE.SendToGate(player.GateAppId, &mq.NetMsg{ MsgType: mq.MsgTypeConnCtrl, EventId: mq.KickPlayerNotify, ConnCtrlMsg: &mq.ConnCtrlMsg{ diff --git a/gs/game/local_event_manager.go b/gs/game/local_event_manager.go index 4dee0f40..b60ec103 100644 --- a/gs/game/local_event_manager.go +++ b/gs/game/local_event_manager.go @@ -3,6 +3,7 @@ package game import ( "time" + "hk4e/common/mq" "hk4e/gs/model" "hk4e/pkg/logger" "hk4e/pkg/object" @@ -11,9 +12,10 @@ import ( // 本地事件队列管理器 const ( - LoadLoginUserFromDbFinish = iota - CheckUserExistOnRegFromDbFinish - RunUserCopyAndSave + LoadLoginUserFromDbFinish = iota // 玩家登录从数据库加载完成回调 + CheckUserExistOnRegFromDbFinish // 玩家注册从数据库查询是否已存在完成回调 + RunUserCopyAndSave // 执行一次在线玩家内存数据复制到数据库写入协程 + UserOfflineSaveToDbFinish ) type LocalEvent struct { @@ -89,5 +91,30 @@ func (l *LocalEventManager) LocalEventHandle(localEvent *LocalEvent) { endTime := time.Now().UnixNano() costTime := endTime - startTime logger.Info("run save user copy cost time: %v ns", costTime) + case UserOfflineSaveToDbFinish: + playerOfflineInfo := localEvent.Msg.(*PlayerOfflineInfo) + USER_MANAGER.DeleteUser(playerOfflineInfo.Player.PlayerID) + MESSAGE_QUEUE.SendToAll(&mq.NetMsg{ + MsgType: mq.MsgTypeServer, + EventId: mq.ServerUserOnlineStateChangeNotify, + ServerMsg: &mq.ServerMsg{ + UserId: playerOfflineInfo.Player.PlayerID, + IsOnline: false, + }, + }) + if playerOfflineInfo.ChangeGsInfo.IsChangeGs { + gsAppId := USER_MANAGER.GetRemoteUserGsAppId(playerOfflineInfo.ChangeGsInfo.JoinHostUserId) + MESSAGE_QUEUE.SendToGate(playerOfflineInfo.Player.GateAppId, &mq.NetMsg{ + MsgType: mq.MsgTypeServer, + EventId: mq.ServerUserGsChangeNotify, + ServerMsg: &mq.ServerMsg{ + UserId: playerOfflineInfo.Player.PlayerID, + GameServerAppId: gsAppId, + JoinHostUserId: playerOfflineInfo.ChangeGsInfo.JoinHostUserId, + }, + }) + logger.Info("user change gs notify to gate, uid: %v, gate appid: %v, gs appid: %v, host uid: %v", + playerOfflineInfo.Player.PlayerID, playerOfflineInfo.Player.GateAppId, gsAppId, playerOfflineInfo.ChangeGsInfo.JoinHostUserId) + } } } diff --git a/gs/game/route_manager.go b/gs/game/route_manager.go index 602eccfa..f76f3312 100644 --- a/gs/game/route_manager.go +++ b/gs/game/route_manager.go @@ -42,6 +42,10 @@ func (r *RouteManager) doRoute(cmdId uint16, userId uint32, clientSeq uint32, pa GAME_MANAGER.DisconnectPlayer(userId, kcp.EnetNotFoundSession) return } + if !player.Online { + logger.Error("player not online, uid: %v", userId) + return + } player.ClientSeq = clientSeq SELF = player handlerFunc(player, payloadMsg) @@ -139,8 +143,6 @@ func (r *RouteManager) RouteHandle(netMsg *mq.NetMsg) { return } r.doRoute(gameMsg.CmdId, gameMsg.UserId, gameMsg.ClientSeq, gameMsg.PayloadMessage) - case mq.UserOfflineNotify: - GAME_MANAGER.OnUserOffline(gameMsg.UserId) } case mq.MsgTypeConnCtrl: if netMsg.OriginServerType != api.GATE { @@ -152,6 +154,10 @@ func (r *RouteManager) RouteHandle(netMsg *mq.NetMsg) { GAME_MANAGER.ClientRttNotify(connCtrlMsg.UserId, connCtrlMsg.ClientRtt) case mq.ClientTimeNotify: GAME_MANAGER.ClientTimeNotify(connCtrlMsg.UserId, connCtrlMsg.ClientTime) + case mq.UserOfflineNotify: + GAME_MANAGER.OnUserOffline(connCtrlMsg.UserId, &ChangeGsInfo{ + IsChangeGs: false, + }) } case mq.MsgTypeServer: serverMsg := netMsg.ServerMsg @@ -161,10 +167,18 @@ func (r *RouteManager) RouteHandle(netMsg *mq.NetMsg) { USER_MANAGER.SetRemoteUserOnlineState(serverMsg.UserId, serverMsg.IsOnline, netMsg.OriginServerAppId) case mq.ServerAppidBindNotify: GAME_MANAGER.ServerAppidBindNotify(serverMsg.UserId, serverMsg.FightServerAppId, serverMsg.JoinHostUserId) - case mq.ServerGetUserBaseInfoReq: - GAME_MANAGER.ServerGetUserBaseInfoReq(serverMsg.UserBaseInfo, netMsg.OriginServerAppId) - case mq.ServerGetUserBaseInfoRsp: - GAME_MANAGER.ServerGetUserBaseInfoRsp(serverMsg.UserBaseInfo) + case mq.ServerUserBaseInfoReq: + GAME_MANAGER.ServerUserBaseInfoReq(serverMsg.UserBaseInfo, netMsg.OriginServerAppId) + case mq.ServerUserBaseInfoRsp: + GAME_MANAGER.ServerUserBaseInfoRsp(serverMsg.UserBaseInfo) + case mq.ServerUserMpReq: + GAME_MANAGER.ServerUserMpReq(serverMsg.UserMpInfo, netMsg.OriginServerAppId) + case mq.ServerUserMpRsp: + GAME_MANAGER.ServerUserMpRsp(serverMsg.UserMpInfo) + case mq.ServerChatMsgNotify: + GAME_MANAGER.ServerChatMsgNotify(serverMsg.ChatMsgInfo) + case mq.ServerAddFriendNotify: + GAME_MANAGER.ServerAddFriendNotify(serverMsg.AddFriendInfo) } } } diff --git a/gs/game/user_chat.go b/gs/game/user_chat.go index 0e1e1beb..2fc40078 100644 --- a/gs/game/user_chat.go +++ b/gs/game/user_chat.go @@ -3,6 +3,7 @@ package game import ( "time" + "hk4e/common/mq" "hk4e/gs/model" "hk4e/pkg/logger" "hk4e/protocol/cmd" @@ -78,15 +79,14 @@ func (g *GameManager) PullPrivateChatReq(player *model.Player, payloadMsg pb.Mes } // SendPrivateChat 发送私聊文本消息给玩家 -func (g *GameManager) SendPrivateChat(player, targetPlayer *model.Player, content any) { +func (g *GameManager) SendPrivateChat(player *model.Player, targetUid uint32, content any) { chatInfo := &proto.ChatInfo{ Time: uint32(time.Now().Unix()), Sequence: 101, - ToUid: targetPlayer.PlayerID, + ToUid: targetUid, Uid: player.PlayerID, IsRead: false, } - // 根据传入的值判断消息类型 switch content.(type) { case string: @@ -100,21 +100,52 @@ func (g *GameManager) SendPrivateChat(player, targetPlayer *model.Player, conten Icon: content.(uint32), } } - + chatMsg := g.ConvChatInfoToChatMsg(chatInfo) // 消息加入自己的队列 - msgList, exist := player.ChatMsgMap[targetPlayer.PlayerID] + msgList, exist := player.ChatMsgMap[targetUid] if !exist { msgList = make([]*model.ChatMsg, 0) } - msgList = append(msgList, g.ConvChatInfoToChatMsg(chatInfo)) - player.ChatMsgMap[targetPlayer.PlayerID] = msgList + msgList = append(msgList, chatMsg) + player.ChatMsgMap[targetUid] = msgList + privateChatNotify := &proto.PrivateChatNotify{ + ChatInfo: chatInfo, + } + g.SendMsg(cmd.PrivateChatNotify, player.PlayerID, player.ClientSeq, privateChatNotify) + + targetPlayer := USER_MANAGER.GetOnlineUser(targetUid) + if targetPlayer == nil { + if USER_MANAGER.GetRemoteUserOnlineState(targetUid) { + // 目标玩家在别的服在线 + gsAppId := USER_MANAGER.GetRemoteUserGsAppId(targetUid) + MESSAGE_QUEUE.SendToGs(gsAppId, &mq.NetMsg{ + MsgType: mq.MsgTypeServer, + EventId: mq.ServerChatMsgNotify, + ServerMsg: &mq.ServerMsg{ + ChatMsgInfo: &mq.ChatMsgInfo{ + Time: chatMsg.Time, + ToUid: chatMsg.ToUid, + Uid: chatMsg.Uid, + IsRead: chatMsg.IsRead, + MsgType: chatMsg.MsgType, + Text: chatMsg.Text, + Icon: chatMsg.Icon, + }, + }, + }) + } else { + // 目标玩家全服离线 + // TODO 接入redis直接同步写入数据 + } + return + } // 消息加入目标玩家的队列 msgList, exist = targetPlayer.ChatMsgMap[player.PlayerID] if !exist { msgList = make([]*model.ChatMsg, 0) } - msgList = append(msgList, g.ConvChatInfoToChatMsg(chatInfo)) + msgList = append(msgList, chatMsg) targetPlayer.ChatMsgMap[player.PlayerID] = msgList // 如果目标玩家在线发送消息 @@ -124,11 +155,6 @@ func (g *GameManager) SendPrivateChat(player, targetPlayer *model.Player, conten } g.SendMsg(cmd.PrivateChatNotify, targetPlayer.PlayerID, player.ClientSeq, privateChatNotify) } - - privateChatNotify := &proto.PrivateChatNotify{ - ChatInfo: chatInfo, - } - g.SendMsg(cmd.PrivateChatNotify, player.PlayerID, player.ClientSeq, privateChatNotify) } func (g *GameManager) PrivateChatReq(player *model.Player, payloadMsg pb.Message) { @@ -137,12 +163,6 @@ func (g *GameManager) PrivateChatReq(player *model.Player, payloadMsg pb.Message targetUid := req.TargetUid content := req.Content - // TODO 同步阻塞待优化 - targetPlayer := USER_MANAGER.LoadTempOfflineUserSync(targetUid) - if targetPlayer == nil { - return - } - // 根据发送的类型发送消息 switch content.(type) { case *proto.PrivateChatReq_Text: @@ -150,25 +170,14 @@ func (g *GameManager) PrivateChatReq(player *model.Player, payloadMsg pb.Message if len(text) == 0 { return } - // 发送私聊文本消息 - g.SendPrivateChat(player, targetPlayer, text) - + g.SendPrivateChat(player, targetUid, text) // 输入命令 会检测是否为命令的 COMMAND_MANAGER.InputCommand(player, text) - - if text == "VPU" { - g.VideoPlayerUpdate(false) - } else if text == "VPUR" { - g.VideoPlayerUpdate(true) - } - case *proto.PrivateChatReq_Icon: icon := content.(*proto.PrivateChatReq_Icon).Icon - // 发送私聊图标消息 - g.SendPrivateChat(player, targetPlayer, icon) - + g.SendPrivateChat(player, targetUid, icon) default: return } @@ -281,3 +290,37 @@ func (g *GameManager) ConvChatMsgToChatInfo(chatMsg *model.ChatMsg) (chatInfo *p } return chatInfo } + +// 跨服玩家聊天通知 + +func (g *GameManager) ServerChatMsgNotify(chatMsgInfo *mq.ChatMsgInfo) { + targetPlayer := USER_MANAGER.GetOnlineUser(chatMsgInfo.ToUid) + if targetPlayer == nil { + logger.Error("player is nil, uid: %v", chatMsgInfo.ToUid) + return + } + chatMsg := &model.ChatMsg{ + Time: chatMsgInfo.Time, + ToUid: chatMsgInfo.ToUid, + Uid: chatMsgInfo.Uid, + IsRead: chatMsgInfo.IsRead, + MsgType: chatMsgInfo.MsgType, + Text: chatMsgInfo.Text, + Icon: chatMsgInfo.Icon, + } + // 消息加入目标玩家的队列 + msgList, exist := targetPlayer.ChatMsgMap[chatMsgInfo.Uid] + if !exist { + msgList = make([]*model.ChatMsg, 0) + } + msgList = append(msgList, chatMsg) + targetPlayer.ChatMsgMap[chatMsgInfo.Uid] = msgList + + // 如果目标玩家在线发送消息 + if targetPlayer.Online { + privateChatNotify := &proto.PrivateChatNotify{ + ChatInfo: g.ConvChatMsgToChatInfo(chatMsg), + } + g.SendMsg(cmd.PrivateChatNotify, targetPlayer.PlayerID, targetPlayer.ClientSeq, privateChatNotify) + } +} diff --git a/gs/game/user_common_handler.go b/gs/game/user_common_handler.go index af229599..62ede04f 100644 --- a/gs/game/user_common_handler.go +++ b/gs/game/user_common_handler.go @@ -3,7 +3,6 @@ package game import ( "time" - "hk4e/common/constant" "hk4e/common/mq" "hk4e/gs/model" "hk4e/pkg/logger" @@ -131,7 +130,10 @@ func (g *GameManager) ServerAppidBindNotify(userId uint32, fightAppId string, jo } if joinHostUserId != 0 { hostPlayer := USER_MANAGER.GetOnlineUser(joinHostUserId) - g.HostEnterMpWorld(hostPlayer, player.PlayerID) + if hostPlayer == nil { + logger.Error("player is nil, uid: %v", joinHostUserId) + return + } g.JoinOtherWorld(player, hostPlayer) return } @@ -139,7 +141,7 @@ func (g *GameManager) ServerAppidBindNotify(userId uint32, fightAppId string, jo player.FightAppId = fightAppId // 创建世界 world := WORLD_MANAGER.CreateWorld(player) - GAME_MANAGER.messageQueue.SendToFight(fightAppId, &mq.NetMsg{ + MESSAGE_QUEUE.SendToFight(fightAppId, &mq.NetMsg{ MsgType: mq.MsgTypeFight, EventId: mq.AddFightRoutine, FightMsg: &mq.FightMsg{ @@ -153,53 +155,3 @@ func (g *GameManager) ServerAppidBindNotify(userId uint32, fightAppId string, jo player.SceneLoadState = model.SceneNone g.SendMsg(cmd.PlayerEnterSceneNotify, userId, player.ClientSeq, g.PacketPlayerEnterSceneNotifyLogin(player, proto.EnterType_ENTER_TYPE_SELF)) } - -func (g *GameManager) ServerGetUserBaseInfoReq(userBaseInfo *mq.UserBaseInfo, gsAppId string) { - player := USER_MANAGER.GetOnlineUser(userBaseInfo.UserId) - if player == nil { - logger.Error("player is nil, uid: %v", userBaseInfo.UserId) - return - } - world := WORLD_MANAGER.GetWorldByID(player.WorldId) - g.messageQueue.SendToGs(gsAppId, &mq.NetMsg{ - MsgType: mq.MsgTypeServer, - EventId: mq.ServerGetUserBaseInfoRsp, - ServerMsg: &mq.ServerMsg{ - UserBaseInfo: &mq.UserBaseInfo{ - OriginInfo: userBaseInfo.OriginInfo, - UserId: player.PlayerID, - Nickname: player.NickName, - PlayerLevel: player.PropertiesMap[constant.PlayerPropertyConst.PROP_PLAYER_LEVEL], - MpSettingType: uint8(player.PropertiesMap[constant.PlayerPropertyConst.PROP_PLAYER_MP_SETTING_TYPE]), - NameCardId: player.NameCard, - Signature: player.Signature, - HeadImageId: player.HeadImage, - WorldPlayerNum: uint32(world.GetWorldPlayerNum()), - }, - }, - }) -} - -func (g *GameManager) ServerGetUserBaseInfoRsp(userBaseInfo *mq.UserBaseInfo) { - switch userBaseInfo.OriginInfo.CmdName { - case "GetOnlinePlayerInfoReq": - player := USER_MANAGER.GetOnlineUser(userBaseInfo.OriginInfo.UserId) - if player == nil { - logger.Error("player is nil, uid: %v", userBaseInfo.OriginInfo.UserId) - return - } - g.SendMsg(cmd.GetOnlinePlayerInfoRsp, player.PlayerID, player.ClientSeq, &proto.GetOnlinePlayerInfoRsp{ - TargetUid: userBaseInfo.UserId, - TargetPlayerInfo: &proto.OnlinePlayerInfo{ - Uid: userBaseInfo.UserId, - Nickname: userBaseInfo.Nickname, - PlayerLevel: userBaseInfo.PlayerLevel, - MpSettingType: proto.MpSettingType(userBaseInfo.MpSettingType), - NameCardId: userBaseInfo.NameCardId, - Signature: userBaseInfo.Signature, - ProfilePicture: &proto.ProfilePicture{AvatarId: userBaseInfo.HeadImageId}, - CurPlayerNumInWorld: userBaseInfo.WorldPlayerNum, - }, - }) - } -} diff --git a/gs/game/user_login.go b/gs/game/user_login.go index 8b59b236..224dc3ed 100644 --- a/gs/game/user_login.go +++ b/gs/game/user_login.go @@ -61,14 +61,16 @@ func (g *GameManager) OnLoginOk(userId uint32, player *model.Player, clientSeq u g.LoginNotify(userId, player, clientSeq) - g.messageQueue.SendToAll(&mq.NetMsg{ - MsgType: mq.MsgTypeServer, - EventId: mq.ServerUserOnlineStateChangeNotify, - ServerMsg: &mq.ServerMsg{ - UserId: userId, - IsOnline: true, - }, - }) + if userId >= 100000000 { + MESSAGE_QUEUE.SendToAll(&mq.NetMsg{ + MsgType: mq.MsgTypeServer, + EventId: mq.ServerUserOnlineStateChangeNotify, + ServerMsg: &mq.ServerMsg{ + UserId: userId, + IsOnline: true, + }, + }) + } } func (g *GameManager) OnReg(userId uint32, clientSeq uint32, gateAppId string, payloadMsg pb.Message) { @@ -106,7 +108,7 @@ func (g *GameManager) OnRegOk(exist bool, req *proto.SetPlayerBornDataReq, userI g.OnLogin(userId, clientSeq, gateAppId) } -func (g *GameManager) OnUserOffline(userId uint32) { +func (g *GameManager) OnUserOffline(userId uint32, changeGsInfo *ChangeGsInfo) { logger.Info("user offline, uid: %v", userId) player := USER_MANAGER.GetOnlineUser(userId) if player == nil { @@ -120,16 +122,7 @@ func (g *GameManager) OnUserOffline(userId uint32) { player.OfflineTime = uint32(time.Now().Unix()) player.Online = false player.TotalOnlineTime += uint32(time.Now().UnixMilli()) - player.OnlineTime - USER_MANAGER.DeleteUser(player) - - g.messageQueue.SendToAll(&mq.NetMsg{ - MsgType: mq.MsgTypeServer, - EventId: mq.ServerUserOnlineStateChangeNotify, - ServerMsg: &mq.ServerMsg{ - UserId: userId, - IsOnline: false, - }, - }) + USER_MANAGER.OfflineUser(player, changeGsInfo) } func (g *GameManager) LoginNotify(userId uint32, player *model.Player, clientSeq uint32) { diff --git a/gs/game/user_manager.go b/gs/game/user_manager.go index 84bc96ab..9bdcfc0b 100644 --- a/gs/game/user_manager.go +++ b/gs/game/user_manager.go @@ -6,56 +6,36 @@ import ( "hk4e/gs/dao" "hk4e/gs/model" "hk4e/pkg/logger" + "hk4e/pkg/object" "hk4e/protocol/proto" ) -type SaveUserData struct { - insertPlayerList []*model.Player - updatePlayerList []*model.Player -} +// 玩家管理器 + +// 玩家注册 从db查询对应uid是否存在并异步回调返回结果 +// 玩家登录 从db查询出来然后写入redis并异步回调返回玩家对象 +// 玩家离线 写入db和redis +// 玩家定时保存 写入db和redis type UserManager struct { - dao *dao.Dao - playerMap map[uint32]*model.Player - saveUserChan chan *SaveUserData - remotePlayerMap map[uint32]string // 远程玩家 key:userId value:玩家所在gs的appid + dao *dao.Dao // db对象 + playerMap map[uint32]*model.Player // 内存玩家数据 + saveUserChan chan *SaveUserData // 用于主协程发送玩家数据给定时保存协程 + remotePlayerMap map[uint32]string // 远程玩家 key:userId value:玩家所在gs的appid } func NewUserManager(dao *dao.Dao) (r *UserManager) { r = new(UserManager) r.dao = dao r.playerMap = make(map[uint32]*model.Player) - r.saveUserChan = make(chan *SaveUserData) + r.saveUserChan = make(chan *SaveUserData) // 无缓冲区chan 避免主协程在写入时被迫加锁 r.remotePlayerMap = make(map[uint32]string) return r } -func (u *UserManager) GetRemoteUserOnlineState(userId uint32) bool { - _, exist := u.remotePlayerMap[userId] - if !exist { - return false - } else { - return true - } -} - -func (u *UserManager) GetRemoteUserGsAppId(userId uint32) string { - appId, exist := u.remotePlayerMap[userId] - if !exist { - return "" - } else { - return appId - } -} - -func (u *UserManager) SetRemoteUserOnlineState(userId uint32, isOnline bool, appId string) { - if isOnline { - u.remotePlayerMap[userId] = appId - } else { - delete(u.remotePlayerMap, userId) - } -} +// 在线玩家相关操作 +// GetUserOnlineState 获取玩家在线状态 func (u *UserManager) GetUserOnlineState(userId uint32) bool { player, exist := u.playerMap[userId] if !exist { @@ -65,6 +45,7 @@ func (u *UserManager) GetUserOnlineState(userId uint32) bool { } } +// GetOnlineUser 获取在线玩家对象 func (u *UserManager) GetOnlineUser(userId uint32) *model.Player { player, exist := u.playerMap[userId] if !exist { @@ -78,6 +59,7 @@ func (u *UserManager) GetOnlineUser(userId uint32) *model.Player { } } +// GetAllOnlineUserList 获取全部在线玩家 func (u *UserManager) GetAllOnlineUserList() map[uint32]*model.Player { onlinePlayerMap := make(map[uint32]*model.Player) for userId, player := range u.playerMap { @@ -97,13 +79,14 @@ type PlayerRegInfo struct { GateAppId string } +// CheckUserExistOnReg 玩家注册检查是否已存在 func (u *UserManager) CheckUserExistOnReg(userId uint32, req *proto.SetPlayerBornDataReq, clientSeq uint32, gateAppId string) (exist bool, asyncWait bool) { _, exist = u.playerMap[userId] if exist { return true, false } else { go func() { - player := u.loadUserFromDb(userId) + player := u.LoadUserFromDbSync(userId) exist = false if player != nil { exist = true @@ -123,38 +106,7 @@ func (u *UserManager) CheckUserExistOnReg(userId uint32, req *proto.SetPlayerBor } } -func (u *UserManager) LoadTempOfflineUserSync(userId uint32) *model.Player { - player, exist := u.playerMap[userId] - if exist { - return player - } else { - player = u.loadUserFromDb(userId) - if player == nil { - return nil - } - u.ChangeUserDbState(player, model.DbDelete) - u.playerMap[player.PlayerID] = player - return player - } -} - -func (u *UserManager) loadUserFromDb(userId uint32) *model.Player { - player, err := u.dao.QueryPlayerByID(userId) - if err != nil { - logger.Error("query player error: %v", err) - return nil - } - return player -} - -func (u *UserManager) saveUserToDb(player *model.Player) { - err := u.dao.UpdatePlayer(player) - if err != nil { - logger.Error("update player error: %v", err) - return - } -} - +// AddUser 向内存玩家数据里添加一个玩家 func (u *UserManager) AddUser(player *model.Player) { if player == nil { return @@ -163,12 +115,9 @@ func (u *UserManager) AddUser(player *model.Player) { u.playerMap[player.PlayerID] = player } -func (u *UserManager) DeleteUser(player *model.Player) { - if player == nil { - return - } - u.ChangeUserDbState(player, model.DbDelete) - u.playerMap[player.PlayerID] = player +// DeleteUser 从内存玩家数据里删除一个玩家 +func (u *UserManager) DeleteUser(userId uint32) { + delete(u.playerMap, userId) } type PlayerLoginInfo struct { @@ -178,6 +127,7 @@ type PlayerLoginInfo struct { GateAppId string } +// OnlineUser 玩家上线 func (u *UserManager) OnlineUser(userId uint32, clientSeq uint32, gateAppId string) (*model.Player, bool) { player, exist := u.playerMap[userId] if exist { @@ -185,8 +135,9 @@ func (u *UserManager) OnlineUser(userId uint32, clientSeq uint32, gateAppId stri return player, false } else { go func() { - player = u.loadUserFromDb(userId) + player = u.LoadUserFromDbSync(userId) if player != nil { + u.SaveUserToRedisSync(player) u.ChangeUserDbState(player, model.DbNormal) } else { logger.Error("can not find user from db, uid: %v", userId) @@ -205,6 +156,38 @@ func (u *UserManager) OnlineUser(userId uint32, clientSeq uint32, gateAppId stri } } +type ChangeGsInfo struct { + IsChangeGs bool + JoinHostUserId uint32 +} + +type PlayerOfflineInfo struct { + Player *model.Player + ChangeGsInfo *ChangeGsInfo +} + +// OfflineUser 玩家离线 +func (u *UserManager) OfflineUser(player *model.Player, changeGsInfo *ChangeGsInfo) { + playerCopy := new(model.Player) + err := object.FastDeepCopy(playerCopy, player) + if err != nil { + logger.Error("deep copy player error: %v", err) + return + } + go func() { + u.SaveUserToDbSync(playerCopy) + u.SaveUserToRedisSync(playerCopy) + LOCAL_EVENT_MANAGER.localEventChan <- &LocalEvent{ + EventId: UserOfflineSaveToDbFinish, + Msg: &PlayerOfflineInfo{ + Player: player, + ChangeGsInfo: changeGsInfo, + }, + } + }() +} + +// ChangeUserDbState 玩家存档状态机 主要用于玩家定时保存时进行分类处理 func (u *UserManager) ChangeUserDbState(player *model.Player, state int) { if player == nil { return @@ -238,8 +221,140 @@ func (u *UserManager) ChangeUserDbState(player *model.Player, state int) { } } -// 用户数据库定时同步 +// 远程玩家相关操作 +func (u *UserManager) GetRemoteUserOnlineState(userId uint32) bool { + _, exist := u.remotePlayerMap[userId] + if !exist { + return false + } else { + return true + } +} + +func (u *UserManager) GetRemoteUserGsAppId(userId uint32) string { + appId, exist := u.remotePlayerMap[userId] + if !exist { + return "" + } else { + return appId + } +} + +func (u *UserManager) SetRemoteUserOnlineState(userId uint32, isOnline bool, appId string) { + if isOnline { + u.remotePlayerMap[userId] = appId + } else { + delete(u.remotePlayerMap, userId) + u.DeleteUser(userId) + } +} + +// GetRemoteOnlineUserList 获取指定数量的远程在线玩家 +func (u *UserManager) GetRemoteOnlineUserList(total int) map[uint32]*model.Player { + if total > 50 { + return nil + } + onlinePlayerMap := make(map[uint32]*model.Player) + count := 0 + for userId := range u.remotePlayerMap { + player := u.LoadTempOfflineUser(userId) + if player == nil { + continue + } + onlinePlayerMap[player.PlayerID] = player + count++ + if count >= total { + break + } + } + return onlinePlayerMap +} + +// LoadGlobalPlayer 加载并返回一个全服玩家及其在线状态 +// 参见LoadTempOfflineUser说明 +func (u *UserManager) LoadGlobalPlayer(userId uint32) (player *model.Player, online bool, remote bool) { + online = u.GetUserOnlineState(userId) + remote = false + if !online { + // 本地不在线就看看远程在不在线 + online = u.GetRemoteUserOnlineState(userId) + if online { + remote = true + } + } + if online { + if remote { + // 远程在线玩家 为了简化实现流程 直接加载数据库临时档 + player = u.LoadTempOfflineUser(userId) + } else { + // 本地在线玩家 + player = u.GetOnlineUser(userId) + } + } else { + // 全服离线玩家 + player = u.LoadTempOfflineUser(userId) + } + return player, online, remote +} + +// 离线玩家相关操作 + +// LoadTempOfflineUser 加载临时离线玩家 +// 正常情况速度较快可以同步阻塞调用 +func (u *UserManager) LoadTempOfflineUser(userId uint32) *model.Player { + player := u.GetOnlineUser(userId) + if player != nil && player.Online { + logger.Error("not allow get a online player as offline player, uid: %v", userId) + return nil + } + player = u.LoadUserFromRedisSync(userId) + if player == nil { + // 玩家可能不存在于redis 尝试从db查询出来然后写入redis + // 大多数情况下活跃玩家都在redis 所以不会走到下面 + // TODO 布隆过滤器防止恶意攻击造成redis缓存穿透 + if userId < 100000000 || userId > 200000000 { + logger.Error("try to load a not exist uid, uid: %v", userId) + return nil + } + player = u.LoadUserFromDbSync(userId) + if player == nil { + // 玩家根本就不存在 + logger.Error("try to load a not exist player from db, uid: %v", userId) + return nil + } + u.SaveUserToRedisSync(player) + } + u.ChangeUserDbState(player, model.DbDelete) + u.playerMap[player.PlayerID] = player + return player +} + +// SaveTempOfflineUser 保存临时离线玩家 +// 如果在调用LoadTempOfflineUser后修改了离线玩家数据 则必须立即调用此函数回写 +func (u *UserManager) SaveTempOfflineUser(player *model.Player) { + // 主协程同步写入redis + u.SaveUserToRedisSync(player) + // 另一个协程异步的写回db + playerCopy := new(model.Player) + err := object.FastDeepCopy(playerCopy, player) + if err != nil { + logger.Error("deep copy player error: %v", err) + return + } + go func() { + u.SaveUserToDbSync(playerCopy) + }() +} + +// db和redis相关操作 + +type SaveUserData struct { + insertPlayerList []*model.Player + updatePlayerList []*model.Player +} + +// StartAutoSaveUser 玩家定时保存 func (u *UserManager) StartAutoSaveUser() { go func() { ticker := time.NewTicker(time.Minute * 5) @@ -253,12 +368,30 @@ func (u *UserManager) StartAutoSaveUser() { go func() { for { saveUserData := <-u.saveUserChan - u.SaveUser(saveUserData) + u.SaveUserListToDbSync(saveUserData) + u.SaveUserListToRedisSync(saveUserData) } }() } -func (u *UserManager) SaveUser(saveUserData *SaveUserData) { +func (u *UserManager) LoadUserFromDbSync(userId uint32) *model.Player { + player, err := u.dao.QueryPlayerByID(userId) + if err != nil { + logger.Error("query player error: %v", err) + return nil + } + return player +} + +func (u *UserManager) SaveUserToDbSync(player *model.Player) { + err := u.dao.UpdatePlayer(player) + if err != nil { + logger.Error("update player error: %v", err) + return + } +} + +func (u *UserManager) SaveUserListToDbSync(saveUserData *SaveUserData) { err := u.dao.InsertPlayerList(saveUserData.insertPlayerList) if err != nil { logger.Error("insert player list error: %v", err) @@ -271,3 +404,23 @@ func (u *UserManager) SaveUser(saveUserData *SaveUserData) { } logger.Info("save user finish, insert user count: %v, update user count: %v", len(saveUserData.insertPlayerList), len(saveUserData.updatePlayerList)) } + +func (u *UserManager) LoadUserFromRedisSync(userId uint32) *model.Player { + player := u.dao.GetRedisPlayer(userId) + return player +} + +func (u *UserManager) SaveUserToRedisSync(player *model.Player) { + u.dao.SetRedisPlayer(player) +} + +func (u *UserManager) SaveUserListToRedisSync(saveUserData *SaveUserData) { + setPlayerList := make([]*model.Player, 0, len(saveUserData.insertPlayerList)+len(saveUserData.updatePlayerList)) + for _, player := range saveUserData.insertPlayerList { + setPlayerList = append(setPlayerList, player) + } + for _, player := range saveUserData.updatePlayerList { + setPlayerList = append(setPlayerList, player) + } + u.dao.SetRedisPlayerList(setPlayerList) +} diff --git a/gs/game/user_multiplayer.go b/gs/game/user_multiplayer.go index 977c0ef9..461b985e 100644 --- a/gs/game/user_multiplayer.go +++ b/gs/game/user_multiplayer.go @@ -5,7 +5,6 @@ import ( "hk4e/common/constant" "hk4e/common/mq" - "hk4e/gate/kcp" "hk4e/gs/model" "hk4e/pkg/logger" "hk4e/pkg/object" @@ -27,16 +26,7 @@ func (g *GameManager) PlayerApplyEnterMpReq(player *model.Player, payloadMsg pb. } g.SendMsg(cmd.PlayerApplyEnterMpRsp, player.PlayerID, player.ClientSeq, playerApplyEnterMpRsp) - ok := g.UserApplyEnterWorld(player, targetUid) - if !ok { - playerApplyEnterMpResultNotify := &proto.PlayerApplyEnterMpResultNotify{ - TargetUid: targetUid, - TargetNickname: "", - IsAgreed: false, - Reason: proto.PlayerApplyEnterMpResultNotify_REASON_PLAYER_CANNOT_ENTER_MP, - } - g.SendMsg(cmd.PlayerApplyEnterMpResultNotify, player.PlayerID, player.ClientSeq, playerApplyEnterMpResultNotify) - } + g.UserApplyEnterWorld(player, targetUid) } func (g *GameManager) PlayerApplyEnterMpResultReq(player *model.Player, payloadMsg pb.Message) { @@ -73,22 +63,12 @@ func (g *GameManager) JoinPlayerSceneReq(player *model.Player, payloadMsg pb.Mes if !USER_MANAGER.GetRemoteUserOnlineState(req.TargetUid) { // 全服不存在该在线玩家 logger.Error("target user not online in any game server, uid: %v", req.TargetUid) - g.DisconnectPlayer(player.PlayerID, kcp.EnetServerKick) return } // 走玩家在线跨服迁移流程 - g.OnUserOffline(player.PlayerID) - // TODO 改成异步写入数据库 - USER_MANAGER.saveUserToDb(player) - gsAppId := USER_MANAGER.GetRemoteUserGsAppId(req.TargetUid) - g.messageQueue.SendToGate(player.GateAppId, &mq.NetMsg{ - MsgType: mq.MsgTypeServer, - EventId: mq.ServerUserGsChangeNotify, - ServerMsg: &mq.ServerMsg{ - UserId: player.PlayerID, - GameServerAppId: gsAppId, - JoinHostUserId: req.TargetUid, - }, + g.OnUserOffline(player.PlayerID, &ChangeGsInfo{ + IsChangeGs: true, + JoinHostUserId: req.TargetUid, }) return } @@ -98,13 +78,7 @@ func (g *GameManager) JoinPlayerSceneReq(player *model.Player, payloadMsg pb.Mes func (g *GameManager) JoinOtherWorld(player *model.Player, hostPlayer *model.Player) { hostWorld := WORLD_MANAGER.GetWorldByID(hostPlayer.WorldId) - _, exist := hostWorld.waitEnterPlayerMap[player.PlayerID] - if !exist { - return - } - if hostPlayer.SceneLoadState == model.SceneEnterDone { - delete(hostWorld.waitEnterPlayerMap, player.PlayerID) player.Pos.X = hostPlayer.Pos.X player.Pos.Y = hostPlayer.Pos.Y player.Pos.Z = hostPlayer.Pos.Z @@ -112,11 +86,13 @@ func (g *GameManager) JoinOtherWorld(player *model.Player, hostPlayer *model.Pla player.Rot.Y = hostPlayer.Rot.Y player.Rot.Z = hostPlayer.Rot.Z player.SceneId = hostPlayer.SceneId - g.UserWorldAddPlayer(hostWorld, player) - player.SceneLoadState = model.SceneNone - g.SendMsg(cmd.PlayerEnterSceneNotify, player.PlayerID, player.ClientSeq, g.PacketPlayerEnterSceneNotifyLogin(player, proto.EnterType_ENTER_TYPE_OTHER)) + + playerEnterSceneNotify := g.PacketPlayerEnterSceneNotifyLogin(player, proto.EnterType_ENTER_TYPE_OTHER) + g.SendMsg(cmd.PlayerEnterSceneNotify, player.PlayerID, player.ClientSeq, playerEnterSceneNotify) + } else { + hostWorld.waitEnterPlayerMap[player.PlayerID] = time.Now().UnixMilli() } } @@ -173,6 +149,10 @@ func (g *GameManager) SceneKickPlayerReq(player *model.Player, payloadMsg pb.Mes } targetUid := req.TargetUid targetPlayer := USER_MANAGER.GetOnlineUser(targetUid) + if targetPlayer == nil { + logger.Error("player is nil, uid: %v", targetUid) + return + } ok := g.UserLeaveWorld(targetPlayer) if !ok { g.CommonRetError(cmd.SceneKickPlayerRsp, player, &proto.SceneKickPlayerRsp{}, proto.Retcode_RET_MP_TARGET_PLAYER_IN_TRANSFER) @@ -193,41 +173,112 @@ func (g *GameManager) SceneKickPlayerReq(player *model.Player, payloadMsg pb.Mes g.SendMsg(cmd.SceneKickPlayerRsp, player.PlayerID, player.ClientSeq, sceneKickPlayerRsp) } -func (g *GameManager) UserApplyEnterWorld(player *model.Player, targetUid uint32) bool { - targetPlayer := USER_MANAGER.GetOnlineUser(targetUid) - if targetPlayer == nil { - return false +func (g *GameManager) UserApplyEnterWorld(player *model.Player, targetUid uint32) { + applyFailNotify := func() { + playerApplyEnterMpResultNotify := &proto.PlayerApplyEnterMpResultNotify{ + TargetUid: targetUid, + TargetNickname: "", + IsAgreed: false, + Reason: proto.PlayerApplyEnterMpResultNotify_REASON_PLAYER_CANNOT_ENTER_MP, + } + g.SendMsg(cmd.PlayerApplyEnterMpResultNotify, player.PlayerID, player.ClientSeq, playerApplyEnterMpResultNotify) } world := WORLD_MANAGER.GetWorldByID(player.WorldId) if world.multiplayer { - return false + applyFailNotify() + return + } + targetPlayer := USER_MANAGER.GetOnlineUser(targetUid) + if targetPlayer == nil { + if !USER_MANAGER.GetRemoteUserOnlineState(targetUid) { + // 全服不存在该在线玩家 + logger.Error("target user not online in any game server, uid: %v", targetUid) + applyFailNotify() + return + } + gsAppId := USER_MANAGER.GetRemoteUserGsAppId(targetUid) + MESSAGE_QUEUE.SendToGs(gsAppId, &mq.NetMsg{ + MsgType: mq.MsgTypeServer, + EventId: mq.ServerUserMpReq, + ServerMsg: &mq.ServerMsg{ + UserMpInfo: &mq.UserMpInfo{ + OriginInfo: &mq.OriginInfo{ + CmdName: "PlayerApplyEnterMpReq", + UserId: player.PlayerID, + }, + HostUserId: targetUid, + ApplyUserId: player.PlayerID, + ApplyPlayerOnlineInfo: &mq.UserBaseInfo{ + UserId: player.PlayerID, + Nickname: player.NickName, + PlayerLevel: player.PropertiesMap[constant.PlayerPropertyConst.PROP_PLAYER_LEVEL], + MpSettingType: uint8(player.PropertiesMap[constant.PlayerPropertyConst.PROP_PLAYER_MP_SETTING_TYPE]), + NameCardId: player.NameCard, + Signature: player.Signature, + HeadImageId: player.HeadImage, + WorldPlayerNum: uint32(world.GetWorldPlayerNum()), + }, + }, + }, + }) + return } applyTime, exist := targetPlayer.CoopApplyMap[player.PlayerID] if exist && time.Now().UnixNano() < applyTime+int64(10*time.Second) { - return false + applyFailNotify() + return } targetPlayer.CoopApplyMap[player.PlayerID] = time.Now().UnixNano() targetWorld := WORLD_MANAGER.GetWorldByID(targetPlayer.WorldId) if targetWorld.multiplayer && targetWorld.owner.PlayerID != targetPlayer.PlayerID { - return false + // 向同一世界内的非房主玩家申请时直接拒绝 + applyFailNotify() + return } playerApplyEnterMpNotify := new(proto.PlayerApplyEnterMpNotify) playerApplyEnterMpNotify.SrcPlayerInfo = g.PacketOnlinePlayerInfo(player) g.SendMsg(cmd.PlayerApplyEnterMpNotify, targetPlayer.PlayerID, targetPlayer.ClientSeq, playerApplyEnterMpNotify) - return true } func (g *GameManager) UserDealEnterWorld(hostPlayer *model.Player, otherUid uint32, agree bool) { - otherPlayer := USER_MANAGER.GetOnlineUser(otherUid) - if otherPlayer == nil { - return - } applyTime, exist := hostPlayer.CoopApplyMap[otherUid] if !exist || time.Now().UnixNano() > applyTime+int64(10*time.Second) { return } delete(hostPlayer.CoopApplyMap, otherUid) + if !agree { + return + } + g.HostEnterMpWorld(hostPlayer, otherUid) + + otherPlayer := USER_MANAGER.GetOnlineUser(otherUid) + if otherPlayer == nil { + if !USER_MANAGER.GetRemoteUserOnlineState(otherUid) { + // 全服不存在该在线玩家 + logger.Error("target user not online in any game server, uid: %v", otherUid) + return + } + gsAppId := USER_MANAGER.GetRemoteUserGsAppId(otherUid) + MESSAGE_QUEUE.SendToGs(gsAppId, &mq.NetMsg{ + MsgType: mq.MsgTypeServer, + EventId: mq.ServerUserMpReq, + ServerMsg: &mq.ServerMsg{ + UserMpInfo: &mq.UserMpInfo{ + OriginInfo: &mq.OriginInfo{ + CmdName: "PlayerApplyEnterMpResultReq", + UserId: hostPlayer.PlayerID, + }, + HostUserId: hostPlayer.PlayerID, + ApplyUserId: otherUid, + Agreed: agree, + HostNickname: hostPlayer.NickName, + }, + }, + }) + return + } + otherPlayerWorld := WORLD_MANAGER.GetWorldByID(otherPlayer.WorldId) if otherPlayerWorld.multiplayer { playerApplyEnterMpResultNotify := &proto.PlayerApplyEnterMpResultNotify{ @@ -247,15 +298,10 @@ func (g *GameManager) UserDealEnterWorld(hostPlayer *model.Player, otherUid uint Reason: proto.PlayerApplyEnterMpResultNotify_REASON_PLAYER_JUDGE, } g.SendMsg(cmd.PlayerApplyEnterMpResultNotify, otherPlayer.PlayerID, otherPlayer.ClientSeq, playerApplyEnterMpResultNotify) - - if agree { - g.HostEnterMpWorld(hostPlayer, otherPlayer.PlayerID) - } } -func (g *GameManager) HostEnterMpWorld(hostPlayer *model.Player, otherPlayerId uint32) { +func (g *GameManager) HostEnterMpWorld(hostPlayer *model.Player, otherUid uint32) { world := WORLD_MANAGER.GetWorldByID(hostPlayer.WorldId) - world.waitEnterPlayerMap[otherPlayerId] = time.Now().UnixMilli() if world.multiplayer { return } @@ -286,7 +332,7 @@ func (g *GameManager) HostEnterMpWorld(hostPlayer *model.Player, otherPlayerId u guestBeginEnterSceneNotify := &proto.GuestBeginEnterSceneNotify{ SceneId: hostPlayer.SceneId, - Uid: otherPlayerId, + Uid: otherUid, } g.SendMsg(cmd.GuestBeginEnterSceneNotify, hostPlayer.PlayerID, hostPlayer.ClientSeq, guestBeginEnterSceneNotify) @@ -357,7 +403,7 @@ func (g *GameManager) UserWorldRemovePlayer(world *World, player *model.Player) if world.owner.PlayerID == player.PlayerID { // 房主离开销毁世界 WORLD_MANAGER.DestroyWorld(world.id) - GAME_MANAGER.messageQueue.SendToFight(world.owner.FightAppId, &mq.NetMsg{ + MESSAGE_QUEUE.SendToFight(world.owner.FightAppId, &mq.NetMsg{ MsgType: mq.MsgTypeFight, EventId: mq.DelFightRoutine, FightMsg: &mq.FightMsg{ @@ -462,3 +508,112 @@ func (g *GameManager) UpdateWorldPlayerInfo(hostWorld *World, excludePlayer *mod g.SendMsg(cmd.SyncScenePlayTeamEntityNotify, worldPlayer.PlayerID, worldPlayer.ClientSeq, syncScenePlayTeamEntityNotify) } } + +// 跨服玩家多人世界相关请求 + +func (g *GameManager) ServerUserMpReq(userMpInfo *mq.UserMpInfo, gsAppId string) { + switch userMpInfo.OriginInfo.CmdName { + case "PlayerApplyEnterMpReq": + applyFailNotify := func() { + MESSAGE_QUEUE.SendToGs(gsAppId, &mq.NetMsg{ + MsgType: mq.MsgTypeServer, + EventId: mq.ServerUserMpRsp, + ServerMsg: &mq.ServerMsg{ + UserMpInfo: &mq.UserMpInfo{ + OriginInfo: userMpInfo.OriginInfo, + HostUserId: userMpInfo.HostUserId, + ApplyOk: false, + }, + }, + }) + } + hostPlayer := USER_MANAGER.GetOnlineUser(userMpInfo.HostUserId) + if hostPlayer == nil { + logger.Error("player is nil, uid: %v", userMpInfo.HostUserId) + applyFailNotify() + return + } + applyTime, exist := hostPlayer.CoopApplyMap[userMpInfo.ApplyUserId] + if exist && time.Now().UnixNano() < applyTime+int64(10*time.Second) { + applyFailNotify() + return + } + hostPlayer.CoopApplyMap[userMpInfo.ApplyUserId] = time.Now().UnixNano() + hostWorld := WORLD_MANAGER.GetWorldByID(hostPlayer.WorldId) + if hostWorld.multiplayer && hostWorld.owner.PlayerID != hostPlayer.PlayerID { + // 向同一世界内的非房主玩家申请时直接拒绝 + applyFailNotify() + return + } + + playerApplyEnterMpNotify := new(proto.PlayerApplyEnterMpNotify) + playerApplyEnterMpNotify.SrcPlayerInfo = &proto.OnlinePlayerInfo{ + Uid: userMpInfo.ApplyPlayerOnlineInfo.UserId, + Nickname: userMpInfo.ApplyPlayerOnlineInfo.Nickname, + PlayerLevel: userMpInfo.ApplyPlayerOnlineInfo.PlayerLevel, + MpSettingType: proto.MpSettingType(userMpInfo.ApplyPlayerOnlineInfo.MpSettingType), + NameCardId: userMpInfo.ApplyPlayerOnlineInfo.NameCardId, + Signature: userMpInfo.ApplyPlayerOnlineInfo.Signature, + ProfilePicture: &proto.ProfilePicture{AvatarId: userMpInfo.ApplyPlayerOnlineInfo.HeadImageId}, + CurPlayerNumInWorld: userMpInfo.ApplyPlayerOnlineInfo.WorldPlayerNum, + } + g.SendMsg(cmd.PlayerApplyEnterMpNotify, hostPlayer.PlayerID, hostPlayer.ClientSeq, playerApplyEnterMpNotify) + + MESSAGE_QUEUE.SendToGs(gsAppId, &mq.NetMsg{ + MsgType: mq.MsgTypeServer, + EventId: mq.ServerUserMpRsp, + ServerMsg: &mq.ServerMsg{ + UserMpInfo: &mq.UserMpInfo{ + OriginInfo: userMpInfo.OriginInfo, + HostUserId: userMpInfo.HostUserId, + ApplyOk: true, + }, + }, + }) + case "PlayerApplyEnterMpResultReq": + applyPlayer := USER_MANAGER.GetOnlineUser(userMpInfo.ApplyUserId) + if applyPlayer == nil { + logger.Error("player is nil, uid: %v", userMpInfo.ApplyUserId) + return + } + applyPlayerWorld := WORLD_MANAGER.GetWorldByID(applyPlayer.WorldId) + if applyPlayerWorld.multiplayer { + playerApplyEnterMpResultNotify := &proto.PlayerApplyEnterMpResultNotify{ + TargetUid: userMpInfo.HostUserId, + TargetNickname: userMpInfo.HostNickname, + IsAgreed: false, + Reason: proto.PlayerApplyEnterMpResultNotify_REASON_PLAYER_CANNOT_ENTER_MP, + } + g.SendMsg(cmd.PlayerApplyEnterMpResultNotify, applyPlayer.PlayerID, applyPlayer.ClientSeq, playerApplyEnterMpResultNotify) + return + } + + playerApplyEnterMpResultNotify := &proto.PlayerApplyEnterMpResultNotify{ + TargetUid: userMpInfo.HostUserId, + TargetNickname: userMpInfo.HostNickname, + IsAgreed: userMpInfo.Agreed, + Reason: proto.PlayerApplyEnterMpResultNotify_REASON_PLAYER_JUDGE, + } + g.SendMsg(cmd.PlayerApplyEnterMpResultNotify, applyPlayer.PlayerID, applyPlayer.ClientSeq, playerApplyEnterMpResultNotify) + } +} + +func (g *GameManager) ServerUserMpRsp(userMpInfo *mq.UserMpInfo) { + switch userMpInfo.OriginInfo.CmdName { + case "PlayerApplyEnterMpReq": + player := USER_MANAGER.GetOnlineUser(userMpInfo.OriginInfo.UserId) + if player == nil { + logger.Error("player is nil, uid: %v", userMpInfo.OriginInfo.UserId) + return + } + if !userMpInfo.ApplyOk { + playerApplyEnterMpResultNotify := &proto.PlayerApplyEnterMpResultNotify{ + TargetUid: userMpInfo.HostUserId, + TargetNickname: "", + IsAgreed: false, + Reason: proto.PlayerApplyEnterMpResultNotify_REASON_PLAYER_CANNOT_ENTER_MP, + } + g.SendMsg(cmd.PlayerApplyEnterMpResultNotify, player.PlayerID, player.ClientSeq, playerApplyEnterMpResultNotify) + } + } +} diff --git a/gs/game/user_scene.go b/gs/game/user_scene.go index c07276e4..bb9269c7 100644 --- a/gs/game/user_scene.go +++ b/gs/game/user_scene.go @@ -263,21 +263,14 @@ func (g *GameManager) EnterSceneDoneReq(player *model.Player, payloadMsg pb.Mess world.PlayerEnter(player) for otherPlayerId := range world.waitEnterPlayerMap { + // 房主第一次进入多人世界场景完成 开始通知等待列表中的玩家进入场景 delete(world.waitEnterPlayerMap, otherPlayerId) otherPlayer := USER_MANAGER.GetOnlineUser(otherPlayerId) - otherPlayer.Pos.X = player.Pos.X - otherPlayer.Pos.Y = player.Pos.Y - otherPlayer.Pos.Z = player.Pos.Z - otherPlayer.Rot.X = player.Rot.X - otherPlayer.Rot.Y = player.Rot.Y - otherPlayer.Rot.Z = player.Rot.Z - otherPlayer.SceneId = player.SceneId - - g.UserWorldAddPlayer(world, otherPlayer) - - otherPlayer.SceneLoadState = model.SceneNone - playerEnterSceneNotify := g.PacketPlayerEnterSceneNotifyLogin(otherPlayer, proto.EnterType_ENTER_TYPE_OTHER) - g.SendMsg(cmd.PlayerEnterSceneNotify, otherPlayer.PlayerID, otherPlayer.ClientSeq, playerEnterSceneNotify) + if otherPlayer == nil { + logger.Error("player is nil, uid: %v", otherPlayerId) + continue + } + g.JoinOtherWorld(otherPlayer, player) } } diff --git a/gs/game/user_social.go b/gs/game/user_social.go index 657422cd..db65c7e1 100644 --- a/gs/game/user_social.go +++ b/gs/game/user_social.go @@ -21,12 +21,28 @@ func (g *GameManager) GetPlayerSocialDetailReq(player *model.Player, payloadMsg req := payloadMsg.(*proto.GetPlayerSocialDetailReq) targetUid := req.Uid - // TODO 同步阻塞待优化 - targetPlayer := USER_MANAGER.LoadTempOfflineUserSync(targetUid) + targetPlayer, _, remote := USER_MANAGER.LoadGlobalPlayer(targetUid) if targetPlayer == nil { g.CommonRetError(cmd.GetPlayerSocialDetailRsp, player, &proto.GetPlayerSocialDetailRsp{}, proto.Retcode_RET_PLAYER_NOT_EXIST) return } + if remote { + gsAppId := USER_MANAGER.GetRemoteUserGsAppId(targetUid) + MESSAGE_QUEUE.SendToGs(gsAppId, &mq.NetMsg{ + MsgType: mq.MsgTypeServer, + EventId: mq.ServerUserBaseInfoReq, + ServerMsg: &mq.ServerMsg{ + UserBaseInfo: &mq.UserBaseInfo{ + OriginInfo: &mq.OriginInfo{ + CmdName: "GetPlayerSocialDetailReq", + UserId: player.PlayerID, + }, + UserId: targetUid, + }, + }, + }) + return + } _, exist := player.FriendList[targetPlayer.PlayerID] socialDetail := &proto.SocialDetail{ Uid: targetPlayer.PlayerID, @@ -160,19 +176,18 @@ func (g *GameManager) GetPlayerFriendListReq(player *model.Player, payloadMsg pb // 用于实现好友列表内的系统且不更改原先的内容 tempFriendList := COMMAND_MANAGER.GetFriendList(player.FriendList) for uid := range tempFriendList { - // TODO 同步阻塞待优化 - var onlineState proto.FriendOnlineState - online := USER_MANAGER.GetUserOnlineState(uid) + + friendPlayer, online, _ := USER_MANAGER.LoadGlobalPlayer(uid) + if friendPlayer == nil { + logger.Error("target player is nil, uid: %v", player.PlayerID) + continue + } + var onlineState proto.FriendOnlineState = 0 if online { onlineState = proto.FriendOnlineState_FRIEND_ONLINE_STATE_ONLINE } else { onlineState = proto.FriendOnlineState_FRIEND_ONLINE_STATE_DISCONNECT } - friendPlayer := USER_MANAGER.LoadTempOfflineUserSync(uid) - if friendPlayer == nil { - logger.Error("target player is nil, uid: %v", player.PlayerID) - continue - } friendBrief := &proto.FriendBrief{ Uid: friendPlayer.PlayerID, Nickname: friendPlayer.NickName, @@ -200,19 +215,17 @@ func (g *GameManager) GetPlayerAskFriendListReq(player *model.Player, payloadMsg AskFriendList: make([]*proto.FriendBrief, 0), } for uid := range player.FriendApplyList { - // TODO 同步阻塞待优化 + friendPlayer, online, _ := USER_MANAGER.LoadGlobalPlayer(uid) + if friendPlayer == nil { + logger.Error("target player is nil, uid: %v", player.PlayerID) + continue + } var onlineState proto.FriendOnlineState - online := USER_MANAGER.GetUserOnlineState(uid) if online { onlineState = proto.FriendOnlineState_FRIEND_ONLINE_STATE_ONLINE } else { onlineState = proto.FriendOnlineState_FRIEND_ONLINE_STATE_DISCONNECT } - friendPlayer := USER_MANAGER.LoadTempOfflineUserSync(uid) - if friendPlayer == nil { - logger.Error("target player is nil, uid: %v", player.PlayerID) - continue - } friendBrief := &proto.FriendBrief{ Uid: friendPlayer.PlayerID, Nickname: friendPlayer.NickName, @@ -238,13 +251,58 @@ func (g *GameManager) AskAddFriendReq(player *model.Player, payloadMsg pb.Messag req := payloadMsg.(*proto.AskAddFriendReq) targetUid := req.TargetUid - // TODO 同步阻塞待优化 - targetPlayerOnline := USER_MANAGER.GetUserOnlineState(targetUid) - targetPlayer := USER_MANAGER.LoadTempOfflineUserSync(targetUid) + askAddFriendRsp := &proto.AskAddFriendRsp{ + TargetUid: targetUid, + } + g.SendMsg(cmd.AskAddFriendRsp, player.PlayerID, player.ClientSeq, askAddFriendRsp) + + targetPlayer := USER_MANAGER.GetOnlineUser(targetUid) if targetPlayer == nil { - logger.Error("apply add friend target player is nil, uid: %v", player.PlayerID) + // 非本地玩家 + if USER_MANAGER.GetRemoteUserOnlineState(targetUid) { + // 远程在线玩家 + gsAppId := USER_MANAGER.GetRemoteUserGsAppId(targetUid) + MESSAGE_QUEUE.SendToGs(gsAppId, &mq.NetMsg{ + MsgType: mq.MsgTypeServer, + EventId: mq.ServerAddFriendNotify, + ServerMsg: &mq.ServerMsg{ + AddFriendInfo: &mq.AddFriendInfo{ + OriginInfo: &mq.OriginInfo{ + CmdName: "AskAddFriendReq", + UserId: player.PlayerID, + }, + TargetUserId: targetUid, + ApplyPlayerOnlineInfo: &mq.UserBaseInfo{ + UserId: player.PlayerID, + Nickname: player.NickName, + PlayerLevel: player.PropertiesMap[constant.PlayerPropertyConst.PROP_PLAYER_LEVEL], + NameCardId: player.NameCard, + Signature: player.Signature, + HeadImageId: player.HeadImage, + WorldLevel: player.PropertiesMap[constant.PlayerPropertyConst.PROP_PLAYER_WORLD_LEVEL], + }, + }, + }, + }) + } else { + // 全服离线玩家 + targetPlayer, _, _ := USER_MANAGER.LoadGlobalPlayer(targetUid) + if targetPlayer == nil { + logger.Error("apply add friend target player is nil, uid: %v", targetUid) + return + } + _, applyExist := targetPlayer.FriendApplyList[player.PlayerID] + _, friendExist := targetPlayer.FriendList[player.PlayerID] + if applyExist || friendExist { + logger.Error("friend or apply already exist, uid: %v", player.PlayerID) + return + } + targetPlayer.FriendApplyList[player.PlayerID] = true + USER_MANAGER.SaveTempOfflineUser(targetPlayer) + } return } + _, applyExist := targetPlayer.FriendApplyList[player.PlayerID] _, friendExist := targetPlayer.FriendList[player.PlayerID] if applyExist || friendExist { @@ -253,43 +311,26 @@ func (g *GameManager) AskAddFriendReq(player *model.Player, payloadMsg pb.Messag } targetPlayer.FriendApplyList[player.PlayerID] = true - if targetPlayerOnline { - askAddFriendNotify := &proto.AskAddFriendNotify{ - TargetUid: player.PlayerID, - } - askAddFriendNotify.TargetFriendBrief = &proto.FriendBrief{ - Uid: player.PlayerID, - Nickname: player.NickName, - Level: player.PropertiesMap[constant.PlayerPropertyConst.PROP_PLAYER_LEVEL], - ProfilePicture: &proto.ProfilePicture{AvatarId: player.HeadImage}, - WorldLevel: player.PropertiesMap[constant.PlayerPropertyConst.PROP_PLAYER_WORLD_LEVEL], - Signature: player.Signature, - OnlineState: proto.FriendOnlineState_FRIEND_ONLINE_STATE_ONLINE, - IsMpModeAvailable: true, - LastActiveTime: player.OfflineTime, - NameCardId: player.NameCard, - Param: (uint32(time.Now().Unix()) - player.OfflineTime) / 3600 / 24, - IsGameSource: true, - PlatformType: proto.PlatformType_PLATFORM_TYPE_PC, - } - g.SendMsg(cmd.AskAddFriendNotify, targetPlayer.PlayerID, targetPlayer.ClientSeq, askAddFriendNotify) + // 目标玩家在线则通知 + askAddFriendNotify := &proto.AskAddFriendNotify{ + TargetUid: player.PlayerID, } - - askAddFriendRsp := &proto.AskAddFriendRsp{ - TargetUid: targetUid, + askAddFriendNotify.TargetFriendBrief = &proto.FriendBrief{ + Uid: player.PlayerID, + Nickname: player.NickName, + Level: player.PropertiesMap[constant.PlayerPropertyConst.PROP_PLAYER_LEVEL], + ProfilePicture: &proto.ProfilePicture{AvatarId: player.HeadImage}, + WorldLevel: player.PropertiesMap[constant.PlayerPropertyConst.PROP_PLAYER_WORLD_LEVEL], + Signature: player.Signature, + OnlineState: proto.FriendOnlineState_FRIEND_ONLINE_STATE_ONLINE, + IsMpModeAvailable: true, + LastActiveTime: player.OfflineTime, + NameCardId: player.NameCard, + Param: (uint32(time.Now().Unix()) - player.OfflineTime) / 3600 / 24, + IsGameSource: true, + PlatformType: proto.PlatformType_PLATFORM_TYPE_PC, } - g.SendMsg(cmd.AskAddFriendRsp, player.PlayerID, player.ClientSeq, askAddFriendRsp) -} - -func (g *GameManager) AddFriend(player *model.Player, targetUid uint32) { - player.FriendList[targetUid] = true - // TODO 同步阻塞待优化 - targetPlayer := USER_MANAGER.LoadTempOfflineUserSync(targetUid) - if targetPlayer == nil { - logger.Error("agree friend apply target player is nil, uid: %v", player.PlayerID) - return - } - targetPlayer.FriendList[player.PlayerID] = true + g.SendMsg(cmd.AskAddFriendNotify, targetPlayer.PlayerID, targetPlayer.ClientSeq, askAddFriendNotify) } func (g *GameManager) DealAddFriendReq(player *model.Player, payloadMsg pb.Message) { @@ -298,8 +339,12 @@ func (g *GameManager) DealAddFriendReq(player *model.Player, payloadMsg pb.Messa targetUid := req.TargetUid result := req.DealAddFriendResult + agree := false if result == proto.DealAddFriendResultType_DEAL_ADD_FRIEND_RESULT_TYPE_ACCEPT { - g.AddFriend(player, targetUid) + agree = true + } + if agree { + player.FriendList[targetUid] = true } delete(player.FriendApplyList, targetUid) @@ -308,6 +353,44 @@ func (g *GameManager) DealAddFriendReq(player *model.Player, payloadMsg pb.Messa DealAddFriendResult: result, } g.SendMsg(cmd.DealAddFriendRsp, player.PlayerID, player.ClientSeq, dealAddFriendRsp) + + if agree { + targetPlayer := USER_MANAGER.GetOnlineUser(targetUid) + if targetPlayer == nil { + // 非本地玩家 + if USER_MANAGER.GetRemoteUserOnlineState(targetUid) { + // 远程在线玩家 + gsAppId := USER_MANAGER.GetRemoteUserGsAppId(targetUid) + MESSAGE_QUEUE.SendToGs(gsAppId, &mq.NetMsg{ + MsgType: mq.MsgTypeServer, + EventId: mq.ServerAddFriendNotify, + ServerMsg: &mq.ServerMsg{ + AddFriendInfo: &mq.AddFriendInfo{ + OriginInfo: &mq.OriginInfo{ + CmdName: "DealAddFriendReq", + UserId: player.PlayerID, + }, + TargetUserId: targetUid, + ApplyPlayerOnlineInfo: &mq.UserBaseInfo{ + UserId: player.PlayerID, + }, + }, + }, + }) + } else { + // 全服离线玩家 + targetPlayer, _, _ := USER_MANAGER.LoadGlobalPlayer(targetUid) + if targetPlayer == nil { + logger.Error("apply add friend target player is nil, uid: %v", targetUid) + return + } + targetPlayer.FriendList[player.PlayerID] = true + USER_MANAGER.SaveTempOfflineUser(targetPlayer) + } + return + } + targetPlayer.FriendList[player.PlayerID] = true + } } func (g *GameManager) GetOnlinePlayerListReq(player *model.Player, payloadMsg pb.Message) { @@ -315,6 +398,7 @@ func (g *GameManager) GetOnlinePlayerListReq(player *model.Player, payloadMsg pb count := 0 onlinePlayerList := make([]*model.Player, 0) + // 优先获取本地的在线玩家 for _, onlinePlayer := range USER_MANAGER.GetAllOnlineUserList() { if onlinePlayer.PlayerID == player.PlayerID { continue @@ -325,6 +409,19 @@ func (g *GameManager) GetOnlinePlayerListReq(player *model.Player, payloadMsg pb break } } + if count < 50 { + // 本地不够时获取远程的在线玩家 + for _, onlinePlayer := range USER_MANAGER.GetRemoteOnlineUserList(50 - count) { + if onlinePlayer.PlayerID == player.PlayerID { + continue + } + onlinePlayerList = append(onlinePlayerList, onlinePlayer) + count++ + if count >= 50 { + break + } + } + } getOnlinePlayerListRsp := &proto.GetOnlinePlayerListRsp{ PlayerInfoList: make([]*proto.OnlinePlayerInfo, 0), @@ -344,11 +441,6 @@ func (g *GameManager) GetOnlinePlayerInfoReq(player *model.Player, payloadMsg pb return } - // TODO 删除我 - g.JoinPlayerSceneReq(player, &proto.JoinPlayerSceneReq{ - TargetUid: targetUid.TargetUid, - }) - if USER_MANAGER.GetUserOnlineState(targetUid.TargetUid) { g.SendMsg(cmd.GetOnlinePlayerInfoRsp, player.PlayerID, player.ClientSeq, &proto.GetOnlinePlayerInfoRsp{ TargetUid: targetUid.TargetUid, @@ -358,9 +450,9 @@ func (g *GameManager) GetOnlinePlayerInfoReq(player *model.Player, payloadMsg pb } if USER_MANAGER.GetRemoteUserOnlineState(targetUid.TargetUid) { gsAppId := USER_MANAGER.GetRemoteUserGsAppId(targetUid.TargetUid) - g.messageQueue.SendToGs(gsAppId, &mq.NetMsg{ + MESSAGE_QUEUE.SendToGs(gsAppId, &mq.NetMsg{ MsgType: mq.MsgTypeServer, - EventId: mq.ServerGetUserBaseInfoReq, + EventId: mq.ServerUserBaseInfoReq, ServerMsg: &mq.ServerMsg{ UserBaseInfo: &mq.UserBaseInfo{ OriginInfo: &mq.OriginInfo{ @@ -378,6 +470,11 @@ func (g *GameManager) GetOnlinePlayerInfoReq(player *model.Player, payloadMsg pb func (g *GameManager) PacketOnlinePlayerInfo(player *model.Player) *proto.OnlinePlayerInfo { world := WORLD_MANAGER.GetWorldByID(player.WorldId) + worldPlayerNum := uint32(1) + // TODO 远程玩家的世界内人数 + if world != nil { + worldPlayerNum = uint32(world.GetWorldPlayerNum()) + } onlinePlayerInfo := &proto.OnlinePlayerInfo{ Uid: player.PlayerID, Nickname: player.NickName, @@ -386,7 +483,138 @@ func (g *GameManager) PacketOnlinePlayerInfo(player *model.Player) *proto.Online NameCardId: player.NameCard, Signature: player.Signature, ProfilePicture: &proto.ProfilePicture{AvatarId: player.HeadImage}, - CurPlayerNumInWorld: uint32(world.GetWorldPlayerNum()), + CurPlayerNumInWorld: worldPlayerNum, } return onlinePlayerInfo } + +// 跨服玩家基础数据请求 + +func (g *GameManager) ServerUserBaseInfoReq(userBaseInfo *mq.UserBaseInfo, gsAppId string) { + switch userBaseInfo.OriginInfo.CmdName { + case "GetOnlinePlayerInfoReq": + fallthrough + case "GetPlayerSocialDetailReq": + player := USER_MANAGER.GetOnlineUser(userBaseInfo.UserId) + if player == nil { + logger.Error("player is nil, uid: %v", userBaseInfo.UserId) + return + } + world := WORLD_MANAGER.GetWorldByID(player.WorldId) + MESSAGE_QUEUE.SendToGs(gsAppId, &mq.NetMsg{ + MsgType: mq.MsgTypeServer, + EventId: mq.ServerUserBaseInfoRsp, + ServerMsg: &mq.ServerMsg{ + UserBaseInfo: &mq.UserBaseInfo{ + OriginInfo: userBaseInfo.OriginInfo, + UserId: player.PlayerID, + Nickname: player.NickName, + PlayerLevel: player.PropertiesMap[constant.PlayerPropertyConst.PROP_PLAYER_LEVEL], + MpSettingType: uint8(player.PropertiesMap[constant.PlayerPropertyConst.PROP_PLAYER_MP_SETTING_TYPE]), + NameCardId: player.NameCard, + Signature: player.Signature, + HeadImageId: player.HeadImage, + WorldPlayerNum: uint32(world.GetWorldPlayerNum()), + WorldLevel: player.PropertiesMap[constant.PlayerPropertyConst.PROP_PLAYER_WORLD_LEVEL], + Birthday: player.Birthday, + }, + }, + }) + } +} + +func (g *GameManager) ServerUserBaseInfoRsp(userBaseInfo *mq.UserBaseInfo) { + switch userBaseInfo.OriginInfo.CmdName { + case "GetOnlinePlayerInfoReq": + player := USER_MANAGER.GetOnlineUser(userBaseInfo.OriginInfo.UserId) + if player == nil { + logger.Error("player is nil, uid: %v", userBaseInfo.OriginInfo.UserId) + return + } + g.SendMsg(cmd.GetOnlinePlayerInfoRsp, player.PlayerID, player.ClientSeq, &proto.GetOnlinePlayerInfoRsp{ + TargetUid: userBaseInfo.UserId, + TargetPlayerInfo: &proto.OnlinePlayerInfo{ + Uid: userBaseInfo.UserId, + Nickname: userBaseInfo.Nickname, + PlayerLevel: userBaseInfo.PlayerLevel, + MpSettingType: proto.MpSettingType(userBaseInfo.MpSettingType), + NameCardId: userBaseInfo.NameCardId, + Signature: userBaseInfo.Signature, + ProfilePicture: &proto.ProfilePicture{AvatarId: userBaseInfo.HeadImageId}, + CurPlayerNumInWorld: userBaseInfo.WorldPlayerNum, + }, + }) + case "GetPlayerSocialDetailReq": + player := USER_MANAGER.GetOnlineUser(userBaseInfo.OriginInfo.UserId) + if player == nil { + logger.Error("player is nil, uid: %v", userBaseInfo.OriginInfo.UserId) + return + } + _, exist := player.FriendList[userBaseInfo.UserId] + socialDetail := &proto.SocialDetail{ + Uid: userBaseInfo.UserId, + ProfilePicture: &proto.ProfilePicture{AvatarId: userBaseInfo.HeadImageId}, + Nickname: userBaseInfo.Nickname, + Signature: userBaseInfo.Signature, + Level: userBaseInfo.PlayerLevel, + Birthday: &proto.Birthday{Month: uint32(userBaseInfo.Birthday[0]), Day: uint32(userBaseInfo.Birthday[1])}, + WorldLevel: userBaseInfo.WorldLevel, + NameCardId: userBaseInfo.NameCardId, + IsShowAvatar: false, + FinishAchievementNum: 0, + IsFriend: exist, + } + getPlayerSocialDetailRsp := &proto.GetPlayerSocialDetailRsp{ + DetailData: socialDetail, + } + g.SendMsg(cmd.GetPlayerSocialDetailRsp, player.PlayerID, player.ClientSeq, getPlayerSocialDetailRsp) + } +} + +// 跨服添加好友通知 + +func (g *GameManager) ServerAddFriendNotify(addFriendInfo *mq.AddFriendInfo) { + switch addFriendInfo.OriginInfo.CmdName { + case "AskAddFriendReq": + targetPlayer := USER_MANAGER.GetOnlineUser(addFriendInfo.TargetUserId) + if targetPlayer == nil { + logger.Error("player is nil, uid: %v", addFriendInfo.TargetUserId) + return + } + _, applyExist := targetPlayer.FriendApplyList[addFriendInfo.ApplyPlayerOnlineInfo.UserId] + _, friendExist := targetPlayer.FriendList[addFriendInfo.ApplyPlayerOnlineInfo.UserId] + if applyExist || friendExist { + logger.Error("friend or apply already exist, uid: %v", addFriendInfo.ApplyPlayerOnlineInfo.UserId) + return + } + targetPlayer.FriendApplyList[addFriendInfo.ApplyPlayerOnlineInfo.UserId] = true + + // 目标玩家在线则通知 + askAddFriendNotify := &proto.AskAddFriendNotify{ + TargetUid: addFriendInfo.ApplyPlayerOnlineInfo.UserId, + } + askAddFriendNotify.TargetFriendBrief = &proto.FriendBrief{ + Uid: addFriendInfo.ApplyPlayerOnlineInfo.UserId, + Nickname: addFriendInfo.ApplyPlayerOnlineInfo.Nickname, + Level: addFriendInfo.ApplyPlayerOnlineInfo.PlayerLevel, + ProfilePicture: &proto.ProfilePicture{AvatarId: addFriendInfo.ApplyPlayerOnlineInfo.HeadImageId}, + WorldLevel: addFriendInfo.ApplyPlayerOnlineInfo.WorldLevel, + Signature: addFriendInfo.ApplyPlayerOnlineInfo.Signature, + OnlineState: proto.FriendOnlineState_FRIEND_ONLINE_STATE_ONLINE, + IsMpModeAvailable: true, + LastActiveTime: 0, + NameCardId: addFriendInfo.ApplyPlayerOnlineInfo.NameCardId, + Param: 0, + IsGameSource: true, + PlatformType: proto.PlatformType_PLATFORM_TYPE_PC, + } + g.SendMsg(cmd.AskAddFriendNotify, targetPlayer.PlayerID, targetPlayer.ClientSeq, askAddFriendNotify) + case "DealAddFriendReq": + targetPlayer := USER_MANAGER.GetOnlineUser(addFriendInfo.TargetUserId) + if targetPlayer == nil { + logger.Error("player is nil, uid: %v", addFriendInfo.TargetUserId) + return + } + targetPlayer.FriendList[addFriendInfo.ApplyPlayerOnlineInfo.UserId] = true + } +} diff --git a/gs/game/user_team.go b/gs/game/user_team.go index 8fa41d38..bd833dfe 100644 --- a/gs/game/user_team.go +++ b/gs/game/user_team.go @@ -206,6 +206,10 @@ func (g *GameManager) PacketSceneTeamUpdateNotify(world *World) *proto.SceneTeam empty := new(proto.AbilitySyncStateInfo) for _, worldAvatar := range world.GetWorldAvatarList() { worldPlayer := USER_MANAGER.GetOnlineUser(worldAvatar.uid) + if worldPlayer == nil { + logger.Error("player is nil, uid: %v", worldAvatar.uid) + continue + } worldPlayerScene := world.GetSceneById(worldPlayer.SceneId) worldPlayerAvatar := worldPlayer.AvatarMap[worldAvatar.avatarId] equipIdList := make([]uint32, 0) diff --git a/gs/game/user_video_player.go b/gs/game/video_player.go similarity index 100% rename from gs/game/user_video_player.go rename to gs/game/video_player.go diff --git a/gs/game/world_manager.go b/gs/game/world_manager.go index c3ab5858..b57f540c 100644 --- a/gs/game/world_manager.go +++ b/gs/game/world_manager.go @@ -112,7 +112,7 @@ type World struct { chatMsgList []*proto.ChatInfo // 世界聊天消息列表 aoiManager *aoi.AoiManager // 当前世界地图的aoi管理器 playerFirstEnterMap map[uint32]int64 // 玩家第一次进入世界的时间 key:uid value:进入时间 - waitEnterPlayerMap map[uint32]int64 // 等待进入世界的列表 key:uid value:开始时间 + waitEnterPlayerMap map[uint32]int64 // 进入世界的玩家等待列表 key:uid value:开始时间 multiplayerTeam *MultiplayerTeam peerList []*model.Player // 玩家编号列表 } @@ -701,7 +701,7 @@ func (s *Scene) CreateEntityAvatar(player *model.Player, avatarId uint32) uint32 if avatarId == s.world.GetPlayerActiveAvatarId(player) { s.world.aoiManager.AddEntityIdToGridByPos(entity.id, float32(entity.pos.X), float32(entity.pos.Y), float32(entity.pos.Z)) } - GAME_MANAGER.messageQueue.SendToFight(s.world.owner.FightAppId, &mq.NetMsg{ + MESSAGE_QUEUE.SendToFight(s.world.owner.FightAppId, &mq.NetMsg{ MsgType: mq.MsgTypeFight, EventId: mq.FightRoutineAddEntity, FightMsg: &mq.FightMsg{ @@ -751,7 +751,7 @@ func (s *Scene) CreateEntityMonster(pos *model.Vector, level uint8, fightProp ma } s.entityMap[entity.id] = entity s.world.aoiManager.AddEntityIdToGridByPos(entity.id, float32(entity.pos.X), float32(entity.pos.Y), float32(entity.pos.Z)) - GAME_MANAGER.messageQueue.SendToFight(s.world.owner.FightAppId, &mq.NetMsg{ + MESSAGE_QUEUE.SendToFight(s.world.owner.FightAppId, &mq.NetMsg{ MsgType: mq.MsgTypeFight, EventId: mq.FightRoutineAddEntity, FightMsg: &mq.FightMsg{ @@ -901,7 +901,7 @@ func (s *Scene) DestroyEntity(entityId uint32) { } s.world.aoiManager.RemoveEntityIdFromGridByPos(entity.id, float32(entity.pos.X), float32(entity.pos.Y), float32(entity.pos.Z)) delete(s.entityMap, entityId) - GAME_MANAGER.messageQueue.SendToFight(s.world.owner.FightAppId, &mq.NetMsg{ + MESSAGE_QUEUE.SendToFight(s.world.owner.FightAppId, &mq.NetMsg{ MsgType: mq.MsgTypeFight, EventId: mq.FightRoutineDelEntity, FightMsg: &mq.FightMsg{ diff --git a/pkg/logger/logger.go b/pkg/logger/logger.go index 0835f958..ce42e2ee 100644 --- a/pkg/logger/logger.go +++ b/pkg/logger/logger.go @@ -60,7 +60,6 @@ type LogInfo struct { FuncName string Line int GoroutineId string - Stack string } func InitLogger(appName string) { @@ -81,32 +80,29 @@ func (l *Logger) doLog() { logInfo := <-l.LogInfoChan timeNow := time.Now() timeNowStr := timeNow.Format("2006-01-02 15:04:05.000") - logHeader := CYAN + "[" + timeNowStr + "]" + RESET + " " + logStr := CYAN + "[" + timeNowStr + "]" + RESET + " " if logInfo.Level == DEBUG { - logHeader += BLUE + "[" + l.getLevelStr(logInfo.Level) + "]" + RESET + " " + logStr += BLUE + "[" + l.getLevelStr(logInfo.Level) + "]" + RESET } else if logInfo.Level == INFO { - logHeader += GREEN + "[" + l.getLevelStr(logInfo.Level) + "]" + RESET + " " + logStr += GREEN + "[" + l.getLevelStr(logInfo.Level) + "]" + RESET } else if logInfo.Level == WARN { - logHeader += YELLOW + "[" + l.getLevelStr(logInfo.Level) + "]" + RESET + " " + logStr += YELLOW + "[" + l.getLevelStr(logInfo.Level) + "]" + RESET } else if logInfo.Level == ERROR { - logHeader += RED + "[" + l.getLevelStr(logInfo.Level) + "]" + RESET + " " + logStr += RED + "[" + l.getLevelStr(logInfo.Level) + "]" + RESET + } + if logInfo.Level == ERROR { + logStr += " " + RED + fmt.Sprintf(logInfo.Msg, logInfo.Param...) + RESET + " " + } else { + logStr += " " + fmt.Sprintf(logInfo.Msg, logInfo.Param...) + " " } if l.Track { - logHeader += MAGENTA + "[" + + logStr += MAGENTA + "[" + logInfo.FileName + ":" + strconv.Itoa(logInfo.Line) + " " + logInfo.FuncName + "()" + " " + "goroutine:" + logInfo.GoroutineId + - "]" + RESET + " " - } - logStr := logHeader - if logInfo.Level == ERROR { - logStr += RED + fmt.Sprintf(logInfo.Msg, logInfo.Param...) + RESET + "\n" - } else { - logStr += fmt.Sprintf(logInfo.Msg, logInfo.Param...) + "\n" - } - if logInfo.Stack != "" { - logStr += logInfo.Stack + "]" + RESET } + logStr += "\n" if l.Mode == CONSOLE { log.Print(logStr) } else if l.Mode == FILE { @@ -222,22 +218,6 @@ func Error(msg string, param ...any) { LOG.LogInfoChan <- logInfo } -func ErrorStack(msg string, param ...any) { - if LOG.Level > ERROR { - return - } - logInfo := new(LogInfo) - logInfo.Level = ERROR - logInfo.Msg = msg - logInfo.Param = param - if LOG.Track { - logInfo.FileName, logInfo.Line, logInfo.FuncName = LOG.getLineFunc() - logInfo.GoroutineId = LOG.getGoroutineId() - logInfo.Stack = Stack() - } - LOG.LogInfoChan <- logInfo -} - func (l *Logger) getLevelInt(level string) (ret int) { switch level { case "DEBUG":