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.
Purge oldest guild posts beyond the limit (100 messages, 4 news) after
each new post is created. Replace misleading alliance application TODO
with a note that the feature is not yet implemented.
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.
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.
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.
- Remove deprecated version field from docker-compose.yml
- Pin Postgres to 18-alpine (matches existing db-data)
- Remove undocumented web (Apache) service
- Fix config/bin volume mounts to use docker/ directory
- Gitignore docker/savedata, docker/bin, docker/config.json
- Rewrite docker/README.md: fix typos, use docker compose V2
commands, match actual compose file behavior
- Link docker/README.md from main README Docker section
Reorganize README to put Quick Start first with three install paths
(Docker/binary/source), give quest files their own section, consolidate
updating instructions, trim configuration to essentials with wiki link,
and move informational sections (features, architecture) below setup.
Absorb community tool links from the now-removed pastebin FAQ and update
dead URLs: Ferias → English Project, damage calc → fist.moe, armor set
searcher → mhfz-ass GitHub releases.
The final fallback in seasonConversion blindly constructed a filename
without checking if it existed on disk. When the file was missing,
handleMsgSysGetFile would send doAckBufFail, but the original Frontier
client does not gracefully handle this during quest loading — causing a
softlock instead of showing the built-in error dialog.
Now every fallback path validates file existence before returning, and
also tries the opposite time-of-day variant as a last resort. If no
file variant exists at all, the original filename is returned with a
warning log so the failure ack is still sent.
Multi-stage Dockerfile for smaller runtime image, CD workflow triggers
on main branch pushes and version tags, docker-compose defaults to the
prebuilt GHCR image.
Import 18 network packet test files and 5 server infrastructure test
files, adapted for main branch APIs: fix config import alias (_config),
remove non-existent DevMode field, use global handlerTable instead of
per-server handlers map, and correct validateToken mock expectations
to include both token and tokenID arguments.
Adds go-sqlmock dependency for database mocking in signserver tests.
pg_restore would fail because the dump contains CREATE DATABASE but
POSTGRES_DB already creates it. With set -e this aborted the script
before update/patch schemas could run.
- Allow pg_restore to continue past non-fatal errors
- Add --no-owner --no-acl to avoid permission mismatches
- Force LF line endings for .sh files via .gitattributes
- Quote file path variables in schema loops
Port test files from v9.2.x-stable branch to increase channelserver
coverage from 13.8% to 25.6% (556 tests passing).
Adapted all files to main's struct definitions: config import alias,
Airou/CatDefinition rename, packet field mismatches, Raviente struct
differences, and maxPlayers defaults. Removed tests referencing
production code not yet on main (Player, FestivalColour, etc.).
Excluded handlers_register_test.go (Raviente completely redesigned).
When a quest or scenario file was missing, handleMsgSysGetFile sent nil
data via doAckBufSucceed, which crashed the game client. Now sends
doAckBufFail so the client can handle the missing file gracefully.
Closes#109
Wrap *rand.Rand in a mutex-protected SafeRand type to make the global
RNG safe for concurrent use across goroutines. The previous bare
*rand.Rand caused data races detected by go test -race.
UTF8ToSJIS panicked when encountering characters outside the Shift-JIS
range (emoji, Lenny faces, cuneiform, etc.), crashing the server when
such characters were sent via the Discord relay channel.
Replace the panic with graceful filtering that drops unmappable runes
and preserves valid content. Also fix ToNGWord index-out-of-range panic
on empty encoder output.
Closes#116
Resolve conflict in handlers_stage.go: keep lock-free packet
building pattern (copy session list, release lock, then build)
over upstream's in-lock QueueSendMHF approach.
Fix test compilation: remove objectIDs field references after
upstream removed it from Server struct.
Resync vendor directory with updated go.mod dependencies.
Mainly, there are differences in the MHFG low version network protocol when pkt.ShopType=1 or 2 in handleMgMhfEnumerationShop. Conducted code judgment and differentiation processing.
If you need to configure the optional material list among the three options,
Configure directly in gacha_detries
The same Entry Type can be merged and displayed in GG
In addition, the prizes are also directly configured in the gacha-entries table,
MHFG1~GG does not use the gacha_items table throughout the entire process, which meets the lottery function of MHFG with a more single function
In addition, the MHFG function itself is relatively simple
Example of lottery configuration for G1~GG:
eg:
gachaname:test
entry:
itemgroup:
group1:(choose one of the two) ITEM_1_ID:7 COUNT:1 ITEM_1_ID:8 COUNT:2
group2:ITEM_1_ID:9 COUNT:3
reward:
reward1: ITEM_ID:1 COUNT:4 weight:10%
reward1: ITEM_ID:2 COUNT:5 weight:90%
table:gacha_shop
|id|min_gr|min_hr|name|url_bannel|url_feature|url_thmubnail|wide|recommendded|gacha_type|hidden|
|1|0|0|test|null|null|null|f|f|3|t|
table:gacha_entries
|id|gacha_id|entry_type|item_type|item_number|item_quantity|weight|rarity|rolls|frontier_points|daily_limit|name|
|1|1|0|7|7|1|0|0|0|0|0|null|
|4|1|0|7|8|2|0|0|0|0|0|null|
|5|1|1|7|9|3|0|0|0|0|0|null|
|8|1|100|7|1|4|1000|0|0|0|0|null|
|9|1|100|7|2|5|9000|0|0|0|0|null|