mirror of
https://github.com/FlourishingWorld/hk4e.git
synced 2026-02-04 14:22:26 +08:00
优化注册与登录流程
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 世界里所有玩家的网络延迟广播
|
||||
|
||||
@@ -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:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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{
|
||||
|
||||
@@ -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 // 头像
|
||||
|
||||
@@ -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 // 溺水返回安全点延时
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user