1684 Commits

Author SHA1 Message Date
Houmgaor
b1972e3c96 feat(savedata): write back zenny/gzenny/CP to ZZ save blob
Mirrors the read path added in 47277c7: updateSaveDataWithStruct now
flushes Zenny/GZenny/CP back to the blob for ZZ, using the same
`ok && off > 0 && off+size <= len(blob)` guard so unmapped modes remain
inert.

Tests lock down byte-level idempotence — the most important invariant
for save data. Parsing a live kirito ZZ blob and immediately writing
the struct back produces a byte-identical blob, so enabling these
fields cannot silently corrupt existing player saves on the next save
cycle. Additional coverage: round-trip through both paths, non-ZZ
modes never touch the blob bytes, and truncated blobs don't panic on
write.
2026-04-17 23:06:16 +02:00
Houmgaor
47277c712d feat(savedata): parse zenny/gzenny/CP from ZZ save blob
Adds read-only parsing for three scalar fields in the ZZ character save
blob: zenny (0xB0), gzenny (0x1FF64) and caravan points (0x212E4). Also
registers an offset for current_equip (0x1F604); extraction deferred
until its length is reverse-engineered. Offsets sourced from
Chakratos/mhf-save-manager and validated against a live HR999 blob.

Scope is intentionally ZZ-only: mhf-save-manager's F5 and G1-G5.2 maps
are not validated against live data, and the dormant pPlaytime vs
item_pouch collision in those versions is not resolved yet. Non-ZZ
modes leave the new pointers unmapped, and the read path is guarded by
`ok && off > 0 && off+size <= len(blob)` so unverified versions cannot
accidentally read from the blob.

Tests cover positive-path roundtrip (including live kirito blob),
regression guards for existing fields, non-ZZ isolation, new-character
skip, and bounds safety against truncated blobs.
2026-04-17 23:04:30 +02:00
Houmgaor
538724e6c9 fix(savedata): skip bookshelf read on pre-G1 clients
Bookshelf was introduced after Forward.5 (verified: F5 mhfo.dll has no
Bookshelf symbols, modern clients export .?AVBookshelfForm@@). For
F4/F5/S6 the configured pointers place the bookshelf region past the
end of the smaller save blob, causing a slice-bounds panic on every
MSG_MHF_SAVEDATA and rolling characters back to creation state. The
read is now bounds-checked and skipped when absent; bookshelf state
is persisted via house packets into user_binary.bookshelf, so leaving
BookshelfData nil is safe.
2026-04-07 21:56:40 +02:00
Houmgaor
44fd637a59 fix(api): use configured channel port in dashboard stats
The dashboard JSON hardcoded `54000 + server_id` as the channel port,
which is wrong whenever operators configure non-default ports in
config.json. Resolve the actual port from `Entrance.Entries[].Channels[]`
via a server_id map mirroring main.go's sid formula.
2026-04-07 09:20:50 +02:00
Houmgaor
4655336dfa docs(readme): document i18n support and update branch strategy 2026-04-06 20:35:54 +02:00
Houmgaor
0da28b42eb feat(i18n): add Chinese (zh) language support 2026-04-06 20:28:08 +02:00
Houmgaor
3dc74b0515 docs(changelog): condense #188 into a single entry 2026-04-06 20:09:05 +02:00
Houmgaor
5361e67b1a feat(i18n): per-session i18n routing and localized scenarios
Phase C of #188 — the last phase of server-side multi-language support.

Adds Session.I18n(), a cached per-session i18n table resolver built via
getLangStringsFor(s.Lang()). The pointer is stable until SetLang
invalidates the cache, so hot-path handlers pay zero allocations on
repeated calls. All 51 s.server.i18n.* call sites across commands,
guild, guild scout, cafe, and cast-binary handlers now route through
s.I18n().*, so chat replies, guild invite mail templates, cafe reset
notices, and quest-timer broadcasts are served in the player's
preferred language instead of the server-wide default.

