Skip to content

Plot expiration & misc home fixes - #525

Merged
AngeloTadeucci merged 4 commits into
masterfrom
plot-expiration
Jul 5, 2025
Merged

Plot expiration & misc home fixes#525
AngeloTadeucci merged 4 commits into
masterfrom
plot-expiration

Conversation

@AngeloTadeucci

@AngeloTadeucci AngeloTadeucci commented Jul 5, 2025

Copy link
Copy Markdown
Collaborator
  • Reworked how cubes are saved on db
  • Outdoor plots now broadcast the state to all channels and includes:
    • Expiration
    • Changing name
    • Adding, removing, replacing and rotating blocks
  • Fixed going and leaving planner/blueprint planner
  • Fixed applying layouts with items not available to be bought
  • Fixed changing channels

Summary by CodeRabbit

  • New Features

    • Added support for updating and synchronizing field plots and cubes across channels and the world, including new gRPC endpoints and protobuf messages.
    • Introduced plot lifecycle management with automatic plot expiry, forfeiture, and cube return to owners.
    • Enhanced debug commands for resetting homes and reloading plots.
    • Added cube rotation functionality and improved cube placement/removal operations.
    • Added retrieval and management methods for outdoor plots and cubes, including detailed plot info and cube CRUD operations.
    • Introduced session retrieval by account ID and channel tracking in sessions.
    • Added plot update broadcasting with detailed block-level operations.
  • Bug Fixes

    • Improved error handling and consistency for cube placement, removal, and plot updates.
    • Fixed serialization and packet issues related to plot names and states.
    • Added null checks and early returns to prevent errors in cube request handlers.
    • Corrected plot state evaluation logic for expiry and ownership.
  • Refactor

    • Streamlined initialization and management of user home data and cube collections.
    • Refactored database interactions for plot and cube management to improve reliability and maintainability.
    • Simplified item and furnishing management methods, including renaming and signature changes.
    • Improved layout request error handling to log warnings instead of aborting.
    • Reorganized cube placement logic to use centralized create/delete helpers with database persistence.
    • Updated field manager and home field manager to handle room ID and cube clearing logic.
    • Added channel tracking property to sessions and refined migration logic.
  • Chores

    • Removed unused code and updated namespaces for clarity.
    • Improved inline documentation and code comments for better maintainability.
    • Cleaned up using directives and adjusted class inheritance for packet handlers.

@coderabbitai

coderabbitai Bot commented Jul 5, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

This update implements extensive enhancements to plot and cube management for the game's housing and field systems. It introduces new gRPC methods and protobuf messages for plot updates, adds robust plot and cube lifecycle management in both server and database layers, refines packet serialization, and improves error handling, synchronization, and data consistency across services.

Changes

Files/Groups Change Summary
Maple2.Database/Storage/Game/GameStorage.Map.cs Adds/updates methods for plot and cube CRUD, plot state transitions, and conversion improvements in GameStorage.Request.
Maple2.Database/Storage/Game/GameStorage.Nurturing.cs Changes parameter from characterId to accountId in GetNurturing.
Maple2.Model/Game/Cube/PlotCube.cs Adds Rotate(bool clockwise) method and clarifies ID usage.
Maple2.Model/Game/User/Home.cs Moves field initializations to declarations, removes constructor.
Maple2.Model/Game/User/PlotInfo.cs Updates PlotInfo.State logic, Plot.SetPlannerMode, and adds Plot.ToString().
Maple2.Model/Metadata/Constants.cs Changes UgcHomeSaleWaitingTime from int to TimeSpan.
Maple2.Server.Core/proto/channel.proto,
Maple2.Server.Core/proto/world/world.proto
Adds UpdateFieldPlot RPC to Channel and World services.
Maple2.Server.Core/proto/common.proto Adds FieldPlotRequest/FieldPlotResponse messages and nested update messages.
Maple2.Server.Game/Commands/DebugCommand.cs Adds ResetHomeCommand and ReloadPlotsCommand, updates constructor.
Maple2.Server.Game/GameServer.cs Adds GetSessionByAccountId method.
Maple2.Server.Game/Manager/Field/FieldManager/FieldManager.Factory.cs,
Maple2.Server.Game/Manager/Field/FieldManager/HomeFieldManager.cs
Adds roomId to HomeFieldManager constructor and logic for clearing cubes.
Maple2.Server.Game/Manager/Field/FieldManager/FieldManager.State.cs Broadcasts function cube addition after adding interact.
Maple2.Server.Game/Manager/Field/FieldManager/FieldManager.Ugc.cs Refactors PlaceCube, adds UpdateAllPlots, changes world update logic.
Maple2.Server.Game/Manager/HousingManager.cs Refactors cube placement/removal, adds CreateCube/DeleteCube, improves plot update sync and error handling.
Maple2.Server.Game/Manager/Items/FurnishingManager.cs Renames TryPlaceCube to TryAddCube, changes signature and logic.
Maple2.Server.Game/Manager/Items/ItemManager.cs Makes Furnishing a property, adds ReInstantiateFurnishing.
Maple2.Server.Game/PacketHandlers/ChannelHandler.cs,
Maple2.Server.Game/PacketHandlers/TimeSyncHandler.cs
Changes base class/namespace for ChannelHandler and TimeSyncHandler.
Maple2.Server.Game/PacketHandlers/DungeonRoomHandler.cs,
Maple2.Tools/Collision/IPolygon.cs,
Maple2.Tools/Extensions/PacketExtensions.cs
Removes unused using directives.
Maple2.Server.Game/PacketHandlers/LoadUgcMapHandler.cs Adds comment, changes plot filtering logic for packets.
Maple2.Server.Game/PacketHandlers/RequestCubeHandler.cs Adds session.Field null checks, refactors cube op logic, adds world update notifications.
Maple2.Server.Game/Packets/LoadCubesPacket.cs Changes PlotState serialization, writes plot Name, updates PlotExpiry serialization.
Maple2.Server.Game/Service/ChannelService.UpdateFieldPlot.cs Adds ChannelService.UpdateFieldPlot method for handling plot/block updates via gRPC.
Maple2.Server.Game/Service/ChannelService.cs Adds ItemMetadataStorage parameter to constructor.
Maple2.Server.Game/Session/GameSession.cs Updates MigrateToPlanner RoomId setting based on PlotMode.
Maple2.Server.World/Service/WorldService.UpdateFieldPlot.cs Adds WorldService.UpdateFieldPlot gRPC method for plot updates and expiry checks.
Maple2.Server.World/WorldServer.cs Adds FieldPlotExpiryCheck scheduled task, plot state transitions, and cube return logic; updates constructor.

Sequence Diagram(s)

Plot Update Request Flow (High-Level)

sequenceDiagram
    participant Client
    participant GameServer
    participant ChannelService
    participant WorldService
    participant Database

    Client->>GameServer: Request plot/cube update
    GameServer->>ChannelService: UpdateFieldPlot(FieldPlotRequest)
    ChannelService->>WorldService: UpdateFieldPlot(FieldPlotRequest)
    WorldService->>ChannelService: Broadcast UpdateFieldPlot to channels
    ChannelService->>Database: Update plot/cube data
    ChannelService->>GameServer: Broadcast updated plot/cube info
    GameServer->>Client: Send update confirmation/packets
