Commit Graph

856 Commits

Author SHA1 Message Date
Houmgaor
1d507b3d11 fix: replace fmt.Sprintf in logger calls with structured fields and add LoopDelay default
fmt.Sprintf inside zap logger calls defeats structured logging,
making log aggregation and filtering harder. All 6 sites now use
proper zap fields (zap.Uint32, zap.Uint8, zap.String).

LoopDelay had no viper.SetDefault, so omitting it from config.json
caused a zero-value (0 ms) busy-loop in the recv loop. Default is
now 50 ms, matching config.example.json.
2026-02-22 16:32:43 +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
cd630a7a58 test(channelserver): add handler tests for session, gacha, shop, and plate
Cover critical paths that previously had no test coverage:
- Session: login success/error paths, ping, logkey, record log,
  global sema lock/unlock, rights reload, announce
- Gacha: point queries, coin deduction, item receive with overflow
  and freeze, normal/stepup/box gacha play, stepup status lifecycle,
  weighted random selection
- Shop: enumeration across all shop types, exchange purchases,
  fpoint-to-item and item-to-fpoint exchange, fpoint exchange list
  with Z2 vs ZZ encoding
- Plate: load/save for platedata, platebox, platemyset with
  oversized payload rejection, diff path, and cache invalidation

Add mockSessionRepo, mockGachaRepo, mockShopRepo, and
mockUserRepoGacha to support the new test scenarios. Add
loadColumnErr field to mockCharacterRepo for diff-path error
testing.
2026-02-22 16:05:25 +01:00
Houmgaor
ad4afb4d3b refactor(channelserver): replace global stagesLock with sync.Map-backed StageMap
The global stagesLock sync.RWMutex protected map[string]*Stage, causing
all stage operations to contend on a single lock even for unrelated
stages. Any stage creation or deletion blocked all reads server-wide.

Replace with a typed StageMap wrapper around sync.Map which provides
lock-free reads and allows concurrent writes to disjoint keys. Per-stage
sync.RWMutex remains unchanged for protecting individual stage state.

StageMap exposes Get, GetOrCreate, StoreIfAbsent, Store, Delete, and
Range methods. Updated ~50 call sites across 6 production files and
9 test files.
2026-02-22 15:47:21 +01:00
Houmgaor
2a5cd50e3f test(channelserver): add handler tests for guild info, alliance, cooking, adventure, and treasure
Cover 5 more handler files with mock-based unit tests, bringing
package coverage from 43.7% to 47.7%. Extend mockGuildRepoOps with
alliance, cooking, adventure, treasure hunt, and hunt data methods.
2026-02-21 18:10:19 +01:00
Houmgaor
7852e8505f test(channelserver): add handler tests for guild ops, scout, board, and items
Cover the 4 handler files that had no tests: handlers_guild_ops.go,
handlers_guild_scout.go, handlers_guild_board.go, and handlers_items.go.
44 new tests exercise the error-logging paths added in 8fe6f60 and the
core handler logic (disband, resign, apply, leave, accept/reject/kick,
scout answer, message board CRUD, weekly stamps, item box parsing).

New mock types: mockGuildRepoOps (enhanced guild repo with configurable
errors and state tracking), mockUserRepoForItems, mockStampRepoForItems,
mockHouseRepoForItems. Coverage rises from 41.1% to 43.7%.
2026-02-21 17:58:08 +01:00
Houmgaor
8fe6f60813 fix(channelserver): add error logging to silently ignored repo calls
19 repository calls across 8 handler files were silently discarding
errors with `_ =`, making database failures invisible. Add structured
logging at appropriate levels: Error for write operations where data
loss may occur (guild saves, member updates), Warn for read operations
where handlers continue with zero-value defaults (application checks,
warehouse loads, item box queries). No control flow changes.
2026-02-21 17:37:29 +01:00
Houmgaor
6fbd294575 refactor(channelserver): eliminate *sql.Tx from repository interfaces
Hide transaction management inside repository implementations so
interfaces only expose domain types, enabling clean mocking and
decoupling handlers from PostgreSQL internals.

- Replace BeginTx + UpdateEventQuestStartTime with batch
  UpdateEventQuestStartTimes that manages its own transaction
- Remove tx parameter from CreateApplication, add composite
  CreateApplicationWithMail for atomic scout+mail operations
