From c4bc4b8ca1bee3c75d41d3d5169d4ccacde38af3 Mon Sep 17 00:00:00 2001 From: huangxiaolei <1782360262@qq.com> Date: Wed, 21 Dec 2022 17:42:56 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AF=BB=E8=B7=AF=E6=9C=8D=E5=8A=A1=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 9 +- cmd/hk4e/pathfinding.go | 23 +++ cmd/pathfinding/application.toml | 8 + cmd/pathfinding/main.go | 25 +++ common/mq/topic.go | 12 +- gate/net/forward.go | 38 ++-- gs/config/game_data_config.go | 39 ----- gs/game/game_manager.go | 1 - gs/game/route_manager.go | 1 - gs/game/tick_manager.go | 24 +-- gs/game/user_common_handler.go | 7 + gs/game/user_map.go | 20 +-- gs/game/world_manager.go | 11 +- gs/game/world_static.go | 163 ------------------ pathfinding/app/app.go | 47 +++++ pathfinding/handle/handle.go | 70 ++++++++ pathfinding/handle/query_path.go | 74 ++++++++ .../pfalg}/bfs_pathfinding.go | 18 +- pathfinding/pfalg/mesh_vector.go | 7 + pathfinding/world/world_static.go | 88 ++++++++++ pkg/alg/common_pathfinding.go | 7 - protocol/cmd/cmd_id_proto_obj_map.go | 2 +- 22 files changed, 419 insertions(+), 275 deletions(-) create mode 100644 cmd/hk4e/pathfinding.go create mode 100644 cmd/pathfinding/application.toml create mode 100644 cmd/pathfinding/main.go delete mode 100644 gs/game/world_static.go create mode 100644 pathfinding/app/app.go create mode 100644 pathfinding/handle/handle.go create mode 100644 pathfinding/handle/query_path.go rename {pkg/alg => pathfinding/pfalg}/bfs_pathfinding.go (90%) create mode 100644 pathfinding/pfalg/mesh_vector.go create mode 100644 pathfinding/world/world_static.go delete mode 100644 pkg/alg/common_pathfinding.go diff --git a/README.md b/README.md index 5c7b609b..e1adb0e2 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,9 @@ hk4e game server * mongodb * nats-server -1. 启动dispatch `cmd/dispatch && go run .` -1. 启动gate `cd cmd/gate && go run .` -1. 启动gs `cd cmd/gs && go run .` +1. 启动http登录服务器 `cmd/dispatch && go run .` +2. 启动网关服务器 `cd cmd/gate && go run .` +3. 启动游戏服务器 `cd cmd/gs && go run .` +4. 启动游戏管理服务器 `cmd/gm && go run .` +5. 启动战斗服务器 `cmd/fight && go run .` +6. 启动寻路服务器 `cmd/pathfinding && go run .` diff --git a/cmd/hk4e/pathfinding.go b/cmd/hk4e/pathfinding.go new file mode 100644 index 00000000..dd8f97da --- /dev/null +++ b/cmd/hk4e/pathfinding.go @@ -0,0 +1,23 @@ +package main + +import ( + "context" + + "hk4e/pathfinding/app" + + "github.com/spf13/cobra" +) + +// PathfindingCmd 检查配表命令 +func PathfindingCmd() *cobra.Command { + var cfg string + c := &cobra.Command{ + Use: "pathfinding", + Short: "pathfinding server", + RunE: func(cmd *cobra.Command, args []string) error { + return app.Run(context.Background(), cfg) + }, + } + c.Flags().StringVar(&cfg, "config", "application.toml", "config file") + return c +} diff --git a/cmd/pathfinding/application.toml b/cmd/pathfinding/application.toml new file mode 100644 index 00000000..c4df3002 --- /dev/null +++ b/cmd/pathfinding/application.toml @@ -0,0 +1,8 @@ +[logger] +level = "DEBUG" +mode = "BOTH" +track = true +max_size = 10485760 + +[mq] +nats_url = "nats://nats:4222" diff --git a/cmd/pathfinding/main.go b/cmd/pathfinding/main.go new file mode 100644 index 00000000..005cf18f --- /dev/null +++ b/cmd/pathfinding/main.go @@ -0,0 +1,25 @@ +package main + +import ( + "context" + "flag" + "fmt" + _ "net/http/pprof" + "os" + + "hk4e/pathfinding/app" +) + +var ( + config = flag.String("config", "application.toml", "config file") +) + +func main() { + flag.Parse() + // go statsviz_serve.Serve("0.0.0.0:2345") + err := app.Run(context.TODO(), *config) + if err != nil { + fmt.Println(err) + os.Exit(1) + } +} diff --git a/common/mq/topic.go b/common/mq/topic.go index 37e349d8..2e6d7088 100644 --- a/common/mq/topic.go +++ b/common/mq/topic.go @@ -5,9 +5,10 @@ import ( ) const ( - GATE = "GATE_${APPID}_HK4E" - GS = "GS_${APPID}_HK4E" - FIGHT = "FIGHT_${APPID}_HK4E" + GATE = "GATE_${APPID}_HK4E" + GS = "GS_${APPID}_HK4E" + FIGHT = "FIGHT_${APPID}_HK4E" + PATHFINDING = "PATHFINDING_${APPID}_HK4E" ) func (m *MessageQueue) getTopic(serverType string, appId string) string { @@ -29,3 +30,8 @@ func (m *MessageQueue) SendToFight(appId string, netMsg *NetMsg) { netMsg.Topic = m.getTopic(FIGHT, appId) m.netMsgInput <- netMsg } + +func (m *MessageQueue) SendToPathfinding(appId string, netMsg *NetMsg) { + netMsg.Topic = m.getTopic(PATHFINDING, appId) + m.netMsgInput <- netMsg +} diff --git a/gate/net/forward.go b/gate/net/forward.go index 0a68d52e..35c5445f 100644 --- a/gate/net/forward.go +++ b/gate/net/forward.go @@ -104,18 +104,21 @@ func (k *KcpConnectManager) recvMsgHandle(protoMsg *ProtoMsg, session *Session) logger.Error("conn not active so drop packet, cmdId: %v, userId: %v, convId: %v", protoMsg.CmdId, userId, protoMsg.ConvId) return } - // 转发到GS - gameMsg := new(mq.GameMsg) - gameMsg.UserId = userId - gameMsg.CmdId = protoMsg.CmdId - gameMsg.ClientSeq = protoMsg.HeadMessage.ClientSequenceId - gameMsg.PayloadMessage = protoMsg.PayloadMessage - k.messageQueue.SendToGs("1", &mq.NetMsg{ - MsgType: mq.MsgTypeGame, - EventId: mq.NormalMsg, - GameMsg: gameMsg, - }) - // 转发到FIGHT + // 只转发到寻路服务器 + if protoMsg.CmdId == cmd.QueryPathReq || protoMsg.CmdId == cmd.ObstacleModifyNotify { + gameMsg := new(mq.GameMsg) + gameMsg.UserId = userId + gameMsg.CmdId = protoMsg.CmdId + gameMsg.ClientSeq = protoMsg.HeadMessage.ClientSequenceId + gameMsg.PayloadMessage = protoMsg.PayloadMessage + k.messageQueue.SendToPathfinding("1", &mq.NetMsg{ + MsgType: mq.MsgTypeGame, + EventId: mq.NormalMsg, + GameMsg: gameMsg, + }) + return + } + // 同时转发到战斗服务器 if protoMsg.CmdId == cmd.CombatInvocationsNotify { gameMsg := new(mq.GameMsg) gameMsg.UserId = userId @@ -128,6 +131,17 @@ func (k *KcpConnectManager) recvMsgHandle(protoMsg *ProtoMsg, session *Session) GameMsg: gameMsg, }) } + // 转发到GS + gameMsg := new(mq.GameMsg) + gameMsg.UserId = userId + gameMsg.CmdId = protoMsg.CmdId + gameMsg.ClientSeq = protoMsg.HeadMessage.ClientSequenceId + gameMsg.PayloadMessage = protoMsg.PayloadMessage + k.messageQueue.SendToGs("1", &mq.NetMsg{ + MsgType: mq.MsgTypeGame, + EventId: mq.NormalMsg, + GameMsg: gameMsg, + }) } } diff --git a/gs/config/game_data_config.go b/gs/config/game_data_config.go index ab1b7e76..93dcc87b 100644 --- a/gs/config/game_data_config.go +++ b/gs/config/game_data_config.go @@ -95,42 +95,3 @@ func (g *GameDataConfig) loadAll() { g.excelBinPrefix += "/" g.load() } - -func (g *GameDataConfig) ReadWorldTerrain() []byte { - resourcePath := g.getResourcePathPrefix() - dirInfo, err := os.Stat(resourcePath) - if err != nil || !dirInfo.IsDir() { - logger.Error("open game data config dir error: %v", err) - return nil - } - dirInfo, err = os.Stat(resourcePath + "/WorldStatic") - if err != nil || !dirInfo.IsDir() { - logger.Error("open game data world static dir error: %v", err) - return nil - } - data, err := os.ReadFile(resourcePath + "/WorldStatic/world_terrain.bin") - if err != nil { - logger.Error("read world terrain file error: %v", err) - return nil - } - return data -} - -func (g *GameDataConfig) WriteWorldTerrain(data []byte) { - resourcePath := g.getResourcePathPrefix() - dirInfo, err := os.Stat(resourcePath) - if err != nil || !dirInfo.IsDir() { - logger.Error("open game data config dir error: %v", err) - return - } - dirInfo, err = os.Stat(resourcePath + "/WorldStatic") - if err != nil || !dirInfo.IsDir() { - logger.Error("open game data world static dir error: %v", err) - return - } - err = os.WriteFile(resourcePath+"/WorldStatic/world_terrain.bin", data, 0644) - if err != nil { - logger.Error("write world terrain file error: %v", err) - return - } -} diff --git a/gs/game/game_manager.go b/gs/game/game_manager.go index f2442982..059c9b86 100644 --- a/gs/game/game_manager.go +++ b/gs/game/game_manager.go @@ -140,7 +140,6 @@ func (g *GameManager) Stop() { EventId: RunUserCopyAndSave, } time.Sleep(time.Second * 5) - // g.worldManager.worldStatic.SaveTerrain() } // SendMsg 发送消息给客户端 diff --git a/gs/game/route_manager.go b/gs/game/route_manager.go index 2713f973..48387a17 100644 --- a/gs/game/route_manager.go +++ b/gs/game/route_manager.go @@ -66,7 +66,6 @@ func (r *RouteManager) InitRoute() { r.registerRouter(cmd.ChooseCurAvatarTeamReq, GAME_MANAGER.ChooseCurAvatarTeamReq) r.registerRouter(cmd.GetGachaInfoReq, GAME_MANAGER.GetGachaInfoReq) r.registerRouter(cmd.DoGachaReq, GAME_MANAGER.DoGachaReq) - r.registerRouter(cmd.QueryPathReq, GAME_MANAGER.QueryPathReq) r.registerRouter(cmd.CombatInvocationsNotify, GAME_MANAGER.CombatInvocationsNotify) r.registerRouter(cmd.AbilityInvocationsNotify, GAME_MANAGER.AbilityInvocationsNotify) r.registerRouter(cmd.ClientAbilityInitFinishNotify, GAME_MANAGER.ClientAbilityInitFinishNotify) diff --git a/gs/game/tick_manager.go b/gs/game/tick_manager.go index d17e6d14..131b56bc 100644 --- a/gs/game/tick_manager.go +++ b/gs/game/tick_manager.go @@ -209,19 +209,19 @@ func (t *TickManager) onTickSecond(now int64) { worldPlayerRTTNotify.PlayerRttList = append(worldPlayerRTTNotify.PlayerRttList, playerRTTInfo) } GAME_MANAGER.SendMsg(cmd.WorldPlayerRTTNotify, player.PlayerID, 0, worldPlayerRTTNotify) - } - if !world.IsBigWorld() && world.owner.SceneLoadState == model.SceneEnterDone { // 刷怪 - scene := world.GetSceneById(3) - monsterEntityCount := 0 - for _, entity := range scene.entityMap { - if entity.entityType == uint32(proto.ProtEntityType_PROT_ENTITY_TYPE_MONSTER) { - monsterEntityCount++ + if !world.IsBigWorld() && world.owner.SceneLoadState == model.SceneEnterDone { + scene := world.GetSceneById(3) + monsterEntityCount := 0 + for _, entity := range scene.entityMap { + if entity.entityType == uint32(proto.ProtEntityType_PROT_ENTITY_TYPE_MONSTER) { + monsterEntityCount++ + } + } + if monsterEntityCount < 30 { + monsterEntityId := t.createMonster(world.owner, scene) + GAME_MANAGER.AddSceneEntityNotify(world.owner, proto.VisionType_VISION_TYPE_BORN, []uint32{monsterEntityId}, true, false) } - } - if monsterEntityCount < 30 { - monsterEntityId := t.createMonster(scene) - GAME_MANAGER.AddSceneEntityNotify(world.owner, proto.VisionType_VISION_TYPE_BORN, []uint32{monsterEntityId}, true, false) } } } @@ -241,7 +241,7 @@ func (t *TickManager) onTick200MilliSecond(now int64) { func (t *TickManager) onTick100MilliSecond(now int64) { } -func (t *TickManager) createMonster(scene *Scene) uint32 { +func (t *TickManager) createMonster(player *model.Player, scene *Scene) uint32 { pos := &model.Vector{ X: 2747, Y: 194, diff --git a/gs/game/user_common_handler.go b/gs/game/user_common_handler.go index 1e17f564..9bf514de 100644 --- a/gs/game/user_common_handler.go +++ b/gs/game/user_common_handler.go @@ -108,6 +108,13 @@ func (g *GameManager) ToTheMoonEnterSceneReq(player *model.Player, payloadMsg pb g.SendMsg(cmd.ToTheMoonEnterSceneRsp, player.PlayerID, player.ClientSeq, new(proto.ToTheMoonEnterSceneRsp)) } +func (g *GameManager) PathfindingEnterSceneReq(player *model.Player, payloadMsg pb.Message) { + logger.Debug("user pf enter scene, uid: %v", player.PlayerID) + req := payloadMsg.(*proto.PathfindingEnterSceneReq) + _ = req + g.SendMsg(cmd.PathfindingEnterSceneRsp, player.PlayerID, player.ClientSeq, new(proto.PathfindingEnterSceneRsp)) +} + func (g *GameManager) SetEntityClientDataNotify(player *model.Player, payloadMsg pb.Message) { logger.Debug("user set entity client data, uid: %v", player.PlayerID) ntf := payloadMsg.(*proto.SetEntityClientDataNotify) diff --git a/gs/game/user_map.go b/gs/game/user_map.go index 66c520fd..348683e8 100644 --- a/gs/game/user_map.go +++ b/gs/game/user_map.go @@ -1,9 +1,10 @@ package game import ( - "hk4e/gs/constant" "strconv" + "hk4e/gs/constant" + gdc "hk4e/gs/config" "hk4e/gs/model" "hk4e/pkg/logger" @@ -108,23 +109,6 @@ func (g *GameManager) TeleportPlayer(player *model.Player, enterReason uint32, s g.SendMsg(cmd.PlayerEnterSceneNotify, player.PlayerID, player.ClientSeq, playerEnterSceneNotify) } -func (g *GameManager) PathfindingEnterSceneReq(player *model.Player, payloadMsg pb.Message) { - logger.Debug("user pathfinding enter scene, uid: %v", player.PlayerID) - g.SendMsg(cmd.PathfindingEnterSceneRsp, player.PlayerID, player.ClientSeq, new(proto.PathfindingEnterSceneRsp)) -} - -func (g *GameManager) QueryPathReq(player *model.Player, payloadMsg pb.Message) { - // logger.Debug("user query path, uid: %v", player.PlayerID) - req := payloadMsg.(*proto.QueryPathReq) - - queryPathRsp := &proto.QueryPathRsp{ - Corners: []*proto.Vector{req.DestinationPos[0]}, - QueryId: req.QueryId, - QueryStatus: proto.QueryPathRsp_PATH_STATUS_TYPE_SUCC, - } - g.SendMsg(cmd.QueryPathRsp, player.PlayerID, player.ClientSeq, queryPathRsp) -} - func (g *GameManager) GetScenePointReq(player *model.Player, payloadMsg pb.Message) { logger.Debug("user get scene point, uid: %v", player.PlayerID) req := payloadMsg.(*proto.GetScenePointReq) diff --git a/gs/game/world_manager.go b/gs/game/world_manager.go index f713ba9a..7aa39ecf 100644 --- a/gs/game/world_manager.go +++ b/gs/game/world_manager.go @@ -18,20 +18,15 @@ import ( // 世界管理器 type WorldManager struct { - worldMap map[uint32]*World - snowflake *alg.SnowflakeWorker - worldStatic *WorldStatic - bigWorld *World + worldMap map[uint32]*World + snowflake *alg.SnowflakeWorker + bigWorld *World } func NewWorldManager(snowflake *alg.SnowflakeWorker) (r *WorldManager) { r = new(WorldManager) r.worldMap = make(map[uint32]*World) r.snowflake = snowflake - r.worldStatic = NewWorldStatic() - r.worldStatic.InitTerrain() - // r.worldStatic.Pathfinding() - // r.worldStatic.ConvPathVectorListToAiMoveVectorList() return r } diff --git a/gs/game/world_static.go b/gs/game/world_static.go deleted file mode 100644 index e1a5bc0f..00000000 --- a/gs/game/world_static.go +++ /dev/null @@ -1,163 +0,0 @@ -package game - -import ( - "bytes" - "encoding/gob" - "unsafe" - - gdc "hk4e/gs/config" - "hk4e/gs/model" - "hk4e/pkg/alg" - "hk4e/pkg/logger" -) - -// 世界的静态资源坐标点数据 - -type MeshMapPos struct { - X int16 - Y int16 - Z int16 -} - -type WorldStatic struct { - // x y z -> if terrain exist - terrain map[MeshMapPos]bool - // x y z -> gather id - gather map[MeshMapPos]uint32 - pathfindingStartPos MeshMapPos - pathfindingEndPos MeshMapPos - pathVectorList []MeshMapPos - aiMoveMeshSpeedParam int - aiMoveVectorList []*model.Vector - aiMoveCurrIndex int -} - -func NewWorldStatic() (r *WorldStatic) { - r = new(WorldStatic) - r.terrain = make(map[MeshMapPos]bool) - r.gather = make(map[MeshMapPos]uint32) - r.InitGather() - r.pathfindingStartPos = MeshMapPos{ - X: 2747, - Y: 194, - Z: -1719, - } - r.pathfindingEndPos = MeshMapPos{ - X: 2588, - Y: 211, - Z: -1349, - } - r.pathVectorList = make([]MeshMapPos, 0) - r.aiMoveMeshSpeedParam = 3 - r.aiMoveVectorList = make([]*model.Vector, 0) - r.aiMoveCurrIndex = 0 - return r -} - -func (w *WorldStatic) InitTerrain() bool { - data := gdc.CONF.ReadWorldTerrain() - decoder := gob.NewDecoder(bytes.NewReader(data)) - err := decoder.Decode(&w.terrain) - if err != nil { - logger.Error("unmarshal world terrain data error: %v", err) - return false - } - return true -} - -func (w *WorldStatic) SaveTerrain() bool { - var buffer bytes.Buffer - encoder := gob.NewEncoder(&buffer) - err := encoder.Encode(w.terrain) - if err != nil { - logger.Error("marshal world terrain data error: %v", err) - return false - } - gdc.CONF.WriteWorldTerrain(buffer.Bytes()) - return true -} - -func (w *WorldStatic) GetTerrain(x int16, y int16, z int16) (exist bool) { - pos := MeshMapPos{ - X: x, - Y: y, - Z: z, - } - exist = w.terrain[pos] - return exist -} - -func (w *WorldStatic) SetTerrain(x int16, y int16, z int16) { - pos := MeshMapPos{ - X: x, - Y: y, - Z: z, - } - w.terrain[pos] = true -} - -func (w *WorldStatic) InitGather() { -} - -func (w *WorldStatic) GetGather(x int16, y int16, z int16) (gatherId uint32, exist bool) { - pos := MeshMapPos{ - X: x, - Y: y, - Z: z, - } - gatherId, exist = w.gather[pos] - return gatherId, exist -} - -func (w *WorldStatic) SetGather(x int16, y int16, z int16, gatherId uint32) { - pos := MeshMapPos{ - X: x, - Y: y, - Z: z, - } - w.gather[pos] = gatherId -} - -func (w *WorldStatic) ConvWSTMapToPFMap() map[alg.MeshMapPos]bool { - return *(*map[alg.MeshMapPos]bool)(unsafe.Pointer(&w.terrain)) -} - -func (w *WorldStatic) ConvWSPosToPFPos(v MeshMapPos) alg.MeshMapPos { - return alg.MeshMapPos(v) -} - -func (w *WorldStatic) ConvPFPVLToWSPVL(v []alg.MeshMapPos) []MeshMapPos { - return *(*[]MeshMapPos)(unsafe.Pointer(&v)) -} - -func (w *WorldStatic) Pathfinding() { - bfs := alg.NewBFS() - bfs.InitMap( - w.ConvWSTMapToPFMap(), - w.ConvWSPosToPFPos(w.pathfindingStartPos), - w.ConvWSPosToPFPos(w.pathfindingEndPos), - 100, - ) - pathVectorList := bfs.Pathfinding() - if pathVectorList == nil { - logger.Error("could not find path") - return - } - logger.Debug("find path success, path: %v", pathVectorList) - w.pathVectorList = w.ConvPFPVLToWSPVL(pathVectorList) -} - -func (w *WorldStatic) ConvPathVectorListToAiMoveVectorList() { - for index, currPathVector := range w.pathVectorList { - if index > 0 { - lastPathVector := w.pathVectorList[index-1] - for i := 0; i < w.aiMoveMeshSpeedParam; i++ { - w.aiMoveVectorList = append(w.aiMoveVectorList, &model.Vector{ - X: float64(lastPathVector.X) + float64(currPathVector.X-lastPathVector.X)/float64(w.aiMoveMeshSpeedParam)*float64(i), - Y: float64(lastPathVector.Y) + float64(currPathVector.Y-lastPathVector.Y)/float64(w.aiMoveMeshSpeedParam)*float64(i), - Z: float64(lastPathVector.Z) + float64(currPathVector.Z-lastPathVector.Z)/float64(w.aiMoveMeshSpeedParam)*float64(i), - }) - } - } - } -} diff --git a/pathfinding/app/app.go b/pathfinding/app/app.go new file mode 100644 index 00000000..acf6362e --- /dev/null +++ b/pathfinding/app/app.go @@ -0,0 +1,47 @@ +package app + +import ( + "context" + _ "net/http/pprof" + "os" + "os/signal" + "syscall" + "time" + + "hk4e/common/config" + "hk4e/common/mq" + "hk4e/pathfinding/handle" + "hk4e/pkg/logger" +) + +func Run(ctx context.Context, configFile string) error { + config.InitConfig(configFile) + + logger.InitLogger("pathfinding") + logger.Warn("pathfinding start") + + messageQueue := mq.NewMessageQueue(mq.PATHFINDING, "1") + defer messageQueue.Close() + + _ = handle.NewHandle(messageQueue) + + c := make(chan os.Signal, 1) + signal.Notify(c, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT) + for { + select { + case <-ctx.Done(): + return nil + case s := <-c: + logger.Warn("get a signal %s", s.String()) + switch s { + case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT: + logger.Warn("pathfinding exit") + time.Sleep(time.Second) + return nil + case syscall.SIGHUP: + default: + return nil + } + } + } +} diff --git a/pathfinding/handle/handle.go b/pathfinding/handle/handle.go new file mode 100644 index 00000000..de0b8af4 --- /dev/null +++ b/pathfinding/handle/handle.go @@ -0,0 +1,70 @@ +package handle + +import ( + "hk4e/common/mq" + "hk4e/pathfinding/world" + "hk4e/pkg/logger" + "hk4e/protocol/cmd" + + pb "google.golang.org/protobuf/proto" +) + +type Handle struct { + worldStatic *world.WorldStatic + messageQueue *mq.MessageQueue +} + +func NewHandle(messageQueue *mq.MessageQueue) (r *Handle) { + r = new(Handle) + r.worldStatic = world.NewWorldStatic() + r.worldStatic.InitTerrain() + r.messageQueue = messageQueue + go r.run() + return r +} + +func (h *Handle) run() { + for i := 0; i < 4; i++ { + go func() { + for { + netMsg := <-h.messageQueue.GetNetMsg() + if netMsg.MsgType != mq.MsgTypeGame { + continue + } + if netMsg.EventId != mq.NormalMsg { + continue + } + gameMsg := netMsg.GameMsg + switch gameMsg.CmdId { + case cmd.QueryPathReq: + h.QueryPath(gameMsg.UserId, gameMsg.PayloadMessage) + case cmd.ObstacleModifyNotify: + h.ObstacleModifyNotify(gameMsg.UserId, gameMsg.PayloadMessage) + } + } + }() + } +} + +// SendMsg 发送消息给客户端 +func (h *Handle) SendMsg(cmdId uint16, userId uint32, payloadMsg pb.Message) { + if userId < 100000000 || payloadMsg == nil { + return + } + gameMsg := new(mq.GameMsg) + gameMsg.UserId = userId + gameMsg.CmdId = cmdId + gameMsg.ClientSeq = 0 + // 在这里直接序列化成二进制数据 防止发送的消息内包含各种游戏数据指针 而造成并发读写的问题 + payloadMessageData, err := pb.Marshal(payloadMsg) + if err != nil { + logger.Error("parse payload msg to bin error: %v", err) + return + } + gameMsg.PayloadMessageData = payloadMessageData + h.messageQueue.SendToGate("1", &mq.NetMsg{ + MsgType: mq.MsgTypeGame, + EventId: mq.NormalMsg, + GameMsg: gameMsg, + }) +} diff --git a/pathfinding/handle/query_path.go b/pathfinding/handle/query_path.go new file mode 100644 index 00000000..32d05c4b --- /dev/null +++ b/pathfinding/handle/query_path.go @@ -0,0 +1,74 @@ +package handle + +import ( + "hk4e/pathfinding/pfalg" + "hk4e/pkg/logger" + "hk4e/protocol/cmd" + "hk4e/protocol/proto" + + pb "google.golang.org/protobuf/proto" +) + +func (h *Handle) ConvPbVecToMeshVec(pbVec *proto.Vector) pfalg.MeshVector { + return pfalg.MeshVector{ + X: int16(pbVec.X), + Y: int16(pbVec.Y), + Z: int16(pbVec.Z), + } +} + +func (h *Handle) ConvMeshVecToPbVec(meshVec pfalg.MeshVector) *proto.Vector { + return &proto.Vector{ + X: float32(meshVec.X), + Y: float32(meshVec.Y), + Z: float32(meshVec.Z), + } +} + +func (h *Handle) ConvPbVecListToMeshVecList(pbVecList []*proto.Vector) []pfalg.MeshVector { + ret := make([]pfalg.MeshVector, 0) + for _, pbVec := range pbVecList { + ret = append(ret, h.ConvPbVecToMeshVec(pbVec)) + } + return ret +} + +func (h *Handle) ConvMeshVecListToPbVecList(meshVecList []pfalg.MeshVector) []*proto.Vector { + ret := make([]*proto.Vector, 0) + for _, meshVec := range meshVecList { + ret = append(ret, h.ConvMeshVecToPbVec(meshVec)) + } + return ret +} + +func (h *Handle) QueryPath(userId uint32, payloadMsg pb.Message) { + req := payloadMsg.(*proto.QueryPathReq) + logger.Debug("query path req: %v, uid: %v", req, userId) + var ok = false + var path []pfalg.MeshVector = nil + for _, destinationPos := range req.DestinationPos { + ok, path = h.worldStatic.Pathfinding(h.ConvPbVecToMeshVec(req.SourcePos), h.ConvPbVecToMeshVec(destinationPos)) + if ok { + break + } + } + if !ok { + queryPathRsp := &proto.QueryPathRsp{ + QueryId: req.QueryId, + QueryStatus: proto.QueryPathRsp_PATH_STATUS_TYPE_FAIL, + } + h.SendMsg(cmd.QueryPathRsp, userId, queryPathRsp) + return + } + queryPathRsp := &proto.QueryPathRsp{ + QueryId: req.QueryId, + QueryStatus: proto.QueryPathRsp_PATH_STATUS_TYPE_SUCC, + Corners: h.ConvMeshVecListToPbVecList(path), + } + h.SendMsg(cmd.QueryPathRsp, userId, queryPathRsp) +} + +func (h *Handle) ObstacleModifyNotify(userId uint32, payloadMsg pb.Message) { + req := payloadMsg.(*proto.ObstacleModifyNotify) + logger.Debug("obstacle modify req: %v, uid: %v", req, userId) +} diff --git a/pkg/alg/bfs_pathfinding.go b/pathfinding/pfalg/bfs_pathfinding.go similarity index 90% rename from pkg/alg/bfs_pathfinding.go rename to pathfinding/pfalg/bfs_pathfinding.go index 3e10dc8a..2dbeb370 100644 --- a/pkg/alg/bfs_pathfinding.go +++ b/pathfinding/pfalg/bfs_pathfinding.go @@ -1,4 +1,8 @@ -package alg +package pfalg + +import ( + "hk4e/pkg/alg" +) const ( NODE_NONE = iota @@ -27,7 +31,7 @@ func NewBFS() (r *BFS) { return r } -func (b *BFS) InitMap(terrain map[MeshMapPos]bool, start MeshMapPos, end MeshMapPos, extR int16) { +func (b *BFS) InitMap(terrain map[MeshVector]bool, start MeshVector, end MeshVector, extR int16) { xLen := end.X - start.X yLen := end.Y - start.Y zLen := end.Z - start.Z @@ -58,7 +62,7 @@ func (b *BFS) InitMap(terrain map[MeshMapPos]bool, start MeshMapPos, end MeshMap } else if x == end.X && y == end.Y && z == end.Z { state = NODE_END } else { - _, exist := terrain[MeshMapPos{ + _, exist := terrain[MeshVector{ X: x, Y: y, Z: z, @@ -161,8 +165,8 @@ func (b *BFS) GetPath() []*PathNode { return path } -func (b *BFS) Pathfinding() []MeshMapPos { - queue := NewALQueue[*PathNode]() +func (b *BFS) Pathfinding() []MeshVector { + queue := alg.NewALQueue[*PathNode]() b.startPathNode.visit = true queue.EnQueue(b.startPathNode) for queue.Len() > 0 { @@ -183,10 +187,10 @@ func (b *BFS) Pathfinding() []MeshMapPos { if path == nil { return nil } - pathVectorList := make([]MeshMapPos, 0) + pathVectorList := make([]MeshVector, 0) for i := len(path) - 1; i >= 0; i-- { node := path[i] - pathVectorList = append(pathVectorList, MeshMapPos{ + pathVectorList = append(pathVectorList, MeshVector{ X: node.x, Y: node.y, Z: node.z, diff --git a/pathfinding/pfalg/mesh_vector.go b/pathfinding/pfalg/mesh_vector.go new file mode 100644 index 00000000..bfb873e0 --- /dev/null +++ b/pathfinding/pfalg/mesh_vector.go @@ -0,0 +1,7 @@ +package pfalg + +type MeshVector struct { + X int16 + Y int16 + Z int16 +} diff --git a/pathfinding/world/world_static.go b/pathfinding/world/world_static.go new file mode 100644 index 00000000..4b6bfddc --- /dev/null +++ b/pathfinding/world/world_static.go @@ -0,0 +1,88 @@ +package world + +import ( + "bytes" + "encoding/gob" + "os" + + "hk4e/pathfinding/pfalg" + "hk4e/pkg/logger" +) + +type WorldStatic struct { + // x y z -> if terrain exist + terrain map[pfalg.MeshVector]bool +} + +func NewWorldStatic() (r *WorldStatic) { + r = new(WorldStatic) + r.terrain = make(map[pfalg.MeshVector]bool) + return r +} + +func (w *WorldStatic) InitTerrain() bool { + data, err := os.ReadFile("./world_terrain.bin") + if err != nil { + logger.Error("read world terrain file error: %v", err) + return false + } + decoder := gob.NewDecoder(bytes.NewReader(data)) + err = decoder.Decode(&w.terrain) + if err != nil { + logger.Error("unmarshal world terrain data error: %v", err) + return false + } + return true +} + +func (w *WorldStatic) SaveTerrain() bool { + var buffer bytes.Buffer + encoder := gob.NewEncoder(&buffer) + err := encoder.Encode(w.terrain) + if err != nil { + logger.Error("marshal world terrain data error: %v", err) + return false + } + err = os.WriteFile("./world_terrain.bin", buffer.Bytes(), 0644) + if err != nil { + logger.Error("write world terrain file error: %v", err) + return false + } + return true +} + +func (w *WorldStatic) GetTerrain(x int16, y int16, z int16) (exist bool) { + pos := pfalg.MeshVector{ + X: x, + Y: y, + Z: z, + } + exist = w.terrain[pos] + return exist +} + +func (w *WorldStatic) SetTerrain(x int16, y int16, z int16) { + pos := pfalg.MeshVector{ + X: x, + Y: y, + Z: z, + } + w.terrain[pos] = true +} + +func (w *WorldStatic) Pathfinding(startPos pfalg.MeshVector, endPos pfalg.MeshVector) (bool, []pfalg.MeshVector) { + bfs := pfalg.NewBFS() + bfs.InitMap( + w.terrain, + startPos, + endPos, + 100, + ) + pathVectorList := bfs.Pathfinding() + if pathVectorList == nil { + logger.Error("could not find path") + return false, nil + } + logger.Debug("find path success, path: %v", pathVectorList) + return true, pathVectorList +} diff --git a/pkg/alg/common_pathfinding.go b/pkg/alg/common_pathfinding.go deleted file mode 100644 index cecd2ade..00000000 --- a/pkg/alg/common_pathfinding.go +++ /dev/null @@ -1,7 +0,0 @@ -package alg - -type MeshMapPos struct { - X int16 - Y int16 - Z int16 -} diff --git a/protocol/cmd/cmd_id_proto_obj_map.go b/protocol/cmd/cmd_id_proto_obj_map.go index dbab2e94..3157cd3b 100644 --- a/protocol/cmd/cmd_id_proto_obj_map.go +++ b/protocol/cmd/cmd_id_proto_obj_map.go @@ -100,6 +100,7 @@ func (c *CmdProtoMap) registerAllMessage() { c.registerMessage(LifeStateChangeNotify, &proto.LifeStateChangeNotify{}) // 实体存活状态改变通知 c.registerMessage(SceneEntityDrownReq, &proto.SceneEntityDrownReq{}) // 场景实体溺水请求 c.registerMessage(SceneEntityDrownRsp, &proto.SceneEntityDrownRsp{}) // 场景实体溺水响应 + c.registerMessage(ObstacleModifyNotify, &proto.ObstacleModifyNotify{}) // 寻路阻挡变动通知 // 战斗与同步 c.registerMessage(AvatarFightPropNotify, &proto.AvatarFightPropNotify{}) // 角色战斗属性通知 @@ -254,7 +255,6 @@ func (c *CmdProtoMap) registerAllMessage() { // c.registerMessage(EntityConfigHashNotify, &proto.EntityConfigHashNotify{}) // c.registerMessage(MonsterAIConfigHashNotify, &proto.MonsterAIConfigHashNotify{}) // c.registerMessage(GetRegionSearchReq, &proto.GetRegionSearchReq{}) - // c.registerMessage(ObstacleModifyNotify, &proto.ObstacleModifyNotify{}) // 空消息 c.registerMessage(65535, &proto.NullMsg{})