Loading

Field Plot Expiry Check (WorldServer)

sequenceDiagram
    participant WorldServer
    participant Database
    participant ChannelClients

    WorldServer->>Database: Get expired plots
    Database-->>WorldServer: List of expired plots
    WorldServer->>Database: Set plot pending/open, delete cubes as needed
    WorldServer->>ChannelClients: UpdateFieldPlot (notify plot state changes)
    WorldServer->>WorldServer: Schedule next expiry check
Loading

Possibly related PRs

  • AngeloTadeucci/Maple2#301: Refactors and enhances cube creation and management in HousingManager.InitNewHome, closely related to error handling and metadata checks during home initialization.
  • AngeloTadeucci/Maple2#421: Modifies cube placement and storage logic in HousingManager and FurnishingManager, directly connected to changes in how cubes are added to furnishing storage.
  • AngeloTadeucci/Maple2#367: Adds cube lifecycle management and plot cube handling improvements in GameStorage.Request, related to cube data model and lifecycle enhancements.

Suggested reviewers

  • Zintixx

Poem

In fields and plots where cubes reside,
The rabbits code, with ears held wide.
Plots expire, cubes rotate,
Updates flow between each state.
Packets fly, the world aligns,
Hop, hop, hooray—see how it shines!
🐇✨


📜 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 98f6919 and 376e67e.

