Illusion Temple Mini Game - Event - #893
Conversation
Select and persist defending and attacking alliances using registration scores and tie order. Synchronize join sides for players in the siege map, track battle participation, deliver configured online or pending rewards, award guild scores, and add the B4/B5 guild-list handlers with persistence and regression tests. # Conflicts: # src/GameLogic/CastleSiege/CastleSiegeContext.cs # src/GameLogic/CastleSiege/CastleSiegePlugIn.cs # src/GameServer/Properties/PlugInResources.Designer.cs
Remove the redundant concurrent collections import and simplify the guild-selection test array declarations. This resolves the reported Codacy findings without changing runtime behavior. # Conflicts: # src/GameLogic/CastleSiege/CastleSiegeContext.cs
Handle disconnected players safely when sending Castle Siege join-side notifications. Remove the unused Life Stone cleanup placeholder from the guild-selection phase and propagate Dapr runtime guild-resolution failures instead of treating them as missing guilds. Add regression coverage for notifications after disconnect. # Conflicts: # src/GameLogic/CastleSiege/CastleSiegePlugIn.cs
Track exact participant time across delayed ticks, map transitions, and battle completion. Persist cleared final guild selections when the game-server context is unavailable and centralize guild ordering for persistence and packet responses. Integrate NPC map tracking and lifecycle synchronization with map-scoped join-side assignment and authorization. Add regression coverage for elapsed participation, stale persisted guild lists, deterministic ordering, and defender gate operation.
Preserve Castle Siege join-side and participation state across transient player states and reconnects. Resolve battle winners and offline alliances correctly, deliver pending rewards atomically, batch guild score updates, and replace broad persistence queries with targeted lookups. Synchronize runtime NPC and magic-effect access while keeping player map events independent of the state-machine lock. Add regression coverage for participant timing, reconnect overlap, reward delivery, and winning-alliance scoring.
- use a JSON-safe Dapr request model for guild score updates - synchronize join-side effects only for tracked siege players - preserve participation tracking when players leave the siege map - prevent rewarding the previous castle owner before Crown capture - notify clients about delivered rewards and batch pending reward writes - discard permanently invalid pending rewards with a warning - retain the in-memory alliance lookup fast path with persistence fallback - prevent incomplete alliance data from consuming an attacking side - add regression coverage for selection, tracking, and reward edge cases
Initialize and restore the defending alliance as the current battle owner so guild score rewards have a valid winner until a Crown capture replaces it. Track fully loaded alliances and query persistence only for incomplete runtime caches, preserving cache-only repeated alliance lookups. Add regression coverage for defender victories, restart restoration, alliance persistence fallback, and hot-path cache reuse.
…ills Implements the full Illusion Temple event as a team-based PvP mini game: - Stone Statue (NPC 380) holds a sacred relic; a player talks to it to become the carrier, delivers it to his team's storage (383/384) to score, and drops it on death or when leaving. - Two teams (Allied/Illusion Forces), assigned and spawned on game start, with a live per-player state update (own team positions, relic carrier, remaining time) and an end-of-game result screen. - A team needs at least 2 points, and more than the opposing team, to be declared the winner - matching the original event (a 1:0 finish is a draw, like in the reference server). - Experience is granted automatically to the winning team at game end; other reward types (item drops) are only granted once the player explicitly claims them (0xBF05), closing the result dialog - mirroring the original "click Close to be compensated" flow. - Skill points (start at 10, cap 90): +1 for killing an enemy player, +2 for killing a roaming arena monster. They fuel four special skills (210 Order of Protection, 211 Restraint, 212 Tracking, 213 Weaken), each costing 10 points, requested via a dedicated 0xBF02 packet. - MiniGameDefinition.MinimumPlayerCount is now admin-configurable (EF migration included) instead of hardcoded per event type. - Fixed a base MiniGameContext bug where players stuck on an event map with too few participants were never moved back to safezone. New server<->client packets: IllusionTempleEventState, HolyItemRelics, SkillUsageResult, SkillPointUpdate, SkillEnded, RewardRequest handling, and corrected byte layouts for IllusionTempleState/Result (missing RelicCarrierId field, wrong array offset, and a 3-byte padding gap in PlayerResult that the original client's C struct alignment expects). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Registers the periodic mini game start plugin (every 2 hours, matching the official Webzen server's Illusion Temple schedule) and its game server state tracking, plus a chat command for game masters to start an Illusion Temple match on demand for testing. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Corrects the statue/guardian/relic-box spawn data on all six temple maps, verified against a working Season 6 Episode 3 server's spawn list: two stone statue positions (not three), added the two decorative team guardian NPCs (381/382), and fixed both relic storage box coordinates, which were off by one tile. Adds the 32 roaming "Illusion Sorc. Spirit" arena monster spawns (NPC 386-399, cycling per temple level) to temples 1-5 - temple 6 has none, matching the reference data. Also sets each temple's safezone map to Devias explicitly: without it, a temple's own spawn gate made BaseMapInitializer default the safezone to the temple map itself, so a player warped to "safezone" (e.g. when too few players joined) was simply sent back into the arena instead of actually leaving it. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Existing servers won't get the Illusion Temple changes above just from an EF migration - the mini game definitions, monster/item data and map spawns are seed data, normally only created on a fresh install. This update plugin applies all of it to an already-running database instead: - Fixes the "Illusion Sorcerer Covenant"/"Scroll of Blood" ticket item numbers (50/51 were swapped - IllusionTempleInitializer expects the ticket at Group 13, Number 51), by correcting the Number field on the existing item entities so already-owned instances keep working. - Adds the sacred relic item (Group 14, Number 64) if missing. - Adds the 14 arena monster definitions (386-399) and their spawns, the corrected statue/guardian/box spawns, and the safezone map fix - all matching the fresh-install map data from the previous commit. - Adds the two special-skill magic effects (210/211). - Creates the mini game definitions on a database that doesn't have Illusion Temple at all yet, or backfills MinimumPlayerCount on ones that already do. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Covers entrance/entry timing, finishing early when too few players remain, team assignment for even and odd player counts, game start, the statue/relic pickup-death-pickup loop, scoring for the carrier's own team vs. the enemy's, and the end-of-game reward flow (experience granted immediately, an item reward only once claimed). MiniGameContext auto-starts a real-time countdown on construction (clamped to at least 30s), far too slow for a test suite, so these tests build a fully wired IllusionTempleContext and drive its lifecycle hooks directly via reflection instead of waiting on the real timers. Extends GameContextTestHelper.CreateGameContext with an optional IDropGenerator parameter (needed to test item rewards) and a MaximumLevel default (needed for AddExperienceAsync to actually grant anything - it no-ops above the configured max level, which defaulted to 0). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Removed the unused _activeStatue field (only ever written, never read) and made the two team spawn coordinate fields readonly, since neither is ever reassigned after construction. - Renamed a local variable in TeleportToStartCoordinatesAsync that was shadowing the illusionForcesCoordinates field. - In the test file: documented why the two reflection calls that bypass accessibility are safe (test-only code, hardcoded member names, no external input), replaced a switch without a default case with if/else, dropped a redundant explicit default-value argument, gave the fake IDropGenerator's genuinely unused interface parameters discard-style names, and made the test spawn-area helper actually use its "number" parameter to build a stable Guid. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The DyingDropsTheRelicAndItCanBePickedUpAgain test was failing deterministically: the test entered players via TryEnterAsync and the client map-change handshake, but skipped the WarpToAsync to the event entrance that EnterMiniGameAction performs in between. Without it the players stayed on the default map instead of the mini game's own map instance, so the dropped relic landed on a map the event isn't subscribed to and OnItemDroppedOnMap - the single place that clears the relic carrier - never ran. The entry helper now mirrors the real server flow, and the whole suite passes. Codacy follow-ups: - Moved the usings of IllusionTempleContext inside the namespace, as the rest of the code base does, and dropped two that were genuinely unused. The System.Collections.Concurrent one is kept - it is used by the ConcurrentDictionary fields. - Documented the two reflection helpers with SuppressMessage justifications instead of only comments: both are test-only, operate on types from this solution, and are passed hardcoded member names. - Gave the fake IDropGenerator its interface parameter names back and justified them at class level - they are mandated by the interface and deliberately ignored. - Added the missing else branch in AwaitResultAsync. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Adds a documentation website which brings the existing guides together and closes the biggest gap: the admin panel had no usage documentation at all. Content: * Getting Started - requirements/ports, docker, from source, connecting a game client, test accounts * Deployment - the three variants, HTTPS, and the start parameters and environment variables of the startup project * Admin Panel - one page per screen (setup, configuration updates, servers, accounts, online accounts, game configuration, plugins, chat commands, map editor, live map, logs and monitoring, users) plus a page with task-oriented how-tos * Development - architecture, solution structure, the plugin system and contributing * Reference - ports and a pointer to the generated packet documentation Setup: * Docusaurus 3 in docs-website, docs-only mode, offline search (@easyops-cn/docusaurus-search-local), light/dark theme * The build fails on broken links, anchors and image references * A GitHub Actions workflow builds the site for pull requests which touch it * The deployment URL is configurable via DOCS_URL/DOCS_BASE_URL, so a Cloudflare Pages project or a fork doesn't need a code change * README.md and QuickStart.md link to the site The favicon is a placeholder, and screenshots for the admin panel pages are still to be added. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LidmXPYjGN64P1RgCiY7Ri
ReviewSubstantial, well-documented feature work — the comments explain why (the client's C-struct padding, the hardcoded arena barriers, the score-table-before-warp ordering) rather than restating the code, and the packet-layout corrections look right. CI is green (Codacy is Blocking1.
So "existing databases get all of the above without a full reinstall" doesn't actually happen. Should be 2. Re-entrant warp in
This fires on the normal end-of-game path, on 3. Every failed entry is reported to the client as success — public static byte ToIllusionTempleEnterResult(this EnterResult enterResult)
{
return 0;
}The parameter is ignored and the remark itself says failures should be 4. Item rewards are silently lost for most winners —
5. The 20s preparation delay runs inside The tests drive Correctness6. 7. 8. Special skills hit your own team. 9. Reported experience is wrong for 10. Relic race and item leak — 11. The relic is never reclaimed at game end. A carrier who never delivers keeps "Cursed Castle Water" in his inventory permanently. 12. Dead paths. 13. Hardcoded Devias exit gate duplicated in Consistency / style
Questions
Generated by Claude Code |
Blocking: - Added the missing <summary> tag on UpdateVersion.IllusionTempleData (the number collision with AddCastleSiegeData resolved itself when the branch was rebased - it's 104 now). - OnObjectRemovedFromMapAsync no longer warps the leaving player: it runs from inside WarpToAsync, so warping again nested the warps and left the player on two maps with duplicate map-change packets, while also bypassing the per-temple safezone. It's now pure state cleanup; leaving is done by ClaimRewardAsync and the base class's exit handling. - ToIllusionTempleEnterResult actually maps its parameter now, instead of reporting success for every refusal. - Item rewards reached only the first winner, because DoesRewardApply compares parties and a party is disposed once fewer than two members remain. The winner-related predicates moved into overridable IsWinner/IsInWinningParty/IsWinnerOrInWinningParty methods, which the illusion temple answers by team - so every member of the winning team is rewarded. - The 20 second preparation delay is configurable (PreparationDuration). The tests set it to zero, which brings the suite from ~4.8 minutes down to ~5 seconds. Correctness: - Team mates are spread over consecutive tiles again - the previous code incremented a copy of a readonly field, so everyone stacked on one tile. Typo in the comment fixed as well. - Dropped the redundant "player.Party = null", which bypassed KickMySelfAsync and left the player in the old party's member array. - Restraint and Weaken only hit opponents now, and using a special skill requires a running event and a living caster. - The reported experience mirrors what is actually granted, including the per-remaining-second reward type. - Talking to the statue claims the carrier slot atomically, so two players can't both walk away with a relic, and the item is only created after the inventory-space check succeeds (with the orphan deleted if it doesn't). - The relic is taken away when its carrier leaves, so it can't stay in an inventory after the match. - The Ended and WaitingRoom event states are sent now, and the skill-ended view plugin is wired to the magic effect's timeout. - The hardcoded Devias gate is gone; leaving uses the map's configured safezone. Consistency: - The mini game entry refusals are localized PlayerMessage resources instead of hardcoded English strings (including the backtick typo). - Fixed copy-paste documentation in the chat command, the game server state and MiniGameContext.GetSpawnGate. - The chat command reports back when the event isn't configured, which also resolves its CS1998. - Collapsed the identical 383/384 cases in TalkNpcAction and indented the switch properly. - IllusionTempleTeam moved to the MiniGames namespace, matching its folder. - Removed the whitespace noise in MiniGameContext and Player. - GameContextTestHelper takes the maximum level as a parameter instead of changing it globally for every test. - Deduplicated the relic's group/number checks behind IsRelicDefinition. - The update plugin removes the obsolete 658-668 statue spawns, so existing databases don't keep the placeholders next to the new ones. - FinishesWhenTooFewPlayersRemainAsync asserts that the match actually ends, not just the precondition. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The site duplicated what it was built from, so each topic now has exactly one home: * The website owns what a user, operator or new contributor reads: getting started, deployment, the admin panel, server features, architecture, the plugin system and contributing. * The repository keeps the documentation which is derived from or tightly bound to the code: the generated packet descriptions, the game mechanics in docs/ and the implementation notes in the projects' Readme files. Moved to the website: * docs/Bots.md becomes the new "Server features" section, cross-linked with the plugins, servers, logs and game configuration pages of the admin panel * src/Web/AdminPanel/Readme.md and src/Web/Map/Readme.md - the latter was an outdated copy of the former; their "ideas for the future" are now the planned sections of the admin panel overview and the live map page Replaced by a short pointer to the page which now holds the content: QuickStart.md, the four deploy readmes, src/Startup/Readme.md, src/PlugIns/Readme.md, docs/Bots.md, src/Web/AdminPanel/Readme.md and src/Web/Map/Readme.md. README.md keeps the project introduction and gains a documentation index; its duplicated sections (used technologies, gameplay differences, deployment, contribution rules) now live on the website and in CONTRIBUTING.md. docs/Readme.md becomes the index of the code-bound documentation and explains the split. All relative links were verified to resolve, and the site build - which fails on broken links - passes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LidmXPYjGN64P1RgCiY7Ri
The packet analysis of the network analyzer is useful outside of the WinForms tool, e.g. to show the traffic of a connected player in the admin panel. Its platform neutral part is therefore extracted into a net10.0 library, so it can be referenced by the servers and the web projects. * MUnique.OpenMU.Network.Analyzer (src/Network/Analyzer) is now a library which contains Packet, PacketAnalyzer, ICapturedConnection, SavedConnection and the mucap save/load extensions. It also carries the packet definition xml files, so they are copied to the output of every referencing project. * MUnique.OpenMU.Network.Analyzer.WinForms (src/Network/Analyzer.WinForms) is the tool itself, with the forms, the proxy (LiveConnection, LiveConnectionListener) and the program entry point. Its assembly is renamed accordingly, because the library assembly keeps the previous name and lands in the same output folder. * MUnique.OpenMU.Network.csproj removes the new folder from its default item globs, like it already does for Analyzer and Packets. Without that, the WinForms resx files would be compiled into the network library, which fails because they contain non-string resources. There is no functional change: all files are moved as they are. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Pb82LmoaUVdZtBtQs7xrtA
The plugin implementation is a developer topic, so it doesn't belong on the documentation website, which is aimed at users and server operators. src/PlugIns/Readme.md is restored to its previous state (byte-identical to master) and is the single source again; the website's development/plugins.md is removed and the places which linked to it now link to the readme: * the Development section of the sidebar and the architecture page * the PlugIns row of the solution structure * "Writing your own plugin" on the admin panel's Plugins page, which keeps covering what operators do with plugins: activate, deactivate, configure * CONTRIBUTING.md and the docs index Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LidmXPYjGN64P1RgCiY7Ri
Resolves the conflict in docs/Bots.md, whose content moved to the documentation website in this branch while master reworked the bot party behaviour: the file keeps its pointer and the changes from master were applied to the page which now holds the content. Also follows the network analyzer split from master, which turned the analyzer into a library plus a WinForms tool, in the solution structure page. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LidmXPYjGN64P1RgCiY7Ri
…tup-a82apj docs: add Docusaurus documentation site in docs-website
Character class: The result screen showed the same wrong class for every player. A packet capture confirmed the server sends correct, distinct values, so the client decodes them differently than assumed: it reads the class line from the lower nibble (0 Dark Wizard, 1 Dark Knight, 2 Fairy Elf, 3 Magic Gladiator, 4 Dark Lord, 5 Summoner, 6 Rage Fighter) and the evolution step from the upper one, while the internal numbering packs both the other way around. Two players of different lines therefore collapsed onto the same entry whenever the sent values happened to share their lower nibble - both were shown as the Magic Gladiator line's master class. The conversion is inverted accordingly and verified on a live client. Uninitialized packet bytes: Each player entry carries three alignment bytes which are never written, and the pipe buffer isn't zeroed, so leftovers of previous packets went out on the wire. The buffer is cleared before writing now. Entry dialog couldn't be opened a second time: The player state is only reset back to EnteredWorld after the dialog has been shown, so a failure while querying the temple user counts left the player stuck in NpcDialogOpened - and opening the dialog requires exactly that transition. The reset moved into a finally block. The user count view plugin also missed the connection check every other view plugin has, which is one way that query could throw. Statue could stay locked for the rest of the match: Claiming the carrier slot before looking up the relic item meant an exception (e.g. a database without the item) left the slot claimed forever, and nobody could pick up the relic anymore. The claim is now released on every failure path, and a missing item definition is logged instead of thrown. Also reverted the WaitingRoom event state which was sent on entry: it was added on the assumption that the unused enum value belongs there, without evidence from the reference server, and it sent a new packet to the client before the player was even on the event map. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ills Implements the full Illusion Temple event as a team-based PvP mini game: - Stone Statue (NPC 380) holds a sacred relic; a player talks to it to become the carrier, delivers it to his team's storage (383/384) to score, and drops it on death or when leaving. - Two teams (Allied/Illusion Forces), assigned and spawned on game start, with a live per-player state update (own team positions, relic carrier, remaining time) and an end-of-game result screen. - A team needs at least 2 points, and more than the opposing team, to be declared the winner - matching the original event (a 1:0 finish is a draw, like in the reference server). - Experience is granted automatically to the winning team at game end; other reward types (item drops) are only granted once the player explicitly claims them (0xBF05), closing the result dialog - mirroring the original "click Close to be compensated" flow. - Skill points (start at 10, cap 90): +1 for killing an enemy player, +2 for killing a roaming arena monster. They fuel four special skills (210 Order of Protection, 211 Restraint, 212 Tracking, 213 Weaken), each costing 10 points, requested via a dedicated 0xBF02 packet. - MiniGameDefinition.MinimumPlayerCount is now admin-configurable (EF migration included) instead of hardcoded per event type. - Fixed a base MiniGameContext bug where players stuck on an event map with too few participants were never moved back to safezone. New server<->client packets: IllusionTempleEventState, HolyItemRelics, SkillUsageResult, SkillPointUpdate, SkillEnded, RewardRequest handling, and corrected byte layouts for IllusionTempleState/Result (missing RelicCarrierId field, wrong array offset, and a 3-byte padding gap in PlayerResult that the original client's C struct alignment expects). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Registers the periodic mini game start plugin (every 2 hours, matching the official Webzen server's Illusion Temple schedule) and its game server state tracking, plus a chat command for game masters to start an Illusion Temple match on demand for testing. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Corrects the statue/guardian/relic-box spawn data on all six temple maps, verified against a working Season 6 Episode 3 server's spawn list: two stone statue positions (not three), added the two decorative team guardian NPCs (381/382), and fixed both relic storage box coordinates, which were off by one tile. Adds the 32 roaming "Illusion Sorc. Spirit" arena monster spawns (NPC 386-399, cycling per temple level) to temples 1-5 - temple 6 has none, matching the reference data. Also sets each temple's safezone map to Devias explicitly: without it, a temple's own spawn gate made BaseMapInitializer default the safezone to the temple map itself, so a player warped to "safezone" (e.g. when too few players joined) was simply sent back into the arena instead of actually leaving it. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Existing servers won't get the Illusion Temple changes above just from an EF migration - the mini game definitions, monster/item data and map spawns are seed data, normally only created on a fresh install. This update plugin applies all of it to an already-running database instead: - Fixes the "Illusion Sorcerer Covenant"/"Scroll of Blood" ticket item numbers (50/51 were swapped - IllusionTempleInitializer expects the ticket at Group 13, Number 51), by correcting the Number field on the existing item entities so already-owned instances keep working. - Adds the sacred relic item (Group 14, Number 64) if missing. - Adds the 14 arena monster definitions (386-399) and their spawns, the corrected statue/guardian/box spawns, and the safezone map fix - all matching the fresh-install map data from the previous commit. - Adds the two special-skill magic effects (210/211). - Creates the mini game definitions on a database that doesn't have Illusion Temple at all yet, or backfills MinimumPlayerCount on ones that already do. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Covers entrance/entry timing, finishing early when too few players remain, team assignment for even and odd player counts, game start, the statue/relic pickup-death-pickup loop, scoring for the carrier's own team vs. the enemy's, and the end-of-game reward flow (experience granted immediately, an item reward only once claimed). MiniGameContext auto-starts a real-time countdown on construction (clamped to at least 30s), far too slow for a test suite, so these tests build a fully wired IllusionTempleContext and drive its lifecycle hooks directly via reflection instead of waiting on the real timers. Extends GameContextTestHelper.CreateGameContext with an optional IDropGenerator parameter (needed to test item rewards) and a MaximumLevel default (needed for AddExperienceAsync to actually grant anything - it no-ops above the configured max level, which defaulted to 0). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Removed the unused _activeStatue field (only ever written, never read) and made the two team spawn coordinate fields readonly, since neither is ever reassigned after construction. - Renamed a local variable in TeleportToStartCoordinatesAsync that was shadowing the illusionForcesCoordinates field. - In the test file: documented why the two reflection calls that bypass accessibility are safe (test-only code, hardcoded member names, no external input), replaced a switch without a default case with if/else, dropped a redundant explicit default-value argument, gave the fake IDropGenerator's genuinely unused interface parameters discard-style names, and made the test spawn-area helper actually use its "number" parameter to build a stable Guid. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The DyingDropsTheRelicAndItCanBePickedUpAgain test was failing deterministically: the test entered players via TryEnterAsync and the client map-change handshake, but skipped the WarpToAsync to the event entrance that EnterMiniGameAction performs in between. Without it the players stayed on the default map instead of the mini game's own map instance, so the dropped relic landed on a map the event isn't subscribed to and OnItemDroppedOnMap - the single place that clears the relic carrier - never ran. The entry helper now mirrors the real server flow, and the whole suite passes. Codacy follow-ups: - Moved the usings of IllusionTempleContext inside the namespace, as the rest of the code base does, and dropped two that were genuinely unused. The System.Collections.Concurrent one is kept - it is used by the ConcurrentDictionary fields. - Documented the two reflection helpers with SuppressMessage justifications instead of only comments: both are test-only, operate on types from this solution, and are passed hardcoded member names. - Gave the fake IDropGenerator its interface parameter names back and justified them at class level - they are mandated by the interface and deliberately ignored. - Added the missing else branch in AwaitResultAsync. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Blocking: - Added the missing <summary> tag on UpdateVersion.IllusionTempleData (the number collision with AddCastleSiegeData resolved itself when the branch was rebased - it's 104 now). - OnObjectRemovedFromMapAsync no longer warps the leaving player: it runs from inside WarpToAsync, so warping again nested the warps and left the player on two maps with duplicate map-change packets, while also bypassing the per-temple safezone. It's now pure state cleanup; leaving is done by ClaimRewardAsync and the base class's exit handling. - ToIllusionTempleEnterResult actually maps its parameter now, instead of reporting success for every refusal. - Item rewards reached only the first winner, because DoesRewardApply compares parties and a party is disposed once fewer than two members remain. The winner-related predicates moved into overridable IsWinner/IsInWinningParty/IsWinnerOrInWinningParty methods, which the illusion temple answers by team - so every member of the winning team is rewarded. - The 20 second preparation delay is configurable (PreparationDuration). The tests set it to zero, which brings the suite from ~4.8 minutes down to ~5 seconds. Correctness: - Team mates are spread over consecutive tiles again - the previous code incremented a copy of a readonly field, so everyone stacked on one tile. Typo in the comment fixed as well. - Dropped the redundant "player.Party = null", which bypassed KickMySelfAsync and left the player in the old party's member array. - Restraint and Weaken only hit opponents now, and using a special skill requires a running event and a living caster. - The reported experience mirrors what is actually granted, including the per-remaining-second reward type. - Talking to the statue claims the carrier slot atomically, so two players can't both walk away with a relic, and the item is only created after the inventory-space check succeeds (with the orphan deleted if it doesn't). - The relic is taken away when its carrier leaves, so it can't stay in an inventory after the match. - The Ended and WaitingRoom event states are sent now, and the skill-ended view plugin is wired to the magic effect's timeout. - The hardcoded Devias gate is gone; leaving uses the map's configured safezone. Consistency: - The mini game entry refusals are localized PlayerMessage resources instead of hardcoded English strings (including the backtick typo). - Fixed copy-paste documentation in the chat command, the game server state and MiniGameContext.GetSpawnGate. - The chat command reports back when the event isn't configured, which also resolves its CS1998. - Collapsed the identical 383/384 cases in TalkNpcAction and indented the switch properly. - IllusionTempleTeam moved to the MiniGames namespace, matching its folder. - Removed the whitespace noise in MiniGameContext and Player. - GameContextTestHelper takes the maximum level as a parameter instead of changing it globally for every test. - Deduplicated the relic's group/number checks behind IsRelicDefinition. - The update plugin removes the obsolete 658-668 statue spawns, so existing databases don't keep the placeholders next to the new ones. - FinishesWhenTooFewPlayersRemainAsync asserts that the match actually ends, not just the precondition. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Character class: The result screen showed the same wrong class for every player. A packet capture confirmed the server sends correct, distinct values, so the client decodes them differently than assumed: it reads the class line from the lower nibble (0 Dark Wizard, 1 Dark Knight, 2 Fairy Elf, 3 Magic Gladiator, 4 Dark Lord, 5 Summoner, 6 Rage Fighter) and the evolution step from the upper one, while the internal numbering packs both the other way around. Two players of different lines therefore collapsed onto the same entry whenever the sent values happened to share their lower nibble - both were shown as the Magic Gladiator line's master class. The conversion is inverted accordingly and verified on a live client. Uninitialized packet bytes: Each player entry carries three alignment bytes which are never written, and the pipe buffer isn't zeroed, so leftovers of previous packets went out on the wire. The buffer is cleared before writing now. Entry dialog couldn't be opened a second time: The player state is only reset back to EnteredWorld after the dialog has been shown, so a failure while querying the temple user counts left the player stuck in NpcDialogOpened - and opening the dialog requires exactly that transition. The reset moved into a finally block. The user count view plugin also missed the connection check every other view plugin has, which is one way that query could throw. Statue could stay locked for the rest of the match: Claiming the carrier slot before looking up the relic item meant an exception (e.g. a database without the item) left the slot claimed forever, and nobody could pick up the relic anymore. The claim is now released on every failure path, and a missing item definition is logged instead of thrown. Also reverted the WaitingRoom event state which was sent on entry: it was added on the assumption that the unused enum value belongs there, without evidence from the reference server, and it sent a new packet to the client before the player was even on the event map. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ashi/OpenMU into feature/illusion-temple-mini-game
Illusion Temple mini-game event
Implements the full Illusion Temple event: a team-based PvP mini game where two teams (Allied Forces / Illusion Forces) fight over a sacred relic held by a randomly-spawning Stone Statue.
What's included
IllusionTempleState/IllusionTempleResult(missing field, wrong offset, and a padding gap that the original client's C struct expects) — new packets for event state, holy-relic notification, skill usage/points, and reward requests.Known issues