Ai世界玩家人数超过4人测试

This commit is contained in:
flswld
2023-01-10 19:21:56 +08:00
parent 3ecdb6ff70
commit 1793054ef4
22 changed files with 596 additions and 251 deletions

View File

@@ -63,6 +63,10 @@ func Run(ctx context.Context, configFile string) error {
AppId: APPID,
})
}()
mainGsAppid, err := client.Discovery.GetMainGameServerAppId(context.TODO(), &api.NullMsg{})
if err != nil {
return err
}
logger.InitLogger("gs_" + APPID)
logger.Warn("gs start, appid: %v, gsid: %v", APPID, GSID)
@@ -74,15 +78,15 @@ func Run(ctx context.Context, configFile string) error {
db, err := dao.NewDao()
if err != nil {
panic(err)
return err
}
defer db.CloseDao()
messageQueue := mq.NewMessageQueue(api.GS, APPID, client)
defer messageQueue.Close()
gameManager := game.NewGameManager(db, messageQueue, GSID)
defer gameManager.Stop()
gameManager := game.NewGameManager(db, messageQueue, GSID, APPID, mainGsAppid.AppId)
defer gameManager.Close()
// natsrpc server
conn, err := nats.Connect(config.CONF.MQ.NatsUrl)

View File

@@ -6,7 +6,6 @@ import (
"hk4e/gs/model"
"hk4e/pkg/logger"
"hk4e/protocol/proto"
)
// 命令管理器
@@ -36,7 +35,7 @@ type CommandMessage struct {
// CommandManager 命令管理器
type CommandManager struct {
system *model.Player // 机器人 目前负责收发消息 以及 大世界
system *model.Player // GM指令聊天消息机器人
commandFuncRouter map[string]CommandFunc // 记录命令处理函数
commandPermMap map[string]CommandPerm // 记录命令对应的权限
commandTextInput chan *CommandMessage // 传输要处理的命令文本
@@ -45,22 +44,17 @@ type CommandManager struct {
// NewCommandManager 新建命令管理器
func NewCommandManager() *CommandManager {
r := new(CommandManager)
// 创建AI世界
GAME_MANAGER.OnRegOk(false, &proto.SetPlayerBornDataReq{AvatarId: 10000007, NickName: "System"}, 1, 0, "")
GAME_MANAGER.ServerAppidBindNotify(1, "", 0)
r.system = USER_MANAGER.GetOnlineUser(1)
r.system.DbState = model.DbNormal
r.system.SceneLoadState = model.SceneEnterDone
WORLD_MANAGER.InitBigWorld(r.system)
// 初始化
r.commandTextInput = make(chan *CommandMessage, 1000)
r.InitRouter() // 初始化路由
return r
}
// SetSystem 设置GM指令聊天消息机器人
func (c *CommandManager) SetSystem(system *model.Player) {
c.system = system
}
// InitRouter 初始化命令路由
func (c *CommandManager) InitRouter() {
c.commandFuncRouter = make(map[string]CommandFunc)

View File

@@ -1,6 +1,7 @@
package game
import (
"encoding/json"
"reflect"
"time"
@@ -8,10 +9,12 @@ import (
"hk4e/common/mq"
"hk4e/gate/client_proto"
"hk4e/gate/kcp"
"hk4e/gdconf"
"hk4e/gs/dao"
"hk4e/gs/model"
"hk4e/pkg/alg"
"hk4e/pkg/logger"
"hk4e/pkg/random"
"hk4e/pkg/reflection"
"hk4e/protocol/cmd"
"hk4e/protocol/proto"
@@ -19,6 +22,15 @@ import (
pb "google.golang.org/protobuf/proto"
)
const (
AiBaseUid = 10000
AiName = "GM"
AiSign = "快捷指令"
BigWorldAiUid = 100
BigWorldAiName = "小可爱"
BigWorldAiSign = "UnKownOwO"
)
var GAME_MANAGER *GameManager = nil
var LOCAL_EVENT_MANAGER *LocalEventManager = nil
var ROUTE_MANAGER *RouteManager = nil
@@ -36,10 +48,18 @@ type GameManager struct {
snowflake *alg.SnowflakeWorker
clientCmdProtoMap *client_proto.ClientCmdProtoMap
clientCmdProtoMapRefValue reflect.Value
gsId uint32
gsAppid string
mainGsAppid string
ai *model.Player // 本服的Ai玩家对象
}
func NewGameManager(dao *dao.Dao, messageQueue *mq.MessageQueue, gsId uint32) (r *GameManager) {
func NewGameManager(dao *dao.Dao, messageQueue *mq.MessageQueue, gsId uint32, gsAppid string, mainGsAppid string) (r *GameManager) {
r = new(GameManager)
if appConfig.CONF.Hk4e.ClientProtoProxyEnable {
// 反射调用的方法在启动时测试是否正常防止中途panic
r.GetClientProtoObjByName("PingReq")
}
r.dao = dao
MESSAGE_QUEUE = messageQueue
r.snowflake = alg.NewSnowflakeWorker(int64(gsId))
@@ -47,6 +67,9 @@ func NewGameManager(dao *dao.Dao, messageQueue *mq.MessageQueue, gsId uint32) (r
r.clientCmdProtoMap = client_proto.NewClientCmdProtoMap()
r.clientCmdProtoMapRefValue = reflect.ValueOf(r.clientCmdProtoMap)
}
r.gsId = gsId
r.gsAppid = gsAppid
r.mainGsAppid = mainGsAppid
GAME_MANAGER = r
LOCAL_EVENT_MANAGER = NewLocalEventManager()
ROUTE_MANAGER = NewRouteManager()
@@ -55,10 +78,78 @@ func NewGameManager(dao *dao.Dao, messageQueue *mq.MessageQueue, gsId uint32) (r
TICK_MANAGER = NewTickManager()
COMMAND_MANAGER = NewCommandManager()
GCG_MANAGER = NewGCGManager()
// 创建本服的Ai世界
uid := AiBaseUid + gsId
name := AiName
sign := AiSign
if r.IsMainGs() {
// 约定MainGameServer的Ai的AiWorld叫BigWorld
// 此世界会出现在全服的在线玩家列表中 所有的玩家都可以进入到此世界里来
uid = BigWorldAiUid
name = BigWorldAiName
sign = BigWorldAiSign
}
r.ai = r.CreateRobot(uid, name, sign)
WORLD_MANAGER.InitAiWorld(r.ai)
COMMAND_MANAGER.SetSystem(r.ai)
USER_MANAGER.SetRemoteUserOnlineState(BigWorldAiUid, true, mainGsAppid)
if r.IsMainGs() {
// TODO 测试
for i := 1; i < 8; i++ {
uid := 1000000 + uint32(i)
avatarId := uint32(0)
for _, avatarData := range gdconf.CONF.AvatarDataMap {
avatarId = uint32(avatarData.AvatarId)
break
}
robot := r.CreateRobot(uid, random.GetRandomStr(8), random.GetRandomStr(10))
r.AddUserAvatar(uid, avatarId)
r.SetUpAvatarTeamReq(robot, &proto.SetUpAvatarTeamReq{
TeamId: 1,
AvatarTeamGuidList: []uint64{robot.AvatarMap[avatarId].Guid},
CurAvatarGuid: robot.AvatarMap[avatarId].Guid,
})
robot.Pos.X += random.GetRandomFloat64(0.0, 1.0)
robot.Pos.Z += random.GetRandomFloat64(0.0, 1.0)
r.UserWorldAddPlayer(WORLD_MANAGER.GetAiWorld(), robot)
}
}
r.run()
return r
}
func (g *GameManager) GetGsId() uint32 {
return g.gsId
}
func (g *GameManager) GetGsAppid() string {
return g.gsAppid
}
func (g *GameManager) GetMainGsAppid() string {
return g.mainGsAppid
}
func (g *GameManager) IsMainGs() bool {
// 目前的实现逻辑是当前GsId最小的Gs做MainGs
return g.gsAppid == g.mainGsAppid
}
// GetAi 获取本服的Ai玩家对象
func (g *GameManager) GetAi() *model.Player {
return g.ai
}
func (g *GameManager) CreateRobot(uid uint32, name string, sign string) *model.Player {
GAME_MANAGER.OnRegOk(false, &proto.SetPlayerBornDataReq{AvatarId: 10000007, NickName: name}, uid, 0, "")
GAME_MANAGER.ServerAppidBindNotify(uid, "", 0)
robot := USER_MANAGER.GetOnlineUser(uid)
robot.DbState = model.DbNormal
robot.SceneLoadState = model.SceneEnterDone
robot.Signature = sign
return robot
}
func (g *GameManager) run() {
go g.gameMainLoopD()
}
@@ -72,12 +163,14 @@ func (g *GameManager) gameMainLoopD() {
}
func (g *GameManager) gameMainLoop() {
// panic捕获
defer func() {
if err := recover(); err != nil {
logger.Error("!!! GAME MAIN LOOP PANIC !!!")
logger.Error("error: %v", err)
logger.Error("stack: %v", logger.Stack())
logger.Error("user: %v", SELF)
motherfuckerPlayerInfo, _ := json.Marshal(SELF)
logger.Error("the motherfucker player info: %v", motherfuckerPlayerInfo)
if SELF != nil {
GAME_MANAGER.DisconnectPlayer(SELF.PlayerID, kcp.EnetServerKick)
}
@@ -90,6 +183,7 @@ func (g *GameManager) gameMainLoop() {
localEventCost := int64(0)
commandCost := int64(0)
for {
// 消耗CPU时间性能统计
now := time.Now().UnixNano()
if now-lastTime > intervalTime {
routeCost /= 1e6
@@ -134,7 +228,7 @@ func (g *GameManager) gameMainLoop() {
end := time.Now().UnixNano()
localEventCost += end - start
case command := <-COMMAND_MANAGER.commandTextInput:
// 处理传入的命令 (普通玩家 GM命令)
// 处理传入的命令(普通玩家 GM命令)
start := time.Now().UnixNano()
COMMAND_MANAGER.HandleCommand(command)
end := time.Now().UnixNano()
@@ -143,24 +237,27 @@ func (g *GameManager) gameMainLoop() {
}
}
func (g *GameManager) Stop() {
// 下线玩家
userList := USER_MANAGER.GetAllOnlineUserList()
for _, player := range userList {
g.DisconnectPlayer(player.PlayerID, kcp.EnetServerShutdown)
}
time.Sleep(time.Second * 3)
var EXIT_SAVE_FIN_CHAN chan bool
func (g *GameManager) Close() {
// 保存玩家数据
onlinePlayerMap := USER_MANAGER.GetAllOnlineUserList()
saveUserIdList := make([]uint32, 0, len(onlinePlayerMap))
for userId := range onlinePlayerMap {
saveUserIdList = append(saveUserIdList, userId)
}
EXIT_SAVE_FIN_CHAN = make(chan bool)
LOCAL_EVENT_MANAGER.localEventChan <- &LocalEvent{
EventId: RunUserCopyAndSave,
EventId: ExitRunUserCopyAndSave,
Msg: saveUserIdList,
}
time.Sleep(time.Second * 3)
<-EXIT_SAVE_FIN_CHAN
// 单纯的告诉网关下线玩家
userList := USER_MANAGER.GetAllOnlineUserList()
for _, player := range userList {
g.DisconnectPlayer(player.PlayerID, kcp.EnetServerShutdown)
}
time.Sleep(time.Second)
}
// SendMsgToGate 发送消息给客户端 指定网关
@@ -169,7 +266,7 @@ func (g *GameManager) SendMsgToGate(cmdId uint16, userId uint32, clientSeq uint3
return
}
if payloadMsg == nil {
logger.Error("payload msg is nil")
logger.Error("payload msg is nil, stack: %v", logger.Stack())
return
}
gameMsg := &mq.GameMsg{
@@ -191,12 +288,12 @@ func (g *GameManager) SendMsg(cmdId uint16, userId uint32, clientSeq uint32, pay
return
}
if payloadMsg == nil {
logger.Error("payload msg is nil")
logger.Error("payload msg is nil, stack: %v", logger.Stack())
return
}
player := USER_MANAGER.GetOnlineUser(userId)
if player == nil {
logger.Error("player not exist, uid: %v", userId)
logger.Error("player not exist, uid: %v, stack: %v", userId, logger.Stack())
return
}
gameMsg := new(mq.GameMsg)
@@ -206,7 +303,7 @@ func (g *GameManager) SendMsg(cmdId uint16, userId uint32, clientSeq uint32, pay
// 在这里直接序列化成二进制数据 防止发送的消息内包含各种游戏数据指针 而造成并发读写的问题
payloadMessageData, err := pb.Marshal(payloadMsg)
if err != nil {
logger.Error("parse payload msg to bin error: %v", err)
logger.Error("parse payload msg to bin error: %v, stack: %v", err, logger.Stack())
return
}
gameMsg.PayloadMessageData = payloadMessageData
@@ -257,7 +354,7 @@ func (g *GameManager) SendToWorldA(world *World, cmdId uint16, seq uint32, msg p
}
}
// SendToWorldAEC 给世界内除自己以外的所有玩家发消息
// SendToWorldAEC 给世界内除某玩家(一般是自己)以外的所有玩家发消息
func (g *GameManager) SendToWorldAEC(world *World, cmdId uint16, seq uint32, msg pb.Message, uid uint32) {
for _, v := range world.GetAllPlayer() {
if uid == v.PlayerID {
@@ -293,10 +390,6 @@ func (g *GameManager) DisconnectPlayer(userId uint32, reason uint32) {
}
func (g *GameManager) GetClientProtoObjByName(protoObjName string) pb.Message {
if !appConfig.CONF.Hk4e.ClientProtoProxyEnable {
logger.Error("client proto proxy func not enable")
return nil
}
fn := g.clientCmdProtoMapRefValue.MethodByName("GetClientProtoObjByName")
ret := fn.Call([]reflect.Value{reflect.ValueOf(protoObjName)})
obj := ret[0].Interface()

View File

@@ -15,6 +15,7 @@ const (
LoadLoginUserFromDbFinish = iota // 玩家登录从数据库加载完成回调
CheckUserExistOnRegFromDbFinish // 玩家注册从数据库查询是否已存在完成回调
RunUserCopyAndSave // 执行一次在线玩家内存数据复制到数据库写入协程
ExitRunUserCopyAndSave
UserOfflineSaveToDbFinish
)
@@ -44,6 +45,8 @@ func (l *LocalEventManager) LocalEventHandle(localEvent *LocalEvent) {
case CheckUserExistOnRegFromDbFinish:
playerRegInfo := localEvent.Msg.(*PlayerRegInfo)
GAME_MANAGER.OnRegOk(playerRegInfo.Exist, playerRegInfo.Req, playerRegInfo.UserId, playerRegInfo.ClientSeq, playerRegInfo.GateAppId)
case ExitRunUserCopyAndSave:
fallthrough
case RunUserCopyAndSave:
saveUserIdList := localEvent.Msg.([]uint32)
startTime := time.Now().UnixNano()
@@ -90,13 +93,22 @@ func (l *LocalEventManager) LocalEventHandle(localEvent *LocalEvent) {
updatePlayerList = append(updatePlayerList, playerCopy)
}
}
USER_MANAGER.saveUserChan <- &SaveUserData{
saveUserData := &SaveUserData{
insertPlayerList: insertPlayerList,
updatePlayerList: updatePlayerList,
exitSave: false,
}
if localEvent.EventId == ExitRunUserCopyAndSave {
saveUserData.exitSave = true
}
USER_MANAGER.saveUserChan <- saveUserData
endTime := time.Now().UnixNano()
costTime := endTime - startTime
logger.Info("run save user copy cost time: %v ns", costTime)
if localEvent.EventId == ExitRunUserCopyAndSave {
// 在此阻塞掉主协程 不再进行任何消息和任务的处理
select {}
}
case UserOfflineSaveToDbFinish:
playerOfflineInfo := localEvent.Msg.(*PlayerOfflineInfo)
USER_MANAGER.DeleteUser(playerOfflineInfo.Player.PlayerID)

View File

@@ -237,7 +237,7 @@ func (t *TickManager) onTick10Second(now int64) {
func (t *TickManager) onTick5Second(now int64) {
for _, world := range WORLD_MANAGER.GetAllWorld() {
if world.IsBigWorld() {
if WORLD_MANAGER.IsAiWorld(world) {
for applyUid := range world.owner.CoopApplyMap {
GAME_MANAGER.UserDealEnterWorld(world.owner, applyUid, true)
}
@@ -349,7 +349,7 @@ func (t *TickManager) onTickSecond(now int64) {
}
}
// 刷怪
if !world.IsBigWorld() && world.owner.SceneLoadState == model.SceneEnterDone {
if !WORLD_MANAGER.IsRobotWorld(world) && world.owner.SceneLoadState == model.SceneEnterDone {
scene := world.GetSceneById(3)
monsterEntityCount := 0
for _, entity := range scene.entityMap {
@@ -386,10 +386,10 @@ func (t *TickManager) onTick100MilliSecond(now int64) {
func (t *TickManager) onTick50MilliSecond(now int64) {
// 音乐播放器
for i := 0; i < len(AUDIO_CHAN); i++ {
bigWorld := WORLD_MANAGER.GetBigWorld()
GAME_MANAGER.SendToWorldA(bigWorld, cmd.SceneAudioNotify, 0, &proto.SceneAudioNotify{
world := WORLD_MANAGER.GetAiWorld()
GAME_MANAGER.SendToWorldA(world, cmd.SceneAudioNotify, 0, &proto.SceneAudioNotify{
Type: 5,
SourceUid: bigWorld.owner.PlayerID,
SourceUid: world.owner.PlayerID,
Param1: []uint32{1, <-AUDIO_CHAN},
Param2: nil,
Param3: nil,

View File

@@ -1,6 +1,7 @@
package game
import (
appConfig "hk4e/common/config"
"hk4e/common/constant"
"hk4e/common/utils"
"hk4e/gs/model"
@@ -103,14 +104,22 @@ func (g *GameManager) CombatInvocationsNotify(player *model.Player, payloadMsg p
switch entry.ArgumentType {
case proto.CombatTypeArgument_COMBAT_TYPE_ARGUMENT_EVT_BEING_HIT:
hitInfo := new(proto.EvtBeingHitInfo)
clientProtoObj := g.GetClientProtoObjByName("EvtBeingHitInfo")
if clientProtoObj == nil {
logger.Error("get client proto obj is nil")
return
}
ok := utils.UnmarshalProtoObj(hitInfo, clientProtoObj, entry.CombatData)
if !ok {
continue
if appConfig.CONF.Hk4e.ClientProtoProxyEnable {
clientProtoObj := g.GetClientProtoObjByName("EvtBeingHitInfo")
if clientProtoObj == nil {
logger.Error("get client proto obj is nil")
continue
}
ok := utils.UnmarshalProtoObj(hitInfo, clientProtoObj, entry.CombatData)
if !ok {
continue
}
} else {
err := pb.Unmarshal(entry.CombatData, hitInfo)
if err != nil {
logger.Error("parse EvtBeingHitInfo error: %v", err)
continue
}
}
attackResult := hitInfo.AttackResult
if attackResult == nil {
@@ -149,14 +158,22 @@ func (g *GameManager) CombatInvocationsNotify(player *model.Player, payloadMsg p
player.CombatInvokeHandler.AddEntry(entry.ForwardType, entry)
case proto.CombatTypeArgument_COMBAT_TYPE_ARGUMENT_ENTITY_MOVE:
entityMoveInfo := new(proto.EntityMoveInfo)
clientProtoObj := g.GetClientProtoObjByName("EntityMoveInfo")
if clientProtoObj == nil {
logger.Error("get client proto obj is nil")
return
}
ok := utils.UnmarshalProtoObj(entityMoveInfo, clientProtoObj, entry.CombatData)
if !ok {
continue
if appConfig.CONF.Hk4e.ClientProtoProxyEnable {
clientProtoObj := g.GetClientProtoObjByName("EntityMoveInfo")
if clientProtoObj == nil {
logger.Error("get client proto obj is nil")
continue
}
ok := utils.UnmarshalProtoObj(entityMoveInfo, clientProtoObj, entry.CombatData)
if !ok {
continue
}
} else {
err := pb.Unmarshal(entry.CombatData, entityMoveInfo)
if err != nil {
logger.Error("parse EntityMoveInfo error: %v", err)
continue
}
}
motionInfo := entityMoveInfo.MotionInfo
if motionInfo.Pos == nil || motionInfo.Rot == nil {
@@ -202,14 +219,22 @@ func (g *GameManager) CombatInvocationsNotify(player *model.Player, payloadMsg p
player.CombatInvokeHandler.AddEntry(entry.ForwardType, entry)
case proto.CombatTypeArgument_COMBAT_TYPE_ARGUMENT_ANIMATOR_STATE_CHANGED:
evtAnimatorStateChangedInfo := new(proto.EvtAnimatorStateChangedInfo)
clientProtoObj := g.GetClientProtoObjByName("EvtAnimatorStateChangedInfo")
if clientProtoObj == nil {
logger.Error("get client proto obj is nil")
return
}
ok := utils.UnmarshalProtoObj(evtAnimatorStateChangedInfo, clientProtoObj, entry.CombatData)
if !ok {
continue
if appConfig.CONF.Hk4e.ClientProtoProxyEnable {
clientProtoObj := g.GetClientProtoObjByName("EvtAnimatorStateChangedInfo")
if clientProtoObj == nil {
logger.Error("get client proto obj is nil")
continue
}
ok := utils.UnmarshalProtoObj(evtAnimatorStateChangedInfo, clientProtoObj, entry.CombatData)
if !ok {
continue
}
} else {
err := pb.Unmarshal(entry.CombatData, evtAnimatorStateChangedInfo)
if err != nil {
logger.Error("parse EvtAnimatorStateChangedInfo error: %v", err)
continue
}
}
logger.Debug("EvtAnimatorStateChangedInfo: %v", entry, player.PlayerID)
player.CombatInvokeHandler.AddEntry(entry.ForwardType, entry)
@@ -295,14 +320,22 @@ func (g *GameManager) ClientAbilityChangeNotify(player *model.Player, payloadMsg
switch abilityInvokeEntry.ArgumentType {
case proto.AbilityInvokeArgument_ABILITY_INVOKE_ARGUMENT_META_ADD_NEW_ABILITY:
abilityMetaAddAbility := new(proto.AbilityMetaAddAbility)
clientProtoObj := g.GetClientProtoObjByName("AbilityMetaAddAbility")
if clientProtoObj == nil {
logger.Error("get client proto obj is nil")
return
}
ok := utils.UnmarshalProtoObj(abilityMetaAddAbility, clientProtoObj, abilityInvokeEntry.AbilityData)
if !ok {
continue
if appConfig.CONF.Hk4e.ClientProtoProxyEnable {
clientProtoObj := g.GetClientProtoObjByName("AbilityMetaAddAbility")
if clientProtoObj == nil {
logger.Error("get client proto obj is nil")
continue
}
ok := utils.UnmarshalProtoObj(abilityMetaAddAbility, clientProtoObj, abilityInvokeEntry.AbilityData)
if !ok {
continue
}
} else {
err := pb.Unmarshal(abilityInvokeEntry.AbilityData, abilityMetaAddAbility)
if err != nil {
logger.Error("parse AbilityMetaAddAbility error: %v", err)
continue
}
}
worldAvatar := world.GetWorldAvatarByEntityId(abilityInvokeEntry.EntityId)
if worldAvatar == nil {
@@ -314,14 +347,22 @@ func (g *GameManager) ClientAbilityChangeNotify(player *model.Player, payloadMsg
worldAvatar.abilityList = append(worldAvatar.abilityList, abilityMetaAddAbility.Ability)
case proto.AbilityInvokeArgument_ABILITY_INVOKE_ARGUMENT_META_MODIFIER_CHANGE:
abilityMetaModifierChange := new(proto.AbilityMetaModifierChange)
clientProtoObj := g.GetClientProtoObjByName("AbilityMetaModifierChange")
if clientProtoObj == nil {
logger.Error("get client proto obj is nil")
return
}
ok := utils.UnmarshalProtoObj(abilityMetaModifierChange, clientProtoObj, abilityInvokeEntry.AbilityData)
if !ok {
continue
if appConfig.CONF.Hk4e.ClientProtoProxyEnable {
clientProtoObj := g.GetClientProtoObjByName("AbilityMetaModifierChange")
if clientProtoObj == nil {
logger.Error("get client proto obj is nil")
continue
}
ok := utils.UnmarshalProtoObj(abilityMetaModifierChange, clientProtoObj, abilityInvokeEntry.AbilityData)
if !ok {
continue
}
} else {
err := pb.Unmarshal(abilityInvokeEntry.AbilityData, abilityMetaModifierChange)
if err != nil {
logger.Error("parse AbilityMetaModifierChange error: %v", err)
continue
}
}
abilityAppliedModifier := &proto.AbilityAppliedModifier{
ModifierLocalId: abilityMetaModifierChange.ModifierLocalId,

View File

@@ -49,7 +49,6 @@ func (g *GameManager) OnLoginOk(userId uint32, player *model.Player, clientSeq u
// 初始化
player.InitAll()
// player.TeamConfig.UpdateTeam()
// 确保玩家位置安全
player.Pos.X = player.SafePos.X
@@ -65,18 +64,21 @@ func (g *GameManager) OnLoginOk(userId uint32, player *model.Player, clientSeq u
player.CombatInvokeHandler = model.NewInvokeHandler[proto.CombatInvokeEntry]()
player.AbilityInvokeHandler = model.NewInvokeHandler[proto.AbilityInvokeEntry]()
if userId < 100000000 {
return
}
g.LoginNotify(userId, player, clientSeq)
if userId >= 100000000 {
MESSAGE_QUEUE.SendToAll(&mq.NetMsg{
MsgType: mq.MsgTypeServer,
EventId: mq.ServerUserOnlineStateChangeNotify,
ServerMsg: &mq.ServerMsg{
UserId: userId,
IsOnline: true,
},
})
}
MESSAGE_QUEUE.SendToAll(&mq.NetMsg{
MsgType: mq.MsgTypeServer,
EventId: mq.ServerUserOnlineStateChangeNotify,
ServerMsg: &mq.ServerMsg{
UserId: userId,
IsOnline: true,
},
})
TICK_MANAGER.CreateUserGlobalTick(userId)
TICK_MANAGER.CreateUserTimer(userId, UserTimerActionTest, 100)
}

View File

@@ -353,6 +353,7 @@ func (u *UserManager) SaveTempOfflineUser(player *model.Player) {
type SaveUserData struct {
insertPlayerList []*model.Player
updatePlayerList []*model.Player
exitSave bool
}
func (u *UserManager) saveUserHandle() {
@@ -360,6 +361,10 @@ func (u *UserManager) saveUserHandle() {
saveUserData := <-u.saveUserChan
u.SaveUserListToDbSync(saveUserData)
u.SaveUserListToRedisSync(saveUserData)
if saveUserData.exitSave {
// 停服落地玩家数据完毕 通知APP主协程关闭程序
EXIT_SAVE_FIN_CHAN <- true
}
}
}

View File

@@ -359,6 +359,9 @@ func (g *GameManager) UserLeaveWorld(player *model.Player) bool {
}
func (g *GameManager) UserWorldAddPlayer(world *World, player *model.Player) {
if !WORLD_MANAGER.IsBigWorld(world) && world.GetWorldPlayerNum() >= 4 {
return
}
_, exist := world.playerMap[player.PlayerID]
if exist {
return

View File

@@ -417,8 +417,8 @@ func (g *GameManager) AddSceneEntityNotifyBroadcast(player *model.Player, scene
continue
}
g.SendMsg(cmd.SceneEntityAppearNotify, scenePlayer.PlayerID, scenePlayer.ClientSeq, sceneEntityAppearNotify)
logger.Debug("SceneEntityAppearNotify, uid: %v, type: %v, len: %v",
scenePlayer.PlayerID, sceneEntityAppearNotify.AppearType, len(sceneEntityAppearNotify.EntityList))
// logger.Debug("SceneEntityAppearNotify, uid: %v, type: %v, len: %v",
// scenePlayer.PlayerID, sceneEntityAppearNotify.AppearType, len(sceneEntityAppearNotify.EntityList))
}
}
@@ -428,8 +428,8 @@ func (g *GameManager) RemoveSceneEntityNotifyToPlayer(player *model.Player, visi
DisappearType: visionType,
}
g.SendMsg(cmd.SceneEntityDisappearNotify, player.PlayerID, player.ClientSeq, sceneEntityDisappearNotify)
logger.Debug("SceneEntityDisappearNotify, uid: %v, type: %v, len: %v",
player.PlayerID, sceneEntityDisappearNotify.DisappearType, len(sceneEntityDisappearNotify.EntityList))
// logger.Debug("SceneEntityDisappearNotify, uid: %v, type: %v, len: %v",
// player.PlayerID, sceneEntityDisappearNotify.DisappearType, len(sceneEntityDisappearNotify.EntityList))
}
func (g *GameManager) RemoveSceneEntityNotifyBroadcast(scene *Scene, visionType proto.VisionType, entityIdList []uint32) {
@@ -459,8 +459,8 @@ func (g *GameManager) AddSceneEntityNotify(player *model.Player, visionType prot
}
entityList := make([]*proto.SceneEntityInfo, 0)
for _, entityId := range entityIdList[begin:end] {
entity, ok := scene.entityMap[entityId]
if !ok {
entity, exist := scene.entityMap[entityId]
if !exist {
logger.Error("get entity is nil, entityId: %v", entityId)
continue
}

View File

@@ -380,12 +380,29 @@ func (g *GameManager) GetOnlinePlayerListReq(player *model.Player, payloadMsg pb
logger.Debug("user get online player list, uid: %v", player.PlayerID)
count := 0
getOnlinePlayerListRsp := &proto.GetOnlinePlayerListRsp{
PlayerInfoList: make([]*proto.OnlinePlayerInfo, 0),
}
getOnlinePlayerListRsp.PlayerInfoList = append(getOnlinePlayerListRsp.PlayerInfoList, &proto.OnlinePlayerInfo{
Uid: BigWorldAiUid,
Nickname: BigWorldAiName,
PlayerLevel: 1,
MpSettingType: proto.MpSettingType_MP_SETTING_TYPE_ENTER_AFTER_APPLY,
NameCardId: 210001,
Signature: BigWorldAiSign,
ProfilePicture: &proto.ProfilePicture{AvatarId: 10000007},
CurPlayerNumInWorld: 1,
})
count++
onlinePlayerList := make([]*model.Player, 0)
// 优先获取本地的在线玩家
for _, onlinePlayer := range USER_MANAGER.GetAllOnlineUserList() {
if onlinePlayer.PlayerID == player.PlayerID {
continue
}
if g.IsMainGs() && onlinePlayer.PlayerID == g.GetAi().PlayerID {
continue
}
onlinePlayerList = append(onlinePlayerList, onlinePlayer)
count++
if count >= 50 {
@@ -406,9 +423,6 @@ func (g *GameManager) GetOnlinePlayerListReq(player *model.Player, payloadMsg pb
}
}
getOnlinePlayerListRsp := &proto.GetOnlinePlayerListRsp{
PlayerInfoList: make([]*proto.OnlinePlayerInfo, 0),
}
for _, onlinePlayer := range onlinePlayerList {
onlinePlayerInfo := g.PacketOnlinePlayerInfo(onlinePlayer)
getOnlinePlayerListRsp.PlayerInfoList = append(getOnlinePlayerListRsp.PlayerInfoList, onlinePlayerInfo)

View File

@@ -4,6 +4,7 @@ import (
"strings"
"time"
appConfig "hk4e/common/config"
"hk4e/common/constant"
"hk4e/common/utils"
"hk4e/gdconf"
@@ -22,14 +23,22 @@ func (g *GameManager) HandleAbilityStamina(player *model.Player, entry *proto.Ab
case proto.AbilityInvokeArgument_ABILITY_INVOKE_ARGUMENT_MIXIN_COST_STAMINA:
// 大剑重击 或 持续技能 耐力消耗
costStamina := new(proto.AbilityMixinCostStamina)
clientProtoObj := g.GetClientProtoObjByName("AbilityMixinCostStamina")
if clientProtoObj == nil {
logger.Error("get client proto obj is nil")
return
}
ok := utils.UnmarshalProtoObj(costStamina, clientProtoObj, entry.AbilityData)
if !ok {
return
if appConfig.CONF.Hk4e.ClientProtoProxyEnable {
clientProtoObj := g.GetClientProtoObjByName("AbilityMixinCostStamina")
if clientProtoObj == nil {
logger.Error("get client proto obj is nil")
return
}
ok := utils.UnmarshalProtoObj(costStamina, clientProtoObj, entry.AbilityData)
if !ok {
return
}
} else {
err := pb.Unmarshal(entry.AbilityData, costStamina)
if err != nil {
logger.Error("parse AbilityMixinCostStamina error: %v", err)
return
}
}
// 处理持续耐力消耗
g.SkillSustainStamina(player, costStamina.IsSwim)

View File

@@ -218,7 +218,7 @@ func (g *GameManager) VideoPlayerUpdate(rgb bool) {
if err != nil {
return
}
world := WORLD_MANAGER.GetBigWorld()
world := WORLD_MANAGER.GetAiWorld()
scene := world.GetSceneById(3)
for _, v := range SCREEN_ENTITY_ID_LIST {
scene.DestroyEntity(v)

View File

@@ -18,7 +18,7 @@ import (
type WorldManager struct {
worldMap map[uint32]*World
snowflake *alg.SnowflakeWorker
bigWorld *World
aiWorld *World // 本服的Ai玩家世界
}
func NewWorldManager(snowflake *alg.SnowflakeWorker) (r *WorldManager) {
@@ -60,13 +60,6 @@ func (w *WorldManager) CreateWorld(owner *model.Player) *World {
multiplayerTeam: CreateMultiplayerTeam(),
peerList: make([]*model.Player, 0),
}
if world.IsBigWorld() {
// world.aoiManager = aoi.NewAoiManager(
// -8000, 4000, 800,
// -2000, 2000, 1,
// -5500, 6500, 800,
// )
}
world.mpLevelEntityId = world.GetNextWorldEntityId(constant.EntityIdTypeConst.MPLEVEL)
w.worldMap[worldId] = world
return world
@@ -81,20 +74,28 @@ func (w *WorldManager) DestroyWorld(worldId uint32) {
delete(w.worldMap, worldId)
}
// GetBigWorld 获取本服务器的AI世界
func (w *WorldManager) GetBigWorld() *World {
return w.bigWorld
// GetAiWorld 获取本服务器的Ai世界
func (w *WorldManager) GetAiWorld() *World {
return w.aiWorld
}
// InitBigWorld 初始化AI世界
func (w *WorldManager) InitBigWorld(owner *model.Player) {
w.bigWorld = w.GetWorldByID(owner.WorldId)
w.bigWorld.ChangeToMultiplayer()
// InitAiWorld 初始化Ai世界
func (w *WorldManager) InitAiWorld(owner *model.Player) {
w.aiWorld = w.GetWorldByID(owner.WorldId)
w.aiWorld.ChangeToMultiplayer()
go RunPlayAudio()
}
func (w *World) IsBigWorld() bool {
return w.owner.PlayerID == 1
func (w *WorldManager) IsAiWorld(world *World) bool {
return world.id == w.aiWorld.id
}
func (w *WorldManager) IsRobotWorld(world *World) bool {
return world.owner.PlayerID < 100000000
}
func (w *WorldManager) IsBigWorld(world *World) bool {
return (world.id == w.aiWorld.id) && (w.aiWorld.owner.PlayerID == BigWorldAiUid)
}
// 世界数据结构
@@ -152,7 +153,7 @@ func (w *World) GetPlayerPeerId(player *model.Player) uint32 {
peerId = uint32(peerIdIndex) + 1
}
}
logger.Debug("get player peer id is: %v, uid: %v", peerId, player.PlayerID)
// logger.Debug("get player peer id is: %v, uid: %v", peerId, player.PlayerID)
return peerId
}
@@ -181,7 +182,15 @@ func (w *World) AddPlayer(player *model.Player, sceneId uint32) {
activeAvatarId := player.TeamConfig.GetActiveAvatarId()
w.SetPlayerLocalTeam(player, []uint32{activeAvatarId})
}
w.UpdateMultiplayerTeam()
playerNum := w.GetWorldPlayerNum()
if playerNum > 4 {
if !WORLD_MANAGER.IsBigWorld(w) {
return
}
w.AddMultiplayerTeam(player)
} else {
w.UpdateMultiplayerTeam()
}
for _, worldPlayer := range w.playerMap {
list := w.GetPlayerWorldAvatarList(worldPlayer)
maxIndex := len(list) - 1
@@ -207,7 +216,17 @@ func (w *World) RemovePlayer(player *model.Player) {
delete(w.multiplayerTeam.localTeamMap, player.PlayerID)
delete(w.multiplayerTeam.localAvatarIndexMap, player.PlayerID)
delete(w.multiplayerTeam.localTeamEntityMap, player.PlayerID)
w.UpdateMultiplayerTeam()
playerNum := w.GetWorldPlayerNum()
if playerNum > 4 {
if !WORLD_MANAGER.IsBigWorld(w) {
return
}
w.RemoveMultiplayerTeam(player)
} else {
if player.PlayerID != w.owner.PlayerID {
w.UpdateMultiplayerTeam()
}
}
}
// WorldAvatar 世界角色
@@ -432,14 +451,37 @@ func (w *World) copyLocalTeamToWorld(start int, end int, peerId uint32) {
}
}
// TODO 为了实现大世界无限人数写的
// 现在看来把世界里所有人放进队伍里发给客户端超过8个客户端会崩溃
// 看来还是不能简单的走通用逻辑 需要对大世界场景队伍做特殊处理 欺骗客户端其他玩家仅仅以场景角色实体的形式出现
func (w *World) AddMultiplayerTeam(player *model.Player) {
if !WORLD_MANAGER.IsBigWorld(w) {
return
}
localTeam := w.GetPlayerLocalTeam(player)
w.multiplayerTeam.worldTeam = append(w.multiplayerTeam.worldTeam, localTeam...)
}
func (w *World) RemoveMultiplayerTeam(player *model.Player) {
worldTeam := make([]*WorldAvatar, 0)
for _, worldAvatar := range w.multiplayerTeam.worldTeam {
if worldAvatar.uid == player.PlayerID {
continue
}
worldTeam = append(worldTeam, worldAvatar)
}
w.multiplayerTeam.worldTeam = worldTeam
}
// UpdateMultiplayerTeam 整合所有玩家的本地队伍计算出世界队伍
func (w *World) UpdateMultiplayerTeam() {
_, exist := w.playerMap[w.owner.PlayerID]
if !exist {
playerNum := w.GetWorldPlayerNum()
if playerNum > 4 {
return
}
w.multiplayerTeam.worldTeam = make([]*WorldAvatar, 4)
switch w.GetWorldPlayerNum() {
switch playerNum {
case 1:
// 1P*4
w.copyLocalTeamToWorld(0, 3, 1)
@@ -458,8 +500,6 @@ func (w *World) UpdateMultiplayerTeam() {
w.copyLocalTeamToWorld(1, 1, 2)
w.copyLocalTeamToWorld(2, 2, 3)
w.copyLocalTeamToWorld(3, 3, 4)
default:
break
}
}