Scenario JSON gets the same plain-or-map LocalizedString treatment
that quests received in phase B: subheader Strings and inline entry
Text accept either a plain string (backwards compatible) or a
language-keyed object. CompileScenarioJSON takes the compiling
session's language, loadScenarioBinary passes s.Lang(), and
ParseScenarioBinary emits plain-string LocalizedStrings so existing
.bin files round-trip byte-for-byte through the JSON path.

World-wide broadcasts (Raviente siege announcements via
BroadcastRaviente) intentionally stay on the server default — they
have no single-session context to resolve against.
2026-04-06 20:08:27 +02:00
Houmgaor
f7ea275540 feat(i18n): localized quest text and per-lang quest cache
Phase B of #188. Quest JSON title/description/text_main/text_sub_a/
text_sub_b/success_cond/fail_cond/contractor now accept either a plain
string (existing behaviour) or a language-keyed object like
  "title": { "jp": "...", "en": "...", "fr": "..." }
CompileQuestJSON takes the compiling session's language and resolves
each field through a fallback chain (requested -> plain -> jp -> en ->
any non-empty), so existing single-language quest JSONs keep working
byte-for-byte unchanged.

The quest cache is re-keyed on (questID, language) so compiled binaries
for different languages never leak between sessions on a multi-language
server. loadQuestBinary and loadQuestFile now pass s.Lang() into both
the compiler and the cache.

ParseQuestBinary emits plain-string LocalizedStrings, so the
binary -> JSON -> binary round-trip still produces identical output.

The new LocalizedString type lives in its own file and is reusable by
phase C (scenarios, mail templates, shop text). Shift-JIS encoding
still applies to the wire format, so localized values must use
characters representable in Shift-JIS — ASCII, kana, CJK — which is
documented on the type.
2026-04-06 20:00:43 +02:00
Houmgaor
5b38bfde3f feat(i18n): per-session language preference and !lang command
Phase A plumbing for #188. Adds a users.language column (migration
0022), UserRepo.GetLanguage/SetLanguage, and Session.Lang()/SetLang
accessors so future phases can resolve localized content per session
instead of falling back to the server-wide config.Language.

The preference is loaded from the DB on login and persisted via a new
!lang <en|jp|fr|es> chat command that shows the current language when
called without an argument, validates the code (case-insensitive), and
replies in the newly selected language so the switch is visible
immediately. An empty stored value falls back to config.Language.

sys_language.go exposes getLangStringsFor(code) as the new dispatch
primitive; getLangStrings(server) is now a thin wrapper so existing
callers keep working unchanged. isSupportedLang + supportedLangs keep
the !lang validator in sync with the dispatcher.

Localized quest/scenario content and per-session i18n lookups in
existing handlers are deliberately out of scope for phase A — this
commit ships only the plumbing so it can be reviewed and deployed
independently.
2026-04-06 19:52:19 +02:00
Houmgaor
803996adac style: gofmt realignment across channelserver 2026-04-06 19:33:32 +02:00
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 v9.3.2 2026-04-06 18:37:18 +02:00
Houmgaor
90875c602a docs(changelog): cover gacha, boost and protbot changes for 9.3.2
Catch Unreleased up to the commits pushed this session:

