Kanturu Refinery Tower: refactor context, fix re-entry, persist tower window - #970
eduardosmaniotto wants to merge 9 commits into
Conversation
… window Decomposes the KanturuContext god object into focused collaborators with unit test coverage, fixes event/tower re-entry, and makes the Tower of Refinement replayable: it stays open for a configurable window that survives server restarts, without replaying the boss phases. Bugs fixed: - Re-entering Kanturu crashed the enter-result packet with ArgumentOutOfRangeException (MiniGameType.Kanturu unhandled); Kanturu now maps through its own 0xD1/0x01 packet. - The tower couldn't be re-entered after victory, teleport-out, or restart (button disabled); entry now works while the window is open. - Fresh tower maps had a closed barrier and wrong spawn; they now replicate the victory state (open barrier, Nightmare-zone spawn) from creation, with correct open/close timers in the gateway dialog. Refactoring: - KanturuContext split into KanturuKillTracker, KanturuMonsterComparer, KanturuNightmarePhaseSelector, KanturuBarrierAreaHelper, KanturuRequiredItemHelper, KanturuTowerWindow, KanturuTowerEntry, and IKanturuPhaseRunner with wave/transition/nightmare runners. - Monster death handling is synchronous again (no async void), following the invasion death-broadcast pattern. - Generic mini-game layer stays free of Kanturu specifics; Kanturu behavior lives behind MiniGameContext virtuals and the Kanturu enter-request handler.
…port) - KanturuKillTracker now swaps atomic per-phase generations, so a kill landing during a phase change can neither pollute nor complete the new phase; adds parallel stress and generation-isolation tests. - Tower waits honor cancellation for prompt teardown instead of lingering on uncancelable multi-hour delays. - Nightmare teleport rechecks IsAlive after the delay and after the move, so a mid-teleport kill can't resurrect the boss after victory. - Tower reuse is state-defined (Open/Closed/Playing); the gateway dialog reuses MiniGameContext.IsJoinable instead of duplicating the rule. - Tower window persist uses read-modify-write, so a concurrent admin schedule save can't clobber it; unknown phase kinds log a warning instead of silently falling back to the wave runner.
- Gateway dialog reuses MiniGameContext.IsJoinable instead of hand-rolling the entry rule, so both stay in agreement. - Drop the no-op second live assignment when persisting the tower window.
sven-n
left a comment
There was a problem hiding this comment.
Quick review of the Kanturu Refinery Tower changes — nice decomposition of KanturuContext, and the tower-window persistence follows the established BotFeaturePlugIn read-modify-write pattern.
Six inline findings, four of them worth a look before merge:
- Scheduling: the new tower-window gate in
KanturuStartPlugIncan skip the next day's event entirely, with no retry. - Gateway:
canEntercan't becometrueduring the entrance window, becauseCurrentKanturuStateis stillNonethen. - Construction:
MiniGameContext's constructor reads the virtualMinimumEnterDurationbeforeTowerModeis assigned, andRunGameAsyncraces the derived constructor body. - Wave timing: a
TimeLimitofTimeSpan.Zerofails the phase instantly instead of meaning "no limit".
Plus two minor ones (an unobserved task in the WhenAny race, and a lost early return in OnMonsterDied that costs a full-map scan per kill).
Things I checked and found sound: MiniGameMapKey compatibility of the transient TowerMiniGameDefinition, the KanturuTowerWindow persistence pattern, spawn-capture arming order in KanturuNightmareRunner, Died/OnMonsterDied subscription for late joiners, CurrentMiniGame clearing on map removal, the coordinate-255 guard in KanturuBarrierAreaHelper, and the UpdateVersion/GUID uniqueness of the new update plug-in.
Note: no .NET SDK was available in my environment, so nothing here was compiled or run — these are review findings only.
Generated by Claude Code
# Conflicts: # src/GameServer/RemoteView/MiniGames/Extensions.cs # src/GameServer/RemoteView/MiniGames/ShowMiniGameEnterResultViewPlugIn.cs # src/Persistence/Initialization/Updates/UpdateVersion.cs
|
I've managed to find the Kanturu event official information in the Wayback Machine: https://web.archive.org/web/20200802210156/http://muonline.webzen.com/guides/25/180/game-contents/kanturu I noticed some differences from our version, and I'll address those in this PR now. PS. The official article specifies a generic "Players must kill all 50 monsters" with no mention of which ones. So I left the normal waves as they currently are. |
- Share one countdown per wave (monsters + boss) via KanturuWaveGroup, TimeLimitGroup and KanturuWaveTimer; followers inherit the remainder, expired remainders fail the wave. - Trigger Nightmare summons from HP thresholds through configured summon waves (9-11, 7x Dread Fear); teleports drift toward the tower gate and no longer restore boss health; teleport messages updated. - Run Maya's wide attack (broadcast, damage, pendant insta-kill) through KanturuMayaWideAttacker; Nightmare HUD counts minions only. - Allow refills during Maya standbys up to 15 players and pendant-free tower re-entry; gateway dialog reflects live state with standby entry. - Schedule runs every 6 hours with silent starts: no entrance announcements even with custom timetables, blank messages skipped. - GM /startkanturu always disposes, clears the tower window and resets the cooldown before forcing; entry checks evaluate joinability atomically. - Cover waves, timers, summons, attacker selection and entry with tests.
Bugs fixed
ArgumentOutOfRangeException(
MiniGameType.Kanturuunhandled by the enter-result packet). Kanturu nowmaps through
ToKanturuEnterResult()to its own0xD1/0x01packet.Entry works while the window is open: live tower games are joined, otherwise
a tower-only game is recreated without event phases (same map key, tower
timers, no waves/rewards). Tower games start instantly (no lobby,
countdown, or victory message) and spawn at the Nightmare entry.
MayaBattleflicker. They now replicate the victory state from creation (open barrier,
Towerstate from birth, Nightmare-zone spawn via the handshake-timeGetEntrySpawnPositionhook) with the correct remaining-time dialog.Ended) games no longeradvertise entry at the gateway and no longer shadow tower recreation;
tower entry also skips disposed/disposing instances.
/startkanturudestroyed the tower and then gotblocked by the window. It now always disposes first, clears the tower
window, and resets the task cooldown, so the forced start succeeds
first try; the regular schedule stays blocked.
15 min for waves 1–2, 20 min for wave 3 and Nightmare. Boss phases inherit
the remainder; an already-expired remainder fails the wave at once.
summon waves (9–11, 7x Dread Fear); teleports drift toward the tower gate
and no longer restore boss health (messages reworded accordingly);
guardians open the fight with a 30s intro; the HUD counts minions only
(0 while the boss is still up).
kills players without the Moonstone Pendant via
KillInstantlyAsync(dodge-proof, full death flow), Broken Shower pattern; pendant check runs
on equipped items only.
(gateway shows the standby entry state); death warps out to Kanturu Relics
via the engine respawn flow; joinability is now evaluated atomically under
the entering lock; idle never-entered towers end after a grace period.
Refactoring
KanturuContextdecomposed intoKanturuKillTracker(atomic per-phasegenerations),
KanturuMonsterComparer,KanturuNightmarePhaseSelector,KanturuBarrierAreaHelper,KanturuRequiredItemHelper,KanturuTowerWindow,KanturuTowerEntry,KanturuMayaWideAttacker(attack loop, damage, pendant kill),
KanturuWaveTimer(shared countdown),KanturuWaveGroup, andIKanturuPhaseRunner(monster-wave / transition /nightmare runners, spawn-wave seam for summons).
OnMonsterDiedis synchronous again (fire-and-forget broadcasts), samepattern as the invasion death broadcast — no
async voidsuppression.joinable entry, map-requirement waiver, cancellable waits); the gateway
dialog reuses
MiniGameContext.IsJoinableplus small state helpers.attacker selection, entry matrix, minion-count exclusion.
Configuration / data
KanturuStartConfigurationdefaults: 6-hour timetable, 6-hour taskduration,
TowerOpenDuration23h (admin-editable) and persistedTowerOpenUntilUtc;CreateDefaulttower default 23h.RefreshKanturuDataupdate: safezone fix, 15 participants,summon waves, one-shot start-config reset; seeder guards against duplicates
and fails loudly on missing monsters.
message rewordings (no recovery claims).
Verification
with open barrier → restart → instant tower re-entry → correct "closes in
N hours" dialog → window expiry returns to the regular schedule.