Skip to content

Feat: More Game Events#437

Merged
AngeloTadeucci merged 5 commits intomasterfrom
events
May 3, 2025
Merged

Feat: More Game Events#437
AngeloTadeucci merged 5 commits intomasterfrom
events

Conversation

@Zintixx
Copy link
Collaborator

@Zintixx Zintixx commented May 1, 2025

  • Implements Bingo event
  • Partial implementation of TimeRunEvent
  • Fixed Mirror NPC in Beauty Salon

Summary by CodeRabbit

  • New Features

    • Introduced support for new in-game events: Bingo and TimeRun, including event data handling, serialization, and reward claiming.
    • Added new event types for Maple Survival (opening/closure) and On Time popups.
    • Implemented Bingo board interactions: opening the board, crossing off numbers, and claiming rewards.
    • Added support for using Quest Scroll items to start quests directly from inventory.
  • Enhancements

    • Improved event reward handling with new commands for TimeRun and Bingo events.
    • Updated NPC script interactions to ensure dialog and movement packets are sent in the correct order.
    • Refined user value management with controlled value setting methods.
  • Other Changes

    • Extended enums and data models to support new event types and user values.
    • Added new packet commands and constructions for event rewards and item use.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented May 1, 2025

Walkthrough

This update introduces new event types and related logic for handling Bingo and TimeRun events across the server, model, and packet layers. The event data model is extended with new record types for Bingo, TimeRun, MapleSurvivalOpenPeriod, ShutdownMapleSurvival, and Ontime events. Corresponding parsing and serialization logic is implemented in the server and model layers. The server now processes new event reward commands for Bingo and TimeRun, including user interactions such as opening a bingo board, crossing off numbers, and claiming rewards. Additionally, item use handling is enhanced with support for quest scrolls, and related packet construction methods are added.

Changes

File(s) Change Summary
Maple2.File.Ingest/Mapper/ServerTableMapper.cs Extended ParseGameEventData to handle new event types: BingoEvent, TimeRunEvent, MapleSurvivalOpenPeriod, and ShutdownMapleSurvival, including XML parsing and object construction for each.
Maple2.Model/Enum/GameEventUserValueType.cs Added new enum members: BingoUid, BingoRewardsClaimed, and BingoNumbersChecked (values 4000–4002) for Bingo event tracking.
Maple2.Model/Game/Event/GameEvent.cs Updated WriteTo method to serialize new event types: BingoEvent and TimeRunEvent. Removed unused using directives.
Maple2.Model/Metadata/ServerTable/GameEventTable.cs Introduced new public record types: BingoEvent, TimeRunEvent, MapleSurvivalOpenPeriod, ShutdownMapleSurvival, and Ontime as part of the GameEventData hierarchy, with JSON polymorphic registration.
Maple2.Server.Game/Manager/NpcScriptManager.cs Changed the order in which NPC script-related packets are sent, ensuring all NpcTalkPacket packets are sent before any portal movement packets.
Maple2.Server.Game/PacketHandlers/EventRewardHandler.cs Added support for new event reward commands: TimeRun step/final rewards and Bingo event (open board, cross off number, claim reward). Implemented corresponding private handler methods.
Maple2.Server.Game/PacketHandlers/ItemUseHandler.cs Added handling for ItemFunction.QuestScroll, including XML parsing for quest IDs and starting quests when a quest scroll is used.
Maple2.Server.Game/Packets/EventRewardPacket.cs Extended packet commands and methods for new event reward types, including Bingo and TimeRun events. Added serialization for bingo board state, reward claims, and step/final rewards.
Maple2.Server.Game/Packets/ItemUsePacket.cs Added a new command and method for quest scroll item use packets.
Maple2.Database/Model/GameEventUserValue.cs Modified implicit conversion operator to use constructor with value parameter instead of property assignment.
Maple2.Model/Game/User/GameEventUserValue.cs Changed Value from public field to property with private setter; replaced parameterless constructor with constructor accepting optional string; added SetValue method for controlled modification.
Maple2.Server.Game/Manager/GameEventManager.cs Updated code to use SetValue method instead of direct assignment to Value property on GameEventUserValue instances; adjusted exception messages slightly.
Maple2.Server.Game/Packets/GameEventUserValuePacket.cs Added using directive Maple2.Database.Extensions; (no functional changes).

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Server
    participant EventRewardHandler
    participant Inventory
    participant Mailbox

    %% Bingo: Claim Reward
    Client->>Server: RequestClaimBingoReward(rewardIndex)
    Server->>EventRewardHandler: HandleClaimBingoReward
    EventRewardHandler->>Inventory: TryAddRewardItem
    alt Inventory full
        EventRewardHandler->>Mailbox: MailRewardItem
    end
    EventRewardHandler-->>Client: ClaimBingoRewardPacket
    EventRewardHandler-->>Client: UpdateBingoPacket
