Table of Contents

Class CharacterMailService

Namespace
FishMMO.Database.Npgsql.Services
Assembly
FishMMO-DB.dll

Service for managing character mail in the database. Provides async operations for CRUD operations on character mail data. Implements execution strategies for automatic retry on transient database failures. Returns DatabaseResult for consistent, safe error handling.

public sealed class CharacterMailService : BaseService<CharacterMailEntity>, ICharacterMailService, ICountByKeyAction<long>, IDeleteByKeyVersionedAction<long>, IFetchCollectionByKeyAction<long, CharacterMailData>
Inheritance
CharacterMailService
Implements
Inherited Members

Remarks

This service manages character mail including:

  • Sending new mail with atomic INSERT operations
  • Mail deletion (individual and bulk by character)
  • Mail retrieval and count queries

All exceptions are classified by BaseService and mapped to DatabaseResult error codes (e.g., UNIQUE_VIOLATION, FOREIGN_KEY_VIOLATION, STALE_STATE, DATABASE_ERROR). Transient failures are retried automatically. Methods return DatabaseResult to provide structured error handling without throwing exceptions to calling code.

Constructors

CharacterMailService(INpgsqlDbContextFactory)

Initializes a new instance of the CharacterMailService class.

public CharacterMailService(INpgsqlDbContextFactory dbContextFactory)

Parameters

dbContextFactory INpgsqlDbContextFactory

Factory for creating database contexts.

Exceptions

ArgumentNullException

Thrown when dbContextFactory is null.

Methods

ClaimAttachmentAsync(long, long, long, CancellationToken)

Takes the attachment off one mail, returning what was on it.

public Task<DatabaseResult<CharacterMailAttachmentData?>> ClaimAttachmentAsync(long mailId, long characterId, long incomingVersion, CancellationToken cancellationToken = default)

Parameters

mailId long

The mail to claim from.

characterId long

The owning character ID, for authorization.

incomingVersion long

The authoritative, monotonic version for this write.

cancellationToken CancellationToken

Token to cancel the operation.

Returns

Task<DatabaseResult<CharacterMailAttachmentData?>>

The attachment as it stood before the clear, or no data when the mail had nothing attached, does not belong to this character, or has already been claimed.

Remarks

Read and clear are one statement. The attachment columns are zeroed and their previous values returned by the same UPDATE, whose WHERE additionally requires that something is still attached. Two claims racing on one mail therefore serialise on the row lock and only the first affects a row — the second matches nothing and returns no data. Fetching the mail and then clearing it would leave a window in which both reads see the same item and both callers grant it.

The caller grants what comes back. That ordering means a crash between the clear and the grant loses the attachment rather than duplicating it, which is the correct direction for an authoritative server — the same reasoning the merchant and corpse loot paths use.

CountAsync(long, CancellationToken)

Counts items for the given key.

public Task<DatabaseResult<int>> CountAsync(long characterId, CancellationToken cancellationToken = default)

Parameters

characterId long
cancellationToken CancellationToken

Token to cancel the operation.

Returns

Task<DatabaseResult<int>>

DeleteAsync(long, long, long, CancellationToken)

Deletes a specific mail message by its ID if incomingVersion is newer.

public Task<DatabaseResult> DeleteAsync(long mailId, long characterId, long incomingVersion, CancellationToken cancellationToken = default)

Parameters

mailId long

The mail ID to delete.

characterId long

The owning character ID (for authorization).

incomingVersion long

The authoritative, monotonic version for this delete operation.

cancellationToken CancellationToken

Token to cancel the operation.

Returns

Task<DatabaseResult>

A DatabaseResult indicating success or failure.

DeleteAsync(long, long, CancellationToken)

Deletes the entity identified by the given key if incomingVersion is newer.

public Task<DatabaseResult> DeleteAsync(long characterId, long incomingVersion, CancellationToken cancellationToken = default)

Parameters

characterId long
incomingVersion long

The authoritative, monotonic version for this delete operation.

cancellationToken CancellationToken

Token to cancel the operation.

Returns

Task<DatabaseResult>

FetchAsync(long, CancellationToken)

Fetches a collection of items for the given key.

public Task<DatabaseResult<IReadOnlyList<CharacterMailData>>> FetchAsync(long characterId, CancellationToken cancellationToken = default)

Parameters

characterId long
cancellationToken CancellationToken

Token to cancel the operation.

Returns

Task<DatabaseResult<IReadOnlyList<CharacterMailData>>>

SendAsync(long, string, long, string, string, int, int, uint, int, long, CancellationToken)

Sends a new mail message from one character to another.

public Task<DatabaseResult> SendAsync(long senderCharacterId, string senderName, long recipientCharacterId, string subject, string body, int itemAttachmentTemplateID, int itemAttachmentSeed, uint itemAttachmentAmount, int currencyAttachment, long incomingVersion, CancellationToken cancellationToken = default)

Parameters

senderCharacterId long

The sending character ID.

senderName string

The display name of the sender.

recipientCharacterId long

The recipient character ID.

subject string

The mail subject.

body string

The mail body text.

itemAttachmentTemplateID int

The attached item template ID (0 for none).

itemAttachmentSeed int

The attached item seed (0 for none).

itemAttachmentAmount uint

The attached item amount (0 for none).

currencyAttachment int
incomingVersion long

The authoritative, monotonic version for this persist operation.

cancellationToken CancellationToken

Token to cancel the operation.

Returns

Task<DatabaseResult>

A DatabaseResult indicating success or failure.