goonserver/db/db.go

123 lines
2.4 KiB
Go

package db
import (
"crypto/sha256"
"database/sql"
"encoding/hex"
"errors"
"time"
_ "github.com/mattn/go-sqlite3"
)
var db *sql.DB
var (
ErrUserExists = errors.New("username already exists")
ErrInvalidCredentials = errors.New("invalid username or password")
)
func InitDB(dbPath string) error {
var err error
db, err = sql.Open("sqlite3", dbPath)
if err != nil {
return err
}
// Create tables if they don't exist
_, err = db.Exec(`
CREATE TABLE IF NOT EXISTS players (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE NOT NULL,
password_hash TEXT NOT NULL,
created_at DATETIME NOT NULL
);
CREATE TABLE IF NOT EXISTS player_states (
player_id INTEGER PRIMARY KEY,
x INTEGER NOT NULL,
y INTEGER NOT NULL,
last_seen DATETIME NOT NULL,
FOREIGN KEY(player_id) REFERENCES players(id)
);
`)
return err
}
func hashPassword(password string) string {
hash := sha256.Sum256([]byte(password))
return hex.EncodeToString(hash[:])
}
func RegisterPlayer(username, password string) (int, error) {
// Check if username exists
var exists bool
err := db.QueryRow("SELECT EXISTS(SELECT 1 FROM players WHERE username = ?)", username).Scan(&exists)
if err != nil {
return 0, err
}
if exists {
return 0, ErrUserExists
}
// Create new player
result, err := db.Exec(`
INSERT INTO players (username, password_hash, created_at)
VALUES (?, ?, ?)`,
username, hashPassword(password), time.Now().UTC(),
)
if err != nil {
return 0, err
}
id, err := result.LastInsertId()
return int(id), err
}
func AuthenticatePlayer(username, password string) (int, error) {
var id int
var storedHash string
err := db.QueryRow(`
SELECT id, password_hash
FROM players
WHERE username = ?`,
username,
).Scan(&id, &storedHash)
if err == sql.ErrNoRows {
return 0, ErrInvalidCredentials
}
if err != nil {
return 0, err
}
if storedHash != hashPassword(password) {
return 0, ErrInvalidCredentials
}
return id, nil
}
func SavePlayerState(playerID int, x, y int) error {
_, err := db.Exec(`
INSERT OR REPLACE INTO player_states (
player_id, x, y, last_seen
) VALUES (?, ?, ?, ?)`,
playerID, x, y, time.Now().UTC(),
)
return err
}
func LoadPlayerState(playerID int) (x, y int, err error) {
err = db.QueryRow(`
SELECT x, y FROM player_states
WHERE player_id = ?`,
playerID,
).Scan(&x, &y)
if err == sql.ErrNoRows {
// Return default position for new players
return 5, 5, nil
}
return x, y, err
}