Loading
sequenceDiagram
    participant Client
    participant Server
    participant ItemUseHandler
    participant Inventory

    %% Quest Scroll Usage
    Client->>Server: UseItem(QuestScroll)
    Server->>ItemUseHandler: HandleQuestScroll
    ItemUseHandler->>Inventory: ConsumeItem(QuestScroll)
    ItemUseHandler-->>Client: QuestScrollUsedPacket
Loading

Suggested reviewers

  • AngeloTadeucci

Poem

In the meadow of code, new events now bloom,
Bingo and TimeRun, dispelling old gloom.
Scrolls start adventures, rewards now abound,
With packets and records all neatly wound.
Rabbits hop with joy, for features anew—
May your inventory always have room for you!
🐇✨


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5615146 and 41afabe.

📒 Files selected for processing (13)
  • Maple2.Database/Model/GameEventUserValue.cs (1 hunks)
  • Maple2.File.Ingest/Mapper/ServerTableMapper.cs (1 hunks)
  • Maple2.Model/Enum/GameEventUserValueType.cs (1 hunks)
  • Maple2.Model/Game/Event/GameEvent.cs (2 hunks)
  • Maple2.Model/Game/User/GameEventUserValue.cs (2 hunks)
  • Maple2.Model/Metadata/ServerTable/GameEventTable.cs (2 hunks)
  • Maple2.Server.Game/Manager/GameEventManager.cs (2 hunks)
  • Maple2.Server.Game/Manager/NpcScriptManager.cs (1 hunks)
  • Maple2.Server.Game/PacketHandlers/EventRewardHandler.cs (2 hunks)
  • Maple2.Server.Game/PacketHandlers/ItemUseHandler.cs (2 hunks)
  • Maple2.Server.Game/Packets/EventRewardPacket.cs (2 hunks)
  • Maple2.Server.Game/Packets/GameEventUserValuePacket.cs (1 hunks)
  • Maple2.Server.Game/Packets/ItemUsePacket.cs (2 hunks)
✅ Files skipped from review due to trivial changes (1)
  • Maple2.Server.Game/Packets/GameEventUserValuePacket.cs
🚧 Files skipped from review as they are similar to previous changes (12)
  • Maple2.Database/Model/GameEventUserValue.cs
  • Maple2.Server.Game/Packets/ItemUsePacket.cs
  • Maple2.Model/Enum/GameEventUserValueType.cs
  • Maple2.Server.Game/Manager/NpcScriptManager.cs
  • Maple2.Server.Game/Manager/GameEventManager.cs
  • Maple2.Model/Game/User/GameEventUserValue.cs
  • Maple2.Server.Game/PacketHandlers/ItemUseHandler.cs
  • Maple2.Server.Game/Packets/EventRewardPacket.cs
  • Maple2.Model/Game/Event/GameEvent.cs
  • Maple2.Server.Game/PacketHandlers/EventRewardHandler.cs
  • Maple2.Model/Metadata/ServerTable/GameEventTable.cs
  • Maple2.File.Ingest/Mapper/ServerTableMapper.cs
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: build
  • GitHub Check: build
✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 8

🧹 Nitpick comments (7)
Maple2.Model/Enum/GameEventUserValueType.cs (1)

32-35: Address the TODO and consider documenting enum values

