Commit Graph

68 Commits

Author SHA1 Message Date
Houmgaor
e48d33ca76 feat(shutdown): passive drain and rename DisableSoftCrash
Shutdown now proceeds in three phases: close listeners immediately on
signal, broadcast the in-game countdown, wait up to ShutdownDrainSeconds
(default 30) for sessions to disconnect naturally via DrainPassive, then
force-close any stragglers. This prevents players from entering new
quests after the countdown starts, and lets mid-quest sessions finish
saving without being killed mid-write. A second SIGINT during passive
drain cancels it so the force-close phase runs immediately.

Renamed DisableSoftCrash -> DisableShutdownCountdown since the flag
controls the countdown, not crash behaviour. Existing config.json files
keep working via a Viper alias on the legacy key.

Closes #179.
2026-04-06 19:32:35 +02:00
Houmgaor
9b0f735335 chore(merge): merge develop into main for 9.4.0 cycle
Brings 53 develop commits (i18n, Diva, campaign, guild invites, save
transfer, return/rookie guilds, hunting tournament, JSON quest/scenario
loaders, Ghidra-derived user binary parsing, and misc fixes) onto main
now that 9.3.2 has been tagged and released.

Resolves two overlap zones:

1. Migration number collision. Main shipped 0010_fix_zero_rasta_id and
   0011_fix_stale_boost_time in 9.3.2; develop had independently
   numbered 0010_campaign..0015_tournament. The migration runner keys
   applied versions by integer, so coexisting files with the same
   numeric prefix would silently skip each other. Develop's files have
   been renumbered to 0016..0021, leaving main's 0010/0011 intact. A
   schema_version rename script is required on any server that had
   already applied the old develop numbers (only frontier.mogapedia.fr
   at the time of this merge).

2. CHANGELOG.md. Develop's in-progress feature entries move into
   [Unreleased] with updated migration references; the [9.3.2] section
   is preserved verbatim.

main.go version string bumped to 9.4.0-dev to mark the new cycle.

Full test suite (go test -race ./...) passes.
2026-04-06 19:06:09 +02:00
Houmgaor
e9510c3b3b chore(release): 9.3.2 2026-04-06 18:37:18 +02:00
Houmgaor
d1d3bb8698 chore(merge): merge main into develop
Resolves CHANGELOG.md conflict: preserve develop's [Unreleased] block,
insert the [9.3.1] section from main, remove the duplicate
DisableSaveIntegrityCheck entry that had been in [Unreleased].
2026-03-23 11:15:20 +01:00
Houmgaor
3803fd431b chore(release): prepare 9.3.1 2026-03-23 10:13:00 +01:00
Houmgaor
e6a415310f fix(startup): detect duplicate channel ports before binding
Catches misconfigured port collisions early and reports which channel
already claimed the port, rather than failing with a generic OS error.
2026-03-22 21:25:55 +01:00
Houmgaor
c43be33680 feat(shutdown): graceful drain + configurable countdown
Add ShutdownAndDrain to the channel server (issue #179 non-breaking
subset): on SIGTERM/SIGINT, force-close all active sessions so that
logoutPlayer runs for each one (saves character data, cleans up stages
and semaphores), then poll until the session map empties or a 30-second
context deadline passes.  Existing Shutdown() is unchanged.

Add ShutdownCountdownSeconds int config field (default 10) alongside
DisableSoftCrash so operators can tune the broadcast countdown without
patching code.  A zero value falls back to 10 for safety.

Fix pre-existing test failures: MsgMhfAddRewardSongCount has a complete
Parse() implementation so it no longer belongs in the "NOT IMPLEMENTED"
parse test list; its handler test is updated to pass a real packet and
assert an ACK response instead of calling with nil.
2026-03-21 01:36:31 +01:00
Houmgaor
835f97d3c2 fix(shutdown): force-stop on second SIGINT during countdown
A second Ctrl+C/SIGINT while the 10-second shutdown countdown is
running now exits immediately instead of being silently dropped.
The signal channel is buffered to 2 so the second signal is captured,
and the countdown select exits via os.Exit(1) on receipt.
2026-03-18 21:36:24 +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
c3d089cca1 chore: update version string from 9.3b to 9.3.0 2026-03-10 11:44:52 +01:00
Houmgaor
69ad4ca54a fix: diagnose specific database connection errors at startup
Instead of a generic "Failed to ping" message, detect the actual
cause and show a targeted fix:
- net error → PostgreSQL not reachable, check if it's running
- pq 28P01 → wrong password, update config.json
- pq 3D000 → database doesn't exist, create it
2026-03-05 23:25:00 +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
d38fef08bb refactor(discordbot): introduce Session interface for testability
Extract a Session interface from *discordgo.Session so DiscordBot methods
can be tested with a mock — no live Discord connection required. Add
AddHandler, RegisterCommands, and UserID methods to DiscordBot so
external callers (main.go, sys_channel_server.go) no longer reach
through bot.Session directly. Rewrite tests with a mockSession,
raising discordbot coverage from 12.5% to 66.7%.
2026-02-27 11:45:20 +01:00
Houmgaor
bcdc4e0b7e fix(setup): reduce friction in installation procedure
- Add Language default ("jp") so missing field no longer produces empty
  string, and include language selector in setup wizard
