玩家管理模块接入redis,已完整实现好友、多人世界的跨服交互功能

This commit is contained in:
flswld
2022-12-29 22:42:57 +08:00
parent 0a2e13fad3
commit 6fd3d6a349
25 changed files with 1094 additions and 407 deletions

View File

@@ -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)
}
}

53
gs/dao/player_redis.go Normal file
View File

@@ -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)
}
}

View File

@@ -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)

View File

@@ -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{

View File

@@ -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)
}
}
}

View File

@@ -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)
}
}
}

View File

@@ -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)
}
}

View File

@@ -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,
},
})
}
}

View File

@@ -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) {

View File

@@ -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)
}

View File

@@ -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)
}
}
}

View File

@@ -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)
}
}

View File

@@ -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
}
}

View File

@@ -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)

View File

@@ -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{