The added Bingo event types include a TODO comment indicating these are placeholder values. Before finalizing, determine the actual confirmed values. Additionally, consider adding documentation comments to explain what each enum value represents and how they're used in the Bingo event system.

-    // Bingo - TODO: These are not the actual confirmed values. Just using it as a way to store this data for now.
+    // Bingo event tracking - Used to store user-specific bingo state
     BingoUid = 4000,                // Unique identifier for the player's bingo card
     BingoRewardsClaimed = 4001,     // Bitmap of rewards that have been claimed
     BingoNumbersChecked = 4002,     // Bitmap of numbers that have been checked off
Maple2.Server.Game/Packets/EventRewardPacket.cs (2)

20-48: Missing count for reward items

Unknown1, ClaimTimeRunFinalReward, and ClaimTimeRunStepReward only write itemId and rarity, omitting count.
Other reward-related packets (e.g., Gallery) include count when applicable. Verify the client expects a quantity; if so, extend the payload:

-    pWriter.WriteInt(item.ItemId);
-    pWriter.WriteShort(item.Rarity);
+    pWriter.WriteInt(item.ItemId);
+    pWriter.WriteShort(item.Rarity);
+    pWriter.WriteInt(item.Amount);

70-91: Validate string length vs. protocol limits

checkedNumbers and rewardsClaimed can grow over time. If the protocol caps these fields (commonly 255 or 1024 bytes), enforce a length check or truncate to prevent malformed packets and potential disconnects.

Maple2.File.Ingest/Mapper/ServerTableMapper.cs (1)

1216-1269: TimeRunEvent: step-reward metadata ignored

Step rewards are initialised with an empty dictionary:

StepRewards: new Dictionary<int, RewardItem>(), // No step rewards were added …

If step-reward information later appears in the XML (likely value4), this implementation will silently drop it, forcing a server patch.
Consider parsing value4 now (or at least logging a TODO) to future-proof the mapper.

Maple2.Server.Game/PacketHandlers/EventRewardHandler.cs (1)

32-54: Missing default case logs / safeguard

The new switch arms look good, but the switch still lacks a default: branch.
Without it, unsupported command IDs silently do nothing, which is painful when debugging malformed packets.

+        default:
+            Logger.Warn($"Unhandled EventReward command: {(byte)command}");
+            return;
Maple2.Model/Metadata/ServerTable/GameEventTable.cs (2)

165-182: Consider validating Bingo array dimensions

int[][] Numbers leaves the board size implicit.
Downstream code will need to assume a fixed 5×5 grid (or similar). If any inner array is shorter, runtime errors ensue. Recommend adding a validation helper or switching to IReadOnlyList<int[/*5*/]>.


241-245: Attribute list is growing—watch the polymorphism limit

System.Text.Json currently allows up to 64 derived types; you are at 29+. Keep track to avoid hitting the runtime limit in future expansions.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d7acbb0 and e66ac15.