📒 Files selected for processing (4)
  • Maple2.Server.Game/Manager/Field/FieldManager/FieldManager.Ugc.cs (3 hunks)
  • Maple2.Server.Game/Manager/HousingManager.cs (14 hunks)
  • Maple2.Server.Game/PacketHandlers/RequestCubeHandler.cs (6 hunks)
  • Maple2.Server.Game/Session/GameSession.cs (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • Maple2.Server.Game/Session/GameSession.cs
  • Maple2.Server.Game/PacketHandlers/RequestCubeHandler.cs
  • Maple2.Server.Game/Manager/Field/FieldManager/FieldManager.Ugc.cs
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: AngeloTadeucci
PR: AngeloTadeucci/Maple2#236
File: Maple2.Server.Game/PacketHandlers/RequestCubeHandler.cs:375-422
Timestamp: 2024-09-16T07:50:30.281Z
Learning: In `RequestCubeHandler`, the `HandleLoadLayout` method is responsible for applying the layout to the plot after missing cubes are calculated in `HandleRequestLayout`.
Maple2.Server.Game/Manager/HousingManager.cs (2)
Learnt from: AngeloTadeucci
PR: AngeloTadeucci/Maple2#236
File: Maple2.Server.Game/PacketHandlers/RequestCubeHandler.cs:375-422
Timestamp: 2024-09-16T07:50:30.281Z
Learning: In `RequestCubeHandler`, the `HandleLoadLayout` method is responsible for applying the layout to the plot after missing cubes are calculated in `HandleRequestLayout`.
Learnt from: Zintixx
PR: AngeloTadeucci/Maple2#279
File: Maple2.Database/Storage/Game/GameStorage.Wedding.cs:147-159
Timestamp: 2024-10-13T18:10:08.059Z
Learning: In the `GameStorage` class in `Maple2.Database/Storage/Game/GameStorage.Wedding.cs`, the `GetMarriage` method is necessary to properly convert the `Marriage` object and initialize `Partner1` and `Partner2`.
🧬 Code Graph Analysis (1)
Maple2.Server.Game/Manager/HousingManager.cs (13)
Maple2.Model/Metadata/Constants.cs (1)
  • Constant (10-959)
Maple2.Model/Game/User/Home.cs (2)
  • Home (10-199)
  • Vector3 (129-149)
Maple2.Database/Storage/Game/GameStorage.Map.cs (17)
  • PlotInfo (50-70)
  • PlotInfo (72-89)
  • PlotInfo (91-111)
  • PlotInfo (113-123)
  • PlotInfo (359-381)
  • GameStorage (16-449)
  • Request (17-448)
  • SavePlotInfoName (194-204)
  • SavePlotInfo (177-192)
  • PlotCube (247-270)
  • PlotCube (284-293)
  • PlotCube (404-419)
  • PlotCube (421-436)
  • Plot (157-164)
  • Plot (318-357)
  • InteractCube (396-402)
  • DeleteCube (272-282)
Maple2.Model/Game/User/PlotInfo.cs (3)
  • PlotInfo (7-48)
  • PlotInfo (32-34)
  • Plot (50-60)
Maple2.Database/Storage/Game/GameStorage.Nurturing.cs (2)
  • GameStorage (8-56)
  • Request (9-55)
Maple2.Server.Game/GameServer.cs (4)
  • GameServer (20-208)
  • GameServer (34-64)
  • GetChannel (205-207)
  • Broadcast (197-203)
Maple2.Server.Game/Manager/Field/FieldManager/IField.cs (1)
  • Broadcast (66-66)
Maple2.Model/Game/Cube/PlotCube.cs (2)
  • PlotCube (6-37)
  • PlotCube (22-28)
Maple2.Server.Game/Manager/Items/FurnishingManager.cs (10)
  • Item (66-70)
  • Item (72-76)
  • TryAddCube (85-111)
  • FurnishingManager (15-316)
  • FurnishingManager (26-45)
  • NextCubeId (17-17)
  • AddCube (149-164)
  • AddCube (167-182)
  • AddCube (184-199)
  • PurchaseCube (113-147)
Maple2.Model/Game/Cube/InteractCube.cs (3)
  • InteractCube (10-55)
  • InteractCube (22-37)
  • InteractCube (39-46)
Maple2.Model/Game/Cube/HeldCube.cs (1)
  • HeldCube (7-37)
Maple2.Model/Common/Vector.cs (2)
  • Vector3B (10-10)
  • Vector3B (12-22)
Maple2.File.Ingest/Mapper/TableMapper.cs (1)
  • FurnishingShopTable (1189-1201)
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: build
  • GitHub Check: build
🔇 Additional comments (20)
Maple2.Server.Game/Manager/HousingManager.cs (20)

44-44: Good defensive programming.

Adding the null check for session.Field prevents potential null reference exceptions.


52-70: Enhanced database operations and world synchronization.

The refactoring to use GameStorage.Request context for direct database operations and adding World.UpdateFieldPlot calls for outdoor plot updates improves data consistency and synchronization across the game world.


72-73: Improved broadcasting scope.

Changing from session.Send() to session.Field.Broadcast() ensures all players in the field receive the updates, which is more appropriate for plot name changes.


206-211: Consistent world synchronization in BuyPlot.

Adding World.UpdateFieldPlot call maintains consistency with the new synchronization pattern introduced in other methods.


248-256: Enhanced forfeit plot synchronization.

The World.UpdateFieldPlot call with forfeit flag properly notifies other channels about the plot state change.


307-312: Consistent synchronization pattern.

Adding World.UpdateFieldPlot call in ExtendPlot maintains the established synchronization pattern.


338-338: Simplified Save method.

Removing the redundant SavePlots() call simplifies the method since indoor plots are now handled directly in the database context.


370-378: Improved cube creation with error handling.

The refactored cube creation logic uses the new CreateCube helper method and includes proper error handling for failed cube creation.


491-527: Well-designed helper methods for cube lifecycle management.

The CreateCube and DeleteCube methods properly encapsulate cube creation and deletion logic, including database persistence and nurturing initialization. The error handling and logging are appropriate.


532-532: Essential null check for field operations.

The null check for session.Field is necessary since cube placement requires field operations.


596-605: Improved planner mode validation.

The enhanced validation logic for planner mode properly checks shop entries and existing cube counts, preventing invalid cube placements.


621-630: Robust cube creation with proper cleanup.

The use of CreateCube helper method with proper error handling and cleanup via DeleteCube when operations fail is well-designed.


635-644: Enhanced error handling with cleanup.

The error handling properly cleans up created cubes when purchase operations fail, preventing orphaned database entries.


648-652: Consistent cleanup pattern.

The cleanup pattern using DeleteCube when TryAddCube fails maintains database consistency.


656-657: Proper field interaction setup.

Adding field function interact for cubes with function metadata ensures proper game mechanics.


685-685: Consistent deletion pattern.

Using the DeleteCube helper method maintains consistency with the new cube lifecycle management approach.


722-725: Improved error handling with graceful degradation.

Changing from error to warning when shop entries are missing allows layout application to continue, which is more user-friendly.


735-738: Consistent error handling pattern.

The warning and continue pattern for missing shop entries is consistent with the previous change and maintains graceful degradation.


782-785: Essential null check for item metadata.

The null check for item metadata prevents potential null reference exceptions during layout application.


553-553: No further action required for AccelerationStructure usage

The null‐propagation (?.FirstSellableTile) and subsequent if (groundTile is null) check in HousingManager.cs already guard against an uninitialized AccelerationStructure. Additionally, FieldManager assigns and logs errors on load failure:

  • Initialization in Maple2.Server.Game/Manager/Field/FieldManager/FieldManager.cs:
    AccelerationStructure = accelerationStructure; (logs on failure)
  • Usage in HousingManager.cs safely handles null by returning an error to the client.

Everything behaves as intended—no additional null checks or initialization safeguards are needed here.

✨ 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.
    • Explain this complex logic.
    • 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 explain this code block.
    • @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 explain its main purpose.
    • @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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 11

🔭 Outside diff range comments (4)
Maple2.Tools/Extensions/PacketExtensions.cs (1)

65-71: ReadArray<T> loop condition prevents any element from being read

for (int i = 0; i > size; i++) never executes because 0 > size is false for every non-negative size. This results in always returning an empty array.

-        for (int i = 0; i > size; i++) {
+        for (int i = 0; i < size; i++) {

Without this fix, every call to ReadArray silently drops data.

Maple2.Tools/Collision/IPolygon.cs (1)

19-20: [] collection expression may require C# 12 (preview)

[] for an empty array relies on the new collection-expression feature (C# 12).
If the project isn’t compiled with the latest language version / preview features, replace with the traditional form:

-        public override Vector2[] Points => [];
+        public override Vector2[] Points => Array.Empty<Vector2>();
Maple2.Server.Game/Service/ChannelService.cs (1)

16-22: Missing field assignment for ItemMetadataStorage parameter.

The itemMetadata parameter is added to the constructor but not assigned to a field, making it unusable by service methods. Consider adding a private readonly field and assignment.

 public partial class ChannelService : Channel.Service.Channel.ChannelBase {
     private readonly GameServer server;
     private readonly PlayerInfoStorage playerInfos;
     private readonly GameStorage gameStorage;
     private readonly TableMetadataStorage tableMetadata;
     private readonly ServerTableMetadataStorage serverTableMetadata;
+    private readonly ItemMetadataStorage itemMetadata;

     private readonly ILogger logger = Log.Logger.ForContext<ChannelService>();

     public ChannelService(GameServer server, PlayerInfoStorage playerInfos, GameStorage gameStorage, ServerTableMetadataStorage serverTableMetadata, TableMetadataStorage tableMetadata, ItemMetadataStorage itemMetadata) {
         this.server = server;
         this.playerInfos = playerInfos;
         this.gameStorage = gameStorage;
         this.serverTableMetadata = serverTableMetadata;
         this.tableMetadata = tableMetadata;
+        this.itemMetadata = itemMetadata;
     }
Maple2.Server.Game/Manager/Items/FurnishingManager.cs (1)

85-111: Add null validation for the cube parameter.

The method now accepts a PlotCube parameter but doesn't validate it's not null before use. Consider adding a null check at the beginning of the method.

 public bool TryAddCube(long uid, PlotCube cube) {
+    if (cube == null) {
+        logger.Error("Attempted to add null cube");
+        return false;
+    }
     const int amount = 1;
     lock (session.Item) {
         if (session.Field == null) {
             return false;
         }
🧹 Nitpick comments (5)
Maple2.Tools/Extensions/PacketExtensions.cs (1)

95-106: Null-collection branch writes an implicit value – be explicit

writer.WriteInt(); relies on the overload that defaults to 0. Being explicit improves readability and avoids accidental misuse if the API changes.

-            writer.WriteInt(); // 0 items
+            writer.WriteInt(0); // 0 items
Maple2.Tools/Collision/IPolygon.cs (1)

8-10: Inconsistent default epsilon between overloads

The float-based overload defaults to 0.001f, while the Vector2 overload defaults to 1e-5f.
Confirm this intentional; otherwise standardise the default to avoid surprising callers.

Maple2.Server.World/Service/WorldService.UpdateFieldPlot.cs (1)

6-21: Consider adding error handling for channel broadcast failures.

The current implementation doesn't handle potential failures when broadcasting to channel clients. Consider wrapping the broadcast calls in try-catch blocks to ensure one failed channel doesn't prevent updates to other channels.

 public override Task<FieldPlotResponse> UpdateFieldPlot(FieldPlotRequest request, ServerCallContext context) {
     if (request.MapId == -1) {
         worldServer.FieldPlotExpiryCheck();
         return Task.FromResult(new FieldPlotResponse());
     }
 
     if (request.MapId <= 0) {
         throw new RpcException(new Status(StatusCode.InvalidArgument, "Invalid map ID"));
     }
 
     foreach ((int, Channel.Service.Channel.ChannelClient) channel in channelClients) {
-        channel.Item2.UpdateFieldPlot(request);
+        try {
+            channel.Item2.UpdateFieldPlot(request);
+        } catch (Exception ex) {
+            logger.Error(ex, "Failed to update field plot on channel {ChannelId}", channel.Item1);
+        }
     }
 
     return Task.FromResult(new FieldPlotResponse());
 }
Maple2.Database/Storage/Game/GameStorage.Map.cs (1)

147-155: Use consistent null check formatting.

For consistency with the codebase style, add braces to the null check.

Apply this diff:

 public void SetPlotOpen(long plotId) {
     UgcMap? model = Context.UgcMap.Find(plotId);
-    if (model == null) return;
+    if (model == null) {
+        return;
+    }
 
     model.ExpiryTime = DateTimeOffset.MinValue;
 
     Context.UgcMap.Update(model);
     Context.TrySaveChanges();
 }
Maple2.Server.Game/Manager/HousingManager.cs (1)

491-527: Consider using transactions for database consistency.

The CreateCube method performs multiple database operations (nurturing creation and cube creation) that should be atomic. If cube creation fails after nurturing is created, it could leave orphaned nurturing records.

Consider wrapping the operations in a transaction to ensure consistency:

 private PlotCube? CreateCube(Plot plot, ItemMetadata itemMetadata, FunctionCubeMetadata? functionCubeMetadata, UgcItemLook? template, Vector3 position, float rotation) {
     var result = new PlotCube(itemMetadata, 0, template) {
         Type = PlotCube.CubeType.Construction,
         Position = position,
         Rotation = rotation,
     };
 
     using GameStorage.Request db = session.GameStorage.Context();
+    // Consider using a transaction here
+    // using var transaction = db.BeginTransaction();
 
     if (functionCubeMetadata is not null) {
         result.Interact = new InteractCube(position, functionCubeMetadata);
         if (result.Interact.Nurturing is not null && result.Interact.Metadata.Nurturing is not null) {
             Nurturing? nurturing = db.GetNurturing(session.AccountId, result.ItemId, result.Interact.Metadata.Nurturing);
             if (nurturing is null) {
                 nurturing = db.CreateNurturing(session.AccountId, result.Interact.Metadata.Nurturing, result.Interact.Metadata.Id);
                 if (nurturing is null) {
                     logger.Error("Failed to create Nurturing for {AccountId}, ItemId {ItemId}", session.AccountId, result.ItemId);
+                    // transaction?.Rollback();
                     return null;
                 }
             }
         }
     }
 
     result = db.CreateCube(plot, result);
     if (result is null) {
         logger.Error("Failed to create cube for plot {PlotNumber} at position {Position}.", plot.Number, position);
+        // transaction?.Rollback();
         return null;
     }
 
+    // transaction?.Commit();
     return result;
 }
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between a08569e and 720c1be.

📒 Files selected for processing (31)
  • Maple2.Database/Storage/Game/GameStorage.Map.cs (4 hunks)
  • Maple2.Database/Storage/Game/GameStorage.Nurturing.cs (1 hunks)
  • Maple2.Model/Game/Cube/PlotCube.cs (2 hunks)
  • Maple2.Model/Game/User/Home.cs (1 hunks)
  • Maple2.Model/Game/User/PlotInfo.cs (2 hunks)
  • Maple2.Model/Metadata/Constants.cs (1 hunks)
  • Maple2.Server.Core/proto/channel/channel.proto (1 hunks)
  • Maple2.Server.Core/proto/common.proto (1 hunks)
  • Maple2.Server.Core/proto/world/world.proto (1 hunks)
  • Maple2.Server.Game/Commands/DebugCommand.cs (3 hunks)
  • Maple2.Server.Game/GameServer.cs (1 hunks)
  • Maple2.Server.Game/Manager/Field/FieldManager/FieldManager.Factory.cs (1 hunks)
  • Maple2.Server.Game/Manager/Field/FieldManager/FieldManager.State.cs (1 hunks)
  • Maple2.Server.Game/Manager/Field/FieldManager/FieldManager.Ugc.cs (3 hunks)
  • Maple2.Server.Game/Manager/Field/FieldManager/HomeFieldManager.cs (2 hunks)
  • Maple2.Server.Game/Manager/HousingManager.cs (14 hunks)
  • Maple2.Server.Game/Manager/Items/FurnishingManager.cs (2 hunks)
  • Maple2.Server.Game/Manager/Items/ItemManager.cs (2 hunks)
  • Maple2.Server.Game/PacketHandlers/ChannelHandler.cs (2 hunks)
  • Maple2.Server.Game/PacketHandlers/DungeonRoomHandler.cs (0 hunks)
  • Maple2.Server.Game/PacketHandlers/LoadUgcMapHandler.cs (2 hunks)
  • Maple2.Server.Game/PacketHandlers/RequestCubeHandler.cs (6 hunks)
  • Maple2.Server.Game/PacketHandlers/TimeSyncHandler.cs (1 hunks)
  • Maple2.Server.Game/Packets/LoadCubesPacket.cs (3 hunks)
  • Maple2.Server.Game/Service/ChannelService.UpdateFieldPlot.cs (1 hunks)
  • Maple2.Server.Game/Service/ChannelService.cs (1 hunks)
  • Maple2.Server.Game/Session/GameSession.cs (3 hunks)
  • Maple2.Server.World/Service/WorldService.UpdateFieldPlot.cs (1 hunks)
  • Maple2.Server.World/WorldServer.cs (5 hunks)
  • Maple2.Tools/Collision/IPolygon.cs (1 hunks)
  • Maple2.Tools/Extensions/PacketExtensions.cs (1 hunks)
💤 Files with no reviewable changes (1)
  • Maple2.Server.Game/PacketHandlers/DungeonRoomHandler.cs
🧰 Additional context used
🧠 Learnings (15)
📓 Common learnings
Learnt from: AngeloTadeucci
PR: AngeloTadeucci/Maple2#236
File: Maple2.Server.Game/PacketHandlers/RequestCubeHandler.cs:375-422
Timestamp: 2024-09-16T07:50:30.281Z
Learning: In `RequestCubeHandler`, the `HandleLoadLayout` method is responsible for applying the layout to the plot after missing cubes are calculated in `HandleRequestLayout`.
Maple2.Tools/Extensions/PacketExtensions.cs (3)
Learnt from: Zintixx
PR: AngeloTadeucci/Maple2#279
File: Maple2.File.Ingest/Mapper/TableMapper.cs:1525-1551
Timestamp: 2024-10-12T20:08:58.356Z
Learning: When reviewing C# code, remember that initializing lists with `[]` is valid in C# 12 and later.
Learnt from: AngeloTadeucci
PR: AngeloTadeucci/Maple2#415
File: Maple2.Database/Storage/Metadata/QuestMetadataStorage.cs:12-12
Timestamp: 2025-04-13T23:57:40.599Z
Learning: C# 12 (released with .NET 8) supports collection expressions which allow using `[]` syntax to initialize collections including ConcurrentDictionary and other collection types.
Learnt from: AngeloTadeucci
PR: AngeloTadeucci/Maple2#415
File: Maple2.Database/Storage/Metadata/QuestMetadataStorage.cs:12-12
Timestamp: 2025-04-13T23:57:40.599Z
Learning: C# 12 (released with .NET 8) supports collection expressions which allow using `[]` syntax to initialize collections including ConcurrentDictionary and other collection types.
Maple2.Tools/Collision/IPolygon.cs (2)
Learnt from: mettaursp
PR: AngeloTadeucci/Maple2#270
File: Maple2.Tools/VectorMath/Transform.cs:92-94
Timestamp: 2024-10-09T04:13:28.776Z
Learning: In the `Transform.Scale` setter in `Maple2.Tools/VectorMath/Transform.cs`, the axes are normalized and directly scaled without considering the original scale, as it's no longer a factor after normalization.
Learnt from: mettaursp
PR: AngeloTadeucci/Maple2#270
File: Maple2.Tools/VectorMath/Transform.cs:92-94
Timestamp: 2024-09-30T04:20:44.252Z
Learning: In the `Transform.Scale` setter in `Maple2.Tools/VectorMath/Transform.cs`, the axes are normalized and directly scaled without considering the original scale, as it's no longer a factor after normalization.
Maple2.Server.Game/PacketHandlers/ChannelHandler.cs (2)
Learnt from: AngeloTadeucci
PR: AngeloTadeucci/Maple2#199
File: Maple2.Server.Game/PacketHandlers/FileHandler.cs:0-0
Timestamp: 2024-10-09T04:13:28.776Z
Learning: Packets can't ever be null inside the `Handle` function of packet handlers in the repository.
Learnt from: AngeloTadeucci
PR: AngeloTadeucci/Maple2#199
File: Maple2.Server.Game/PacketHandlers/FileHandler.cs:0-0
Timestamp: 2024-07-12T04:31:25.376Z
Learning: Packets can't ever be null inside the `Handle` function of packet handlers in the repository.
Maple2.Server.Game/PacketHandlers/LoadUgcMapHandler.cs (4)
Learnt from: AngeloTadeucci
PR: AngeloTadeucci/Maple2#215
File: Maple2.Server.Core/Packets/UgcPacket.cs:111-127
Timestamp: 2024-08-22T01:51:54.865Z
Learning: The `counter1` variable and the associated loop in the `LoadBanners` method of `UgcPacket` are intended to document the packet structure rather than serve a functional purpose.
Learnt from: AngeloTadeucci
PR: AngeloTadeucci/Maple2#215
File: Maple2.Server.Core/Packets/UgcPacket.cs:111-127
Timestamp: 2024-10-09T04:13:28.776Z
Learning: The `counter1` variable and the associated loop in the `LoadBanners` method of `UgcPacket` are intended to document the packet structure rather than serve a functional purpose.
Learnt from: Zintixx
PR: AngeloTadeucci/Maple2#279
File: Maple2.Database/Storage/Game/GameStorage.Wedding.cs:147-159
Timestamp: 2024-10-13T18:10:08.059Z
Learning: In the `GameStorage` class in `Maple2.Database/Storage/Game/GameStorage.Wedding.cs`, the `GetMarriage` method is necessary to properly convert the `Marriage` object and initialize `Partner1` and `Partner2`.
Learnt from: AngeloTadeucci
PR: AngeloTadeucci/Maple2#236
File: Maple2.Server.Game/PacketHandlers/RequestCubeHandler.cs:375-422
Timestamp: 2024-09-16T07:50:30.281Z
Learning: In `RequestCubeHandler`, the `HandleLoadLayout` method is responsible for applying the layout to the plot after missing cubes are calculated in `HandleRequestLayout`.
Maple2.Server.Game/PacketHandlers/TimeSyncHandler.cs (2)
Learnt from: AngeloTadeucci
PR: AngeloTadeucci/Maple2#199
File: Maple2.Server.Game/PacketHandlers/FileHandler.cs:0-0
Timestamp: 2024-10-09T04:13:28.776Z
Learning: Packets can't ever be null inside the `Handle` function of packet handlers in the repository.
Learnt from: AngeloTadeucci
PR: AngeloTadeucci/Maple2#199
File: Maple2.Server.Game/PacketHandlers/FileHandler.cs:0-0
Timestamp: 2024-07-12T04:31:25.376Z
Learning: Packets can't ever be null inside the `Handle` function of packet handlers in the repository.
Maple2.Server.Game/GameServer.cs (2)
Learnt from: AngeloTadeucci
PR: AngeloTadeucci/Maple2#239
File: Maple2.Server.Game/Session/GameSession.State.cs:26-27
Timestamp: 2024-09-19T04:27:27.320Z
Learning: In `GameSession` class, fields are public. When adding new fields to this class, they should be public for consistency.
Learnt from: AngeloTadeucci
PR: AngeloTadeucci/Maple2#239
File: Maple2.Server.Game/Session/GameSession.State.cs:26-27
Timestamp: 2024-10-09T04:13:28.776Z
Learning: In `GameSession` class, fields are public. When adding new fields to this class, they should be public for consistency.
Maple2.Database/Storage/Game/GameStorage.Nurturing.cs (1)
Learnt from: Zintixx
PR: AngeloTadeucci/Maple2#279
File: Maple2.Database/Storage/Game/GameStorage.Wedding.cs:147-159
Timestamp: 2024-10-13T18:10:08.059Z
Learning: In the `GameStorage` class in `Maple2.Database/Storage/Game/GameStorage.Wedding.cs`, the `GetMarriage` method is necessary to properly convert the `Marriage` object and initialize `Partner1` and `Partner2`.
Maple2.Server.Game/Manager/Items/ItemManager.cs (1)
Learnt from: Zintixx
PR: AngeloTadeucci/Maple2#279
File: Maple2.Database/Storage/Game/GameStorage.Wedding.cs:147-159
Timestamp: 2024-10-13T18:10:08.059Z
Learning: In the `GameStorage` class in `Maple2.Database/Storage/Game/GameStorage.Wedding.cs`, the `GetMarriage` method is necessary to properly convert the `Marriage` object and initialize `Partner1` and `Partner2`.
Maple2.Server.Game/Session/GameSession.cs (3)
Learnt from: AngeloTadeucci
PR: AngeloTadeucci/Maple2#239
File: Maple2.Server.Game/Session/GameSession.State.cs:26-27
Timestamp: 2024-09-19T04:27:27.320Z
Learning: In `GameSession` class, fields are public. When adding new fields to this class, they should be public for consistency.
Learnt from: AngeloTadeucci
PR: AngeloTadeucci/Maple2#239
File: Maple2.Server.Game/Session/GameSession.State.cs:26-27
Timestamp: 2024-10-09T04:13:28.776Z
Learning: In `GameSession` class, fields are public. When adding new fields to this class, they should be public for consistency.
Learnt from: Zintixx
PR: AngeloTadeucci/Maple2#279
File: Maple2.Database/Storage/Game/GameStorage.Wedding.cs:147-159
Timestamp: 2024-10-13T18:10:08.059Z
Learning: In the `GameStorage` class in `Maple2.Database/Storage/Game/GameStorage.Wedding.cs`, the `GetMarriage` method is necessary to properly convert the `Marriage` object and initialize `Partner1` and `Partner2`.
Maple2.Server.Game/Manager/Field/FieldManager/HomeFieldManager.cs (2)
Learnt from: Zintixx
PR: AngeloTadeucci/Maple2#279
File: Maple2.Database/Storage/Game/GameStorage.Wedding.cs:147-159
Timestamp: 2024-10-13T18:10:08.059Z
Learning: In the `GameStorage` class in `Maple2.Database/Storage/Game/GameStorage.Wedding.cs`, the `GetMarriage` method is necessary to properly convert the `Marriage` object and initialize `Partner1` and `Partner2`.
Learnt from: AngeloTadeucci
PR: AngeloTadeucci/Maple2#236
File: Maple2.Server.Game/PacketHandlers/RequestCubeHandler.cs:375-422
Timestamp: 2024-09-16T07:50:30.281Z
Learning: In `RequestCubeHandler`, the `HandleLoadLayout` method is responsible for applying the layout to the plot after missing cubes are calculated in `HandleRequestLayout`.
Maple2.Server.Game/PacketHandlers/RequestCubeHandler.cs (3)
Learnt from: AngeloTadeucci
PR: AngeloTadeucci/Maple2#236
File: Maple2.Server.Game/PacketHandlers/RequestCubeHandler.cs:375-422
Timestamp: 2024-09-16T07:50:30.281Z
Learning: In `RequestCubeHandler`, the `HandleLoadLayout` method is responsible for applying the layout to the plot after missing cubes are calculated in `HandleRequestLayout`.
Learnt from: AngeloTadeucci
PR: AngeloTadeucci/Maple2#199
File: Maple2.Server.Game/PacketHandlers/FileHandler.cs:0-0
Timestamp: 2024-10-09T04:13:28.776Z
Learning: Packets can't ever be null inside the `Handle` function of packet handlers in the repository.
Learnt from: AngeloTadeucci
PR: AngeloTadeucci/Maple2#199
File: Maple2.Server.Game/PacketHandlers/FileHandler.cs:0-0
Timestamp: 2024-07-12T04:31:25.376Z
Learning: Packets can't ever be null inside the `Handle` function of packet handlers in the repository.
Maple2.Server.Game/Manager/Field/FieldManager/FieldManager.Ugc.cs (1)
Learnt from: AngeloTadeucci
PR: AngeloTadeucci/Maple2#236
File: Maple2.Server.Game/PacketHandlers/RequestCubeHandler.cs:375-422
Timestamp: 2024-09-16T07:50:30.281Z
Learning: In `RequestCubeHandler`, the `HandleLoadLayout` method is responsible for applying the layout to the plot after missing cubes are calculated in `HandleRequestLayout`.
Maple2.Database/Storage/Game/GameStorage.Map.cs (2)
Learnt from: Zintixx
PR: AngeloTadeucci/Maple2#279
File: Maple2.Database/Storage/Game/GameStorage.Wedding.cs:147-159
Timestamp: 2024-10-13T18:10:08.059Z
Learning: In the `GameStorage` class in `Maple2.Database/Storage/Game/GameStorage.Wedding.cs`, the `GetMarriage` method is necessary to properly convert the `Marriage` object and initialize `Partner1` and `Partner2`.
Learnt from: AngeloTadeucci
PR: AngeloTadeucci/Maple2#236
File: Maple2.Server.Game/PacketHandlers/RequestCubeHandler.cs:375-422
Timestamp: 2024-09-16T07:50:30.281Z
Learning: In `RequestCubeHandler`, the `HandleLoadLayout` method is responsible for applying the layout to the plot after missing cubes are calculated in `HandleRequestLayout`.
Maple2.Server.Game/Manager/HousingManager.cs (2)
Learnt from: AngeloTadeucci
PR: AngeloTadeucci/Maple2#236
File: Maple2.Server.Game/PacketHandlers/RequestCubeHandler.cs:375-422
Timestamp: 2024-09-16T07:50:30.281Z
Learning: In `RequestCubeHandler`, the `HandleLoadLayout` method is responsible for applying the layout to the plot after missing cubes are calculated in `HandleRequestLayout`.
Learnt from: Zintixx
PR: AngeloTadeucci/Maple2#279
File: Maple2.Database/Storage/Game/GameStorage.Wedding.cs:147-159
Timestamp: 2024-10-13T18:10:08.059Z
Learning: In the `GameStorage` class in `Maple2.Database/Storage/Game/GameStorage.Wedding.cs`, the `GetMarriage` method is necessary to properly convert the `Marriage` object and initialize `Partner1` and `Partner2`.
🧬 Code Graph Analysis (6)
Maple2.Server.Game/Manager/Field/FieldManager/FieldManager.Factory.cs (1)
Maple2.Server.Game/Manager/Field/FieldManager/HomeFieldManager.cs (2)
  • HomeFieldManager (7-53)
  • HomeFieldManager (15-19)
Maple2.Server.Game/Manager/Items/ItemManager.cs (3)
Maple2.Server.Game/Manager/Items/FurnishingManager.cs (3)
  • FurnishingManager (15-316)
  • FurnishingManager (26-45)
  • Load (47-64)
Maple2.Database/Storage/Game/GameStorage.Map.cs (2)
  • GameStorage (16-450)
  • Request (17-449)
Maple2.Database/Storage/Game/GameStorage.Item.cs (2)
  • GameStorage (11-192)
  • Request (12-191)
Maple2.Server.Game/Session/GameSession.cs (1)
Maple2.Server.World/Containers/ChannelClientLookup.cs (2)
  • Channel (36-59)
  • Channel (49-58)
Maple2.Model/Game/User/PlotInfo.cs (4)
Maple2.Server.Game/Model/Field/Actor/FieldPlayer.cs (1)
  • ToString (681-687)
Maple2.Tools/Collision/BoundingBox.cs (1)
  • ToString (29-31)
Maple2.Model/Game/TriggerObject.cs (1)
  • ToString (123-125)
Maple2.Server.Game/Model/Field/Buff.cs (1)
  • ToString (333-335)
Maple2.Server.Game/Commands/DebugCommand.cs (6)
Maple2.Server.Game/Commands/CommandRouter.cs (2)
  • GameCommand (108-113)
  • GameCommand (110-112)
Maple2.Server.Game/Session/GameSession.cs (3)
  • GameSession (36-850)
  • GameSession (116-126)
  • GameSession (740-740)
Maple2.Database/Storage/Metadata/MapMetadataStorage.cs (3)
  • MapMetadataStorage (10-93)
  • MapMetadataStorage (17-24)
  • TryGetUgc (49-64)
Maple2.Database/Storage/Game/GameStorage.Map.cs (7)
  • GameStorage (16-450)
  • Request (17-449)
  • PlotInfo (50-70)
  • PlotInfo (72-89)
  • PlotInfo (91-111)
  • PlotInfo (113-123)
  • PlotInfo (360-382)
Maple2.Model/Game/User/Home.cs (1)
  • Home (10-199)
Maple2.Model/Game/User/PlotInfo.cs (2)
  • PlotInfo (7-48)
  • PlotInfo (32-34)
Maple2.Database/Storage/Game/GameStorage.Map.cs (7)
Maple2.Model/Game/User/PlotInfo.cs (3)
  • PlotInfo (7-48)
  • PlotInfo (32-34)
  • Plot (50-60)
Maple2.Server.Game/Manager/HousingManager.cs (5)
  • PlotInfo (215-258)
  • Plot (118-135)
  • Plot (137-146)
  • PlotCube (491-521)
  • DeleteCube (523-527)
Maple2.Database/Model/Map/UgcMap.cs (1)
  • UgcMap (8-45)
Maple2.Model/Metadata/Constants.cs (1)
  • Constant (10-959)
Maple2.Database/Extensions/DbContextExtensions.cs (1)
  • TrySaveChanges (16-25)
Maple2.Model/Game/Cube/PlotCube.cs (2)
  • PlotCube (6-37)
  • PlotCube (22-28)
Maple2.Database/Model/Map/UgcMapCube.cs (1)
  • UgcMapCube (11-49)
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: build
  • GitHub Check: build
🔇 Additional comments (42)
Maple2.Tools/Extensions/PacketExtensions.cs (1)

1-1: Unused using System; removal looks good

Nice clean-up; the directive was genuinely unused and its removal reduces noise.

Maple2.Tools/Collision/IPolygon.cs (1)

14-14: Return type now implicitly depends on System.Range; confirm target framework version

Even after re-adding using System, ensure the target framework is netstandard2.1 / net6.0+, otherwise System.Range is unavailable.
Verify the libraries consuming IPolygon are built against a compatible TFM.

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

15-18: Re-verifying behavioral parity after swapping base class

Changing the inheritance from FieldPacketHandler to PacketHandler<GameSession> alters:

  1. Lifecycle hooks (e.g. any virtual OnConnected/OnDisconnected logic).
  2. The guarantee that session.Field was non-null in previous implementations.

While the current handler still calls session.Field?.MapId, losing any field-specific pre-checks could surface NOREF bugs in edge cases (e.g. user changes to a channel from the login-lobby, where Field might be null).

Double-check:

  • Other overrides (OnEnterField, etc.) were not inadvertently dropped.
  • Upstream DI registrations bind ChannelHandler exactly once per session regardless of the new base type.
  • Unit/integration tests still cover “change channel while not loaded into a field”.

No code change required if these are already covered; just ensure parity.

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

1-4: LGTM: Clean namespace reorganization.

The namespace change properly moves the handler to the game server package while maintaining the inheritance relationship through the using directive. This aligns with the broader architectural changes mentioned in the PR objectives.

Maple2.Database/Storage/Game/GameStorage.Nurturing.cs (1)

10-11: LGTM: Parameter name change aligns with architectural shift.

The change from characterId to accountId properly reflects the semantic shift toward account-based identification mentioned in the AI summary. The implementation is consistent with the parameter name change.

Maple2.Server.Core/proto/channel/channel.proto (1)

51-52: LGTM: RPC addition supports plot management system.

The new UpdateFieldPlot RPC method is properly integrated into the Channel service and follows the existing pattern. This supports the broader plot lifecycle management and synchronization features mentioned in the PR objectives.

Maple2.Server.Core/proto/world/world.proto (1)

65-66: LGTM: Consistent RPC addition enables cross-server plot updates.

The UpdateFieldPlot RPC method mirrors the Channel service implementation, enabling proper coordination between world and channel servers for plot state synchronization as described in the PR objectives.

Maple2.Server.Game/PacketHandlers/LoadUgcMapHandler.cs (2)

47-47: LGTM: Helpful comment clarifies data source.

The comment clearly explains why the home is fetched from the database instead of using the session's player value, which provides valuable context for future maintainers.


83-86: LGTM: Refined plot filtering logic.

The filtering logic has been improved to be more specific:

  • PlotOwners now only includes plots with state PlotState.Taken (more precise than all non-open plots)
  • PlotExpiry maintains the existing logic for all non-open plots

This refinement aligns with the enhanced plot state management described in the AI summary and should provide more accurate plot information to clients.

Maple2.Model/Metadata/Constants.cs (1)

278-278: LGTM! Excellent type safety improvement.

Converting the constant from raw seconds to a strongly-typed TimeSpan improves code clarity and prevents unit confusion. This aligns well with other time-related constants in the same region.

Maple2.Server.Game/Manager/Field/FieldManager/FieldManager.Factory.cs (1)

138-138: LGTM! Constructor parameter alignment.

The addition of the roomId parameter correctly aligns with the updated HomeFieldManager constructor. This supports the new room-specific plot management functionality where roomId == -1 indicates planner plots that should start empty.

Maple2.Server.Game/Packets/LoadCubesPacket.cs (3)

45-45: LGTM! Simplified plot state serialization.

Writing a boolean for whether the plot is Taken simplifies client-side logic compared to serializing the full enum value.


57-58: LGTM! Added missing plot data fields.

The addition of ApartmentNumber and Name fields aligns with the database schema updates and ensures clients receive complete plot ownership information.


72-72: LGTM! Consistent apartment number inclusion.

Adding ApartmentNumber to the plot expiry packet maintains consistency with the other packet methods.

Maple2.Server.Game/GameServer.cs (1)

86-90: LGTM! Well-implemented session lookup by account ID.

The new method follows the established pattern with proper thread safety using the existing mutex. This complements the character ID-based lookup and supports plot management features that operate at the account level.

Maple2.Model/Game/Cube/PlotCube.cs (2)

10-13: LGTM! Helpful clarification comment.

The comment clearly distinguishes between PlotCube and HeldCube ID purposes, which will help prevent confusion during development.


30-36: Clockwise rotation logic is correct
In our coordinate system (e.g. Unity’s), positive angles rotate counter-clockwise. Adding 270° (equivalent to –90°) for a “clockwise” spin and +90° for counter-clockwise matches that convention—no changes needed.

Maple2.Server.Game/Session/GameSession.cs (3)

56-56: LGTM! Property design follows good encapsulation practices.

The Channel property with private setter provides controlled access while maintaining the public accessibility pattern consistent with this class design.


145-145: Correct channel assignment from migration response.

The assignment properly sets the session's channel for multi-channel coordination.


657-657: Well-coordinated conditional logic for planner modes.

The conditional RoomId assignment correctly differentiates between normal and planner modes, aligning with the HomeFieldManager's cube clearing logic when roomId is -1.

Maple2.Server.Game/Manager/Items/ItemManager.cs (2)

16-16: Good refactor to enable dynamic furnishing management.

Converting from readonly field to property with private setter properly enables reassignment while maintaining encapsulation.


27-31: Well-implemented furnishing reloading method.

The method correctly uses using for database context disposal, creates a fresh FurnishingManager instance, and calls Load() to update the client. Resource management and functionality are properly implemented.

Maple2.Server.Game/Manager/Field/FieldManager/FieldManager.State.cs (1)

670-670: Correct placement of function cube broadcast.

The broadcast is properly positioned after adding the entity to the field collection, ensuring state consistency before client notification. This aligns well with the PR's plot state broadcasting objectives.

Maple2.Server.Game/Manager/Field/FieldManager/HomeFieldManager.cs (3)

13-13: Appropriate field addition for roomId storage.

The readonly field properly stores the roomId parameter for use throughout the class lifecycle.


15-19: Well-implemented constructor enhancement.

The roomId parameter is properly added and assigned to the field, enabling the planner mode functionality integration.


26-29: Correct implementation of planner mode cube clearing.

The conditional logic properly clears cubes for planner modes (roomId == -1), providing a clean slate for planning activities. The comment clearly explains the intent.

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

57-59: LGTM!

The ToString() implementation follows the codebase patterns and provides useful debugging information.


36-48: Plot state logic verified — no changes required

  • Maple2.Model/Game/User/PlotInfo.cs: State now correctly treats any ExpiryTime > 0 as active (Taken/Pending) and all non‐positive values as Open.
  • Maple2.Server.World/WorldServer.cs: FieldPlotExpiryCheck compares ExpiryTime to DateTimeOffset.UtcNow.ToUnixTimeSeconds(), marking expired plots Pending/Open in the database.

No further adjustments needed.


53-55: Preserving cubes on entering planner mode is intentional

The SetPlannerMode change in Maple2.Model/Game/User/PlotInfo.cs now only updates PlotMode, preserving the existing Cubes collection in the client model. Server-side logic still clears cubes where needed—e.g.:

  • In FieldManager.Ugc.cs (line 86): plot.Cubes.Clear()
  • In HomeFieldManager.cs (line 28): plot.Cubes.Clear()
  • In RequestCubeHandler.HandleLoadLayout (lines 603–606): enforces an empty cube list before loading layouts

This behavior supports the PR’s planner/blueprint editing improvements by retaining cubes on the client while ensuring server-side layout loads still require a cleared state.

Maple2.Server.Game/Service/ChannelService.UpdateFieldPlot.cs (2)

73-150: Well-structured block update handling!

The implementation properly validates inputs, manages cube lifecycle, handles interactions, and sends appropriate packets for each operation type. The error logging is comprehensive and will help with debugging.


65-70: Check session availability before sending forfeit confirmation.

The session variable might be null if the player is offline (lines 40-48), but the forfeit confirmation is sent unconditionally at line 68.

 if (request.Forfeit) {
     if (fieldManager.Plots.TryGetValue(plotNumber, out Plot? plot)) {
         fieldManager.Broadcast(CubePacket.ForfeitPlot(plot));
-        session?.Send(CubePacket.ConfirmForfeitPlot(plot));
+        if (session != null) {
+            session.Send(CubePacket.ConfirmForfeitPlot(plot));
+        }
     }
 }
⛔ Skipped due to learnings
Learnt from: AngeloTadeucci
PR: AngeloTadeucci/Maple2#236
File: Maple2.Server.Game/PacketHandlers/RequestCubeHandler.cs:375-422
Timestamp: 2024-09-16T07:50:30.281Z
Learning: In `RequestCubeHandler`, the `HandleLoadLayout` method is responsible for applying the layout to the plot after missing cubes are calculated in `HandleRequestLayout`.
Maple2.Model/Game/User/Home.cs (1)

31-41: LGTM!

Moving field initializations inline is a good practice in modern C# and improves code readability.

Maple2.Server.Game/PacketHandlers/RequestCubeHandler.cs (2)

219-219: Good addition of null checks

The early return null checks for session.Field prevent potential null reference exceptions.

Also applies to: 253-253, 293-293


232-244: Consistent implementation of plot update notifications

The addition of FieldPlotRequest updates for cube operations on outdoor plots ensures proper synchronization across channels. The pattern is consistently applied across remove, rotate, and replace operations.

Also applies to: 269-283, 314-324

Maple2.Database/Storage/Game/GameStorage.Map.cs (2)

247-270: Well-implemented cube creation with proper error handling.

The method correctly handles exceptions, logs errors, and manages nurturing metadata when present.


134-145: Add early return after null check.

The method continues execution even when ugcMap is null, which could lead to unexpected behavior.

Apply this diff:

 public void SetPlotPending(long plotId) {
     // Set expiry time to now
     UgcMap? ugcMap = Context.UgcMap.Find(plotId);
-    if (ugcMap == null) return;
+    if (ugcMap == null) {
+        return;
+    }
 
     ugcMap.ExpiryTime = DateTimeOffset.UtcNow;
     ugcMap.OwnerId = 0;
     ugcMap.Name = string.Empty;
 
     Context.UgcMap.Update(ugcMap);
     Context.TrySaveChanges();
 }

Likely an incorrect or invalid review comment.

Maple2.Server.Game/Manager/HousingManager.cs (4)

43-74: Good integration with world update notifications.

The method now properly saves plot names to the database and notifies other channels of the update.


621-661: Excellent error handling and cleanup logic.

The method properly cleans up created cubes from the database if subsequent operations fail, ensuring data consistency.


206-212: Consistent world update notifications across plot operations.

All plot operations (buy, forfeit, extend) now properly notify the world service of changes, ensuring synchronization across channels.

Also applies to: 248-257, 307-312


722-738: Good resilience with null checks and warning logs.

The methods now handle missing shop entries and item metadata gracefully by logging warnings and continuing, rather than failing the entire operation.

Also applies to: 782-786

Maple2.Server.Game/Manager/Field/FieldManager/FieldManager.Ugc.cs (1)

175-194: Well-implemented plot update method

The UpdateAllPlots method properly handles concurrent access with appropriate locking and provides comprehensive plot state synchronization.

Maple2.Server.World/WorldServer.cs (1)

38-55: Constructor properly integrates ItemMetadataStorage dependency

The dependency injection and initialization of the plot expiry check are implemented correctly.

Comment thread Maple2.Tools/Collision/IPolygon.cs
Comment thread Maple2.Server.Game/PacketHandlers/ChannelHandler.cs
Comment thread Maple2.Server.Core/proto/common.proto
Comment thread Maple2.Server.Game/Commands/DebugCommand.cs
Comment thread Maple2.Server.Game/Commands/DebugCommand.cs
Comment thread Maple2.Database/Storage/Game/GameStorage.Map.cs
Comment thread Maple2.Server.Game/Manager/HousingManager.cs
Comment thread Maple2.Server.Game/Manager/Field/FieldManager/FieldManager.Ugc.cs
Comment thread Maple2.Server.World/WorldServer.cs
Comment thread Maple2.Server.World/WorldServer.cs
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Comment thread Maple2.Server.Game/Session/GameSession.cs Outdated
@AngeloTadeucci
AngeloTadeucci merged commit f769b83 into master Jul 5, 2025
4 checks passed
@AngeloTadeucci
AngeloTadeucci deleted the plot-expiration branch July 5, 2025 16:15
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