Skip to content

feat: Add Kanturu Refinery Tower event implementation#719

Open
apraxico wants to merge 3 commits intoMUnique:masterfrom
apraxico:feature/kanturu-event
Open

feat: Add Kanturu Refinery Tower event implementation#719
apraxico wants to merge 3 commits intoMUnique:masterfrom
apraxico:feature/kanturu-event

Conversation

@apraxico
Copy link
Copy Markdown

Summary

Implements the full server-side logic for the Kanturu Refinery Tower mini-game event (maps 38-40), following the same plugin patterns already used by BloodCastle and ChaosCastle.

The upstream already contained the three map definitions (KanturuEvent.cs, KanturuRelics.cs, KanturuRuins.cs) and the two client-side packet docs (C1-D1-00, C1-D1-01). This PR adds the missing logic layer that wires those maps into a playable event.

New files

File Purpose
KanturuContext Event game loop — 8 sequential phases (wave clears → Maya L/R Hand → Nightmare → Tower of Refinement)
IKanturuEventViewPlugIn View-plugin interface for client packet dispatch
KanturuEventViewPlugIn Sends opening-state and monster-count packets to clients
KanturuGatewayPlugIn NPC gateway that controls event entry
KanturuStartPlugIn Periodic task that starts the event on a configurable interval
KanturuStartConfiguration Configuration class for the periodic start task
KanturuGameServerState Tracks per-server event state (open / running / closed)
KanturuEnterRequestHandlerPlugIn Handles C1-D1-01 entry packet
KanturuInfoRequestHandlerPlugIn Handles C1-D1-00 info packet
KanturuGroupHandlerPlugIn Registers the D1 packet group handler
KanturuInitializer Seeds MiniGameDefinition and spawn waves at startup

Modified files

  • MiniGameType.cs — added Kanturu enum value
  • GameConfigurationInitializer.cs — registers KanturuInitializer
  • KanturuEvent.cs — added full wave monster spawn definitions (Blade Hunters, Dreadfear, Twin Tale, Genocider, Persona, Maya hands, Nightmare)

Event flow

Phase 1 → Wave of Blade Hunters + Dreadfear → Maya Left Hand
Phase 2 → Wave of Blade Hunters + Dreadfear → Maya Right Hand
Phase 3 → Wave of Dreadfear + Twin Tale → Both Maya Hands
Nightmare Prep → Genocider + Dreadfear + Persona
Nightmare → Boss fight (teleports at 75/50/25% HP)
Victory → Elphis barrier opens → Tower of Refinement

Players who die respawn at Kanturu Relics (Map 38) via the map's SafezoneMap setting.

Test plan

  • dotnet build src/ compiles without errors
  • dotnet test tests/MUnique.OpenMU.Persistence.Initialization.Tests/ passes
  • In-game: talk to Kanturu NPC → info dialog appears
  • In-game: enter with valid character (lv 350-400, Moonstone Pendant equipped)
  • Event progresses through all 8 phases
  • Defeat: player respawns at Kanturu Relics
  • Victory: Tower of Refinement is accessible

🤖 Generated with Claude Code

Implements the full server-side logic for the Kanturu Refinery Tower
mini-game event (maps 38-40), following the same plugin patterns used
by BloodCastle and ChaosCastle.

New files:
- KanturuContext: event game loop with 8 sequential phases
  (Phase 1-3 wave clears, Nightmare preparation, Nightmare boss fight,
   Tower of Refinement victory)
- IKanturuEventViewPlugIn: view plugin interface for client communication
- KanturuEventViewPlugIn: sends state packets to connected clients
- KanturuGatewayPlugIn: NPC gateway plugin for event entry
- KanturuStartPlugIn / Configuration / GameServerState: periodic task
  scheduler that starts the event on a configurable interval
