169 lines
3.9 KiB
Go
169 lines
3.9 KiB
Go
package player
|
|
|
|
import (
|
|
"database/sql"
|
|
"fmt"
|
|
"sync"
|
|
|
|
"eq2emu/internal/database"
|
|
)
|
|
|
|
// PlayerDatabase manages player data persistence using MySQL
|
|
type PlayerDatabase struct {
|
|
db *database.Database
|
|
mutex sync.RWMutex
|
|
}
|
|
|
|
// NewPlayerDatabase creates a new player database instance
|
|
func NewPlayerDatabase(db *database.Database) *PlayerDatabase {
|
|
return &PlayerDatabase{
|
|
db: db,
|
|
}
|
|
}
|
|
|
|
// LoadPlayer loads a player from the database
|
|
func (pdb *PlayerDatabase) LoadPlayer(characterID int32) (*Player, error) {
|
|
pdb.mutex.RLock()
|
|
defer pdb.mutex.RUnlock()
|
|
|
|
player := NewPlayer()
|
|
player.SetCharacterID(characterID)
|
|
|
|
query := `SELECT name, level, race, class, zone_id, x, y, z, heading
|
|
FROM characters WHERE id = ?`
|
|
|
|
row := pdb.db.QueryRow(query, characterID)
|
|
var name string
|
|
var level int16
|
|
var race, class int8
|
|
var zoneID int32
|
|
var x, y, z, heading float32
|
|
|
|
err := row.Scan(&name, &level, &race, &class, &zoneID, &x, &y, &z, &heading)
|
|
if err != nil {
|
|
if err == sql.ErrNoRows {
|
|
return nil, fmt.Errorf("player not found: %d", characterID)
|
|
}
|
|
return nil, fmt.Errorf("failed to load player: %w", err)
|
|
}
|
|
|
|
player.SetName(name)
|
|
player.SetLevel(level)
|
|
player.SetRace(race)
|
|
player.SetClass(class)
|
|
player.SetZone(zoneID)
|
|
player.SetX(x)
|
|
player.SetY(y, false)
|
|
player.SetZ(z)
|
|
player.SetHeadingFromFloat(heading)
|
|
|
|
return player, nil
|
|
}
|
|
|
|
// SavePlayer saves a player to the database
|
|
func (pdb *PlayerDatabase) SavePlayer(player *Player) error {
|
|
if player == nil {
|
|
return fmt.Errorf("cannot save nil player")
|
|
}
|
|
|
|
pdb.mutex.Lock()
|
|
defer pdb.mutex.Unlock()
|
|
|
|
characterID := player.GetCharacterID()
|
|
if characterID == 0 {
|
|
// Insert new player
|
|
return pdb.insertPlayer(player)
|
|
}
|
|
|
|
// Try to update existing player first
|
|
return pdb.updatePlayer(player)
|
|
}
|
|
|
|
// insertPlayer inserts a new player record
|
|
func (pdb *PlayerDatabase) insertPlayer(player *Player) error {
|
|
query := `INSERT INTO characters
|
|
(name, level, race, class, zone_id, x, y, z, heading, created_date)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NOW())`
|
|
|
|
result, err := pdb.db.Exec(query,
|
|
player.GetName(),
|
|
player.GetLevel(),
|
|
player.GetRace(),
|
|
player.GetClass(),
|
|
player.GetZone(),
|
|
player.GetX(),
|
|
player.GetY(),
|
|
player.GetZ(),
|
|
player.GetHeading(),
|
|
)
|
|
|
|
if err != nil {
|
|
return fmt.Errorf("failed to insert player: %w", err)
|
|
}
|
|
|
|
// Get the inserted character ID
|
|
characterID, err := result.LastInsertId()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get inserted character ID: %w", err)
|
|
}
|
|
|
|
player.SetCharacterID(int32(characterID))
|
|
return nil
|
|
}
|
|
|
|
// updatePlayer updates an existing player record
|
|
func (pdb *PlayerDatabase) updatePlayer(player *Player) error {
|
|
query := `UPDATE characters
|
|
SET name=?, level=?, race=?, class=?, zone_id=?, x=?, y=?, z=?, heading=?
|
|
WHERE id=?`
|
|
|
|
_, err := pdb.db.Exec(query,
|
|
player.GetName(),
|
|
player.GetLevel(),
|
|
player.GetRace(),
|
|
player.GetClass(),
|
|
player.GetZone(),
|
|
player.GetX(),
|
|
player.GetY(),
|
|
player.GetZ(),
|
|
player.GetHeading(),
|
|
player.GetCharacterID(),
|
|
)
|
|
|
|
if err != nil {
|
|
return fmt.Errorf("failed to update player: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// DeletePlayer soft-deletes a player (marks as deleted)
|
|
func (pdb *PlayerDatabase) DeletePlayer(characterID int32) error {
|
|
pdb.mutex.Lock()
|
|
defer pdb.mutex.Unlock()
|
|
|
|
query := `UPDATE characters SET deleted_date = NOW() WHERE id = ?`
|
|
|
|
_, err := pdb.db.Exec(query, characterID)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to delete player %d: %w", characterID, err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// PlayerExists checks if a player exists in the database
|
|
func (pdb *PlayerDatabase) PlayerExists(characterID int32) (bool, error) {
|
|
pdb.mutex.RLock()
|
|
defer pdb.mutex.RUnlock()
|
|
|
|
var count int
|
|
query := `SELECT COUNT(*) FROM characters WHERE id = ? AND (deleted_date IS NULL OR deleted_date = 0)`
|
|
|
|
err := pdb.db.QueryRow(query, characterID).Scan(&count)
|
|
if err != nil {
|
|
return false, fmt.Errorf("failed to check player existence: %w", err)
|
|
}
|
|
|
|
return count > 0, nil
|
|
} |