- empty-bytea gacha crash fix (#175) with the RE citation for the
  ZZ client's CSync_man::putReceive_gacha_item response buffer path
- StructScan warn-logging for misconfigured gacha_items rows
- stray double-ACK removal in handleMsgMhfGetBoostTimeLimit
- pre-1970 boost_time wraparound fix + 0011 healing migration
- protbot --action boost / gacha inspection scenarios
2026-04-06 18:26:24 +02:00
Houmgaor
4ad0012f62 fix(handlers): guard GetBoostTimeLimit against past boost_time
Live-server testing via protbot surfaced an inconsistency between
GetBoostTimeLimit and GetBoostRight: on the same character, the
former reported a far-future boost limit (2288912640 = year 2042)
while the latter correctly reported "expired / available". The two
handlers read the same boost_time row and disagreed.

Root cause: GetBoostTimeLimit was doing a naked uint32(int64) cast
on boostLimit.Unix(). The test character's boost_time was actually
year 1906 (a pre-1970 sentinel left behind by the pre-#187 bug),
whose negative int64 Unix timestamp wraps through uint32 to a huge
positive value the client interprets as a permanently active boost.

Harmonise the guard with GetBoostRight: return 0 whenever the stored
boost_time is not strictly after TimeAdjusted(), covering both the
pre-1970 wraparound and the "already expired" case.

Add a healing migration (0011_fix_stale_boost_time) that NULLs out
any boost_time column older than 1970 or more than 10 years in the
future, so affected characters recover on upgrade without waiting
for a fresh boost start.

Regression test uses the exact year-1906 value observed on the live
frontier.mogapedia.fr test account.
2026-04-06 18:26:15 +02:00
Houmgaor
e2b0a8ad8c style(deltacomp): gofmt realignment 2026-04-06 18:04:34 +02:00
Houmgaor
ca12635234 chore: gitignore CLAUDE.local.md 2026-04-06 18:04:34 +02:00
Houmgaor
8b2667f7a0 fix(handlers): harden gacha_items and boost time limit ACK paths
Both bugs surfaced when running protbot against a live Erupe instance.

LoadColumnWithDefault now treats an empty bytea ('\x', len 0) the same
as NULL. The postgres driver returns a non-nil empty slice for empty
bytea, so the prior `data == nil` check let a zero-byte slice reach
handleMsgMhfReceiveGachaItem, which forwarded it to the client as a
malformed gacha_items response. The MHF client interprets a zero-byte
count field as a protocol error and crashes the gacha menu, matching
the #175 symptom class. The handler also gains a defensive fallback
for len(data) == 0 in case another caller hits the same edge.

handleMsgMhfGetBoostTimeLimit was sending two ACKs for a single
request: doAckBufSucceed with the real payload, then an unconditional
doAckSimpleSucceed on the same ack handle. The second ACK was dead on
arrival (the handle was already consumed) but is a latent protocol
bug. Drop it and update the regression test that was asserting the
buggy 2-packet behavior.
2026-04-06 18:04:10 +02:00
Houmgaor
3e9f3d1b62 feat(protbot): add boost and gacha inspection scenarios
Adds non-destructive test scenarios for the #187 boost-time fix and
the #175 / gacha-logging changes so regressions in those paths can be
caught without a full game client.

- boost: queries GET_BOOST_TIME_LIMIT, GET_BOOST_RIGHT, and
  GET_KEEP_LOGIN_BOOST_STATUS, flagging a zero boost_limit and all-zero
  login boost entries as the expected DisableBoostTime/DisableLoginBoost
  state.
- gacha: snapshots GET_GACHA_POINT and RECEIVE_GACHA_ITEM (freeze=true,
  so temp storage is not cleared), with an opt-in --roll flag that
  exercises PLAY_NORMAL_GACHA end-to-end. Detects the post-#175
  single-byte validation-failure ACK.
2026-04-06 17:58:04 +02:00
Houmgaor
6fa07ae4ae fix(gacha): log and skip gacha_items rows that fail to scan
Previously GetItemsForEntry/GetGuaranteedItems silently swallowed
StructScan errors, so misconfigured rows (item_type > 255 or
item_id/quantity > 65535) disappeared from rewards with no trace,
making config bugs hard to diagnose without a DB dump.

Pass a zap.Logger into GachaRepository and emit a Warn pointing at
the likely cause and the offending entry/gacha ID.
2026-04-06 17:48:12 +02:00
Houmgaor
05ea321d9e fix(migrations): heal rasta_id=0 rows (#163)
The pre-106cf85 SaveMercenary bug could overwrite a character's NULL
rasta_id with 0, a value not drawn from rasta_id_seq that causes silent
save failures. The code fix prevents new occurrences but leaves already
affected rows stuck. Migration 0010 sets rasta_id=NULL where it is 0 so
existing users auto-heal on upgrade.
2026-04-06 17:10:33 +02:00
Houmgaor
49a5069e3d fix(handlers): correct quest tune-value multiplier handling
Two bugs in handleMsgMhfEnumerateQuest affecting reward multipliers:

1. A Value > 0 filter silently dropped any multiplier set to exactly
   0.0 in config, causing the client to fall back to its hardcoded
   default (100%). So ZennyMultiplier: 0.0 produced *full* zenny
   instead of none. Removed the filter so zero values are sent
   verbatim.

2. uint16(float32(0.20) * 100) yields 19, not 20, due to float32
   representation of 0.20 being ~0.19999998. Added a
   multiplierToTuneValue helper using math.Round and applied it to
   all 18 multiplier call sites (HRP/SRP/GRP/GSRP/Zenny/GZenny/
   Material/GMaterial/GCP/GUrgent and NC variants).
2026-04-06 16:45:56 +02:00
Houmgaor
4c725adbb7 test(handlers): regression tests for DisableBoostTime/DisableLoginBoost (#187)
Three focused tests asserting the response payloads when the config flags
are set: GetBoostTimeLimit and GetBoostRight return zero when
DisableBoostTime is true, and UseKeepLoginBoost returns an all-zero ack
when DisableLoginBoost is true. Verified to fail against the pre-fix
source.
2026-04-06 16:22:23 +02:00
Houmgaor
84e72f7d35 fix(handlers): honor DisableLoginBoost and DisableBoostTime fully (#187)
GetBoostTimeLimit and GetBoostRight now respect DisableBoostTime, and
UseKeepLoginBoost now respects DisableLoginBoost. Also fix a latent
zero-time.Time wraparound in GetBoostTimeLimit that caused the
"Boost Time" overlay to appear on fresh characters regardless of
config, since time.Time{}.Unix() cast to uint32 yields a large value
the client interprets as an active timestamp.
2026-04-06 16:16:05 +02:00
Houmgaor
99e6ea26f1 feat(handlers): parse and log user binary types 1-3
Reverse-engineered from mhfo-hd.dll via Ghidra: type 1 = character name,
type 2 = player profile (208B), type 3 = equipment snapshot (384B).
Adds structured zap logging and size validation warnings to
handleMsgSysSetUserBinary.
2026-04-06 16:05:19 +02:00
Houmgaor
883e503ec9 fix(handlers): ack savedata save failure to prevent softlock 2026-04-06 16:05:15 +02:00
Houmgaor
9e41d59bd1 Merge pull request #186 from Brentdbr/patch-1
Update README with further setup instructions
2026-04-01 14:26:35 +02:00
Brent
7521e21fa0 Update README with database setup instructions
Added instructions for using pgAdmin4 to create the database and clarified steps for configuring the application.
2026-03-31 20:28:01 +02:00
Houmgaor
9b3884fc26 chore(merge): merge main into develop
Backports two softlock fixes and playtime regression fix from main.
Also removes handleMsgMhfEnterTournamentQuest from the nil-stub test
list — it's a real DB-backed handler and has its own dedicated test.
2026-03-23 22:50:27 +01:00
Houmgaor
abab6dc3a1 fix(handlers): fix softlock on forge purchases and N-points
MSG_CA_EXCHANGE_ITEM and MSG_MHF_USE_UD_SHOP_COIN had Parse() returning
"NOT IMPLEMENTED". The dispatch loop in handlePacketGroup treats any
Parse error as a silent drop — no ACK is sent, causing the client to
wait indefinitely (softlock). Reported on 9.3.0-rc1 for forge item
purchases and Hunting Road N-point interactions.

Fix follows the pattern from d27da5e: parse only the AckHandle, return
nil from Parse, and respond with doAckBufFail so the client's error
branch exits cleanly without reading response fields.
2026-03-23 22:20:32 +01:00
Houmgaor
72088db4ff fix(savedata): write playtime back to binary blob on save
updateSaveDataWithStruct only wrote RP and KQF into the blob, leaving
the playtime field stale. On each reconnect, GetCharacterSaveData read
the old in-game counter from the blob and reset s.playtime to it,
rolling back all progress accumulated during the previous session.

Playtime is now persisted into the blob alongside RP, using the same
S6 mode guard as the read path in updateStructWithSaveData.
2026-03-23 22:00:06 +01:00
Houmgaor
6f7852cc12 feat(api): add GET /v2/server/info endpoint for launcher compatibility
Exposes the server's configured client mode in a form that mhf-outpost
and other launcher tools can consume to warn users when their local game
version does not match what the server expects.

Returns clientMode (raw, e.g. "G9.1") and manifestId (normalized for
mhf-outpost: lowercase, dots stripped, e.g. "g91"). No auth required.
2026-03-23 19:55:55 +01:00
Houmgaor
9e59ce69e3 fix(tests): fix three CI failures on develop
- alliance recruiting default: change DEFAULT true → false in both
  0001_init.sql and 0004_alliance_recruiting.sql; new alliances should
  not be open for recruitment by default (TestSetAllianceRecruiting)
- guild_invites migration: add IF NOT EXISTS so re-running migrations
  on an existing DB does not fail with "relation already exists"
  (TestMigrateExistingDBWithoutSchemaVersion)
- test character name: shorten "Idem_Rollover_Leader" (19 chars) to
  "IdemRollLeader" to fit the VARCHAR(15) constraint
  (TestRolloverDailyRP_Idempotent)
2026-03-23 12:52:28 +01:00
Houmgaor
19a49fa0ae fix(migrations): remove explicit BEGIN/COMMIT from migrations 0012-0015
applyMigration() already wraps each file in a db.Begin()/tx.Commit()
transaction. The inner BEGIN/COMMIT in these files caused the outer
transaction to commit early, leaving applyMigration trying to insert
into schema_version on an idle connection:
  pq: unexpected transaction status idle

Affected: 0012_guild_invites, 0014_return_guilds, 0015_tournament.
2026-03-23 11:59:57 +01:00
Houmgaor
470a761191 fix(scenario): enforce scenarioChunkSizeLimit in CompileScenarioJSON
The constant was declared and documented but never used, causing a
golangci-lint failure. Wire it into compileScenario() so oversized
chunks are rejected with a clear error rather than silently served
to the client (which discards them per FUN_11525c60 RE findings).
2026-03-23 11:19:50 +01: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
e1aa863a1f test(channelserver): reduce polling interval from 10ms to 1ms
LoopDelay is 0 in test servers (bare struct, no Viper defaults), so
sendLoop spins without delay and satisfies conditions in <1ms. The
10ms poll interval was the new bottleneck — dropping to 1ms cuts the
channelserver test suite from ~136s to ~5s.
2026-03-23 11:06:50 +01:00
Houmgaor
d0efc4e81a test(channelserver): replace time.Sleep with polling loops
Blind sleeps accumulate serially (no t.Parallel anywhere) and inflate
under the race detector's scheduling overhead — contributing to the
~136s channelserver test run time.

Replace ~75 arbitrary sleeps (50ms–1s) across 7 test files with 2s
polling loops that exit as soon as the expected condition holds. Sleeps
that are genuinely intentional (race-condition stress tests, cache
expiry, temporal spacing in timestamp tests, backpressure pacing) are
left untouched.
2026-03-23 10:57:01 +01:00
Houmgaor
0c6dc39371 ci: upgrade actions to Node.js 24-compatible versions
Node.js 20 actions are deprecated and will be forced to Node.js 24
starting June 2, 2026. Bump to versions that ship Node.js 24 runtimes:

  actions/checkout        v4 → v6
  actions/setup-go        v5 → v6
  golangci-lint-action    v7 → v9
  actions/upload-artifact v4 → v6
  actions/download-artifact v4 → v8
2026-03-23 10:36:31 +01:00
Houmgaor
635b9890c8 test(broadcast): fix flaky TestBroadcastMHFAllSessions under race detector
The fixed 100ms sleep was too short for sendLoop goroutines to drain
under the race detector's scheduling overhead, causing intermittent
count=4/want=5 failures. Replace with a 2s polling loop that exits
as soon as all sessions report delivery.
v9.3.1
2026-03-23 10:26:29 +01:00
Houmgaor
1f0544fd10 fix(savedata): add recovery hint to integrity check failure log
Admins importing saves from another server instance will hit the
hash mismatch error with no guidance. The log message now tells them
to set DisableSaveIntegrityCheck=true or null the hash for the
specific character via SQL.
2026-03-23 10:15:25 +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
842426e001 docs(tower): document Sky Corridor system status and remaining gaps
Most of the Tower/Tenrouirai system is already implemented on develop.
This doc captures what works, what is broken (PostTenrouirai Op=1 no-op,
block2 never written, PresentBox empty), and the remaining unknown packet
fields — so future contributors know exactly where to focus effort.

Also updates unimplemented.md to note the feature/tower branch was
superseded by direct integration into develop.
2026-03-22 20:35:56 +01:00
Houmgaor
dca7152656 docs(hunting-tournament): document tournament RE gaps and branch status
Add hunting-tournament.md covering the 公式狩猟大会 system: game
context (cups, schedule, rewards), what is already implemented in
develop (handlers_tournament.go is mostly functional), and the four
remaining gaps requiring RE — quest clear time recording, ClanID
leaderboard filtering, AcquireTournament reward delivery, and guild
cup soul attribution to Mezeporta Festival.

Documents why feature/hunting-tournament is not mergeable (duplicate
handlers) and preserves its useful findings: ClanID field name on
EnumerateOrder and the festa timing corrections.

Update unimplemented.md open branches summary accordingly.
2026-03-22 20:24:09 +01:00
Houmgaor
93f8c677d9 docs(conquest): document Conquest War RE status and implementation gaps
The feature/conquest branch drifted too far from develop without completing
the core gameplay loop. This doc captures all known packet wire formats,
handler states, confirmed values from captures, and a prioritised table of
unknowns needed before the feature can be implemented cleanly.

Also updates unimplemented.md to link to the new reference.
2026-03-22 20:16:44 +01:00
Houmgaor
63312dac1b docs(fort-attack): document Interceptor's Base event RE status
Add fort-attack-event.md capturing everything known about the fort
attack event system (packet wire formats, register plumbing, DB schema
gap, quest IDs) and what needs reverse-engineering before implementation
is possible. The feature/enum-event branch covered only scheduling and
is not mergeable; its findings are preserved here for future reference.

Update unimplemented.md to point to the new doc and correctly describe
the branch scope.
2026-03-22 20:11:22 +01:00
Houmgaor
f03476e6e0 docs(unimplemented): remove merged branches from open branches summary
feature/return-guild and fix/clan-invites have been merged into develop.
Remove them from the open branches table and clean up the stale branch
reference in the ShutClient handler note.
2026-03-22 19:33:31 +01:00
Houmgaor
56a0173b40 fix(i18n): translate remaining English strings in lang_jp.go
Six strings were left in English in the original Japanese translation:
noOp, kqf.version, ban.* (all five fields), and ravi.version.
2026-03-22 17:11:12 +01:00