优化注册与登录流程

This commit is contained in:
flswld
2023-04-04 18:09:14 +08:00
parent 4bc835e0b1
commit d542647f55
22 changed files with 474 additions and 600 deletions

View File

@@ -141,10 +141,10 @@ func (g *Game) GetAi() *model.Player {
}
func (g *Game) CreateRobot(uid uint32, name string, sign string) *model.Player {
GAME.OnRegOk(false, &proto.SetPlayerBornDataReq{AvatarId: 10000007, NickName: name}, uid, 0, "")
GAME.ServerAppidBindNotify(uid, "", 0)
g.OnLogin(uid, 0, "", nil, 0)
robot := USER_MANAGER.GetOnlineUser(uid)
robot.DbState = model.DbNormal
g.SetPlayerBornDataReq(robot, &proto.SetPlayerBornDataReq{AvatarId: 10000007, NickName: name})
robot.SceneLoadState = model.SceneEnterDone
robot.Signature = sign
return robot

View File

@@ -16,13 +16,12 @@ import (
// 本地事件队列管理器
const (
LoadLoginUserFromDbFinish = iota // 玩家登录从数据库加载完成回调
CheckUserExistOnRegFromDbFinish // 玩家注册从数据库查询是否已存在完成回调
RunUserCopyAndSave // 执行一次在线玩家内存数据复制到数据库写入协程
ExitRunUserCopyAndSave // 停服时执行全部玩家保存操作
UserOfflineSaveToDbFinish // 玩家离线保存完成
ReloadGameDataConfig // 执行热更表
ReloadGameDataConfigFinish // 热更表完成
LoadLoginUserFromDbFinish = iota // 玩家登录从数据库加载完成回调
RunUserCopyAndSave // 执行一次在线玩家内存数据复制到数据库写入协程
ExitRunUserCopyAndSave // 停服时执行全部玩家保存操作
UserOfflineSaveToDbFinish // 玩家离线保存完成
ReloadGameDataConfig // 执行热更表
ReloadGameDataConfigFinish // 热更表完成
)
const (
@@ -66,13 +65,7 @@ func (l *LocalEventManager) LocalEventHandle(localEvent *LocalEvent) {
switch localEvent.EventId {
case LoadLoginUserFromDbFinish:
playerLoginInfo := localEvent.Msg.(*PlayerLoginInfo)
if playerLoginInfo.Player != nil {
USER_MANAGER.AddUser(playerLoginInfo.Player)
}
GAME.OnLoginOk(playerLoginInfo.UserId, playerLoginInfo.ClientSeq, playerLoginInfo.GateAppId, false, playerLoginInfo.Player)
case CheckUserExistOnRegFromDbFinish:
playerRegInfo := localEvent.Msg.(*PlayerRegInfo)
GAME.OnRegOk(playerRegInfo.Exist, playerRegInfo.Req, playerRegInfo.UserId, playerRegInfo.ClientSeq, playerRegInfo.GateAppId)
GAME.OnLogin(playerLoginInfo.UserId, playerLoginInfo.ClientSeq, playerLoginInfo.GateAppId, playerLoginInfo.Player, playerLoginInfo.JoinHostUserId)
case ExitRunUserCopyAndSave:
fallthrough
case RunUserCopyAndSave:

View File

@@ -54,6 +54,7 @@ func (r *RouteManager) doRoute(cmdId uint16, userId uint32, clientSeq uint32, pa
}
func (r *RouteManager) initRoute() {
r.registerRouter(cmd.SetPlayerBornDataReq, GAME.SetPlayerBornDataReq)
r.registerRouter(cmd.QueryPathReq, GAME.QueryPathReq)
r.registerRouter(cmd.UnionCmdNotify, GAME.UnionCmdNotify)
r.registerRouter(cmd.MassiveEntityElementOpBatchNotify, GAME.MassiveEntityElementOpBatchNotify)
@@ -164,10 +165,6 @@ func (r *RouteManager) RouteHandle(netMsg *mq.NetMsg) {
GAME.PlayerLoginReq(gameMsg.UserId, gameMsg.ClientSeq, netMsg.OriginServerAppId, gameMsg.PayloadMessage)
return
}
if gameMsg.CmdId == cmd.SetPlayerBornDataReq {
GAME.SetPlayerBornDataReq(gameMsg.UserId, gameMsg.ClientSeq, netMsg.OriginServerAppId, gameMsg.PayloadMessage)
return
}
r.doRoute(gameMsg.CmdId, gameMsg.UserId, gameMsg.ClientSeq, gameMsg.PayloadMessage)
}
case mq.MsgTypeConnCtrl:
@@ -192,7 +189,7 @@ func (r *RouteManager) RouteHandle(netMsg *mq.NetMsg) {
logger.Debug("remote user online state change, uid: %v, online: %v", serverMsg.UserId, serverMsg.IsOnline)
USER_MANAGER.SetRemoteUserOnlineState(serverMsg.UserId, serverMsg.IsOnline, netMsg.OriginServerAppId)
case mq.ServerAppidBindNotify:
GAME.ServerAppidBindNotify(serverMsg.UserId, serverMsg.AnticheatServerAppId, serverMsg.JoinHostUserId)
GAME.ServerAppidBindNotify(serverMsg.UserId, serverMsg.AnticheatServerAppId)
case mq.ServerUserMpReq:
GAME.ServerUserMpReq(serverMsg.UserMpInfo, netMsg.OriginServerAppId)
case mq.ServerUserMpRsp:

View File

@@ -152,12 +152,6 @@ func (t *TickManager) OnGameServerTick() {
if t.globalTickCount%(60000*60/ServerTickTime) == 0 {
t.onTickHour(now)
}
if t.globalTickCount%(60000*60*24/ServerTickTime) == 0 {
t.onTickDay(now)
}
if t.globalTickCount%(60000*60*24*7/ServerTickTime) == 0 {
t.onTickWeek(now)
}
for userId, userTick := range t.userTickMap {
if len(userTick.globalTick.C) == 0 {
// 跳过还没到时间的定时器
@@ -184,14 +178,6 @@ func (t *TickManager) OnGameServerTick() {
}
}
func (t *TickManager) onTickWeek(now int64) {
logger.Info("on tick week, time: %v", now)
}
func (t *TickManager) onTickDay(now int64) {
logger.Info("on tick day, time: %v", now)
}
func (t *TickManager) onTickHour(now int64) {
logger.Info("on tick hour, time: %v", now)
}

View File

@@ -8,14 +8,12 @@ import (
"hk4e/gs/dao"
"hk4e/gs/model"
"hk4e/pkg/logger"
"hk4e/protocol/proto"
"github.com/vmihailenco/msgpack/v5"
)
// 玩家管理器
// 玩家注册 从db查询对应uid是否存在并异步回调返回结果
// 玩家登录 从db查询出来然后写入redis并异步回调返回玩家对象
// 玩家离线 写入db和redis
// 玩家定时保存 写入db和redis
@@ -78,42 +76,6 @@ func (u *UserManager) GetAllOnlineUserList() map[uint32]*model.Player {
return onlinePlayerMap
}
type PlayerRegInfo struct {
Exist bool
Req *proto.SetPlayerBornDataReq
UserId uint32
ClientSeq uint32
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.LoadUserFromDbSync(userId)
exist = false
if player != nil {
exist = true
}
LOCAL_EVENT_MANAGER.GetLocalEventChan() <- &LocalEvent{
EventId: CheckUserExistOnRegFromDbFinish,
Msg: &PlayerRegInfo{
Exist: exist,
Req: req,
UserId: userId,
ClientSeq: clientSeq,
GateAppId: gateAppId,
},
}
}()
return false, true
}
}
// AddUser 向内存玩家数据里添加一个玩家
func (u *UserManager) AddUser(player *model.Player) {
if player == nil {
@@ -128,55 +90,51 @@ func (u *UserManager) DeleteUser(userId uint32) {
}
type PlayerLoginInfo struct {
UserId uint32
Player *model.Player
ClientSeq uint32
GateAppId string
UserId uint32
Player *model.Player
ClientSeq uint32
GateAppId string
JoinHostUserId uint32
}
// OnlineUser 玩家上线
func (u *UserManager) OnlineUser(userId uint32, clientSeq uint32, gateAppId string) (*model.Player, bool) {
player, exist := u.playerMap[userId]
if userId > PlayerBaseUid {
// 正常登录
if exist {
// 每次玩家上线必须从数据库加载最新的档 如果之前存在于内存则删掉
u.DeleteUser(userId)
}
go func() {
// 加离线玩家数据分布式锁
ok := u.dao.DistLockSync(userId)
if !ok {
logger.Error("lock redis offline player data error, uid: %v", userId)
return
}
player := u.LoadUserFromDbSync(userId)
if player != nil {
u.SaveUserToRedisSync(player)
}
// 解离线玩家数据分布式锁
u.dao.DistUnlock(userId)
if player != nil {
u.ChangeUserDbState(player, model.DbNormal)
player.ChatMsgMap = u.LoadUserChatMsgFromDbSync(userId)
} else {
logger.Error("can not find user from db, uid: %v", userId)
}
LOCAL_EVENT_MANAGER.GetLocalEventChan() <- &LocalEvent{
EventId: LoadLoginUserFromDbFinish,
Msg: &PlayerLoginInfo{
UserId: userId,
Player: player,
ClientSeq: clientSeq,
GateAppId: gateAppId,
},
}
}()
return nil, false
} else {
// 机器人
return player, true
func (u *UserManager) OnlineUser(userId uint32, clientSeq uint32, gateAppId string, joinHostUserId uint32) {
_, exist := u.playerMap[userId]
// 正常登录
if exist {
// 每次玩家上线必须从数据库加载最新的档 如果之前存在于内存则删掉
u.DeleteUser(userId)
}
go func() {
// 加离线玩家数据分布式锁
ok := u.dao.DistLockSync(userId)
if !ok {
logger.Error("lock redis offline player data error, uid: %v", userId)
return
}
player := u.LoadUserFromDbSync(userId)
if player != nil {
u.SaveUserToRedisSync(player)
}
// 解离线玩家数据分布式锁
u.dao.DistUnlock(userId)
if player != nil {
u.ChangeUserDbState(player, model.DbNormal)
player.ChatMsgMap = u.LoadUserChatMsgFromDbSync(userId)
} else {
logger.Error("can not find user from db, uid: %v", userId)
}
LOCAL_EVENT_MANAGER.GetLocalEventChan() <- &LocalEvent{
EventId: LoadLoginUserFromDbFinish,
Msg: &PlayerLoginInfo{
UserId: userId,
Player: player,
ClientSeq: clientSeq,
GateAppId: gateAppId,
JoinHostUserId: joinHostUserId,
},
}
}()
}
type ChangeGsInfo struct {

View File

@@ -421,6 +421,15 @@ func (w *WorldAvatar) GetAbilityList() []*proto.AbilityAppliedAbility {
return w.abilityList
}
func (w *WorldAvatar) GetAbilityByInstanceId(instanceId uint32) *proto.AbilityAppliedAbility {
for _, ability := range w.abilityList {
if ability.InstancedAbilityId == instanceId {
return ability
}
}
return nil
}
func (w *WorldAvatar) SetAbilityList(abilityList []*proto.AbilityAppliedAbility) {
w.abilityList = abilityList
}

View File

@@ -119,31 +119,14 @@ func (g *Game) ObstacleModifyNotify(player *model.Player, payloadMsg pb.Message)
// logger.Debug("ObstacleModifyNotify: %v, uid: %v", ntf, player.PlayerID)
}
func (g *Game) ServerAppidBindNotify(userId uint32, anticheatAppId string, joinHostUserId uint32) {
func (g *Game) ServerAppidBindNotify(userId uint32, anticheatAppId string) {
player := USER_MANAGER.GetOnlineUser(userId)
if player == nil {
logger.Error("player is nil, uid: %v", userId)
return
}
if joinHostUserId != 0 {
hostPlayer := USER_MANAGER.GetOnlineUser(joinHostUserId)
if hostPlayer == nil {
logger.Error("player is nil, uid: %v", joinHostUserId)
return
}
g.JoinOtherWorld(player, hostPlayer)
return
}
logger.Debug("server appid bind notify, uid: %v, anticheatAppId: %v", userId, anticheatAppId)
player.AnticheatAppId = anticheatAppId
// 创建世界
world := WORLD_MANAGER.CreateWorld(player)
world.AddPlayer(player, player.SceneId)
player.WorldId = world.GetId()
// 进入场景
player.SceneJump = true
player.SceneLoadState = model.SceneNone
g.SendMsg(cmd.PlayerEnterSceneNotify, userId, player.ClientSeq, g.PacketPlayerEnterSceneNotifyLogin(player, proto.EnterType_ENTER_SELF))
}
// WorldPlayerRTTNotify 世界里所有玩家的网络延迟广播

View File

@@ -136,15 +136,9 @@ func (g *Game) CombatInvocationsNotify(player *model.Player, payloadMsg pb.Messa
}
logger.Debug("[EvtBeingHit] GadgetData: %+v, uid: %v", gadgetDataConfig, player.PlayerID)
// TODO 临时的解决方案
switch gadgetDataConfig.ServerLuaScript {
case "SubfieldDrop_WoodenObject_Broken":
g.KillEntity(player, scene, target.GetId(), proto.PlayerDieType_PLAYER_DIE_GM)
case "SetGadgetState":
if strings.Contains(gadgetDataConfig.ServerLuaScript, "SetGadgetState") {
g.ChangeGadgetState(player, target.GetId(), constant.GADGET_STATE_GEAR_START)
}
if strings.Contains(gadgetDataConfig.ServerLuaScript, "SubfieldDrop_Ore_") {
g.KillEntity(player, scene, target.GetId(), proto.PlayerDieType_PLAYER_DIE_GM)
}
if strings.Contains(gadgetDataConfig.ServerLuaScript, "Controller") {
g.ChangeGadgetState(player, target.GetId(), constant.GADGET_STATE_GEAR_START)
}
@@ -331,12 +325,8 @@ func (g *Game) AbilityInvocationsNotify(player *model.Player, payloadMsg pb.Mess
if player.SceneLoadState != model.SceneEnterDone {
return
}
world := WORLD_MANAGER.GetWorldByID(player.WorldId)
if world == nil {
return
}
for _, entry := range req.Invokes {
// logger.Debug("AbilityInvocationsNotify: %v", entry, player.PlayerID)
player.AbilityInvokeHandler.AddEntry(entry.ForwardType, entry)
switch entry.ArgumentType {
case proto.AbilityInvokeArgument_ABILITY_META_MODIFIER_CHANGE:
modifierChange := new(proto.AbilityMetaModifierChange)
@@ -345,29 +335,50 @@ func (g *Game) AbilityInvocationsNotify(player *model.Player, payloadMsg pb.Mess
logger.Error("parse AbilityMetaModifierChange error: %v", err)
continue
}
// logger.Error("MC: %v", modifierChange)
worldAvatar := world.GetWorldAvatarByEntityId(entry.EntityId)
if worldAvatar != nil {
for _, ability := range worldAvatar.abilityList {
if ability.InstancedAbilityId == entry.Head.InstancedAbilityId {
// logger.Error("A: %v", ability)
}
}
for _, modifier := range worldAvatar.modifierList {
if modifier.InstancedAbilityId == entry.Head.InstancedAbilityId {
// logger.Error("B: %v", modifier)
}
}
for _, modifier := range worldAvatar.modifierList {
if modifier.InstancedModifierId == entry.Head.InstancedModifierId {
// logger.Error("C: %v", modifier)
}
}
// logger.Debug("entry: %v, ModifierChange: %v", entry, modifierChange)
// 处理耐力消耗
g.HandleAbilityStamina(player, entry)
case proto.AbilityInvokeArgument_ABILITY_MIXIN_COST_STAMINA:
costStamina := new(proto.AbilityMixinCostStamina)
err := pb.Unmarshal(entry.AbilityData, costStamina)
if err != nil {
logger.Error("parse AbilityMixinCostStamina error: %v", err)
continue
}
logger.Debug("entry: %v, MixinCostStamina: %v", entry, costStamina)
// 处理耐力消耗
g.HandleAbilityStamina(player, entry)
case proto.AbilityInvokeArgument_ABILITY_ACTION_DEDUCT_STAMINA:
deductStamina := new(proto.AbilityActionDeductStamina)
err := pb.Unmarshal(entry.AbilityData, deductStamina)
if err != nil {
logger.Error("parse AbilityActionDeductStamina error: %v", err)
continue
}
logger.Debug("entry: %v, ActionDeductStamina: %v", entry, deductStamina)
// 处理耐力消耗
g.HandleAbilityStamina(player, entry)
case proto.AbilityInvokeArgument_ABILITY_META_MODIFIER_DURABILITY_CHANGE:
modifierDurabilityChange := new(proto.AbilityMetaModifierDurabilityChange)
err := pb.Unmarshal(entry.AbilityData, modifierDurabilityChange)
if err != nil {
logger.Error("parse AbilityMetaModifierDurabilityChange error: %v", err)
continue
}
logger.Debug("entry: %v, DurabilityChange: %v", entry, modifierDurabilityChange)
case proto.AbilityInvokeArgument_ABILITY_META_DURABILITY_IS_ZERO:
durabilityIsZero := new(proto.AbilityMetaDurabilityIsZero)
err := pb.Unmarshal(entry.AbilityData, durabilityIsZero)
if err != nil {
logger.Error("parse AbilityMetaDurabilityIsZero error: %v", err)
continue
}
logger.Debug("entry: %v, DurabilityIsZero: %v", entry, durabilityIsZero)
case proto.AbilityInvokeArgument_ABILITY_MIXIN_ELITE_SHIELD:
case proto.AbilityInvokeArgument_ABILITY_MIXIN_ELEMENT_SHIELD:
case proto.AbilityInvokeArgument_ABILITY_MIXIN_GLOBAL_SHIELD:
case proto.AbilityInvokeArgument_ABILITY_MIXIN_SHIELD_BAR:
}
// 处理耐力消耗
g.HandleAbilityStamina(player, entry)
player.AbilityInvokeHandler.AddEntry(entry.ForwardType, entry)
}
}
@@ -394,13 +405,11 @@ func (g *Game) ClientAbilityChangeNotify(player *model.Player, payloadMsg pb.Mes
invokeHandler := model.NewInvokeHandler[proto.AbilityInvokeEntry]()
for _, entry := range req.Invokes {
// logger.Debug("ClientAbilityChangeNotify: %v", entry)
invokeHandler.AddEntry(entry.ForwardType, entry)
}
DoForward[proto.AbilityInvokeEntry](player, invokeHandler,
cmd.ClientAbilityChangeNotify, new(proto.ClientAbilityChangeNotify), "Invokes",
req, []string{"IsInitHash", "EntityId"})
world := WORLD_MANAGER.GetWorldByID(player.WorldId)
if world == nil {
return
@@ -453,7 +462,6 @@ func (g *Game) ClientAbilityChangeNotify(player *model.Player, payloadMsg pb.Mes
modifierList := worldAvatar.GetModifierList()
modifierList = append(modifierList, abilityAppliedModifier)
worldAvatar.SetModifierList(modifierList)
default:
}
}
}

View File

@@ -372,7 +372,7 @@ func (g *Game) doGachaOnce(userId uint32, gachaType uint32, mustGetUpEnable bool
return false, 0
}
// 找到卡池对应的掉落组
dropGroupDataConfig := gdconf.CONF.GachaDropGroupDataMap[int32(gachaType)]
dropGroupDataConfig := gdconf.GetGachaDropGroupDataByDropId(int32(gachaType))
if dropGroupDataConfig == nil {
logger.Error("drop group not found, drop id: %v", gachaType)
return false, 0
@@ -511,7 +511,7 @@ func (g *Game) doGachaOnce(userId uint32, gachaType uint32, mustGetUpEnable bool
// 替换本次结果为5星大保底
if gachaPoolInfo.MustGetUpOrange {
logger.Debug("trigger must get up orange, uid: %v", userId)
upOrangeDropGroupDataConfig := gdconf.CONF.GachaDropGroupDataMap[upOrangeDropId]
upOrangeDropGroupDataConfig := gdconf.GetGachaDropGroupDataByDropId(upOrangeDropId)
if upOrangeDropGroupDataConfig == nil {
logger.Error("drop group not found, drop id: %v", upOrangeDropId)
return false, 0
@@ -538,7 +538,7 @@ func (g *Game) doGachaOnce(userId uint32, gachaType uint32, mustGetUpEnable bool
// 替换本次结果为4星大保底
if gachaPoolInfo.MustGetUpPurple {
logger.Debug("trigger must get up purple, uid: %v", userId)
upPurpleDropGroupDataConfig := gdconf.CONF.GachaDropGroupDataMap[upPurpleDropId]
upPurpleDropGroupDataConfig := gdconf.GetGachaDropGroupDataByDropId(upPurpleDropId)
if upPurpleDropGroupDataConfig == nil {
logger.Error("drop group not found, drop id: %v", upPurpleDropId)
return false, 0
@@ -574,7 +574,7 @@ func (g *Game) doGachaRandDropFull(gachaDropGroupDataConfig *gdconf.GachaDropGro
return true, drop
}
// 进行下一步掉落流程
gachaDropGroupDataConfig = gdconf.CONF.GachaDropGroupDataMap[drop.Result]
gachaDropGroupDataConfig = gdconf.GetGachaDropGroupDataByDropId(drop.Result)
if gachaDropGroupDataConfig == nil {
logger.Error("drop config error, drop id: %v", drop.Result)
return false, nil

View File

@@ -19,37 +19,60 @@ func (g *Game) PlayerLoginReq(userId uint32, clientSeq uint32, gateAppId string,
logger.Info("user login req, uid: %v, gateAppId: %v", userId, gateAppId)
req := payloadMsg.(*proto.PlayerLoginReq)
logger.Debug("login data: %v", req)
g.OnLogin(userId, clientSeq, gateAppId, false, nil)
USER_MANAGER.OnlineUser(userId, clientSeq, gateAppId, req.TargetUid)
}
func (g *Game) SetPlayerBornDataReq(userId uint32, clientSeq uint32, gateAppId string, payloadMsg pb.Message) {
logger.Info("user reg req, uid: %v, gateAppId: %v", userId, gateAppId)
func (g *Game) SetPlayerBornDataReq(player *model.Player, payloadMsg pb.Message) {
req := payloadMsg.(*proto.SetPlayerBornDataReq)
logger.Debug("reg data: %v", req)
if userId < PlayerBaseUid {
logger.Error("uid can not less than player base uid, reg req uid: %v", userId)
logger.Debug("avatar id: %v, nickname: %v", req.AvatarId, req.NickName)
if player.IsBorn {
logger.Error("player is already born, uid: %v", player.PlayerID)
return
}
g.OnReg(userId, clientSeq, gateAppId, req)
}
func (g *Game) OnLogin(userId uint32, clientSeq uint32, gateAppId string, isReg bool, regPlayer *model.Player) {
logger.Info("user login, uid: %v", userId)
if isReg {
g.OnLoginOk(userId, clientSeq, gateAppId, true, regPlayer)
mainCharAvatarId := req.AvatarId
if mainCharAvatarId != 10000005 && mainCharAvatarId != 10000007 {
logger.Error("invalid main char avatar id: %v", mainCharAvatarId)
return
}
player, isRobot := USER_MANAGER.OnlineUser(userId, clientSeq, gateAppId)
if isRobot {
g.OnLoginOk(userId, clientSeq, gateAppId, false, player)
player.NickName = req.NickName
player.HeadImage = mainCharAvatarId
dbAvatar := player.GetDbAvatar()
dbAvatar.MainCharAvatarId = mainCharAvatarId
// 添加选定的主角
dbAvatar.AddAvatar(player, dbAvatar.MainCharAvatarId)
// 添加主角初始武器
avatarDataConfig := gdconf.GetAvatarDataById(int32(dbAvatar.MainCharAvatarId))
if avatarDataConfig == nil {
logger.Error("get avatar data config is nil, avatarId: %v", dbAvatar.MainCharAvatarId)
return
}
weaponId := uint64(g.snowflake.GenId())
dbWeapon := player.GetDbWeapon()
dbWeapon.AddWeapon(player, uint32(avatarDataConfig.InitialWeapon), weaponId)
weapon := dbWeapon.WeaponMap[weaponId]
dbAvatar.WearWeapon(dbAvatar.MainCharAvatarId, weapon)
dbTeam := player.GetDbTeam()
dbTeam.GetActiveTeam().SetAvatarIdList([]uint32{dbAvatar.MainCharAvatarId})
g.AcceptQuest(player, false)
g.SendMsg(cmd.SetPlayerBornDataRsp, player.PlayerID, player.ClientSeq, new(proto.SetPlayerBornDataRsp))
g.LoginNotify(player.PlayerID, player.ClientSeq, player)
g.SendMsg(cmd.PlayerEnterSceneNotify, player.PlayerID, player.ClientSeq, g.PacketPlayerEnterSceneNotifyLogin(player, proto.EnterType_ENTER_SELF))
}
func (g *Game) OnLoginOk(userId uint32, clientSeq uint32, gateAppId string, isReg bool, player *model.Player) {
func (g *Game) OnLogin(userId uint32, clientSeq uint32, gateAppId string, player *model.Player, joinHostUserId uint32) {
if player == nil {
g.SendMsgToGate(cmd.DoSetPlayerBornDataNotify, userId, clientSeq, gateAppId, new(proto.DoSetPlayerBornDataNotify))
return
player := g.CreatePlayer(userId)
USER_MANAGER.ChangeUserDbState(player, model.DbInsert)
}
USER_MANAGER.AddUser(player)
SELF = player
player.OnlineTime = uint32(time.Now().UnixMilli())
@@ -67,27 +90,6 @@ func (g *Game) OnLoginOk(userId uint32, clientSeq uint32, gateAppId string, isRe
dbItem := player.GetDbItem()
dbItem.InitAllItem(player)
if isReg {
// 添加选定的主角
dbAvatar.AddAvatar(player, dbAvatar.MainCharAvatarId)
// 添加主角初始武器
avatarDataConfig := gdconf.GetAvatarDataById(int32(dbAvatar.MainCharAvatarId))
if avatarDataConfig == nil {
logger.Error("get avatar data config is nil, avatarId: %v", dbAvatar.MainCharAvatarId)
return
}
weaponId := uint64(g.snowflake.GenId())
dbWeapon := player.GetDbWeapon()
dbWeapon.AddWeapon(player, uint32(avatarDataConfig.InitialWeapon), weaponId)
weapon := dbWeapon.WeaponMap[weaponId]
dbAvatar.WearWeapon(dbAvatar.MainCharAvatarId, weapon)
dbTeam := player.GetDbTeam()
dbTeam.GetActiveTeam().SetAvatarIdList([]uint32{dbAvatar.MainCharAvatarId})
g.AcceptQuest(player, false)
}
// 确保玩家位置安全
player.Pos.X = player.SafePos.X
player.Pos.Y = player.SafePos.Y
@@ -98,12 +100,55 @@ func (g *Game) OnLoginOk(userId uint32, clientSeq uint32, gateAppId string, isRe
player.Rot = &model.Vector{X: 0, Y: 307, Z: 0}
}
TICK_MANAGER.CreateUserGlobalTick(userId)
TICK_MANAGER.CreateUserTimer(userId, UserTimerActionTest, 100, player.NickName)
if player.IsBorn {
g.LoginNotify(userId, clientSeq, player)
}
if joinHostUserId != 0 {
hostPlayer := USER_MANAGER.GetOnlineUser(joinHostUserId)
if hostPlayer == nil {
logger.Error("player is nil, uid: %v", joinHostUserId)
} else {
g.JoinOtherWorld(player, hostPlayer)
}
} else {
// 创建世界
world := WORLD_MANAGER.CreateWorld(player)
world.AddPlayer(player, player.SceneId)
player.WorldId = world.GetId()
// 进入场景
player.SceneJump = true
player.SceneLoadState = model.SceneNone
if player.IsBorn {
g.SendMsg(cmd.PlayerEnterSceneNotify, userId, clientSeq, g.PacketPlayerEnterSceneNotifyLogin(player, proto.EnterType_ENTER_SELF))
}
}
if !player.IsBorn {
g.SendMsg(cmd.DoSetPlayerBornDataNotify, userId, clientSeq, new(proto.DoSetPlayerBornDataNotify))
}
playerLoginRsp := &proto.PlayerLoginRsp{
IsUseAbilityHash: true,
AbilityHashCode: 0,
IsEnableClientHashDebug: true,
IsScOpen: false,
ScInfo: []byte{},
TotalTickTime: 0.0,
GameBiz: "hk4e_global",
RegisterCps: "mihoyo",
CountryCode: "US",
Birthday: "2000-01-01",
}
g.SendMsg(cmd.PlayerLoginRsp, userId, clientSeq, playerLoginRsp)
if userId < PlayerBaseUid {
return
}
g.LoginNotify(userId, player, clientSeq)
MESSAGE_QUEUE.SendToAll(&mq.NetMsg{
MsgType: mq.MsgTypeServer,
EventId: mq.ServerUserOnlineStateChangeNotify,
@@ -112,53 +157,17 @@ func (g *Game) OnLoginOk(userId uint32, clientSeq uint32, gateAppId string, isRe
IsOnline: true,
},
})
TICK_MANAGER.CreateUserGlobalTick(userId)
TICK_MANAGER.CreateUserTimer(userId, UserTimerActionTest, 100, player.NickName)
atomic.AddInt32(&ONLINE_PLAYER_NUM, 1)
SELF = nil
}
func (g *Game) OnReg(userId uint32, clientSeq uint32, gateAppId string, payloadMsg pb.Message) {
logger.Debug("user reg, uid: %v", userId)
req := payloadMsg.(*proto.SetPlayerBornDataReq)
logger.Debug("avatar id: %v, nickname: %v", req.AvatarId, req.NickName)
exist, asyncWait := USER_MANAGER.CheckUserExistOnReg(userId, req, clientSeq, gateAppId)
if !asyncWait {
g.OnRegOk(exist, req, userId, clientSeq, gateAppId)
}
}
func (g *Game) OnRegOk(exist bool, req *proto.SetPlayerBornDataReq, userId uint32, clientSeq uint32, gateAppId string) {
if exist {
logger.Error("recv reg req, but user is already exist, uid: %v", userId)
return
}
nickName := req.NickName
mainCharAvatarId := req.GetAvatarId()
if mainCharAvatarId != 10000005 && mainCharAvatarId != 10000007 {
logger.Error("invalid main char avatar id: %v", mainCharAvatarId)
return
}
player := g.CreatePlayer(userId, nickName, mainCharAvatarId)
if player == nil {
logger.Error("player is nil, uid: %v", userId)
return
}
USER_MANAGER.ChangeUserDbState(player, model.DbInsert)
USER_MANAGER.AddUser(player)
g.SendMsgToGate(cmd.SetPlayerBornDataRsp, userId, clientSeq, gateAppId, new(proto.SetPlayerBornDataRsp))
g.OnLogin(userId, clientSeq, gateAppId, true, player)
}
func (g *Game) CreatePlayer(userId uint32, nickName string, mainCharAvatarId uint32) *model.Player {
func (g *Game) CreatePlayer(userId uint32) *model.Player {
player := new(model.Player)
player.PlayerID = userId
player.NickName = nickName
player.NickName = ""
player.Signature = ""
player.HeadImage = mainCharAvatarId
player.HeadImage = 0
player.Birthday = []uint8{0, 0}
player.NameCard = 210001
player.NameCardList = make([]uint32, 0)
@@ -185,10 +194,13 @@ func (g *Game) CreatePlayer(userId uint32, nickName string, mainCharAvatarId uin
player.PropertiesMap[constant.PLAYER_PROP_PLAYER_MP_SETTING_TYPE] = 2
player.PropertiesMap[constant.PLAYER_PROP_IS_MP_MODE_AVAILABLE] = 1
player.SafePos = new(model.Vector)
player.Pos = new(model.Vector)
player.Rot = new(model.Vector)
sceneLuaConfig := gdconf.GetSceneLuaConfigById(int32(player.SceneId))
if sceneLuaConfig == nil {
logger.Error("get scene lua config is nil, sceneId: %v, uid: %v", player.SceneId, player.PlayerID)
return nil
return player
}
player.SafePos = &model.Vector{
X: float64(sceneLuaConfig.SceneConfig.BornPos.X),
@@ -206,9 +218,6 @@ func (g *Game) CreatePlayer(userId uint32, nickName string, mainCharAvatarId uin
Z: float64(sceneLuaConfig.SceneConfig.BornRot.Z),
}
dbAvatar := player.GetDbAvatar()
dbAvatar.MainCharAvatarId = mainCharAvatarId
return player
}
@@ -231,7 +240,7 @@ func (g *Game) OnUserOffline(userId uint32, changeGsInfo *ChangeGsInfo) {
atomic.AddInt32(&ONLINE_PLAYER_NUM, -1)
}
func (g *Game) LoginNotify(userId uint32, player *model.Player, clientSeq uint32) {
func (g *Game) LoginNotify(userId uint32, clientSeq uint32, player *model.Player) {
g.SendMsg(cmd.PlayerDataNotify, userId, clientSeq, g.PacketPlayerDataNotify(player))
g.SendMsg(cmd.StoreWeightLimitNotify, userId, clientSeq, g.PacketStoreWeightLimitNotify())
g.SendMsg(cmd.PlayerStoreNotify, userId, clientSeq, g.PacketPlayerStoreNotify(player))
@@ -240,25 +249,6 @@ func (g *Game) LoginNotify(userId uint32, player *model.Player, clientSeq uint32
g.SendMsg(cmd.QuestListNotify, userId, clientSeq, g.PacketQuestListNotify(player))
g.SendMsg(cmd.FinishedParentQuestNotify, userId, clientSeq, g.PacketFinishedParentQuestNotify(player))
// g.GCGLogin(player) // 发送GCG登录相关的通知包
playerLoginRsp := &proto.PlayerLoginRsp{
IsUseAbilityHash: true,
AbilityHashCode: 0,
IsEnableClientHashDebug: true,
IsScOpen: false,
ScInfo: []byte{},
TotalTickTime: 0.0,
GameBiz: "hk4e_global",
RegisterCps: "mihoyo",
CountryCode: "US",
Birthday: "2000-01-01",
}
g.SendMsg(cmd.PlayerLoginRsp, userId, clientSeq, playerLoginRsp)
playerTimeNotify := &proto.PlayerTimeNotify{
IsPaused: player.Pause,
PlayerTime: uint64(player.TotalOnlineTime),
ServerTime: uint64(time.Now().UnixMilli()),
}
g.SendMsg(cmd.PlayerTimeNotify, player.PlayerID, 0, playerTimeNotify)
}
func (g *Game) PacketPlayerDataNotify(player *model.Player) *proto.PlayerDataNotify {

View File

@@ -1,13 +1,11 @@
package game
import (
"strings"
"time"
"hk4e/common/constant"
"hk4e/gdconf"
"hk4e/gs/model"
"hk4e/pkg/endec"
"hk4e/pkg/logger"
"hk4e/protocol/cmd"
"hk4e/protocol/proto"
@@ -15,68 +13,6 @@ import (
pb "google.golang.org/protobuf/proto"
)
// HandleAbilityStamina 处理来自ability的耐力消耗
func (g *Game) HandleAbilityStamina(player *model.Player, entry *proto.AbilityInvokeEntry) {
switch entry.ArgumentType {
case proto.AbilityInvokeArgument_ABILITY_MIXIN_COST_STAMINA:
// 大剑重击 或 持续技能 耐力消耗
costStamina := new(proto.AbilityMixinCostStamina)
err := pb.Unmarshal(entry.AbilityData, costStamina)
if err != nil {
logger.Error("parse AbilityMixinCostStamina error: %v", err)
return
}
// 处理持续耐力消耗
g.SkillSustainStamina(player, costStamina.IsSwim)
case proto.AbilityInvokeArgument_ABILITY_META_MODIFIER_CHANGE:
// 普通角色重击耐力消耗
world := WORLD_MANAGER.GetWorldByID(player.WorldId)
// 获取世界中的角色实体
worldAvatar := world.GetWorldAvatarByEntityId(entry.EntityId)
if worldAvatar == nil {
return
}
// 查找是不是属于该角色实体的ability id
abilityNameHashCode := uint32(0)
for _, ability := range worldAvatar.GetAbilityList() {
if ability.InstancedAbilityId == entry.Head.InstancedAbilityId {
// logger.Error("%v", ability)
abilityNameHashCode = ability.AbilityName.GetHash()
}
}
if abilityNameHashCode == 0 {
return
}
// 根据ability name查找到对应的技能表里的技能配置
var avatarAbility *gdconf.AvatarSkillData = nil
for _, avatarSkillData := range gdconf.GetAvatarSkillDataMap() {
hashCode := endec.Hk4eAbilityHashCode(avatarSkillData.AbilityName)
if uint32(hashCode) == abilityNameHashCode {
avatarAbility = avatarSkillData
break
}
}
if avatarAbility == nil {
return
}
// 距离技能开始过去的时间
startPastTime := time.Now().UnixMilli() - player.StaminaInfo.LastSkillTime
// 距离上次技能消耗的时间
changePastTime := time.Now().UnixMilli() - player.StaminaInfo.LastSkillChargeTime
// 法器角色轻击也会算触发重击消耗 胡桃等角色重击一次会多次消耗
// 所以通过策略判断 必须距离技能开始过去200ms才算重击 两次技能耐力消耗之间需间隔500ms
// 暂时就这样实现重击消耗 以后应该还会有更好的办法~
if player.StaminaInfo.LastSkillId == uint32(avatarAbility.AvatarSkillId) && startPastTime > 200 && changePastTime > 500 {
// 重击对应的耐力消耗
g.ChargedAttackStamina(player, worldAvatar, avatarAbility)
player.StaminaInfo.LastSkillChargeTime = time.Now().UnixMilli()
}
default:
break
}
}
// SceneAvatarStaminaStepReq 缓慢游泳或缓慢攀爬时消耗耐力
func (g *Game) SceneAvatarStaminaStepReq(player *model.Player, payloadMsg pb.Message) {
req := payloadMsg.(*proto.SceneAvatarStaminaStepReq)
@@ -115,13 +51,62 @@ func (g *Game) SceneAvatarStaminaStepReq(player *model.Player, payloadMsg pb.Mes
g.UpdatePlayerStamina(player, constant.STAMINA_COST_SWIMMING)
}
// PacketSceneAvatarStaminaStepRsp
sceneAvatarStaminaStepRsp := new(proto.SceneAvatarStaminaStepRsp)
sceneAvatarStaminaStepRsp.UseClientRot = true
sceneAvatarStaminaStepRsp.Rot = req.Rot
g.SendMsg(cmd.SceneAvatarStaminaStepRsp, player.PlayerID, player.ClientSeq, sceneAvatarStaminaStepRsp)
}
// HandleAbilityStamina 处理来自ability的耐力消耗
func (g *Game) HandleAbilityStamina(player *model.Player, entry *proto.AbilityInvokeEntry) {
world := WORLD_MANAGER.GetWorldByID(player.WorldId)
// 获取世界中的角色实体
worldAvatar := world.GetWorldAvatarByEntityId(entry.EntityId)
if worldAvatar == nil {
return
}
// 查找是不是属于该角色实体的ability id
ability := worldAvatar.GetAbilityByInstanceId(entry.Head.InstancedAbilityId)
abilityNameHashCode := ability.AbilityName.GetHash()
if abilityNameHashCode == 0 {
return
}
// 根据ability name查找到对应的技能表里的技能配置
staminaDataConfig := gdconf.GetSkillStaminaDataByAbilityHashCode(int32(abilityNameHashCode))
if staminaDataConfig == nil {
return
}
staminaInfo := player.StaminaInfo
switch entry.ArgumentType {
case proto.AbilityInvokeArgument_ABILITY_META_MODIFIER_CHANGE:
// 普通角色重击耐力消耗
// 距离技能开始过去的时间
startPastTime := time.Now().UnixMilli() - staminaInfo.LastSkillTime
// 距离上次技能消耗的时间
changePastTime := time.Now().UnixMilli() - staminaInfo.LastSkillChargeTime
// 法器角色轻击也会算触发重击消耗 胡桃等角色重击一次会多次消耗
// 所以通过策略判断 必须距离技能开始过去200ms才算重击 两次技能耐力消耗之间需间隔500ms
// 暂时就这样实现重击消耗 以后应该还会有更好的办法~
if startPastTime > 200 && changePastTime > 500 {
costStamina := -(staminaDataConfig.CostStamina * 100)
logger.Debug("stamina cost, skillId: %v, cost: %v", staminaDataConfig.AvatarSkillId, costStamina)
g.UpdatePlayerStamina(player, costStamina)
staminaInfo.LastSkillChargeTime = time.Now().UnixMilli()
}
case proto.AbilityInvokeArgument_ABILITY_MIXIN_COST_STAMINA:
// 大剑重击 或 持续技能 耐力消耗
// 根据配置以及距离上次的时间计算消耗的耐力
pastTime := time.Now().UnixMilli() - staminaInfo.LastSkillTime
costStamina := -(staminaDataConfig.CostStamina * 100)
costStamina = int32(float64(pastTime) / 1000 * float64(costStamina))
logger.Debug("stamina cost, skillId: %v, cost: %v", staminaDataConfig.AvatarSkillId, costStamina)
g.UpdatePlayerStamina(player, costStamina)
// 记录最后释放技能的时间
staminaInfo.LastSkillTime = time.Now().UnixMilli()
case proto.AbilityInvokeArgument_ABILITY_ACTION_DEDUCT_STAMINA:
}
}
// ImmediateStamina 处理即时耐力消耗
func (g *Game) ImmediateStamina(player *model.Player, motionState proto.MotionState) {
// 玩家暂停状态不更新耐力
@@ -130,18 +115,14 @@ func (g *Game) ImmediateStamina(player *model.Player, motionState proto.MotionSt
}
staminaInfo := player.StaminaInfo
// logger.Debug("stamina handle, uid: %v, motionState: %v", player.PlayerID, motionState)
// 设置用于持续消耗或恢复耐力的值
staminaInfo.SetStaminaCost(motionState)
// 未改变状态不执行后面 有些仅在动作开始消耗耐力
if motionState == staminaInfo.State {
return
}
// 记录玩家的动作状态
staminaInfo.State = motionState
// 根据玩家的状态立刻消耗耐力
switch motionState {
case proto.MotionState_MOTION_CLIMB:
@@ -159,120 +140,10 @@ func (g *Game) ImmediateStamina(player *model.Player, motionState proto.MotionSt
}
}
// SkillSustainStamina 处理技能持续时的耐力消耗
func (g *Game) SkillSustainStamina(player *model.Player, isSwim bool) {
staminaInfo := player.StaminaInfo
skillId := staminaInfo.LastSkillId
// 读取技能配置表
avatarSkillConfig := gdconf.GetAvatarSkillDataById(int32(skillId))
if avatarSkillConfig == nil {
logger.Error("avatarSkillConfig error, skillId: %v", skillId)
return
}
// 获取释放技能者的角色Id
world := WORLD_MANAGER.GetWorldByID(player.WorldId)
// 获取世界中的角色实体
worldAvatar := world.GetWorldAvatarByEntityId(staminaInfo.LastCasterId)
if worldAvatar == nil {
return
}
// 获取现行角色的配置表
avatarDataConfig := gdconf.GetAvatarDataById(int32(worldAvatar.GetAvatarId()))
if avatarDataConfig == nil {
logger.Error("avatarDataConfig error, avatarId: %v", worldAvatar.GetAvatarId())
return
}
// 需要消耗的耐力值
var costStamina int32
// 如果为0代表使用默认值
if avatarSkillConfig.CostStamina == 0 {
// 大剑持续耐力消耗默认值
if avatarDataConfig.WeaponType == constant.WEAPON_TYPE_CLAYMORE {
costStamina = constant.STAMINA_COST_FIGHT_CLAYMORE_PER
}
} else {
costStamina = -(avatarSkillConfig.CostStamina * 100)
}
// 距离上次执行过去的时间
pastTime := time.Now().UnixMilli() - staminaInfo.LastSkillTime
// 根据配置以及距离上次的时间计算消耗的耐力
costStamina = int32(float64(pastTime) / 1000 * float64(costStamina))
logger.Debug("stamina skill sustain, skillId: %v, cost: %v, isSwim: %v", skillId, costStamina, isSwim)
// 根据配置以及距离上次的时间计算消耗的耐力
g.UpdatePlayerStamina(player, costStamina)
// 记录最后释放技能的时间
player.StaminaInfo.LastSkillTime = time.Now().UnixMilli()
}
// ChargedAttackStamina 处理重击技能即时耐力消耗
func (g *Game) ChargedAttackStamina(player *model.Player, worldAvatar *WorldAvatar, skillData *gdconf.AvatarSkillData) {
// 确保技能为重击
if !strings.Contains(skillData.AbilityName, "ExtraAttack") {
return
}
// 获取现行角色的配置表
avatarDataConfig := gdconf.GetAvatarDataById(int32(worldAvatar.GetAvatarId()))
if avatarDataConfig == nil {
logger.Error("avatarDataConfig error, avatarId: %v", worldAvatar.GetAvatarId())
return
}
// 需要消耗的耐力值
var costStamina int32
// 如果为0代表使用默认值
if skillData.CostStamina == 0 {
// 使用武器对应默认耐力消耗
// 双手剑为持续耐力消耗不在这里处理
switch avatarDataConfig.WeaponType {
case constant.WEAPON_TYPE_SWORD_ONE_HAND:
// 单手剑
costStamina = constant.STAMINA_COST_FIGHT_SWORD_ONE_HAND
case constant.WEAPON_TYPE_POLE:
// 长枪
costStamina = constant.STAMINA_COST_FIGHT_POLE
case constant.WEAPON_TYPE_CATALYST:
// 法器
costStamina = constant.STAMINA_COST_FIGHT_CATALYST
}
} else {
costStamina = -(skillData.CostStamina * 100)
}
logger.Debug("charged attack stamina, skillId: %v, cost: %v", skillData.AvatarSkillId, costStamina)
// 根据配置消耗耐力
g.UpdatePlayerStamina(player, costStamina)
}
// SkillStartStamina 处理技能开始时的即时耐力消耗
func (g *Game) SkillStartStamina(player *model.Player, casterId uint32, skillId uint32) {
staminaInfo := player.StaminaInfo
// 获取该技能开始时所需消耗的耐力
avatarSkillDataConfig := gdconf.GetAvatarSkillDataById(int32(skillId))
// 配置表确保存在技能开始对应的耐力消耗
if avatarSkillDataConfig != nil && avatarSkillDataConfig.CostStamina != 0 {
// 距离上次处理技能开始耐力消耗过去的时间
pastTime := time.Now().UnixMilli() - staminaInfo.LastSkillStartTime
// 上次触发的技能相同则每400ms触发一次消耗
if staminaInfo.LastSkillId != skillId || pastTime > 400 {
logger.Debug("skill start stamina, skillId: %v, cost: %v", skillId, avatarSkillDataConfig.CostStamina)
// 根据配置消耗耐力
g.UpdatePlayerStamina(player, avatarSkillDataConfig.CostStamina)
staminaInfo.LastSkillStartTime = time.Now().UnixMilli()
}
}
// 记录最后释放的技能
staminaInfo.LastCasterId = casterId
staminaInfo.LastSkillId = skillId
staminaInfo.LastSkillTime = time.Now().UnixMilli()
}
@@ -341,7 +212,6 @@ func (g *Game) SustainStaminaHandler(player *model.Player) {
func (g *Game) GetChangeStamina(curStamina int32, maxStamina int32, staminaCost int32) uint32 {
// 即将更改为的耐力值
stamina := curStamina + staminaCost
// 确保耐力值不超出范围
if stamina > maxStamina {
stamina = maxStamina
@@ -371,12 +241,10 @@ func (g *Game) UpdateVehicleStamina(player *model.Player, vehicleEntity *Entity,
// logger.Debug("stamina delay reset, restoreDelay: %v", player.StaminaInfo.VehicleRestoreDelay)
staminaInfo.VehicleRestoreDelay = 0
}
// 确保载具实体存在
if vehicleEntity == nil {
return
}
// 因为载具的耐力需要换算
// 这里先*100后面要用的时候再换算 为了确保精度
// 最大耐力值
@@ -384,15 +252,12 @@ func (g *Game) UpdateVehicleStamina(player *model.Player, vehicleEntity *Entity,
maxStamina := int32(gadgetEntity.GetGadgetVehicleEntity().GetMaxStamina() * 100)
// 现行耐力值
curStamina := int32(gadgetEntity.GetGadgetVehicleEntity().GetCurStamina() * 100)
// 将被变更的耐力
stamina := g.GetChangeStamina(curStamina, maxStamina, staminaCost)
// 当前无变动不要频繁发包
if uint32(curStamina) == stamina {
return
}
// 更改载具耐力 (换算)
g.SetVehicleStamina(player, vehicleEntity, float32(stamina)/100)
}
@@ -417,23 +282,18 @@ func (g *Game) UpdatePlayerStamina(player *model.Player, staminaCost int32) {
// logger.Debug("stamina delay reset, restoreDelay: %v", player.StaminaInfo.RestoreDelay)
staminaInfo.PlayerRestoreDelay = 0
}
// 最大耐力值
maxStamina := int32(player.PropertiesMap[constant.PLAYER_PROP_MAX_STAMINA])
// 现行耐力值
curStamina := int32(player.PropertiesMap[constant.PLAYER_PROP_CUR_PERSIST_STAMINA])
// 将被变更的耐力
stamina := g.GetChangeStamina(curStamina, maxStamina, staminaCost)
// 检测玩家是否没耐力后执行溺水
g.HandleDrown(player, stamina)
// 当前无变动不要频繁发包
if uint32(curStamina) == stamina {
return
}
// 更改玩家的耐力
g.SetPlayerStamina(player, stamina)
}
@@ -488,7 +348,6 @@ func (g *Game) HandleDrown(player *model.Player, stamina uint32) {
if stamina != 0 || player.StaminaInfo.DrownBackDelay != 0 {
return
}
// 确保玩家正在游泳
if player.StaminaInfo.State == proto.MotionState_MOTION_SWIM_MOVE || player.StaminaInfo.State == proto.MotionState_MOTION_SWIM_DASH {
logger.Debug("player drown, curStamina: %v, state: %v", stamina, player.StaminaInfo.State)
@@ -506,7 +365,6 @@ func (g *Game) SetVehicleStamina(player *model.Player, vehicleEntity *Entity, st
gadgetEntity.GetGadgetVehicleEntity().SetCurStamina(stamina)
// logger.Debug("vehicle stamina set, stamina: %v", stamina)
// PacketVehicleStaminaNotify
vehicleStaminaNotify := new(proto.VehicleStaminaNotify)
vehicleStaminaNotify.EntityId = vehicleEntity.GetId()
vehicleStaminaNotify.CurStamina = stamina
@@ -519,13 +377,10 @@ func (g *Game) SetPlayerStamina(player *model.Player, stamina uint32) {
prop := constant.PLAYER_PROP_CUR_PERSIST_STAMINA
player.PropertiesMap[prop] = stamina
// logger.Debug("player stamina set, stamina: %v", stamina)
// PacketPlayerPropNotify
g.PlayerPropNotify(player, prop)
}
func (g *Game) PlayerPropNotify(player *model.Player, playerPropId uint16) {
// PacketPlayerPropNotify
playerPropNotify := new(proto.PlayerPropNotify)
playerPropNotify.PropMap = make(map[uint32]*proto.PropValue)
playerPropNotify.PropMap[uint32(playerPropId)] = &proto.PropValue{

View File

@@ -27,6 +27,7 @@ type Player struct {
// 离线数据 请尽量不要定义接口等复杂数据结构
ID primitive.ObjectID `bson:"_id,omitempty"`
PlayerID uint32 `bson:"PlayerID"` // 玩家uid
IsBorn bool // 是否完成开场动画
NickName string // 昵称
Signature string // 签名
HeadImage uint32 // 头像

View File

@@ -10,10 +10,7 @@ type StaminaInfo struct {
CostStamina int32 // 消耗或恢复的耐力
PlayerRestoreDelay uint8 // 玩家耐力回复延时
VehicleRestoreDelay uint8 // 载具耐力回复延时
LastCasterId uint32 // 最后释放技能者的Id
LastSkillId uint32 // 最后释放的技能Id
LastSkillTime int64 // 最后释放技能的时间
LastSkillStartTime int64 // 最后执行开始技能耐力消耗的时间
LastSkillChargeTime int64 // 最后执行技能耐力消耗的时间
DrownBackDelay uint8 // 溺水返回安全点延时
}