CharacterSaveData.Save() silently returned on failure (nil decompressed
data, compression error, DB error) while the caller unconditionally
logged "Saved character data successfully". This made diagnosing save
failures difficult (ref #163).
Save() now returns an error, and all six call sites check it. The
success log in saveAllCharacterData only fires when the save actually
persisted.
The tower repo had business logic beyond simple CRUD: AddGem used a
fetch-transform-save pattern, progress capping was inline in the
handler, and RP donation orchestrated multiple repo calls with
conditional page advancement. Move these into a new TowerService
following the established service layer pattern.
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.
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.
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.
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
Several handlers used packet fields as array indices or SQL column
names without bounds checking, allowing crafted packets to panic the
server or produce malformed SQL.
Panic fixes (high severity):
- handlers_mail: bounds check AccIndex against mailList length
- handlers_misc: validate ArmourID >= 10000 and MogType <= 4
- handlers_mercenary: check RawDataPayload length before slicing
- handlers_house: check RawDataPayload length in SaveDecoMyset
- handlers_register: guard empty RawDataPayload in OperateRegister
SQL column name fixes (medium severity):
- handlers_misc: early return on unknown PointType
- handlers_items: reject unknown StampType in weekly stamp handlers
- handlers_achievement: cap AchievementID at 32
- handlers_goocoo: skip goocoo.Index > 4
- handlers_house: cap BoxIndex for warehouse operations
- handlers_tower: fix MissionIndex=0 bypassing normalization guard
Add package-level documentation (doc.go) to all 22 first-party
packages and godoc comments to ~150 previously undocumented
exported symbols across common/, network/, and server/.
Add explicit error discards (_ =) for Close() calls on network
connections, SQL rows, and file handles across 28 files. Also add
.golangci.yml with standard linter defaults to match CI configuration.
138 bare db.Exec calls across 22 handler files silently dropped write
errors. Each is now wrapped with error check and zap logging.
4 QueryRow sites that legitimately return sql.ErrNoRows during normal
operation (new player mezfes, festa rankings, empty guild item box)
now filter it out to reduce log noise.
Fix unchecked error returns on bf.Seek(), db.Exec(), QueryRow().Scan(),
pkt.Build(), logger.Sync(), and binary.Write() calls. The linter now
passes with 0 errors, build compiles, and all tests pass with -race.
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.
Add error checking and logging for ~25 database call sites that were
silently dropping errors, preventing resource leaks (unclosed rows),
nil pointer panics, and silent data corruption in festa transactions.