Skip to content

Account Wide Mails#293

Merged
AngeloTadeucci merged 4 commits intomasterfrom
account-wide-mails
Nov 22, 2024
Merged

Account Wide Mails#293
AngeloTadeucci merged 4 commits intomasterfrom
account-wide-mails

Conversation

@AngeloTadeucci
Copy link
Collaborator

@AngeloTadeucci AngeloTadeucci commented Nov 21, 2024

Summary by CodeRabbit

Release Notes

  • New Features

    • Enhanced mail handling allows retrieval and management of mail using both account and character identifiers.
    • New method added to retrieve player information based on account ID, improving mail notification capabilities.
  • Improvements

    • Mail notifications now include updates for nurturing actions, ensuring plot owners receive relevant notifications.
    • Input validation added for mail notifications to ensure valid identifiers are used.
  • Bug Fixes

    • Improved error handling and logging for mail creation and notification processes.
    • Enhanced clarity of mail properties and functionality through updated comments and configurations.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 21, 2024

Walkthrough

The pull request introduces significant modifications to the mail handling system within the Maple2 project. Key changes include updates to the Mail class to clarify the ReceiverId property, alterations in the GameStorage.Request class to support account-wide mail retrieval, and enhancements in the MailManager class to incorporate account identifiers in mail operations. Additionally, the sync.proto file is updated to include an optional account_id field in the MailNotificationRequest. These changes collectively improve the flexibility and functionality of the mail system across various components.

Changes

File Path Change Summary
Maple2.Database/Model/Mail.cs Updated comment for ReceiverId; modified Configure method to remove foreign key constraint.
Maple2.Database/Storage/Game/GameStorage.Mail.cs Updated method signatures to include accountId; renamed GetReceivedMail to GetAllMail.
Maple2.Model/Game/Mail.cs Updated comment for ReceiverId property.
Maple2.Server.Core/proto/sync.proto Changed character_id to optional; added account_id field.
Maple2.Server.Game/Manager/MailManager.cs Updated methods to include session.AccountId for mail operations.
Maple2.Server.Game/PacketHandlers/FunctionCubeHandler.cs Updated mail handling in HandlePlayNurturing; modified CreateOwnerMail method.
Maple2.Server.World/Containers/PlayerInfoLookup.cs Added TryGetByAccountId method for retrieving player info by account ID.
Maple2.Server.World/Service/WorldService.Sync.cs Enhanced MailNotification method with input validation and improved error handling.

Assessment against linked issues

