优化注册与登录流程

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

@@ -22,9 +22,4 @@ const (
STAMINA_COST_POWERED_SKIFF int32 = 500 // 浪船加速(风圈等) (回复耐力)
STAMINA_COST_IN_SKIFF int32 = 500 // 处于浪船中回复角色耐力 (回复耐力)
STAMINA_COST_SKIFF_NOBODY int32 = 500 // 浪船无人时回复载具耐力 (回复耐力)
// 武器消耗默认值
STAMINA_COST_FIGHT_SWORD_ONE_HAND int32 = -2000 // 单手剑
STAMINA_COST_FIGHT_POLE int32 = -2500 // 长枪
STAMINA_COST_FIGHT_CATALYST int32 = -5000 // 法器
STAMINA_COST_FIGHT_CLAYMORE_PER int32 = -4000 // 双手剑 (每秒消耗)
)

View File

@@ -52,8 +52,7 @@ type KcpConnectManager struct {
serverCmdProtoMap *cmd.CmdProtoMap
clientCmdProtoMap *client_proto.ClientCmdProtoMap
// 输入输出管道
messageQueue *mq.MessageQueue
localMsgOutput chan *ProtoMsg
messageQueue *mq.MessageQueue
// 密钥
dispatchKey []byte
signRsaKey []byte
@@ -77,7 +76,6 @@ func NewKcpConnectManager(messageQueue *mq.MessageQueue, discovery *rpc.Discover
r.clientCmdProtoMap = client_proto.NewClientCmdProtoMap()
}
r.messageQueue = messageQueue
r.localMsgOutput = make(chan *ProtoMsg, 1000)
r.run()
return r
}
@@ -166,8 +164,6 @@ func (k *KcpConnectManager) acceptHandle(listener *kcp.Listener) {
gsServerAppId: "",
anticheatServerAppId: "",
pathfindingServerAppId: "",
changeGameServer: false,
joinHostUserId: 0,
useMagicSeed: false,
}
go k.recvHandle(session)
@@ -282,8 +278,6 @@ type Session struct {
gsServerAppId string
anticheatServerAppId string
pathfindingServerAppId string
changeGameServer bool
joinHostUserId uint32
useMagicSeed bool
}

View File

