167 lines
4.4 KiB
Go
167 lines
4.4 KiB
Go
package repository
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"a301_game_server/internal/db"
|
|
)
|
|
|
|
// CharacterData holds persisted character state.
|
|
type CharacterData struct {
|
|
ID int64
|
|
AccountID int64
|
|
Name string
|
|
Level int32
|
|
Exp int64
|
|
HP int32
|
|
MaxHP int32
|
|
MP int32
|
|
MaxMP int32
|
|
Str int32
|
|
Dex int32
|
|
IntStat int32
|
|
ZoneID int32
|
|
PosX float32
|
|
PosY float32
|
|
PosZ float32
|
|
Rotation float32
|
|
}
|
|
|
|
// CharacterRepo handles character persistence.
|
|
type CharacterRepo struct {
|
|
pool *db.Pool
|
|
}
|
|
|
|
// NewCharacterRepo creates a new character repository.
|
|
func NewCharacterRepo(pool *db.Pool) *CharacterRepo {
|
|
return &CharacterRepo{pool: pool}
|
|
}
|
|
|
|
// Create inserts a new character.
|
|
func (r *CharacterRepo) Create(ctx context.Context, accountID int64, name string) (*CharacterData, error) {
|
|
c := &CharacterData{
|
|
AccountID: accountID,
|
|
Name: name,
|
|
Level: 1,
|
|
HP: 100,
|
|
MaxHP: 100,
|
|
MP: 50,
|
|
MaxMP: 50,
|
|
Str: 10,
|
|
Dex: 10,
|
|
IntStat: 10,
|
|
ZoneID: 1,
|
|
}
|
|
|
|
err := r.pool.QueryRow(ctx,
|
|
`INSERT INTO characters (account_id, name, level, hp, max_hp, mp, max_mp, str, dex, int_stat, zone_id)
|
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
|
|
RETURNING id`,
|
|
c.AccountID, c.Name, c.Level, c.HP, c.MaxHP, c.MP, c.MaxMP, c.Str, c.Dex, c.IntStat, c.ZoneID,
|
|
).Scan(&c.ID)
|
|
|
|
if err != nil {
|
|
return nil, fmt.Errorf("create character: %w", err)
|
|
}
|
|
return c, nil
|
|
}
|
|
|
|
// GetByAccountID returns all characters for an account.
|
|
func (r *CharacterRepo) GetByAccountID(ctx context.Context, accountID int64) ([]*CharacterData, error) {
|
|
rows, err := r.pool.Query(ctx,
|
|
`SELECT id, account_id, name, level, exp, hp, max_hp, mp, max_mp, str, dex, int_stat,
|
|
zone_id, pos_x, pos_y, pos_z, rotation
|
|
FROM characters WHERE account_id = $1`, accountID,
|
|
)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("query characters: %w", err)
|
|
}
|
|
defer rows.Close()
|
|
|
|
var chars []*CharacterData
|
|
for rows.Next() {
|
|
c := &CharacterData{}
|
|
if err := rows.Scan(
|
|
&c.ID, &c.AccountID, &c.Name, &c.Level, &c.Exp,
|
|
&c.HP, &c.MaxHP, &c.MP, &c.MaxMP,
|
|
&c.Str, &c.Dex, &c.IntStat,
|
|
&c.ZoneID, &c.PosX, &c.PosY, &c.PosZ, &c.Rotation,
|
|
); err != nil {
|
|
return nil, fmt.Errorf("scan character: %w", err)
|
|
}
|
|
chars = append(chars, c)
|
|
}
|
|
return chars, nil
|
|
}
|
|
|
|
// GetByID loads a single character.
|
|
func (r *CharacterRepo) GetByID(ctx context.Context, id int64) (*CharacterData, error) {
|
|
c := &CharacterData{}
|
|
err := r.pool.QueryRow(ctx,
|
|
`SELECT id, account_id, name, level, exp, hp, max_hp, mp, max_mp, str, dex, int_stat,
|
|
zone_id, pos_x, pos_y, pos_z, rotation
|
|
FROM characters WHERE id = $1`, id,
|
|
).Scan(
|
|
&c.ID, &c.AccountID, &c.Name, &c.Level, &c.Exp,
|
|
&c.HP, &c.MaxHP, &c.MP, &c.MaxMP,
|
|
&c.Str, &c.Dex, &c.IntStat,
|
|
&c.ZoneID, &c.PosX, &c.PosY, &c.PosZ, &c.Rotation,
|
|
)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("get character %d: %w", id, err)
|
|
}
|
|
return c, nil
|
|
}
|
|
|
|
// Save persists the current character state.
|
|
func (r *CharacterRepo) Save(ctx context.Context, c *CharacterData) error {
|
|
_, err := r.pool.Exec(ctx,
|
|
`UPDATE characters SET
|
|
level = $2, exp = $3, hp = $4, max_hp = $5, mp = $6, max_mp = $7,
|
|
str = $8, dex = $9, int_stat = $10,
|
|
zone_id = $11, pos_x = $12, pos_y = $13, pos_z = $14, rotation = $15,
|
|
updated_at = NOW()
|
|
WHERE id = $1`,
|
|
c.ID, c.Level, c.Exp, c.HP, c.MaxHP, c.MP, c.MaxMP,
|
|
c.Str, c.Dex, c.IntStat,
|
|
c.ZoneID, c.PosX, c.PosY, c.PosZ, c.Rotation,
|
|
)
|
|
if err != nil {
|
|
return fmt.Errorf("save character %d: %w", c.ID, err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// SaveBatch saves multiple characters in a single transaction.
|
|
func (r *CharacterRepo) SaveBatch(ctx context.Context, chars []*CharacterData) error {
|
|
if len(chars) == 0 {
|
|
return nil
|
|
}
|
|
|
|
tx, err := r.pool.Begin(ctx)
|
|
if err != nil {
|
|
return fmt.Errorf("begin tx: %w", err)
|
|
}
|
|
defer tx.Rollback(ctx)
|
|
|
|
for _, c := range chars {
|
|
_, err := tx.Exec(ctx,
|
|
`UPDATE characters SET
|
|
level = $2, exp = $3, hp = $4, max_hp = $5, mp = $6, max_mp = $7,
|
|
str = $8, dex = $9, int_stat = $10,
|
|
zone_id = $11, pos_x = $12, pos_y = $13, pos_z = $14, rotation = $15,
|
|
updated_at = NOW()
|
|
WHERE id = $1`,
|
|
c.ID, c.Level, c.Exp, c.HP, c.MaxHP, c.MP, c.MaxMP,
|
|
c.Str, c.Dex, c.IntStat,
|
|
c.ZoneID, c.PosX, c.PosY, c.PosZ, c.Rotation,
|
|
)
|
|
if err != nil {
|
|
return fmt.Errorf("save character %d in batch: %w", c.ID, err)
|
|
}
|
|
}
|
|
|
|
return tx.Commit(ctx)
|
|
}
|