- Add --setup flag to re-run wizard even when config.json exists,
  providing a recovery path for corrupted configs
- Auto-apply seed data on fresh databases so users who skip the wizard
  still get shops, events, and gacha
- Fix stale docs referencing non-existent init/setup.sh and
  schemas/patch-schema/ in docker/README, CONTRIBUTING, and README
2026-02-23 23:39:49 +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
Houmgaor
6a7db47723 feat(setup): add web-based first-run configuration wizard
When config.json is missing, Erupe now launches a temporary HTTP server
on port 8080 serving a guided setup wizard instead of exiting with a
cryptic error. The wizard walks users through database connection,
schema initialization (pg_restore + SQL migrations), and server settings,
then writes config.json and continues normal startup without restart.
2026-02-23 20:55:56 +01:00
Houmgaor
b96cd0904b fix: ease onboarding with startup warnings and doc corrections
- Warn at startup when quest files are missing (clients crash without
  them) and point users to the download link
- Fix Host config description: it's the advertised IP, not a bind
  address — 0.0.0.0 was wrong advice
- Load bundled schemas (shops, events, gacha) in Docker init so new
  users get working demo data out of the box
- Renumber duplicate patch schema 28 → 32 to resolve numbering
  collision
- Fix patch schema example filename to use hyphens matching actual
  files
2026-02-23 20:23:08 +01:00
Houmgaor
53b5bb3b96 refactor(channelserver): remove Channels fallbacks, use Registry as sole cross-channel API
main.go always sets both Channels and Registry together, making the
Channels fallback paths dead code. This removes:

- Server.Channels field from the Server struct
- 3 if/else fallback blocks in handlers_session.go (replaced with
  Registry.FindChannelForStage, SearchSessions, SearchStages)
- 1 if/else fallback block in handlers_guild_ops.go (replaced with
  Registry.NotifyMailToCharID)
- 3 method fallbacks in sys_channel_server.go (WorldcastMHF,
  FindSessionByCharID, DisconnectUser now delegate directly)

Updates anti-patterns.md #6 to "accepted design" — Session struct is
appropriate for this game server's handler pattern, and cross-channel
coupling is now fully routed through the ChannelRegistry interface.
2026-02-22 16:16:44 +01:00
Houmgaor
f17cb96b52 refactor(config): rename package _config to config with cfg alias
The config package used `package _config` with a leading underscore,
which is unconventional in Go. Rename to `package config` (matching the
directory name) and use `cfg` as the standard import alias across all
93 importing files.
2026-02-21 13:20:15 +01:00
Houmgaor
f9d9260274 fix(channelserver): configure DB pool and add transactions for guild ops
sqlx.Open was called with no pool configuration, risking PostgreSQL
connection exhaustion under load. Set max open/idle conns and lifetimes.

CreatePost INSERT + soft-delete UPDATE were two separate queries with
no transaction, risking inconsistent state on partial failure.

CollectAdventure used SELECT then UPDATE without a lock, allowing
concurrent guild members to double-collect. Now uses SELECT FOR UPDATE
within a transaction.
2026-02-21 00:29:09 +01:00
Houmgaor
5f3c843082 refactor(config): eliminate ErupeConfig global variable
Replace the mutable global `_config.ErupeConfig` with dependency
injection across 79 files. Config is now threaded through existing
paths: `ClientContext.RealClientMode` for packet encoding, `s.server.
erupeConfig` for channel handlers, and explicit parameters for utility
functions. This removes hidden coupling, enables test parallelism
without global save/restore, and prevents low-level packages from
reaching up to the config layer.

