make server automatically create DB schema if not exists (#5)

* add default values for CLI args

* add development docker compose file

* prevent crash if userdata dir does not exist

* accounts, acccountStats

* account stats and create db indices

* compensations and daily runs

* ensure uniqueness of daily seed

* start on port 8001 by default for client parity

* make generated schema match production

* sort imports
pull/9/head^2
Up 2024-05-10 21:30:47 +02:00 committed by GitHub
parent 8a32efeaa3
commit 3ed5f41d58
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 108 additions and 20 deletions

6
.gitignore vendored
View File

@ -1,9 +1,13 @@
# no extension on linux, .exe on windows
rogueserver*
userdata/*
!/rogueserver/*
/userdata/*
secret.key
# local testing
/.data/
# Jetbrains IDEs
/.idea/
*.iml

View File

@ -61,22 +61,27 @@ func Init() error {
secret = newSecret
}
err = recordNewDaily()
seed, err := recordNewDaily()
if err != nil {
log.Print(err)
}
log.Printf("Daily Run Seed: %s", Seed())
log.Printf("Daily Run Seed: %s", seed)
scheduler.AddFunc("@daily", func() {
_, err = scheduler.AddFunc("@daily", func() {
time.Sleep(time.Second)
err := recordNewDaily()
seed, err = recordNewDaily()
log.Printf("Daily Run Seed: %s", seed)
if err != nil {
log.Printf("error while recording new daily: %s", err)
}
})
if err != nil {
return err
}
scheduler.Start()
return nil
@ -95,11 +100,6 @@ func deriveSeed(seedTime time.Time) []byte {
return hashedSeed[:]
}
func recordNewDaily() error {
err := db.TryAddDailyRun(Seed())
if err != nil {
return err
}
return nil
func recordNewDaily() (string, error) {
return db.TryAddDailyRun(Seed())
}

View File

@ -19,6 +19,7 @@ package api
import (
"database/sql"
"encoding/base64"
"encoding/json"
"fmt"
"net/http"
@ -303,7 +304,13 @@ func handleSaveData(w http.ResponseWriter, r *http.Request) {
}
// doesn't return a save, but it works
save, err = savedata.Clear(uuid, slot, daily.Seed(), s)
var seed string
seed, err = db.GetDailyRunSeed()
if err != nil {
httpError(w, r, err, http.StatusInternalServerError)
return
}
save, err = savedata.Clear(uuid, slot, seed, s)
}
if err != nil {
httpError(w, r, err, http.StatusInternalServerError)
@ -327,7 +334,21 @@ func handleSaveData(w http.ResponseWriter, r *http.Request) {
// daily
func handleDailySeed(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(daily.Seed()))
seed, err := db.GetDailyRunSeed()
if err != nil {
httpError(w, r, err, http.StatusInternalServerError)
return
}
bytes, err := base64.StdEncoding.DecodeString(seed)
if err != nil {
httpError(w, r, err, http.StatusInternalServerError)
return
}
_, err = w.Write(bytes)
if err != nil {
httpError(w, r, err, http.StatusInternalServerError)
return
}
}
func handleDailyRankings(w http.ResponseWriter, r *http.Request) {

View File

@ -23,13 +23,25 @@ import (
"github.com/pagefaultgames/rogueserver/defs"
)
func TryAddDailyRun(seed string) error {
_, err := handle.Exec("INSERT INTO dailyRuns (seed, date) VALUES (?, UTC_DATE()) ON DUPLICATE KEY UPDATE date = date", seed)
func TryAddDailyRun(seed string) (string, error) {
var actualSeed string
err := handle.QueryRow("INSERT INTO dailyRuns (seed, date) VALUES (?, UTC_DATE()) ON DUPLICATE KEY UPDATE date = date RETURNING seed", seed).Scan(&actualSeed)
if err != nil {
return err
return "INVALID", err
}
return nil
return actualSeed, nil
}
func GetDailyRunSeed() (string, error) {
var seed string
err := handle.QueryRow("SELECT seed FROM dailyRuns WHERE date = UTC_DATE()").Scan(&seed)
if err != nil {
return "INVALID", err
}
return seed, nil
}
func AddOrUpdateAccountDailyRun(uuid []byte, score int, wave int) error {

View File

@ -52,14 +52,51 @@ func Init(username, password, protocol, address, database string) error {
if err != nil {
panic(err)
}
// accounts
tx.Exec("CREATE TABLE IF NOT EXISTS accounts (uuid BINARY(16) NOT NULL PRIMARY KEY, username VARCHAR(16) UNIQUE NOT NULL, hash BINARY(32) NOT NULL, salt BINARY(16) NOT NULL, registered TIMESTAMP NOT NULL, lastLoggedIn TIMESTAMP DEFAULT NULL, lastActivity TIMESTAMP DEFAULT NULL, banned TINYINT(1) NOT NULL DEFAULT 0, trainerId SMALLINT(5) UNSIGNED DEFAULT 0, secretId SMALLINT(5) UNSIGNED DEFAULT 0)")
// sessions
tx.Exec("CREATE TABLE IF NOT EXISTS sessions (token BINARY(32) NOT NULL PRIMARY KEY, uuid BINARY(16) NOT NULL, active TINYINT(1) NOT NULL DEFAULT 0, expire TIMESTAMP DEFAULT NULL, CONSTRAINT sessions_ibfk_1 FOREIGN KEY (uuid) REFERENCES accounts (uuid) ON DELETE CASCADE ON UPDATE CASCADE)")
tx.Exec("CREATE INDEX IF NOT EXISTS sessionsByUuid ON sessions (uuid)")
// stats
tx.Exec("CREATE TABLE IF NOT EXISTS accountStats (uuid BINARY(16) NOT NULL PRIMARY KEY, playTime INT(11) NOT NULL DEFAULT 0, battles INT(11) NOT NULL DEFAULT 0, classicSessionsPlayed INT(11) NOT NULL DEFAULT 0, sessionsWon INT(11) NOT NULL DEFAULT 0, highestEndlessWave INT(11) NOT NULL DEFAULT 0, highestLevel INT(11) NOT NULL DEFAULT 0, pokemonSeen INT(11) NOT NULL DEFAULT 0, pokemonDefeated INT(11) NOT NULL DEFAULT 0, pokemonCaught INT(11) NOT NULL DEFAULT 0, pokemonHatched INT(11) NOT NULL DEFAULT 0, eggsPulled INT(11) NOT NULL DEFAULT 0, regularVouchers INT(11) NOT NULL DEFAULT 0, plusVouchers INT(11) NOT NULL DEFAULT 0, premiumVouchers INT(11) NOT NULL DEFAULT 0, goldenVouchers INT(11) NOT NULL DEFAULT 0, CONSTRAINT accountStats_ibfk_1 FOREIGN KEY (uuid) REFERENCES accounts (uuid) ON DELETE CASCADE ON UPDATE CASCADE)")
// compensations
tx.Exec("CREATE TABLE IF NOT EXISTS accountCompensations (id INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY, uuid BINARY(16) NOT NULL, voucherType INT(11) NOT NULL, count INT(11) NOT NULL DEFAULT 1, claimed BIT(1) NOT NULL DEFAULT b'0', CONSTRAINT accountCompensations_ibfk_1 FOREIGN KEY (uuid) REFERENCES accounts (uuid) ON DELETE CASCADE ON UPDATE CASCADE)")
tx.Exec("CREATE INDEX IF NOT EXISTS accountCompensationsByUuid ON accountCompensations (uuid)")
// daily runs
tx.Exec("CREATE TABLE IF NOT EXISTS dailyRuns (date DATE NOT NULL PRIMARY KEY, seed CHAR(24) CHARACTER SET ascii COLLATE ascii_bin NOT NULL)")
tx.Exec("CREATE INDEX IF NOT EXISTS dailyRunsByDateAndSeed ON dailyRuns (date, seed)")
tx.Exec("CREATE TABLE IF NOT EXISTS dailyRunCompletions (uuid BINARY(16) NOT NULL, seed CHAR(24) CHARACTER SET ascii COLLATE ascii_bin NOT NULL, mode INT(11) NOT NULL DEFAULT 0, score INT(11) NOT NULL DEFAULT 0, timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (uuid, seed), CONSTRAINT dailyRunCompletions_ibfk_1 FOREIGN KEY (uuid) REFERENCES accounts (uuid) ON DELETE CASCADE ON UPDATE CASCADE)")
tx.Exec("CREATE INDEX IF NOT EXISTS dailyRunCompletionsByUuidAndSeed ON dailyRunCompletions (uuid, seed)")
tx.Exec("CREATE TABLE IF NOT EXISTS accountDailyRuns (uuid BINARY(16) NOT NULL, date DATE NOT NULL, score INT(11) NOT NULL DEFAULT 0, wave INT(11) NOT NULL DEFAULT 0, timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (uuid, date), CONSTRAINT accountDailyRuns_ibfk_1 FOREIGN KEY (uuid) REFERENCES accounts (uuid) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT accountDailyRuns_ibfk_2 FOREIGN KEY (date) REFERENCES dailyRuns (date) ON DELETE NO ACTION ON UPDATE NO ACTION)")
tx.Exec("CREATE INDEX IF NOT EXISTS accountDailyRunsByDate ON accountDailyRuns (date)")
// save data
tx.Exec("CREATE TABLE IF NOT EXISTS systemSaveData (uuid BINARY(16) PRIMARY KEY, data LONGBLOB, timestamp TIMESTAMP)")
tx.Exec("CREATE TABLE IF NOT EXISTS sessionSaveData (uuid BINARY(16), slot TINYINT, data LONGBLOB, timestamp TIMESTAMP, PRIMARY KEY (uuid, slot))")
err = tx.Commit()
if err != nil {
panic(err)
}
// TODO temp code
_, err = os.Stat("userdata")
if err != nil {
if os.IsNotExist(err) { // not found, do not migrate
return nil
} else {
log.Fatalf("failed to stat userdata directory: %s", err)
return err
}
}
entries, err := os.ReadDir("userdata")
if err != nil {
log.Fatalln(err)

View File

@ -0,0 +1,14 @@
services:
db:
image: mariadb:11
container_name: pokerogue-db-local
restart: on-failure
environment:
MYSQL_ROOT_PASSWORD: admin
MYSQL_DATABASE: pokeroguedb
MYSQL_USER: pokerogue
MYSQL_PASSWORD: pokerogue
ports:
- "3306:3306"
volumes:
- ./.data/db:/var/lib/mysql

View File

@ -34,10 +34,10 @@ func main() {
debug := flag.Bool("debug", false, "use debug mode")
proto := flag.String("proto", "tcp", "protocol for api to use (tcp, unix)")
addr := flag.String("addr", "0.0.0.0", "network address for api to listen on")
addr := flag.String("addr", "0.0.0.0:8001", "network address for api to listen on")
dbuser := flag.String("dbuser", "pokerogue", "database username")
dbpass := flag.String("dbpass", "", "database password")
dbpass := flag.String("dbpass", "pokerogue", "database password")
dbproto := flag.String("dbproto", "tcp", "protocol for database connection")
dbaddr := flag.String("dbaddr", "localhost", "database address")
dbname := flag.String("dbname", "pokeroguedb", "database name")