📒 Files selected for processing (9)
  • Maple2.File.Ingest/Mapper/ServerTableMapper.cs (1 hunks)
  • Maple2.Model/Enum/GameEventUserValueType.cs (1 hunks)
  • Maple2.Model/Game/Event/GameEvent.cs (2 hunks)
  • Maple2.Model/Metadata/ServerTable/GameEventTable.cs (2 hunks)
  • Maple2.Server.Game/Manager/NpcScriptManager.cs (1 hunks)
  • Maple2.Server.Game/PacketHandlers/EventRewardHandler.cs (2 hunks)
  • Maple2.Server.Game/PacketHandlers/ItemUseHandler.cs (2 hunks)
  • Maple2.Server.Game/Packets/EventRewardPacket.cs (2 hunks)
  • Maple2.Server.Game/Packets/ItemUsePacket.cs (2 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
Maple2.Model/Metadata/ServerTable/GameEventTable.cs (1)
Maple2.File.Ingest/Mapper/ServerTableMapper.cs (1)
  • GameEventData (829-1277)
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: build
  • GitHub Check: build
  • GitHub Check: format
🔇 Additional comments (10)
Maple2.Server.Game/Packets/ItemUsePacket.cs (2)

13-14: LGTM! The QuestScroll command is properly added to the Command enum.

The new command follows the existing pattern and numbering scheme.


53-58: LGTM! The QuestScroll packet creation method is well-implemented.

The implementation correctly follows the established pattern of other packet creation methods in this class.

Maple2.Server.Game/PacketHandlers/ItemUseHandler.cs (1)

123-125: LGTM! QuestScroll case properly added to the item function switch statement.

The case is appropriately placed in the switch statement and correctly calls the new handler method.

Maple2.Server.Game/Manager/NpcScriptManager.cs (3)

369-373: LGTM! Improved packet ordering for NPC interactions

The added comment clearly explains the reasoning for the packet ordering, and the code correctly sends the movement packet first.


374-376: LGTM! Moved OpenDialog packet to a more appropriate position

The OpenDialog packet is now sent after the MovePlayer packet but before any portal movement, which aligns with the requirement that NpcTalk packets be sent first.


382-384: LGTM! Portal existence check improves reliability

The conditional sending of the MoveByPortal packet now correctly checks if the portal exists in the current field, preventing potential errors.

Maple2.Model/Game/Event/GameEvent.cs (1)

207-218: Potentially missing Bingo board numbers in serialization

BingoEvent currently serializes reward data and pencil item IDs, but it never writes the Numbers grid that was parsed into BingoEvent.Numbers.
If clients reconstruct the board from the packet, omitting this information will prevent them from rendering the initial state.

Please double-check the protocol spec: if the board’s numbers are expected on the wire, they must be written here (most likely as a 2-D array preceded by row/col counts).

Maple2.Server.Game/Packets/EventRewardPacket.cs (1)

10-18: Enum values may collide with future additions

New enum members are inserted with hard-coded byte values (1, 3, 4, 20, 21, 23).
Please confirm these IDs are unique in the full protocol enum (including values defined elsewhere).
Consider adding a comment that references the official opcode table to avoid accidental reuse.

Maple2.Server.Game/PacketHandlers/EventRewardHandler.cs (1)

19-27: Verify enum values do not collide with legacy commands

ClaimTimeRunFinalReward = 3 and ClaimTimeRunStepReward = 4 are inserted into the existing enum.
Please double-check that values 3 and 4 are unused by earlier client builds; if they overlap, packets will be mis-decoded.

Would you run a quick grep on EventRewardPacket (and the client opcode table, if available) to confirm that 0x03 and 0x04 were previously unassigned?

Maple2.Model/Metadata/ServerTable/GameEventTable.cs (1)

183-200: IReadOnlyDictionary<int, RewardItem> serialises with string keys

System.Text.Json writes dictionary keys as strings; on deserialisation they must match exactly. If any tooling emits "1" instead of 1, you’re fine, but be aware when editing data by hand.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (4)
Maple2.Server.Game/PacketHandlers/EventRewardHandler.cs (4)

80-98: ⚠️ Potential issue

TimeRun final reward can be claimed multiple times

The final reward implementation doesn't prevent a player from claiming the reward repeatedly.

Apply this diff to prevent multiple claims:

private void HandleClaimTimeRunFinalReward(GameSession session) {
    GameEvent? gameEvent = session.FindEvent(GameEventType.TimeRunEvent).FirstOrDefault();
    if (gameEvent?.Metadata.Data is not TimeRunEvent timeRunEvent) {
        return;
    }

+   // Check if final reward was already claimed
+   GameEventUserValue claimed = session.GameEvent.Get(
+       GameEventUserValueType.TimeRunFinalRewardClaimed, gameEvent.Id, gameEvent.EndTime);
+   if (claimed.Int() > 0) {
+       return; // Already claimed
+   }

    RewardItem rewardItem = timeRunEvent.FinalReward;

    Item? item = session.Field.ItemDrop.CreateItem(rewardItem.ItemId, rewardItem.Rarity, rewardItem.Amount);
    if (item == null) {
        return;
    }

    if (!session.Item.Inventory.Add(item, true)) {
        session.Item.MailItem(item);
    }

    session.Send(EventRewardPacket.ClaimTimeRunFinalReward(rewardItem));
+   
+   // Record that the final reward has been claimed
+   session.GameEvent.Set(gameEvent.Id, GameEventUserValueType.TimeRunFinalRewardClaimed, 1);
}

Note: You'll need to add TimeRunFinalRewardClaimed to the GameEventUserValueType enum.


200-240: ⚠️ Potential issue

Bingo number crossing issues

There are several issues with this implementation:

  1. Line 222: Whitespace formatting error as indicated by pipeline failures
  2. Line 235: checkedNumbers.Value is used directly after adding to the list but before updating the string representation
  3. No concurrency protection when updating the checkedNumbers

Apply these fixes:

private void HandleCrossOffBingoNumber(GameSession session, IByteReader packet) {
    GameEvent? gameEvent = session.FindEvent(GameEventType.BingoEvent).FirstOrDefault();
    if (gameEvent?.Metadata.Data is not BingoEvent bingoEvent) {
        return;
    }

    int itemId = packet.ReadInt();
    int bingoNumber = packet.ReadInt();

    // Check if the item is a pencil
    if (itemId != bingoEvent.PencilItemId && itemId != bingoEvent.PencilPlusItemId) {
        return;
    }

    GameEventUserValue checkedNumbers = session.GameEvent.Get(GameEventUserValueType.BingoNumbersChecked, gameEvent.Id, gameEvent.EndTime);
    List<int> checkedNumbersList = string.IsNullOrEmpty(checkedNumbers.Value) ? [] : checkedNumbers.Value.Split(',').Select(int.Parse).ToList();
    if (checkedNumbersList.Contains(bingoNumber)) {
        return;
    }

    if (itemId == bingoEvent.PencilItemId) {
        DateTime startTime = gameEvent.StartTime.FromEpochSeconds();
-       int days = Math.Max(0, (int)(DateTime.UtcNow - startTime).TotalDays);
+       int days = Math.Max(0, (int) (DateTime.UtcNow - startTime).TotalDays);
        if (!bingoEvent.Numbers[days].Contains(bingoNumber)) {
            return;
        }
    }

    Item? item = session.Item.Inventory.Find(itemId).FirstOrDefault();
    if (item == null) {
        return;
    }

+   // Lock to prevent concurrent modifications
+   lock (session) {
        checkedNumbersList.Add(bingoNumber);
        session.Item.Inventory.Consume(item.Uid, 1);
-       session.GameEvent.Set(gameEvent.Id, GameEventUserValueType.BingoNumbersChecked, checkedNumbers.Value);
+       session.GameEvent.Set(gameEvent.Id, GameEventUserValueType.BingoNumbersChecked, string.Join(',', checkedNumbersList));
+   }

    GameEventUserValue rewardsClaimed = session.GameEvent.Get(GameEventUserValueType.BingoRewardsClaimed, gameEvent.Id, gameEvent.EndTime);

    session.Send(EventRewardPacket.UpdateBingo(checkedNumbers.Value, rewardsClaimed.Value));
}
🧰 Tools
🪛 GitHub Actions: Format

[error] 222-222: Whitespace formatting error: Fix whitespace formatting. Insert '\s'.


242-275: ⚠️ Potential issue

Bingo reward claim lacks validation

The implementation doesn't verify that the player has actually completed the required number of bingo lines before claiming rewards.

Apply these changes to add validation and simplify response packets:

private void HandleClaimBingoReward(GameSession session, IByteReader packet) {
    int index = packet.ReadInt();
    GameEvent? gameEvent = session.FindEvent(GameEventType.BingoEvent).FirstOrDefault();
    if (gameEvent?.Metadata.Data is not BingoEvent bingoEvent) {
        return;
    }

    if (index >= bingoEvent.Rewards.Length) {
        return;
    }

    GameEventUserValue rewardsClaimed = session.GameEvent.Get(GameEventUserValueType.BingoRewardsClaimed, gameEvent.Id, gameEvent.EndTime);
    List<int> rewardsClaimedList = string.IsNullOrEmpty(rewardsClaimed.Value) ? [] : rewardsClaimed.Value.Split(',').Select(int.Parse).ToList();
    if (rewardsClaimedList.Contains(index)) {
        return;
    }
+
+   // Validate that the player has completed enough bingo lines
+   GameEventUserValue checkedNumbers = session.GameEvent.Get(GameEventUserValueType.BingoNumbersChecked, gameEvent.Id, gameEvent.EndTime);
+   List<int> checkedNumbersList = string.IsNullOrEmpty(checkedNumbers.Value) ? [] : checkedNumbers.Value.Split(',').Select(int.Parse).ToList();
+   
+   // TODO: Add validation logic here to verify the player has completed at least 'index+1' bingo lines
+   // int completedLines = CalculateCompletedBingoLines(checkedNumbersList);
+   // if (completedLines < index + 1) {
+   //     return; // Not enough lines completed for this reward
+   // }

    rewardsClaimedList.Add(index);
    session.GameEvent.Set(gameEvent.Id, GameEventUserValueType.BingoRewardsClaimed, string.Join(',', rewardsClaimedList));

-   GameEventUserValue checkedNumbers = session.GameEvent.Get(GameEventUserValueType.BingoNumbersChecked, gameEvent.Id, gameEvent.EndTime);

    foreach (RewardItem rewardItem in bingoEvent.Rewards[index].Items) {
        Item? item = session.Field.ItemDrop.CreateItem(rewardItem.ItemId, rewardItem.Rarity, rewardItem.Amount);
        if (item == null) {
            continue;
        }

        if (!session.Item.Inventory.Add(item, true)) {
            session.Item.MailItem(item);
        }
    }
-   session.Send(EventRewardPacket.ClaimBingoReward(bingoEvent.Rewards[index].Items));
-   session.Send(EventRewardPacket.UpdateBingo(checkedNumbers.Value, rewardsClaimed.Value));
+   
+   // Send a combined update to reduce packet overhead
+   session.Send(EventRewardPacket.ClaimBingoReward(bingoEvent.Rewards[index].Items));
+   session.Send(EventRewardPacket.UpdateBingo(checkedNumbers.Value, string.Join(',', rewardsClaimedList)));
}

You need to implement a CalculateCompletedBingoLines function that counts how many bingo lines (horizontal, vertical, diagonal) the player has completed based on their checked numbers.


56-78: ⚠️ Potential issue

TimeRun step reward lacks validation and persistence

The implementation has two critical issues:

  1. There's no validation that the player has actually traveled the specified distance
  2. There's no mechanism to prevent claiming the same step reward multiple times

Apply this diff to fix the issues:

private void HandleClaimTimeRunStepReward(GameSession session, IByteReader packet) {
    int steps = packet.ReadInt();

    GameEvent? gameEvent = session.FindEvent(GameEventType.TimeRunEvent).FirstOrDefault();
    if (gameEvent?.Metadata.Data is not TimeRunEvent timeRunEvent) {
        return;
    }

+   // Check if this step reward was already claimed
+   GameEventUserValue claimed = session.GameEvent.Get(
+       GameEventUserValueType.TimeRunStepRewardsClaimed, gameEvent.Id, gameEvent.EndTime);
+   List<int> claimedSteps = string.IsNullOrEmpty(claimed.Value) 
+       ? [] 
+       : claimed.Value.Split(',').Select(int.Parse).ToList();
+   
+   if (claimedSteps.Contains(steps)) {
+       return; // Already claimed this step reward
+   }

    if (!timeRunEvent.StepRewards.TryGetValue(steps, out RewardItem rewardItem)) {
        return;
    }

+   // TODO: Add validation that player has actually traveled this distance
+   // if (!session.TimeRun.HasReachedDistance(steps)) {
+   //     return;
+   // }

    Item? item = session.Field.ItemDrop.CreateItem(rewardItem.ItemId, rewardItem.Rarity, rewardItem.Amount);
    if (item == null) {
        return;
    }

    if (!session.Item.Inventory.Add(item, true)) {
        session.Item.MailItem(item);
    }

+   // Record that this step reward has been claimed
+   claimedSteps.Add(steps);
+   session.GameEvent.Set(gameEvent.Id,
+       GameEventUserValueType.TimeRunStepRewardsClaimed,
+       string.Join(",", claimedSteps));

    session.Send(EventRewardPacket.ClaimTimeRunStepReward(rewardItem));
}

Note: You'll need to add TimeRunStepRewardsClaimed to the GameEventUserValueType enum.

🧹 Nitpick comments (2)
Maple2.Server.Game/PacketHandlers/EventRewardHandler.cs (2)

182-182: Fix whitespace formatting error as reported by pipeline

-        int days = Math.Max(0, (int)(DateTime.UtcNow - startTime).TotalDays);
+        int days = Math.Max(0, (int) (DateTime.UtcNow - startTime).TotalDays);
🧰 Tools
🪛 GitHub Actions: Format

[error] 182-182: Whitespace formatting error: Fix whitespace formatting. Insert '\s'.


222-222: Fix whitespace formatting error as reported by pipeline

-            int days = Math.Max(0, (int)(DateTime.UtcNow - startTime).TotalDays);
+            int days = Math.Max(0, (int) (DateTime.UtcNow - startTime).TotalDays);
🧰 Tools
🪛 GitHub Actions: Format

[error] 222-222: Whitespace formatting error: Fix whitespace formatting. Insert '\s'.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e66ac15 and 5615146.

📒 Files selected for processing (6)
  • Maple2.Database/Model/GameEventUserValue.cs (1 hunks)
  • Maple2.Model/Game/User/GameEventUserValue.cs (2 hunks)
  • Maple2.Server.Game/Manager/GameEventManager.cs (2 hunks)
  • Maple2.Server.Game/PacketHandlers/EventRewardHandler.cs (2 hunks)
  • Maple2.Server.Game/PacketHandlers/ItemUseHandler.cs (2 hunks)
  • Maple2.Server.Game/Packets/GameEventUserValuePacket.cs (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • Maple2.Server.Game/PacketHandlers/ItemUseHandler.cs
🧰 Additional context used
🧬 Code Graph Analysis (1)
Maple2.Server.Game/Manager/GameEventManager.cs (1)
Maple2.Model/Game/User/GameEventUserValue.cs (2)
  • SetValue (24-26)
  • Long (30-30)
🪛 GitHub Actions: Format
Maple2.Server.Game/PacketHandlers/EventRewardHandler.cs

[error] 182-182: Whitespace formatting error: Fix whitespace formatting. Insert '\s'.


[error] 222-222: Whitespace formatting error: Fix whitespace formatting. Insert '\s'.

⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: build
  • GitHub Check: build
🔇 Additional comments (6)
Maple2.Database/Model/GameEventUserValue.cs (1)

27-27: Good refactoring to match constructor usage

This change properly adapts to the updated GameEventUserValue class in the Model layer, which now controls the Value property through a constructor rather than direct property assignment.

Maple2.Server.Game/Manager/GameEventManager.cs (2)

133-134: Good adaptation to the new SetValue method

This change correctly uses the new SetValue method instead of direct property assignment, respecting the encapsulation introduced in the GameEventUserValue class.


166-166: Good adaptation to the new SetValue method

This change correctly uses the new SetValue method instead of direct property assignment, maintaining consistency with the encapsulation pattern.

Maple2.Model/Game/User/GameEventUserValue.cs (3)

9-9: Good encapsulation of the Value property

Converting the public field to a property with a private setter improves encapsulation and helps prevent unexpected modifications.


13-15: Good constructor refactoring

This constructor properly initializes the Value property and provides a simpler way to create instances with an initial value.


24-26: Good addition of the SetValue method

The SetValue method provides controlled access to modify the Value property, maintaining encapsulation while allowing necessary updates.

@AngeloTadeucci AngeloTadeucci merged commit 0d70af2 into master May 3, 2025
4 checks passed
@Zintixx Zintixx deleted the events branch May 4, 2025 17:59
@coderabbitai coderabbitai bot mentioned this pull request Feb 18, 2026
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

Comments