Objective Addressed Explanation
Implement account-wide mails (#280)
Add key AccountId to mail table, rename ReceiverId to CharacterId (#280) Changes to rename ReceiverId were not implemented.
Remove required from CharacterId (#280) The required constraint on CharacterId was not removed.
Load mails from AccountId on MailManager.Fetch (#280)
Update gRPC MailNotificationRequest to accept AccountId (#280)

Possibly related PRs

  • Feat: Pet Nurturing #281: This PR involves modifications to the Nurturing class and its integration with the mail system, which relates to the changes made to the Mail class and mail operations in this PR.

Suggested reviewers

  • Zintixx

Poem

🐰 In the land of Maple, where the mail does flow,
A rabbit hops swiftly, with news to bestow.
Account and character, now side by side,
In the world of our game, let our friendships abide!
With every new message, joy leaps in the air,
For every kind letter, a tale we can share! 📬✨

Warning

Rate limit exceeded

@AngeloTadeucci has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 9 minutes and 10 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between d0ee64a and 7b536ca.


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ 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.

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 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: 4

🧹 Outside diff range and nitpick comments (8)
Maple2.Server.World/Service/WorldService.Sync.cs (2)

71-73: Improve the error message clarity.

The error message could be more descriptive about the requirements. Consider clarifying that at least one identifier must be provided.

-            throw new RpcException(new Status(StatusCode.InvalidArgument, "AccountId and CharacterId not specified"));
+            throw new RpcException(new Status(StatusCode.InvalidArgument, "Either AccountId or CharacterId must be specified"));

74-79: Optimize the player lookup logic.

Consider checking if CharacterId is provided before attempting the lookup to avoid unnecessary database queries.

-        if (!playerLookup.TryGet(request.CharacterId, out PlayerInfo? info)) {
-            info = playerLookup.TryGetByAccountId(request.AccountId);
-            if (info == null) {
-                return Task.FromResult(new MailNotificationResponse());
-            }
-        }
+        PlayerInfo? info = null;
+        if (request.CharacterId > 0) {
+            if (!playerLookup.TryGet(request.CharacterId, out info)) {
+                info = null;
+            }
+        }
+        if (info == null && request.AccountId > 0) {
+            info = playerLookup.TryGetByAccountId(request.AccountId);
+        }
+        if (info == null) {
+            return Task.FromResult(new MailNotificationResponse());
+        }
Maple2.Database/Storage/Game/GameStorage.Mail.cs (3)

45-46: Consider performance optimizations for mail retrieval.

The current implementation might face performance issues with large datasets:

  1. The OR condition in the WHERE clause (mail.ReceiverId == characterId || mail.ReceiverId == accountId) might not utilize indexes effectively
  2. Loading all items for all mails at once could be memory-intensive
  3. No pagination implementation beyond the minId filter

Consider these optimizations:

 public ICollection<Mail> GetAllMail(long accountId, long characterId, long minId = 0) {
-    Mail[] mails = Context.Mail.Where(mail => mail.ReceiverId == characterId || mail.ReceiverId == accountId)
+    // Add pagination
+    const int pageSize = 100;
+    Mail[] mails = Context.Mail
+        .Where(mail => mail.Id > minId)
+        .Where(mail => mail.ReceiverId == characterId || mail.ReceiverId == accountId)
+        .OrderBy(mail => mail.Id)
+        .Take(pageSize)
         .Where(mail => mail.Id > minId)
         .AsEnumerable()
         .Select<Model.Mail, Mail>(mail => mail)
         .ToArray();

88-100: Consider adding concurrency control for mail read status.

The implementation correctly handles both character and account-wide mails, but might be susceptible to race conditions during concurrent access.

Consider adding optimistic concurrency control:

 public Mail? MarkMailRead(long mailId, long accountId, long characterId) {
     Context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.TrackAll;
 
     Model.Mail? mail = Context.Mail.Find(characterId, mailId);
     if (mail == null) {
         mail = Context.Mail.Find(accountId, mailId);
         if (mail == null) {
             return null;
         }
     }
 
+    // Add concurrency token check
+    if (mail.ConcurrencyToken != expectedToken) {
+        throw new DbUpdateConcurrencyException("Mail was modified by another process");
+    }
+
     if (mail.ReadTime > DateTime.UnixEpoch) {
         return null;
     }

Line range hint 1-134: Consider separating account and character mail handling.

While the current implementation successfully adds account-wide mail support, consider extracting the mail handling logic into separate strategies for account and character mails to improve maintainability and reduce complexity.

This could be achieved by:

  1. Creating an interface IMailStrategy with implementations for AccountMailStrategy and CharacterMailStrategy
  2. Using the strategy pattern to handle the different mail types
  3. Moving the common logic to a base class

This would make the code more maintainable and easier to extend in the future.

Maple2.Database/Model/Mail.cs (1)

Line range hint 94-102: Consider adding an index for ReceiverId and handling data integrity

The removal of the foreign key constraint aligns with the new requirements, but there are some considerations:

  1. Performance: Consider adding an index on ReceiverId since it will be frequently queried:
 builder.HasKey(mail => new { mail.ReceiverId, mail.Id });
+builder.HasIndex(mail => mail.ReceiverId);
  1. Data Integrity: Since we've removed the foreign key constraint, consider adding application-level validation to ensure ReceiverId references valid accounts/characters:
 builder.Property(mail => mail.ReceiverId).IsRequired();
+// Add check constraint to ensure ReceiverId > 0
+builder.HasCheckConstraint("CK_Mail_ReceiverId", "[ReceiverId] > 0");
Maple2.Server.World/Containers/PlayerInfoLookup.cs (1)

60-62: Enhance XML documentation for better clarity.

While the inline comments explain the purpose, consider adding XML documentation to better describe the method's behavior, especially regarding:

  • The return value when multiple characters are online for the same account
  • The implications of the online-only filter
  • Usage guidelines for mail notifications

Consider adding this documentation:

+/// <summary>
+/// Retrieves the first online player found for the specified account ID from the cache.
+/// This method is specifically designed for account-wide mail notifications and should be used with caution.
+/// </summary>
+/// <param name="accountId">The account ID to search for</param>
+/// <returns>The first online PlayerInfo found for the account, or null if no online players are found</returns>
+/// <remarks>
+/// Note: If multiple characters from the same account are online, only the first one found will be returned.
+/// This method only searches the cache and will not query the database for offline players.
+/// </remarks>
Maple2.Server.Game/PacketHandlers/FunctionCubeHandler.cs (1)

217-226: Consider moving content mappings to configuration

The hardcoded mapping of quest tags to content IDs could become a maintenance burden. Consider moving these mappings to a configuration file or database table for easier maintenance.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 35f69e4 and d0ee64a.

⛔ Files ignored due to path filters (3)
  • Maple2.Server.World/Migrations/20241121023155_RemoveMailReceiverIdFK.Designer.cs is excluded by !Maple2.Server.World/Migrations/*
  • Maple2.Server.World/Migrations/20241121023155_RemoveMailReceiverIdFK.cs is excluded by !Maple2.Server.World/Migrations/*
  • Maple2.Server.World/Migrations/Ms2ContextModelSnapshot.cs is excluded by !Maple2.Server.World/Migrations/*
📒 Files selected for processing (8)
  • Maple2.Database/Model/Mail.cs (2 hunks)
  • Maple2.Database/Storage/Game/GameStorage.Mail.cs (4 hunks)
  • Maple2.Model/Game/Mail.cs (1 hunks)
  • Maple2.Server.Core/proto/sync.proto (1 hunks)
  • Maple2.Server.Game/Manager/MailManager.cs (6 hunks)
  • Maple2.Server.Game/PacketHandlers/FunctionCubeHandler.cs (1 hunks)
  • Maple2.Server.World/Containers/PlayerInfoLookup.cs (1 hunks)
  • Maple2.Server.World/Service/WorldService.Sync.cs (2 hunks)
✅ Files skipped from review due to trivial changes (1)
  • Maple2.Model/Game/Mail.cs
🔇 Additional comments (15)
Maple2.Server.Core/proto/sync.proto (2)

33-34: LGTM! Changes align with account-wide mail requirements.

The modifications to MailNotificationRequest correctly support the new account-wide mail functionality by:

  • Making character_id optional to remove the strict requirement
  • Adding account_id to support account-level mail visibility

33-34: Ensure server-side validation of mail visibility rules.

Since both character_id and account_id are optional in the proto definition, there's no protocol-level prevention of invalid states (both being 0). Please ensure proper server-side validation enforces the visibility rules:

  • If account_id is 0: mail is restricted to specific character_id
  • If character_id is 0: mail is accessible to all characters under the account
  • Both cannot be 0 simultaneously

Let's verify the server-side validation:

✅ Verification successful

Let's search for mail-related files and validation patterns to better understand how these fields are handled.


Let's examine the mail management implementation to understand how these IDs are validated.


Server-side validation is properly implemented for mail visibility rules

Based on the code analysis, the server-side validation is correctly implemented in multiple layers:

  1. In GameStorage.Mail.cs, the GetMail and GetAllMail methods enforce the visibility rules:

    • First checks for character-specific mail (mail.ReceiverId == characterId)
    • Then checks for account-wide mail (mail.ReceiverId == accountId)
    • Returns null if neither matches, preventing unauthorized access
  2. In WorldService.Sync.cs, there's explicit validation:

if (request.CharacterId <= 0 && request.AccountId <= 0) {
    throw new RpcException(new Status(StatusCode.InvalidArgument, "AccountId and CharacterId not specified"));
}
  1. The MailManager class handles all mail operations through these validated paths, ensuring the visibility rules are enforced throughout the application.
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for validation logic in mail-related files
rg -A 5 "account.*id.*0.*character.*id.*0|character.*id.*0.*account.*id.*0" 

# Look for mail visibility validation patterns
ast-grep --pattern 'if ($account_id == 0 || $character_id == 0) {
  $$$
}'

Length of output: 154


Script:

#!/bin/bash
# Search for files related to mail handling
fd -e cs -e proto "mail|Mail" --type f

# Look for validation patterns with character_id and account_id
rg -i "character.*id.*account.*id" -A 5

# Search for any validation checks in proto handlers
rg -i "validate.*mail|mail.*validate" -A 5

Length of output: 14764


Script:

#!/bin/bash
# Let's examine the mail-related files more closely
cat Maple2.Server.Game/Manager/MailManager.cs
cat Maple2.Database/Storage/Game/GameStorage.Mail.cs

# Also search for any mail validation or error handling
rg -i "mail.*error|invalid.*mail" -A 5

Length of output: 30641

Maple2.Server.World/Service/WorldService.Sync.cs (2)

88-91: Verify gRPC contract compatibility.

The logic to ensure a valid CharacterId is present is correct. However, since the PR objectives mention that updating the gRPC MailNotificationRequest will be handled in a future PR, let's verify that modifying the request object here is safe.

✅ Verification successful

Based on the search results, I can now generate the final response:

Modifying CharacterId in the request is safe and follows the contract

The gRPC contract in sync.proto explicitly defines character_id as optional, and all usages across the codebase properly handle this optionality:

  • The proto definition shows: optional int64 character_id = 2
  • The WorldService already validates for either CharacterId or AccountId
  • The ChannelService requires a valid CharacterId for session lookup

The current change ensures compatibility between these services by populating the required field when available.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if MailNotificationRequest is used elsewhere in the codebase
# and verify that modifying CharacterId is safe.

# Search for MailNotificationRequest usage
rg -A 5 "MailNotificationRequest"

# Search for proto definition
fd -e proto | xargs rg "message MailNotificationRequest"

Length of output: 9437


Line range hint 71-91: Verify mail visibility rules implementation.

The PR objectives specify important visibility rules:

  • If AccountId is 0, mail is restricted to specific CharacterId
  • If CharacterId is 0, mail is accessible to all characters under the account
  • Both values cannot be 0 simultaneously (which is correctly validated)

Please ensure these visibility rules are properly implemented in the channel service's MailNotification handler.

✅ Verification successful

Let me generate additional scripts to search for the mail visibility implementation in the channel service.


Let me search for more specific mail-related implementations.


Based on the code analysis, I can now generate the final response:

Mail visibility rules are correctly implemented in the database model and service layer.

The implementation aligns with the specified visibility rules:

  • The Mail model in both Maple2.Model and Maple2.Database explicitly supports dual-mode receivers through the ReceiverId field, documented as "Can be either AccountId or CharacterId"
  • The MailManager in the game service properly handles mail operations using both AccountId and CharacterId
  • The database queries in GetMail method use both AccountId and CharacterId parameters for proper filtering
  • The mail creation across various features (BlackMarket, Marriage, Guild, etc.) correctly sets the ReceiverId based on whether it's account-wide or character-specific
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check the channel service implementation for mail visibility rules

# Search for the MailNotification handler in channel service
ast-grep --pattern 'class $_ {
  $$$
  MailNotification($_, $_) {
    $$$
  }
  $$$
}'

# Search for any mail-related visibility checks
rg -A 10 "AccountId.*CharacterId|CharacterId.*AccountId"

Length of output: 38966


Script:

#!/bin/bash
# Search for mail-related code in channel service and database
ast-grep --pattern 'class $_ {
  $$$
  GetMail($_, $_, $_) {
    $$$
  }
  $$$
}'

# Search for mail database queries
rg -A 5 "GetMail.*AccountId.*CharacterId|GetMail.*CharacterId.*AccountId"

# Search for mail table definitions
rg -A 10 "CREATE TABLE.*mail|CREATE TABLE.*Mail"

Length of output: 1127


Script:

#!/bin/bash
# Search for mail database implementations and models
rg -A 10 "class MailManager|class Mail"

# Search for mail-related database operations
ast-grep --pattern 'class $_ {
  $$$
  DeleteMail($_, $_, $_) {
    $$$
  }
  $$$
}'

# Search for mail table access patterns
rg -A 10 "ReceiverId.*AccountId|ReceiverId.*CharacterId"

Length of output: 33169

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

12-19: LGTM! Verify mail retrieval order.

The implementation correctly handles both character-specific and account-wide mail retrieval, with proper null checks.

Let's verify the mail retrieval order and usage:

✅ Verification successful

Mail retrieval order and access pattern verified

The verification shows consistent mail retrieval patterns across the codebase:

  • All mail access goes through the GetMail method in GameStorage.Mail.cs
  • The retrieval order is consistently implemented: character-specific first, then account-wide
  • No direct database queries bypass this method, as all calls from MailManager use the proper GetMail interface
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for any direct database queries that might bypass this method
# and ensure consistent mail retrieval order across the codebase

rg -A 3 "Context\.Mail\.Find|GetMail" --type cs

Length of output: 2786

Maple2.Database/Model/Mail.cs (1)

Line range hint 1-102: Verify the impact of Mail model changes

Let's verify the usage of the Mail model in the codebase to ensure all queries and relationships are properly updated.

✅ Verification successful

Based on the search results, I can now provide a final response regarding the Mail model changes:

Mail model changes are safe and properly implemented

The changes to the Mail model are well-structured and maintain backward compatibility:

  • The ReceiverId field is used consistently throughout the codebase for both account and character IDs
  • The database schema properly handles the dual-purpose ReceiverId through composite primary key (ReceiverId, Id)
  • The foreign key constraint to Character table has been intentionally removed (see migration RemoveMailReceiverIdFK) to support the dual-purpose ID
  • All mail-related operations in the codebase properly handle both account-wide and character-specific mails through the storage layer
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for Mail model usage in queries and joins
rg -l "Join.*Mail" --type cs

# Search for potential places where Character foreign key was used
rg -l "Include.*Mail.*Character" --type cs

# Look for existing queries that might be affected by the dual-purpose ReceiverId
ast-grep --pattern 'from $_ in $_Mail
  where $_.ReceiverId == $_
  select $_'

Length of output: 156


Script:

#!/bin/bash
# Search for Mail model usage with more context
rg "Mail\." -A 5 -B 5 --type cs

# Search for ReceiverId usage in Mail-related code
rg "ReceiverId" -A 3 -B 3 --type cs

# Look for Mail-related database operations
ast-grep --pattern 'dbContext.$_Mail.$_'

Length of output: 163493

Maple2.Server.World/Containers/PlayerInfoLookup.cs (2)

63-65: 🛠️ Refactor suggestion

Consider performance and concurrency implications.

The current implementation has several potential concerns:

  1. Performance: Full scan of cache values for each call
  2. Race conditions: Cache values could change during iteration
  3. Multiple characters: Only returns first match arbitrarily

Consider these improvements:

 public PlayerInfo? TryGetByAccountId(long accountId) {
-    return cache.Values.FirstOrDefault(player => player.AccountId == accountId && player.Online);
+    // Create a secondary index for account lookups
+    private readonly ConcurrentDictionary<long, HashSet<long>> accountToCharacterIds = new();
+
+    // Update the index in the Update method
+    private void UpdateAccountIndex(PlayerInfo info) {
+        if (!accountToCharacterIds.TryGetValue(info.AccountId, out var characterIds)) {
+            characterIds = new HashSet<long>();
+            accountToCharacterIds[info.AccountId] = characterIds;
+        }
+        characterIds.Add(info.CharacterId);
+    }
+
+    public IEnumerable<PlayerInfo> GetOnlinePlayersByAccountId(long accountId) {
+        if (!accountToCharacterIds.TryGetValue(accountId, out var characterIds)) {
+            return Enumerable.Empty<PlayerInfo>();
+        }
+
+        return characterIds
+            .Select(charId => cache.TryGetValue(charId, out var player) ? player : null)
+            .Where(player => player != null && player.Online);
+    }
 }

This refactor:

  1. Improves lookup performance with a secondary index
  2. Returns all online characters for better visibility
  3. Reduces race conditions by using concurrent collections

Let's verify the impact of this change:


60-65: Verify mail notification behavior with multiple online characters.

The current implementation might miss notifying other online characters from the same account about mail updates.

Let's verify the mail notification flow:

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

228-239: Verify mail creation aligns with account-wide mail requirements

The implementation sets ReceiverId to ownerId which aligns with the PR objectives for account-wide mails. However, ensure this matches the visibility rules specified in issue #280:

  • If AccountId is set to 0, mail is restricted to specific CharacterId
  • If CharacterId is 0, mail is accessible to all characters under the account
✅ Verification successful

Mail implementation correctly handles account-wide visibility

Based on the code analysis, the mail system is correctly implemented for account-wide visibility:

  1. In Maple2.Database/Model/Mail.cs, the ReceiverId field is explicitly documented to handle both AccountId and CharacterId: public long ReceiverId { get; set; } // Can be either AccountId or CharacterId
  2. In the provided code snippet, setting ReceiverId = ownerId is correct as it follows the same pattern used throughout the codebase for system mails.
  3. The database schema and mail creation logic support this dual-purpose identifier approach, allowing mails to be associated with either an account or a specific character.
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if Mail class has both AccountId and CharacterId fields
ast-grep --pattern 'class Mail {
  $$$
  AccountId $_
  $$$
  CharacterId $_
  $$$
}'

Length of output: 87


Script:

#!/bin/bash
# Let's first find the Mail class definition
rg "class Mail" -A 20

# Also search for any Mail-related database operations
rg "CreateMail|InsertMail" -A 5

Length of output: 22032

Maple2.Server.Game/Manager/MailManager.cs (6)

52-52: Inclusion of AccountId in DeleteMail ensures proper mail deletion

The addition of session.AccountId to the db.DeleteMail method aligns with the new account-wide mail functionality. It ensures that mails are correctly deleted based on both the account and character identifiers.


67-67: Modification of GetMail to include AccountId enhances mail retrieval

By adding session.AccountId to the db.GetMail call, the system now accurately retrieves mails associated with the account, supporting both account-wide and character-specific mails.


87-87: Including AccountId in Collect method supports account-wide mail collection

The inclusion of session.AccountId in the db.GetMail call within the Collect method ensures that account-wide mails can be collected appropriately by the user.


104-105: Assignment of AccountId and CharacterId to local variables improves readability

Storing session.AccountId and session.CharacterId in local variables accountId and characterId enhances code readability and simplifies subsequent method calls within the Fetch method.


114-114: Updating mail fetching logic to support account-wide mails

Using db.GetAllMail(accountId, characterId, lastReceivedMail) allows the Fetch method to retrieve all relevant mails, whether they are account-wide or character-specific, thereby enhancing the user experience.


140-140: Enhancement of MarkMailRead with AccountId for accurate status updates

Adding session.AccountId to the db.MarkMailRead call ensures that the mail read status is accurately updated for mails associated with the account, maintaining consistency in mail state management.

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: 1

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between d0ee64a and bc85659.

📒 Files selected for processing (2)
  • Maple2.Database/Storage/Game/GameStorage.Mail.cs (1 hunks)
  • Maple2.Server.Game/Manager/MailManager.cs (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • Maple2.Server.Game/Manager/MailManager.cs
🔇 Additional comments (2)
Maple2.Database/Storage/Game/GameStorage.Mail.cs (2)

Line range hint 134-144: Update DeleteMail to support account-wide mails.

The method needs to be updated to handle both character-specific and account-wide mails, similar to the PR objectives.

Update the implementation:

-public bool DeleteMail(long mailId, long characterId) {
+public bool DeleteMail(long mailId, long characterId, long accountId) {
     Context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.TrackAll;
 
     Model.Mail? mail = Context.Mail.Find(characterId, mailId);
     if (mail == null) {
+        mail = Context.Mail.Find(accountId, mailId);
+        if (mail == null) {
             return false;
+        }
     }
 
+    // Validate that the requester has permission to delete this mail
+    if (mail.ReceiverId != characterId && mail.ReceiverId != accountId) {
+        return false;
+    }
+
     Context.Mail.Remove(mail);
     return SaveChanges();
 }

Line range hint 62-76: Implementation doesn't match PR objectives for account-wide mails.

The method only retrieves character-specific mails, but according to the PR objectives, it should also include account-wide mails.

Modify the implementation to include both character and account mails:

 public ICollection<Mail> GetAllMail(long characterId, long minId = 0) {
-    Mail[] mails = Context.Mail.Where(mail => mail.ReceiverId == characterId)
+    Mail[] mails = Context.Mail
+        .Where(mail => mail.ReceiverId == characterId || mail.ReceiverId == GetAccountId(characterId))
         .Where(mail => mail.Id > minId)
+        .Include(mail => mail.Items)  // Optimize by eager loading items
         .AsEnumerable()
         .Select<Model.Mail, Mail>(mail => mail)
         .ToArray();

-    foreach (Mail mail in mails) {
-        foreach (Item item in GetAllItems(mail.Id)) {
-            mail.Items.Add(item);
-        }
-    }

     return mails;
 }

Additionally, consider adding pagination to prevent performance issues with large mail volumes.

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

🧹 Outside diff range and nitpick comments (1)
Maple2.Database/Storage/Game/GameStorage.Mail.cs (1)

Line range hint 11-20: Update GetMail and DeleteMail to handle account-wide mails.

These methods need to be updated to handle mails that belong to an account. They should check both accountId and characterId when looking up mails.

Consider these changes:

-public Mail? GetMail(long mailId, long characterId) {
+public Mail? GetMail(long mailId, long accountId, long characterId) {
     Model.Mail? model = Context.Mail.Find(characterId, mailId);
+    if (model == null) {
+        model = Context.Mail.Find(accountId, mailId);
+    }
     if (model == null) {
         return null;
     }
     // ... rest of the method
 }

-public bool DeleteMail(long mailId, long characterId) {
+public bool DeleteMail(long mailId, long accountId, long characterId) {
     Context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.TrackAll;
 
     Model.Mail? mail = Context.Mail.Find(characterId, mailId);
+    if (mail == null) {
+        mail = Context.Mail.Find(accountId, mailId);
+    }
     if (mail == null) {
         return false;
     }
 
+    // Validate that the requester has permission to delete this mail
+    if (mail.ReceiverId != characterId && mail.ReceiverId != accountId) {
+        return false;
+    }
+
     Context.Mail.Remove(mail);
     return SaveChanges();
 }

Also applies to: 125-135

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between d0ee64a and 7b536ca.

📒 Files selected for processing (2)
  • Maple2.Database/Storage/Game/GameStorage.Mail.cs (1 hunks)
  • Maple2.Server.Game/Manager/MailManager.cs (2 hunks)
🔇 Additional comments (2)
Maple2.Server.Game/Manager/MailManager.cs (2)

104-105: LGTM: Clear variable declarations for account and character IDs.

The variables are appropriately extracted from the session object, making the code more readable and maintainable.


114-115: Verify the order of operations in mail fetching.

The implementation now binds account mails to the character before fetching all mails. Please ensure that:

  1. BindAccountMailsToCharacter properly handles the case where a mail is already bound
  2. GetAllMail correctly retrieves both character-specific and account-wide mails

Let's verify the implementation of these methods:

✅ Verification successful

Based on the code analysis, I can now provide a final response:

The mail binding and fetching implementation is correctly ordered and safe.

  1. BindAccountMailsToCharacter safely handles duplicates through transaction management and early exit if no mails exist:

    • Uses transaction tracking
    • Checks if there are any mails to bind (if (mails.Count == 0) { Commit(); return; })
    • Operates within a transaction scope
  2. GetAllMail properly retrieves all mails for a character:

    • Filters by receiver ID (mail.ReceiverId == characterId)
    • Includes pagination support through minId parameter
    • Loads associated items for each mail

The implementation ensures that account-wide mails are properly bound to the character before fetching, and the database operations are performed atomically within transactions.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for the implementation of the new methods
ast-grep --pattern 'BindAccountMailsToCharacter($_,$_)' 
ast-grep --pattern 'GetAllMail($_,$_)'

Length of output: 98


Script:

#!/bin/bash
# Let's try a broader search with ripgrep to find these method implementations
rg -A 10 "BindAccountMailsToCharacter|GetAllMail" --type cs

Length of output: 3014


Script:

#!/bin/bash
# Let's check if there's any duplicate handling or unique constraints in the mail binding
rg -A 5 "Mail\(" --type cs

# Also check for any mail-related database model definitions
rg -A 10 "class Mail" --type cs

Length of output: 30285

@AngeloTadeucci AngeloTadeucci merged commit afa87d0 into master Nov 22, 2024
@AngeloTadeucci AngeloTadeucci deleted the account-wide-mails branch November 22, 2024 21:46
@coderabbitai coderabbitai bot mentioned this pull request Apr 1, 2025
@coderabbitai coderabbitai bot mentioned this pull request Jun 24, 2025
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.

Account wide mails

2 participants

Comments