feat: Add Kanturu Refinery Tower event (resubmission of #719) - #923
Conversation
Resubmits the work of PR #719 (by @apraxico) together with the fixes contributed by @foxtnaider on top of the current master. Contents: - KanturuContext with the eight event phases, Maya hands, Nightmare boss and the Tower of Refinement phase - View plug-in, packet handlers and packet definitions for the D1 packet group - Periodic start plug-in and configuration - Season 6 initializer, map spawn definitions and configuration update plug-ins for existing databases Fixes taken from the follow-up work: - Kanturu packets are defined inside <Packets> so the XSLT generator emits the connection extensions instead of them being hand-written - GameContext creates a KanturuContext for MiniGameType.Kanturu Co-Authored-By: apraxico <80994201+apraxico@users.noreply.github.com> Co-Authored-By: foxtnaider <46683504+foxtnaider@users.noreply.github.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G6STKnRbNYW5JPSuNXNFx4
Follow-up on the resubmitted work of #719: - Resolve all analyzer warnings the new files introduced (SA1201, SA1204, SA1210, SA1501, SA1515, SA1525, SA1611, SA1623, SA1117, VSTHRD200, CS1574). - Apply the configured KanturuStartConfiguration.TowerOfRefinementDuration: it was only settable, the context always used its hard-coded default, because GameContext creates the context without the optional parameter. The context now reads the value from the start plug-in's configuration. - Move the Kanturu state enums into their own file, so the view plug-in interface file only contains the interface. - Document the KanturuStateChange.StateType enum values in the packet definition, which removes six CS1591 warnings from the generated code. - Share the event wave spawns between the map initializer and AddKanturuMapContentUpdatePlugIn instead of maintaining two copies of the same 16 spawn areas, and name the monster numbers. - Use localized plug-in names/descriptions (PlugInResources) for the new GameServer plug-ins, like all other plug-ins there. - Restore the byte order mark of the two touched Season 6 files and fix the Nightmare spawn coordinates in the map comments. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G6STKnRbNYW5JPSuNXNFx4
ServerToClientPacketTests.cs is generated from ServerToClientPackets.xml and checked in. As long as the Kanturu packets were outside <Packets>, the generator skipped them, so the file didn't contain their tests. Now that they are inside, it does. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G6STKnRbNYW5JPSuNXNFx4
The comment line ended with a semicolon and started with a method name, so SonarCSharp_S125 flagged it as commented out code (the one issue Codacy reported on this PR). Same meaning, different wording. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G6STKnRbNYW5JPSuNXNFx4
Addresses the review of the Kanturu event. Own sub-namespace, one type per file: All Kanturu game logic moved into MUnique.OpenMU.GameLogic.MiniGames.Kanturu. The four client state enums, which shared one file, now have their own. A configuration instead of hardcoded values: The run of the event is described by a KanturuEventDefinition, which is configured at the KanturuStartPlugIn and can therefore be changed in the admin panel without code changes. It holds the intro, the ordered list of phases and the settings of the Tower of Refinement. Each KanturuPhaseDefinition describes what the context previously had hardcoded in its game loop and in the switch of OnMonsterDied: the state which is sent to the clients, the spawn wave, the monsters which count towards the kill target, the time limit, the messages and the standby time after the phase. A KanturuPhaseKind selects how a phase is executed, so the three repeating shapes - monster wave, transition and boss fight - each exist once instead of once per phase. The boss fight gets its teleport positions and health thresholds from KanturuNightmareDefinition, the barrier areas and the Elphis coordinates come from the definition too. The game loop is now a foreach over the phases, and OnMonsterDied is a lookup in the current phase instead of a nine case switch. KanturuEventDefinition.Default describes the original season 6 event, so the behaviour is unchanged. Opened NPC check: The gateway plug-in now sets LeavesDialogOpen, so the player stays assigned to the Gateway Machine while its dialog is open. The 0xD1/0x00 and 0xD1/0x01 handlers check that assignment, like the castle siege handlers do, so the packets can't be used without talking to the NPC first. On a successful entry the assignment is cleared, because the client closes the dialog when it changes the map. Also adds the byte order mark to the Kanturu files which still missed it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G6STKnRbNYW5JPSuNXNFx4
The phases and the boss fight referenced their monsters by number, which required the numbers to be looked up by hand. They now hold MonsterDefinition references instead, so the admin panel can offer the monsters for selection. The custom configuration of a plug-in is serialized with the ByDataSourceReferenceHandler, so the references are stored as the id of the monster and resolved from the game configuration when they are read - the same way the NpcChatCommandPlugIn references its merchant. Because ISupportDefaultCustomConfiguration.CreateDefaultConfig has no game configuration, it can't resolve the monsters. KanturuEventDefinition.Default therefore became CreateDefault(GameConfiguration), and the data initialization seeds the configuration of the start plug-in with it, like it already does for the BlessJewelConsumeHandlerPlugIn. For databases which have no configuration yet, the context falls back to CreateDefault with the game configuration it runs on, so the event works either way. The spawned monsters are compared to the configured definitions by their number, because the definition of a spawned monster can be a different instance than the configured one. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G6STKnRbNYW5JPSuNXNFx4
|
I tested this independently in a clean, isolated worktree (checked out Build
The three problems from #719, confirmed fixed and wired end to end, not just patched locally:
Other things I checked while reading the diff:
Minor nitpicks, nothing blocking:
I didn't manage to spin up the server end-to-end (no Docker/Postgres on my test machine) to click through Game Configuration → Mini Games in the AdminPanel, so I can't personally confirm the visual/admin side beyond what @foxtnaider already documented with screenshots on #719. Everything I could verify independently checks out: clean build, byte-identical codegen, full test suite, and a manual read of the three previously-reported bugs plus the surrounding code. Nice work pulling this together from two separate contributions. |
Resolves the UpdateVersion conflict: master added ConfigureCastleSiegeLifeStone as 112, so the two Kanturu updates move to 113 and 114. They are only referenced by name, so the renumbering doesn't affect anything else. Also removes the indentation of the two otherwise empty lines around them, which SA1028 reported. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G6STKnRbNYW5JPSuNXNFx4
Convert and ConvertChange were two near-identical one line casts, as didiconcs noticed in the review. Each had a single caller, so they are inlined instead of folded into one generic helper - a generic conversion between the two unrelated packet enums would have to go through object and box the value. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G6STKnRbNYW5JPSuNXNFx4
Deploying openmudocs with
|
| Latest commit: |
d84958c
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://ba5f308b.openmudocs.pages.dev |
| Branch Preview URL: | https://claude-pr-719-review-resubmi.openmudocs.pages.dev |
Entry check: EnterMiniGameAction warps the player to the entrance of the mini game directly, and PlayerMapTransitions.WarpToAsync doesn't evaluate the map requirements - only the warp actions do. The pendant was therefore only required by the client, which is what jkok25 reported on #719. The action now checks the requirements of the entrance map and shows the same message the warp actions use. Kanturu and Icarus are the only maps with requirements, and Icarus is no mini game, so nothing else changes. Wear during the event: The items which provide an attribute the event map requires now lose durability in a configurable interval. When such an item is destroyed, it is removed from the inventory and the player is moved to the safezone of the event map, which also removes it from the running event. The items aren't hardcoded: they are looked up as the equipped items whose definition provides one of the attributes in the map requirements, so it follows the Moonstone Pendant through the same configuration which gates the entry. The players are moved out after ForEachPlayerAsync returned: it holds the reader lock of the enter lock, and removing a player from the map waits for the same lock as a writer. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G6STKnRbNYW5JPSuNXNFx4
Resubmission of the stale PR #719 by @apraxico, rebased onto the current
masterand combined with the fixes @foxtnaider contributed on hiskanturu-fixesbranch, plus a review pass on top.Credit for the implementation goes to @apraxico; credit for making it build and actually run goes to @foxtnaider, who tested the result end-to-end on a Season 6 server.
What it adds
The server-side logic for the Kanturu Refinery Tower mini-game (map 39), following the plug-in patterns of BloodCastle and ChaosCastle:
KanturuContextIKanturuEventViewPlugIn/KanturuStatesKanturuEventViewPlugInKanturuGatewayPlugInKanturuStartPlugIn/KanturuStartConfiguration/KanturuGameServerStateKanturuEnterRequestHandlerPlugIn/KanturuInfoRequestHandlerPlugIn/KanturuGroupHandlerPlugInKanturuInitializerMiniGameDefinitionAddKanturuDataUpdatePlugIn/AddKanturuMapContentUpdatePlugInModified:
MiniGameType(newKanturuvalue),GameContext(creates aKanturuContext),GameConfigurationInitializer,KanturuEvent(bosses and wave spawns), the server-to-client packet definitions and the player messages.Fixes carried over from the follow-up work
<Packet>elements were direct children of the root, outside<Packets>.GenerateExtensions.xsltonly iterates the children of<Packets>, so the generator emitted nothing for them and theSendKanturuXxxAsyncmethods had been added to the generatedConnectionExtensions.csby hand. A clean build overwrote them, which is theCS1061build failure reported on feat: Add Kanturu Refinery Tower event implementation #719. The elements are now inside<Packets>and everything is generated.GameContexthad nocase MiniGameType.Kanturu, so the event fell through to a plainMiniGameContext: it opened the map, accepted players and then did nothing.ObjClassH/ObjClassLarguments onSendKanturuMayaWideAreaAttackAsync(CS7036).Review pass in this PR
KanturuStartConfiguration.TowerOfRefinementDurationwas write-only:GameContextconstructs the context without the optional parameter, so the hard-coded one-hour default always won. The context now reads the value from the start plug-in's configuration.CS1591warnings in the generated packet code, which are fixed by documenting theKanturuStateChange.StateTypeenum values in the packet definition.KanturuEvent, once inAddKanturuMapContentUpdatePlugIn. They are now shared, and the monster numbers are named constants.PlugInResourcesnames and descriptions like the other plug-ins there.Verification
dotnet build src/Startup/MUnique.OpenMU.Startup.csproj— clean, from a fresh checkout, with the packet generator running: 0 errors, no warnings from the new files, and the generated files are byte-identical to what is committed.dotnet build ... -p:ci=true(what the CI workflow runs) — 0 errors.dotnet test tests/MUnique.OpenMU.Persistence.Initialization.Tests— 12 passed, 2 skipped.Closes #719.
Generated by Claude Code