Skip to content

Kanturu Refinery Tower: refactor context, fix re-entry, persist tower window - #970

Open
eduardosmaniotto wants to merge 9 commits into
MUnique:masterfrom
eduardosmaniotto:fix/kanturu-refinery-tower
Open

eduardosmaniotto wants to merge 9 commits into
MUnique:masterfrom
eduardosmaniotto:fix/kanturu-refinery-tower

Conversation

@eduardosmaniotto

@eduardosmaniotto eduardosmaniotto commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

Bugs fixed

  • Re-entry crash: entering Kanturu hit ArgumentOutOfRangeException
    (MiniGameType.Kanturu unhandled by the enter-result packet). Kanturu now
    maps through ToKanturuEnterResult() to its own 0xD1/0x01 packet.
  • No tower re-entry after victory, teleport-out, or server restart.
    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.
  • Fresh tower maps were wrong: closed barrier, event-start spawn, MayaBattle
    flicker. They now replicate the victory state from creation (open barrier,
    Tower state from birth, Nightmare-zone spawn via the handshake-time
    GetEntrySpawnPosition hook) with the correct remaining-time dialog.
  • Stale games blocked entry: tearing-down (Ended) games no longer
    advertise entry at the gateway and no longer shadow tower recreation;
    tower entry also skips disposed/disposing instances.
  • GM restart dead-end: /startkanturu destroyed the tower and then got
    blocked 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.
  • Shared wave timers: each wave (monsters + boss) runs on one clock —
    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.
  • Nightmare fight: summons fire from HP thresholds through configured
    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).
  • Maya pendant kill: the wide-area attack damages everyone and instantly
    kills players without the Moonstone Pendant via KillInstantlyAsync
    (dodge-proof, full death flow), Broken Shower pattern; pendant check runs
    on equipped items only.
  • Refills and access: Maya standbys accept re-entry up to 15 players
    (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.
  • Scheduling: runs every 6 hours.

Refactoring

  • KanturuContext decomposed into KanturuKillTracker (atomic per-phase
    generations), KanturuMonsterComparer, KanturuNightmarePhaseSelector,
    KanturuBarrierAreaHelper, KanturuRequiredItemHelper,
    KanturuTowerWindow, KanturuTowerEntry, KanturuMayaWideAttacker
    (attack loop, damage, pendant kill), KanturuWaveTimer (shared countdown),
    KanturuWaveGroup, and IKanturuPhaseRunner (monster-wave / transition /
    nightmare runners, spawn-wave seam for summons).
  • OnMonsterDied is synchronous again (fire-and-forget broadcasts), same
    pattern as the invasion death broadcast — no async void suppression.
  • Generic mini-game layer stays free of Kanturu specifics (virtual hooks:
    joinable entry, map-requirement waiver, cancellable waits); the gateway
    dialog reuses MiniGameContext.IsJoinable plus small state helpers.
  • Extended Kanturu coverage: wave/group timers, summon thresholds and skips,
    attacker selection, entry matrix, minion-count exclusion.

Configuration / data

  • KanturuStartConfiguration defaults: 6-hour timetable, 6-hour task
    duration, TowerOpenDuration 23h (admin-editable) and persisted
    TowerOpenUntilUtc; CreateDefault tower default 23h.
  • Merged RefreshKanturuData update: safezone fix, 15 participants,
    summon waves, one-shot start-config reset; seeder guards against duplicates
    and fails loudly on missing monsters.
  • Map data: summon waves 9–11 around the teleport targets; teleport
    message rewordings (no recovery claims).

Verification

  • Full test suite: 1109/1109 green; zero new warnings in touched files.
  • In-game verified: victory → teleport out → re-enter at Nightmare entry
    with open barrier → restart → instant tower re-entry → correct "closes in
    N hours" dialog → window expiry returns to the regular schedule.

… 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 sven-n left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 KanturuStartPlugIn can skip the next day's event entirely, with no retry.
  • Gateway: canEnter can't become true during the entrance window, because CurrentKanturuState is still None then.
  • Construction: MiniGameContext's constructor reads the virtual MinimumEnterDuration before TowerMode is assigned, and RunGameAsync races the derived constructor body.
  • Wave timing: a TimeLimit of TimeSpan.Zero fails 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

Comment thread src/GameLogic/PlugIns/PeriodicTasks/KanturuStartPlugIn.cs
Comment thread src/GameLogic/PlugIns/KanturuGatewayPlugIn.cs Outdated
Comment thread src/GameLogic/MiniGames/MiniGameContext.cs Outdated
Comment thread src/GameLogic/MiniGames/Kanturu/KanturuContext.cs Outdated
Comment thread src/GameLogic/MiniGames/Kanturu/KanturuContext.cs
Comment thread src/GameLogic/MiniGames/Kanturu/KanturuContext.cs Outdated
# Conflicts:
#	src/GameServer/RemoteView/MiniGames/Extensions.cs
#	src/GameServer/RemoteView/MiniGames/ShowMiniGameEnterResultViewPlugIn.cs
#	src/Persistence/Initialization/Updates/UpdateVersion.cs
@eduardosmaniotto
eduardosmaniotto marked this pull request as draft September 25, 2026 16:15
@eduardosmaniotto

eduardosmaniotto commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor Author

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

Kanturu Event.pdf

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.
@eduardosmaniotto
eduardosmaniotto marked this pull request as ready for review September 26, 2026 03:47

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants