Files
a301_game_server/internal/db/repository/character.go
2026-02-26 17:52:48 +09:00

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)
}