Commit Graph

17 Commits

Author SHA1 Message Date
Houmgaor
01b829d0e9 feat(savedata): add tier 2 integrity protections
Strengthen savedata persistence against corruption and race conditions:

- SHA-256 checksum: hash the decompressed blob on every save, store in
  new savedata_hash column, verify on load to detect silent corruption.
  Pre-existing characters with no hash are silently upgraded on next save.
- Atomic transactions: wrap character data + house data + hash + backup
  into a single DB transaction via SaveCharacterDataAtomic, so a crash
  mid-save never leaves partial state.
- Per-character save mutex: CharacterLocks (sync.Map of charID → Mutex)
  serializes concurrent saves for the same character, preventing races
  that could defeat corruption detection. Different characters remain
  fully independent.

Migration 0008 adds the savedata_hash column to the characters table.
2026-03-17 19:21:55 +01:00
Houmgaor
d578e68b79 docs(gacha): clarify G1-GG vs ZZ configuration to prevent client crashes (#175)
The G1-GG gacha code path (PR #150) included example data in comments
that used non-zero item_type/item_number/item_quantity on entry_type=100
rows. Users copying these values for ZZ servers caused client crashes
because the ZZ client interprets those fields as material requirements.

- Replace unclear PR #150 comment with explicit WARNING against using
  G1-GG example values for ZZ servers
- Add gacha configuration guide header to GachaDemo.sql explaining the
  3-table system, entry types, and item type codes
- Add Mega Potion example showing correct way to add custom items
- Reference Enumerations.md for the full item type list
2026-03-17 19:11:59 +01:00
Houmgaor
b40217c7fe feat(savedata): add tier 1 data integrity protections
Prevent savedata corruption and denial-of-service by adding four layers
of protection to the save pipeline:

- Bounded decompression (nullcomp.DecompressWithLimit): caps output size
  to prevent OOM from crafted payloads that expand to exhaust memory
- Bounds-checked delta patching (deltacomp.ApplyDataDiffWithLimit):
  validates offsets before writing, returns errors for negative offsets,
  truncated patches, and oversized output; ApplyDataDiff now returns
  original data on error instead of partial corruption
- Size limits on save handlers: rejects compressed payloads >512KB and
  decompressed data >1MB before processing; applied to main savedata,
  platedata, and platebox diff paths
- Rotating savedata backups: 3 slots per character with 30-minute
  interval, snapshots the previous state before overwriting, backed by
  new savedata_backups table (migration 0007)
2026-03-17 19:03:43 +01:00
Houmgaor
5009a37d19 fix: create user_binary row on character creation (#176)
New characters were missing their user_binary record, preventing them
from entering their house. Both sign server and API character creation
paths now insert the row. A backfill migration fixes existing databases.
2026-03-16 17:11:55 +01:00
Houmgaor
7657ddbd50 fix: prevent startup panics on databases missing base tables
The catch-up migration now creates the servers table if missing, like
sign_sessions. Startup cleanup queries in main.go use Exec instead of
MustExec so missing tables log warnings rather than panicking.
2026-03-16 00:04:45 +01:00
Houmgaor
31aa02a8e2 fix(migrations): create sign_sessions table before altering it
The catch-up migration assumed sign_sessions already existed, but
databases created from older schema dumps may not have this table.
Adding CREATE TABLE IF NOT EXISTS prevents the migration from failing
with "relation does not exist" on those databases.
2026-03-15 23:53:10 +01:00
Houmgaor
ba7ec122f8 revert: remove SQLite support
An MMO server without multiplayer defeats the purpose. PostgreSQL
is the right choice and Docker Compose already solves the setup
pain. This reverts the common/db wrapper, SQLite schema, config
Driver field, modernc.org/sqlite dependency, and all repo type
changes while keeping the dashboard, wizard, and CI improvements
from the previous commit.
2026-03-05 23:05:55 +01:00
Houmgaor
ecfe58ffb4 feat: add SQLite support, setup wizard enhancements, and live dashboard
Add zero-dependency SQLite mode so users can run Erupe without
PostgreSQL. A transparent db.DB wrapper auto-translates PostgreSQL
SQL ($N placeholders, now(), ::casts, ILIKE, public. prefix,
TRUNCATE) for SQLite at runtime — all 28 repo files use the wrapper
with no per-query changes needed.

Setup wizard gains two new steps: quest file detection with download
link, and gameplay presets (solo/small/community/rebalanced). The API
server gets a /dashboard endpoint with auto-refreshing stats.

CI release workflow now builds and pushes Docker images to GHCR
alongside binary artifacts on tag push.

Key changes:
- common/db: DB/Tx wrapper with 6 SQL translation rules
- server/migrations/sqlite: full SQLite schema (0001-0005)
- config: Database.Driver field ("postgres" or "sqlite")
- main.go: SQLite connection with WAL mode, single writer
- server/setup: quest check + preset selection steps
- server/api: /dashboard with live stats
- .github/workflows: Docker in release, deduplicate docker.yml
2026-03-05 18:00:30 +01:00
Houmgaor
03adb21e99 fix(channelserver): post-RC1 stabilization sprint
Fix rasta_id=0 overwriting NULL in SaveMercenary, which prevented
game state saving for characters without a mercenary (#163).

Also includes:
- CHANGELOG updated with all 10 post-RC1 commits
- Setup wizard fmt.Printf replaced with zap structured logging
- technical-debt.md updated with 6 newly completed items
- Scenario binary format documented (docs/scenario-format.md)
- Tests: alliance nil-guard (#171), handler dispatch table,
  migrations (sorted/SQL/baseline), setup wizard (10 tests),
  protbot protocol sign/entrance/channel (23 tests)
2026-03-05 16:39:15 +01:00
Houmgaor
9a5a8dfb36 fix(migrations): add IF NOT EXISTS guard to alliance recruiting column
Without this guard, migration 0004 fails on databases where the column
already exists, such as during the existing-DB-without-schema-version
upgrade path where 0001 baseline is auto-marked and 0002-0005 re-applied.
2026-02-28 19:26:21 +01:00
Houmgaor
bb16306f91 test(migrations): update expected counts after adding migrations 0004-0005
Test assertions were still expecting 3 total migrations from when only
0001-0003 existed. Updated to reflect 5 migrations (0001-0005).
2026-02-28 18:02:36 +01:00
Houmgaor
fa09e4a39c fix(migrations): drop unused data column from distribution table (#169)
The distribution table had a `data bytea NOT NULL` column that was never
read by the Go code — item data is stored in distribution_items instead.
The NOT NULL constraint forced dummy values in seed data and test inserts.

Remove the column from the baseline schema, seed data, and tests, and
add migration 0005 to drop it from existing databases.
2026-02-27 18:19:57 +01:00
Houmgaor
d6938f2a27 fix(guild): implement alliance application toggle (#166)
Alliance applications were hardcoded to always-open. Add a `recruiting`
column to guild_alliances and handle OperateJoint actions 0x06 (Allow)
and 0x07 (Deny) confirmed via Wii U debug symbols. Only the parent
guild leader can toggle the setting, matching the existing disband guard.
2026-02-27 14:59:18 +01:00
Houmgaor
a399ba7419 fix(shop): fix syntax error and update migration tests after #150 merge
PR #150 introduced a double brace `{  {` on handlers_shop.go:109 that
broke compilation. Migration tests were also hardcoded for 1 migration
but 3 now exist (0001–0003).
2026-02-26 21:55:12 +01:00
Houmgaor
d9f90e3b46 fix(shop): resolve ambiguous column and missing unique constraint in RecordPurchase
The ON CONFLICT upsert referenced unqualified "bought" which PostgreSQL
rejected as ambiguous, and the table lacked the UNIQUE constraint needed
for ON CONFLICT. Adds a unique index on (character_id, shop_item_id) via
migration 0003 and qualifies the column as shop_items_bought.bought.
2026-02-24 17:06:38 +01:00
Houmgaor
2f92b4ff62 feat(db): add catch-up migration for partially-patched databases
The migration consolidation (27fb0fa) merged 33 incremental patches
into 0001_init.sql and marks the baseline as applied for any existing
database. Users who only ran some of the 33 patches have schema gaps
that cause runtime errors.

0002_catch_up_patches.sql replays all 33 patches (skipping 15 and 20,
which are destructive data resets) with idempotency guards so it is a
no-op on fresh or fully-patched databases and fills gaps otherwise.
2026-02-24 11:37:37 +01:00
Houmgaor
27fb0faa1e feat(db): add embedded auto-migrating schema system
Replace 4 independent schema management code paths (Docker shell
script, setup wizard pg_restore, test helpers, manual psql) with a
single migration runner embedded in the server binary.

The new server/migrations/ package uses Go embed to bundle all SQL
schemas. On startup, Migrate() creates a schema_version tracking
table, detects existing databases (auto-marks baseline as applied),
and runs pending migrations in transactions.

Key changes:
- Consolidated init.sql + 9.2-update + 33 patches into 0001_init.sql
- Setup wizard simplified to single "Apply schema" checkbox
- Test helpers use migrations.Migrate() instead of pg_restore
- Docker no longer needs schema volume mounts or init script
- Seed data (shops, events, gacha) embedded and applied via API
- Future migrations just add 0002_*.sql files — no manual steps
2026-02-23 21:19:21 +01:00