Key changes:
- Enrich ClientContext with RealClientMode for packet files
- Add mode parameter to CryptConn, mhfitem, mhfcourse functions
- Convert handlers_commands init() to lazy sync.Once initialization
- Delete global var, init(), and helper functions from config.go
- Update all tests to pass config explicitly
2026-02-20 17:07:42 +01:00
Houmgaor
754b5a3bff feat(channelserver): decouple channel servers for independent operation (#33)
Enable multiple Erupe instances to share a single PostgreSQL database
without destroying each other's state, fix existing data races in
cross-channel access, and lay groundwork for future distributed
channel server deployments.

Phase 1 — DB safety:
- Scope DELETE FROM servers/sign_sessions to this instance's server IDs
- Fix ci++ bug where failed channel start shifted subsequent IDs

Phase 2 — Fix data races in cross-channel access:
- Lock sessions map in FindSessionByCharID and DisconnectUser
- Lock stagesLock in handleMsgSysLockGlobalSema
- Snapshot sessions/stages under lock in TransitMessage types 1-4
- Lock channel when finding mail notification targets

Phase 3 — ChannelRegistry interface:
- Define ChannelRegistry interface with 7 cross-channel operations
- Implement LocalChannelRegistry with proper locking
- Add SessionSnapshot/StageSnapshot immutable copy types
- Delegate WorldcastMHF, FindSessionByCharID, DisconnectUser to Registry
- Migrate LockGlobalSema and guild mail handlers to use Registry
- Add comprehensive tests including concurrent access

Phase 4 — Per-channel enable/disable:
- Add Enabled *bool to EntranceChannelInfo (nil defaults to true)
- Skip disabled channels in startup loop, preserving ID stability
- Add IsEnabled() helper with backward-compatible default
- Update config.example.json with Enabled field
2026-02-19 18:13:34 +01:00
Houmgaor
2a0e3e2c84 fix: re-enable CI lint job and fix ~65 lint errors (partial)
Re-enable the golangci-lint job in CI (disabled Oct 2025), update to
Go 1.25 and golangci-lint-action v7. Fix errcheck, gosimple S1009,
staticcheck SA4031 and SA2001 errors across 54 files. Remaining ~39
lint errors will be addressed in follow-up commits.
2026-02-17 17:59:00 +01:00
Houmgaor
d2b5bb72f8 refactor: extract gametime package, replace fmt.Printf with zap logging
Move time utilities (TimeAdjusted, TimeMidnight, TimeWeekStart, TimeWeekNext,
TimeGameAbsolute) from channelserver into common/gametime to break the
inappropriate dependency where signserver, entranceserver, and api imported
the 38K-line channelserver package just for time functions.

Replace all fmt.Printf debug logging in sys_session.go and handlers_object.go
with structured zap logging for consistent observability.
2026-02-17 17:54:51 +01:00
Houmgaor
90c8a50316 fix(main): wrong configuration in last commit. 2025-10-18 19:24:24 +02:00
Houmgaor
f410cbd48b chore: minor files update. 2025-10-18 18:57:16 +02:00
stratic-dev
d123182a2f Renamed signv2 to api and enabled it by default 2024-03-15 19:37:55 +00:00
wish
0d28637095 support long messages, rename to RelayChannel, move commands out of main 2024-01-01 21:22:51 +11:00
wish
b3a265e218 Merge remote-tracking branch 'origin/main' into feature/discord-login 2023-12-31 22:38:08 +11:00
wish
0ea0dc217b simplify config 2023-12-31 12:51:24 +11:00
Matthew
38b57c6d98 refactor: Change to using rand.Read instead of whatever the hell else was before 2023-11-27 01:08:40 -05:00
Matthew
226adddc43 feat: Generate hashes for Discord and allow password resets 2023-11-26 16:47:54 -05:00
wish
22b1d1b716 Merge pull request #99 from rockisch/dev-proxy
Add dev proxy config
2023-11-26 20:53:55 +11:00
wish
a2f488e5e3 move ProxyPort config out of DevMode 2023-11-26 20:50:08 +11:00
rockisch
e39630564e Add dev proxy config 2023-11-22 00:01:04 -03:00
wish
2f8d09b09e more GuildTresure optimisation 2023-10-24 21:21:21 +11:00
wish
f23da6fba5 remove Season from database 2023-07-18 23:00:00 +10:00
wish
1f93419cb7 add support for more versions 2023-07-03 00:30:44 +10:00
wish
b4df642ee3 add ClientMode config option 2023-06-18 20:31:18 +10:00
wish
23138b2d5b update versioning 2023-04-11 21:08:00 +10:00
wish
f0db7f0a19 finalise 9.2 2023-04-01 17:58:26 +11:00
wish
ce30c1231d remove countdown on auto restart 2023-03-09 22:23:45 +11:00
wish
dcab41a6c7 rework logging code 2023-03-09 22:05:32 +11:00
wish
d5dc15fc93 implement guild semaphore locking 2023-03-09 17:31:43 +11:00
wish
cfe6bd118c update startup logging 2023-03-05 22:24:33 +11:00
wish
aa3deca70a remove obsolete LauncherServer 2022-12-31 01:57:16 +11:00
wish
f8e21483ef escape db connection arguments 2022-12-24 00:09:38 +11:00
wish
ec2ff61199 update versioning 2022-11-07 08:43:46 +11:00
wish
492e64d0d0 rename sign server and merge conflicts 2022-11-07 08:37:40 +11:00