- Remove SendMailTx from MailRepo (sole caller migrated)
- Remove database/sql import from repo_interfaces.go
2026-02-21 14:56:59 +01:00
Houmgaor
35d8471d59 fix(channelserver): resolve all golangci-lint issues and add handler tests
Fix errcheck violations across 11 repo files by wrapping deferred
rows.Close() and tx.Rollback() calls to discard the error return.
Fix unchecked Scan/Exec calls in guild store tests. Fix staticcheck
SA9003 empty branch in test helpers.

Add 6 mock-based unit tests for GetCharacterSaveData covering nil
savedata, sql.ErrNoRows, DB errors, compressed round-trip,
new-character skip, and config mode/pointer propagation.
2026-02-21 14:47:25 +01:00
Houmgaor
bd8e30d570 refactor(channelserver): eliminate *sql.Rows from EventRepo.GetEventQuests
Return []EventQuest instead of a raw database cursor, removing the last
*sql.Rows leak from the repository layer. The handler now iterates a
slice, and makeEventQuest reads fields from the struct directly instead
of scanning rows twice. This makes the method fully mockable and
eliminates the risk of unclosed cursors.
2026-02-21 14:37:29 +01:00
Houmgaor
f2f5696a22 test(channelserver): add store tests for new repository methods
Cover the three repository methods added in the previous commit:
- CharacterRepo.LoadSaveData: normal, new-character, and not-found cases
- EventRepo.GetEventQuests: empty, multi-row, ordering, tx commit/rollback
- UserRepo.BanUser: permanent, temporary, and both upsert directions
2026-02-21 14:21:32 +01:00
Houmgaor
2be589beae refactor(channelserver): eliminate *sqlx.Rows/*sql.Rows from repository interfaces
Move scan loops from handlers into repository methods so that interfaces
return typed slices instead of leaking database cursors. This fixes
resource leaks (7 of 12 call sites never closed rows) and makes all
12 methods mockable for unit tests.

Affected repos: CafeRepo, ShopRepo, EventRepo, RengokuRepo, DivaRepo,
ScenarioRepo, MiscRepo, MercenaryRepo. New structs: DivaEvent,
MercenaryLoan, GuildHuntCatUsage. EventRepo.GetEventQuests left as-is
(requires broader Server refactor).
2026-02-21 14:16:58 +01:00
Houmgaor
a9cca84bc3 refactor(channelserver): move remaining s.server.db calls into repositories
Eliminate the last three direct DB accesses from handler code:

- CharacterRepo.LoadSaveData: replaces db.Query in GetCharacterSaveData,
  using QueryRow instead of Query+Next for cleaner single-row access
- EventRepo.GetEventQuests, UpdateEventQuestStartTime, BeginTx: moves
  event quest enumeration and rotation queries behind the repo layer
- UserRepo.BanUser: consolidates permanent/temporary ban upserts into a
  single method with nil/*time.Time semantics
2026-02-21 14:08:01 +01:00
Houmgaor
9a473260b2 test(channelserver): add mock-based handler unit tests
Leverage the new repository interfaces to test handler logic without
a database. Adds shared mock implementations (achievement, mail,
character, goocoo, guild) and 32 new handler tests covering
achievement, mail, cafe/boost, and goocoo handlers.
2026-02-21 14:01:52 +01:00
Houmgaor
da1e62d7c6 test(channelserver): add store tests and document lock ordering
Add unit tests with race-detector coverage for QuestCache,
UserBinaryStore, and MinidataStore (18 tests covering hits, misses,
expiry, copy isolation, and concurrent access).

Document the lock acquisition order on the Server struct to prevent
future deadlocks: Server.Mutex → stagesLock → Stage → semaphoreLock.
2026-02-21 13:56:46 +01:00
Houmgaor
1d5026c3a5 refactor(channelserver): introduce repository interfaces for all 21 repos
Replace concrete pointer types on the Server struct with interfaces to
decouple handlers from PostgreSQL implementations. This enables mock/stub
injection for unit tests and opens the door to alternative storage
backends (SQLite, in-memory).

Also adds 9 missing repo initializations to SetTestDB() (event,
achievement, shop, cafe, goocoo, diva, misc, scenario, mercenary)
to match NewServer().
2026-02-21 13:52:28 +01:00
Houmgaor
3b044fb987 fix(channelserver): handle silently swallowed DB scan and exec errors
Several handlers discarded errors from rows.Scan() and db.Exec(),
masking data corruption or connection issues. Scan failures in diva
schedule, event quests, and trend weapons are now logged or returned.
InitializeWarehouse now surfaces its insert error to the caller.
2026-02-21 13:49:25 +01:00
Houmgaor
9da467e00f perf(channelserver): use package-level map for ignored opcodes
The ignored() function is called on every packet when debug logging
is enabled. It was rebuilding a slice and map from scratch each time.
Move to a package-level map initialized once at startup.
2026-02-21 13:42:33 +01:00
Houmgaor
c04fa504cc refactor(channelserver): extract UserBinaryStore and MinidataStore
The userBinary and minidata maps with their locks were spread across
Server as raw fields with manual lock management. Cross-channel session
searches also required acquiring nested locks (server lock + binary
lock). Encapsulating in dedicated types eliminates the nested locking
and reduces Server's field count by 4.
2026-02-21 13:39:44 +01:00
Houmgaor
2757a5432f refactor(channelserver): extract QuestCache from Server struct
The quest cache fields (lock, data map, expiry map) were spread across
Server with manual lock management. The old read path also missed the
RLock entirely, creating a data race. Encapsulating in a dedicated type
fixes the race and reduces Server's field count by 2.
2026-02-21 13:35:04 +01:00
Houmgaor
f689770e94 fix(channelserver): handle db.Begin() error in event quest enumeration
A failed Begin() returned a nil tx that would panic on the next
tx.Exec() call. Now logs the error, closes rows, and returns an
empty response gracefully.
2026-02-21 13:30:25 +01:00
Houmgaor
2738b19c32 refactor(channelserver): extract Goocoo, Diva, Misc, Scenario, and Mercenary repositories
Move remaining raw s.server.db.* queries from handler files into
dedicated repository structs, completing the repository extraction
effort. Also adds SaveCharacterData and SaveHouseData to
CharacterRepository.

Fixes guild_hunts query to select both cats_used and start columns
to match the existing two-column Scan call. Adds slot index
validation in GoocooRepository to prevent SQL injection via
fmt.Sprintf.
2026-02-21 13:27:08 +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
ad73f2fb55 refactor(channelserver): extract Event, Achievement, Shop, and Cafe repositories
Move 22 raw SQL queries from 4 handler files into dedicated repository
structs, continuing the repository extraction pattern. Achievement
insert uses ON CONFLICT DO NOTHING to eliminate check-then-insert race,
and IncrementScore validates the column index to prevent SQL injection.
2026-02-21 13:13:55 +01:00
Houmgaor
87040c55bb fix(channelserver): prevent guild RP rollover race and redundant stepup query
RolloverDailyRP now locks the guild row with SELECT FOR UPDATE and
re-checks rp_reset_at inside the transaction, so concurrent callers
cannot double-rollover and zero out freshly donated RP.

Gacha stepup entry-type check is now skipped when the row was already
deleted as stale, avoiding a redundant DELETE on step 0.
2026-02-21 00:53:10 +01:00
Houmgaor
f584c5a688 feat(channelserver): add daily noon resets for gacha stepup and guild RP
Gacha stepup progress now resets when queried after the most recent
noon boundary, using a new created_at column on gacha_stepup.

Guild member rp_today rolls into rp_yesterday lazily when members are
enumerated after noon, using a new rp_reset_at column on guilds.

Both follow the established lazy-reset pattern from the cafe handler.
2026-02-21 00:50:55 +01:00
Houmgaor
7932d8ac06 feat(guild): persist weekly bonus exceptional user count
The handler was a stub that discarded pkt.NumUsers. Now it
looks up the player's guild and atomically accumulates the
count via a new weekly_bonus_users column on the guilds table.
2026-02-21 00:42:16 +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
0a489e7cc5 fix(channelserver): fix flaky integration tests from 3 isolation issues
- testhelpers_db: retry truncateAllTables up to 3 times on deadlock,
  which occurs when previous tests' goroutines still hold DB connections
- handlers_rengoku_integration_test: restore rengoku_score table after
  TestRengokuData_SaveOnDBError drops it, preventing cascading failures
  in all subsequent rengoku tests
- client_connection_simulation_test: fix TestClientConnection_PacketDuringLogout
  to accept both race outcomes (save-wins or logout-wins) since both
  handlers independently load from DB and last-writer-wins is valid
2026-02-21 00:28:27 +01:00
Houmgaor
d640bec8af refactor(channelserver): extract StampRepository, DistributionRepository, and SessionRepository
Eliminate 18 direct s.server.db calls from handlers_items.go,
handlers_distitem.go, and handlers_session.go by moving queries into
dedicated repository types.

New repositories:
- StampRepository (7 methods, stamps table)
- DistributionRepository (4 methods, distribution/distribution_items)
- SessionRepository (4 methods, sign_sessions/servers)

Also adds ClearTreasureHunt and InsertKillLog to GuildRepository,
which already owns those tables for read operations.
2026-02-21 00:06:23 +01:00
Houmgaor
eb66de8ef9 fix(channelserver): correct 3 test bugs causing 10 deterministic failures
- Use users.frontier_points instead of characters.frontier_points
  (column moved in 9.2 schema migration) across savedata and session
  lifecycle tests
- Use BYTEA column (otomoairou) instead of INTEGER column
  (kouryou_point) in repo_character Load/SaveColumn tests
- Build blocked CSV from actual auto-incremented character IDs instead
  of hardcoded IDs in ListMember integration test
- Fix nil charRepo panic in CompleteSaveLoadCycle by using SetTestDB()
2026-02-20 23:55:02 +01:00
Houmgaor
f2f31cdfbb test(channelserver): add comprehensive handler-level tests for handlers_house
Cover all 14 handler functions in handlers_house.go with 25 new tests:

- 7 unit tests for guard paths (payload size limits, box index
  bounds, no-op handlers) that run without a database
- 18 integration tests against real PostgreSQL covering interior
  updates, house state/password, house enumeration by char ID and
  name, house loading with access control, mission data CRUD,
  title acquisition with dedup, warehouse operations (box names,
  usage limits, rename guards), item storage round-trips, and
  deco myset defaults

Introduces readAck() helper to parse MsgSysAck wire format from
the sendPackets channel, and setupHouseTest() for DB + session
scaffolding with user_binary row initialization.
2026-02-20 23:46:04 +01:00
Houmgaor
339487c3d8 fix(channelserver): eliminate test log spam, schema errors, and slow setup
Three fixes for the channelserver test suite:

- Add net.ErrClosed check in acceptClients() so a closed listener
  breaks the loop immediately instead of spinning and logging warnings.
  This is correct production behavior too.

- Remove -c flag from pg_restore that conflicted with CleanTestDB's
  prior DROP, causing "gook DROP CONSTRAINT" errors. Clean the schema
  with DROP SCHEMA CASCADE instead of per-table drops.

- Use sync.Once to apply the test schema once per binary run, then
  TRUNCATE tables for isolation. Reduces ~60 pg_restore + 29-patch
  cycles to a single setup pass.
2026-02-20 23:40:15 +01:00
Houmgaor
de3bf9173a refactor(channelserver): extract RengokuRepository and MailRepository
Move all direct DB access from handlers_rengoku.go (11 calls) and
handlers_mail.go (10 calls) into dedicated repository types, continuing
the established extraction pattern.

RengokuRepository provides UpsertScore and GetRanking, replacing a
3-call check/insert/update sequence and an 8-case switch of nearly
identical queries respectively.

MailRepository provides SendMail, SendMailTx, GetListForCharacter,
GetByID, MarkRead, MarkDeleted, SetLocked, and MarkItemReceived.
The old Mail.Send(), Mail.MarkRead(), GetMailListForCharacter, and
GetMailByID free functions are removed. Guild handlers that sent mail
via Mail.Send(s, ...) now call mailRepo directly.
2026-02-20 23:31:27 +01:00
Houmgaor
b17b2f3b38 fix(channelserver): consolidate stages map locking to prevent data race
The stages map was protected by two incompatible locks: the embedded
Server.Mutex and Server.stagesLock (RWMutex). Since these are separate
mutexes they don't exclude each other, and many handlers accessed the
map with no lock at all.

Route all stages map access through stagesLock: read-only lookups use
RLock, writes (create/delete) use Lock. Per-stage field mutations
continue to use each stage's own RWMutex. Restructure
handleMsgSysUnlockStage to avoid holding stagesLock nested inside a
stage RLock, preventing potential deadlock with destructEmptyStages.
2026-02-20 23:21:14 +01:00
Houmgaor
b507057cc9 refactor(channelserver): extract FestaRepository and TowerRepository
Move all direct DB calls from handlers_festa.go (23 calls across 8
tables) and handlers_tower.go (16 calls across 4 tables) into
dedicated repository structs following the established pattern.

FestaRepository (14 methods): lifecycle cleanup, event management,
team souls, trial stats/rankings, user state, voting, registration,
soul submission, prize claiming/enumeration.

TowerRepository (12 methods): personal tower data (skills, progress,
gems), guild tenrouirai progress/scores/page advancement, tower RP.

Also fix pre-existing nil pointer panics in integration tests by
adding SetTestDB helper that initializes both the DB connection and
all repositories, and wire the done channel in createTestServerWithDB
to prevent Shutdown panics.
2026-02-20 23:09:51 +01:00
Houmgaor
a02251e486 fix(channelserver): mitigate house theme corruption on save (#92)
The game client sometimes writes -1 (0xFF bytes) into the house_tier
field during save, which causes the house theme to vanish on next
login. Snapshot the house tier before applying the save delta and
restore it if the incoming value is corrupted.
2026-02-20 22:57:40 +01:00
Houmgaor
7436ac0870 test(channelserver): add comprehensive GuildRepository tests
Add 34 tests covering all previously-untested GuildRepository methods:
invitations/scouts, guild posts, alliances, adventures, treasure
hunts, meals, kill tracking, and edge cases like disband with
alliance cleanup.

Fix test schema setup to apply the 9.2 update schema after init.sql,
which bridges v9.1.0 to v9.2.0 and resolves 9 pre-existing test
failures caused by missing columns (rp_today, created_at, etc.).

Make patch schemas 14 and 19 idempotent so they no longer fail when
applied against a schema that already has the target state (e.g.
festival_color type already renamed, legacy column names).
2026-02-20 22:53:16 +01:00
Houmgaor
93f28c721a refactor(channelserver): extract GachaRepository and HouseRepository
Centralizes all gacha_shop/gacha_entries/gacha_items/gacha_stepup/gacha_box
table access into GachaRepository (15 methods) and all user_binary house
columns, warehouse, and titles table access into HouseRepository (17 methods).
Eliminates all direct DB calls from handlers_gacha.go and handlers_house.go.
2026-02-20 22:30:28 +01:00
Houmgaor
ab9fd0bc9c refactor(channelserver): extract UserRepository for users table access
Centralizes all 31 direct users-table SQL queries from 11 handler
files into a single UserRepository, following the same pattern as
CharacterRepository and GuildRepository. The only excluded query is
the sign_sessions JOIN in handleMsgSysLogin which spans multiple
tables.
2026-02-20 22:18:46 +01:00
Houmgaor
d2a9b0ba5c refactor(channelserver): migrate remaining guild SQL into GuildRepository
Move ~39 inline SQL queries from six handler files into repo_guild.go,
consolidating all guild-related DB access (posts, alliances, adventures,
treasure hunts, meals, kill logs, scouts) behind GuildRepository methods.

Handler files now contain only packet serialization, business logic, and
ACK responses with no direct database calls.
2026-02-20 22:14:48 +01:00
Houmgaor
96d07f1c04 refactor(channelserver): extract GuildRepository for guild table access
Per anti-patterns.md item #9, guild-related SQL was scattered across
~15 handler files with no repository abstraction. Following the same
pattern established by CharacterRepository, this centralizes all
guilds, guild_characters, and guild_applications table access into a
single GuildRepository (~30 methods).

guild_model.go and handlers_guild_member.go are trimmed to types and
pure business logic only. All handler files (guild_*, festa, mail,
house, mercenary, rengoku) now call s.server.guildRepo methods
instead of direct DB queries or methods on domain objects.
2026-02-20 22:06:55 +01:00
Houmgaor
d642cbef24 refactor(channelserver): migrate remaining character queries to CharacterRepository
Add 18 new typed methods to CharacterRepository (ReadTime, SaveTime,
SaveInt, SaveBool, SaveString, ReadBool, ReadString, LoadColumnWithDefault,
SetDeleted, UpdateDailyCafe, ResetDailyQuests, ReadEtcPoints, ResetCafeTime,
UpdateGuildPostChecked, ReadGuildPostChecked, SaveMercenary, UpdateGCPAndPact,
FindByRastaID) and migrate ~56 inline SQL queries across 13 handler files.

Pure refactor — zero behavior change. Each handler produces identical SQL
with identical parameters. Cross-table JOINs and bulk CharacterSaveData
operations are intentionally left out of scope.
2026-02-20 21:57:24 +01:00
Houmgaor
197e44d04c refactor(channelserver): extract CharacterRepository for characters table access
Centralizes all characters table SQL behind a CharacterRepository struct
in repo_character.go. The 4 existing helpers (loadCharacterData,
saveCharacterData, readCharacterInt, adjustCharacterInt) now delegate to
the repository, keeping identical signatures so all ~70 callsites remain
unchanged. Direct queries in handlers_session.go, sys_channel_server.go
(DisconnectUser), and handlers_mail.go are also migrated.

Pure refactor with zero behavior change — first step toward eliminating
the ~130 scattered character queries identified in anti-patterns #9.
2026-02-20 21:38:21 +01:00
Houmgaor
e899a2f790 style: check error returns flagged by errcheck linter
golangci-lint's errcheck rule requires explicit handling of error
return values from Close, Write, and Logout calls. Use blank
identifier assignment for cleanup paths where errors are
intentionally discarded.
2026-02-20 21:22:01 +01:00
Houmgaor
458d8c9397 refactor(channelserver): add numeric column helpers and extract protocol constants
Add readCharacterInt/adjustCharacterInt helpers for single-column
integer operations on the characters table. Eliminates fmt.Sprintf
SQL construction in handlers_misc.go and replaces inline queries
across cafe, kouryou, and mercenary handlers.

Second round of protocol constant extraction: adds constants_time.go
(secsPerDay, secsPerWeek), constants_raviente.go (register IDs,
semaphore constants), and named constants across 14 handler files
replacing raw hex/numeric literals. Updates anti-patterns doc to
mark #4 (magic numbers) as substantially fixed.
2026-02-20 21:18:40 +01:00
Houmgaor
d456bd23e0 fix(channelserver): handle ignored DB errors and cache userID on session
Silently ignored DB errors in handlers could cause data loss (frontier
point transactions completing without DB writes), reward duplication
(stamp exchange granting items on failed UPDATE), and crashes (tower
mission page=0 causing index-out-of-bounds). House access state
defaulting to 0 on DB failure also bypassed all access controls.

HIGH risk fixes:
- frontier point buy/sell now fails with ACK on DB error
- stamp exchange/stampcard abort on failed UPDATE
- guild meal INSERT returns fail ACK instead of orphaned ID 0
- mercenary/airou creation aborts on failed sequence nextval

MEDIUM risk fixes:
- tower mission page clamped to >= 1 preventing array underflow
- tower RP donation returns early on failed guild state read
- house state defaults to 2 (password-protected) on DB failure
- playtime read failure logged instead of silently resetting RP

Also cache userID on Session at login time, eliminating ~25 redundant
subqueries of the form WHERE u.id=(SELECT c.user_id FROM characters
c WHERE c.id=$1) across shop, gacha, command, and distitem handlers.
2026-02-20 21:06:16 +01:00
Houmgaor
d5c44b5557 docs: mark binary I/O and copy-paste anti-patterns as resolved
Binary I/O (#5): all 12 remaining encoding/binary calls are
legitimate (zero-alloc spot-reads, random-access into game blobs).
Copy-paste handlers (#8): loadCharacterData/saveCharacterData helpers
now cover standard blob patterns.

Also upgrades saveCharacterData to send doAckSimpleFail on oversize
payloads and DB errors, and migrates handleMsgMhfSaveScenarioData
to the improved helper.
2026-02-20 20:55:06 +01:00
Houmgaor
a752c5187e fix(channelserver): send ACK on empty Raviente register payload
handleMsgSysOperateRegister returned without sending an ACK when the
payload was empty, causing the client to softlock waiting for a response.
Send doAckBufSucceed with nil data on the early-return path to match
the success-path ACK type.

Also update tests to expect error returns instead of panics from
unimplemented Build/Parse stubs (matching prior panic→error refactor),
and mark resolved anti-patterns in docs.
2026-02-20 20:05:52 +01:00
Houmgaor
ac59188488 refactor(byteframe): replace read-overflow panic with sticky error
ByteFrame previously panicked on out-of-bounds reads, which crashed
the server when parsing malformed client packets. Now sets a sticky
error (checked via Err()) and returns zero values, matching the
encoding/binary scanner pattern. The session recv loop checks Err()
after parsing to reject malformed packets gracefully.

Also replaces remaining panic("Not implemented") stubs in network
packet Build/Parse methods with proper error returns.
2026-02-20 20:00:54 +01:00