@@ -29,6 +29,7 @@ import (
const (
ConnEst = iota
ConnWaitLogin
ConnActive
ConnClose
)
@@ -47,6 +48,7 @@ func (k *KcpConnectManager) recvMsgHandle(protoMsg *ProtoMsg, session *Session)
if connState != ConnEst {
return
}
session.connState = ConnWaitLogin
getPlayerTokenReq := protoMsg.PayloadMessage.(*proto.GetPlayerTokenReq)
getPlayerTokenRsp := k.getPlayerToken(getPlayerTokenReq, session)
if getPlayerTokenRsp == nil {
@@ -58,7 +60,7 @@ func (k *KcpConnectManager) recvMsgHandle(protoMsg *ProtoMsg, session *Session)
rsp.CmdId = cmd.GetPlayerTokenRsp
rsp.HeadMessage = k.getHeadMsg(protoMsg.HeadMessage.ClientSequenceId)
rsp.PayloadMessage = getPlayerTokenRsp
k.localMsgOutput <- rsp
session.kcpRawSendChan <- rsp
case cmd.PlayerForceExitReq:
// 玩家退出游戏请求
if connState != ConnActive {
@@ -81,7 +83,7 @@ func (k *KcpConnectManager) recvMsgHandle(protoMsg *ProtoMsg, session *Session)
rsp.CmdId = cmd.PingRsp
rsp.HeadMessage = k.getHeadMsg(protoMsg.HeadMessage.ClientSequenceId)
rsp.PayloadMessage = pingRsp
k.localMsgOutput <- rsp
session.kcpRawSendChan <- rsp
logger.Debug("convId: %v, RTO: %v, SRTT: %v, RTTVar: %v",
protoMsg.ConvId, session.conn.GetRTO(), session.conn.GetSRTT(), session.conn.GetSRTTVar())
if connState != ConnActive {
@@ -106,8 +108,26 @@ func (k *KcpConnectManager) recvMsgHandle(protoMsg *ProtoMsg, session *Session)
EventId: mq.ClientTimeNotify,
ConnCtrlMsg: connCtrlMsg,
})
case cmd.PlayerLoginReq:
if connState != ConnWaitLogin {
return
}
playerLoginReq := protoMsg.PayloadMessage.(*proto.PlayerLoginReq)
playerLoginReq.TargetUid = 0
playerLoginReq.TargetHomeOwnerUid = 0
gameMsg := new(mq.GameMsg)
gameMsg.UserId = userId
gameMsg.CmdId = protoMsg.CmdId
gameMsg.ClientSeq = protoMsg.HeadMessage.ClientSequenceId
gameMsg.PayloadMessage = playerLoginReq
// 转发到GS
k.messageQueue.SendToGs(session.gsServerAppId, &mq.NetMsg{
MsgType: mq.MsgTypeGame,
EventId: mq.NormalMsg,
GameMsg: gameMsg,
})
default:
if connState != ConnActive && !(protoMsg.CmdId == cmd.PlayerLoginReq || protoMsg.CmdId == cmd.SetPlayerBornDataReq) {
if connState != ConnActive {
logger.Error("conn not active so drop packet, cmdId: %v, userId: %v, convId: %v", protoMsg.CmdId, userId, protoMsg.ConvId)
return
}
@@ -161,43 +181,6 @@ func (k *KcpConnectManager) sendMsgHandle() {
// 函数栈内缓存 添加删除事件走chan 避免频繁加锁
convSessionMap := make(map[uint64]*Session)
userIdConvMap := make(map[uint32]uint64)
// 分发到每个连接具体的发送协程
sendToClient := func(protoMsg *ProtoMsg) {
session := convSessionMap[protoMsg.ConvId]
if session == nil {
logger.Error("session is nil, convId: %v", protoMsg.ConvId)
return
}
kcpRawSendChan := session.kcpRawSendChan
if kcpRawSendChan == nil {
logger.Error("kcpRawSendChan is nil, convId: %v", protoMsg.ConvId)
return
}
if len(kcpRawSendChan) == 1000 {
logger.Error("kcpRawSendChan is full, convId: %v", protoMsg.ConvId)
return
}
if protoMsg.CmdId == cmd.PlayerLoginRsp {
logger.Debug("session active, convId: %v", protoMsg.ConvId)
session.connState = ConnActive
// 通知GS玩家各个服务器的appid
serverMsg := new(mq.ServerMsg)
serverMsg.UserId = session.userId
if session.changeGameServer {
serverMsg.JoinHostUserId = session.joinHostUserId
session.changeGameServer = false
session.joinHostUserId = 0
} else {
serverMsg.AnticheatServerAppId = session.anticheatServerAppId
}
k.messageQueue.SendToGs(session.gsServerAppId, &mq.NetMsg{
MsgType: mq.MsgTypeServer,
EventId: mq.ServerAppidBindNotify,
ServerMsg: serverMsg,
})
}
kcpRawSendChan <- protoMsg
}
// 远程全局顶号注册列表
reLoginRemoteKickRegMap := make(map[uint32]chan bool)
for {
@@ -212,14 +195,13 @@ func (k *KcpConnectManager) sendMsgHandle() {
case remoteKick := <-k.reLoginRemoteKickRegChan:
reLoginRemoteKickRegMap[remoteKick.userId] = remoteKick.kickFinishNotifyChan
remoteKick.regFinishNotifyChan <- true
case protoMsg := <-k.localMsgOutput:
sendToClient(protoMsg)
case netMsg := <-k.messageQueue.GetNetMsg():
switch netMsg.MsgType {
case mq.MsgTypeGame:
gameMsg := netMsg.GameMsg
switch netMsg.EventId {
case mq.NormalMsg:
// 分发到每个连接具体的发送协程
convId, exist := userIdConvMap[gameMsg.UserId]
if !exist {
logger.Error("can not find convId by userId")
@@ -230,7 +212,34 @@ func (k *KcpConnectManager) sendMsgHandle() {
protoMsg.CmdId = gameMsg.CmdId
protoMsg.HeadMessage = k.getHeadMsg(gameMsg.ClientSeq)
protoMsg.PayloadMessage = gameMsg.PayloadMessage
sendToClient(protoMsg)
session := convSessionMap[protoMsg.ConvId]
if session == nil {
logger.Error("session is nil, convId: %v", protoMsg.ConvId)
return
}
kcpRawSendChan := session.kcpRawSendChan
if kcpRawSendChan == nil {
logger.Error("kcpRawSendChan is nil, convId: %v", protoMsg.ConvId)
return
}
if len(kcpRawSendChan) == 1000 {
logger.Error("kcpRawSendChan is full, convId: %v", protoMsg.ConvId)
return
}
if protoMsg.CmdId == cmd.PlayerLoginRsp {
logger.Debug("session active, convId: %v", protoMsg.ConvId)
session.connState = ConnActive
// 通知GS玩家各个服务器的appid
serverMsg := new(mq.ServerMsg)
serverMsg.UserId = session.userId
serverMsg.AnticheatServerAppId = session.anticheatServerAppId
k.messageQueue.SendToGs(session.gsServerAppId, &mq.NetMsg{
MsgType: mq.MsgTypeServer,
EventId: mq.ServerAppidBindNotify,
ServerMsg: serverMsg,
})
}
kcpRawSendChan <- protoMsg
}
case mq.MsgTypeConnCtrl:
connCtrlMsg := netMsg.ConnCtrlMsg
@@ -263,14 +272,15 @@ func (k *KcpConnectManager) sendMsgHandle() {
}
session.gsServerAppId = serverMsg.GameServerAppId
session.anticheatServerAppId = ""
session.changeGameServer = true
session.joinHostUserId = serverMsg.JoinHostUserId
// 网关代发登录请求到新的GS
gameMsg := new(mq.GameMsg)
gameMsg.UserId = serverMsg.UserId
gameMsg.CmdId = cmd.PlayerLoginReq
gameMsg.ClientSeq = 0
gameMsg.PayloadMessage = new(proto.PlayerLoginReq)
gameMsg.PayloadMessage = &proto.PlayerLoginReq{
TargetUid: serverMsg.JoinHostUserId,
TargetHomeOwnerUid: 0,
}
k.messageQueue.SendToGs(session.gsServerAppId, &mq.NetMsg{
MsgType: mq.MsgTypeGame,
EventId: mq.NormalMsg,
@@ -339,10 +349,6 @@ func (k *KcpConnectManager) getPlayerToken(req *proto.GetPlayerTokenReq, session
}
uid := tokenVerifyRsp.PlayerID
loginFailRsp := func(retCode int32, isForbid bool, forbidEndTime uint32) *proto.GetPlayerTokenRsp {
// 关联session信息 不然包发不出去
session.userId = uid
k.SetSession(session, session.conn.GetConv(), session.userId)
k.createSessionChan <- session
rsp := new(proto.GetPlayerTokenRsp)
rsp.Uid = uid
rsp.IsProficientPlayer = true
@@ -397,6 +403,7 @@ func (k *KcpConnectManager) getPlayerToken(req *proto.GetPlayerTokenReq, session
loginFailClose()
return nil
case <-regFinishNotifyChan:
timer.Stop()
}
oldSession := k.GetSessionByUserId(uid)
if oldSession != nil {
@@ -426,6 +433,7 @@ func (k *KcpConnectManager) getPlayerToken(req *proto.GetPlayerTokenReq, session
loginFailClose()
return nil
case <-kickFinishNotifyChan:
timer.Stop()
}
}
// 关联玩家uid和连接信息

View File

@@ -8,10 +8,9 @@ import (
type AvatarSkillData struct {
AvatarSkillId int32 `csv:"ID"`
AbilityName string `csv:"Ability名称,omitempty"`
// TODO 这个字段实际上并不是拿来直接扣体力的 体力应该由ability来做 但是现在我捋不清所以摆烂了 改了一下表暂时先这么用着
CostStamina int32 `csv:"消耗体力,omitempty"`
CostElemType int32 `csv:"消耗能量类型,omitempty"`
CostElemVal int32 `csv:"消耗能量值,omitempty"`
CostStamina int32 `csv:"消耗体力,omitempty"`
CostElemType int32 `csv:"消耗能量类型,omitempty"`
CostElemVal int32 `csv:"消耗能量,omitempty"`
}
func (g *GameDataConfig) loadAvatarSkillData() {

View File

@@ -40,3 +40,7 @@ func (g *GameDataConfig) loadGachaDropGroupData() {
}
logger.Info("GachaDropGroupData count: %v", len(g.GachaDropGroupDataMap))
}
func GetGachaDropGroupDataByDropId(dropId int32) *GachaDropGroupData {
return CONF.GachaDropGroupDataMap[dropId]
}

View File

@@ -68,6 +68,7 @@ type GameDataConfig struct {
GCGCharDataMap map[int32]*GCGCharData // 七圣召唤角色卡牌
GCGSkillDataMap map[int32]*GCGSkillData // 七圣召唤卡牌技能
GachaDropGroupDataMap map[int32]*GachaDropGroupData // 卡池掉落组 临时的
SkillStaminaDataMap map[int32]*SkillStaminaData // 角色技能消耗体力 临时的
}
func InitGameDataConfig() {
@@ -168,6 +169,7 @@ func (g *GameDataConfig) load() {
g.loadGCGCharData() // 七圣召唤角色卡牌
g.loadGCGSkillData() // 七圣召唤卡牌技能
g.loadGachaDropGroupData() // 卡池掉落组 临时的
g.loadSkillStaminaData() // 角色技能消耗体力 临时的
}
// CSV相关

View File

@@ -0,0 +1,67 @@
AvatarSkillId,AbilityName,CostStamina
int32,string,int32
ID,Ability名称,消耗体力
10024,Avatar_Ayaka_ExtraAttack,20
10031,Avatar_Qin_ExtraAttack,20
10033,Avatar_Qin_VortexSmash,20
10060,Avatar_Lisa_ExtraAttack,50
10070,Avatar_Barbara_ExtraAttack,50
10073,Avatar_Kaeya_ExtraAttack,20
10160,Avatar_Diluc_ExtraAttack,40
10201,Avatar_Razor_ExtraAttack,40
10231,Avatar_Xiangling_ExtraAttack,25
10241,Avatar_Beidou_ExtraAttack,40
10261,Avatar_Xiao_ExtraAttack,25
10271,Avatar_Ningguang_ExtraAttack,50
10281,Avatar_Prototype_ExtraAttack,999999
10291,Avatar_Klee_ExtraAttack,50
10301,Avatar_Zhongli_ExtraAttack,25
10321,Avatar_Bennett_ExtraAttack,20
10341,Avatar_Noel_ExtraAttack,40
10351,Avatar_Qiqi_ExtraAttack,20
11301,Avatar_Beidou_ExtraAttack,40
10381,Avatar_Xingqiu_ExtraAttack,20
10386,Avatar_Albedo_ExtraAttack,20
10401,Avatar_Beidou_ExtraAttack_Chongyun,40
10411,Avatar_Mona_ExtraAttack,50
10421,Avatar_Keqing_ExtraAttack,25
10431,Avatar_Sucrose_ExtraAttack,50
10441,Avatar_Xinyan_ExtraAttack,40
10451,Avatar_Rosaria_ExtraAttack,25
10461,Avatar_Hutao_ExtraAttack,25
10471,Avatar_Kazuha_ExtraAttack,20
10481,Avatar_Feiyan_ExtraAttack,50
10501,Avatar_Tohma_ExtraAttack,25
10511,Avatar_Eula_ExtraAttack,40
10521,Avatar_Shougun_ExtraAttack,25
10531,Avatar_Sayu_ExtraAttack,40
10541,Avatar_Kokomi_ExtraAttack,50
10571,Avatar_Itto_ExtraAttack,20
10581,Avatar_Yae_ExtraAttack,50
10591,Avatar_Heizo_ExtraAttack,25
10631,Avatar_Shenhe_ExtraAttack,25
10641,Avatar_Yunjin_ExtraAttack,25
10651,Avatar_Shinobu_ExtraAttack,20
10661,Avatar_Ayato_ExtraAttack,20
10681,Avatar_Dori_ExtraAttack,40
10701,Avatar_Nilou_ExtraAttack,20
10711,Avatar_Cyno_ExtraAttack,25
10721,Avatar_Candace_ExtraAttack,25
10731,Avatar_Nahida_ExtraAttack,50
10741,Avatar_Layla_ExtraAttack,20
10751,Avatar_Wanderer_ExtraAttack,50
10771,Avatar_Yaoyao_ExtraAttack,25
10781,Avatar_Alhatham_ExtraAttack,20
10791,Avatar_Dehya_ExtraAttack,40
10801,Avatar_Mika_ExtraAttack,25
10821,Avatar_Baizhuer_ExtraAttack,50
100540,Avatar_PlayerBoy_ExtraAttack_Common,20
100543,Avatar_PlayerBoy_ExtraAttack_Wind,20
100545,Avatar_PlayerBoy_ExtraAttack_Rock,20
100546,Avatar_PlayerBoy_ExtraAttack_Electric,20
100547,Avatar_PlayerBoy_ExtraAttack_Grass,20
100550,Avatar_PlayerGirl_ExtraAttack_Common,20
100553,Avatar_PlayerGirl_ExtraAttack_Wind,20
100555,Avatar_PlayerGirl_ExtraAttack_Rock,20
100556,Avatar_PlayerGirl_ExtraAttack_Electric,20
100557,Avatar_PlayerGirl_ExtraAttack_Grass,20
1 AvatarSkillId AbilityName CostStamina
2 int32 string int32
3 ID Ability名称 消耗体力
4 10024 Avatar_Ayaka_ExtraAttack 20
5 10031 Avatar_Qin_ExtraAttack 20
6 10033 Avatar_Qin_VortexSmash 20
7 10060 Avatar_Lisa_ExtraAttack 50
8 10070 Avatar_Barbara_ExtraAttack 50
9 10073 Avatar_Kaeya_ExtraAttack 20
10 10160 Avatar_Diluc_ExtraAttack 40
11 10201 Avatar_Razor_ExtraAttack 40
12 10231 Avatar_Xiangling_ExtraAttack 25
13 10241 Avatar_Beidou_ExtraAttack 40
14 10261 Avatar_Xiao_ExtraAttack 25
15 10271 Avatar_Ningguang_ExtraAttack 50
16 10281 Avatar_Prototype_ExtraAttack 999999
17 10291 Avatar_Klee_ExtraAttack 50
18 10301 Avatar_Zhongli_ExtraAttack 25
19 10321 Avatar_Bennett_ExtraAttack 20
20 10341 Avatar_Noel_ExtraAttack 40
21 10351 Avatar_Qiqi_ExtraAttack 20
22 11301 Avatar_Beidou_ExtraAttack 40
23 10381 Avatar_Xingqiu_ExtraAttack 20
24 10386 Avatar_Albedo_ExtraAttack 20
25 10401 Avatar_Beidou_ExtraAttack_Chongyun 40
26 10411 Avatar_Mona_ExtraAttack 50
27 10421 Avatar_Keqing_ExtraAttack 25
28 10431 Avatar_Sucrose_ExtraAttack 50
29 10441 Avatar_Xinyan_ExtraAttack 40
30 10451 Avatar_Rosaria_ExtraAttack 25
31 10461 Avatar_Hutao_ExtraAttack 25
32 10471 Avatar_Kazuha_ExtraAttack 20
33 10481 Avatar_Feiyan_ExtraAttack 50
34 10501 Avatar_Tohma_ExtraAttack 25
35 10511 Avatar_Eula_ExtraAttack 40
36 10521 Avatar_Shougun_ExtraAttack 25
37 10531 Avatar_Sayu_ExtraAttack 40
38 10541 Avatar_Kokomi_ExtraAttack 50
39 10571 Avatar_Itto_ExtraAttack 20
40 10581 Avatar_Yae_ExtraAttack 50
41 10591 Avatar_Heizo_ExtraAttack 25
42 10631 Avatar_Shenhe_ExtraAttack 25
43 10641 Avatar_Yunjin_ExtraAttack 25
44 10651 Avatar_Shinobu_ExtraAttack 20
45 10661 Avatar_Ayato_ExtraAttack 20
46 10681 Avatar_Dori_ExtraAttack 40
47 10701 Avatar_Nilou_ExtraAttack 20
48 10711 Avatar_Cyno_ExtraAttack 25
49 10721 Avatar_Candace_ExtraAttack 25
50 10731 Avatar_Nahida_ExtraAttack 50
51 10741 Avatar_Layla_ExtraAttack 20
52 10751 Avatar_Wanderer_ExtraAttack 50
53 10771 Avatar_Yaoyao_ExtraAttack 25
54 10781 Avatar_Alhatham_ExtraAttack 20
55 10791 Avatar_Dehya_ExtraAttack 40
56 10801 Avatar_Mika_ExtraAttack 25
57 10821 Avatar_Baizhuer_ExtraAttack 50
58 100540 Avatar_PlayerBoy_ExtraAttack_Common 20
59 100543 Avatar_PlayerBoy_ExtraAttack_Wind 20
60 100545 Avatar_PlayerBoy_ExtraAttack_Rock 20
61 100546 Avatar_PlayerBoy_ExtraAttack_Electric 20
62 100547 Avatar_PlayerBoy_ExtraAttack_Grass 20
63 100550 Avatar_PlayerGirl_ExtraAttack_Common 20
64 100553 Avatar_PlayerGirl_ExtraAttack_Wind 20
65 100555 Avatar_PlayerGirl_ExtraAttack_Rock 20
66 100556 Avatar_PlayerGirl_ExtraAttack_Electric 20
67 100557 Avatar_PlayerGirl_ExtraAttack_Grass 20

View File

@@ -9,7 +9,7 @@ ID Ability名称 是远程 技能CD 无视冷却缩减属性 消耗体力 消耗
10102 10 0 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 0
10111 0 10 1 11 CircleLockEnemy 1 1 0.3 0 1 0
10012 2 0 1 2 CircleLockEnemyR15H10HC 1 1 0.3 0 1 0
10013 0 15 1 3 CircleLockEnemyR5H6HC 1 1 0.3 0 1 1 233 1
10013 0 1 1 3 CircleLockEnemyR5H6HC 1 1 0.3 0 1 1 233 1
10014 8 0 4 30 1 5 CircleLockEnemyR15H10HC 1 1 0.3 0 1 0
10015 8 0 1 4 CircleLockEnemyR15H10HC 1 1 0.3 0 1 0
10016 0 0 1 4 CircleLockEnemyR5H6HC 1 1 0.3 0 1 0
@@ -20,10 +20,10 @@ ID Ability名称 是远程 技能CD 无视冷却缩减属性 消耗体力 消耗
10021 15 0 5 60 1 6 CircleLockEnemyR15H10HC 1 1 0.3 0 1 0
10022 Test_CreateSharkTorpedo 5 0 2 0 CircleLockEnemyR10H6HC 1 1 0.3 0 1 0
10023 Test_CreateSteamBang 10 0 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 0
10024 Avatar_Ayaka_ExtraAttack 0 20 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 231
10031 Avatar_Qin_ExtraAttack 0 20 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 331
10024 Avatar_Ayaka_ExtraAttack 0 0 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 231
10031 Avatar_Qin_ExtraAttack 0 0 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 331
10032 15 0 1 2 CircleLockEnemyR15H10HC 1 1 0.3 0 1 2 1 2132
10033 Avatar_Qin_VortexSmash 6 20 1 2 CircleLockEnemyR5H6HC 1 1 0.3 0 1 1 332
10033 Avatar_Qin_VortexSmash 6 0 1 2 CircleLockEnemyR5H6HC 1 1 0.3 0 1 1 332
10034 Avatar_Qin_DandelionWind 20 0 7 80 1 5 CircleLockEnemyR15H10HC 1 1 0.3 0 1 339
10035 15 1 0 1 11 CircleLockEnemyR10 1 1 0.3 0 1 2 1 0
10036 Avatar_Qin_EnterSkill 30 1 0 CircleLockEnemyR10 1 1 0.3 0 0
@@ -42,7 +42,7 @@ ID Ability名称 是远程 技能CD 无视冷却缩减属性 消耗体力 消耗
10057 Avatar_Player_ExtraAttack 0 0 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 0
10058 Avatar_Player_ExtraAttack 0 0 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 0
10059 Avatar_Player_ExtraAttack 0 0 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 0
10060 Avatar_Lisa_ExtraAttack 0 50 1 0 CircleLockEnemyR15H10HC 1 1 0.3 0 1 431
10060 Avatar_Lisa_ExtraAttack 0 0 1 0 CircleLockEnemyR15H10HC 1 1 0.3 0 1 431
10061 16 0 1 2 CircleLockEnemyR15H10HC 1 1 0.3 0 1 1 1 432
10062 20 0 4 80 1 5 CircleLockEnemyR15H10HC 1 1 0.3 0 1 439
10063 Test_CreateSharkTorpedo 5 0 2 0 CircleLockEnemyR8H6HC 1 1 0.3 0 1 0
@@ -52,10 +52,10 @@ ID Ability名称 是远程 技能CD 无视冷却缩减属性 消耗体力 消耗
10067 Avatar_Player_UziExplode 1 5 0 1 2 CircleLockEnemyR15H10HC 1 1 0.3 0 1 732
10068 Avatar_Player_WindBreathe 1 15 0 7 60 1 5 CircleLockEnemyR15H10HC 1 1 0.3 0 1 739
10069 0 0 1 1 CircleLockEnemyR5H6HC 1 1 0.3 0 1 0
10070 Avatar_Barbara_ExtraAttack 0 50 1 0 CircleLockEnemyR15H10HC 1 1 0.3 0 1 1431
10070 Avatar_Barbara_ExtraAttack 0 0 1 0 CircleLockEnemyR15H10HC 1 1 0.3 0 1 1431
10071 32 0 1 2 CircleLockEnemyR15H10HC 1 1 0.3 0 1 1 1 1432
10072 20 0 2 80 1 5 CircleLockEnemyR15H10HC 1 1 0.3 0 1 1439
10073 Avatar_Kaeya_ExtraAttack 0 20 1 0 CircleLockEnemyR8H6HC 1 1 0.3 0 1 1531
10073 Avatar_Kaeya_ExtraAttack 0 0 1 0 CircleLockEnemyR8H6HC 1 1 0.3 0 1 1531
10074 6 0 1 2 CircleLockEnemyR15H10HC 1 1 0.3 0 1 1532
10075 15 0 5 60 1 5 CircleLockEnemyR15H10HC 1 1 0.3 0 1 1539
10076 Avatar_Barbara_ReBorn 900 1 0 1 0 CircleLockEnemyR15H10HC 1 1 0.3 0 1 1 1 Skill_B_Barbara_01 0 1
@@ -68,13 +68,13 @@ ID Ability名称 是远程 技能CD 无视冷却缩减属性 消耗体力 消耗
10083 Avatar_Lsai_TeamTalent_Skill 30 0 1 0 CircleLockEnemyR15H10HC 1 1 0.3 0 1 0
10117 8 0 1 32 CircleLockEnemyR10H6HC 1 1 0.3 0 1 1132
10118 20 0 3 80 1 35 CircleLockEnemyR8H6HC 1 1 0.3 0 1 1139
10160 Avatar_Diluc_ExtraAttack 0 40 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 1631
10160 Avatar_Diluc_ExtraAttack 0 0 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 1631
10161 Diluc_Fire_Blade 10 0 1 2 CircleLockEnemyR10H6HC 1 1 0.3 0 1 1632
10162 Diluc_Fire_Blade 2 0 1 3 CircleLockEnemyR10H6HC 1 1 0.3 0 1 0
10163 Diluc_Fire_Blade 2 0 1 4 CircleLockEnemyR10H6HC 1 1 0.3 0 1 0
10165 Diluc_FireCross 12 0 1 40 1 5 CircleLockEnemyR15H10HC 1 1 0.3 0 1 1639
10166 Diluc_EnterSkill 30 0 1 0 CircleLockEnemyR10H6HC 1 1 0.3 0 1 0
10201 Avatar_Razor_ExtraAttack 0 40 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 2031
10201 Avatar_Razor_ExtraAttack 0 0 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 2031
10202 Avatar_Razor_ClawMark_Click 6 0 1 2 CircleLockEnemyR15H10HC 1 1 0.3 0 1 2032
10203 Avatar_Razor_LycanMode 20 0 4 80 1 5 CircleLockEnemyR15H10HC 1 1 0.3 0 1 2039
10204 Avatar_Razor_ClawMark_Click 3 0 1 2 CircleLockEnemyR15H10HC 1 1 0.3 0 1 0
@@ -84,10 +84,10 @@ ID Ability名称 是远程 技能CD 无视冷却缩减属性 消耗体力 消耗
10223 1 0 0 1 1 CircleLockEnemyR15H10HC 1 1 0.3 0 1 0
10224 6 0 1 2 CircleLockEnemyR15H10HC 1 1 0.3 0 1 2232
10225 15 0 7 60 1 5 CircleLockEnemyR15H10HC 1 1 0.3 0 1 2239
10231 Avatar_Xiangling_ExtraAttack 0 25 1 0 CircleLockEnemyR8H6HC 1 1 0.3 0 1 2331
10231 Avatar_Xiangling_ExtraAttack 0 0 1 0 CircleLockEnemyR8H6HC 1 1 0.3 0 1 2331
10232 12 0 1 2 CircleLockEnemyR15H10HC 1 1 0.3 0 1 2332
10235 20 0 1 80 1 5 CircleLockEnemyR15H10HC 1 1 0.3 0 1 2339
10241 Avatar_Beidou_ExtraAttack 0 40 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 2431
10241 Avatar_Beidou_ExtraAttack 0 0 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 2431
10242 Avatar_Beidou_ThunderCounter_Handler 7.5 0 1 2 CircleLockEnemyR8H6HC 1 1 0.3 0 1 2 1 2432
10245 Avatar_Beidou_ThunderShield 20 0 4 80 1 5 CircleLockEnemyR15H10HC 1 1 0.3 0 1 2439
20000 0 0 1 99 CircleLockEnemyR15H10HC 1 1 0.3 0 1 0
@@ -96,24 +96,24 @@ ID Ability名称 是远程 技能CD 无视冷却缩减属性 消耗体力 消耗
10253 1 2 0 1 1 CircleLockEnemyR8H6HC 1 1 0.3 0 1 0
10254 10 0 1 2 CircleLockEnemyR8H6HC 1 1 0.3 0 1 0
10255 15 0 5 60 1 5 CircleLockEnemyR8H6HC 1 1 0.3 0 1 0
10261 Avatar_Xiao_ExtraAttack 0 25 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 2631
10261 Avatar_Xiao_ExtraAttack 0 0 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 2631
10262 10 0 2 2 CircleLockEnemyR8H6HC 1 1 0.3 0 1 2632
10263 10 0 2 2 CircleLockEnemyR8H6HC 1 1 0.3 0 1 0 2
10264 0 0 1 11 CircleLockEnemyR5H6HC 1 1 0.3 0 1 0
10265 18 0 7 70 1 5 CircleLockEnemyR8H6HC 1 1 0.3 0 1 2639
10271 Avatar_Ningguang_ExtraAttack 0 50 1 0 CircleLockEnemyR15H10HC 1 1 0.3 0 1 2731
10271 Avatar_Ningguang_ExtraAttack 0 0 1 0 CircleLockEnemyR15H10HC 1 1 0.3 0 1 2731
10272 12 0 1 2 CircleLockEnemyR15H10HC 1 1 0.3 0 1 2732
10273 1 0 1 12 CircleLockEnemy 1 1 0.3 0 1 0
10274 12 0 8 40 1 5 CircleLockEnemyR15H10HC 1 1 0.3 0 1 2739
10275 Avatar_Ningguang_Trigger_ElementalArt_CD 1 0 1 0 CircleLockEnemyR15H10HC 1 1 0.3 0 1 0
10281 Avatar_Prototype_ExtraAttack 0 666666 1 0 CircleLockEnemyR15H10HC 1 1 0.3 0 1 0
10281 Avatar_Prototype_ExtraAttack 0 0 1 0 CircleLockEnemyR15H10HC 1 1 0.3 0 1 0
10282 0 0 1 2 CircleLockEnemyR5H6HC 1 1 0.3 0 1 0
10283 10 0 1 1 3 CircleLockEnemyR8H6HC 1 1 0.3 0 1 0
10291 Avatar_Klee_ExtraAttack 0 50 1 0 CircleLockEnemyR15H10HC 1 1 0.3 0 1 2931
10291 Avatar_Klee_ExtraAttack 0 0 1 0 CircleLockEnemyR15H10HC 1 1 0.3 0 1 2931
10292 20 0 2 2 CircleLockEnemyR15H10HC 1 1 0.3 0 1 2932
10295 15 0 1 60 1 5 CircleLockEnemyR15H10HC 1 1 0.3 0 1 2939
10300 0 0 1 3 CircleLockEnemyR5H6HC 1 1 0.3 0 1 0
10301 Avatar_Zhongli_ExtraAttack 0 25 1 0 CircleLockEnemyR8H6HC 1 1 0.3 0 1 3031
10301 Avatar_Zhongli_ExtraAttack 0 0 1 0 CircleLockEnemyR8H6HC 1 1 0.3 0 1 3031
10302 12 0 1 2 CircleLockEnemyR8H6HC 1 1 0.3 0 1 3032
10303 12 0 8 40 1 5 CircleLockEnemyR8H6HC 1 1 0.3 0 1 3039
10311 Avatar_Fischl_PressShoot 1 0 0 1 0 CircleLockEnemyR15H10HC 1 1 0.3 0 1 3131
@@ -122,7 +122,7 @@ ID Ability名称 是远程 技能CD 无视冷却缩减属性 消耗体力 消耗
10314 Avatar_Fischl_AimPressShoot 1 0 0 1 0 CircleLockEnemyR15H10HC 1 1 0.3 0 1 0
10315 1 0 0 1 1 CircleLockEnemyR15H10HC 1 1 0.3 0 1 0
10316 1.5 1 0 1 16 CircleLockEnemyR15H10HC 1 1 0.3 0 1 0
10321 Avatar_Bennett_ExtraAttack 0 20 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 3231
10321 Avatar_Bennett_ExtraAttack 0 0 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 3231
10322 Avatar_Bennett_ElementalArt_Handler 5 0 1 2 CircleLockEnemyR10H6HC 1 1 0.3 0 1 3232
10323 15 0 1 60 1 5 CircleLockEnemyR8H6HC 1 1 0.3 0 1 3239
10331 Avatar_Tartaglia_PressShoot 1 0 0 1 0 CircleLockEnemyR15H10HC 1 1 0.3 0 1 3331
@@ -132,11 +132,11 @@ ID Ability名称 是远程 技能CD 无视冷却缩减属性 消耗体力 消耗
10335 1 0 0 1 1 CircleLockEnemyR15H10HC 1 1 0.3 0 1 0
10336 Avatar_Tartaglia_Gandalfr_TriggerOff 8 0 1 0 CircleLockEnemyR8H6HC 1 1 0.3 0 1 0
10337 Avatar_Tartaglia_PressShoot 0 0 1 0 CircleLockEnemyR8H6HC 1 1 0.3 0 1 0
10341 Avatar_Noel_ExtraAttack 0 40 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 3431
10341 Avatar_Noel_ExtraAttack 0 0 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 3431
10342 24 0 1 2 CircleLockEnemyR10H6HC 1 1 0.3 0 1 3432
10343 15 0 8 60 1 5 CircleLockEnemyR8H6HC 1 1 0.3 0 1 3439
10344 1 1 0 1 0 CircleLockEnemyR15H10HC 1 1 0.3 0 1 0
10351 Avatar_Qiqi_ExtraAttack 0 20 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 3531
10351 Avatar_Qiqi_ExtraAttack 0 0 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 3531
10352 30 0 1 2 CircleLockEnemyR10H6HC 1 1 0.3 0 1 3532
10353 20 0 5 80 1 5 CircleLockEnemyR8H6HC 1 1 0.3 0 1 3539
10355 Avatar_Qiqi_Revive 900 1 0 1 0 CircleLockEnemyR15H10HC 1 1 0.3 0 1 2 1 Skill_B_Barbara_01 0 1
@@ -144,7 +144,7 @@ ID Ability名称 是远程 技能CD 无视冷却缩减属性 消耗体力 消耗
10372 10 0 1 2 CircleLockEnemyR8H6HC 1 1 0.3 0 1 3732
10373 15 0 5 60 1 5 CircleLockEnemyR15H10HC 1 1 0.3 0 1 3739
10374 Avatar_Ganyu_AimPressShoot 1 0 0 1 0 CircleLockEnemyR15H10HC 1 1 0.3 0 1 0
11301 Avatar_Beidou_ExtraAttack 0 666666 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 0
11301 Avatar_Beidou_ExtraAttack 0 0 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 0
11302 9 0 1 2 CircleLockEnemyR8H6HC 1 1 0.3 0 1 0
11305 25 0 4 60 1 5 CircleLockEnemyR15H10HC 1 1 0.3 0 1 0
10256 8 0 3 2 CircleLockEnemyR8H6HC 1 1 0.3 0 1 0
@@ -160,10 +160,10 @@ ID Ability名称 是远程 技能CD 无视冷却缩减属性 消耗体力 消耗
11373 15 0 5 60 1 5 CircleLockEnemyR15H10HC 1 1 0.3 0 1 0
11374 1 0 0 1 0 CircleLockEnemyR15H10HC 1 1 0.3 0 1 0
11375 1 0 0 1 1 CircleLockEnemyR15H10HC 1 1 0.3 0 1 0
10381 Avatar_Xingqiu_ExtraAttack 0 20 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 2531
10381 Avatar_Xingqiu_ExtraAttack 0 0 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 2531
10382 Avatar_Xingqiu_SkillHoldHandler 21 0 1 2 CircleLockEnemyR8H6HC 1 1 0.3 0 1 2532
10385 20 0 2 80 1 5 CircleLockEnemyR5H6HC 1 1 0.3 0 1 2539
10386 Avatar_Albedo_ExtraAttack 0 20 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 3831
10386 Avatar_Albedo_ExtraAttack 0 0 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 3831
10387 4 0 1 2 CircleLockEnemyR5H6HC 1 1 0.3 0 1 3832
10388 12 0 8 40 1 5 CircleLockEnemyR8H6HC 1 1 0.3 0 1 3839
10391 Avatar_Diona_PressShoot 1 0 0 1 0 CircleLockEnemyR15H10HC 1 1 0.3 0 1 3931
@@ -171,58 +171,58 @@ ID Ability名称 是远程 技能CD 无视冷却缩减属性 消耗体力 消耗
10393 Avatar_Diona_AimPressShoot 1 0 0 1 0 CircleLockEnemyR15H10HC 1 1 0.3 0 1 0
10394 1 0 0 1 1 CircleLockEnemyR15H10HC 1 1 0.3 0 1 0
10395 1 20 0 5 80 1 5 CircleLockEnemyR15H10HC 1 1 0.3 0 1 3939
10401 Avatar_Beidou_ExtraAttack_Chongyun 0 666666 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 3631
10401 Avatar_Beidou_ExtraAttack_Chongyun 0 0 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 3631
10402 Avatar_Razor_ClawMark_Click 15 0 1 2 CircleLockEnemyR8H6HC 1 1 0.3 0 1 3632
10403 Diluc_FireCross 12 0 5 40 1 5 CircleLockEnemyR15H10HC 1 1 0.3 0 1 3639
10411 Avatar_Mona_ExtraAttack 0 50 1 0 CircleLockEnemyR15H10HC 1 1 0.3 0 1 4131
10411 Avatar_Mona_ExtraAttack 0 0 1 0 CircleLockEnemyR15H10HC 1 1 0.3 0 1 4131
10412 12 0 1 2 CircleLockEnemyR15H10HC 1 1 0.3 0 1 4132
10413 0 15 1 3 CircleLockEnemyR5H6HC 1 1 0.3 0 1 1 4133 1
10413 0 1 1 3 CircleLockEnemyR5H6HC 1 1 0.3 0 1 1 4133 1
10415 15 0 2 60 1 5 CircleLockEnemyR15H10HC 1 1 0.3 0 1 4139
10421 Avatar_Keqing_ExtraAttack 0 25 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 4231
10421 Avatar_Keqing_ExtraAttack 0 0 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 4231
10422 7.5 0 1 2 CircleLockEnemyR15H10HC 1 1 0.3 0 1 4232
10423 0 0 1 3 CircleLockEnemyR15H10HC 1 1 0.3 0 1 0
10425 12 0 4 40 1 5 CircleLockEnemyR5H6HC 1 1 0.3 0 1 4239
10431 Avatar_Sucrose_ExtraAttack 0 50 1 0 CircleLockEnemyR15H10HC 1 1 0.3 0 1 4331
10431 Avatar_Sucrose_ExtraAttack 0 0 1 0 CircleLockEnemyR15H10HC 1 1 0.3 0 1 4331
10432 15 0 1 2 CircleLockEnemyR15H10HC 1 1 0.3 0 1 4332
10433 1 5 1 12 CircleLockEnemy 1 1 0.3 0 1 0
10434 0 0 1 0 CircleLockEnemy 1 1 0.3 0 1 0
10435 20 0 7 80 1 5 CircleLockEnemyR15H10HC 1 1 0.3 0 1 4339
10441 Avatar_Xinyan_ExtraAttack 0 40 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 4431
10441 Avatar_Xinyan_ExtraAttack 0 0 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 4431
10442 18 0 1 2 CircleLockEnemyR10H6HC 1 1 0.3 0 1 4432
10443 15 0 1 60 1 5 CircleLockEnemyR8H6HC 1 1 0.3 0 1 4439
10451 Avatar_Rosaria_ExtraAttack 0 25 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 4531
10451 Avatar_Rosaria_ExtraAttack 0 0 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 4531
10452 6 0 1 2 CircleLockEnemyR15H10HC 1 1 0.3 0 1 4532
10453 15 0 5 60 1 5 CircleLockEnemyR8H6HC 1 1 0.3 0 1 4539
10461 Avatar_Hutao_ExtraAttack 0 25 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 4631
10461 Avatar_Hutao_ExtraAttack 0 0 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 4631
10462 16 0 1 2 CircleLockEnemyR5H6HC 1 1 0.3 0 1 4632
10463 15 0 1 60 1 5 CircleLockEnemyR5H6HC 1 1 0.3 0 1 4639
10464 Avatar_Hutao_Constellation_Limbo_Trigger 60 1 0 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 0 1
10471 Avatar_Kazuha_ExtraAttack 0 20 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 4731
10471 Avatar_Kazuha_ExtraAttack 0 0 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 4731
10472 6 0 1 2 CircleLockEnemyR8H6HC 1 1 0.3 0 1 4732
10473 6 0 1 3 CircleLockEnemyR8H6HC 1 1 0.3 0 1 0 2
10474 0 0 1 4 CircleLockEnemyR5H10HC 1 1 0.3 0 1 0
10475 15 0 7 60 1 5 CircleLockEnemyR15H10HC 1 1 0.3 0 1 4739
10481 Avatar_Feiyan_ExtraAttack 0 50 1 0 CircleLockEnemyR15H10HC 1 1 0.3 0 1 4831
10481 Avatar_Feiyan_ExtraAttack 0 0 1 0 CircleLockEnemyR15H10HC 1 1 0.3 0 1 4831
10482 9 0 1 2 CircleLockEnemyR15H10HC 1 1 0.3 0 1 4832
10485 20 0 1 80 1 5 CircleLockEnemyR5H6HC 1 1 0.3 0 1 4839
10491 Avatar_Yoimiya_PressShoot 1 0 0 1 0 CircleLockEnemyR15H10HC 1 1 0.3 0 1 4931
10492 1 18 0 1 2 CircleLockEnemyR15H10HC 1 1 0.3 0 1 4932
10493 Avatar_Yoimiya_AimPressShoot 1 0 0 1 0 CircleLockEnemyR15H10HC 1 1 0.3 0 1 0
10495 1 15 0 1 60 1 5 CircleLockEnemyR10H6HC 1 1 0.3 0 1 4939
10501 Avatar_Tohma_ExtraAttack 0 25 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 5031
10501 Avatar_Tohma_ExtraAttack 0 0 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 5031
10502 15 0 1 2 CircleLockEnemyR8H6HC 1 1 0.3 0 1 5032
10505 20 0 1 80 1 5 CircleLockEnemyR5H6HC 1 1 0.3 0 1 5039
10511 Avatar_Eula_ExtraAttack 0 40 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 5131
10511 Avatar_Eula_ExtraAttack 0 0 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 5131
10512 4 0 1 2 CircleLockEnemyR8H6HC 1 1 0.3 0 1 5132
10515 20 0 5 80 1 5 CircleLockEnemyR15H10HC 1 1 0.3 0 1 5139
10521 Avatar_Shougun_ExtraAttack 0 25 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 5231
10521 Avatar_Shougun_ExtraAttack 0 0 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 5231
10522 10 0 1 2 CircleLockEnemyR8H6HC 1 1 0.3 0 1 5232
10525 18 0 4 90 1 5 CircleLockEnemyR8H6HC 1 1 0.3 0 1 5239
10531 Avatar_Sayu_ExtraAttack 0 40 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 5331
10531 Avatar_Sayu_ExtraAttack 0 0 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 5331
10532 6 0 1 2 CircleLockEnemyR8H6HC 1 1 0.3 0 1 5332
10533 0 0 1 3 CircleLockEnemyR8H6HC 1 1 0.3 0 1 0
10535 20 0 7 80 1 5 CircleLockEnemyR15H10HC 1 1 0.3 0 1 5339
10541 Avatar_Kokomi_ExtraAttack 0 50 1 0 CircleLockEnemyR15H10HC 1 1 0.3 0 1 5431
10541 Avatar_Kokomi_ExtraAttack 0 0 1 0 CircleLockEnemyR15H10HC 1 1 0.3 0 1 5431
10542 20 0 1 2 CircleLockEnemyR15H10HC 1 1 0.3 0 1 5432
10545 18 0 2 70 1 5 CircleLockEnemyR15H10HC 1 1 0.3 0 1 5439
10551 Avatar_Gorou_PressShoot 1 0 0 1 0 CircleLockEnemyR15H10HC 1 1 0.3 0 1 5531
@@ -235,14 +235,14 @@ ID Ability名称 是远程 技能CD 无视冷却缩减属性 消耗体力 消耗
10563 Avatar_Sara_AimPressShoot 1 0 0 1 0 CircleLockEnemyR15H10HC 1 1 0.3 0 1 0
10564 1 0 0 1 1 CircleLockEnemyR15H10HC 1 1 0.3 0 1 0
10565 1 20 0 4 80 1 5 CircleLockEnemyR15H10HC 1 1 0.3 0 1 5639
10571 Avatar_Itto_ExtraAttack 0 20 1 0 CircleLockEnemyR7H6HC 1 1 0.6 0 1 5731
10571 Avatar_Itto_ExtraAttack 0 0 1 0 CircleLockEnemyR7H6HC 1 1 0.6 0 1 5731
10572 10 0 1 2 CircleLockEnemyR8H6HC 1 1 0.3 0 1 5732
10575 18 0 8 70 1 5 CircleLockEnemyR15H10HC 1 1 0.3 0 1 5739
10581 Avatar_Yae_ExtraAttack 0 50 1 0 CircleLockEnemyR15H10HC 1 1 0.3 0 1 5831
10581 Avatar_Yae_ExtraAttack 0 0 1 0 CircleLockEnemyR15H10HC 1 1 0.3 0 1 5831
10582 4 0 3 2 CircleLockEnemyR15H10HC 1 1 0.3 0 1 1 5832
10583 4 0 3 3 CircleLockEnemyR15H10HC 1 1 0.3 0 1 0
10585 22 0 4 90 1 5 CircleLockEnemyR15H10HC 1 1 0.3 0 1 5839
10591 Avatar_Heizo_ExtraAttack 0 25 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 5931
10591 Avatar_Heizo_ExtraAttack 0 0 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 5931
10592 10 0 1 2 CircleLockEnemyR8H6HC 1 1 0.3 0 1 5932
10595 12 0 7 40 1 5 CircleLockEnemyR15H10HC 1 1 0.3 0 1 5939
10602 13.5 0 1 22 CircleLockEnemyR15H10HC 1 1 0.3 0 1 1032
@@ -256,19 +256,19 @@ ID Ability名称 是远程 技能CD 无视冷却缩减属性 消耗体力 消耗
10622 20 0 1 2 CircleLockEnemyR15H10HC 1 1 0.3 0 1 6232
10623 Avatar_Aloy_AimPressShoot 1 0 0 1 0 CircleLockEnemyR15H10HC 1 1 0.3 0 1 0
10624 1 0 0 1 1 CircleLockEnemyR15H10HC 1 1 0.3 0 1 0
10631 Avatar_Shenhe_ExtraAttack 0 25 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 6331
10631 Avatar_Shenhe_ExtraAttack 0 0 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 6331
10632 10 0 1 2 CircleLockEnemyR8H6HC 1 1 0.3 0 1 6332
10635 20 0 5 80 1 5 CircleLockEnemyR8H6HC 1 1 0.3 0 1 6339
10625 12 0 5 40 1 5 CircleLockEnemyR15H10HC 1 1 0.3 0 1 6239
10641 Avatar_Yunjin_ExtraAttack 0 25 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 6431
10641 Avatar_Yunjin_ExtraAttack 0 0 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 6431
10642 9 0 1 2 CircleLockEnemyR8H6HC 1 1 0.3 0 1 6432
10643 15 0 8 60 1 5 CircleLockEnemyR8H6HC 1 1 0.3 0 1 6439
10651 Avatar_Shinobu_ExtraAttack 0 20 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 6531
10651 Avatar_Shinobu_ExtraAttack 0 0 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 6531
10652 15 0 1 2 CircleLockEnemyR8H6HC 1 1 0.3 0 1 6532
10655 15 0 4 60 1 5 CircleLockEnemyR8H6HC 1 1 0.3 0 1 6539
10656 Avatar_Shinobu_Constellation6_Limbo_Trigger 60 1 0 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 0 1
10657 Avatar_Shinobu_Constellation6_Limbo_Trigger 60 1 0 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 0 1
10661 Avatar_Ayato_ExtraAttack 0 20 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 6631
10661 Avatar_Ayato_ExtraAttack 0 0 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 6631
10662 12 0 1 2 CircleLockEnemyR8H6HC 1 1 0.3 0 1 6632
10663 Avatar_Ayato_KendoPose_Click 0 0 1 0 CircleLockEnemyR10H6HC 1 1 0.3 0 1 0
10665 20 0 2 80 1 5 CircleLockEnemyR5H6HC 1 1 0.3 0 1 6639
@@ -276,14 +276,14 @@ ID Ability名称 是远程 技能CD 无视冷却缩减属性 消耗体力 消耗
10672 12 0 1 2 CircleLockEnemyR15H10HC 1 1 0.3 0 1 6732
10675 15 0 3 60 1 5 CircleLockEnemyR15H10HC 1 1 0.3 0 1 6739
10674 Avatar_Ganyu_AimPressShoot 1 0 0 1 0 CircleLockEnemyR15H10HC 1 1 0.3 0 1 0
10681 Avatar_Dori_ExtraAttack 0 40 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 6831
10681 Avatar_Dori_ExtraAttack 0 0 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 6831
10682 9 0 1 2 CircleLockEnemyR15H10HC 1 1 0.3 0 1 6832
10685 20 0 4 80 1 5 CircleLockEnemyR8H6HC 1 1 0.3 0 1 6839
10691 Avatar_Tighnari_PressShoot 1 0 0 1 0 CircleLockEnemyR15H10HC 1 1 0.3 0 1 6931
10692 12 0 1 2 CircleLockEnemyR15H10HC 1 1 0.3 0 1 6932
10693 Avatar_Tighnari_AimPressShoot 1 0 0 1 0 CircleLockEnemyR15H10HC 1 1 0.3 0 1
10695 12 0 3 40 1 5 CircleLockEnemyR15H10HC 1 1 0.3 0 1 6939
10701 Avatar_Nilou_ExtraAttack 0 20 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 7031
10701 Avatar_Nilou_ExtraAttack 0 0 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 7031
10702 18 0 1 2 CircleLockEnemyR8H6HC 1 1 0.3 0 1 7032
10703 0 0 1 3 CircleLockEnemyR8H6HC 1 1 0.3 0 1
10704 0 0 1 4 CircleLockEnemyR8H6HC 1 1 0.3 0 1
@@ -291,20 +291,20 @@ ID Ability名称 是远程 技能CD 无视冷却缩减属性 消耗体力 消耗
10707 0 0 1 22 CircleLockEnemyR8H6HC 1 1 0.3 0 1
10705 18 0 2 70 1 5 CircleLockEnemyR15H10HC 1 1 0.3 0 1 7039
10708 0 0 1 23 CircleLockEnemyR8H6HC 1 1 0.3 0 1
10711 Avatar_Cyno_ExtraAttack 0 25 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 7131
10711 Avatar_Cyno_ExtraAttack 0 0 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 7131
10712 7.5 0 1 2 CircleLockEnemyR8H6HC 1 1 0.3 0 1 7132
10713 3 0 1 2 CircleLockEnemyR8H6HC 1 1 0.3 0 1 0 2
10715 20 0 4 80 1 5 CircleLockEnemyR8H6HC 1 1 0.3 0 1 7139
10721 Avatar_Candace_ExtraAttack 0 25 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 7231
10721 Avatar_Candace_ExtraAttack 0 0 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 7231
10722 · 6 0 1 2 CircleLockEnemyR8H6HC 1 1 0.3 0 1 2 1 7232
10725 15 0 2 60 1 5 CircleLockEnemyR8H6HC 1 1 0.3 0 1 7239
10731 Avatar_Nahida_ExtraAttack 0 50 1 0 CircleLockEnemyR10H6HC 1 1 0.3 0 1 7331
10731 Avatar_Nahida_ExtraAttack 0 0 1 0 CircleLockEnemyR10H6HC 1 1 0.3 0 1 7331
10732 · 5 0 1 2 CircleLockEnemyR8H6HC 1 1 0.3 0 1 7332
10735 13.5 0 3 50 1 5 CircleLockEnemyR15H10HC 1 1 0.3 0 1 7339
10741 Avatar_Layla_ExtraAttack 0 20 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 7431
10741 Avatar_Layla_ExtraAttack 0 0 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 7431
10742 · 12 0 1 2 CircleLockEnemyR8H6HC 1 1 0.3 0 1 7432
10745 12 0 5 40 1 5 CircleLockEnemyR15H10HC 1 1 0.3 0 1 7439
10751 Avatar_Wanderer_ExtraAttack 0 50 1 0 CircleLockEnemyR15H10HC 1 1 0.3 0 1 7531
10751 Avatar_Wanderer_ExtraAttack 0 0 1 0 CircleLockEnemyR15H10HC 1 1 0.3 0 1 7531
10752 6 0 1 2 CircleLockEnemyR15H10HC 1 1 0.3 0 1 7532
10753 15 0 7 60 1 3 CircleLockEnemyR15H10HC 1 1 0.3 0 1
10755 15 0 7 60 1 5 CircleLockEnemyR15H10HC 1 1 0.3 0 1 7539
@@ -316,21 +316,21 @@ ID Ability名称 是远程 技能CD 无视冷却缩减属性 消耗体力 消耗
10762 8 0 1 2 CircleLockEnemyR15H10HC 1 1 0.3 0 1 0
10764 Avatar_Faruzan_AimPressShoot 1 0 0 1 0 CircleLockEnemyR15H10HC 1 1 0.3 0 1 0
10765 12 0 7 40 1 5 CircleLockEnemyR15H10HC 1 1 0.3 0 1 0
10771 Avatar_Yaoyao_ExtraAttack 0 25 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 0
10771 Avatar_Yaoyao_ExtraAttack 0 0 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 0
10772 10 0 1 2 CircleLockEnemyR8H6HC 1 1 0.3 0 1 0
10775 20 0 3 80 1 5 CircleLockEnemyR8H6HC 1 1 0.3 0 1 0
10781 Avatar_Alhatham_ExtraAttack 0 20 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 0
10781 Avatar_Alhatham_ExtraAttack 0 0 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 0
10782 · 10 0 1 2 CircleLockEnemyR15H10HC 1 1 0.3 0 1 0
10785 15 0 3 60 1 5 CircleLockEnemyR15H10HC 1 1 0.3 0 1 0
10791 Avatar_Dehya_ExtraAttack 0 666666 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 0
10791 Avatar_Dehya_ExtraAttack 0 0 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 0
10792 8 0 1 2 CircleLockEnemyR15H10HC 1 1 0.3 0 1 0
10793 0 0 1 3 CircleLockEnemyR15H10HC 1 1 0.3 0 1 0
10794 0.5 0 1 4 CircleLockEnemyR15H10HC 1 1 0.3 0 1 0
10795 15 0 1 60 1 5 CircleLockEnemyR15H10HC 1 1 0.3 0 1 0
10801 Avatar_Mika_ExtraAttack 0 666666 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 0
10801 Avatar_Mika_ExtraAttack 0 0 1 0 CircleLockEnemyR5H6HC 1 1 0.3 0 1 0
10802 Avatar_Mika_ElementalArt 10 0 1 2 CircleLockEnemyR8H6HC 1 1 0.3 0 1 0
10805 20 0 5 60 1 5 CircleLockEnemyR5H6HC 1 1 0.3 0 1 0
10821 Avatar_Baizhuer_ExtraAttack 1 0 666666 1 0 CircleLockEnemyR15H10HC 1 1 0.3 0 1 0
10821 Avatar_Baizhuer_ExtraAttack 1 0 0 1 0 CircleLockEnemyR15H10HC 1 1 0.3 0 1 0
10822 · 10 0 1 2 CircleLockEnemyR15H10HC 1 1 0.3 0 1 0
10825 15 0 3 60 1 5 CircleLockEnemyR15H10HC 1 1 0.3 0 1 0
20001 Dvalin_S01_AirGun 0 999999 1 0 CircleLockEnemyR10 1 1 0.3 0 1 2 1 0

View File

@@ -0,0 +1,28 @@
package gdconf
import (
"hk4e/pkg/endec"
"hk4e/pkg/logger"
)
// 体力应该由ability来做 但是现在我捋不清所以摆烂了 改了一下表暂时先这么用着
type SkillStaminaData struct {
AvatarSkillId int32 `csv:"AvatarSkillId"`
AbilityName string `csv:"AbilityName"`
CostStamina int32 `csv:"CostStamina"`
}
func (g *GameDataConfig) loadSkillStaminaData() {
g.SkillStaminaDataMap = make(map[int32]*SkillStaminaData)
skillStaminaDataList := make([]*SkillStaminaData, 0)
readExtCsv[SkillStaminaData](g.extPrefix+"SkillStaminaData.csv", &skillStaminaDataList)
for _, skillStaminaData := range skillStaminaDataList {
g.SkillStaminaDataMap[endec.Hk4eAbilityHashCode(skillStaminaData.AbilityName)] = skillStaminaData
}
logger.Info("SkillStaminaData count: %v", len(g.SkillStaminaDataMap))
}
func GetSkillStaminaDataByAbilityHashCode(abilityHashCode int32) *SkillStaminaData {
return CONF.SkillStaminaDataMap[abilityHashCode]
}

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 // 溺水返回安全点延时
}