- KanturuEnterRequestHandlerPlugIn: handles C1-D1-01 entry packet
- KanturuInfoRequestHandlerPlugIn: handles C1-D1-00 info packet
- KanturuGroupHandlerPlugIn: packet group handler registration
- KanturuInitializer: seeds MiniGameDefinition and spawn waves

Modified files:
- MiniGameType.cs: added Kanturu enum value
- GameConfigurationInitializer.cs: registers KanturuInitializer
- KanturuEvent.cs: added full wave monster spawn definitions

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@gemini-code-assist
Copy link
Copy Markdown

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces the complete server-side implementation of the Kanturu Refinery Tower event, a multi-phase mini-game designed to enhance player engagement. It provides a robust framework for managing event progression, monster spawns, boss mechanics, and client-side synchronization, significantly expanding the game's dynamic content offerings.

Highlights

  • New Event Implementation: Implemented the full server-side logic for the Kanturu Refinery Tower mini-game event, following existing plugin patterns used by other events like BloodCastle and ChaosCastle.
  • Core Event Logic: Introduced KanturuContext to manage the event's 8 sequential phases, including monster waves, Maya boss fights, Nightmare boss fights with HP-based teleports, and the Tower of Refinement phase.
  • Client-Server Communication: Added a dedicated view plugin (IKanturuEventViewPlugIn and KanturuEventViewPlugIn) and request handlers (KanturuEnterRequestHandlerPlugIn, KanturuInfoRequestHandlerPlugIn) to synchronize event states, countdowns, monster counts, and visual effects with the client.
  • Event Scheduling and Configuration: Created KanturuStartPlugIn and KanturuStartConfiguration to enable periodic scheduling of the event, allowing for configurable start times and duration of the Tower of Refinement phase.
  • Game Data Integration: Integrated the Kanturu event into the game's data model by adding a new MiniGameType.Kanturu enum value, defining the event's properties via KanturuInitializer, and updating the KanturuEvent.cs map definition with specific monster spawns and boss attributes.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a comprehensive implementation of the Kanturu Refinery Tower event. The changes are well-structured, following existing patterns for mini-games and plugins. The core event logic in KanturuContext is well-thought-out, especially regarding concurrency and the different event phases. The new view plugins and packet handlers are correctly implemented. I've identified a couple of areas for improvement related to exception handling in asynchronous fire-and-forget tasks to enhance server stability. Overall, this is a solid feature implementation.

Comment on lines +193 to +194
var pc = (byte)Math.Min(255, this.PlayerCount);
_ = Task.Run(() => this.SendMonsterUserCountAsync(remaining, pc).AsTask());
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

This fire-and-forget task can lead to an unhandled exception if SendMonsterUserCountAsync throws, for example due to a player disconnecting. This could crash the server process. It's safer to handle potential exceptions within the task, for instance by wrapping the call in a try-catch block that logs the error. This applies to all similar Task.Run calls in this method used for sending client updates.

// Fire barrier opening immediately from the death event.
// Do NOT wait for the game loop — it may be interrupted by
// GameEndedToken cancellation before reaching OpenElphisBarrierAsync.
_ = Task.Run(() => this.OpenElphisBarrierAsync().AsTask());
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

This fire-and-forget task for OpenElphisBarrierAsync lacks exception handling. An exception here would be unhandled and could crash the server. Since opening the barrier is a critical part of the event progression, any failure should be logged. Please consider adding a try-catch block to handle potential exceptions.

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.

  1. Convert static classes to enums, remove numerical values. In the packet definitions, similar enums are required, where the values should be specified. Also mapping code is required to map between the enum types.
  2. Use the enum types for method parameters
  3. Change time parameters like remainTimeSec to be TimeSpan. The conversion to seconds can be done in the view plugin implementation.
  4. Change method names from Send... to Show.... The game logic should not be aware that it's actually sending something over the network.
  5. Change counts from byte to int. Conversion to byte can be done inside the view plugin implementation.

