diff --git a/README.md b/README.md index 8d425539..09d53c26 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,9 @@ #### 『原神』 Game Server But Golang Ver. -#### 代号hk4e中的hk起源于『Honkai Impact 3rd』 +#### 本项目的目标为构建一个高性能高可用的ARPG游戏服务端,并非以完整还原游戏内原本功能点为目的 -#### 项目的客户端协议([360NENZ/teyvat-helper-hk4e-proto](https://github.com/360NENZ/teyvat-helper-hk4e-proto))、配置表([TomyJan/xudong](https://github.com/TomyJan/xudong))主要基于3.2版本修改而来,因此请尽量使用3.2版本的客户端,但不是必须的 +#### 项目的客户端协议、配置表主要基于3.2版本修改而来,因此请尽量使用3.2版本的客户端,但不是必须的 [3.2.0国际服客户端下载链接](https://autopatchhk.yuanshen.com/client_app/download/pc_zip/20221024103618_h2e3o3zijYKEqHnQ/GenshinImpact_3.2.0.zip) @@ -16,12 +16,11 @@ ## 特性 -* 原生的高可用集群架构,任意节点宕机不会影响到整个系统,可大量水平扩展,支撑千万级DAU不是梦 -* 玩家级无状态游戏服务器,无锁单线程模型,开发省时省力,完善的玩家数据交换机制(内存-缓存-数据库) - ,拒绝同步阻塞的数据库访问,掌控每一纳秒的CPU时间不是梦 -* 新颖的玩家在线跨服无缝迁移功能,以多人世界之名,反复横跳于多个服务器进程之间不是梦 -* 独创的网关服务器侧客户端协议代理转换功能,拒绝因协议号消息号混淆而带来代码改动的烦恼,不同协议版本客户端同时在线联机不是梦 -* 完整的密钥交换机制实现,安全性++,拒绝一个写死的随机数种子和XOR密钥文件用到天荒地老,搭建一个属于自己的别具一格的聊天渠道不是梦 +* 原生的高可用集群架构,任意节点宕机不会影响到整个系统,可大量水平扩展 +* 玩家级无状态游戏服务器,无锁单线程模型,开发省时省力,完善的玩家数据交换机制(内存-缓存-数据库),拒绝同步阻塞的数据库访问 +* 新颖的玩家在线跨服无缝迁移功能 +* 独创的网关服务器侧客户端协议代理转换功能,拒绝因协议号消息号混淆而带来代码改动的烦恼 +* 完整的密钥交换机制实现,安全性++,拒绝一个写死的随机数种子和XOR密钥文件用到天荒地老 ## 编译和运行环境 diff --git a/gs/game/player_fight_sync.go b/gs/game/player_fight_sync.go index 8f00b950..14d9d09d 100644 --- a/gs/game/player_fight_sync.go +++ b/gs/game/player_fight_sync.go @@ -321,6 +321,9 @@ func (g *GameManager) AoiPlayerMove(player *model.Player, oldPos *model.Vector, } // 旧有新没有的group即为消失的 group := scene.GetGroupById(groupId) + if group == nil { + continue + } for _, entity := range group.GetAllEntity() { delEntityIdList = append(delEntityIdList, entity.GetId()) } diff --git a/gs/game/player_scene.go b/gs/game/player_scene.go index 86915e5e..f21ef1ed 100644 --- a/gs/game/player_scene.go +++ b/gs/game/player_scene.go @@ -245,6 +245,7 @@ func (g *GameManager) SceneInitFinishReq(player *model.Player, payloadMsg pb.Mes func (g *GameManager) AddSceneGroup(scene *Scene, groupConfig *gdconf.Group) { initSuiteId := int(groupConfig.GroupInitConfig.Suite) if initSuiteId > len(groupConfig.SuiteList) { + logger.Error("invalid init suite id: %v", initSuiteId) return } scene.AddGroupSuite(uint32(groupConfig.Id), uint8(initSuiteId)) @@ -285,34 +286,41 @@ func (g *GameManager) EnterSceneDoneReq(player *model.Player, payloadMsg pb.Mess activeAvatarEntityId := world.GetPlayerWorldAvatarEntityId(player, activeAvatarId) g.AddSceneEntityNotify(player, visionType, []uint32{activeAvatarEntityId}, true, false) + // 加载附近的group aoiManager, exist := WORLD_MANAGER.GetSceneBlockAoiMap()[scene.GetId()] + addEntityIdList := make([]uint32, 0) if exist { objectList := aoiManager.GetObjectListByPos(float32(player.Pos.X), 0.0, float32(player.Pos.Z)) for _, groupAny := range objectList { - group := groupAny.(*gdconf.Group) - distance2D := math.Sqrt((player.Pos.X-float64(group.Pos.X))*(player.Pos.X-float64(group.Pos.X)) + - (player.Pos.Z-float64(group.Pos.Z))*(player.Pos.Z-float64(group.Pos.Z))) + groupConfig := groupAny.(*gdconf.Group) + distance2D := math.Sqrt((player.Pos.X-float64(groupConfig.Pos.X))*(player.Pos.X-float64(groupConfig.Pos.X)) + + (player.Pos.Z-float64(groupConfig.Pos.Z))*(player.Pos.Z-float64(groupConfig.Pos.Z))) if distance2D > ENTITY_LOD { continue } - if group.DynamicLoad { + if groupConfig.DynamicLoad { continue } - g.AddSceneGroup(scene, group) + g.AddSceneGroup(scene, groupConfig) + group := scene.GetGroupById(uint32(groupConfig.Id)) + for _, entity := range group.GetAllEntity() { + addEntityIdList = append(addEntityIdList, entity.GetId()) + } } } + entityIdList := make([]uint32, 0) if player.SceneJump { visionType = proto.VisionType_VISION_MEET + entityMap := scene.GetAllEntity() + for _, entity := range entityMap { + if entity.GetId() == activeAvatarEntityId { + continue + } + entityIdList = append(entityIdList, entity.GetId()) + } } else { visionType = proto.VisionType_VISION_TRANSPORT - } - entityMap := scene.GetAllEntity() - entityIdList := make([]uint32, 0) - for _, entity := range entityMap { - if entity.GetId() == activeAvatarEntityId { - continue - } - entityIdList = append(entityIdList, entity.GetId()) + entityIdList = addEntityIdList } g.AddSceneEntityNotify(player, visionType, entityIdList, false, false) diff --git a/gs/game/world_scene.go b/gs/game/world_scene.go index da7564f7..f8766096 100644 --- a/gs/game/world_scene.go +++ b/gs/game/world_scene.go @@ -17,37 +17,11 @@ type Scene struct { id uint32 world *World playerMap map[uint32]*model.Player - entityMap map[uint32]*Entity - groupMap map[uint32]*Group - gameTime uint32 // 游戏内提瓦特大陆的时间 - createTime int64 // 场景创建时间 - meeoIndex uint32 // 客户端风元素染色同步协议的计数器 -} - -type Group struct { - suiteMap map[uint8]*Suite -} - -func (g *Group) GetAllSuite() map[uint8]*Suite { - return g.suiteMap -} - -func (g *Group) GetAllEntity() map[uint32]*Entity { - entityMap := make(map[uint32]*Entity) - for _, suite := range g.suiteMap { - for _, entity := range suite.entityMap { - entityMap[entity.id] = entity - } - } - return entityMap -} - -type Suite struct { - entityMap map[uint32]*Entity -} - -func (s *Suite) GetAllEntity() map[uint32]*Entity { - return s.entityMap + entityMap map[uint32]*Entity // 场景中全部的实体 + groupMap map[uint32]*Group // 场景中按group->suite分类的实体 + gameTime uint32 // 游戏内提瓦特大陆的时间 + createTime int64 // 场景创建时间 + meeoIndex uint32 // 客户端风元素染色同步协议的计数器 } func (s *Scene) GetId() uint32 { @@ -420,6 +394,7 @@ func (s *Scene) GetEntity(entityId uint32) *Entity { func (s *Scene) AddGroupSuite(groupId uint32, suiteId uint8) { groupConfig := gdconf.GetSceneGroup(int32(groupId)) if groupConfig == nil { + logger.Error("get scene group config is nil, groupId: %v", groupId) return } suiteConfig := groupConfig.SuiteList[suiteId-1] @@ -429,6 +404,7 @@ func (s *Scene) AddGroupSuite(groupId uint32, suiteId uint8) { for _, monsterConfigId := range suiteConfig.MonsterConfigIdList { monster, exist := groupConfig.MonsterMap[monsterConfigId] if !exist { + logger.Error("monster config not exist, monsterConfigId: %v", monsterConfigId) continue } entityId := s.createConfigEntity(uint32(groupConfig.Id), monster) @@ -438,6 +414,7 @@ func (s *Scene) AddGroupSuite(groupId uint32, suiteId uint8) { for _, gadgetConfigId := range suiteConfig.GadgetConfigIdList { gadget, exist := groupConfig.GadgetMap[gadgetConfigId] if !exist { + logger.Error("gadget config not exist, gadgetConfigId: %v", gadgetConfigId) continue } entityId := s.createConfigEntity(uint32(groupConfig.Id), gadget) @@ -461,10 +438,12 @@ func (s *Scene) AddGroupSuite(groupId uint32, suiteId uint8) { func (s *Scene) RemoveGroupSuite(groupId uint32, suiteId uint8) { group := s.groupMap[groupId] if group == nil { + logger.Error("group not exist, groupId: %v", groupId) return } suite := group.suiteMap[suiteId] if suite == nil { + logger.Error("suite not exist, suiteId: %v", suiteId) return } for _, entity := range suite.entityMap { @@ -531,6 +510,7 @@ func (s *Scene) createConfigEntity(groupId uint32, entityConfig any) uint32 { } } +// TODO 临时写死 func getTempFightPropMap() map[uint32]float32 { fpm := map[uint32]float32{ constant.FIGHT_PROP_CUR_HP: float32(72.91699), @@ -552,6 +532,40 @@ func getTempFightPropMap() map[uint32]float32 { return fpm } +type Group struct { + suiteMap map[uint8]*Suite +} + +type Suite struct { + entityMap map[uint32]*Entity +} + +func (g *Group) GetSuiteById(suiteId uint8) *Suite { + return g.suiteMap[suiteId] +} + +func (g *Group) GetAllSuite() map[uint8]*Suite { + return g.suiteMap +} + +func (g *Group) GetAllEntity() map[uint32]*Entity { + entityMap := make(map[uint32]*Entity) + for _, suite := range g.suiteMap { + for _, entity := range suite.entityMap { + entityMap[entity.id] = entity + } + } + return entityMap +} + +func (s *Suite) GetEntityById(entityId uint32) *Entity { + return s.entityMap[entityId] +} + +func (s *Suite) GetAllEntity() map[uint32]*Entity { + return s.entityMap +} + // Entity 场景实体数据结构 type Entity struct { id uint32 // 实体id