package socket import ( "context" "database/sql" "errors" "fmt" "log" "sync" "time" ) // Server 游戏服务器核心类,管理玩家、星球和游戏逻辑 type Server struct { players map[int64]*entity.Player planets map[int64]*planet.Planet mutex sync.RWMutex gameRepo repo.GameResourceRepo serverRepo repo.ServerRepo accountRepo repo.AccountRepo playerInfoRepo repo.PlayerInfoRepo playerItemRepo repo.PlayerItemInfoRepo petRepo repo.PetRepo } // NewServer 创建新的游戏服务器实例 func NewServer( gameRepo repo.GameResourceRepo, serverRepo repo.ServerRepo, accountRepo repo.AccountRepo, playerInfoRepo repo.PlayerInfoRepo, playerItemRepo repo.PlayerItemInfoRepo, petRepo repo.PetRepo, ) *Server { s := &Server{ players: make(map[int64]*entity.Player), planets: make(map[int64]*planet.Planet), gameRepo: gameRepo, serverRepo: serverRepo, accountRepo: accountRepo, playerInfoRepo: playerInfoRepo, playerItemRepo: playerItemRepo, petRepo: petRepo, } s.initializePlanets() return s } // initializePlanets 初始化所有星球 func (s *Server) initializePlanets() { maps := s.gameRepo.GetAllMaps() for _, config := range maps { planet := s.generatePlanet(config) s.planets[planet.GetId()] = planet } } // generatePlanet 根据地图配置生成星球 func (s *Server) generatePlanet(config *mapInfo.MapXmlModel) *planet.Planet { entries := config.GetEntries() positions := make(map[int64]structs.Point) if len(entries) == 0 { positions = make(map[int64]structs.Point) } else { for _, entry := range entries { positions[entry.GetFromMap()] = s.generatePoint(entry) } } return planet.NewPlanet( s, int64(config.GetId()), config.GetName(), structs.Point{X: config.GetX(), Y: config.GetY()}, positions, s.gameRepo.CanMapRefresh(config.GetId()), ) } // generatePoint 从入口配置生成点坐标 func (s *Server) generatePoint(xml *mapInfo.EntryXmlModel) structs.Point { return structs.Point{X: xml.GetPosX(), Y: xml.GetPosY()} } // GetPlayer 获取玩家信息 func (s *Server) GetPlayer(accountID int64) (*entity.Player, error) { s.mutex.RLock() defer s.mutex.RUnlock() player, exists := s.players[accountID] if !exists { return nil, errors.New("玩家不存在") } return player, nil } // GetPlanet 获取星球信息 func (s *Server) GetPlanet(planetID int64) (*planet.Planet, error) { s.mutex.RLock() defer s.mutex.RUnlock() planet, exists := s.planets[planetID] if !exists { return nil, errors.New("星球不存在") } return planet, nil } // GetDefaultPlanet 获取默认星球 func (s *Server) GetDefaultPlanet() (*planet.Planet, error) { s.mutex.RLock() defer s.mutex.RUnlock() planet, exists := s.planets[1] // 假设1是默认星球ID if !exists { return nil, errors.New("未找到默认星球") } return planet, nil } // PlayerEnter 玩家加入服务器 func (s *Server) PlayerEnter(player *entity.Player) { s.mutex.Lock() defer s.mutex.Unlock() s.players[player.GetAccountID()] = player } // PlayerOffline 玩家离线处理 func (s *Server) PlayerOffline(player *entity.Player) { s.mutex.Lock() defer s.mutex.Unlock() // 清理缓存会话数据 if err := s.accountRepo.RemoveSessionID(player.GetSessionID()); err != nil { log.Printf("清除会话ID失败: %v", err) } // 清理登录绑定服务器 if err := s.serverRepo.CancelRecordAccount(player.GetAccountID()); err != nil { log.Printf("取消账号绑定失败: %v", err) } // 从玩家列表中移除 delete(s.players, player.GetAccountID()) } // BroadcastMessage 广播消息给所有在线玩家 func (s *Server) BroadcastMessage(message *net.OutboundMessage) { s.mutex.RLock() defer s.mutex.RUnlock() for _, player := range s.players { if player.IsOnline() { player.SendMessage(message) } } } // BroadcastMessageWithFilter 按条件广播消息给在线玩家 func (s *Server) BroadcastMessageWithFilter(message *net.OutboundMessage, filter func(*entity.Player) bool) { s.mutex.RLock() defer s.mutex.RUnlock() for _, player := range s.players { if player.IsOnline() && filter(player) { player.SendMessage(message) } } } // GeneratePetEntity 生成宠物实体 func (s *Server) GeneratePetEntity( playerID int64, petTypeID int, individualValue int16, nature int, abilityTypeEnum int, isShiny bool, level int, ) (*pet.PetEntity, error) { if level < 1 || level > 100 { return nil, fmt.Errorf("精灵等级必须在1到100之间, level: %d", level) } petInfo, err := s.gameRepo.GetMonsterByID(petTypeID) if err != nil { return nil, fmt.Errorf("无效的精灵ID, pet_id: %d, 错误: %v", petTypeID, err) } firstSkillInfo, err := s.gameRepo.GetPetFirstSkillID(petTypeID, level) if err != nil { return nil, fmt.Errorf("精灵没有初始技能, pet_id: %d, 错误: %v", petTypeID, err) } zero := int16(0) pet := pet.NewPetEntityBuilder() .WithAsset(petInfo) .WithLevelToExp(s.gameRepo.GetLevelToExp()) .WithPlayerID(playerID) .WithCapturePlayerID(playerID) .WithCaptureTime(time.Now().Unix()) .WithCaptureMap(0) // 假设0是默认地图ID .WithCaptureRect(0) // 假设0是默认区域 .WithCaptureLevel(level) .WithIndividualValue(individualValue) .WithNature(nature) .WithAbilityTypeEnum(abilityTypeEnum) .WithIsShiny(isShiny) .WithLevel(level) .WithCurrentExp(0) .WithEvHp(zero) .WithEvAttack(zero) .WithEvDefense(zero) .WithEvSpecialAttack(zero) .WithEvSpecialDefense(zero) .WithEvSpeed(zero) .WithSkill1ID(firstSkillInfo[0].GetId()) .WithSkill1Pp(firstSkillInfo[0].GetMaxPp()) .WithSkill2ID(firstSkillInfo[1].GetId()) .WithSkill2Pp(firstSkillInfo[1].GetMaxPp()) .WithSkill3ID(firstSkillInfo[2].GetId()) .WithSkill3Pp(firstSkillInfo[2].GetMaxPp()) .WithSkill4ID(firstSkillInfo[3].GetId()) .WithSkill4Pp(firstSkillInfo[3].GetMaxPp()) .WithIndividualGuarantee(0) .WithNatureGuarantee(0) .Build() if err := s.CalculatePetPanel(pet); err != nil { return nil, err } return pet, nil } // CalculatePetPanel 计算宠物面板属性 func (s *Server) CalculatePetPanel(petEntity *pet.PetEntity) error { natureInfo, err := s.gameRepo.GetNatureInfoByID(petEntity.GetNature()) if err != nil { return fmt.Errorf("无效的性格ID, nature: %d, 错误: %v", petEntity.GetNature(), err) } hp := util.CalculatePetHPPanelSize( petEntity.GetAsset().GetHp(), petEntity.GetIndividualValue(), petEntity.GetLevel(), petEntity.GetEvHp(), ) attack := util.CalculatePetPanelSize( petEntity.GetAsset().GetAtk(), petEntity.GetIndividualValue(), petEntity.GetLevel(), petEntity.GetEvHp(), natureInfo.GetAttackCorrect(), ) defense := util.CalculatePetPanelSize( petEntity.GetAsset().GetDef(), petEntity.GetIndividualValue(), petEntity.GetLevel(), petEntity.GetEvHp(), natureInfo.GetDefenseCorrect(), ) specialAttack := util.CalculatePetPanelSize( petEntity.GetAsset().GetSpAtk(), petEntity.GetIndividualValue(), petEntity.GetLevel(), petEntity.GetEvHp(), natureInfo.GetSaCorrect(), ) specialDefense := util.CalculatePetPanelSize( petEntity.GetAsset().GetSpDef(), petEntity.GetIndividualValue(), petEntity.GetLevel(), petEntity.GetEvHp(), natureInfo.GetSdCorrect(), ) speed := util.CalculatePetPanelSize( petEntity.GetAsset().GetSpd(), petEntity.GetIndividualValue(), petEntity.GetLevel(), petEntity.GetEvHp(), natureInfo.GetSpeedCorrect(), ) petEntity.SetMaxHp(hp) petEntity.SetCurrentHp(hp) petEntity.SetAttack(attack) petEntity.SetDefense(defense) petEntity.SetSpecialAttack(specialAttack) petEntity.SetSpecialDefense(specialDefense) petEntity.SetSpeed(speed) return nil } // SavePlayer 保存玩家信息到数据库 func (s *Server) SavePlayer(player *entity.Player) (bool, error) { ctx := context.Background() tx, err := s.playerInfoRepo.BeginTransaction(ctx) if err != nil { return false, fmt.Errorf("开始事务失败: %v", err) } defer func() { if r := recover(); r != nil { tx.Rollback() log.Printf("保存玩家时发生panic: %v", r) } else if err != nil { tx.Rollback() } else { tx.Commit() } }() playerID := player.GetGameID() pets := player.GetPetBag().GetUsedPets() items := player.GetItemBag().GetItems() // 保存玩家信息 if err := s.playerInfoRepo.Save(ctx, tx, player); err != nil { return false, fmt.Errorf("保存玩家信息失败: %v", err) } // 保存精灵信息 if err := s.petRepo.SaveAll(ctx, tx, playerID, pets); err != nil { return false, fmt.Errorf("保存精灵信息失败: %v", err) } // 保存玩家背包信息 if err := s.playerItemRepo.SaveAll(ctx, tx, playerID, items); err != nil { return false, fmt.Errorf("保存背包信息失败: %v", err) } return true, nil } // Destroy 销毁服务器,清理资源 func (s *Server) Destroy() error { // 保存所有玩家数据 s.mutex.RLock() for _, player := range s.players { go func(p *entity.Player) { if _, err := s.SavePlayer(p); err != nil { log.Printf("保存玩家 %d 数据失败: %v", p.GetAccountID(), err) } }(player) } s.mutex.RUnlock() log.Println("Destroying server ...") return nil }