diff --git a/anticheat/app/app.go b/anticheat/app/app.go index d3c50eb1..78b6770d 100644 --- a/anticheat/app/app.go +++ b/anticheat/app/app.go @@ -12,6 +12,7 @@ import ( "hk4e/common/config" "hk4e/common/mq" "hk4e/common/rpc" + "hk4e/gdconf" "hk4e/node/api" "hk4e/pkg/logger" ) @@ -61,6 +62,8 @@ func Run(ctx context.Context, configFile string) error { logger.CloseLogger() }() + gdconf.InitGameDataConfig() + messageQueue := mq.NewMessageQueue(api.ANTICHEAT, APPID, client) defer messageQueue.Close() diff --git a/anticheat/handle/handle.go b/anticheat/handle/handle.go index 0daf6a79..1e3dd419 100644 --- a/anticheat/handle/handle.go +++ b/anticheat/handle/handle.go @@ -7,6 +7,7 @@ import ( "hk4e/common/constant" "hk4e/common/mq" "hk4e/gate/kcp" + "hk4e/gdconf" "hk4e/node/api" "hk4e/pkg/logger" "hk4e/protocol/cmd" @@ -16,8 +17,10 @@ import ( ) const ( - MoveVectorCacheNum = 100 - MaxMoveSpeed = 50.0 + MoveVectorCacheNum = 10 + MaxMoveSpeed = 30.0 + JumpDistance = 100.0 + PointDistance = 10.0 ) type MoveVector struct { @@ -26,15 +29,41 @@ type MoveVector struct { } type AnticheatContext struct { + sceneId uint32 moveVectorList []*MoveVector } -func (a *AnticheatContext) Move(pos *proto.Vector) { +func (a *AnticheatContext) Move(pos *proto.Vector) bool { now := time.Now().UnixMilli() if len(a.moveVectorList) > 0 { lastMoveVector := a.moveVectorList[len(a.moveVectorList)-1] if now-lastMoveVector.time < 1000 { - return + return true + } + distance := GetDistance(pos, lastMoveVector.pos) + if distance > JumpDistance { + // 瞬时变化太大 判断是否为传送 + scenePointMap := gdconf.GetScenePointMapBySceneId(int32(a.sceneId)) + if scenePointMap == nil { + return true + } + isJump := true + for _, pointData := range scenePointMap { + d := GetDistance(pos, &proto.Vector{ + X: float32(pointData.TranPos.X), + Y: float32(pointData.TranPos.Y), + Z: float32(pointData.TranPos.Z), + }) + if d < PointDistance { + isJump = false + break + } + } + if isJump { + return false + } else { + a.moveVectorList = make([]*MoveVector, 0) + } } } a.moveVectorList = append(a.moveVectorList, &MoveVector{ @@ -44,6 +73,7 @@ func (a *AnticheatContext) Move(pos *proto.Vector) { if len(a.moveVectorList) > MoveVectorCacheNum { a.moveVectorList = a.moveVectorList[len(a.moveVectorList)-MoveVectorCacheNum:] } + return true } func (a *AnticheatContext) GetMoveSpeed() float32 { @@ -57,11 +87,7 @@ func (a *AnticheatContext) GetMoveSpeed() float32 { } nextMoveVector := a.moveVectorList[index+1] beforeMoveVector := a.moveVectorList[index] - dx := float32(math.Sqrt( - float64((nextMoveVector.pos.X-beforeMoveVector.pos.X)*(nextMoveVector.pos.X-beforeMoveVector.pos.X)) + - float64((nextMoveVector.pos.Y-beforeMoveVector.pos.Y)*(nextMoveVector.pos.Y-beforeMoveVector.pos.Y)) + - float64((nextMoveVector.pos.Z-beforeMoveVector.pos.Z)*(nextMoveVector.pos.Z-beforeMoveVector.pos.Z)), - )) + dx := GetDistance(nextMoveVector.pos, beforeMoveVector.pos) dt := float32(nextMoveVector.time-beforeMoveVector.time) / 1000.0 avgMoveSpeed += dx / dt } @@ -71,6 +97,7 @@ func (a *AnticheatContext) GetMoveSpeed() float32 { func NewAnticheatContext() *AnticheatContext { r := &AnticheatContext{ + sceneId: 0, moveVectorList: make([]*MoveVector, 0), } return r @@ -81,13 +108,16 @@ type Handle struct { playerAcCtxMap map[uint32]*AnticheatContext } +func (h *Handle) AddPlayerAcCtx(userId uint32) { + h.playerAcCtxMap[userId] = NewAnticheatContext() +} + +func (h *Handle) DelPlayerAcCtx(userId uint32) { + delete(h.playerAcCtxMap, userId) +} + func (h *Handle) GetPlayerAcCtx(userId uint32) *AnticheatContext { - ctx, exist := h.playerAcCtxMap[userId] - if !exist { - ctx = NewAnticheatContext() - h.playerAcCtxMap[userId] = ctx - } - return ctx + return h.playerAcCtxMap[userId] } func NewHandle(messageQueue *mq.MessageQueue) (r *Handle) { @@ -102,19 +132,31 @@ func (h *Handle) run() { go func() { for { netMsg := <-h.messageQueue.GetNetMsg() - if netMsg.MsgType != mq.MsgTypeGame { - continue - } - if netMsg.EventId != mq.NormalMsg { - continue - } - if netMsg.OriginServerType != api.GATE { - continue - } - gameMsg := netMsg.GameMsg - switch gameMsg.CmdId { - case cmd.CombatInvocationsNotify: - h.CombatInvocationsNotify(gameMsg.UserId, netMsg.OriginServerAppId, gameMsg.PayloadMessage) + switch netMsg.MsgType { + case mq.MsgTypeGame: + if netMsg.OriginServerType != api.GATE { + continue + } + if netMsg.EventId != mq.NormalMsg { + continue + } + gameMsg := netMsg.GameMsg + switch gameMsg.CmdId { + case cmd.CombatInvocationsNotify: + h.CombatInvocationsNotify(gameMsg.UserId, netMsg.OriginServerAppId, gameMsg.PayloadMessage) + case cmd.ToTheMoonEnterSceneReq: + h.ToTheMoonEnterSceneReq(gameMsg.UserId, netMsg.OriginServerAppId, gameMsg.PayloadMessage) + } + case mq.MsgTypeServer: + serverMsg := netMsg.ServerMsg + switch netMsg.EventId { + case mq.ServerUserOnlineStateChangeNotify: + if serverMsg.IsOnline { + h.AddPlayerAcCtx(serverMsg.UserId) + } else { + h.DelPlayerAcCtx(serverMsg.UserId) + } + } } } }() @@ -133,23 +175,54 @@ func (h *Handle) CombatInvocationsNotify(userId uint32, gateAppId string, payloa if GetEntityType(entityMoveInfo.EntityId) != constant.ENTITY_TYPE_AVATAR { continue } + if entityMoveInfo.MotionInfo.Pos != nil { + continue + } // 玩家超速移动检测 ctx := h.GetPlayerAcCtx(userId) - ctx.Move(entityMoveInfo.MotionInfo.Pos) + if ctx == nil { + logger.Error("get player anticheat context is nil, uid: %v", userId) + continue + } + ok := ctx.Move(entityMoveInfo.MotionInfo.Pos) + if !ok { + logger.Warn("player move jump, pos: %v, uid: %v", entityMoveInfo.MotionInfo.Pos, userId) + h.KickPlayer(userId, gateAppId) + continue + } moveSpeed := ctx.GetMoveSpeed() logger.Debug("player move speed: %v, uid: %v", moveSpeed, userId) if moveSpeed > MaxMoveSpeed { logger.Warn("player move overspeed, speed: %v, uid: %v", moveSpeed, userId) h.KickPlayer(userId, gateAppId) + continue } } } } +func (h *Handle) ToTheMoonEnterSceneReq(userId uint32, gateAppId string, payloadMsg pb.Message) { + req := payloadMsg.(*proto.ToTheMoonEnterSceneReq) + ctx := h.GetPlayerAcCtx(userId) + if ctx == nil { + logger.Error("get player anticheat context is nil, uid: %v", userId) + return + } + ctx.sceneId = req.SceneId +} + func GetEntityType(entityId uint32) int { return int(entityId >> 24) } +func GetDistance(v1 *proto.Vector, v2 *proto.Vector) float32 { + return float32(math.Sqrt( + float64((v1.X-v2.X)*(v1.X-v2.X)) + + float64((v1.Y-v2.Y)*(v1.Y-v2.Y)) + + float64((v1.Z-v2.Z)*(v1.Z-v2.Z)), + )) +} + func (h *Handle) KickPlayer(userId uint32, gateAppId string) { h.messageQueue.SendToGate(gateAppId, &mq.NetMsg{ MsgType: mq.MsgTypeConnCtrl, diff --git a/gate/net/session.go b/gate/net/session.go index c7a7a973..0b285725 100644 --- a/gate/net/session.go +++ b/gate/net/session.go @@ -124,21 +124,27 @@ func (k *KcpConnectManager) recvMsgHandle(protoMsg *ProtoMsg, session *Session) k.serverCmdProtoMap.PutProtoObjCache(protoMsg.CmdId, protoMsg.PayloadMessage) gameMsg.PayloadMessageData = payloadMessageData // 转发到寻路服务器 - if session.pathfindingServerAppId != "" && (protoMsg.CmdId == cmd.QueryPathReq || protoMsg.CmdId == cmd.ObstacleModifyNotify) { - k.messageQueue.SendToPathfinding(session.pathfindingServerAppId, &mq.NetMsg{ - MsgType: mq.MsgTypeGame, - EventId: mq.NormalMsg, - GameMsg: gameMsg, - }) - return + if session.pathfindingServerAppId != "" { + if protoMsg.CmdId == cmd.QueryPathReq || + protoMsg.CmdId == cmd.ObstacleModifyNotify { + k.messageQueue.SendToPathfinding(session.pathfindingServerAppId, &mq.NetMsg{ + MsgType: mq.MsgTypeGame, + EventId: mq.NormalMsg, + GameMsg: gameMsg, + }) + return + } } // 转发到反作弊服务器 - if session.anticheatServerAppId != "" && protoMsg.CmdId == cmd.CombatInvocationsNotify { - k.messageQueue.SendToAnticheat(session.anticheatServerAppId, &mq.NetMsg{ - MsgType: mq.MsgTypeGame, - EventId: mq.NormalMsg, - GameMsg: gameMsg, - }) + if session.anticheatServerAppId != "" { + if protoMsg.CmdId == cmd.CombatInvocationsNotify || + protoMsg.CmdId == cmd.ToTheMoonEnterSceneReq { + k.messageQueue.SendToAnticheat(session.anticheatServerAppId, &mq.NetMsg{ + MsgType: mq.MsgTypeGame, + EventId: mq.NormalMsg, + GameMsg: gameMsg, + }) + } } // 转发到GS k.messageQueue.SendToGs(session.gsServerAppId, &mq.NetMsg{ diff --git a/gdconf/chest_drop_data.go b/gdconf/chest_drop_data.go index 12fb607c..68bff083 100644 --- a/gdconf/chest_drop_data.go +++ b/gdconf/chest_drop_data.go @@ -6,7 +6,7 @@ import ( // ChestDropData 宝箱掉落配置表 type ChestDropData struct { - Level int32 `csv:"最小等级"` + MinLevel int32 `csv:"最小等级"` DropTag string `csv:"总索引"` DropId int32 `csv:"掉落ID,omitempty"` DropCount int32 `csv:"掉落次数,omitempty"` @@ -21,7 +21,7 @@ func (g *GameDataConfig) loadChestDropData() { if !exist { g.ChestDropDataMap[chestDropData.DropTag] = make(map[int32]*ChestDropData) } - g.ChestDropDataMap[chestDropData.DropTag][chestDropData.Level] = chestDropData + g.ChestDropDataMap[chestDropData.DropTag][chestDropData.MinLevel] = chestDropData } logger.Info("ChestDropData count: %v", len(g.ChestDropDataMap)) } @@ -31,7 +31,16 @@ func GetChestDropDataByDropTagAndLevel(dropTag string, level int32) *ChestDropDa if !exist { return nil } - return value[level] + resultLevel := int32(0) + for minLevel := range value { + if level < minLevel { + continue + } + if minLevel > resultLevel { + resultLevel = minLevel + } + } + return value[resultLevel] } func GetChestDropDataMap() map[string]map[int32]*ChestDropData { diff --git a/gdconf/monster_drop_data.go b/gdconf/monster_drop_data.go index abb9f8ee..c5262347 100644 --- a/gdconf/monster_drop_data.go +++ b/gdconf/monster_drop_data.go @@ -6,7 +6,7 @@ import ( // MonsterDropData 怪物掉落配置表 type MonsterDropData struct { - Level int32 `csv:"最小等级"` + MinLevel int32 `csv:"最小等级"` DropTag string `csv:"总索引"` DropId int32 `csv:"掉落ID,omitempty"` DropCount int32 `csv:"掉落次数,omitempty"` @@ -21,7 +21,7 @@ func (g *GameDataConfig) loadMonsterDropData() { if !exist { g.MonsterDropDataMap[monsterDropData.DropTag] = make(map[int32]*MonsterDropData) } - g.MonsterDropDataMap[monsterDropData.DropTag][monsterDropData.Level] = monsterDropData + g.MonsterDropDataMap[monsterDropData.DropTag][monsterDropData.MinLevel] = monsterDropData } logger.Info("MonsterDropData count: %v", len(g.MonsterDropDataMap)) } @@ -31,7 +31,16 @@ func GetMonsterDropDataByDropTagAndLevel(dropTag string, level int32) *MonsterDr if !exist { return nil } - return value[level] + resultLevel := int32(0) + for minLevel := range value { + if level < minLevel { + continue + } + if minLevel > resultLevel { + resultLevel = minLevel + } + } + return value[resultLevel] } func GetMonsterDropDataMap() map[string]map[int32]*MonsterDropData { diff --git a/gs/game/game_world_scene.go b/gs/game/game_world_scene.go index de4dfba0..ed784c03 100644 --- a/gs/game/game_world_scene.go +++ b/gs/game/game_world_scene.go @@ -432,9 +432,9 @@ func getTempFightPropMap() map[uint32]float32 { constant.FIGHT_PROP_CUR_ATTACK: float32(50.0), constant.FIGHT_PROP_BASE_DEFENSE: float32(500.0), constant.FIGHT_PROP_CUR_DEFENSE: float32(500.0), - constant.FIGHT_PROP_BASE_HP: float32(100000.0), - constant.FIGHT_PROP_CUR_HP: float32(100000.0), - constant.FIGHT_PROP_MAX_HP: float32(100000.0), + constant.FIGHT_PROP_BASE_HP: float32(10000.0), + constant.FIGHT_PROP_CUR_HP: float32(10000.0), + constant.FIGHT_PROP_MAX_HP: float32(10000.0), constant.FIGHT_PROP_PHYSICAL_SUB_HURT: float32(0.1), constant.FIGHT_PROP_ICE_SUB_HURT: float32(0.1), constant.FIGHT_PROP_FIRE_SUB_HURT: float32(0.1), diff --git a/gs/game/player_scene.go b/gs/game/player_scene.go index 12d840e0..66d5b90f 100644 --- a/gs/game/player_scene.go +++ b/gs/game/player_scene.go @@ -553,27 +553,7 @@ func (g *Game) KillEntity(player *model.Player, scene *Scene, entityId uint32, d entity.fightProp[constant.FIGHT_PROP_CUR_HP] = 0 g.EntityFightPropUpdateNotifyBroadcast(scene.world, entity) // 随机掉落 - sceneGroupConfig := gdconf.GetSceneGroup(int32(entity.GetGroupId())) - monsterConfig := sceneGroupConfig.MonsterMap[int32(entity.GetConfigId())] - monsterDropDataConfig := gdconf.GetMonsterDropDataByDropTagAndLevel(monsterConfig.DropTag, monsterConfig.Level) - if monsterDropDataConfig == nil { - logger.Error("get monster drop data config is nil, monsterConfig: %v, uid: %v", monsterConfig, player.PlayerID) - return - } - dropDataConfig := gdconf.GetDropDataById(monsterDropDataConfig.DropId) - if dropDataConfig == nil { - logger.Error("get drop data config is nil, dropId: %v, uid: %v", monsterDropDataConfig.DropId, player.PlayerID) - return - } - totalItemMap := g.doRandDropFullTimes(dropDataConfig, int(monsterDropDataConfig.DropCount)) - for itemId, count := range totalItemMap { - itemDataConfig := gdconf.GetItemDataById(int32(itemId)) - if itemDataConfig == nil { - logger.Error("get item data config is nil, itemId: %v, uid: %v", itemId, player.PlayerID) - continue - } - g.CreateDropGadget(player, entity.pos, uint32(itemDataConfig.GadgetId), itemId, count) - } + g.monsterDrop(player, entity) } entity.lifeState = constant.LIFE_STATE_DEAD ntf := &proto.LifeStateChangeNotify{ @@ -588,7 +568,6 @@ func (g *Game) KillEntity(player *model.Player, scene *Scene, entityId uint32, d scene.DestroyEntity(entity.GetId()) group := scene.GetGroupById(entity.groupId) if group == nil { - logger.Error("get scene group is nil, groupId: %v, uid: %v", entity.groupId, player.PlayerID) return } group.DestroyEntity(entity.GetId()) diff --git a/gs/game/player_world.go b/gs/game/player_world.go index 5c2c290b..50e8aa5a 100644 --- a/gs/game/player_world.go +++ b/gs/game/player_world.go @@ -337,27 +337,7 @@ func (g *Game) GadgetInteractReq(player *model.Player, payloadMsg pb.Message) { // 宝箱交互结束 开启宝箱 if req.OpType == proto.InterOpType_INTER_OP_FINISH { // 随机掉落 - sceneGroupConfig := gdconf.GetSceneGroup(int32(entity.GetGroupId())) - gadgetConfig := sceneGroupConfig.GadgetMap[int32(entity.GetConfigId())] - chestDropDataConfig := gdconf.GetChestDropDataByDropTagAndLevel(gadgetConfig.DropTag, gadgetConfig.Level) - if chestDropDataConfig == nil { - logger.Error("get chest drop data config is nil, gadgetConfig: %v, uid: %v", gadgetConfig, player.PlayerID) - return - } - dropDataConfig := gdconf.GetDropDataById(chestDropDataConfig.DropId) - if dropDataConfig == nil { - logger.Error("get drop data config is nil, dropId: %v, uid: %v", chestDropDataConfig.DropId, player.PlayerID) - return - } - totalItemMap := g.doRandDropFullTimes(dropDataConfig, int(chestDropDataConfig.DropCount)) - for itemId, count := range totalItemMap { - itemDataConfig := gdconf.GetItemDataById(int32(itemId)) - if itemDataConfig == nil { - logger.Error("get item data config is nil, itemId: %v, uid: %v", itemId, player.PlayerID) - continue - } - g.CreateDropGadget(player, entity.pos, uint32(itemDataConfig.GadgetId), itemId, count) - } + g.chestDrop(player, entity) // 更新宝箱状态 g.SendMsg(cmd.WorldChestOpenNotify, player.PlayerID, player.ClientSeq, &proto.WorldChestOpenNotify{ GroupId: entity.GetGroupId(), @@ -378,6 +358,54 @@ func (g *Game) GadgetInteractReq(player *model.Player, payloadMsg pb.Message) { g.SendMsg(cmd.GadgetInteractRsp, player.PlayerID, player.ClientSeq, rsp) } +func (g *Game) monsterDrop(player *model.Player, entity *Entity) { + sceneGroupConfig := gdconf.GetSceneGroup(int32(entity.GetGroupId())) + monsterConfig := sceneGroupConfig.MonsterMap[int32(entity.GetConfigId())] + monsterDropDataConfig := gdconf.GetMonsterDropDataByDropTagAndLevel(monsterConfig.DropTag, monsterConfig.Level) + if monsterDropDataConfig == nil { + logger.Error("get monster drop data config is nil, monsterConfig: %v, uid: %v", monsterConfig, player.PlayerID) + return + } + dropDataConfig := gdconf.GetDropDataById(monsterDropDataConfig.DropId) + if dropDataConfig == nil { + logger.Error("get drop data config is nil, dropId: %v, uid: %v", monsterDropDataConfig.DropId, player.PlayerID) + return + } + totalItemMap := g.doRandDropFullTimes(dropDataConfig, int(monsterDropDataConfig.DropCount)) + for itemId, count := range totalItemMap { + itemDataConfig := gdconf.GetItemDataById(int32(itemId)) + if itemDataConfig == nil { + logger.Error("get item data config is nil, itemId: %v, uid: %v", itemId, player.PlayerID) + continue + } + g.CreateDropGadget(player, entity.pos, uint32(itemDataConfig.GadgetId), itemId, count) + } +} + +func (g *Game) chestDrop(player *model.Player, entity *Entity) { + sceneGroupConfig := gdconf.GetSceneGroup(int32(entity.GetGroupId())) + gadgetConfig := sceneGroupConfig.GadgetMap[int32(entity.GetConfigId())] + chestDropDataConfig := gdconf.GetChestDropDataByDropTagAndLevel(gadgetConfig.DropTag, gadgetConfig.Level) + if chestDropDataConfig == nil { + logger.Error("get chest drop data config is nil, gadgetConfig: %v, uid: %v", gadgetConfig, player.PlayerID) + return + } + dropDataConfig := gdconf.GetDropDataById(chestDropDataConfig.DropId) + if dropDataConfig == nil { + logger.Error("get drop data config is nil, dropId: %v, uid: %v", chestDropDataConfig.DropId, player.PlayerID) + return + } + totalItemMap := g.doRandDropFullTimes(dropDataConfig, int(chestDropDataConfig.DropCount)) + for itemId, count := range totalItemMap { + itemDataConfig := gdconf.GetItemDataById(int32(itemId)) + if itemDataConfig == nil { + logger.Error("get item data config is nil, itemId: %v, uid: %v", itemId, player.PlayerID) + continue + } + g.CreateDropGadget(player, entity.pos, uint32(itemDataConfig.GadgetId), itemId, count) + } +} + func (g *Game) doRandDropFullTimes(dropDataConfig *gdconf.DropData, times int) map[uint32]uint32 { totalItemMap := make(map[uint32]uint32) for i := 0; i < times; i++ {