Files
bl/common/player/Server.go
2025-06-20 17:00:56 +08:00

361 lines
9.0 KiB
Go

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
}