private readonly IMapInitializer _mapInitializer;
private readonly TimeSpan _towerOfRefinementDuration;

private volatile KanturuPhase _phase = KanturuPhase.Open;
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.

Why volatile?

private int _waveKillCount;
private int _waveKillTarget;
private TaskCompletionSource _phaseComplete = new(TaskCreationOptions.RunContinuationsAsynchronously);
private volatile bool _isVictory;
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.

Why volatile?

Comment on lines +165 to +167
await this.ForEachPlayerAsync(player =>
player.InvokeViewPlugInAsync<IShowMessagePlugIn>(p =>
p.ShowMessageAsync("Maya rises from the depths of the Refinery Tower!", MessageType.GoldenCenter)).AsTask()).ConfigureAwait(false);
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.

Please move messages like this to the PlayerMessage resources. Then please also use the Player.ShowLocalizedBlueMessageAsync and Player.ShowLocalizedGoldenMessageAsync for this and all other player messages.

}

/// <inheritdoc/>
protected override void OnMonsterDied(object? sender, DeathInformation e)
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.

  1. Make the method async void and wrap it with try-catch.
  2. Then you can remove all these Task.Runs, and await the calls, e.g. to SendMonsterUserCountAsync.
  3. Make sure that SendMonsterUserCountAsync doesn't throw exceptions.

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.

Instead of constructing every packet manually here, define them in the ServerToClientPackets.xml. Otherwise, they're not documented and not tested.
Then you can easily use the automatically generated structs to send the packets.

apraxico and others added 2 commits March 24, 2026 21:13
Addresses Gemini Code Assist review feedback (high severity):

- OpenElphisBarrierAsync: wrapped entire body in try-catch so that
  failures while opening the Elphis barrier are logged instead of
  silently propagating as unobserved task exceptions.

- SendMonsterUserCountAsync (8 call sites): introduced
  FireAndForgetMonsterCountAsync helper that wraps the broadcast in a
  try-catch, preventing a disconnecting player from causing an
  unhandled exception in the monster-death callback.

- RunMayaWideAreaAttacksAsync: wrapped ForEachPlayerAsync broadcast in
  try-catch; the loop continues on errors rather than crashing the
  visual-effects task.

- Diagnostic ForEachPlayerAsync in out-of-phase default case: added
  try-catch to the fire-and-forget lambda.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- IKanturuEventViewPlugIn: Replace static classes with enums (KanturuState,
  KanturuMayaDetailState, KanturuNightmareDetailState, KanturuTowerDetailState,
  KanturuEnterResult, KanturuBattleResult, KanturuMayaAttackType). Rename
  Send... methods to Show... and use TimeSpan/int instead of int/byte params.

- ServerToClientPackets.xml: Define all 7 Kanturu packets (0xD1/00-07)
  so they are documented, tested, and auto-generated.

- KanturuEventViewPlugIn: Use auto-generated connection.SendKanturu*Async()
  extension methods with mapping functions between enum and protocol values.

- KanturuContext: Make OnMonsterDied async void with try-catch (VSTHRD100
  pattern matching ChaosCastleContext); remove all Task.Run fire-and-forget
  wrappers; await ShowMonsterUserCountAsync directly. Add volatile field
  XML doc comments explaining threading rationale. Replace all SendKanturu*
  calls with typed ShowMaya/Nightmare/TowerStateToAllAsync helpers. Replace
  Send*Async helpers with Show*Async helpers using int/TimeSpan params.
  Use ShowGoldenMessageAsync + nameof(PlayerMessage.Kanturu*) for all
  localized messages.

- KanturuGatewayPlugIn: Update to use KanturuState enum and new
  ShowStateInfoAsync(KanturuState, byte, bool, int, TimeSpan) signature.

- PlayerMessage: Add 20 Kanturu message keys (Maya phases, Nightmare phases,
  Tower of Refinement) plus KanturuDefeat for the game-end defeat message.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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