diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 7e4b65e3..9ea72c1a 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -42,6 +42,21 @@ Include in every request: - keep active-room indexing and keepalive behavior in `RoomDirectoryGrain` - treat `[KeepAlive]` as explicit infrastructure-only usage +## Orleans grain constraints +- Never use bare `catch { }` — always `catch (Exception ex)` with `ILogger`. +- Parallelize independent grain calls with `Task.WhenAll`, not sequential `await` in loops. +- Hoist repeated grain calls out of loops. +- Batch DB deletes with `WHERE ... IN (...)`, not per-entity `ExecuteDeleteAsync`. +- Use timer-flush for housekeeping writes (see `RoomPersistenceGrain`). +- No hardcoded limits in grains — pass from handlers via `IConfiguration`. +- Use tracked EF deletes when atomicity with inserts is required. +- Replace `.Ignore()` with a `LogAndForget` helper that logs faulted tasks. +- Cap in-memory per-event collections (message history, queues). +- One grain per responsibility — isolate heavy I/O into secondary grains (e.g. `RoomPersistenceGrain`). +- Use grain single-threading for concurrency safety (per-player `PurchaseGrain`, per-item `LimitedItemGrain`). No manual locks. +- Grains orchestrate their own outbound communication via `PlayerPresenceGrain.SendComposerAsync`. Callers do not send composers. +- All mutations to grain-owned data go through grain methods. No direct DB updates for grain-owned state. + ## Task routing hints - Handler task: use neighboring handler + `Turbo.Primitives/Orleans/GrainFactoryExtensions.cs`. - Grain task: use grain interface + snapshot/state types as primary references. diff --git a/AGENTS.md b/AGENTS.md index 77f14009..8056699a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -20,6 +20,7 @@ Activate the relevant skill checklist before editing code in that domain: - `grain-development` - Trigger: editing files under `Turbo.*\\Grains\\**` or `Turbo.Primitives/**/Grains/*.cs`. - Enforce: keep ownership boundaries, lifecycle rules, and snapshot/state coherence. + - Enforce: all rules in the **Orleans grain development rules** section below. - `session-presence-routing` - Trigger: touching session gateway, presence flow, room routing, outbound composer fan-out. - Enforce: player outbound via `PlayerPresenceGrain.SendComposerAsync`; no direct handler socket sends. @@ -64,13 +65,78 @@ Default output format: - Prefer deterministic handlers/services with clear guard clauses. - Preserve cancellation and async flow where it already exists. - Handle failure paths explicitly; do not ship happy-path-only changes. -- Avoid dead code, unused allocations, and broad catch blocks that hide errors. +- Avoid dead code, unused allocations, and broad catch blocks that hide errors (see **Orleans grain development rules** for specifics). - For revision compatibility work, prefer restoring/adding missing incoming message contracts in `Turbo.Primitives/Messages/Incoming/**` before mutating serializer/composer payload behavior. - Do not alter serializer/composer behavior by replacing real payload writes with placeholder constants (for example, unconditional `WriteInteger(0)`) unless explicitly requested. - If work references `Revision` parsers/serializers, edit the plugin repo path: - `../turbo-sample-plugin/TurboSamplePlugin/Revision/**` - Do not hallucinate those trees into `turbo-cloud`. +## Orleans grain development rules +These rules exist because every one of these mistakes has shipped and caused real issues. + +### Never swallow exceptions silently +Every bare `catch { }` hides a real bug path. Always use `catch (Exception ex)` and log it. +If a cross-grain notification fails silently, state goes asymmetric and nobody knows why. +- **Required**: inject `ILogger` into every grain that does cross-grain calls or DB work. +- **Forbidden**: bare `catch { }`, `catch (Exception) { }` without logging. + +### Parallelize independent grain calls +When checking status on N grains (e.g. online status for a friend list), do not `await` each one in a `foreach`. +Grain calls to different grains can run concurrently with `Task.WhenAll`. +- Sequential = O(n) round-trips. Parallel = O(1) wall time. +- Apply everywhere: activation hydration, search results, batch accept/deny. + +### Do not repeat identical grain calls in loops +If a grain method calls its own player's `GetSummaryAsync` inside a loop, hoist the call before the loop. +Same result every iteration = wasted round-trips. + +### Batch DB operations +Do not loop `ExecuteDeleteAsync` per entity. Use a single `WHERE ... IN (...)` query. +Same for composer fan-out: collect all updates, send once. + +### Use timer-based flush for housekeeping writes +Follow the `RoomPersistenceGrain` pattern: queue dirty state, flush with `RegisterGrainTimer` on interval, and flush on `OnDeactivateAsync`. +Do not issue per-event DB writes that block the grain turn. + +### Do not hardcode limits in grains +Handlers already read configuration values (e.g. `Turbo:FriendList:UserFriendLimit`) from `IConfiguration` and pass them to grains. +Magic numbers like `Take(50)`, `Take(20)`, or `maxIgnoreCapacity = 100` must come from configuration parameters on the grain interface method. +A 10,000 user hotel needs different tuning than a 10 player dev server. + +### Use tracked deletes for atomicity +`ExecuteDeleteAsync` commits immediately and bypasses the EF change tracker. +If a delete + insert must succeed or fail together, use `FirstOrDefaultAsync` + `Remove` so both go through one `SaveChangesAsync`. + +### Replace .Ignore() with a LogAndForget helper +Orleans `.Ignore()` makes cross-grain failures invisible. Use a `LogAndForget` extension that calls `ContinueWith(OnlyOnFaulted)` to log the exception. +Still fire-and-forget, but failures are visible in production logs. + +### Bound session/history collections +Any in-memory collection that grows per-message (e.g. conversation history) must have a configurable cap. +Without a cap, long-running sessions leak memory. + +### One grain per responsibility — isolate heavy I/O +Each major domain component should operate in its own grain. When a grain needs heavy I/O (DB writes, persistence flushes), delegate that work to a dedicated secondary grain so it does not block the primary grain's turn. +- Example: `RoomGrain` delegates furniture saves to `RoomPersistenceGrain`. The room grain stays responsive while persistence queues and flushes. +- Do not combine domain logic and persistence flushing in the same grain. + +### Use grain boundaries for thread safety +Orleans grains are single-threaded by design. Use this for concurrency-sensitive operations by giving each user their own grain for the operation. +- Example: each player gets a `PurchaseGrain` so catalog purchases are serialized per-player with no locks needed. +- Example: limited-edition items should use a dedicated grain (e.g. `LimitedItemGrain`) so concurrent buyers are safely serialized. +- Do not add manual locking (`lock`, `SemaphoreSlim`) inside grains — that fights the actor model. + +### Grains orchestrate their own outbound communication +When grain state changes (e.g. wallet balance updates), the grain itself sends the snapshot to `PlayerPresenceGrain.SendComposerAsync`. The caller that triggered the change does not pass or send the composer — the grain owns that responsibility. +- **Correct**: handler calls `grain.UpdateWalletAsync(...)` → grain updates state → grain calls `PlayerPresenceGrain.SendComposerAsync(...)`. +- **Wrong**: handler calls `grain.UpdateWalletAsync(...)` → handler builds composer → handler sends composer to player. + +### Do not mutate the database directly for grain-owned state +Grains may hold cached or in-memory state that will not reflect direct DB changes. All mutations to grain-owned data must go through the grain's methods, even when the player is offline. +- If a grain uses `[PersistentState]`, state is hydrated from the configured store (not DB) on activation. Direct DB edits will be overwritten by stale store data. +- Admin tools and external systems must call grain methods, not issue raw SQL/DB updates, for data that grains own. + ## Profile and grain flow constraints - Keep packet handlers orchestration-only: - validate input diff --git a/CONTEXT.md b/CONTEXT.md index 04373b7e..4904a2cd 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -51,6 +51,21 @@ - Lifecycle: - inactive grains are Orleans-managed and can deactivate automatically unless explicitly marked `[KeepAlive]`. +## Grain runtime patterns +- Every grain that does cross-grain calls or DB work must inject `ILogger` and log caught exceptions. No bare `catch { }`. +- Independent grain calls (e.g. checking online status for N friends) must use `Task.WhenAll`, not sequential `await` in a loop. +- Identical grain calls must not repeat inside loops — hoist before the loop. +- DB batch operations use single `WHERE ... IN (...)` queries, not per-entity `ExecuteDeleteAsync` loops. +- Housekeeping writes (e.g. delivered flags) follow the timer-flush pattern: queue dirty state, flush with `RegisterGrainTimer`, flush on `OnDeactivateAsync`. See `RoomPersistenceGrain` for reference. +- Do not hardcode limits (`Take(N)`, capacity constants) in grains. Pass them from handlers via `IConfiguration`. +- When a delete + insert must be atomic, use EF tracked operations (`Remove` + `SaveChangesAsync`), not `ExecuteDeleteAsync`. +- Replace `.Ignore()` on grain tasks with a `LogAndForget` helper that logs faulted continuations. +- In-memory collections that grow per-event (message history, queues) must have a configurable cap. +- One grain per responsibility: isolate heavy I/O (DB writes, persistence) into secondary grains so the primary domain grain stays responsive (e.g. `RoomGrain` → `RoomPersistenceGrain`). +- Use grain single-threading for concurrency safety: per-player `PurchaseGrain` for catalog buys, dedicated grains for limited-edition items. Do not add manual locks inside grains. +- Grains orchestrate their own outbound: when state changes, the grain sends the composer via `PlayerPresenceGrain.SendComposerAsync`. Callers do not build or send composers after calling a grain method. +- All mutations to grain-owned data must go through grain methods. Do not update the database directly — grains may hold cached state that will not reflect raw DB changes. + ## Placement rules - New host startup/wiring behavior: - `Turbo.Main/` (usually `Program.cs`, `Extensions/`, or `Console/`) diff --git a/Turbo.Database/Context/TurboDbContext.cs b/Turbo.Database/Context/TurboDbContext.cs index 0fc87e40..655b3829 100644 --- a/Turbo.Database/Context/TurboDbContext.cs +++ b/Turbo.Database/Context/TurboDbContext.cs @@ -1,6 +1,7 @@ using Microsoft.EntityFrameworkCore; using Turbo.Database.Entities.Catalog; using Turbo.Database.Entities.Furniture; +using Turbo.Database.Entities.Messenger; using Turbo.Database.Entities.Navigator; using Turbo.Database.Entities.Players; using Turbo.Database.Entities.Room; @@ -58,6 +59,18 @@ public class TurboDbContext(DbContextOptions options) public DbSet PlayerFavouriteRooms { get; init; } + public DbSet MessengerFriends { get; init; } + + public DbSet MessengerRequests { get; init; } + + public DbSet MessengerCategories { get; init; } + + public DbSet MessengerMessages { get; init; } + + public DbSet MessengerBlocked { get; init; } + + public DbSet MessengerIgnored { get; init; } + protected override void OnModelCreating(ModelBuilder mb) { base.OnModelCreating(mb); diff --git a/Turbo.Database/Entities/Messenger/MessengerBlockedEntity.cs b/Turbo.Database/Entities/Messenger/MessengerBlockedEntity.cs new file mode 100644 index 00000000..ba4e7928 --- /dev/null +++ b/Turbo.Database/Entities/Messenger/MessengerBlockedEntity.cs @@ -0,0 +1,22 @@ +using System.ComponentModel.DataAnnotations.Schema; +using Microsoft.EntityFrameworkCore; +using Turbo.Database.Entities.Players; + +namespace Turbo.Database.Entities.Messenger; + +[Table("messenger_blocked")] +[Index(nameof(PlayerEntityId), nameof(BlockedPlayerEntityId), IsUnique = true)] +public class MessengerBlockedEntity : TurboEntity +{ + [Column("player_id")] + public required int PlayerEntityId { get; set; } + + [Column("blocked_player_id")] + public required int BlockedPlayerEntityId { get; set; } + + [ForeignKey(nameof(PlayerEntityId))] + public required PlayerEntity PlayerEntity { get; set; } + + [ForeignKey(nameof(BlockedPlayerEntityId))] + public required PlayerEntity BlockedPlayerEntity { get; set; } +} diff --git a/Turbo.Database/Entities/Messenger/MessengerIgnoredEntity.cs b/Turbo.Database/Entities/Messenger/MessengerIgnoredEntity.cs new file mode 100644 index 00000000..f851e8cd --- /dev/null +++ b/Turbo.Database/Entities/Messenger/MessengerIgnoredEntity.cs @@ -0,0 +1,22 @@ +using System.ComponentModel.DataAnnotations.Schema; +using Microsoft.EntityFrameworkCore; +using Turbo.Database.Entities.Players; + +namespace Turbo.Database.Entities.Messenger; + +[Table("messenger_ignored")] +[Index(nameof(PlayerEntityId), nameof(IgnoredPlayerEntityId), IsUnique = true)] +public class MessengerIgnoredEntity : TurboEntity +{ + [Column("player_id")] + public required int PlayerEntityId { get; set; } + + [Column("ignored_player_id")] + public required int IgnoredPlayerEntityId { get; set; } + + [ForeignKey(nameof(PlayerEntityId))] + public required PlayerEntity PlayerEntity { get; set; } + + [ForeignKey(nameof(IgnoredPlayerEntityId))] + public required PlayerEntity IgnoredPlayerEntity { get; set; } +} diff --git a/Turbo.Database/Entities/Messenger/MessengerMessageEntity.cs b/Turbo.Database/Entities/Messenger/MessengerMessageEntity.cs new file mode 100644 index 00000000..517a4718 --- /dev/null +++ b/Turbo.Database/Entities/Messenger/MessengerMessageEntity.cs @@ -0,0 +1,33 @@ +using System; +using System.ComponentModel.DataAnnotations.Schema; +using Microsoft.EntityFrameworkCore; +using Turbo.Database.Entities.Players; + +namespace Turbo.Database.Entities.Messenger; + +[Table("messenger_messages")] +[Index(nameof(SenderPlayerEntityId), nameof(ReceiverPlayerEntityId), nameof(Timestamp))] +[Index(nameof(ReceiverPlayerEntityId), nameof(SenderPlayerEntityId), nameof(Timestamp))] +public class MessengerMessageEntity : TurboEntity +{ + [Column("sender_id")] + public required int SenderPlayerEntityId { get; set; } + + [Column("receiver_id")] + public required int ReceiverPlayerEntityId { get; set; } + + [Column("message")] + public required string Message { get; set; } + + [Column("timestamp")] + public required DateTime Timestamp { get; set; } + + [Column("delivered")] + public bool Delivered { get; set; } + + [ForeignKey(nameof(SenderPlayerEntityId))] + public required PlayerEntity SenderPlayerEntity { get; set; } + + [ForeignKey(nameof(ReceiverPlayerEntityId))] + public required PlayerEntity ReceiverPlayerEntity { get; set; } +} diff --git a/Turbo.Database/Entities/Players/PlayerEntity.cs b/Turbo.Database/Entities/Players/PlayerEntity.cs index 7c2a73b9..79afc271 100644 --- a/Turbo.Database/Entities/Players/PlayerEntity.cs +++ b/Turbo.Database/Entities/Players/PlayerEntity.cs @@ -86,4 +86,16 @@ public class PlayerEntity : TurboEntity [InverseProperty("PlayerEntity")] public List? RoomChatlogs { get; set; } + + [InverseProperty("PlayerEntity")] + public List? MessengerBlocked { get; set; } + + [InverseProperty("PlayerEntity")] + public List? MessengerIgnored { get; set; } + + [InverseProperty("SenderPlayerEntity")] + public List? MessengerMessagesSent { get; set; } + + [InverseProperty("ReceiverPlayerEntity")] + public List? MessengerMessagesReceived { get; set; } } diff --git a/Turbo.Database/Migrations/20260205185839_AddedModels.cs b/Turbo.Database/Migrations/20260205185839_AddedModels.cs index ac831a74..40167150 100644 --- a/Turbo.Database/Migrations/20260205185839_AddedModels.cs +++ b/Turbo.Database/Migrations/20260205185839_AddedModels.cs @@ -12,113 +12,119 @@ protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.DropIndex( name: "IX_furniture_definitions_sprite_id_type", - table: "furniture_definitions"); + table: "furniture_definitions" + ); - migrationBuilder.DropColumn( - name: "allow_editing", - table: "rooms"); + migrationBuilder.DropColumn(name: "allow_editing", table: "rooms"); - migrationBuilder.DropColumn( - name: "product_name", - table: "furniture_definitions"); + migrationBuilder.DropColumn(name: "product_name", table: "furniture_definitions"); - migrationBuilder.DropColumn( - name: "public_name", - table: "furniture_definitions"); + migrationBuilder.DropColumn(name: "public_name", table: "furniture_definitions"); - migrationBuilder.DropColumn( - name: "stuff_data", - table: "furniture"); + migrationBuilder.DropColumn(name: "stuff_data", table: "furniture"); migrationBuilder.RenameColumn( name: "users_max", table: "rooms", - newName: "players_max"); + newName: "players_max" + ); - migrationBuilder.RenameColumn( - name: "state", - table: "rooms", - newName: "door_mode"); + migrationBuilder.RenameColumn(name: "state", table: "rooms", newName: "door_mode"); migrationBuilder.RenameColumn( name: "chat_weight_type", table: "rooms", - newName: "chat_bubble_type"); + newName: "chat_bubble_type" + ); migrationBuilder.RenameColumn( name: "chat_protection_type", table: "rooms", - newName: "chat_flood_type"); + newName: "chat_flood_type" + ); migrationBuilder.RenameColumn( name: "z", table: "furniture_definitions", - newName: "stack_height"); + newName: "stack_height" + ); migrationBuilder.RenameColumn( name: "y", table: "furniture_definitions", - newName: "length"); + newName: "length" + ); migrationBuilder.RenameColumn( name: "x", table: "furniture_definitions", - newName: "width"); - - migrationBuilder.AlterColumn( - name: "ticket", - table: "security_tickets", - type: "varchar(512)", - maxLength: 512, - nullable: false, - oldClrType: typeof(string), - oldType: "varchar(255)") - .Annotation("MySql:CharSet", "utf8mb4") - .OldAnnotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.AlterColumn( - name: "ip_address", - table: "security_tickets", - type: "varchar(512)", - maxLength: 512, - nullable: false, - oldClrType: typeof(string), - oldType: "longtext") - .Annotation("MySql:CharSet", "utf8mb4") - .OldAnnotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.AlterColumn( - name: "password", - table: "rooms", - type: "varchar(512)", - maxLength: 512, - nullable: true, - oldClrType: typeof(string), - oldType: "longtext", - oldNullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - .OldAnnotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.AlterColumn( - name: "name", - table: "rooms", - type: "varchar(512)", - maxLength: 512, - nullable: false, - oldClrType: typeof(string), - oldType: "longtext") - .Annotation("MySql:CharSet", "utf8mb4") - .OldAnnotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.AlterColumn( - name: "description", - table: "rooms", - type: "varchar(512)", - maxLength: 512, - nullable: true, - oldClrType: typeof(string), - oldType: "longtext", - oldNullable: true) + newName: "width" + ); + + migrationBuilder + .AlterColumn( + name: "ticket", + table: "security_tickets", + type: "varchar(512)", + maxLength: 512, + nullable: false, + oldClrType: typeof(string), + oldType: "varchar(255)" + ) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder + .AlterColumn( + name: "ip_address", + table: "security_tickets", + type: "varchar(512)", + maxLength: 512, + nullable: false, + oldClrType: typeof(string), + oldType: "longtext" + ) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder + .AlterColumn( + name: "password", + table: "rooms", + type: "varchar(512)", + maxLength: 512, + nullable: true, + oldClrType: typeof(string), + oldType: "longtext", + oldNullable: true + ) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder + .AlterColumn( + name: "name", + table: "rooms", + type: "varchar(512)", + maxLength: 512, + nullable: false, + oldClrType: typeof(string), + oldType: "longtext" + ) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder + .AlterColumn( + name: "description", + table: "rooms", + type: "varchar(512)", + maxLength: 512, + nullable: true, + oldClrType: typeof(string), + oldType: "longtext", + oldNullable: true + ) .Annotation("MySql:CharSet", "utf8mb4") .OldAnnotation("MySql:CharSet", "utf8mb4"); @@ -130,196 +136,231 @@ protected override void Up(MigrationBuilder migrationBuilder) defaultValue: false, oldClrType: typeof(bool), oldType: "tinyint(1)", - oldDefaultValue: true); - - migrationBuilder.AlterColumn( - name: "name", - table: "room_models", - type: "varchar(512)", - maxLength: 512, - nullable: false, - oldClrType: typeof(string), - oldType: "varchar(255)") - .Annotation("MySql:CharSet", "utf8mb4") - .OldAnnotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.AlterColumn( - name: "model", - table: "room_models", - type: "varchar(512)", - maxLength: 512, - nullable: false, - oldClrType: typeof(string), - oldType: "longtext") - .Annotation("MySql:CharSet", "utf8mb4") - .OldAnnotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.AlterColumn( - name: "name", - table: "players", - type: "varchar(512)", - maxLength: 512, - nullable: false, - oldClrType: typeof(string), - oldType: "varchar(255)") - .Annotation("MySql:CharSet", "utf8mb4") - .OldAnnotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.AlterColumn( - name: "motto", - table: "players", - type: "varchar(512)", - maxLength: 512, - nullable: true, - oldClrType: typeof(string), - oldType: "longtext", - oldNullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - .OldAnnotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.AlterColumn( - name: "type", - table: "player_currencies", - type: "varchar(512)", - maxLength: 512, - nullable: false, - oldClrType: typeof(string), - oldType: "varchar(255)") - .Annotation("MySql:CharSet", "utf8mb4") - .OldAnnotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.AlterColumn( - name: "badge_code", - table: "player_badges", - type: "varchar(512)", - maxLength: 512, - nullable: false, - oldClrType: typeof(string), - oldType: "varchar(255)") - .Annotation("MySql:CharSet", "utf8mb4") - .OldAnnotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.AlterColumn( - name: "user_agent", - table: "performance_logs", - type: "varchar(512)", - maxLength: 512, - nullable: false, - oldClrType: typeof(string), - oldType: "longtext") - .Annotation("MySql:CharSet", "utf8mb4") - .OldAnnotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.AlterColumn( - name: "os", - table: "performance_logs", - type: "varchar(512)", - maxLength: 512, - nullable: false, - oldClrType: typeof(string), - oldType: "longtext") - .Annotation("MySql:CharSet", "utf8mb4") - .OldAnnotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.AlterColumn( - name: "flash_version", - table: "performance_logs", - type: "varchar(512)", - maxLength: 512, - nullable: false, - oldClrType: typeof(string), - oldType: "longtext") - .Annotation("MySql:CharSet", "utf8mb4") - .OldAnnotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.AlterColumn( - name: "browser", - table: "performance_logs", - type: "varchar(512)", - maxLength: 512, - nullable: false, - oldClrType: typeof(string), - oldType: "longtext") - .Annotation("MySql:CharSet", "utf8mb4") - .OldAnnotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.AlterColumn( - name: "search_code", - table: "navigator_top_level_contexts", - type: "varchar(512)", - maxLength: 512, - nullable: false, - oldClrType: typeof(string), - oldType: "varchar(255)") - .Annotation("MySql:CharSet", "utf8mb4") - .OldAnnotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.AlterColumn( - name: "name", - table: "navigator_flatcats", - type: "varchar(512)", - maxLength: 512, - nullable: false, - oldClrType: typeof(string), - oldType: "longtext") - .Annotation("MySql:CharSet", "utf8mb4") - .OldAnnotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.AlterColumn( - name: "global_category", - table: "navigator_flatcats", - type: "varchar(512)", - maxLength: 512, - nullable: true, - oldClrType: typeof(string), - oldType: "longtext", - oldNullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - .OldAnnotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.AlterColumn( - name: "automatic_category", - table: "navigator_flatcats", - type: "varchar(512)", - maxLength: 512, - nullable: true, - oldClrType: typeof(string), - oldType: "longtext", - oldNullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - .OldAnnotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.AlterColumn( - name: "name", - table: "navigator_eventcats", - type: "varchar(512)", - maxLength: 512, - nullable: false, - oldClrType: typeof(string), - oldType: "longtext") - .Annotation("MySql:CharSet", "utf8mb4") - .OldAnnotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.AlterColumn( - name: "name", - table: "messenger_categories", - type: "varchar(512)", - maxLength: 512, - nullable: false, - oldClrType: typeof(string), - oldType: "varchar(255)") - .Annotation("MySql:CharSet", "utf8mb4") - .OldAnnotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.AlterColumn( - name: "extra_data", - table: "furniture_definitions", - type: "varchar(512)", - maxLength: 512, - nullable: true, - oldClrType: typeof(string), - oldType: "longtext", - oldNullable: true) + oldDefaultValue: true + ); + + migrationBuilder + .AlterColumn( + name: "name", + table: "room_models", + type: "varchar(512)", + maxLength: 512, + nullable: false, + oldClrType: typeof(string), + oldType: "varchar(255)" + ) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder + .AlterColumn( + name: "model", + table: "room_models", + type: "varchar(512)", + maxLength: 512, + nullable: false, + oldClrType: typeof(string), + oldType: "longtext" + ) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder + .AlterColumn( + name: "name", + table: "players", + type: "varchar(512)", + maxLength: 512, + nullable: false, + oldClrType: typeof(string), + oldType: "varchar(255)" + ) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder + .AlterColumn( + name: "motto", + table: "players", + type: "varchar(512)", + maxLength: 512, + nullable: true, + oldClrType: typeof(string), + oldType: "longtext", + oldNullable: true + ) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder + .AlterColumn( + name: "type", + table: "player_currencies", + type: "varchar(512)", + maxLength: 512, + nullable: false, + oldClrType: typeof(string), + oldType: "varchar(255)" + ) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder + .AlterColumn( + name: "badge_code", + table: "player_badges", + type: "varchar(512)", + maxLength: 512, + nullable: false, + oldClrType: typeof(string), + oldType: "varchar(255)" + ) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder + .AlterColumn( + name: "user_agent", + table: "performance_logs", + type: "varchar(512)", + maxLength: 512, + nullable: false, + oldClrType: typeof(string), + oldType: "longtext" + ) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder + .AlterColumn( + name: "os", + table: "performance_logs", + type: "varchar(512)", + maxLength: 512, + nullable: false, + oldClrType: typeof(string), + oldType: "longtext" + ) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder + .AlterColumn( + name: "flash_version", + table: "performance_logs", + type: "varchar(512)", + maxLength: 512, + nullable: false, + oldClrType: typeof(string), + oldType: "longtext" + ) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder + .AlterColumn( + name: "browser", + table: "performance_logs", + type: "varchar(512)", + maxLength: 512, + nullable: false, + oldClrType: typeof(string), + oldType: "longtext" + ) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder + .AlterColumn( + name: "search_code", + table: "navigator_top_level_contexts", + type: "varchar(512)", + maxLength: 512, + nullable: false, + oldClrType: typeof(string), + oldType: "varchar(255)" + ) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder + .AlterColumn( + name: "name", + table: "navigator_flatcats", + type: "varchar(512)", + maxLength: 512, + nullable: false, + oldClrType: typeof(string), + oldType: "longtext" + ) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder + .AlterColumn( + name: "global_category", + table: "navigator_flatcats", + type: "varchar(512)", + maxLength: 512, + nullable: true, + oldClrType: typeof(string), + oldType: "longtext", + oldNullable: true + ) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder + .AlterColumn( + name: "automatic_category", + table: "navigator_flatcats", + type: "varchar(512)", + maxLength: 512, + nullable: true, + oldClrType: typeof(string), + oldType: "longtext", + oldNullable: true + ) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder + .AlterColumn( + name: "name", + table: "navigator_eventcats", + type: "varchar(512)", + maxLength: 512, + nullable: false, + oldClrType: typeof(string), + oldType: "longtext" + ) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder + .AlterColumn( + name: "name", + table: "messenger_categories", + type: "varchar(512)", + maxLength: 512, + nullable: false, + oldClrType: typeof(string), + oldType: "varchar(255)" + ) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder + .AlterColumn( + name: "extra_data", + table: "furniture_definitions", + type: "varchar(512)", + maxLength: 512, + nullable: true, + oldClrType: typeof(string), + oldType: "longtext", + oldNullable: true + ) .Annotation("MySql:CharSet", "utf8mb4") .OldAnnotation("MySql:CharSet", "utf8mb4"); @@ -328,34 +369,41 @@ protected override void Up(MigrationBuilder migrationBuilder) table: "furniture_definitions", type: "int", nullable: false, - defaultValue: 1); - - migrationBuilder.AddColumn( - name: "name", - table: "furniture_definitions", - type: "varchar(512)", - maxLength: 512, - nullable: false, - defaultValue: "") + defaultValue: 1 + ); + + migrationBuilder + .AddColumn( + name: "name", + table: "furniture_definitions", + type: "varchar(512)", + maxLength: 512, + nullable: false, + defaultValue: "" + ) .Annotation("MySql:CharSet", "utf8mb4"); - migrationBuilder.AddColumn( - name: "extra_data", - table: "furniture", - type: "varchar(512)", - maxLength: 512, - nullable: true) + migrationBuilder + .AddColumn( + name: "extra_data", + table: "furniture", + type: "varchar(512)", + maxLength: 512, + nullable: true + ) .Annotation("MySql:CharSet", "utf8mb4"); - migrationBuilder.AlterColumn( - name: "extra_param", - table: "catalog_products", - type: "varchar(512)", - maxLength: 512, - nullable: true, - oldClrType: typeof(string), - oldType: "longtext", - oldNullable: true) + migrationBuilder + .AlterColumn( + name: "extra_param", + table: "catalog_products", + type: "varchar(512)", + maxLength: 512, + nullable: true, + oldClrType: typeof(string), + oldType: "longtext", + oldNullable: true + ) .Annotation("MySql:CharSet", "utf8mb4") .OldAnnotation("MySql:CharSet", "utf8mb4"); @@ -364,16 +412,19 @@ protected override void Up(MigrationBuilder migrationBuilder) table: "catalog_pages", type: "int", nullable: false, - defaultValue: 0); + defaultValue: 0 + ); - migrationBuilder.AlterColumn( - name: "localization_id", - table: "catalog_offers", - type: "varchar(512)", - maxLength: 512, - nullable: false, - oldClrType: typeof(string), - oldType: "longtext") + migrationBuilder + .AlterColumn( + name: "localization_id", + table: "catalog_offers", + type: "varchar(512)", + maxLength: 512, + nullable: false, + oldClrType: typeof(string), + oldType: "longtext" + ) .Annotation("MySql:CharSet", "utf8mb4") .OldAnnotation("MySql:CharSet", "utf8mb4"); @@ -381,7 +432,8 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "IX_furniture_definitions_sprite_id_type_category", table: "furniture_definitions", columns: new[] { "sprite_id", "type", "category" }, - unique: true); + unique: true + ); } /// @@ -389,113 +441,119 @@ protected override void Down(MigrationBuilder migrationBuilder) { migrationBuilder.DropIndex( name: "IX_furniture_definitions_sprite_id_type_category", - table: "furniture_definitions"); + table: "furniture_definitions" + ); - migrationBuilder.DropColumn( - name: "category", - table: "furniture_definitions"); + migrationBuilder.DropColumn(name: "category", table: "furniture_definitions"); - migrationBuilder.DropColumn( - name: "name", - table: "furniture_definitions"); + migrationBuilder.DropColumn(name: "name", table: "furniture_definitions"); - migrationBuilder.DropColumn( - name: "extra_data", - table: "furniture"); + migrationBuilder.DropColumn(name: "extra_data", table: "furniture"); - migrationBuilder.DropColumn( - name: "sort_order", - table: "catalog_pages"); + migrationBuilder.DropColumn(name: "sort_order", table: "catalog_pages"); migrationBuilder.RenameColumn( name: "players_max", table: "rooms", - newName: "users_max"); + newName: "users_max" + ); - migrationBuilder.RenameColumn( - name: "door_mode", - table: "rooms", - newName: "state"); + migrationBuilder.RenameColumn(name: "door_mode", table: "rooms", newName: "state"); migrationBuilder.RenameColumn( name: "chat_flood_type", table: "rooms", - newName: "chat_protection_type"); + newName: "chat_protection_type" + ); migrationBuilder.RenameColumn( name: "chat_bubble_type", table: "rooms", - newName: "chat_weight_type"); + newName: "chat_weight_type" + ); migrationBuilder.RenameColumn( name: "width", table: "furniture_definitions", - newName: "x"); + newName: "x" + ); migrationBuilder.RenameColumn( name: "stack_height", table: "furniture_definitions", - newName: "z"); + newName: "z" + ); migrationBuilder.RenameColumn( name: "length", table: "furniture_definitions", - newName: "y"); - - migrationBuilder.AlterColumn( - name: "ticket", - table: "security_tickets", - type: "varchar(255)", - nullable: false, - oldClrType: typeof(string), - oldType: "varchar(512)", - oldMaxLength: 512) - .Annotation("MySql:CharSet", "utf8mb4") - .OldAnnotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.AlterColumn( - name: "ip_address", - table: "security_tickets", - type: "longtext", - nullable: false, - oldClrType: typeof(string), - oldType: "varchar(512)", - oldMaxLength: 512) - .Annotation("MySql:CharSet", "utf8mb4") - .OldAnnotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.AlterColumn( - name: "password", - table: "rooms", - type: "longtext", - nullable: true, - oldClrType: typeof(string), - oldType: "varchar(512)", - oldMaxLength: 512, - oldNullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - .OldAnnotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.AlterColumn( - name: "name", - table: "rooms", - type: "longtext", - nullable: false, - oldClrType: typeof(string), - oldType: "varchar(512)", - oldMaxLength: 512) - .Annotation("MySql:CharSet", "utf8mb4") - .OldAnnotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.AlterColumn( - name: "description", - table: "rooms", - type: "longtext", - nullable: true, - oldClrType: typeof(string), - oldType: "varchar(512)", - oldMaxLength: 512, - oldNullable: true) + newName: "y" + ); + + migrationBuilder + .AlterColumn( + name: "ticket", + table: "security_tickets", + type: "varchar(255)", + nullable: false, + oldClrType: typeof(string), + oldType: "varchar(512)", + oldMaxLength: 512 + ) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder + .AlterColumn( + name: "ip_address", + table: "security_tickets", + type: "longtext", + nullable: false, + oldClrType: typeof(string), + oldType: "varchar(512)", + oldMaxLength: 512 + ) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder + .AlterColumn( + name: "password", + table: "rooms", + type: "longtext", + nullable: true, + oldClrType: typeof(string), + oldType: "varchar(512)", + oldMaxLength: 512, + oldNullable: true + ) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder + .AlterColumn( + name: "name", + table: "rooms", + type: "longtext", + nullable: false, + oldClrType: typeof(string), + oldType: "varchar(512)", + oldMaxLength: 512 + ) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder + .AlterColumn( + name: "description", + table: "rooms", + type: "longtext", + nullable: true, + oldClrType: typeof(string), + oldType: "varchar(512)", + oldMaxLength: 512, + oldNullable: true + ) .Annotation("MySql:CharSet", "utf8mb4") .OldAnnotation("MySql:CharSet", "utf8mb4"); @@ -507,247 +565,293 @@ protected override void Down(MigrationBuilder migrationBuilder) defaultValue: true, oldClrType: typeof(bool), oldType: "tinyint(1)", - oldDefaultValue: false); + oldDefaultValue: false + ); migrationBuilder.AddColumn( name: "allow_editing", table: "rooms", type: "tinyint(1)", nullable: true, - defaultValue: true); - - migrationBuilder.AlterColumn( - name: "name", - table: "room_models", - type: "varchar(255)", - nullable: false, - oldClrType: typeof(string), - oldType: "varchar(512)", - oldMaxLength: 512) - .Annotation("MySql:CharSet", "utf8mb4") - .OldAnnotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.AlterColumn( - name: "model", - table: "room_models", - type: "longtext", - nullable: false, - oldClrType: typeof(string), - oldType: "varchar(512)", - oldMaxLength: 512) - .Annotation("MySql:CharSet", "utf8mb4") - .OldAnnotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.AlterColumn( - name: "name", - table: "players", - type: "varchar(255)", - nullable: false, - oldClrType: typeof(string), - oldType: "varchar(512)", - oldMaxLength: 512) - .Annotation("MySql:CharSet", "utf8mb4") - .OldAnnotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.AlterColumn( - name: "motto", - table: "players", - type: "longtext", - nullable: true, - oldClrType: typeof(string), - oldType: "varchar(512)", - oldMaxLength: 512, - oldNullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - .OldAnnotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.AlterColumn( - name: "type", - table: "player_currencies", - type: "varchar(255)", - nullable: false, - oldClrType: typeof(string), - oldType: "varchar(512)", - oldMaxLength: 512) - .Annotation("MySql:CharSet", "utf8mb4") - .OldAnnotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.AlterColumn( - name: "badge_code", - table: "player_badges", - type: "varchar(255)", - nullable: false, - oldClrType: typeof(string), - oldType: "varchar(512)", - oldMaxLength: 512) - .Annotation("MySql:CharSet", "utf8mb4") - .OldAnnotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.AlterColumn( - name: "user_agent", - table: "performance_logs", - type: "longtext", - nullable: false, - oldClrType: typeof(string), - oldType: "varchar(512)", - oldMaxLength: 512) - .Annotation("MySql:CharSet", "utf8mb4") - .OldAnnotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.AlterColumn( - name: "os", - table: "performance_logs", - type: "longtext", - nullable: false, - oldClrType: typeof(string), - oldType: "varchar(512)", - oldMaxLength: 512) - .Annotation("MySql:CharSet", "utf8mb4") - .OldAnnotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.AlterColumn( - name: "flash_version", - table: "performance_logs", - type: "longtext", - nullable: false, - oldClrType: typeof(string), - oldType: "varchar(512)", - oldMaxLength: 512) - .Annotation("MySql:CharSet", "utf8mb4") - .OldAnnotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.AlterColumn( - name: "browser", - table: "performance_logs", - type: "longtext", - nullable: false, - oldClrType: typeof(string), - oldType: "varchar(512)", - oldMaxLength: 512) - .Annotation("MySql:CharSet", "utf8mb4") - .OldAnnotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.AlterColumn( - name: "search_code", - table: "navigator_top_level_contexts", - type: "varchar(255)", - nullable: false, - oldClrType: typeof(string), - oldType: "varchar(512)", - oldMaxLength: 512) - .Annotation("MySql:CharSet", "utf8mb4") - .OldAnnotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.AlterColumn( - name: "name", - table: "navigator_flatcats", - type: "longtext", - nullable: false, - oldClrType: typeof(string), - oldType: "varchar(512)", - oldMaxLength: 512) - .Annotation("MySql:CharSet", "utf8mb4") - .OldAnnotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.AlterColumn( - name: "global_category", - table: "navigator_flatcats", - type: "longtext", - nullable: true, - oldClrType: typeof(string), - oldType: "varchar(512)", - oldMaxLength: 512, - oldNullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - .OldAnnotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.AlterColumn( - name: "automatic_category", - table: "navigator_flatcats", - type: "longtext", - nullable: true, - oldClrType: typeof(string), - oldType: "varchar(512)", - oldMaxLength: 512, - oldNullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - .OldAnnotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.AlterColumn( - name: "name", - table: "navigator_eventcats", - type: "longtext", - nullable: false, - oldClrType: typeof(string), - oldType: "varchar(512)", - oldMaxLength: 512) - .Annotation("MySql:CharSet", "utf8mb4") - .OldAnnotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.AlterColumn( - name: "name", - table: "messenger_categories", - type: "varchar(255)", - nullable: false, - oldClrType: typeof(string), - oldType: "varchar(512)", - oldMaxLength: 512) - .Annotation("MySql:CharSet", "utf8mb4") - .OldAnnotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.AlterColumn( - name: "extra_data", - table: "furniture_definitions", - type: "longtext", - nullable: true, - oldClrType: typeof(string), - oldType: "varchar(512)", - oldMaxLength: 512, - oldNullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - .OldAnnotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.AddColumn( - name: "product_name", - table: "furniture_definitions", - type: "longtext", - nullable: false) + defaultValue: true + ); + + migrationBuilder + .AlterColumn( + name: "name", + table: "room_models", + type: "varchar(255)", + nullable: false, + oldClrType: typeof(string), + oldType: "varchar(512)", + oldMaxLength: 512 + ) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder + .AlterColumn( + name: "model", + table: "room_models", + type: "longtext", + nullable: false, + oldClrType: typeof(string), + oldType: "varchar(512)", + oldMaxLength: 512 + ) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder + .AlterColumn( + name: "name", + table: "players", + type: "varchar(255)", + nullable: false, + oldClrType: typeof(string), + oldType: "varchar(512)", + oldMaxLength: 512 + ) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder + .AlterColumn( + name: "motto", + table: "players", + type: "longtext", + nullable: true, + oldClrType: typeof(string), + oldType: "varchar(512)", + oldMaxLength: 512, + oldNullable: true + ) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder + .AlterColumn( + name: "type", + table: "player_currencies", + type: "varchar(255)", + nullable: false, + oldClrType: typeof(string), + oldType: "varchar(512)", + oldMaxLength: 512 + ) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder + .AlterColumn( + name: "badge_code", + table: "player_badges", + type: "varchar(255)", + nullable: false, + oldClrType: typeof(string), + oldType: "varchar(512)", + oldMaxLength: 512 + ) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder + .AlterColumn( + name: "user_agent", + table: "performance_logs", + type: "longtext", + nullable: false, + oldClrType: typeof(string), + oldType: "varchar(512)", + oldMaxLength: 512 + ) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder + .AlterColumn( + name: "os", + table: "performance_logs", + type: "longtext", + nullable: false, + oldClrType: typeof(string), + oldType: "varchar(512)", + oldMaxLength: 512 + ) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder + .AlterColumn( + name: "flash_version", + table: "performance_logs", + type: "longtext", + nullable: false, + oldClrType: typeof(string), + oldType: "varchar(512)", + oldMaxLength: 512 + ) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder + .AlterColumn( + name: "browser", + table: "performance_logs", + type: "longtext", + nullable: false, + oldClrType: typeof(string), + oldType: "varchar(512)", + oldMaxLength: 512 + ) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder + .AlterColumn( + name: "search_code", + table: "navigator_top_level_contexts", + type: "varchar(255)", + nullable: false, + oldClrType: typeof(string), + oldType: "varchar(512)", + oldMaxLength: 512 + ) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder + .AlterColumn( + name: "name", + table: "navigator_flatcats", + type: "longtext", + nullable: false, + oldClrType: typeof(string), + oldType: "varchar(512)", + oldMaxLength: 512 + ) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder + .AlterColumn( + name: "global_category", + table: "navigator_flatcats", + type: "longtext", + nullable: true, + oldClrType: typeof(string), + oldType: "varchar(512)", + oldMaxLength: 512, + oldNullable: true + ) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder + .AlterColumn( + name: "automatic_category", + table: "navigator_flatcats", + type: "longtext", + nullable: true, + oldClrType: typeof(string), + oldType: "varchar(512)", + oldMaxLength: 512, + oldNullable: true + ) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder + .AlterColumn( + name: "name", + table: "navigator_eventcats", + type: "longtext", + nullable: false, + oldClrType: typeof(string), + oldType: "varchar(512)", + oldMaxLength: 512 + ) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder + .AlterColumn( + name: "name", + table: "messenger_categories", + type: "varchar(255)", + nullable: false, + oldClrType: typeof(string), + oldType: "varchar(512)", + oldMaxLength: 512 + ) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder + .AlterColumn( + name: "extra_data", + table: "furniture_definitions", + type: "longtext", + nullable: true, + oldClrType: typeof(string), + oldType: "varchar(512)", + oldMaxLength: 512, + oldNullable: true + ) + .Annotation("MySql:CharSet", "utf8mb4") + .OldAnnotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder + .AddColumn( + name: "product_name", + table: "furniture_definitions", + type: "longtext", + nullable: false + ) .Annotation("MySql:CharSet", "utf8mb4"); - migrationBuilder.AddColumn( - name: "public_name", - table: "furniture_definitions", - type: "longtext", - nullable: false) + migrationBuilder + .AddColumn( + name: "public_name", + table: "furniture_definitions", + type: "longtext", + nullable: false + ) .Annotation("MySql:CharSet", "utf8mb4"); - migrationBuilder.AddColumn( - name: "stuff_data", - table: "furniture", - type: "longtext", - nullable: true) + migrationBuilder + .AddColumn( + name: "stuff_data", + table: "furniture", + type: "longtext", + nullable: true + ) .Annotation("MySql:CharSet", "utf8mb4"); - migrationBuilder.AlterColumn( - name: "extra_param", - table: "catalog_products", - type: "longtext", - nullable: true, - oldClrType: typeof(string), - oldType: "varchar(512)", - oldMaxLength: 512, - oldNullable: true) + migrationBuilder + .AlterColumn( + name: "extra_param", + table: "catalog_products", + type: "longtext", + nullable: true, + oldClrType: typeof(string), + oldType: "varchar(512)", + oldMaxLength: 512, + oldNullable: true + ) .Annotation("MySql:CharSet", "utf8mb4") .OldAnnotation("MySql:CharSet", "utf8mb4"); - migrationBuilder.AlterColumn( - name: "localization_id", - table: "catalog_offers", - type: "longtext", - nullable: false, - oldClrType: typeof(string), - oldType: "varchar(512)", - oldMaxLength: 512) + migrationBuilder + .AlterColumn( + name: "localization_id", + table: "catalog_offers", + type: "longtext", + nullable: false, + oldClrType: typeof(string), + oldType: "varchar(512)", + oldMaxLength: 512 + ) .Annotation("MySql:CharSet", "utf8mb4") .OldAnnotation("MySql:CharSet", "utf8mb4"); @@ -755,7 +859,8 @@ protected override void Down(MigrationBuilder migrationBuilder) name: "IX_furniture_definitions_sprite_id_type", table: "furniture_definitions", columns: new[] { "sprite_id", "type" }, - unique: true); + unique: true + ); } } } diff --git a/Turbo.Database/Migrations/20260208101510_InitialCreate.cs b/Turbo.Database/Migrations/20260208101510_InitialCreate.cs index 0345f068..e39ffdae 100644 --- a/Turbo.Database/Migrations/20260208101510_InitialCreate.cs +++ b/Turbo.Database/Migrations/20260208101510_InitialCreate.cs @@ -12,1290 +12,2127 @@ public partial class InitialCreate : Migration /// protected override void Up(MigrationBuilder migrationBuilder) { - migrationBuilder.AlterDatabase() + migrationBuilder.AlterDatabase().Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder + .CreateTable( + name: "catalog_pages", + columns: table => new + { + id = table + .Column(type: "int", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.IdentityColumn + ), + parent_id = table.Column(type: "int", nullable: true), + localization = table + .Column(type: "varchar(50)", maxLength: 50, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + name = table + .Column(type: "varchar(50)", maxLength: 50, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + icon = table.Column(type: "int", nullable: false, defaultValue: 0), + layout = table + .Column( + type: "varchar(50)", + maxLength: 50, + nullable: false, + defaultValue: "default_3x3" + ) + .Annotation("MySql:CharSet", "utf8mb4"), + image_data = table + .Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + text_data = table + .Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + sort_order = table.Column( + type: "int", + nullable: false, + defaultValue: 0 + ), + visible = table.Column( + type: "tinyint(1)", + nullable: false, + defaultValue: true + ), + created_at = table + .Column(type: "datetime(6)", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.IdentityColumn + ), + updated_at = table + .Column(type: "datetime(6)", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.ComputedColumn + ), + deleted_at = table + .Column(type: "datetime(6)", nullable: true) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.ComputedColumn + ), + }, + constraints: table => + { + table.PrimaryKey("PK_catalog_pages", x => x.id); + table.ForeignKey( + name: "FK_catalog_pages_catalog_pages_parent_id", + column: x => x.parent_id, + principalTable: "catalog_pages", + principalColumn: "id" + ); + } + ) .Annotation("MySql:CharSet", "utf8mb4"); - migrationBuilder.CreateTable( - name: "catalog_pages", - columns: table => new - { - id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - parent_id = table.Column(type: "int", nullable: true), - localization = table.Column(type: "varchar(50)", maxLength: 50, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - name = table.Column(type: "varchar(50)", maxLength: 50, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - icon = table.Column(type: "int", nullable: false, defaultValue: 0), - layout = table.Column(type: "varchar(50)", maxLength: 50, nullable: false, defaultValue: "default_3x3") - .Annotation("MySql:CharSet", "utf8mb4"), - image_data = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - text_data = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - sort_order = table.Column(type: "int", nullable: false, defaultValue: 0), - visible = table.Column(type: "tinyint(1)", nullable: false, defaultValue: true), - created_at = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - updated_at = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn), - deleted_at = table.Column(type: "datetime(6)", nullable: true) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn) - }, - constraints: table => - { - table.PrimaryKey("PK_catalog_pages", x => x.id); - table.ForeignKey( - name: "FK_catalog_pages_catalog_pages_parent_id", - column: x => x.parent_id, - principalTable: "catalog_pages", - principalColumn: "id"); - }) + migrationBuilder + .CreateTable( + name: "currency_types", + columns: table => new + { + id = table + .Column(type: "int", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.IdentityColumn + ), + name = table + .Column(type: "varchar(512)", maxLength: 512, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + type = table.Column(type: "int", nullable: false), + activity_point_type = table.Column(type: "int", nullable: true), + enabled = table.Column( + type: "tinyint(1)", + nullable: false, + defaultValue: true + ), + created_at = table + .Column(type: "datetime(6)", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.IdentityColumn + ), + updated_at = table + .Column(type: "datetime(6)", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.ComputedColumn + ), + deleted_at = table + .Column(type: "datetime(6)", nullable: true) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.ComputedColumn + ), + }, + constraints: table => + { + table.PrimaryKey("PK_currency_types", x => x.id); + } + ) .Annotation("MySql:CharSet", "utf8mb4"); - migrationBuilder.CreateTable( - name: "currency_types", - columns: table => new - { - id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - name = table.Column(type: "varchar(512)", maxLength: 512, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - type = table.Column(type: "int", nullable: false), - activity_point_type = table.Column(type: "int", nullable: true), - enabled = table.Column(type: "tinyint(1)", nullable: false, defaultValue: true), - created_at = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - updated_at = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn), - deleted_at = table.Column(type: "datetime(6)", nullable: true) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn) - }, - constraints: table => - { - table.PrimaryKey("PK_currency_types", x => x.id); - }) + migrationBuilder + .CreateTable( + name: "furniture_definitions", + columns: table => new + { + id = table + .Column(type: "int", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.IdentityColumn + ), + sprite_id = table.Column(type: "int", nullable: false), + name = table + .Column(type: "varchar(512)", maxLength: 512, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + type = table.Column(type: "int", nullable: false, defaultValue: 0), + category = table.Column(type: "int", nullable: false, defaultValue: 1), + logic = table + .Column( + type: "varchar(50)", + maxLength: 50, + nullable: false, + defaultValue: "none" + ) + .Annotation("MySql:CharSet", "utf8mb4"), + total_states = table.Column( + type: "int", + nullable: false, + defaultValue: 0 + ), + width = table.Column(type: "int", nullable: false, defaultValue: 0), + length = table.Column(type: "int", nullable: false, defaultValue: 1), + stack_height = table.Column( + type: "double(10,3)", + nullable: false, + defaultValue: 0.0 + ), + can_stack = table.Column( + type: "tinyint(1)", + nullable: false, + defaultValue: true + ), + can_walk = table.Column( + type: "tinyint(1)", + nullable: false, + defaultValue: false + ), + can_sit = table.Column( + type: "tinyint(1)", + nullable: false, + defaultValue: false + ), + can_lay = table.Column( + type: "tinyint(1)", + nullable: false, + defaultValue: false + ), + can_recycle = table.Column( + type: "tinyint(1)", + nullable: false, + defaultValue: false + ), + can_trade = table.Column( + type: "tinyint(1)", + nullable: false, + defaultValue: true + ), + can_group = table.Column( + type: "tinyint(1)", + nullable: false, + defaultValue: true + ), + can_sell = table.Column( + type: "tinyint(1)", + nullable: false, + defaultValue: true + ), + usage_policy = table.Column( + type: "int", + nullable: false, + defaultValue: 1 + ), + extra_data = table + .Column(type: "varchar(512)", maxLength: 512, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + created_at = table + .Column(type: "datetime(6)", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.IdentityColumn + ), + updated_at = table + .Column(type: "datetime(6)", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.ComputedColumn + ), + deleted_at = table + .Column(type: "datetime(6)", nullable: true) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.ComputedColumn + ), + }, + constraints: table => + { + table.PrimaryKey("PK_furniture_definitions", x => x.id); + } + ) .Annotation("MySql:CharSet", "utf8mb4"); - migrationBuilder.CreateTable( - name: "furniture_definitions", - columns: table => new - { - id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - sprite_id = table.Column(type: "int", nullable: false), - name = table.Column(type: "varchar(512)", maxLength: 512, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - type = table.Column(type: "int", nullable: false, defaultValue: 0), - category = table.Column(type: "int", nullable: false, defaultValue: 1), - logic = table.Column(type: "varchar(50)", maxLength: 50, nullable: false, defaultValue: "none") - .Annotation("MySql:CharSet", "utf8mb4"), - total_states = table.Column(type: "int", nullable: false, defaultValue: 0), - width = table.Column(type: "int", nullable: false, defaultValue: 0), - length = table.Column(type: "int", nullable: false, defaultValue: 1), - stack_height = table.Column(type: "double(10,3)", nullable: false, defaultValue: 0.0), - can_stack = table.Column(type: "tinyint(1)", nullable: false, defaultValue: true), - can_walk = table.Column(type: "tinyint(1)", nullable: false, defaultValue: false), - can_sit = table.Column(type: "tinyint(1)", nullable: false, defaultValue: false), - can_lay = table.Column(type: "tinyint(1)", nullable: false, defaultValue: false), - can_recycle = table.Column(type: "tinyint(1)", nullable: false, defaultValue: false), - can_trade = table.Column(type: "tinyint(1)", nullable: false, defaultValue: true), - can_group = table.Column(type: "tinyint(1)", nullable: false, defaultValue: true), - can_sell = table.Column(type: "tinyint(1)", nullable: false, defaultValue: true), - usage_policy = table.Column(type: "int", nullable: false, defaultValue: 1), - extra_data = table.Column(type: "varchar(512)", maxLength: 512, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - created_at = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - updated_at = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn), - deleted_at = table.Column(type: "datetime(6)", nullable: true) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn) - }, - constraints: table => - { - table.PrimaryKey("PK_furniture_definitions", x => x.id); - }) + migrationBuilder + .CreateTable( + name: "navigator_eventcats", + columns: table => new + { + id = table + .Column(type: "int", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.IdentityColumn + ), + name = table + .Column(type: "varchar(512)", maxLength: 512, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + visible = table.Column(type: "tinyint(1)", nullable: false), + created_at = table + .Column(type: "datetime(6)", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.IdentityColumn + ), + updated_at = table + .Column(type: "datetime(6)", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.ComputedColumn + ), + deleted_at = table + .Column(type: "datetime(6)", nullable: true) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.ComputedColumn + ), + }, + constraints: table => + { + table.PrimaryKey("PK_navigator_eventcats", x => x.id); + } + ) .Annotation("MySql:CharSet", "utf8mb4"); - migrationBuilder.CreateTable( - name: "navigator_eventcats", - columns: table => new - { - id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - name = table.Column(type: "varchar(512)", maxLength: 512, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - visible = table.Column(type: "tinyint(1)", nullable: false), - created_at = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - updated_at = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn), - deleted_at = table.Column(type: "datetime(6)", nullable: true) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn) - }, - constraints: table => - { - table.PrimaryKey("PK_navigator_eventcats", x => x.id); - }) + migrationBuilder + .CreateTable( + name: "navigator_flatcats", + columns: table => new + { + id = table + .Column(type: "int", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.IdentityColumn + ), + name = table + .Column(type: "varchar(512)", maxLength: 512, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + visible = table.Column( + type: "tinyint(1)", + nullable: false, + defaultValue: true + ), + automatic = table.Column( + type: "tinyint(1)", + nullable: false, + defaultValue: true + ), + automatic_category = table + .Column(type: "varchar(512)", maxLength: 512, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + global_category = table + .Column(type: "varchar(512)", maxLength: 512, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + staff_only = table.Column( + type: "tinyint(1)", + nullable: false, + defaultValue: false + ), + min_rank = table.Column(type: "int", nullable: false, defaultValue: 1), + order_num = table.Column( + type: "int", + nullable: false, + defaultValue: 0 + ), + created_at = table + .Column(type: "datetime(6)", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.IdentityColumn + ), + updated_at = table + .Column(type: "datetime(6)", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.ComputedColumn + ), + deleted_at = table + .Column(type: "datetime(6)", nullable: true) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.ComputedColumn + ), + }, + constraints: table => + { + table.PrimaryKey("PK_navigator_flatcats", x => x.id); + } + ) .Annotation("MySql:CharSet", "utf8mb4"); - migrationBuilder.CreateTable( - name: "navigator_flatcats", - columns: table => new - { - id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - name = table.Column(type: "varchar(512)", maxLength: 512, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - visible = table.Column(type: "tinyint(1)", nullable: false, defaultValue: true), - automatic = table.Column(type: "tinyint(1)", nullable: false, defaultValue: true), - automatic_category = table.Column(type: "varchar(512)", maxLength: 512, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - global_category = table.Column(type: "varchar(512)", maxLength: 512, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - staff_only = table.Column(type: "tinyint(1)", nullable: false, defaultValue: false), - min_rank = table.Column(type: "int", nullable: false, defaultValue: 1), - order_num = table.Column(type: "int", nullable: false, defaultValue: 0), - created_at = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - updated_at = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn), - deleted_at = table.Column(type: "datetime(6)", nullable: true) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn) - }, - constraints: table => - { - table.PrimaryKey("PK_navigator_flatcats", x => x.id); - }) + migrationBuilder + .CreateTable( + name: "navigator_top_level_contexts", + columns: table => new + { + id = table + .Column(type: "int", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.IdentityColumn + ), + search_code = table + .Column(type: "varchar(512)", maxLength: 512, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + visible = table.Column( + type: "tinyint(1)", + nullable: false, + defaultValue: true + ), + order_num = table.Column( + type: "int", + nullable: false, + defaultValue: 0 + ), + created_at = table + .Column(type: "datetime(6)", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.IdentityColumn + ), + updated_at = table + .Column(type: "datetime(6)", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.ComputedColumn + ), + deleted_at = table + .Column(type: "datetime(6)", nullable: true) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.ComputedColumn + ), + }, + constraints: table => + { + table.PrimaryKey("PK_navigator_top_level_contexts", x => x.id); + } + ) .Annotation("MySql:CharSet", "utf8mb4"); - migrationBuilder.CreateTable( - name: "navigator_top_level_contexts", - columns: table => new - { - id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - search_code = table.Column(type: "varchar(512)", maxLength: 512, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - visible = table.Column(type: "tinyint(1)", nullable: false, defaultValue: true), - order_num = table.Column(type: "int", nullable: false, defaultValue: 0), - created_at = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - updated_at = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn), - deleted_at = table.Column(type: "datetime(6)", nullable: true) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn) - }, - constraints: table => - { - table.PrimaryKey("PK_navigator_top_level_contexts", x => x.id); - }) + migrationBuilder + .CreateTable( + name: "performance_logs", + columns: table => new + { + id = table + .Column(type: "int", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.IdentityColumn + ), + elapsed_time = table.Column(type: "int", nullable: false), + user_agent = table + .Column(type: "varchar(512)", maxLength: 512, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + flash_version = table + .Column(type: "varchar(512)", maxLength: 512, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + os = table + .Column(type: "varchar(512)", maxLength: 512, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + browser = table + .Column(type: "varchar(512)", maxLength: 512, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + is_debugger = table.Column(type: "tinyint(1)", nullable: false), + memory_usage = table.Column(type: "int", nullable: false), + garbage_collections = table.Column(type: "int", nullable: false), + average_frame_rate = table.Column(type: "int", nullable: false), + ip_address = table + .Column(type: "varchar(45)", maxLength: 45, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + }, + constraints: table => + { + table.PrimaryKey("PK_performance_logs", x => x.id); + } + ) .Annotation("MySql:CharSet", "utf8mb4"); - migrationBuilder.CreateTable( - name: "performance_logs", - columns: table => new - { - id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - elapsed_time = table.Column(type: "int", nullable: false), - user_agent = table.Column(type: "varchar(512)", maxLength: 512, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - flash_version = table.Column(type: "varchar(512)", maxLength: 512, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - os = table.Column(type: "varchar(512)", maxLength: 512, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - browser = table.Column(type: "varchar(512)", maxLength: 512, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - is_debugger = table.Column(type: "tinyint(1)", nullable: false), - memory_usage = table.Column(type: "int", nullable: false), - garbage_collections = table.Column(type: "int", nullable: false), - average_frame_rate = table.Column(type: "int", nullable: false), - ip_address = table.Column(type: "varchar(45)", maxLength: 45, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK_performance_logs", x => x.id); - }) + migrationBuilder + .CreateTable( + name: "player_chat_styles", + columns: table => new + { + id = table + .Column(type: "int", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.IdentityColumn + ), + client_style_id = table.Column( + type: "int", + nullable: false, + defaultValue: 0 + ), + created_at = table + .Column(type: "datetime(6)", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.IdentityColumn + ), + updated_at = table + .Column(type: "datetime(6)", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.ComputedColumn + ), + deleted_at = table + .Column(type: "datetime(6)", nullable: true) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.ComputedColumn + ), + }, + constraints: table => + { + table.PrimaryKey("PK_player_chat_styles", x => x.id); + } + ) .Annotation("MySql:CharSet", "utf8mb4"); - migrationBuilder.CreateTable( - name: "player_chat_styles", - columns: table => new - { - id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - client_style_id = table.Column(type: "int", nullable: false, defaultValue: 0), - created_at = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - updated_at = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn), - deleted_at = table.Column(type: "datetime(6)", nullable: true) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn) - }, - constraints: table => - { - table.PrimaryKey("PK_player_chat_styles", x => x.id); - }) + migrationBuilder + .CreateTable( + name: "players", + columns: table => new + { + id = table + .Column(type: "int", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.IdentityColumn + ), + name = table + .Column(type: "varchar(512)", maxLength: 512, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + motto = table + .Column(type: "varchar(512)", maxLength: 512, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + figure = table + .Column( + type: "varchar(100)", + maxLength: 100, + nullable: false, + defaultValue: "hr-115-42.hd-195-19.ch-3030-82.lg-275-1408.fa-1201.ca-1804-64" + ) + .Annotation("MySql:CharSet", "utf8mb4"), + gender = table.Column(type: "int", nullable: false, defaultValue: 0), + status = table.Column(type: "int", nullable: false, defaultValue: 0), + perk_flags = table.Column( + type: "int", + nullable: false, + defaultValue: 0 + ), + room_chat_style_id = table.Column(type: "int", nullable: true), + created_at = table + .Column(type: "datetime(6)", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.IdentityColumn + ), + updated_at = table + .Column(type: "datetime(6)", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.ComputedColumn + ), + deleted_at = table + .Column(type: "datetime(6)", nullable: true) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.ComputedColumn + ), + }, + constraints: table => + { + table.PrimaryKey("PK_players", x => x.id); + } + ) .Annotation("MySql:CharSet", "utf8mb4"); - migrationBuilder.CreateTable( - name: "players", - columns: table => new - { - id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - name = table.Column(type: "varchar(512)", maxLength: 512, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - motto = table.Column(type: "varchar(512)", maxLength: 512, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - figure = table.Column(type: "varchar(100)", maxLength: 100, nullable: false, defaultValue: "hr-115-42.hd-195-19.ch-3030-82.lg-275-1408.fa-1201.ca-1804-64") - .Annotation("MySql:CharSet", "utf8mb4"), - gender = table.Column(type: "int", nullable: false, defaultValue: 0), - status = table.Column(type: "int", nullable: false, defaultValue: 0), - perk_flags = table.Column(type: "int", nullable: false, defaultValue: 0), - room_chat_style_id = table.Column(type: "int", nullable: true), - created_at = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - updated_at = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn), - deleted_at = table.Column(type: "datetime(6)", nullable: true) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn) - }, - constraints: table => - { - table.PrimaryKey("PK_players", x => x.id); - }) + migrationBuilder + .CreateTable( + name: "room_models", + columns: table => new + { + id = table + .Column(type: "int", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.IdentityColumn + ), + name = table + .Column(type: "varchar(512)", maxLength: 512, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + model = table + .Column(type: "longtext", maxLength: 512, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + door_x = table.Column(type: "int", nullable: false, defaultValue: 0), + door_y = table.Column(type: "int", nullable: false, defaultValue: 0), + door_rotation = table.Column( + type: "int", + nullable: false, + defaultValue: 0 + ), + enabled = table.Column( + type: "tinyint(1)", + nullable: false, + defaultValue: true + ), + custom = table.Column( + type: "tinyint(1)", + nullable: false, + defaultValue: false + ), + created_at = table + .Column(type: "datetime(6)", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.IdentityColumn + ), + updated_at = table + .Column(type: "datetime(6)", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.ComputedColumn + ), + deleted_at = table + .Column(type: "datetime(6)", nullable: true) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.ComputedColumn + ), + }, + constraints: table => + { + table.PrimaryKey("PK_room_models", x => x.id); + } + ) .Annotation("MySql:CharSet", "utf8mb4"); - migrationBuilder.CreateTable( - name: "room_models", - columns: table => new - { - id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - name = table.Column(type: "varchar(512)", maxLength: 512, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - model = table.Column(type: "longtext", maxLength: 512, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - door_x = table.Column(type: "int", nullable: false, defaultValue: 0), - door_y = table.Column(type: "int", nullable: false, defaultValue: 0), - door_rotation = table.Column(type: "int", nullable: false, defaultValue: 0), - enabled = table.Column(type: "tinyint(1)", nullable: false, defaultValue: true), - custom = table.Column(type: "tinyint(1)", nullable: false, defaultValue: false), - created_at = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - updated_at = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn), - deleted_at = table.Column(type: "datetime(6)", nullable: true) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn) - }, - constraints: table => - { - table.PrimaryKey("PK_room_models", x => x.id); - }) + migrationBuilder + .CreateTable( + name: "catalog_offers", + columns: table => new + { + id = table + .Column(type: "int", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.IdentityColumn + ), + page_id = table.Column(type: "int", nullable: false), + localization_id = table + .Column(type: "varchar(512)", maxLength: 512, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + cost_credits = table.Column( + type: "int", + nullable: false, + defaultValue: 0 + ), + cost_currency = table.Column( + type: "int", + nullable: false, + defaultValue: 0 + ), + currency_type_id = table.Column(type: "int", nullable: true), + can_gift = table.Column( + type: "tinyint(1)", + nullable: false, + defaultValue: true + ), + can_bundle = table.Column( + type: "tinyint(1)", + nullable: false, + defaultValue: true + ), + club_level = table.Column( + type: "int", + nullable: false, + defaultValue: 0 + ), + visible = table.Column( + type: "tinyint(1)", + nullable: false, + defaultValue: true + ), + created_at = table + .Column(type: "datetime(6)", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.IdentityColumn + ), + updated_at = table + .Column(type: "datetime(6)", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.ComputedColumn + ), + deleted_at = table + .Column(type: "datetime(6)", nullable: true) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.ComputedColumn + ), + }, + constraints: table => + { + table.PrimaryKey("PK_catalog_offers", x => x.id); + table.ForeignKey( + name: "FK_catalog_offers_catalog_pages_page_id", + column: x => x.page_id, + principalTable: "catalog_pages", + principalColumn: "id", + onDelete: ReferentialAction.Cascade + ); + table.ForeignKey( + name: "FK_catalog_offers_currency_types_currency_type_id", + column: x => x.currency_type_id, + principalTable: "currency_types", + principalColumn: "id" + ); + } + ) .Annotation("MySql:CharSet", "utf8mb4"); - migrationBuilder.CreateTable( - name: "catalog_offers", - columns: table => new - { - id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - page_id = table.Column(type: "int", nullable: false), - localization_id = table.Column(type: "varchar(512)", maxLength: 512, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - cost_credits = table.Column(type: "int", nullable: false, defaultValue: 0), - cost_currency = table.Column(type: "int", nullable: false, defaultValue: 0), - currency_type_id = table.Column(type: "int", nullable: true), - can_gift = table.Column(type: "tinyint(1)", nullable: false, defaultValue: true), - can_bundle = table.Column(type: "tinyint(1)", nullable: false, defaultValue: true), - club_level = table.Column(type: "int", nullable: false, defaultValue: 0), - visible = table.Column(type: "tinyint(1)", nullable: false, defaultValue: true), - created_at = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - updated_at = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn), - deleted_at = table.Column(type: "datetime(6)", nullable: true) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn) - }, - constraints: table => - { - table.PrimaryKey("PK_catalog_offers", x => x.id); - table.ForeignKey( - name: "FK_catalog_offers_catalog_pages_page_id", - column: x => x.page_id, - principalTable: "catalog_pages", - principalColumn: "id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK_catalog_offers_currency_types_currency_type_id", - column: x => x.currency_type_id, - principalTable: "currency_types", - principalColumn: "id"); - }) + migrationBuilder + .CreateTable( + name: "messenger_categories", + columns: table => new + { + id = table + .Column(type: "int", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.IdentityColumn + ), + player_id = table.Column(type: "int", nullable: false), + name = table + .Column(type: "varchar(512)", maxLength: 512, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + created_at = table + .Column(type: "datetime(6)", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.IdentityColumn + ), + updated_at = table + .Column(type: "datetime(6)", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.ComputedColumn + ), + deleted_at = table + .Column(type: "datetime(6)", nullable: true) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.ComputedColumn + ), + }, + constraints: table => + { + table.PrimaryKey("PK_messenger_categories", x => x.id); + table.ForeignKey( + name: "FK_messenger_categories_players_player_id", + column: x => x.player_id, + principalTable: "players", + principalColumn: "id", + onDelete: ReferentialAction.Cascade + ); + } + ) .Annotation("MySql:CharSet", "utf8mb4"); - migrationBuilder.CreateTable( - name: "messenger_categories", - columns: table => new - { - id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - player_id = table.Column(type: "int", nullable: false), - name = table.Column(type: "varchar(512)", maxLength: 512, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - created_at = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - updated_at = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn), - deleted_at = table.Column(type: "datetime(6)", nullable: true) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn) - }, - constraints: table => - { - table.PrimaryKey("PK_messenger_categories", x => x.id); - table.ForeignKey( - name: "FK_messenger_categories_players_player_id", - column: x => x.player_id, - principalTable: "players", - principalColumn: "id", - onDelete: ReferentialAction.Cascade); - }) + migrationBuilder + .CreateTable( + name: "messenger_requests", + columns: table => new + { + id = table + .Column(type: "int", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.IdentityColumn + ), + player_id = table.Column(type: "int", nullable: false), + requested_id = table.Column(type: "int", nullable: false), + created_at = table + .Column(type: "datetime(6)", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.IdentityColumn + ), + updated_at = table + .Column(type: "datetime(6)", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.ComputedColumn + ), + deleted_at = table + .Column(type: "datetime(6)", nullable: true) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.ComputedColumn + ), + }, + constraints: table => + { + table.PrimaryKey("PK_messenger_requests", x => x.id); + table.ForeignKey( + name: "FK_messenger_requests_players_player_id", + column: x => x.player_id, + principalTable: "players", + principalColumn: "id", + onDelete: ReferentialAction.Cascade + ); + table.ForeignKey( + name: "FK_messenger_requests_players_requested_id", + column: x => x.requested_id, + principalTable: "players", + principalColumn: "id", + onDelete: ReferentialAction.Cascade + ); + } + ) .Annotation("MySql:CharSet", "utf8mb4"); - migrationBuilder.CreateTable( - name: "messenger_requests", - columns: table => new - { - id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - player_id = table.Column(type: "int", nullable: false), - requested_id = table.Column(type: "int", nullable: false), - created_at = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - updated_at = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn), - deleted_at = table.Column(type: "datetime(6)", nullable: true) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn) - }, - constraints: table => - { - table.PrimaryKey("PK_messenger_requests", x => x.id); - table.ForeignKey( - name: "FK_messenger_requests_players_player_id", - column: x => x.player_id, - principalTable: "players", - principalColumn: "id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK_messenger_requests_players_requested_id", - column: x => x.requested_id, - principalTable: "players", - principalColumn: "id", - onDelete: ReferentialAction.Cascade); - }) + migrationBuilder + .CreateTable( + name: "player_badges", + columns: table => new + { + id = table + .Column(type: "int", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.IdentityColumn + ), + player_id = table.Column(type: "int", nullable: false), + badge_code = table + .Column(type: "varchar(512)", maxLength: 512, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + slot_id = table.Column(type: "int", nullable: true, defaultValue: 0), + created_at = table + .Column(type: "datetime(6)", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.IdentityColumn + ), + updated_at = table + .Column(type: "datetime(6)", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.ComputedColumn + ), + deleted_at = table + .Column(type: "datetime(6)", nullable: true) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.ComputedColumn + ), + }, + constraints: table => + { + table.PrimaryKey("PK_player_badges", x => x.id); + table.ForeignKey( + name: "FK_player_badges_players_player_id", + column: x => x.player_id, + principalTable: "players", + principalColumn: "id", + onDelete: ReferentialAction.Cascade + ); + } + ) .Annotation("MySql:CharSet", "utf8mb4"); - migrationBuilder.CreateTable( - name: "player_badges", - columns: table => new - { - id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - player_id = table.Column(type: "int", nullable: false), - badge_code = table.Column(type: "varchar(512)", maxLength: 512, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - slot_id = table.Column(type: "int", nullable: true, defaultValue: 0), - created_at = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - updated_at = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn), - deleted_at = table.Column(type: "datetime(6)", nullable: true) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn) - }, - constraints: table => - { - table.PrimaryKey("PK_player_badges", x => x.id); - table.ForeignKey( - name: "FK_player_badges_players_player_id", - column: x => x.player_id, - principalTable: "players", - principalColumn: "id", - onDelete: ReferentialAction.Cascade); - }) + migrationBuilder + .CreateTable( + name: "player_chat_styles_owned", + columns: table => new + { + id = table + .Column(type: "int", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.IdentityColumn + ), + player_id = table.Column(type: "int", nullable: false), + chat_style_id = table.Column(type: "int", nullable: false), + created_at = table + .Column(type: "datetime(6)", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.IdentityColumn + ), + updated_at = table + .Column(type: "datetime(6)", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.ComputedColumn + ), + deleted_at = table + .Column(type: "datetime(6)", nullable: true) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.ComputedColumn + ), + }, + constraints: table => + { + table.PrimaryKey("PK_player_chat_styles_owned", x => x.id); + table.ForeignKey( + name: "FK_player_chat_styles_owned_player_chat_styles_chat_style_id", + column: x => x.chat_style_id, + principalTable: "player_chat_styles", + principalColumn: "id", + onDelete: ReferentialAction.Cascade + ); + table.ForeignKey( + name: "FK_player_chat_styles_owned_players_player_id", + column: x => x.player_id, + principalTable: "players", + principalColumn: "id", + onDelete: ReferentialAction.Cascade + ); + } + ) .Annotation("MySql:CharSet", "utf8mb4"); - migrationBuilder.CreateTable( - name: "player_chat_styles_owned", - columns: table => new - { - id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - player_id = table.Column(type: "int", nullable: false), - chat_style_id = table.Column(type: "int", nullable: false), - created_at = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - updated_at = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn), - deleted_at = table.Column(type: "datetime(6)", nullable: true) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn) - }, - constraints: table => - { - table.PrimaryKey("PK_player_chat_styles_owned", x => x.id); - table.ForeignKey( - name: "FK_player_chat_styles_owned_player_chat_styles_chat_style_id", - column: x => x.chat_style_id, - principalTable: "player_chat_styles", - principalColumn: "id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK_player_chat_styles_owned_players_player_id", - column: x => x.player_id, - principalTable: "players", - principalColumn: "id", - onDelete: ReferentialAction.Cascade); - }) + migrationBuilder + .CreateTable( + name: "player_currencies", + columns: table => new + { + id = table + .Column(type: "int", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.IdentityColumn + ), + player_id = table.Column(type: "int", nullable: false), + currency_type_id = table.Column(type: "int", nullable: false), + amount = table.Column(type: "int", nullable: false, defaultValue: 0), + created_at = table + .Column(type: "datetime(6)", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.IdentityColumn + ), + updated_at = table + .Column(type: "datetime(6)", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.ComputedColumn + ), + deleted_at = table + .Column(type: "datetime(6)", nullable: true) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.ComputedColumn + ), + }, + constraints: table => + { + table.PrimaryKey("PK_player_currencies", x => x.id); + table.ForeignKey( + name: "FK_player_currencies_currency_types_currency_type_id", + column: x => x.currency_type_id, + principalTable: "currency_types", + principalColumn: "id", + onDelete: ReferentialAction.Cascade + ); + table.ForeignKey( + name: "FK_player_currencies_players_player_id", + column: x => x.player_id, + principalTable: "players", + principalColumn: "id", + onDelete: ReferentialAction.Cascade + ); + } + ) .Annotation("MySql:CharSet", "utf8mb4"); - migrationBuilder.CreateTable( - name: "player_currencies", - columns: table => new - { - id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - player_id = table.Column(type: "int", nullable: false), - currency_type_id = table.Column(type: "int", nullable: false), - amount = table.Column(type: "int", nullable: false, defaultValue: 0), - created_at = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - updated_at = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn), - deleted_at = table.Column(type: "datetime(6)", nullable: true) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn) - }, - constraints: table => - { - table.PrimaryKey("PK_player_currencies", x => x.id); - table.ForeignKey( - name: "FK_player_currencies_currency_types_currency_type_id", - column: x => x.currency_type_id, - principalTable: "currency_types", - principalColumn: "id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK_player_currencies_players_player_id", - column: x => x.player_id, - principalTable: "players", - principalColumn: "id", - onDelete: ReferentialAction.Cascade); - }) + migrationBuilder + .CreateTable( + name: "security_tickets", + columns: table => new + { + id = table + .Column(type: "int", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.IdentityColumn + ), + player_id = table.Column(type: "int", nullable: false), + ticket = table + .Column(type: "varchar(512)", maxLength: 512, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ip_address = table + .Column(type: "varchar(512)", maxLength: 512, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + is_locked = table.Column( + type: "tinyint(1)", + nullable: false, + defaultValue: false + ), + created_at = table + .Column(type: "datetime(6)", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.IdentityColumn + ), + updated_at = table + .Column(type: "datetime(6)", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.ComputedColumn + ), + deleted_at = table + .Column(type: "datetime(6)", nullable: true) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.ComputedColumn + ), + }, + constraints: table => + { + table.PrimaryKey("PK_security_tickets", x => x.id); + table.ForeignKey( + name: "FK_security_tickets_players_player_id", + column: x => x.player_id, + principalTable: "players", + principalColumn: "id", + onDelete: ReferentialAction.Cascade + ); + } + ) .Annotation("MySql:CharSet", "utf8mb4"); - migrationBuilder.CreateTable( - name: "security_tickets", - columns: table => new - { - id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - player_id = table.Column(type: "int", nullable: false), - ticket = table.Column(type: "varchar(512)", maxLength: 512, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ip_address = table.Column(type: "varchar(512)", maxLength: 512, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - is_locked = table.Column(type: "tinyint(1)", nullable: false, defaultValue: false), - created_at = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - updated_at = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn), - deleted_at = table.Column(type: "datetime(6)", nullable: true) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn) - }, - constraints: table => - { - table.PrimaryKey("PK_security_tickets", x => x.id); - table.ForeignKey( - name: "FK_security_tickets_players_player_id", - column: x => x.player_id, - principalTable: "players", - principalColumn: "id", - onDelete: ReferentialAction.Cascade); - }) + migrationBuilder + .CreateTable( + name: "rooms", + columns: table => new + { + id = table + .Column(type: "int", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.IdentityColumn + ), + name = table + .Column(type: "varchar(512)", maxLength: 512, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + description = table + .Column(type: "varchar(512)", maxLength: 512, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + player_id = table.Column(type: "int", nullable: false), + door_mode = table.Column( + type: "int", + nullable: false, + defaultValue: 0 + ), + password = table + .Column(type: "varchar(512)", maxLength: 512, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + model_id = table.Column(type: "int", nullable: false), + category_id = table.Column(type: "int", nullable: true), + users_now = table.Column( + type: "int", + nullable: false, + defaultValue: 0 + ), + players_max = table.Column( + type: "int", + nullable: false, + defaultValue: 25 + ), + paint_wall = table.Column( + type: "double", + nullable: false, + defaultValue: 0.0 + ), + paint_floor = table.Column( + type: "double", + nullable: false, + defaultValue: 0.0 + ), + paint_landscape = table.Column( + type: "double", + nullable: false, + defaultValue: 0.0 + ), + wall_height = table.Column( + type: "int", + nullable: false, + defaultValue: -1 + ), + hide_walls = table.Column( + type: "tinyint(1)", + nullable: false, + defaultValue: false + ), + thickness_wall = table.Column( + type: "int", + nullable: false, + defaultValue: 0 + ), + thickness_floor = table.Column( + type: "int", + nullable: false, + defaultValue: 0 + ), + allow_blocking = table.Column( + type: "tinyint(1)", + nullable: false, + defaultValue: false + ), + allow_pets = table.Column( + type: "tinyint(1)", + nullable: false, + defaultValue: false + ), + allow_pets_eat = table.Column( + type: "tinyint(1)", + nullable: false, + defaultValue: false + ), + trade_type = table.Column( + type: "int", + nullable: false, + defaultValue: 0 + ), + mute_type = table.Column( + type: "int", + nullable: false, + defaultValue: 0 + ), + kick_type = table.Column( + type: "int", + nullable: false, + defaultValue: 0 + ), + ban_type = table.Column(type: "int", nullable: false, defaultValue: 0), + chat_mode_type = table.Column( + type: "int", + nullable: false, + defaultValue: 0 + ), + chat_bubble_type = table.Column( + type: "int", + nullable: false, + defaultValue: 1 + ), + chat_speed_type = table.Column( + type: "int", + nullable: false, + defaultValue: 1 + ), + chat_flood_type = table.Column( + type: "int", + nullable: false, + defaultValue: 2 + ), + chat_distance = table.Column( + type: "int", + nullable: false, + defaultValue: 50 + ), + last_active = table + .Column(type: "datetime(6)", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.IdentityColumn + ), + created_at = table + .Column(type: "datetime(6)", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.IdentityColumn + ), + updated_at = table + .Column(type: "datetime(6)", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.ComputedColumn + ), + deleted_at = table + .Column(type: "datetime(6)", nullable: true) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.ComputedColumn + ), + }, + constraints: table => + { + table.PrimaryKey("PK_rooms", x => x.id); + table.ForeignKey( + name: "FK_rooms_navigator_flatcats_category_id", + column: x => x.category_id, + principalTable: "navigator_flatcats", + principalColumn: "id" + ); + table.ForeignKey( + name: "FK_rooms_players_player_id", + column: x => x.player_id, + principalTable: "players", + principalColumn: "id", + onDelete: ReferentialAction.Cascade + ); + table.ForeignKey( + name: "FK_rooms_room_models_model_id", + column: x => x.model_id, + principalTable: "room_models", + principalColumn: "id", + onDelete: ReferentialAction.Cascade + ); + } + ) .Annotation("MySql:CharSet", "utf8mb4"); - migrationBuilder.CreateTable( - name: "rooms", - columns: table => new - { - id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - name = table.Column(type: "varchar(512)", maxLength: 512, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - description = table.Column(type: "varchar(512)", maxLength: 512, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - player_id = table.Column(type: "int", nullable: false), - door_mode = table.Column(type: "int", nullable: false, defaultValue: 0), - password = table.Column(type: "varchar(512)", maxLength: 512, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - model_id = table.Column(type: "int", nullable: false), - category_id = table.Column(type: "int", nullable: true), - users_now = table.Column(type: "int", nullable: false, defaultValue: 0), - players_max = table.Column(type: "int", nullable: false, defaultValue: 25), - paint_wall = table.Column(type: "double", nullable: false, defaultValue: 0.0), - paint_floor = table.Column(type: "double", nullable: false, defaultValue: 0.0), - paint_landscape = table.Column(type: "double", nullable: false, defaultValue: 0.0), - wall_height = table.Column(type: "int", nullable: false, defaultValue: -1), - hide_walls = table.Column(type: "tinyint(1)", nullable: false, defaultValue: false), - thickness_wall = table.Column(type: "int", nullable: false, defaultValue: 0), - thickness_floor = table.Column(type: "int", nullable: false, defaultValue: 0), - allow_blocking = table.Column(type: "tinyint(1)", nullable: false, defaultValue: false), - allow_pets = table.Column(type: "tinyint(1)", nullable: false, defaultValue: false), - allow_pets_eat = table.Column(type: "tinyint(1)", nullable: false, defaultValue: false), - trade_type = table.Column(type: "int", nullable: false, defaultValue: 0), - mute_type = table.Column(type: "int", nullable: false, defaultValue: 0), - kick_type = table.Column(type: "int", nullable: false, defaultValue: 0), - ban_type = table.Column(type: "int", nullable: false, defaultValue: 0), - chat_mode_type = table.Column(type: "int", nullable: false, defaultValue: 0), - chat_bubble_type = table.Column(type: "int", nullable: false, defaultValue: 1), - chat_speed_type = table.Column(type: "int", nullable: false, defaultValue: 1), - chat_flood_type = table.Column(type: "int", nullable: false, defaultValue: 2), - chat_distance = table.Column(type: "int", nullable: false, defaultValue: 50), - last_active = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - created_at = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - updated_at = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn), - deleted_at = table.Column(type: "datetime(6)", nullable: true) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn) - }, - constraints: table => - { - table.PrimaryKey("PK_rooms", x => x.id); - table.ForeignKey( - name: "FK_rooms_navigator_flatcats_category_id", - column: x => x.category_id, - principalTable: "navigator_flatcats", - principalColumn: "id"); - table.ForeignKey( - name: "FK_rooms_players_player_id", - column: x => x.player_id, - principalTable: "players", - principalColumn: "id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK_rooms_room_models_model_id", - column: x => x.model_id, - principalTable: "room_models", - principalColumn: "id", - onDelete: ReferentialAction.Cascade); - }) + migrationBuilder + .CreateTable( + name: "catalog_products", + columns: table => new + { + id = table + .Column(type: "int", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.IdentityColumn + ), + offer_id = table.Column(type: "int", nullable: false), + product_type = table.Column( + type: "int", + nullable: false, + defaultValue: 0 + ), + definition_id = table.Column(type: "int", nullable: true), + extra_param = table + .Column(type: "varchar(512)", maxLength: 512, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + quantity = table.Column(type: "int", nullable: false, defaultValue: 1), + unique_size = table.Column( + type: "int", + nullable: false, + defaultValue: 0 + ), + unique_remaining = table.Column( + type: "int", + nullable: false, + defaultValue: 0 + ), + created_at = table + .Column(type: "datetime(6)", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.IdentityColumn + ), + updated_at = table + .Column(type: "datetime(6)", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.ComputedColumn + ), + deleted_at = table + .Column(type: "datetime(6)", nullable: true) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.ComputedColumn + ), + }, + constraints: table => + { + table.PrimaryKey("PK_catalog_products", x => x.id); + table.ForeignKey( + name: "FK_catalog_products_catalog_offers_offer_id", + column: x => x.offer_id, + principalTable: "catalog_offers", + principalColumn: "id", + onDelete: ReferentialAction.Cascade + ); + table.ForeignKey( + name: "FK_catalog_products_furniture_definitions_definition_id", + column: x => x.definition_id, + principalTable: "furniture_definitions", + principalColumn: "id" + ); + } + ) .Annotation("MySql:CharSet", "utf8mb4"); - migrationBuilder.CreateTable( - name: "catalog_products", - columns: table => new - { - id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - offer_id = table.Column(type: "int", nullable: false), - product_type = table.Column(type: "int", nullable: false, defaultValue: 0), - definition_id = table.Column(type: "int", nullable: true), - extra_param = table.Column(type: "varchar(512)", maxLength: 512, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - quantity = table.Column(type: "int", nullable: false, defaultValue: 1), - unique_size = table.Column(type: "int", nullable: false, defaultValue: 0), - unique_remaining = table.Column(type: "int", nullable: false, defaultValue: 0), - created_at = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - updated_at = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn), - deleted_at = table.Column(type: "datetime(6)", nullable: true) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn) - }, - constraints: table => - { - table.PrimaryKey("PK_catalog_products", x => x.id); - table.ForeignKey( - name: "FK_catalog_products_catalog_offers_offer_id", - column: x => x.offer_id, - principalTable: "catalog_offers", - principalColumn: "id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK_catalog_products_furniture_definitions_definition_id", - column: x => x.definition_id, - principalTable: "furniture_definitions", - principalColumn: "id"); - }) + migrationBuilder + .CreateTable( + name: "messenger_friends", + columns: table => new + { + id = table + .Column(type: "int", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.IdentityColumn + ), + player_id = table.Column(type: "int", nullable: false), + requested_id = table.Column(type: "int", nullable: false), + category_id = table.Column(type: "int", nullable: true), + relation = table.Column(type: "int", nullable: false, defaultValue: 0), + created_at = table + .Column(type: "datetime(6)", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.IdentityColumn + ), + updated_at = table + .Column(type: "datetime(6)", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.ComputedColumn + ), + deleted_at = table + .Column(type: "datetime(6)", nullable: true) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.ComputedColumn + ), + }, + constraints: table => + { + table.PrimaryKey("PK_messenger_friends", x => x.id); + table.ForeignKey( + name: "FK_messenger_friends_messenger_categories_category_id", + column: x => x.category_id, + principalTable: "messenger_categories", + principalColumn: "id" + ); + table.ForeignKey( + name: "FK_messenger_friends_players_player_id", + column: x => x.player_id, + principalTable: "players", + principalColumn: "id", + onDelete: ReferentialAction.Cascade + ); + table.ForeignKey( + name: "FK_messenger_friends_players_requested_id", + column: x => x.requested_id, + principalTable: "players", + principalColumn: "id", + onDelete: ReferentialAction.Cascade + ); + } + ) .Annotation("MySql:CharSet", "utf8mb4"); - migrationBuilder.CreateTable( - name: "messenger_friends", - columns: table => new - { - id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - player_id = table.Column(type: "int", nullable: false), - requested_id = table.Column(type: "int", nullable: false), - category_id = table.Column(type: "int", nullable: true), - relation = table.Column(type: "int", nullable: false, defaultValue: 0), - created_at = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - updated_at = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn), - deleted_at = table.Column(type: "datetime(6)", nullable: true) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn) - }, - constraints: table => - { - table.PrimaryKey("PK_messenger_friends", x => x.id); - table.ForeignKey( - name: "FK_messenger_friends_messenger_categories_category_id", - column: x => x.category_id, - principalTable: "messenger_categories", - principalColumn: "id"); - table.ForeignKey( - name: "FK_messenger_friends_players_player_id", - column: x => x.player_id, - principalTable: "players", - principalColumn: "id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK_messenger_friends_players_requested_id", - column: x => x.requested_id, - principalTable: "players", - principalColumn: "id", - onDelete: ReferentialAction.Cascade); - }) + migrationBuilder + .CreateTable( + name: "furniture", + columns: table => new + { + id = table + .Column(type: "int", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.IdentityColumn + ), + player_id = table.Column(type: "int", nullable: false), + definition_id = table.Column(type: "int", nullable: false), + room_id = table.Column(type: "int", nullable: true), + x = table.Column(type: "int", nullable: false, defaultValue: 0), + y = table.Column(type: "int", nullable: false, defaultValue: 0), + z = table.Column( + type: "double(10,3)", + nullable: false, + defaultValue: 0.0 + ), + direction = table.Column( + type: "int", + nullable: false, + defaultValue: 0 + ), + wall_offset = table.Column( + type: "int", + nullable: false, + defaultValue: 0 + ), + extra_data = table + .Column(type: "varchar(512)", maxLength: 512, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + created_at = table + .Column(type: "datetime(6)", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.IdentityColumn + ), + updated_at = table + .Column(type: "datetime(6)", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.ComputedColumn + ), + deleted_at = table + .Column(type: "datetime(6)", nullable: true) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.ComputedColumn + ), + }, + constraints: table => + { + table.PrimaryKey("PK_furniture", x => x.id); + table.ForeignKey( + name: "FK_furniture_furniture_definitions_definition_id", + column: x => x.definition_id, + principalTable: "furniture_definitions", + principalColumn: "id", + onDelete: ReferentialAction.Cascade + ); + table.ForeignKey( + name: "FK_furniture_players_player_id", + column: x => x.player_id, + principalTable: "players", + principalColumn: "id", + onDelete: ReferentialAction.Cascade + ); + table.ForeignKey( + name: "FK_furniture_rooms_room_id", + column: x => x.room_id, + principalTable: "rooms", + principalColumn: "id" + ); + } + ) .Annotation("MySql:CharSet", "utf8mb4"); - migrationBuilder.CreateTable( - name: "furniture", - columns: table => new - { - id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - player_id = table.Column(type: "int", nullable: false), - definition_id = table.Column(type: "int", nullable: false), - room_id = table.Column(type: "int", nullable: true), - x = table.Column(type: "int", nullable: false, defaultValue: 0), - y = table.Column(type: "int", nullable: false, defaultValue: 0), - z = table.Column(type: "double(10,3)", nullable: false, defaultValue: 0.0), - direction = table.Column(type: "int", nullable: false, defaultValue: 0), - wall_offset = table.Column(type: "int", nullable: false, defaultValue: 0), - extra_data = table.Column(type: "varchar(512)", maxLength: 512, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - created_at = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - updated_at = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn), - deleted_at = table.Column(type: "datetime(6)", nullable: true) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn) - }, - constraints: table => - { - table.PrimaryKey("PK_furniture", x => x.id); - table.ForeignKey( - name: "FK_furniture_furniture_definitions_definition_id", - column: x => x.definition_id, - principalTable: "furniture_definitions", - principalColumn: "id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK_furniture_players_player_id", - column: x => x.player_id, - principalTable: "players", - principalColumn: "id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK_furniture_rooms_room_id", - column: x => x.room_id, - principalTable: "rooms", - principalColumn: "id"); - }) + migrationBuilder + .CreateTable( + name: "player_favorite_rooms", + columns: table => new + { + id = table + .Column(type: "int", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.IdentityColumn + ), + player_id = table.Column(type: "int", nullable: false), + room_id = table.Column(type: "int", nullable: false), + created_at = table + .Column(type: "datetime(6)", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.IdentityColumn + ), + updated_at = table + .Column(type: "datetime(6)", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.ComputedColumn + ), + deleted_at = table + .Column(type: "datetime(6)", nullable: true) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.ComputedColumn + ), + }, + constraints: table => + { + table.PrimaryKey("PK_player_favorite_rooms", x => x.id); + table.ForeignKey( + name: "FK_player_favorite_rooms_players_player_id", + column: x => x.player_id, + principalTable: "players", + principalColumn: "id", + onDelete: ReferentialAction.Cascade + ); + table.ForeignKey( + name: "FK_player_favorite_rooms_rooms_room_id", + column: x => x.room_id, + principalTable: "rooms", + principalColumn: "id", + onDelete: ReferentialAction.Cascade + ); + } + ) .Annotation("MySql:CharSet", "utf8mb4"); - migrationBuilder.CreateTable( - name: "player_favorite_rooms", - columns: table => new - { - id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - player_id = table.Column(type: "int", nullable: false), - room_id = table.Column(type: "int", nullable: false), - created_at = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - updated_at = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn), - deleted_at = table.Column(type: "datetime(6)", nullable: true) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn) - }, - constraints: table => - { - table.PrimaryKey("PK_player_favorite_rooms", x => x.id); - table.ForeignKey( - name: "FK_player_favorite_rooms_players_player_id", - column: x => x.player_id, - principalTable: "players", - principalColumn: "id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK_player_favorite_rooms_rooms_room_id", - column: x => x.room_id, - principalTable: "rooms", - principalColumn: "id", - onDelete: ReferentialAction.Cascade); - }) + migrationBuilder + .CreateTable( + name: "room_bans", + columns: table => new + { + id = table + .Column(type: "int", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.IdentityColumn + ), + room_id = table.Column(type: "int", nullable: false), + player_id = table.Column(type: "int", nullable: false), + date_expires = table.Column(type: "datetime(6)", nullable: false), + created_at = table + .Column(type: "datetime(6)", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.IdentityColumn + ), + updated_at = table + .Column(type: "datetime(6)", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.ComputedColumn + ), + deleted_at = table + .Column(type: "datetime(6)", nullable: true) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.ComputedColumn + ), + }, + constraints: table => + { + table.PrimaryKey("PK_room_bans", x => x.id); + table.ForeignKey( + name: "FK_room_bans_players_player_id", + column: x => x.player_id, + principalTable: "players", + principalColumn: "id", + onDelete: ReferentialAction.Cascade + ); + table.ForeignKey( + name: "FK_room_bans_rooms_room_id", + column: x => x.room_id, + principalTable: "rooms", + principalColumn: "id", + onDelete: ReferentialAction.Cascade + ); + } + ) .Annotation("MySql:CharSet", "utf8mb4"); - migrationBuilder.CreateTable( - name: "room_bans", - columns: table => new - { - id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - room_id = table.Column(type: "int", nullable: false), - player_id = table.Column(type: "int", nullable: false), - date_expires = table.Column(type: "datetime(6)", nullable: false), - created_at = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - updated_at = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn), - deleted_at = table.Column(type: "datetime(6)", nullable: true) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn) - }, - constraints: table => - { - table.PrimaryKey("PK_room_bans", x => x.id); - table.ForeignKey( - name: "FK_room_bans_players_player_id", - column: x => x.player_id, - principalTable: "players", - principalColumn: "id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK_room_bans_rooms_room_id", - column: x => x.room_id, - principalTable: "rooms", - principalColumn: "id", - onDelete: ReferentialAction.Cascade); - }) + migrationBuilder + .CreateTable( + name: "room_chatlogs", + columns: table => new + { + id = table + .Column(type: "int", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.IdentityColumn + ), + room_id = table.Column(type: "int", nullable: false), + player_id = table.Column(type: "int", nullable: false), + target_player_id = table.Column(type: "int", nullable: true), + message = table + .Column(type: "varchar(100)", maxLength: 100, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + created_at = table + .Column(type: "datetime(6)", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.IdentityColumn + ), + updated_at = table + .Column(type: "datetime(6)", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.ComputedColumn + ), + deleted_at = table + .Column(type: "datetime(6)", nullable: true) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.ComputedColumn + ), + }, + constraints: table => + { + table.PrimaryKey("PK_room_chatlogs", x => x.id); + table.ForeignKey( + name: "FK_room_chatlogs_players_player_id", + column: x => x.player_id, + principalTable: "players", + principalColumn: "id", + onDelete: ReferentialAction.Cascade + ); + table.ForeignKey( + name: "FK_room_chatlogs_players_target_player_id", + column: x => x.target_player_id, + principalTable: "players", + principalColumn: "id" + ); + table.ForeignKey( + name: "FK_room_chatlogs_rooms_room_id", + column: x => x.room_id, + principalTable: "rooms", + principalColumn: "id", + onDelete: ReferentialAction.Cascade + ); + } + ) .Annotation("MySql:CharSet", "utf8mb4"); - migrationBuilder.CreateTable( - name: "room_chatlogs", - columns: table => new - { - id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - room_id = table.Column(type: "int", nullable: false), - player_id = table.Column(type: "int", nullable: false), - target_player_id = table.Column(type: "int", nullable: true), - message = table.Column(type: "varchar(100)", maxLength: 100, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - created_at = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - updated_at = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn), - deleted_at = table.Column(type: "datetime(6)", nullable: true) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn) - }, - constraints: table => - { - table.PrimaryKey("PK_room_chatlogs", x => x.id); - table.ForeignKey( - name: "FK_room_chatlogs_players_player_id", - column: x => x.player_id, - principalTable: "players", - principalColumn: "id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK_room_chatlogs_players_target_player_id", - column: x => x.target_player_id, - principalTable: "players", - principalColumn: "id"); - table.ForeignKey( - name: "FK_room_chatlogs_rooms_room_id", - column: x => x.room_id, - principalTable: "rooms", - principalColumn: "id", - onDelete: ReferentialAction.Cascade); - }) + migrationBuilder + .CreateTable( + name: "room_entry_logs", + columns: table => new + { + id = table + .Column(type: "int", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.IdentityColumn + ), + room_id = table.Column(type: "int", nullable: false), + player_id = table.Column(type: "int", nullable: false), + created_at = table + .Column(type: "datetime(6)", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.IdentityColumn + ), + updated_at = table + .Column(type: "datetime(6)", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.ComputedColumn + ), + deleted_at = table + .Column(type: "datetime(6)", nullable: true) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.ComputedColumn + ), + }, + constraints: table => + { + table.PrimaryKey("PK_room_entry_logs", x => x.id); + table.ForeignKey( + name: "FK_room_entry_logs_players_player_id", + column: x => x.player_id, + principalTable: "players", + principalColumn: "id", + onDelete: ReferentialAction.Cascade + ); + table.ForeignKey( + name: "FK_room_entry_logs_rooms_room_id", + column: x => x.room_id, + principalTable: "rooms", + principalColumn: "id", + onDelete: ReferentialAction.Cascade + ); + } + ) .Annotation("MySql:CharSet", "utf8mb4"); - migrationBuilder.CreateTable( - name: "room_entry_logs", - columns: table => new - { - id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - room_id = table.Column(type: "int", nullable: false), - player_id = table.Column(type: "int", nullable: false), - created_at = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - updated_at = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn), - deleted_at = table.Column(type: "datetime(6)", nullable: true) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn) - }, - constraints: table => - { - table.PrimaryKey("PK_room_entry_logs", x => x.id); - table.ForeignKey( - name: "FK_room_entry_logs_players_player_id", - column: x => x.player_id, - principalTable: "players", - principalColumn: "id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK_room_entry_logs_rooms_room_id", - column: x => x.room_id, - principalTable: "rooms", - principalColumn: "id", - onDelete: ReferentialAction.Cascade); - }) + migrationBuilder + .CreateTable( + name: "room_mutes", + columns: table => new + { + id = table + .Column(type: "int", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.IdentityColumn + ), + room_id = table.Column(type: "int", nullable: false), + player_id = table.Column(type: "int", nullable: false), + date_expires = table.Column(type: "datetime(6)", nullable: false), + created_at = table + .Column(type: "datetime(6)", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.IdentityColumn + ), + updated_at = table + .Column(type: "datetime(6)", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.ComputedColumn + ), + deleted_at = table + .Column(type: "datetime(6)", nullable: true) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.ComputedColumn + ), + }, + constraints: table => + { + table.PrimaryKey("PK_room_mutes", x => x.id); + table.ForeignKey( + name: "FK_room_mutes_players_player_id", + column: x => x.player_id, + principalTable: "players", + principalColumn: "id", + onDelete: ReferentialAction.Cascade + ); + table.ForeignKey( + name: "FK_room_mutes_rooms_room_id", + column: x => x.room_id, + principalTable: "rooms", + principalColumn: "id", + onDelete: ReferentialAction.Cascade + ); + } + ) .Annotation("MySql:CharSet", "utf8mb4"); - migrationBuilder.CreateTable( - name: "room_mutes", - columns: table => new - { - id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - room_id = table.Column(type: "int", nullable: false), - player_id = table.Column(type: "int", nullable: false), - date_expires = table.Column(type: "datetime(6)", nullable: false), - created_at = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - updated_at = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn), - deleted_at = table.Column(type: "datetime(6)", nullable: true) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn) - }, - constraints: table => - { - table.PrimaryKey("PK_room_mutes", x => x.id); - table.ForeignKey( - name: "FK_room_mutes_players_player_id", - column: x => x.player_id, - principalTable: "players", - principalColumn: "id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK_room_mutes_rooms_room_id", - column: x => x.room_id, - principalTable: "rooms", - principalColumn: "id", - onDelete: ReferentialAction.Cascade); - }) + migrationBuilder + .CreateTable( + name: "room_rights", + columns: table => new + { + id = table + .Column(type: "int", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.IdentityColumn + ), + room_id = table.Column(type: "int", nullable: false), + player_id = table.Column(type: "int", nullable: false), + created_at = table + .Column(type: "datetime(6)", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.IdentityColumn + ), + updated_at = table + .Column(type: "datetime(6)", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.ComputedColumn + ), + deleted_at = table + .Column(type: "datetime(6)", nullable: true) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.ComputedColumn + ), + }, + constraints: table => + { + table.PrimaryKey("PK_room_rights", x => x.id); + table.ForeignKey( + name: "FK_room_rights_players_player_id", + column: x => x.player_id, + principalTable: "players", + principalColumn: "id", + onDelete: ReferentialAction.Cascade + ); + table.ForeignKey( + name: "FK_room_rights_rooms_room_id", + column: x => x.room_id, + principalTable: "rooms", + principalColumn: "id", + onDelete: ReferentialAction.Cascade + ); + } + ) .Annotation("MySql:CharSet", "utf8mb4"); - migrationBuilder.CreateTable( - name: "room_rights", - columns: table => new - { - id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - room_id = table.Column(type: "int", nullable: false), - player_id = table.Column(type: "int", nullable: false), - created_at = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - updated_at = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn), - deleted_at = table.Column(type: "datetime(6)", nullable: true) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn) - }, - constraints: table => - { - table.PrimaryKey("PK_room_rights", x => x.id); - table.ForeignKey( - name: "FK_room_rights_players_player_id", - column: x => x.player_id, - principalTable: "players", - principalColumn: "id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK_room_rights_rooms_room_id", - column: x => x.room_id, - principalTable: "rooms", - principalColumn: "id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "furniture_teleport_links", - columns: table => new - { - id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - furniture_one_id = table.Column(type: "int", nullable: false), - furniture_two_id = table.Column(type: "int", nullable: false), - created_at = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - updated_at = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn), - deleted_at = table.Column(type: "datetime(6)", nullable: true) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn) - }, - constraints: table => - { - table.PrimaryKey("PK_furniture_teleport_links", x => x.id); - table.ForeignKey( - name: "FK_furniture_teleport_links_furniture_furniture_one_id", - column: x => x.furniture_one_id, - principalTable: "furniture", - principalColumn: "id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK_furniture_teleport_links_furniture_furniture_two_id", - column: x => x.furniture_two_id, - principalTable: "furniture", - principalColumn: "id", - onDelete: ReferentialAction.Cascade); - }) + migrationBuilder + .CreateTable( + name: "furniture_teleport_links", + columns: table => new + { + id = table + .Column(type: "int", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.IdentityColumn + ), + furniture_one_id = table.Column(type: "int", nullable: false), + furniture_two_id = table.Column(type: "int", nullable: false), + created_at = table + .Column(type: "datetime(6)", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.IdentityColumn + ), + updated_at = table + .Column(type: "datetime(6)", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.ComputedColumn + ), + deleted_at = table + .Column(type: "datetime(6)", nullable: true) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.ComputedColumn + ), + }, + constraints: table => + { + table.PrimaryKey("PK_furniture_teleport_links", x => x.id); + table.ForeignKey( + name: "FK_furniture_teleport_links_furniture_furniture_one_id", + column: x => x.furniture_one_id, + principalTable: "furniture", + principalColumn: "id", + onDelete: ReferentialAction.Cascade + ); + table.ForeignKey( + name: "FK_furniture_teleport_links_furniture_furniture_two_id", + column: x => x.furniture_two_id, + principalTable: "furniture", + principalColumn: "id", + onDelete: ReferentialAction.Cascade + ); + } + ) .Annotation("MySql:CharSet", "utf8mb4"); migrationBuilder.CreateIndex( name: "IX_catalog_offers_currency_type_id", table: "catalog_offers", - column: "currency_type_id"); + column: "currency_type_id" + ); migrationBuilder.CreateIndex( name: "IX_catalog_offers_page_id", table: "catalog_offers", - column: "page_id"); + column: "page_id" + ); migrationBuilder.CreateIndex( name: "IX_catalog_pages_parent_id", table: "catalog_pages", - column: "parent_id"); + column: "parent_id" + ); migrationBuilder.CreateIndex( name: "IX_catalog_products_definition_id", table: "catalog_products", - column: "definition_id"); + column: "definition_id" + ); migrationBuilder.CreateIndex( name: "IX_catalog_products_offer_id", table: "catalog_products", - column: "offer_id"); + column: "offer_id" + ); migrationBuilder.CreateIndex( name: "IX_furniture_definition_id", table: "furniture", - column: "definition_id"); + column: "definition_id" + ); migrationBuilder.CreateIndex( name: "IX_furniture_player_id", table: "furniture", - column: "player_id"); + column: "player_id" + ); migrationBuilder.CreateIndex( name: "IX_furniture_room_id", table: "furniture", - column: "room_id"); + column: "room_id" + ); migrationBuilder.CreateIndex( name: "IX_furniture_definitions_sprite_id_type_category", table: "furniture_definitions", columns: new[] { "sprite_id", "type", "category" }, - unique: true); + unique: true + ); migrationBuilder.CreateIndex( name: "IX_furniture_teleport_links_furniture_one_id", table: "furniture_teleport_links", column: "furniture_one_id", - unique: true); + unique: true + ); migrationBuilder.CreateIndex( name: "IX_furniture_teleport_links_furniture_two_id", table: "furniture_teleport_links", column: "furniture_two_id", - unique: true); + unique: true + ); migrationBuilder.CreateIndex( name: "IX_messenger_categories_player_id_name", table: "messenger_categories", columns: new[] { "player_id", "name" }, - unique: true); + unique: true + ); migrationBuilder.CreateIndex( name: "IX_messenger_friends_category_id", table: "messenger_friends", - column: "category_id"); + column: "category_id" + ); migrationBuilder.CreateIndex( name: "IX_messenger_friends_player_id_requested_id", table: "messenger_friends", columns: new[] { "player_id", "requested_id" }, - unique: true); + unique: true + ); migrationBuilder.CreateIndex( name: "IX_messenger_friends_requested_id", table: "messenger_friends", - column: "requested_id"); + column: "requested_id" + ); migrationBuilder.CreateIndex( name: "IX_messenger_requests_player_id_requested_id", table: "messenger_requests", columns: new[] { "player_id", "requested_id" }, - unique: true); + unique: true + ); migrationBuilder.CreateIndex( name: "IX_messenger_requests_requested_id", table: "messenger_requests", - column: "requested_id"); + column: "requested_id" + ); migrationBuilder.CreateIndex( name: "IX_navigator_top_level_contexts_search_code", table: "navigator_top_level_contexts", column: "search_code", - unique: true); + unique: true + ); migrationBuilder.CreateIndex( name: "IX_performance_logs_elapsed_time", table: "performance_logs", - column: "elapsed_time"); + column: "elapsed_time" + ); migrationBuilder.CreateIndex( name: "IX_performance_logs_ip_address", table: "performance_logs", - column: "ip_address"); + column: "ip_address" + ); migrationBuilder.CreateIndex( name: "IX_player_badges_player_id_badge_code", table: "player_badges", columns: new[] { "player_id", "badge_code" }, - unique: true); + unique: true + ); migrationBuilder.CreateIndex( name: "IX_player_chat_styles_client_style_id", table: "player_chat_styles", column: "client_style_id", - unique: true); + unique: true + ); migrationBuilder.CreateIndex( name: "IX_player_chat_styles_owned_chat_style_id", table: "player_chat_styles_owned", - column: "chat_style_id"); + column: "chat_style_id" + ); migrationBuilder.CreateIndex( name: "IX_player_chat_styles_owned_player_id_chat_style_id", table: "player_chat_styles_owned", columns: new[] { "player_id", "chat_style_id" }, - unique: true); + unique: true + ); migrationBuilder.CreateIndex( name: "IX_player_currencies_currency_type_id", table: "player_currencies", - column: "currency_type_id"); + column: "currency_type_id" + ); migrationBuilder.CreateIndex( name: "IX_player_currencies_player_id_currency_type_id", table: "player_currencies", columns: new[] { "player_id", "currency_type_id" }, - unique: true); + unique: true + ); migrationBuilder.CreateIndex( name: "IX_player_favorite_rooms_player_id_room_id", table: "player_favorite_rooms", columns: new[] { "player_id", "room_id" }, - unique: true); + unique: true + ); migrationBuilder.CreateIndex( name: "IX_player_favorite_rooms_room_id", table: "player_favorite_rooms", - column: "room_id"); + column: "room_id" + ); migrationBuilder.CreateIndex( name: "IX_players_name", table: "players", column: "name", - unique: true); + unique: true + ); migrationBuilder.CreateIndex( name: "IX_room_bans_player_id", table: "room_bans", - column: "player_id"); + column: "player_id" + ); migrationBuilder.CreateIndex( name: "IX_room_bans_room_id_player_id", table: "room_bans", columns: new[] { "room_id", "player_id" }, - unique: true); + unique: true + ); migrationBuilder.CreateIndex( name: "IX_room_chatlogs_player_id", table: "room_chatlogs", - column: "player_id"); + column: "player_id" + ); migrationBuilder.CreateIndex( name: "IX_room_chatlogs_room_id", table: "room_chatlogs", - column: "room_id"); + column: "room_id" + ); migrationBuilder.CreateIndex( name: "IX_room_chatlogs_target_player_id", table: "room_chatlogs", - column: "target_player_id"); + column: "target_player_id" + ); migrationBuilder.CreateIndex( name: "IX_room_entry_logs_player_id", table: "room_entry_logs", - column: "player_id"); + column: "player_id" + ); migrationBuilder.CreateIndex( name: "IX_room_entry_logs_room_id", table: "room_entry_logs", - column: "room_id"); + column: "room_id" + ); migrationBuilder.CreateIndex( name: "IX_room_models_name", table: "room_models", column: "name", - unique: true); + unique: true + ); migrationBuilder.CreateIndex( name: "IX_room_mutes_player_id", table: "room_mutes", - column: "player_id"); + column: "player_id" + ); migrationBuilder.CreateIndex( name: "IX_room_mutes_room_id_player_id", table: "room_mutes", columns: new[] { "room_id", "player_id" }, - unique: true); + unique: true + ); migrationBuilder.CreateIndex( name: "IX_room_rights_player_id", table: "room_rights", - column: "player_id"); + column: "player_id" + ); migrationBuilder.CreateIndex( name: "IX_room_rights_room_id_player_id", table: "room_rights", columns: new[] { "room_id", "player_id" }, - unique: true); + unique: true + ); migrationBuilder.CreateIndex( name: "IX_rooms_category_id", table: "rooms", - column: "category_id"); + column: "category_id" + ); migrationBuilder.CreateIndex( name: "IX_rooms_model_id", table: "rooms", - column: "model_id"); + column: "model_id" + ); migrationBuilder.CreateIndex( name: "IX_rooms_player_id", table: "rooms", - column: "player_id"); + column: "player_id" + ); migrationBuilder.CreateIndex( name: "IX_security_tickets_player_id", table: "security_tickets", column: "player_id", - unique: true); + unique: true + ); migrationBuilder.CreateIndex( name: "IX_security_tickets_ticket", table: "security_tickets", column: "ticket", - unique: true); + unique: true + ); } /// protected override void Down(MigrationBuilder migrationBuilder) { - migrationBuilder.DropTable( - name: "catalog_products"); + migrationBuilder.DropTable(name: "catalog_products"); - migrationBuilder.DropTable( - name: "furniture_teleport_links"); + migrationBuilder.DropTable(name: "furniture_teleport_links"); - migrationBuilder.DropTable( - name: "messenger_friends"); + migrationBuilder.DropTable(name: "messenger_friends"); - migrationBuilder.DropTable( - name: "messenger_requests"); + migrationBuilder.DropTable(name: "messenger_requests"); - migrationBuilder.DropTable( - name: "navigator_eventcats"); + migrationBuilder.DropTable(name: "navigator_eventcats"); - migrationBuilder.DropTable( - name: "navigator_top_level_contexts"); + migrationBuilder.DropTable(name: "navigator_top_level_contexts"); - migrationBuilder.DropTable( - name: "performance_logs"); + migrationBuilder.DropTable(name: "performance_logs"); - migrationBuilder.DropTable( - name: "player_badges"); + migrationBuilder.DropTable(name: "player_badges"); - migrationBuilder.DropTable( - name: "player_chat_styles_owned"); + migrationBuilder.DropTable(name: "player_chat_styles_owned"); - migrationBuilder.DropTable( - name: "player_currencies"); + migrationBuilder.DropTable(name: "player_currencies"); - migrationBuilder.DropTable( - name: "player_favorite_rooms"); + migrationBuilder.DropTable(name: "player_favorite_rooms"); - migrationBuilder.DropTable( - name: "room_bans"); + migrationBuilder.DropTable(name: "room_bans"); - migrationBuilder.DropTable( - name: "room_chatlogs"); + migrationBuilder.DropTable(name: "room_chatlogs"); - migrationBuilder.DropTable( - name: "room_entry_logs"); + migrationBuilder.DropTable(name: "room_entry_logs"); - migrationBuilder.DropTable( - name: "room_mutes"); + migrationBuilder.DropTable(name: "room_mutes"); - migrationBuilder.DropTable( - name: "room_rights"); + migrationBuilder.DropTable(name: "room_rights"); - migrationBuilder.DropTable( - name: "security_tickets"); + migrationBuilder.DropTable(name: "security_tickets"); - migrationBuilder.DropTable( - name: "catalog_offers"); + migrationBuilder.DropTable(name: "catalog_offers"); - migrationBuilder.DropTable( - name: "furniture"); + migrationBuilder.DropTable(name: "furniture"); - migrationBuilder.DropTable( - name: "messenger_categories"); + migrationBuilder.DropTable(name: "messenger_categories"); - migrationBuilder.DropTable( - name: "player_chat_styles"); + migrationBuilder.DropTable(name: "player_chat_styles"); - migrationBuilder.DropTable( - name: "catalog_pages"); + migrationBuilder.DropTable(name: "catalog_pages"); - migrationBuilder.DropTable( - name: "currency_types"); + migrationBuilder.DropTable(name: "currency_types"); - migrationBuilder.DropTable( - name: "furniture_definitions"); + migrationBuilder.DropTable(name: "furniture_definitions"); - migrationBuilder.DropTable( - name: "rooms"); + migrationBuilder.DropTable(name: "rooms"); - migrationBuilder.DropTable( - name: "navigator_flatcats"); + migrationBuilder.DropTable(name: "navigator_flatcats"); - migrationBuilder.DropTable( - name: "players"); + migrationBuilder.DropTable(name: "players"); - migrationBuilder.DropTable( - name: "room_models"); + migrationBuilder.DropTable(name: "room_models"); } } } diff --git a/Turbo.Database/Migrations/20260208131801_AddMessengerTables.Designer.cs b/Turbo.Database/Migrations/20260208131801_AddMessengerTables.Designer.cs new file mode 100644 index 00000000..921bb1c1 --- /dev/null +++ b/Turbo.Database/Migrations/20260208131801_AddMessengerTables.Designer.cs @@ -0,0 +1,2582 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Turbo.Database.Context; + +#nullable disable + +namespace Turbo.Database.Migrations +{ + [DbContext(typeof(TurboDbContext))] + [Migration("20260208131801_AddMessengerTables")] + partial class AddMessengerTables + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Turbo.Database.Entities.Catalog.CatalogOfferEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CanBundle") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true) + .HasColumnName("can_bundle"); + + b.Property("CanGift") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true) + .HasColumnName("can_gift"); + + b.Property("CatalogPageEntityId") + .HasColumnType("int") + .HasColumnName("page_id"); + + b.Property("ClubLevel") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("club_level"); + + b.Property("CostCredits") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("cost_credits"); + + b.Property("CostCurrency") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("cost_currency"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("CurrencyTypeId") + .HasColumnType("int") + .HasColumnName("currency_type_id"); + + b.Property("DeletedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("deleted_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("DeletedAt")); + + b.Property("LocalizationId") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("varchar(512)") + .HasColumnName("localization_id"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("UpdatedAt")); + + b.Property("Visible") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true) + .HasColumnName("visible"); + + b.HasKey("Id"); + + b.HasIndex("CatalogPageEntityId"); + + b.HasIndex("CurrencyTypeId"); + + b.ToTable("catalog_offers"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Catalog.CatalogPageEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("DeletedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("deleted_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("DeletedAt")); + + b.Property("Icon") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("icon"); + + b.PrimitiveCollection("ImageData") + .HasColumnType("longtext") + .HasColumnName("image_data"); + + b.Property("Layout") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(50) + .HasColumnType("varchar(50)") + .HasDefaultValue("default_3x3") + .HasColumnName("layout"); + + b.Property("Localization") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)") + .HasColumnName("localization"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("varchar(50)") + .HasColumnName("name"); + + b.Property("ParentEntityId") + .HasColumnType("int") + .HasColumnName("parent_id"); + + b.Property("SortOrder") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("sort_order"); + + b.PrimitiveCollection("TextData") + .HasColumnType("longtext") + .HasColumnName("text_data"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("UpdatedAt")); + + b.Property("Visible") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true) + .HasColumnName("visible"); + + b.HasKey("Id"); + + b.HasIndex("ParentEntityId"); + + b.ToTable("catalog_pages"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Catalog.CatalogProductEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CatalogOfferEntityId") + .HasColumnType("int") + .HasColumnName("offer_id"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("DeletedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("deleted_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("DeletedAt")); + + b.Property("ExtraParam") + .HasMaxLength(512) + .HasColumnType("varchar(512)") + .HasColumnName("extra_param"); + + b.Property("FurnitureDefinitionEntityId") + .HasColumnType("int") + .HasColumnName("definition_id"); + + b.Property("ProductType") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("product_type"); + + b.Property("Quantity") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(1) + .HasColumnName("quantity"); + + b.Property("UniqueRemaining") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("unique_remaining"); + + b.Property("UniqueSize") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("unique_size"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("UpdatedAt")); + + b.HasKey("Id"); + + b.HasIndex("CatalogOfferEntityId"); + + b.HasIndex("FurnitureDefinitionEntityId"); + + b.ToTable("catalog_products"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Catalog.CurrencyTypeEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ActivityPointType") + .HasColumnType("int") + .HasColumnName("activity_point_type"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("CurrencyType") + .HasColumnType("int") + .HasColumnName("type"); + + b.Property("DeletedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("deleted_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("DeletedAt")); + + b.Property("Enabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true) + .HasColumnName("enabled"); + + b.Property("Name") + .HasMaxLength(512) + .HasColumnType("varchar(512)") + .HasColumnName("name"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("UpdatedAt")); + + b.HasKey("Id"); + + b.ToTable("currency_types"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Furniture.FurnitureDefinitionEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CanGroup") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true) + .HasColumnName("can_group"); + + b.Property("CanLay") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false) + .HasColumnName("can_lay"); + + b.Property("CanRecycle") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false) + .HasColumnName("can_recycle"); + + b.Property("CanSell") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true) + .HasColumnName("can_sell"); + + b.Property("CanSit") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false) + .HasColumnName("can_sit"); + + b.Property("CanStack") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true) + .HasColumnName("can_stack"); + + b.Property("CanTrade") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true) + .HasColumnName("can_trade"); + + b.Property("CanWalk") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false) + .HasColumnName("can_walk"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("DeletedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("deleted_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("DeletedAt")); + + b.Property("ExtraData") + .HasMaxLength(512) + .HasColumnType("varchar(512)") + .HasColumnName("extra_data"); + + b.Property("FurniCategory") + .HasColumnType("int") + .HasDefaultValue(1) + .HasColumnName("category"); + + b.Property("Length") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(1) + .HasColumnName("length"); + + b.Property("Logic") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(50) + .HasColumnType("varchar(50)") + .HasDefaultValue("none") + .HasColumnName("logic"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("varchar(512)") + .HasColumnName("name"); + + b.Property("ProductType") + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("type"); + + b.Property("SpriteId") + .HasColumnType("int") + .HasColumnName("sprite_id"); + + b.Property("StackHeight") + .ValueGeneratedOnAdd() + .HasColumnType("double(10,3)") + .HasDefaultValue(0.0) + .HasColumnName("stack_height"); + + b.Property("TotalStates") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("total_states"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("UpdatedAt")); + + b.Property("UsagePolicy") + .HasColumnType("int") + .HasDefaultValue(1) + .HasColumnName("usage_policy"); + + b.Property("Width") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("width"); + + b.HasKey("Id"); + + b.HasIndex("SpriteId", "ProductType", "FurniCategory") + .IsUnique(); + + b.ToTable("furniture_definitions"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Furniture.FurnitureEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("DeletedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("deleted_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("DeletedAt")); + + b.Property("ExtraData") + .HasMaxLength(512) + .HasColumnType("varchar(512)") + .HasColumnName("extra_data"); + + b.Property("FurnitureDefinitionEntityId") + .HasColumnType("int") + .HasColumnName("definition_id"); + + b.Property("PlayerEntityId") + .HasColumnType("int") + .HasColumnName("player_id"); + + b.Property("RoomEntityId") + .HasColumnType("int") + .HasColumnName("room_id"); + + b.Property("Rotation") + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("direction"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("UpdatedAt")); + + b.Property("WallOffset") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("wall_offset"); + + b.Property("X") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("x"); + + b.Property("Y") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("y"); + + b.Property("Z") + .ValueGeneratedOnAdd() + .HasColumnType("double(10,3)") + .HasDefaultValue(0.0) + .HasColumnName("z"); + + b.HasKey("Id"); + + b.HasIndex("FurnitureDefinitionEntityId"); + + b.HasIndex("PlayerEntityId"); + + b.HasIndex("RoomEntityId"); + + b.ToTable("furniture"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Furniture.FurnitureTeleportLinkEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("DeletedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("deleted_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("DeletedAt")); + + b.Property("FurnitureEntityOneId") + .HasColumnType("int") + .HasColumnName("furniture_one_id"); + + b.Property("FurnitureEntityTwoId") + .HasColumnType("int") + .HasColumnName("furniture_two_id"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("UpdatedAt")); + + b.HasKey("Id"); + + b.HasIndex("FurnitureEntityOneId") + .IsUnique(); + + b.HasIndex("FurnitureEntityTwoId") + .IsUnique(); + + b.ToTable("furniture_teleport_links"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Messenger.MessengerBlockedEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("BlockedPlayerEntityId") + .HasColumnType("int") + .HasColumnName("blocked_player_id"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("DeletedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("deleted_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("DeletedAt")); + + b.Property("PlayerEntityId") + .HasColumnType("int") + .HasColumnName("player_id"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("UpdatedAt")); + + b.HasKey("Id"); + + b.HasIndex("BlockedPlayerEntityId"); + + b.HasIndex("PlayerEntityId", "BlockedPlayerEntityId") + .IsUnique(); + + b.ToTable("messenger_blocked"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Messenger.MessengerCategoryEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("DeletedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("deleted_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("DeletedAt")); + + b.Property("Name") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("varchar(512)") + .HasColumnName("name"); + + b.Property("PlayerEntityId") + .HasColumnType("int") + .HasColumnName("player_id"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("UpdatedAt")); + + b.HasKey("Id"); + + b.HasIndex("PlayerEntityId", "Name") + .IsUnique(); + + b.ToTable("messenger_categories"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Messenger.MessengerFriendEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("DeletedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("deleted_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("DeletedAt")); + + b.Property("FriendPlayerEntityId") + .HasColumnType("int") + .HasColumnName("requested_id"); + + b.Property("MessengerCategoryEntityId") + .HasColumnType("int") + .HasColumnName("category_id"); + + b.Property("PlayerEntityId") + .HasColumnType("int") + .HasColumnName("player_id"); + + b.Property("RelationType") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("relation"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("UpdatedAt")); + + b.HasKey("Id"); + + b.HasIndex("FriendPlayerEntityId"); + + b.HasIndex("MessengerCategoryEntityId"); + + b.HasIndex("PlayerEntityId", "FriendPlayerEntityId") + .IsUnique(); + + b.ToTable("messenger_friends"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Messenger.MessengerIgnoredEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("DeletedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("deleted_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("DeletedAt")); + + b.Property("IgnoredPlayerEntityId") + .HasColumnType("int") + .HasColumnName("ignored_player_id"); + + b.Property("PlayerEntityId") + .HasColumnType("int") + .HasColumnName("player_id"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("UpdatedAt")); + + b.HasKey("Id"); + + b.HasIndex("IgnoredPlayerEntityId"); + + b.HasIndex("PlayerEntityId", "IgnoredPlayerEntityId") + .IsUnique(); + + b.ToTable("messenger_ignored"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Messenger.MessengerMessageEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("DeletedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("deleted_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("DeletedAt")); + + b.Property("Message") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("varchar(512)") + .HasColumnName("message"); + + b.Property("ReceiverPlayerEntityId") + .HasColumnType("int") + .HasColumnName("receiver_id"); + + b.Property("SenderPlayerEntityId") + .HasColumnType("int") + .HasColumnName("sender_id"); + + b.Property("Timestamp") + .HasColumnType("datetime(6)") + .HasColumnName("timestamp"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("UpdatedAt")); + + b.HasKey("Id"); + + b.HasIndex("ReceiverPlayerEntityId", "SenderPlayerEntityId", "Timestamp"); + + b.HasIndex("SenderPlayerEntityId", "ReceiverPlayerEntityId", "Timestamp"); + + b.ToTable("messenger_messages"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Messenger.MessengerRequestEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("DeletedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("deleted_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("DeletedAt")); + + b.Property("PlayerEntityId") + .HasColumnType("int") + .HasColumnName("player_id"); + + b.Property("RequestedPlayerEntityId") + .HasColumnType("int") + .HasColumnName("requested_id"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("UpdatedAt")); + + b.HasKey("Id"); + + b.HasIndex("RequestedPlayerEntityId"); + + b.HasIndex("PlayerEntityId", "RequestedPlayerEntityId") + .IsUnique(); + + b.ToTable("messenger_requests"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Navigator.NavigatorEventCategoryEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("DeletedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("deleted_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("DeletedAt")); + + b.Property("Name") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("varchar(512)") + .HasColumnName("name"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("UpdatedAt")); + + b.Property("Visible") + .HasColumnType("tinyint(1)") + .HasColumnName("visible"); + + b.HasKey("Id"); + + b.ToTable("navigator_eventcats"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Navigator.NavigatorFlatCategoryEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("Automatic") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true) + .HasColumnName("automatic"); + + b.Property("AutomaticCategory") + .HasMaxLength(512) + .HasColumnType("varchar(512)") + .HasColumnName("automatic_category"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("DeletedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("deleted_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("DeletedAt")); + + b.Property("GlobalCategory") + .HasMaxLength(512) + .HasColumnType("varchar(512)") + .HasColumnName("global_category"); + + b.Property("MinRank") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(1) + .HasColumnName("min_rank"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("varchar(512)") + .HasColumnName("name"); + + b.Property("OrderNum") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("order_num"); + + b.Property("StaffOnly") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false) + .HasColumnName("staff_only"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("UpdatedAt")); + + b.Property("Visible") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true) + .HasColumnName("visible"); + + b.HasKey("Id"); + + b.ToTable("navigator_flatcats"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Navigator.NavigatorTopLevelContextEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("DeletedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("deleted_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("DeletedAt")); + + b.Property("OrderNum") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("order_num"); + + b.Property("SearchCode") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("varchar(512)") + .HasColumnName("search_code"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("UpdatedAt")); + + b.Property("Visible") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true) + .HasColumnName("visible"); + + b.HasKey("Id"); + + b.HasIndex("SearchCode") + .IsUnique(); + + b.ToTable("navigator_top_level_contexts"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Players.PlayerBadgeEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("BadgeCode") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("varchar(512)") + .HasColumnName("badge_code"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("DeletedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("deleted_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("DeletedAt")); + + b.Property("PlayerEntityId") + .HasColumnType("int") + .HasColumnName("player_id"); + + b.Property("SlotId") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("slot_id"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("UpdatedAt")); + + b.HasKey("Id"); + + b.HasIndex("PlayerEntityId", "BadgeCode") + .IsUnique(); + + b.ToTable("player_badges"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Players.PlayerChatStyleEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClientStyleId") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("client_style_id"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("DeletedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("deleted_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("DeletedAt")); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("UpdatedAt")); + + b.HasKey("Id"); + + b.HasIndex("ClientStyleId") + .IsUnique(); + + b.ToTable("player_chat_styles"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Players.PlayerChatStyleOwnedEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ChatStyleId") + .HasColumnType("int") + .HasColumnName("chat_style_id"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("DeletedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("deleted_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("DeletedAt")); + + b.Property("PlayerEntityId") + .HasColumnType("int") + .HasColumnName("player_id"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("UpdatedAt")); + + b.HasKey("Id"); + + b.HasIndex("ChatStyleId"); + + b.HasIndex("PlayerEntityId", "ChatStyleId") + .IsUnique(); + + b.ToTable("player_chat_styles_owned"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Players.PlayerCurrencyEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("Amount") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("amount"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("CurrencyTypeEntityId") + .HasColumnType("int") + .HasColumnName("currency_type_id"); + + b.Property("DeletedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("deleted_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("DeletedAt")); + + b.Property("PlayerEntityId") + .HasColumnType("int") + .HasColumnName("player_id"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("UpdatedAt")); + + b.HasKey("Id"); + + b.HasIndex("CurrencyTypeEntityId"); + + b.HasIndex("PlayerEntityId", "CurrencyTypeEntityId") + .IsUnique(); + + b.ToTable("player_currencies"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Players.PlayerEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("DeletedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("deleted_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("DeletedAt")); + + b.Property("Figure") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(100) + .HasColumnType("varchar(100)") + .HasDefaultValue("hr-115-42.hd-195-19.ch-3030-82.lg-275-1408.fa-1201.ca-1804-64") + .HasColumnName("figure"); + + b.Property("Gender") + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("gender"); + + b.Property("Motto") + .HasMaxLength(512) + .HasColumnType("varchar(512)") + .HasColumnName("motto"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("varchar(512)") + .HasColumnName("name"); + + b.Property("PlayerPerks") + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("perk_flags"); + + b.Property("PlayerStatus") + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("status"); + + b.Property("RoomChatStyleId") + .HasColumnType("int") + .HasColumnName("room_chat_style_id"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("UpdatedAt")); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("players"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Players.PlayerFavoriteRoomsEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("DeletedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("deleted_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("DeletedAt")); + + b.Property("PlayerEntityId") + .HasColumnType("int") + .HasColumnName("player_id"); + + b.Property("RoomEntityId") + .HasColumnType("int") + .HasColumnName("room_id"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("UpdatedAt")); + + b.HasKey("Id"); + + b.HasIndex("RoomEntityId"); + + b.HasIndex("PlayerEntityId", "RoomEntityId") + .IsUnique(); + + b.ToTable("player_favorite_rooms"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Room.RoomBanEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("DateExpires") + .HasColumnType("datetime(6)") + .HasColumnName("date_expires"); + + b.Property("DeletedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("deleted_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("DeletedAt")); + + b.Property("PlayerEntityId") + .HasColumnType("int") + .HasColumnName("player_id"); + + b.Property("RoomEntityId") + .HasColumnType("int") + .HasColumnName("room_id"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("UpdatedAt")); + + b.HasKey("Id"); + + b.HasIndex("PlayerEntityId"); + + b.HasIndex("RoomEntityId", "PlayerEntityId") + .IsUnique(); + + b.ToTable("room_bans"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Room.RoomChatlogEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("DeletedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("deleted_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("DeletedAt")); + + b.Property("Message") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)") + .HasColumnName("message"); + + b.Property("PlayerEntityId") + .HasColumnType("int") + .HasColumnName("player_id"); + + b.Property("RoomEntityId") + .HasColumnType("int") + .HasColumnName("room_id"); + + b.Property("TargetPlayerEntityId") + .HasColumnType("int") + .HasColumnName("target_player_id"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("UpdatedAt")); + + b.HasKey("Id"); + + b.HasIndex("PlayerEntityId"); + + b.HasIndex("RoomEntityId"); + + b.HasIndex("TargetPlayerEntityId"); + + b.ToTable("room_chatlogs"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Room.RoomEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("AllowBlocking") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false) + .HasColumnName("allow_blocking"); + + b.Property("AllowPets") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false) + .HasColumnName("allow_pets"); + + b.Property("AllowPetsEat") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false) + .HasColumnName("allow_pets_eat"); + + b.Property("BanType") + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("ban_type"); + + b.Property("ChatBubbleType") + .HasColumnType("int") + .HasDefaultValue(1) + .HasColumnName("chat_bubble_type"); + + b.Property("ChatDistance") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(50) + .HasColumnName("chat_distance"); + + b.Property("ChatFloodType") + .HasColumnType("int") + .HasDefaultValue(2) + .HasColumnName("chat_flood_type"); + + b.Property("ChatModeType") + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("chat_mode_type"); + + b.Property("ChatSpeedType") + .HasColumnType("int") + .HasDefaultValue(1) + .HasColumnName("chat_speed_type"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("DeletedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("deleted_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("DeletedAt")); + + b.Property("Description") + .HasMaxLength(512) + .HasColumnType("varchar(512)") + .HasColumnName("description"); + + b.Property("DoorMode") + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("door_mode"); + + b.Property("HideWalls") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false) + .HasColumnName("hide_walls"); + + b.Property("KickType") + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("kick_type"); + + b.Property("LastActive") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("last_active"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("LastActive")); + + b.Property("MuteType") + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("mute_type"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("varchar(512)") + .HasColumnName("name"); + + b.Property("NavigatorCategoryEntityId") + .HasColumnType("int") + .HasColumnName("category_id"); + + b.Property("PaintFloor") + .ValueGeneratedOnAdd() + .HasColumnType("double") + .HasDefaultValue(0.0) + .HasColumnName("paint_floor"); + + b.Property("PaintLandscape") + .ValueGeneratedOnAdd() + .HasColumnType("double") + .HasDefaultValue(0.0) + .HasColumnName("paint_landscape"); + + b.Property("PaintWall") + .ValueGeneratedOnAdd() + .HasColumnType("double") + .HasDefaultValue(0.0) + .HasColumnName("paint_wall"); + + b.Property("Password") + .HasMaxLength(512) + .HasColumnType("varchar(512)") + .HasColumnName("password"); + + b.Property("PlayerEntityId") + .HasColumnType("int") + .HasColumnName("player_id"); + + b.Property("PlayersMax") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(25) + .HasColumnName("players_max"); + + b.Property("RoomModelEntityId") + .HasColumnType("int") + .HasColumnName("model_id"); + + b.Property("ThicknessFloor") + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("thickness_floor"); + + b.Property("ThicknessWall") + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("thickness_wall"); + + b.Property("TradeType") + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("trade_type"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("UpdatedAt")); + + b.Property("UsersNow") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("users_now"); + + b.Property("WallHeight") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(-1) + .HasColumnName("wall_height"); + + b.HasKey("Id"); + + b.HasIndex("NavigatorCategoryEntityId"); + + b.HasIndex("PlayerEntityId"); + + b.HasIndex("RoomModelEntityId"); + + b.ToTable("rooms"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Room.RoomEntryLogEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("DeletedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("deleted_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("DeletedAt")); + + b.Property("PlayerEntityId") + .HasColumnType("int") + .HasColumnName("player_id"); + + b.Property("RoomEntityId") + .HasColumnType("int") + .HasColumnName("room_id"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("UpdatedAt")); + + b.HasKey("Id"); + + b.HasIndex("PlayerEntityId"); + + b.HasIndex("RoomEntityId"); + + b.ToTable("room_entry_logs"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Room.RoomModelEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("Custom") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false) + .HasColumnName("custom"); + + b.Property("DeletedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("deleted_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("DeletedAt")); + + b.Property("DoorRotation") + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("door_rotation"); + + b.Property("DoorX") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("door_x"); + + b.Property("DoorY") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("door_y"); + + b.Property("Enabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true) + .HasColumnName("enabled"); + + b.Property("Model") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("longtext") + .HasColumnName("model"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("varchar(512)") + .HasColumnName("name"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("UpdatedAt")); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("room_models"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Room.RoomMuteEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("DateExpires") + .HasColumnType("datetime(6)") + .HasColumnName("date_expires"); + + b.Property("DeletedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("deleted_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("DeletedAt")); + + b.Property("PlayerEntityId") + .HasColumnType("int") + .HasColumnName("player_id"); + + b.Property("RoomEntityId") + .HasColumnType("int") + .HasColumnName("room_id"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("UpdatedAt")); + + b.HasKey("Id"); + + b.HasIndex("PlayerEntityId"); + + b.HasIndex("RoomEntityId", "PlayerEntityId") + .IsUnique(); + + b.ToTable("room_mutes"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Room.RoomRightEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("DeletedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("deleted_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("DeletedAt")); + + b.Property("PlayerEntityId") + .HasColumnType("int") + .HasColumnName("player_id"); + + b.Property("RoomEntityId") + .HasColumnType("int") + .HasColumnName("room_id"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("UpdatedAt")); + + b.HasKey("Id"); + + b.HasIndex("PlayerEntityId"); + + b.HasIndex("RoomEntityId", "PlayerEntityId") + .IsUnique(); + + b.ToTable("room_rights"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Security.SecurityTicketEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("DeletedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("deleted_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("DeletedAt")); + + b.Property("IpAddress") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("varchar(512)") + .HasColumnName("ip_address"); + + b.Property("IsLocked") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false) + .HasColumnName("is_locked"); + + b.Property("PlayerEntityId") + .HasColumnType("int") + .HasColumnName("player_id"); + + b.Property("Ticket") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("varchar(512)") + .HasColumnName("ticket"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("UpdatedAt")); + + b.HasKey("Id"); + + b.HasIndex("PlayerEntityId") + .IsUnique(); + + b.HasIndex("Ticket") + .IsUnique(); + + b.ToTable("security_tickets"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Tracking.PerformanceLogEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("AverageFrameRate") + .HasColumnType("int") + .HasColumnName("average_frame_rate"); + + b.Property("Browser") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("varchar(512)") + .HasColumnName("browser"); + + b.Property("ElapsedTime") + .HasColumnType("int") + .HasColumnName("elapsed_time"); + + b.Property("FlashVersion") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("varchar(512)") + .HasColumnName("flash_version"); + + b.Property("GarbageCollections") + .HasColumnType("int") + .HasColumnName("garbage_collections"); + + b.Property("IPAddress") + .IsRequired() + .HasMaxLength(45) + .HasColumnType("varchar(45)") + .HasColumnName("ip_address"); + + b.Property("IsDebugger") + .HasColumnType("tinyint(1)") + .HasColumnName("is_debugger"); + + b.Property("MemoryUsage") + .HasColumnType("int") + .HasColumnName("memory_usage"); + + b.Property("OS") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("varchar(512)") + .HasColumnName("os"); + + b.Property("UserAgent") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("varchar(512)") + .HasColumnName("user_agent"); + + b.HasKey("Id"); + + b.HasIndex("ElapsedTime"); + + b.HasIndex("IPAddress"); + + b.ToTable("performance_logs"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Catalog.CatalogOfferEntity", b => + { + b.HasOne("Turbo.Database.Entities.Catalog.CatalogPageEntity", "Page") + .WithMany("Offers") + .HasForeignKey("CatalogPageEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Turbo.Database.Entities.Catalog.CurrencyTypeEntity", "CurrencyTypeEntity") + .WithMany("CatalogOffers") + .HasForeignKey("CurrencyTypeId"); + + b.Navigation("CurrencyTypeEntity"); + + b.Navigation("Page"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Catalog.CatalogPageEntity", b => + { + b.HasOne("Turbo.Database.Entities.Catalog.CatalogPageEntity", "ParentEntity") + .WithMany("Children") + .HasForeignKey("ParentEntityId"); + + b.Navigation("ParentEntity"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Catalog.CatalogProductEntity", b => + { + b.HasOne("Turbo.Database.Entities.Catalog.CatalogOfferEntity", "Offer") + .WithMany("Products") + .HasForeignKey("CatalogOfferEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Turbo.Database.Entities.Furniture.FurnitureDefinitionEntity", "FurnitureDefinition") + .WithMany() + .HasForeignKey("FurnitureDefinitionEntityId"); + + b.Navigation("FurnitureDefinition"); + + b.Navigation("Offer"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Furniture.FurnitureEntity", b => + { + b.HasOne("Turbo.Database.Entities.Furniture.FurnitureDefinitionEntity", "FurnitureDefinitionEntity") + .WithMany("Furnitures") + .HasForeignKey("FurnitureDefinitionEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Turbo.Database.Entities.Players.PlayerEntity", "PlayerEntity") + .WithMany("Furniture") + .HasForeignKey("PlayerEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Turbo.Database.Entities.Room.RoomEntity", "RoomEntity") + .WithMany() + .HasForeignKey("RoomEntityId"); + + b.Navigation("FurnitureDefinitionEntity"); + + b.Navigation("PlayerEntity"); + + b.Navigation("RoomEntity"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Furniture.FurnitureTeleportLinkEntity", b => + { + b.HasOne("Turbo.Database.Entities.Furniture.FurnitureEntity", "FurnitureEntityOne") + .WithMany() + .HasForeignKey("FurnitureEntityOneId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Turbo.Database.Entities.Furniture.FurnitureEntity", "FurnitureEntityTwo") + .WithMany() + .HasForeignKey("FurnitureEntityTwoId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("FurnitureEntityOne"); + + b.Navigation("FurnitureEntityTwo"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Messenger.MessengerBlockedEntity", b => + { + b.HasOne("Turbo.Database.Entities.Players.PlayerEntity", "BlockedPlayerEntity") + .WithMany() + .HasForeignKey("BlockedPlayerEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Turbo.Database.Entities.Players.PlayerEntity", "PlayerEntity") + .WithMany("MessengerBlocked") + .HasForeignKey("PlayerEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("BlockedPlayerEntity"); + + b.Navigation("PlayerEntity"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Messenger.MessengerCategoryEntity", b => + { + b.HasOne("Turbo.Database.Entities.Players.PlayerEntity", "PlayerEntity") + .WithMany("MessengerCategories") + .HasForeignKey("PlayerEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PlayerEntity"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Messenger.MessengerFriendEntity", b => + { + b.HasOne("Turbo.Database.Entities.Players.PlayerEntity", "FriendPlayerEntity") + .WithMany() + .HasForeignKey("FriendPlayerEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Turbo.Database.Entities.Messenger.MessengerCategoryEntity", "MessengerCategoryEntity") + .WithMany() + .HasForeignKey("MessengerCategoryEntityId"); + + b.HasOne("Turbo.Database.Entities.Players.PlayerEntity", "PlayerEntity") + .WithMany("MessengerFriends") + .HasForeignKey("PlayerEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("FriendPlayerEntity"); + + b.Navigation("MessengerCategoryEntity"); + + b.Navigation("PlayerEntity"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Messenger.MessengerIgnoredEntity", b => + { + b.HasOne("Turbo.Database.Entities.Players.PlayerEntity", "IgnoredPlayerEntity") + .WithMany() + .HasForeignKey("IgnoredPlayerEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Turbo.Database.Entities.Players.PlayerEntity", "PlayerEntity") + .WithMany("MessengerIgnored") + .HasForeignKey("PlayerEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IgnoredPlayerEntity"); + + b.Navigation("PlayerEntity"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Messenger.MessengerMessageEntity", b => + { + b.HasOne("Turbo.Database.Entities.Players.PlayerEntity", "ReceiverPlayerEntity") + .WithMany("MessengerMessagesReceived") + .HasForeignKey("ReceiverPlayerEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Turbo.Database.Entities.Players.PlayerEntity", "SenderPlayerEntity") + .WithMany("MessengerMessagesSent") + .HasForeignKey("SenderPlayerEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ReceiverPlayerEntity"); + + b.Navigation("SenderPlayerEntity"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Messenger.MessengerRequestEntity", b => + { + b.HasOne("Turbo.Database.Entities.Players.PlayerEntity", "PlayerEntity") + .WithMany("MessengerRequestsSent") + .HasForeignKey("PlayerEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Turbo.Database.Entities.Players.PlayerEntity", "RequestedPlayerEntity") + .WithMany("MessengerRequests") + .HasForeignKey("RequestedPlayerEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PlayerEntity"); + + b.Navigation("RequestedPlayerEntity"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Players.PlayerBadgeEntity", b => + { + b.HasOne("Turbo.Database.Entities.Players.PlayerEntity", "PlayerEntity") + .WithMany("PlayerBadges") + .HasForeignKey("PlayerEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PlayerEntity"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Players.PlayerChatStyleOwnedEntity", b => + { + b.HasOne("Turbo.Database.Entities.Players.PlayerChatStyleEntity", "ChatStyle") + .WithMany("OwnedChatStyles") + .HasForeignKey("ChatStyleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Turbo.Database.Entities.Players.PlayerEntity", "PlayerEntity") + .WithMany("PlayerOwnedChatStyles") + .HasForeignKey("PlayerEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChatStyle"); + + b.Navigation("PlayerEntity"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Players.PlayerCurrencyEntity", b => + { + b.HasOne("Turbo.Database.Entities.Catalog.CurrencyTypeEntity", "CurrencyTypeEntity") + .WithMany("PlayerCurrencies") + .HasForeignKey("CurrencyTypeEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Turbo.Database.Entities.Players.PlayerEntity", "PlayerEntity") + .WithMany("PlayerCurrencies") + .HasForeignKey("PlayerEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("CurrencyTypeEntity"); + + b.Navigation("PlayerEntity"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Players.PlayerFavoriteRoomsEntity", b => + { + b.HasOne("Turbo.Database.Entities.Players.PlayerEntity", "PlayerEntity") + .WithMany() + .HasForeignKey("PlayerEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Turbo.Database.Entities.Room.RoomEntity", "RoomEntity") + .WithMany() + .HasForeignKey("RoomEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PlayerEntity"); + + b.Navigation("RoomEntity"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Room.RoomBanEntity", b => + { + b.HasOne("Turbo.Database.Entities.Players.PlayerEntity", "PlayerEntity") + .WithMany("RoomBans") + .HasForeignKey("PlayerEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Turbo.Database.Entities.Room.RoomEntity", "RoomEntity") + .WithMany("RoomBans") + .HasForeignKey("RoomEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PlayerEntity"); + + b.Navigation("RoomEntity"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Room.RoomChatlogEntity", b => + { + b.HasOne("Turbo.Database.Entities.Players.PlayerEntity", "PlayerEntity") + .WithMany("RoomChatlogs") + .HasForeignKey("PlayerEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Turbo.Database.Entities.Room.RoomEntity", "RoomEntity") + .WithMany("RoomChats") + .HasForeignKey("RoomEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Turbo.Database.Entities.Players.PlayerEntity", "TargetPlayerEntity") + .WithMany() + .HasForeignKey("TargetPlayerEntityId"); + + b.Navigation("PlayerEntity"); + + b.Navigation("RoomEntity"); + + b.Navigation("TargetPlayerEntity"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Room.RoomEntity", b => + { + b.HasOne("Turbo.Database.Entities.Navigator.NavigatorFlatCategoryEntity", "NavigatorFlatCategoryEntity") + .WithMany() + .HasForeignKey("NavigatorCategoryEntityId"); + + b.HasOne("Turbo.Database.Entities.Players.PlayerEntity", "PlayerEntity") + .WithMany("Rooms") + .HasForeignKey("PlayerEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Turbo.Database.Entities.Room.RoomModelEntity", "RoomModelEntity") + .WithMany() + .HasForeignKey("RoomModelEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("NavigatorFlatCategoryEntity"); + + b.Navigation("PlayerEntity"); + + b.Navigation("RoomModelEntity"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Room.RoomEntryLogEntity", b => + { + b.HasOne("Turbo.Database.Entities.Players.PlayerEntity", "PlayerEntity") + .WithMany() + .HasForeignKey("PlayerEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Turbo.Database.Entities.Room.RoomEntity", "RoomEntity") + .WithMany() + .HasForeignKey("RoomEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PlayerEntity"); + + b.Navigation("RoomEntity"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Room.RoomMuteEntity", b => + { + b.HasOne("Turbo.Database.Entities.Players.PlayerEntity", "PlayerEntity") + .WithMany("RoomMutes") + .HasForeignKey("PlayerEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Turbo.Database.Entities.Room.RoomEntity", "RoomEntity") + .WithMany("RoomMutes") + .HasForeignKey("RoomEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PlayerEntity"); + + b.Navigation("RoomEntity"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Room.RoomRightEntity", b => + { + b.HasOne("Turbo.Database.Entities.Players.PlayerEntity", "PlayerEntity") + .WithMany("RoomRights") + .HasForeignKey("PlayerEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Turbo.Database.Entities.Room.RoomEntity", "RoomEntity") + .WithMany("RoomRights") + .HasForeignKey("RoomEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PlayerEntity"); + + b.Navigation("RoomEntity"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Security.SecurityTicketEntity", b => + { + b.HasOne("Turbo.Database.Entities.Players.PlayerEntity", "PlayerEntity") + .WithMany("SecurityTickets") + .HasForeignKey("PlayerEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PlayerEntity"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Catalog.CatalogOfferEntity", b => + { + b.Navigation("Products"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Catalog.CatalogPageEntity", b => + { + b.Navigation("Children"); + + b.Navigation("Offers"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Catalog.CurrencyTypeEntity", b => + { + b.Navigation("CatalogOffers"); + + b.Navigation("PlayerCurrencies"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Furniture.FurnitureDefinitionEntity", b => + { + b.Navigation("Furnitures"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Players.PlayerChatStyleEntity", b => + { + b.Navigation("OwnedChatStyles"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Players.PlayerEntity", b => + { + b.Navigation("Furniture"); + + b.Navigation("MessengerBlocked"); + + b.Navigation("MessengerCategories"); + + b.Navigation("MessengerFriends"); + + b.Navigation("MessengerIgnored"); + + b.Navigation("MessengerMessagesReceived"); + + b.Navigation("MessengerMessagesSent"); + + b.Navigation("MessengerRequests"); + + b.Navigation("MessengerRequestsSent"); + + b.Navigation("PlayerBadges"); + + b.Navigation("PlayerCurrencies"); + + b.Navigation("PlayerOwnedChatStyles"); + + b.Navigation("RoomBans"); + + b.Navigation("RoomChatlogs"); + + b.Navigation("RoomMutes"); + + b.Navigation("RoomRights"); + + b.Navigation("Rooms"); + + b.Navigation("SecurityTickets"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Room.RoomEntity", b => + { + b.Navigation("RoomBans"); + + b.Navigation("RoomChats"); + + b.Navigation("RoomMutes"); + + b.Navigation("RoomRights"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Turbo.Database/Migrations/20260208131801_AddMessengerTables.cs b/Turbo.Database/Migrations/20260208131801_AddMessengerTables.cs new file mode 100644 index 00000000..35aa9712 --- /dev/null +++ b/Turbo.Database/Migrations/20260208131801_AddMessengerTables.cs @@ -0,0 +1,227 @@ +using System; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Turbo.Database.Migrations +{ + /// + public partial class AddMessengerTables : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder + .CreateTable( + name: "messenger_blocked", + columns: table => new + { + id = table + .Column(type: "int", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.IdentityColumn + ), + player_id = table.Column(type: "int", nullable: false), + blocked_player_id = table.Column(type: "int", nullable: false), + created_at = table + .Column(type: "datetime(6)", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.IdentityColumn + ), + updated_at = table + .Column(type: "datetime(6)", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.ComputedColumn + ), + deleted_at = table + .Column(type: "datetime(6)", nullable: true) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.ComputedColumn + ), + }, + constraints: table => + { + table.PrimaryKey("PK_messenger_blocked", x => x.id); + table.ForeignKey( + name: "FK_messenger_blocked_players_blocked_player_id", + column: x => x.blocked_player_id, + principalTable: "players", + principalColumn: "id", + onDelete: ReferentialAction.Cascade + ); + table.ForeignKey( + name: "FK_messenger_blocked_players_player_id", + column: x => x.player_id, + principalTable: "players", + principalColumn: "id", + onDelete: ReferentialAction.Cascade + ); + } + ) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder + .CreateTable( + name: "messenger_ignored", + columns: table => new + { + id = table + .Column(type: "int", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.IdentityColumn + ), + player_id = table.Column(type: "int", nullable: false), + ignored_player_id = table.Column(type: "int", nullable: false), + created_at = table + .Column(type: "datetime(6)", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.IdentityColumn + ), + updated_at = table + .Column(type: "datetime(6)", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.ComputedColumn + ), + deleted_at = table + .Column(type: "datetime(6)", nullable: true) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.ComputedColumn + ), + }, + constraints: table => + { + table.PrimaryKey("PK_messenger_ignored", x => x.id); + table.ForeignKey( + name: "FK_messenger_ignored_players_ignored_player_id", + column: x => x.ignored_player_id, + principalTable: "players", + principalColumn: "id", + onDelete: ReferentialAction.Cascade + ); + table.ForeignKey( + name: "FK_messenger_ignored_players_player_id", + column: x => x.player_id, + principalTable: "players", + principalColumn: "id", + onDelete: ReferentialAction.Cascade + ); + } + ) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder + .CreateTable( + name: "messenger_messages", + columns: table => new + { + id = table + .Column(type: "int", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.IdentityColumn + ), + sender_id = table.Column(type: "int", nullable: false), + receiver_id = table.Column(type: "int", nullable: false), + message = table + .Column(type: "varchar(512)", maxLength: 512, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + timestamp = table.Column(type: "datetime(6)", nullable: false), + created_at = table + .Column(type: "datetime(6)", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.IdentityColumn + ), + updated_at = table + .Column(type: "datetime(6)", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.ComputedColumn + ), + deleted_at = table + .Column(type: "datetime(6)", nullable: true) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.ComputedColumn + ), + }, + constraints: table => + { + table.PrimaryKey("PK_messenger_messages", x => x.id); + table.ForeignKey( + name: "FK_messenger_messages_players_receiver_id", + column: x => x.receiver_id, + principalTable: "players", + principalColumn: "id", + onDelete: ReferentialAction.Cascade + ); + table.ForeignKey( + name: "FK_messenger_messages_players_sender_id", + column: x => x.sender_id, + principalTable: "players", + principalColumn: "id", + onDelete: ReferentialAction.Cascade + ); + } + ) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateIndex( + name: "IX_messenger_blocked_blocked_player_id", + table: "messenger_blocked", + column: "blocked_player_id" + ); + + migrationBuilder.CreateIndex( + name: "IX_messenger_blocked_player_id_blocked_player_id", + table: "messenger_blocked", + columns: new[] { "player_id", "blocked_player_id" }, + unique: true + ); + + migrationBuilder.CreateIndex( + name: "IX_messenger_ignored_ignored_player_id", + table: "messenger_ignored", + column: "ignored_player_id" + ); + + migrationBuilder.CreateIndex( + name: "IX_messenger_ignored_player_id_ignored_player_id", + table: "messenger_ignored", + columns: new[] { "player_id", "ignored_player_id" }, + unique: true + ); + + migrationBuilder.CreateIndex( + name: "IX_messenger_messages_receiver_id_sender_id_timestamp", + table: "messenger_messages", + columns: new[] { "receiver_id", "sender_id", "timestamp" } + ); + + migrationBuilder.CreateIndex( + name: "IX_messenger_messages_sender_id_receiver_id_timestamp", + table: "messenger_messages", + columns: new[] { "sender_id", "receiver_id", "timestamp" } + ); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable(name: "messenger_blocked"); + + migrationBuilder.DropTable(name: "messenger_ignored"); + + migrationBuilder.DropTable(name: "messenger_messages"); + } + } +} diff --git a/Turbo.Database/Migrations/20260208142253_AddMessengerDeliveredFlag.Designer.cs b/Turbo.Database/Migrations/20260208142253_AddMessengerDeliveredFlag.Designer.cs new file mode 100644 index 00000000..65e73069 --- /dev/null +++ b/Turbo.Database/Migrations/20260208142253_AddMessengerDeliveredFlag.Designer.cs @@ -0,0 +1,2586 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Turbo.Database.Context; + +#nullable disable + +namespace Turbo.Database.Migrations +{ + [DbContext(typeof(TurboDbContext))] + [Migration("20260208142253_AddMessengerDeliveredFlag")] + partial class AddMessengerDeliveredFlag + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Turbo.Database.Entities.Catalog.CatalogOfferEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CanBundle") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true) + .HasColumnName("can_bundle"); + + b.Property("CanGift") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true) + .HasColumnName("can_gift"); + + b.Property("CatalogPageEntityId") + .HasColumnType("int") + .HasColumnName("page_id"); + + b.Property("ClubLevel") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("club_level"); + + b.Property("CostCredits") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("cost_credits"); + + b.Property("CostCurrency") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("cost_currency"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("CurrencyTypeId") + .HasColumnType("int") + .HasColumnName("currency_type_id"); + + b.Property("DeletedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("deleted_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("DeletedAt")); + + b.Property("LocalizationId") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("varchar(512)") + .HasColumnName("localization_id"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("UpdatedAt")); + + b.Property("Visible") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true) + .HasColumnName("visible"); + + b.HasKey("Id"); + + b.HasIndex("CatalogPageEntityId"); + + b.HasIndex("CurrencyTypeId"); + + b.ToTable("catalog_offers"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Catalog.CatalogPageEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("DeletedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("deleted_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("DeletedAt")); + + b.Property("Icon") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("icon"); + + b.PrimitiveCollection("ImageData") + .HasColumnType("longtext") + .HasColumnName("image_data"); + + b.Property("Layout") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(50) + .HasColumnType("varchar(50)") + .HasDefaultValue("default_3x3") + .HasColumnName("layout"); + + b.Property("Localization") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("varchar(50)") + .HasColumnName("localization"); + + b.Property("Name") + .HasMaxLength(50) + .HasColumnType("varchar(50)") + .HasColumnName("name"); + + b.Property("ParentEntityId") + .HasColumnType("int") + .HasColumnName("parent_id"); + + b.Property("SortOrder") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("sort_order"); + + b.PrimitiveCollection("TextData") + .HasColumnType("longtext") + .HasColumnName("text_data"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("UpdatedAt")); + + b.Property("Visible") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true) + .HasColumnName("visible"); + + b.HasKey("Id"); + + b.HasIndex("ParentEntityId"); + + b.ToTable("catalog_pages"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Catalog.CatalogProductEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CatalogOfferEntityId") + .HasColumnType("int") + .HasColumnName("offer_id"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("DeletedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("deleted_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("DeletedAt")); + + b.Property("ExtraParam") + .HasMaxLength(512) + .HasColumnType("varchar(512)") + .HasColumnName("extra_param"); + + b.Property("FurnitureDefinitionEntityId") + .HasColumnType("int") + .HasColumnName("definition_id"); + + b.Property("ProductType") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("product_type"); + + b.Property("Quantity") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(1) + .HasColumnName("quantity"); + + b.Property("UniqueRemaining") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("unique_remaining"); + + b.Property("UniqueSize") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("unique_size"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("UpdatedAt")); + + b.HasKey("Id"); + + b.HasIndex("CatalogOfferEntityId"); + + b.HasIndex("FurnitureDefinitionEntityId"); + + b.ToTable("catalog_products"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Catalog.CurrencyTypeEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ActivityPointType") + .HasColumnType("int") + .HasColumnName("activity_point_type"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("CurrencyType") + .HasColumnType("int") + .HasColumnName("type"); + + b.Property("DeletedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("deleted_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("DeletedAt")); + + b.Property("Enabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true) + .HasColumnName("enabled"); + + b.Property("Name") + .HasMaxLength(512) + .HasColumnType("varchar(512)") + .HasColumnName("name"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("UpdatedAt")); + + b.HasKey("Id"); + + b.ToTable("currency_types"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Furniture.FurnitureDefinitionEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CanGroup") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true) + .HasColumnName("can_group"); + + b.Property("CanLay") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false) + .HasColumnName("can_lay"); + + b.Property("CanRecycle") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false) + .HasColumnName("can_recycle"); + + b.Property("CanSell") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true) + .HasColumnName("can_sell"); + + b.Property("CanSit") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false) + .HasColumnName("can_sit"); + + b.Property("CanStack") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true) + .HasColumnName("can_stack"); + + b.Property("CanTrade") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true) + .HasColumnName("can_trade"); + + b.Property("CanWalk") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false) + .HasColumnName("can_walk"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("DeletedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("deleted_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("DeletedAt")); + + b.Property("ExtraData") + .HasMaxLength(512) + .HasColumnType("varchar(512)") + .HasColumnName("extra_data"); + + b.Property("FurniCategory") + .HasColumnType("int") + .HasDefaultValue(1) + .HasColumnName("category"); + + b.Property("Length") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(1) + .HasColumnName("length"); + + b.Property("Logic") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(50) + .HasColumnType("varchar(50)") + .HasDefaultValue("none") + .HasColumnName("logic"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("varchar(512)") + .HasColumnName("name"); + + b.Property("ProductType") + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("type"); + + b.Property("SpriteId") + .HasColumnType("int") + .HasColumnName("sprite_id"); + + b.Property("StackHeight") + .ValueGeneratedOnAdd() + .HasColumnType("double(10,3)") + .HasDefaultValue(0.0) + .HasColumnName("stack_height"); + + b.Property("TotalStates") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("total_states"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("UpdatedAt")); + + b.Property("UsagePolicy") + .HasColumnType("int") + .HasDefaultValue(1) + .HasColumnName("usage_policy"); + + b.Property("Width") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("width"); + + b.HasKey("Id"); + + b.HasIndex("SpriteId", "ProductType", "FurniCategory") + .IsUnique(); + + b.ToTable("furniture_definitions"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Furniture.FurnitureEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("DeletedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("deleted_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("DeletedAt")); + + b.Property("ExtraData") + .HasMaxLength(512) + .HasColumnType("varchar(512)") + .HasColumnName("extra_data"); + + b.Property("FurnitureDefinitionEntityId") + .HasColumnType("int") + .HasColumnName("definition_id"); + + b.Property("PlayerEntityId") + .HasColumnType("int") + .HasColumnName("player_id"); + + b.Property("RoomEntityId") + .HasColumnType("int") + .HasColumnName("room_id"); + + b.Property("Rotation") + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("direction"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("UpdatedAt")); + + b.Property("WallOffset") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("wall_offset"); + + b.Property("X") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("x"); + + b.Property("Y") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("y"); + + b.Property("Z") + .ValueGeneratedOnAdd() + .HasColumnType("double(10,3)") + .HasDefaultValue(0.0) + .HasColumnName("z"); + + b.HasKey("Id"); + + b.HasIndex("FurnitureDefinitionEntityId"); + + b.HasIndex("PlayerEntityId"); + + b.HasIndex("RoomEntityId"); + + b.ToTable("furniture"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Furniture.FurnitureTeleportLinkEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("DeletedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("deleted_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("DeletedAt")); + + b.Property("FurnitureEntityOneId") + .HasColumnType("int") + .HasColumnName("furniture_one_id"); + + b.Property("FurnitureEntityTwoId") + .HasColumnType("int") + .HasColumnName("furniture_two_id"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("UpdatedAt")); + + b.HasKey("Id"); + + b.HasIndex("FurnitureEntityOneId") + .IsUnique(); + + b.HasIndex("FurnitureEntityTwoId") + .IsUnique(); + + b.ToTable("furniture_teleport_links"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Messenger.MessengerBlockedEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("BlockedPlayerEntityId") + .HasColumnType("int") + .HasColumnName("blocked_player_id"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("DeletedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("deleted_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("DeletedAt")); + + b.Property("PlayerEntityId") + .HasColumnType("int") + .HasColumnName("player_id"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("UpdatedAt")); + + b.HasKey("Id"); + + b.HasIndex("BlockedPlayerEntityId"); + + b.HasIndex("PlayerEntityId", "BlockedPlayerEntityId") + .IsUnique(); + + b.ToTable("messenger_blocked"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Messenger.MessengerCategoryEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("DeletedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("deleted_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("DeletedAt")); + + b.Property("Name") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("varchar(512)") + .HasColumnName("name"); + + b.Property("PlayerEntityId") + .HasColumnType("int") + .HasColumnName("player_id"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("UpdatedAt")); + + b.HasKey("Id"); + + b.HasIndex("PlayerEntityId", "Name") + .IsUnique(); + + b.ToTable("messenger_categories"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Messenger.MessengerFriendEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("DeletedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("deleted_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("DeletedAt")); + + b.Property("FriendPlayerEntityId") + .HasColumnType("int") + .HasColumnName("requested_id"); + + b.Property("MessengerCategoryEntityId") + .HasColumnType("int") + .HasColumnName("category_id"); + + b.Property("PlayerEntityId") + .HasColumnType("int") + .HasColumnName("player_id"); + + b.Property("RelationType") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("relation"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("UpdatedAt")); + + b.HasKey("Id"); + + b.HasIndex("FriendPlayerEntityId"); + + b.HasIndex("MessengerCategoryEntityId"); + + b.HasIndex("PlayerEntityId", "FriendPlayerEntityId") + .IsUnique(); + + b.ToTable("messenger_friends"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Messenger.MessengerIgnoredEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("DeletedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("deleted_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("DeletedAt")); + + b.Property("IgnoredPlayerEntityId") + .HasColumnType("int") + .HasColumnName("ignored_player_id"); + + b.Property("PlayerEntityId") + .HasColumnType("int") + .HasColumnName("player_id"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("UpdatedAt")); + + b.HasKey("Id"); + + b.HasIndex("IgnoredPlayerEntityId"); + + b.HasIndex("PlayerEntityId", "IgnoredPlayerEntityId") + .IsUnique(); + + b.ToTable("messenger_ignored"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Messenger.MessengerMessageEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("DeletedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("deleted_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("DeletedAt")); + + b.Property("Delivered") + .HasColumnType("tinyint(1)") + .HasColumnName("delivered"); + + b.Property("Message") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("varchar(512)") + .HasColumnName("message"); + + b.Property("ReceiverPlayerEntityId") + .HasColumnType("int") + .HasColumnName("receiver_id"); + + b.Property("SenderPlayerEntityId") + .HasColumnType("int") + .HasColumnName("sender_id"); + + b.Property("Timestamp") + .HasColumnType("datetime(6)") + .HasColumnName("timestamp"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("UpdatedAt")); + + b.HasKey("Id"); + + b.HasIndex("ReceiverPlayerEntityId", "SenderPlayerEntityId", "Timestamp"); + + b.HasIndex("SenderPlayerEntityId", "ReceiverPlayerEntityId", "Timestamp"); + + b.ToTable("messenger_messages"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Messenger.MessengerRequestEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("DeletedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("deleted_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("DeletedAt")); + + b.Property("PlayerEntityId") + .HasColumnType("int") + .HasColumnName("player_id"); + + b.Property("RequestedPlayerEntityId") + .HasColumnType("int") + .HasColumnName("requested_id"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("UpdatedAt")); + + b.HasKey("Id"); + + b.HasIndex("RequestedPlayerEntityId"); + + b.HasIndex("PlayerEntityId", "RequestedPlayerEntityId") + .IsUnique(); + + b.ToTable("messenger_requests"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Navigator.NavigatorEventCategoryEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("DeletedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("deleted_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("DeletedAt")); + + b.Property("Name") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("varchar(512)") + .HasColumnName("name"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("UpdatedAt")); + + b.Property("Visible") + .HasColumnType("tinyint(1)") + .HasColumnName("visible"); + + b.HasKey("Id"); + + b.ToTable("navigator_eventcats"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Navigator.NavigatorFlatCategoryEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("Automatic") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true) + .HasColumnName("automatic"); + + b.Property("AutomaticCategory") + .HasMaxLength(512) + .HasColumnType("varchar(512)") + .HasColumnName("automatic_category"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("DeletedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("deleted_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("DeletedAt")); + + b.Property("GlobalCategory") + .HasMaxLength(512) + .HasColumnType("varchar(512)") + .HasColumnName("global_category"); + + b.Property("MinRank") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(1) + .HasColumnName("min_rank"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("varchar(512)") + .HasColumnName("name"); + + b.Property("OrderNum") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("order_num"); + + b.Property("StaffOnly") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false) + .HasColumnName("staff_only"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("UpdatedAt")); + + b.Property("Visible") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true) + .HasColumnName("visible"); + + b.HasKey("Id"); + + b.ToTable("navigator_flatcats"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Navigator.NavigatorTopLevelContextEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("DeletedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("deleted_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("DeletedAt")); + + b.Property("OrderNum") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("order_num"); + + b.Property("SearchCode") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("varchar(512)") + .HasColumnName("search_code"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("UpdatedAt")); + + b.Property("Visible") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true) + .HasColumnName("visible"); + + b.HasKey("Id"); + + b.HasIndex("SearchCode") + .IsUnique(); + + b.ToTable("navigator_top_level_contexts"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Players.PlayerBadgeEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("BadgeCode") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("varchar(512)") + .HasColumnName("badge_code"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("DeletedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("deleted_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("DeletedAt")); + + b.Property("PlayerEntityId") + .HasColumnType("int") + .HasColumnName("player_id"); + + b.Property("SlotId") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("slot_id"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("UpdatedAt")); + + b.HasKey("Id"); + + b.HasIndex("PlayerEntityId", "BadgeCode") + .IsUnique(); + + b.ToTable("player_badges"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Players.PlayerChatStyleEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClientStyleId") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("client_style_id"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("DeletedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("deleted_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("DeletedAt")); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("UpdatedAt")); + + b.HasKey("Id"); + + b.HasIndex("ClientStyleId") + .IsUnique(); + + b.ToTable("player_chat_styles"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Players.PlayerChatStyleOwnedEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ChatStyleId") + .HasColumnType("int") + .HasColumnName("chat_style_id"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("DeletedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("deleted_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("DeletedAt")); + + b.Property("PlayerEntityId") + .HasColumnType("int") + .HasColumnName("player_id"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("UpdatedAt")); + + b.HasKey("Id"); + + b.HasIndex("ChatStyleId"); + + b.HasIndex("PlayerEntityId", "ChatStyleId") + .IsUnique(); + + b.ToTable("player_chat_styles_owned"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Players.PlayerCurrencyEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("Amount") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("amount"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("CurrencyTypeEntityId") + .HasColumnType("int") + .HasColumnName("currency_type_id"); + + b.Property("DeletedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("deleted_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("DeletedAt")); + + b.Property("PlayerEntityId") + .HasColumnType("int") + .HasColumnName("player_id"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("UpdatedAt")); + + b.HasKey("Id"); + + b.HasIndex("CurrencyTypeEntityId"); + + b.HasIndex("PlayerEntityId", "CurrencyTypeEntityId") + .IsUnique(); + + b.ToTable("player_currencies"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Players.PlayerEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("DeletedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("deleted_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("DeletedAt")); + + b.Property("Figure") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(100) + .HasColumnType("varchar(100)") + .HasDefaultValue("hr-115-42.hd-195-19.ch-3030-82.lg-275-1408.fa-1201.ca-1804-64") + .HasColumnName("figure"); + + b.Property("Gender") + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("gender"); + + b.Property("Motto") + .HasMaxLength(512) + .HasColumnType("varchar(512)") + .HasColumnName("motto"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("varchar(512)") + .HasColumnName("name"); + + b.Property("PlayerPerks") + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("perk_flags"); + + b.Property("PlayerStatus") + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("status"); + + b.Property("RoomChatStyleId") + .HasColumnType("int") + .HasColumnName("room_chat_style_id"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("UpdatedAt")); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("players"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Players.PlayerFavoriteRoomsEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("DeletedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("deleted_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("DeletedAt")); + + b.Property("PlayerEntityId") + .HasColumnType("int") + .HasColumnName("player_id"); + + b.Property("RoomEntityId") + .HasColumnType("int") + .HasColumnName("room_id"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("UpdatedAt")); + + b.HasKey("Id"); + + b.HasIndex("RoomEntityId"); + + b.HasIndex("PlayerEntityId", "RoomEntityId") + .IsUnique(); + + b.ToTable("player_favorite_rooms"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Room.RoomBanEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("DateExpires") + .HasColumnType("datetime(6)") + .HasColumnName("date_expires"); + + b.Property("DeletedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("deleted_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("DeletedAt")); + + b.Property("PlayerEntityId") + .HasColumnType("int") + .HasColumnName("player_id"); + + b.Property("RoomEntityId") + .HasColumnType("int") + .HasColumnName("room_id"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("UpdatedAt")); + + b.HasKey("Id"); + + b.HasIndex("PlayerEntityId"); + + b.HasIndex("RoomEntityId", "PlayerEntityId") + .IsUnique(); + + b.ToTable("room_bans"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Room.RoomChatlogEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("DeletedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("deleted_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("DeletedAt")); + + b.Property("Message") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("varchar(100)") + .HasColumnName("message"); + + b.Property("PlayerEntityId") + .HasColumnType("int") + .HasColumnName("player_id"); + + b.Property("RoomEntityId") + .HasColumnType("int") + .HasColumnName("room_id"); + + b.Property("TargetPlayerEntityId") + .HasColumnType("int") + .HasColumnName("target_player_id"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("UpdatedAt")); + + b.HasKey("Id"); + + b.HasIndex("PlayerEntityId"); + + b.HasIndex("RoomEntityId"); + + b.HasIndex("TargetPlayerEntityId"); + + b.ToTable("room_chatlogs"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Room.RoomEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("AllowBlocking") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false) + .HasColumnName("allow_blocking"); + + b.Property("AllowPets") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false) + .HasColumnName("allow_pets"); + + b.Property("AllowPetsEat") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false) + .HasColumnName("allow_pets_eat"); + + b.Property("BanType") + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("ban_type"); + + b.Property("ChatBubbleType") + .HasColumnType("int") + .HasDefaultValue(1) + .HasColumnName("chat_bubble_type"); + + b.Property("ChatDistance") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(50) + .HasColumnName("chat_distance"); + + b.Property("ChatFloodType") + .HasColumnType("int") + .HasDefaultValue(2) + .HasColumnName("chat_flood_type"); + + b.Property("ChatModeType") + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("chat_mode_type"); + + b.Property("ChatSpeedType") + .HasColumnType("int") + .HasDefaultValue(1) + .HasColumnName("chat_speed_type"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("DeletedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("deleted_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("DeletedAt")); + + b.Property("Description") + .HasMaxLength(512) + .HasColumnType("varchar(512)") + .HasColumnName("description"); + + b.Property("DoorMode") + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("door_mode"); + + b.Property("HideWalls") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false) + .HasColumnName("hide_walls"); + + b.Property("KickType") + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("kick_type"); + + b.Property("LastActive") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("last_active"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("LastActive")); + + b.Property("MuteType") + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("mute_type"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("varchar(512)") + .HasColumnName("name"); + + b.Property("NavigatorCategoryEntityId") + .HasColumnType("int") + .HasColumnName("category_id"); + + b.Property("PaintFloor") + .ValueGeneratedOnAdd() + .HasColumnType("double") + .HasDefaultValue(0.0) + .HasColumnName("paint_floor"); + + b.Property("PaintLandscape") + .ValueGeneratedOnAdd() + .HasColumnType("double") + .HasDefaultValue(0.0) + .HasColumnName("paint_landscape"); + + b.Property("PaintWall") + .ValueGeneratedOnAdd() + .HasColumnType("double") + .HasDefaultValue(0.0) + .HasColumnName("paint_wall"); + + b.Property("Password") + .HasMaxLength(512) + .HasColumnType("varchar(512)") + .HasColumnName("password"); + + b.Property("PlayerEntityId") + .HasColumnType("int") + .HasColumnName("player_id"); + + b.Property("PlayersMax") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(25) + .HasColumnName("players_max"); + + b.Property("RoomModelEntityId") + .HasColumnType("int") + .HasColumnName("model_id"); + + b.Property("ThicknessFloor") + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("thickness_floor"); + + b.Property("ThicknessWall") + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("thickness_wall"); + + b.Property("TradeType") + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("trade_type"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("UpdatedAt")); + + b.Property("UsersNow") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("users_now"); + + b.Property("WallHeight") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(-1) + .HasColumnName("wall_height"); + + b.HasKey("Id"); + + b.HasIndex("NavigatorCategoryEntityId"); + + b.HasIndex("PlayerEntityId"); + + b.HasIndex("RoomModelEntityId"); + + b.ToTable("rooms"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Room.RoomEntryLogEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("DeletedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("deleted_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("DeletedAt")); + + b.Property("PlayerEntityId") + .HasColumnType("int") + .HasColumnName("player_id"); + + b.Property("RoomEntityId") + .HasColumnType("int") + .HasColumnName("room_id"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("UpdatedAt")); + + b.HasKey("Id"); + + b.HasIndex("PlayerEntityId"); + + b.HasIndex("RoomEntityId"); + + b.ToTable("room_entry_logs"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Room.RoomModelEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("Custom") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false) + .HasColumnName("custom"); + + b.Property("DeletedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("deleted_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("DeletedAt")); + + b.Property("DoorRotation") + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("door_rotation"); + + b.Property("DoorX") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("door_x"); + + b.Property("DoorY") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0) + .HasColumnName("door_y"); + + b.Property("Enabled") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true) + .HasColumnName("enabled"); + + b.Property("Model") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("longtext") + .HasColumnName("model"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("varchar(512)") + .HasColumnName("name"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("UpdatedAt")); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("room_models"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Room.RoomMuteEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("DateExpires") + .HasColumnType("datetime(6)") + .HasColumnName("date_expires"); + + b.Property("DeletedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("deleted_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("DeletedAt")); + + b.Property("PlayerEntityId") + .HasColumnType("int") + .HasColumnName("player_id"); + + b.Property("RoomEntityId") + .HasColumnType("int") + .HasColumnName("room_id"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("UpdatedAt")); + + b.HasKey("Id"); + + b.HasIndex("PlayerEntityId"); + + b.HasIndex("RoomEntityId", "PlayerEntityId") + .IsUnique(); + + b.ToTable("room_mutes"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Room.RoomRightEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("DeletedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("deleted_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("DeletedAt")); + + b.Property("PlayerEntityId") + .HasColumnType("int") + .HasColumnName("player_id"); + + b.Property("RoomEntityId") + .HasColumnType("int") + .HasColumnName("room_id"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("UpdatedAt")); + + b.HasKey("Id"); + + b.HasIndex("PlayerEntityId"); + + b.HasIndex("RoomEntityId", "PlayerEntityId") + .IsUnique(); + + b.ToTable("room_rights"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Security.SecurityTicketEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("DeletedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("deleted_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("DeletedAt")); + + b.Property("IpAddress") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("varchar(512)") + .HasColumnName("ip_address"); + + b.Property("IsLocked") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false) + .HasColumnName("is_locked"); + + b.Property("PlayerEntityId") + .HasColumnType("int") + .HasColumnName("player_id"); + + b.Property("Ticket") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("varchar(512)") + .HasColumnName("ticket"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("UpdatedAt")); + + b.HasKey("Id"); + + b.HasIndex("PlayerEntityId") + .IsUnique(); + + b.HasIndex("Ticket") + .IsUnique(); + + b.ToTable("security_tickets"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Tracking.PerformanceLogEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("AverageFrameRate") + .HasColumnType("int") + .HasColumnName("average_frame_rate"); + + b.Property("Browser") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("varchar(512)") + .HasColumnName("browser"); + + b.Property("ElapsedTime") + .HasColumnType("int") + .HasColumnName("elapsed_time"); + + b.Property("FlashVersion") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("varchar(512)") + .HasColumnName("flash_version"); + + b.Property("GarbageCollections") + .HasColumnType("int") + .HasColumnName("garbage_collections"); + + b.Property("IPAddress") + .IsRequired() + .HasMaxLength(45) + .HasColumnType("varchar(45)") + .HasColumnName("ip_address"); + + b.Property("IsDebugger") + .HasColumnType("tinyint(1)") + .HasColumnName("is_debugger"); + + b.Property("MemoryUsage") + .HasColumnType("int") + .HasColumnName("memory_usage"); + + b.Property("OS") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("varchar(512)") + .HasColumnName("os"); + + b.Property("UserAgent") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("varchar(512)") + .HasColumnName("user_agent"); + + b.HasKey("Id"); + + b.HasIndex("ElapsedTime"); + + b.HasIndex("IPAddress"); + + b.ToTable("performance_logs"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Catalog.CatalogOfferEntity", b => + { + b.HasOne("Turbo.Database.Entities.Catalog.CatalogPageEntity", "Page") + .WithMany("Offers") + .HasForeignKey("CatalogPageEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Turbo.Database.Entities.Catalog.CurrencyTypeEntity", "CurrencyTypeEntity") + .WithMany("CatalogOffers") + .HasForeignKey("CurrencyTypeId"); + + b.Navigation("CurrencyTypeEntity"); + + b.Navigation("Page"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Catalog.CatalogPageEntity", b => + { + b.HasOne("Turbo.Database.Entities.Catalog.CatalogPageEntity", "ParentEntity") + .WithMany("Children") + .HasForeignKey("ParentEntityId"); + + b.Navigation("ParentEntity"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Catalog.CatalogProductEntity", b => + { + b.HasOne("Turbo.Database.Entities.Catalog.CatalogOfferEntity", "Offer") + .WithMany("Products") + .HasForeignKey("CatalogOfferEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Turbo.Database.Entities.Furniture.FurnitureDefinitionEntity", "FurnitureDefinition") + .WithMany() + .HasForeignKey("FurnitureDefinitionEntityId"); + + b.Navigation("FurnitureDefinition"); + + b.Navigation("Offer"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Furniture.FurnitureEntity", b => + { + b.HasOne("Turbo.Database.Entities.Furniture.FurnitureDefinitionEntity", "FurnitureDefinitionEntity") + .WithMany("Furnitures") + .HasForeignKey("FurnitureDefinitionEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Turbo.Database.Entities.Players.PlayerEntity", "PlayerEntity") + .WithMany("Furniture") + .HasForeignKey("PlayerEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Turbo.Database.Entities.Room.RoomEntity", "RoomEntity") + .WithMany() + .HasForeignKey("RoomEntityId"); + + b.Navigation("FurnitureDefinitionEntity"); + + b.Navigation("PlayerEntity"); + + b.Navigation("RoomEntity"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Furniture.FurnitureTeleportLinkEntity", b => + { + b.HasOne("Turbo.Database.Entities.Furniture.FurnitureEntity", "FurnitureEntityOne") + .WithMany() + .HasForeignKey("FurnitureEntityOneId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Turbo.Database.Entities.Furniture.FurnitureEntity", "FurnitureEntityTwo") + .WithMany() + .HasForeignKey("FurnitureEntityTwoId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("FurnitureEntityOne"); + + b.Navigation("FurnitureEntityTwo"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Messenger.MessengerBlockedEntity", b => + { + b.HasOne("Turbo.Database.Entities.Players.PlayerEntity", "BlockedPlayerEntity") + .WithMany() + .HasForeignKey("BlockedPlayerEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Turbo.Database.Entities.Players.PlayerEntity", "PlayerEntity") + .WithMany("MessengerBlocked") + .HasForeignKey("PlayerEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("BlockedPlayerEntity"); + + b.Navigation("PlayerEntity"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Messenger.MessengerCategoryEntity", b => + { + b.HasOne("Turbo.Database.Entities.Players.PlayerEntity", "PlayerEntity") + .WithMany("MessengerCategories") + .HasForeignKey("PlayerEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PlayerEntity"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Messenger.MessengerFriendEntity", b => + { + b.HasOne("Turbo.Database.Entities.Players.PlayerEntity", "FriendPlayerEntity") + .WithMany() + .HasForeignKey("FriendPlayerEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Turbo.Database.Entities.Messenger.MessengerCategoryEntity", "MessengerCategoryEntity") + .WithMany() + .HasForeignKey("MessengerCategoryEntityId"); + + b.HasOne("Turbo.Database.Entities.Players.PlayerEntity", "PlayerEntity") + .WithMany("MessengerFriends") + .HasForeignKey("PlayerEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("FriendPlayerEntity"); + + b.Navigation("MessengerCategoryEntity"); + + b.Navigation("PlayerEntity"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Messenger.MessengerIgnoredEntity", b => + { + b.HasOne("Turbo.Database.Entities.Players.PlayerEntity", "IgnoredPlayerEntity") + .WithMany() + .HasForeignKey("IgnoredPlayerEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Turbo.Database.Entities.Players.PlayerEntity", "PlayerEntity") + .WithMany("MessengerIgnored") + .HasForeignKey("PlayerEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IgnoredPlayerEntity"); + + b.Navigation("PlayerEntity"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Messenger.MessengerMessageEntity", b => + { + b.HasOne("Turbo.Database.Entities.Players.PlayerEntity", "ReceiverPlayerEntity") + .WithMany("MessengerMessagesReceived") + .HasForeignKey("ReceiverPlayerEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Turbo.Database.Entities.Players.PlayerEntity", "SenderPlayerEntity") + .WithMany("MessengerMessagesSent") + .HasForeignKey("SenderPlayerEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ReceiverPlayerEntity"); + + b.Navigation("SenderPlayerEntity"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Messenger.MessengerRequestEntity", b => + { + b.HasOne("Turbo.Database.Entities.Players.PlayerEntity", "PlayerEntity") + .WithMany("MessengerRequestsSent") + .HasForeignKey("PlayerEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Turbo.Database.Entities.Players.PlayerEntity", "RequestedPlayerEntity") + .WithMany("MessengerRequests") + .HasForeignKey("RequestedPlayerEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PlayerEntity"); + + b.Navigation("RequestedPlayerEntity"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Players.PlayerBadgeEntity", b => + { + b.HasOne("Turbo.Database.Entities.Players.PlayerEntity", "PlayerEntity") + .WithMany("PlayerBadges") + .HasForeignKey("PlayerEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PlayerEntity"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Players.PlayerChatStyleOwnedEntity", b => + { + b.HasOne("Turbo.Database.Entities.Players.PlayerChatStyleEntity", "ChatStyle") + .WithMany("OwnedChatStyles") + .HasForeignKey("ChatStyleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Turbo.Database.Entities.Players.PlayerEntity", "PlayerEntity") + .WithMany("PlayerOwnedChatStyles") + .HasForeignKey("PlayerEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ChatStyle"); + + b.Navigation("PlayerEntity"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Players.PlayerCurrencyEntity", b => + { + b.HasOne("Turbo.Database.Entities.Catalog.CurrencyTypeEntity", "CurrencyTypeEntity") + .WithMany("PlayerCurrencies") + .HasForeignKey("CurrencyTypeEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Turbo.Database.Entities.Players.PlayerEntity", "PlayerEntity") + .WithMany("PlayerCurrencies") + .HasForeignKey("PlayerEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("CurrencyTypeEntity"); + + b.Navigation("PlayerEntity"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Players.PlayerFavoriteRoomsEntity", b => + { + b.HasOne("Turbo.Database.Entities.Players.PlayerEntity", "PlayerEntity") + .WithMany() + .HasForeignKey("PlayerEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Turbo.Database.Entities.Room.RoomEntity", "RoomEntity") + .WithMany() + .HasForeignKey("RoomEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PlayerEntity"); + + b.Navigation("RoomEntity"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Room.RoomBanEntity", b => + { + b.HasOne("Turbo.Database.Entities.Players.PlayerEntity", "PlayerEntity") + .WithMany("RoomBans") + .HasForeignKey("PlayerEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Turbo.Database.Entities.Room.RoomEntity", "RoomEntity") + .WithMany("RoomBans") + .HasForeignKey("RoomEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PlayerEntity"); + + b.Navigation("RoomEntity"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Room.RoomChatlogEntity", b => + { + b.HasOne("Turbo.Database.Entities.Players.PlayerEntity", "PlayerEntity") + .WithMany("RoomChatlogs") + .HasForeignKey("PlayerEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Turbo.Database.Entities.Room.RoomEntity", "RoomEntity") + .WithMany("RoomChats") + .HasForeignKey("RoomEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Turbo.Database.Entities.Players.PlayerEntity", "TargetPlayerEntity") + .WithMany() + .HasForeignKey("TargetPlayerEntityId"); + + b.Navigation("PlayerEntity"); + + b.Navigation("RoomEntity"); + + b.Navigation("TargetPlayerEntity"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Room.RoomEntity", b => + { + b.HasOne("Turbo.Database.Entities.Navigator.NavigatorFlatCategoryEntity", "NavigatorFlatCategoryEntity") + .WithMany() + .HasForeignKey("NavigatorCategoryEntityId"); + + b.HasOne("Turbo.Database.Entities.Players.PlayerEntity", "PlayerEntity") + .WithMany("Rooms") + .HasForeignKey("PlayerEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Turbo.Database.Entities.Room.RoomModelEntity", "RoomModelEntity") + .WithMany() + .HasForeignKey("RoomModelEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("NavigatorFlatCategoryEntity"); + + b.Navigation("PlayerEntity"); + + b.Navigation("RoomModelEntity"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Room.RoomEntryLogEntity", b => + { + b.HasOne("Turbo.Database.Entities.Players.PlayerEntity", "PlayerEntity") + .WithMany() + .HasForeignKey("PlayerEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Turbo.Database.Entities.Room.RoomEntity", "RoomEntity") + .WithMany() + .HasForeignKey("RoomEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PlayerEntity"); + + b.Navigation("RoomEntity"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Room.RoomMuteEntity", b => + { + b.HasOne("Turbo.Database.Entities.Players.PlayerEntity", "PlayerEntity") + .WithMany("RoomMutes") + .HasForeignKey("PlayerEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Turbo.Database.Entities.Room.RoomEntity", "RoomEntity") + .WithMany("RoomMutes") + .HasForeignKey("RoomEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PlayerEntity"); + + b.Navigation("RoomEntity"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Room.RoomRightEntity", b => + { + b.HasOne("Turbo.Database.Entities.Players.PlayerEntity", "PlayerEntity") + .WithMany("RoomRights") + .HasForeignKey("PlayerEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Turbo.Database.Entities.Room.RoomEntity", "RoomEntity") + .WithMany("RoomRights") + .HasForeignKey("RoomEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PlayerEntity"); + + b.Navigation("RoomEntity"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Security.SecurityTicketEntity", b => + { + b.HasOne("Turbo.Database.Entities.Players.PlayerEntity", "PlayerEntity") + .WithMany("SecurityTickets") + .HasForeignKey("PlayerEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PlayerEntity"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Catalog.CatalogOfferEntity", b => + { + b.Navigation("Products"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Catalog.CatalogPageEntity", b => + { + b.Navigation("Children"); + + b.Navigation("Offers"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Catalog.CurrencyTypeEntity", b => + { + b.Navigation("CatalogOffers"); + + b.Navigation("PlayerCurrencies"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Furniture.FurnitureDefinitionEntity", b => + { + b.Navigation("Furnitures"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Players.PlayerChatStyleEntity", b => + { + b.Navigation("OwnedChatStyles"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Players.PlayerEntity", b => + { + b.Navigation("Furniture"); + + b.Navigation("MessengerBlocked"); + + b.Navigation("MessengerCategories"); + + b.Navigation("MessengerFriends"); + + b.Navigation("MessengerIgnored"); + + b.Navigation("MessengerMessagesReceived"); + + b.Navigation("MessengerMessagesSent"); + + b.Navigation("MessengerRequests"); + + b.Navigation("MessengerRequestsSent"); + + b.Navigation("PlayerBadges"); + + b.Navigation("PlayerCurrencies"); + + b.Navigation("PlayerOwnedChatStyles"); + + b.Navigation("RoomBans"); + + b.Navigation("RoomChatlogs"); + + b.Navigation("RoomMutes"); + + b.Navigation("RoomRights"); + + b.Navigation("Rooms"); + + b.Navigation("SecurityTickets"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Room.RoomEntity", b => + { + b.Navigation("RoomBans"); + + b.Navigation("RoomChats"); + + b.Navigation("RoomMutes"); + + b.Navigation("RoomRights"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Turbo.Database/Migrations/20260208142253_AddMessengerDeliveredFlag.cs b/Turbo.Database/Migrations/20260208142253_AddMessengerDeliveredFlag.cs new file mode 100644 index 00000000..d876282f --- /dev/null +++ b/Turbo.Database/Migrations/20260208142253_AddMessengerDeliveredFlag.cs @@ -0,0 +1,28 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Turbo.Database.Migrations +{ + /// + public partial class AddMessengerDeliveredFlag : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "delivered", + table: "messenger_messages", + type: "tinyint(1)", + nullable: false, + defaultValue: false + ); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn(name: "delivered", table: "messenger_messages"); + } + } +} diff --git a/Turbo.Database/Migrations/TurboDbContextModelSnapshot.cs b/Turbo.Database/Migrations/TurboDbContextModelSnapshot.cs index 5f1460df..2f57c564 100644 --- a/Turbo.Database/Migrations/TurboDbContextModelSnapshot.cs +++ b/Turbo.Database/Migrations/TurboDbContextModelSnapshot.cs @@ -611,6 +611,54 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("furniture_teleport_links"); }); + modelBuilder.Entity("Turbo.Database.Entities.Messenger.MessengerBlockedEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("BlockedPlayerEntityId") + .HasColumnType("int") + .HasColumnName("blocked_player_id"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("DeletedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("deleted_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("DeletedAt")); + + b.Property("PlayerEntityId") + .HasColumnType("int") + .HasColumnName("player_id"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("UpdatedAt")); + + b.HasKey("Id"); + + b.HasIndex("BlockedPlayerEntityId"); + + b.HasIndex("PlayerEntityId", "BlockedPlayerEntityId") + .IsUnique(); + + b.ToTable("messenger_blocked"); + }); + modelBuilder.Entity("Turbo.Database.Entities.Messenger.MessengerCategoryEntity", b => { b.Property("Id") @@ -719,6 +767,115 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("messenger_friends"); }); + modelBuilder.Entity("Turbo.Database.Entities.Messenger.MessengerIgnoredEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("DeletedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("deleted_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("DeletedAt")); + + b.Property("IgnoredPlayerEntityId") + .HasColumnType("int") + .HasColumnName("ignored_player_id"); + + b.Property("PlayerEntityId") + .HasColumnType("int") + .HasColumnName("player_id"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("UpdatedAt")); + + b.HasKey("Id"); + + b.HasIndex("IgnoredPlayerEntityId"); + + b.HasIndex("PlayerEntityId", "IgnoredPlayerEntityId") + .IsUnique(); + + b.ToTable("messenger_ignored"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Messenger.MessengerMessageEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("DeletedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("deleted_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("DeletedAt")); + + b.Property("Delivered") + .HasColumnType("tinyint(1)") + .HasColumnName("delivered"); + + b.Property("Message") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("varchar(512)") + .HasColumnName("message"); + + b.Property("ReceiverPlayerEntityId") + .HasColumnType("int") + .HasColumnName("receiver_id"); + + b.Property("SenderPlayerEntityId") + .HasColumnType("int") + .HasColumnName("sender_id"); + + b.Property("Timestamp") + .HasColumnType("datetime(6)") + .HasColumnName("timestamp"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("UpdatedAt")); + + b.HasKey("Id"); + + b.HasIndex("ReceiverPlayerEntityId", "SenderPlayerEntityId", "Timestamp"); + + b.HasIndex("SenderPlayerEntityId", "ReceiverPlayerEntityId", "Timestamp"); + + b.ToTable("messenger_messages"); + }); + modelBuilder.Entity("Turbo.Database.Entities.Messenger.MessengerRequestEntity", b => { b.Property("Id") @@ -2025,6 +2182,25 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("FurnitureEntityTwo"); }); + modelBuilder.Entity("Turbo.Database.Entities.Messenger.MessengerBlockedEntity", b => + { + b.HasOne("Turbo.Database.Entities.Players.PlayerEntity", "BlockedPlayerEntity") + .WithMany() + .HasForeignKey("BlockedPlayerEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Turbo.Database.Entities.Players.PlayerEntity", "PlayerEntity") + .WithMany("MessengerBlocked") + .HasForeignKey("PlayerEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("BlockedPlayerEntity"); + + b.Navigation("PlayerEntity"); + }); + modelBuilder.Entity("Turbo.Database.Entities.Messenger.MessengerCategoryEntity", b => { b.HasOne("Turbo.Database.Entities.Players.PlayerEntity", "PlayerEntity") @@ -2061,6 +2237,44 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("PlayerEntity"); }); + modelBuilder.Entity("Turbo.Database.Entities.Messenger.MessengerIgnoredEntity", b => + { + b.HasOne("Turbo.Database.Entities.Players.PlayerEntity", "IgnoredPlayerEntity") + .WithMany() + .HasForeignKey("IgnoredPlayerEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Turbo.Database.Entities.Players.PlayerEntity", "PlayerEntity") + .WithMany("MessengerIgnored") + .HasForeignKey("PlayerEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IgnoredPlayerEntity"); + + b.Navigation("PlayerEntity"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Messenger.MessengerMessageEntity", b => + { + b.HasOne("Turbo.Database.Entities.Players.PlayerEntity", "ReceiverPlayerEntity") + .WithMany("MessengerMessagesReceived") + .HasForeignKey("ReceiverPlayerEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Turbo.Database.Entities.Players.PlayerEntity", "SenderPlayerEntity") + .WithMany("MessengerMessagesSent") + .HasForeignKey("SenderPlayerEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ReceiverPlayerEntity"); + + b.Navigation("SenderPlayerEntity"); + }); + modelBuilder.Entity("Turbo.Database.Entities.Messenger.MessengerRequestEntity", b => { b.HasOne("Turbo.Database.Entities.Players.PlayerEntity", "PlayerEntity") @@ -2318,10 +2532,18 @@ protected override void BuildModel(ModelBuilder modelBuilder) { b.Navigation("Furniture"); + b.Navigation("MessengerBlocked"); + b.Navigation("MessengerCategories"); b.Navigation("MessengerFriends"); + b.Navigation("MessengerIgnored"); + + b.Navigation("MessengerMessagesReceived"); + + b.Navigation("MessengerMessagesSent"); + b.Navigation("MessengerRequests"); b.Navigation("MessengerRequestsSent"); diff --git a/Turbo.PacketHandlers/FriendList/AcceptFriendMessageHandler.cs b/Turbo.PacketHandlers/FriendList/AcceptFriendMessageHandler.cs index 47ea937a..231559e6 100644 --- a/Turbo.PacketHandlers/FriendList/AcceptFriendMessageHandler.cs +++ b/Turbo.PacketHandlers/FriendList/AcceptFriendMessageHandler.cs @@ -1,18 +1,55 @@ using System.Threading; using System.Threading.Tasks; +using Microsoft.Extensions.Configuration; +using Orleans; using Turbo.Messages.Registry; using Turbo.Primitives.Messages.Incoming.FriendList; +using Turbo.Primitives.Messages.Outgoing.FriendList; +using Turbo.Primitives.Orleans; namespace Turbo.PacketHandlers.FriendList; -public class AcceptFriendMessageHandler : IMessageHandler +public class AcceptFriendMessageHandler(IGrainFactory grainFactory, IConfiguration configuration) + : IMessageHandler { + private readonly IGrainFactory _grainFactory = grainFactory; + private readonly IConfiguration _configuration = configuration; + public async ValueTask HandleAsync( AcceptFriendMessage message, MessageContext ctx, CancellationToken ct ) { - await ValueTask.CompletedTask.ConfigureAwait(false); + if (ctx.PlayerId <= 0) + return; + + var friendLimit = _configuration.GetValue("Turbo:FriendList:UserFriendLimit"); + + var messengerGrain = _grainFactory.GetMessengerGrain(ctx.PlayerId); + var (failures, updates) = await messengerGrain + .AcceptFriendRequestsAsync(message.Friends, friendLimit, ct) + .ConfigureAwait(false); + + // Send the failures result + await ctx.SendComposerAsync( + new AcceptFriendResultMessageComposer { Failures = failures }, + ct + ) + .ConfigureAwait(false); + + // Send the friend list update with newly added friends + if (updates.Count > 0) + { + await ctx.SendComposerAsync( + new FriendListUpdateMessageComposer + { + FriendCategories = [], + Updates = updates, + }, + ct + ) + .ConfigureAwait(false); + } } } diff --git a/Turbo.PacketHandlers/FriendList/DeclineFriendMessageHandler.cs b/Turbo.PacketHandlers/FriendList/DeclineFriendMessageHandler.cs index 4f75dc80..b3f30485 100644 --- a/Turbo.PacketHandlers/FriendList/DeclineFriendMessageHandler.cs +++ b/Turbo.PacketHandlers/FriendList/DeclineFriendMessageHandler.cs @@ -1,18 +1,29 @@ using System.Threading; using System.Threading.Tasks; +using Orleans; using Turbo.Messages.Registry; using Turbo.Primitives.Messages.Incoming.FriendList; +using Turbo.Primitives.Orleans; namespace Turbo.PacketHandlers.FriendList; -public class DeclineFriendMessageHandler : IMessageHandler +public class DeclineFriendMessageHandler(IGrainFactory grainFactory) + : IMessageHandler { + private readonly IGrainFactory _grainFactory = grainFactory; + public async ValueTask HandleAsync( DeclineFriendMessage message, MessageContext ctx, CancellationToken ct ) { - await ValueTask.CompletedTask.ConfigureAwait(false); + if (ctx.PlayerId <= 0) + return; + + var messengerGrain = _grainFactory.GetMessengerGrain(ctx.PlayerId); + await messengerGrain + .DeclineFriendRequestsAsync(message.Friends, message.DeclineAll, ct) + .ConfigureAwait(false); } } diff --git a/Turbo.PacketHandlers/FriendList/FindNewFriendsMessageHandler.cs b/Turbo.PacketHandlers/FriendList/FindNewFriendsMessageHandler.cs index 2effa946..01d570fe 100644 --- a/Turbo.PacketHandlers/FriendList/FindNewFriendsMessageHandler.cs +++ b/Turbo.PacketHandlers/FriendList/FindNewFriendsMessageHandler.cs @@ -1,18 +1,56 @@ using System.Threading; using System.Threading.Tasks; +using Orleans; using Turbo.Messages.Registry; using Turbo.Primitives.Messages.Incoming.FriendList; +using Turbo.Primitives.Messages.Outgoing.FriendList; +using Turbo.Primitives.Messages.Outgoing.Room.Session; +using Turbo.Primitives.Orleans; namespace Turbo.PacketHandlers.FriendList; -public class FindNewFriendsMessageHandler : IMessageHandler +public class FindNewFriendsMessageHandler(IGrainFactory grainFactory) + : IMessageHandler { + private readonly IGrainFactory _grainFactory = grainFactory; + public async ValueTask HandleAsync( FindNewFriendsMessage message, MessageContext ctx, CancellationToken ct ) { - await ValueTask.CompletedTask.ConfigureAwait(false); + if (ctx.PlayerId <= 0) + return; + + // Try to find a random populated room + var roomDirectory = _grainFactory.GetRoomDirectoryGrain(); + var randomRoomId = await roomDirectory + .GetRandomPopulatedRoomAsync(ct) + .ConfigureAwait(false); + + if (randomRoomId is not null && randomRoomId.Value > 0) + { + await ctx.SendComposerAsync( + new FindFriendsProcessResultMessageComposer { Success = true }, + ct + ) + .ConfigureAwait(false); + + // Navigate the player to the random room + await ctx.SendComposerAsync( + new RoomForwardMessageComposer { RoomId = randomRoomId.Value }, + ct + ) + .ConfigureAwait(false); + } + else + { + await ctx.SendComposerAsync( + new FindFriendsProcessResultMessageComposer { Success = false }, + ct + ) + .ConfigureAwait(false); + } } } diff --git a/Turbo.PacketHandlers/FriendList/FollowFriendMessageHandler.cs b/Turbo.PacketHandlers/FriendList/FollowFriendMessageHandler.cs index f4d59f0b..d2a0e734 100644 --- a/Turbo.PacketHandlers/FriendList/FollowFriendMessageHandler.cs +++ b/Turbo.PacketHandlers/FriendList/FollowFriendMessageHandler.cs @@ -1,18 +1,52 @@ using System.Threading; using System.Threading.Tasks; +using Orleans; using Turbo.Messages.Registry; using Turbo.Primitives.Messages.Incoming.FriendList; +using Turbo.Primitives.Messages.Outgoing.FriendList; +using Turbo.Primitives.Messages.Outgoing.Room.Session; +using Turbo.Primitives.Orleans; +using Turbo.Primitives.Rooms; namespace Turbo.PacketHandlers.FriendList; -public class FollowFriendMessageHandler : IMessageHandler +public class FollowFriendMessageHandler(IGrainFactory grainFactory) + : IMessageHandler { + private readonly IGrainFactory _grainFactory = grainFactory; + public async ValueTask HandleAsync( FollowFriendMessage message, MessageContext ctx, CancellationToken ct ) { - await ValueTask.CompletedTask.ConfigureAwait(false); + if (ctx.PlayerId <= 0) + return; + + var messengerGrain = _grainFactory.GetMessengerGrain(ctx.PlayerId); + var (success, roomId, error) = await messengerGrain + .FollowFriendAsync(message.PlayerId, ct) + .ConfigureAwait(false); + + if (!success && error is not null) + { + await ctx.SendComposerAsync( + new FollowFriendFailedMessageComposer { ErrorCode = error.Value }, + ct + ) + .ConfigureAwait(false); + return; + } + + if (success) + { + // Tell the client to navigate to the friend's room + await ctx.SendComposerAsync( + new RoomForwardMessageComposer { RoomId = RoomId.Parse(roomId) }, + ct + ) + .ConfigureAwait(false); + } } } diff --git a/Turbo.PacketHandlers/FriendList/FriendListUpdateMessageHandler.cs b/Turbo.PacketHandlers/FriendList/FriendListUpdateMessageHandler.cs index fbf8c99e..f7a96eb8 100644 --- a/Turbo.PacketHandlers/FriendList/FriendListUpdateMessageHandler.cs +++ b/Turbo.PacketHandlers/FriendList/FriendListUpdateMessageHandler.cs @@ -1,18 +1,33 @@ using System.Threading; using System.Threading.Tasks; +using Orleans; using Turbo.Messages.Registry; using Turbo.Primitives.Messages.Incoming.FriendList; +using Turbo.Primitives.Messages.Outgoing.FriendList; +using Turbo.Primitives.Orleans; namespace Turbo.PacketHandlers.FriendList; -public class FriendListUpdateMessageHandler : IMessageHandler +public class FriendListUpdateMessageHandler(IGrainFactory grainFactory) + : IMessageHandler { + private readonly IGrainFactory _grainFactory = grainFactory; + public async ValueTask HandleAsync( FriendListUpdateMessage message, MessageContext ctx, CancellationToken ct ) { - await ValueTask.CompletedTask.ConfigureAwait(false); + if (ctx.PlayerId <= 0) + return; + + // The client sends this as a periodic poll. + // We respond with an empty update (real updates are pushed via grain notifications). + await ctx.SendComposerAsync( + new FriendListUpdateMessageComposer { FriendCategories = [], Updates = [] }, + ct + ) + .ConfigureAwait(false); } } diff --git a/Turbo.PacketHandlers/FriendList/GetFriendRequestsMessageHandler.cs b/Turbo.PacketHandlers/FriendList/GetFriendRequestsMessageHandler.cs index 16298e91..fc8cdf91 100644 --- a/Turbo.PacketHandlers/FriendList/GetFriendRequestsMessageHandler.cs +++ b/Turbo.PacketHandlers/FriendList/GetFriendRequestsMessageHandler.cs @@ -1,20 +1,31 @@ using System.Threading; using System.Threading.Tasks; +using Orleans; using Turbo.Messages.Registry; using Turbo.Primitives.Messages.Incoming.FriendList; using Turbo.Primitives.Messages.Outgoing.FriendList; +using Turbo.Primitives.Orleans; namespace Turbo.PacketHandlers.FriendList; -public class GetFriendRequestsMessageHandler : IMessageHandler +public class GetFriendRequestsMessageHandler(IGrainFactory grainFactory) + : IMessageHandler { + private readonly IGrainFactory _grainFactory = grainFactory; + public async ValueTask HandleAsync( GetFriendRequestsMessage message, MessageContext ctx, CancellationToken ct ) { - await ctx.SendComposerAsync(new FriendRequestsMessageComposer { Requests = [] }, ct) + if (ctx.PlayerId <= 0) + return; + + var messengerGrain = _grainFactory.GetMessengerGrain(ctx.PlayerId); + var requests = await messengerGrain.GetFriendRequestsAsync(ct).ConfigureAwait(false); + + await ctx.SendComposerAsync(new FriendRequestsMessageComposer { Requests = requests }, ct) .ConfigureAwait(false); } } diff --git a/Turbo.PacketHandlers/FriendList/GetMessengerHistoryMessageHandler.cs b/Turbo.PacketHandlers/FriendList/GetMessengerHistoryMessageHandler.cs index 81456bb8..712ddc53 100644 --- a/Turbo.PacketHandlers/FriendList/GetMessengerHistoryMessageHandler.cs +++ b/Turbo.PacketHandlers/FriendList/GetMessengerHistoryMessageHandler.cs @@ -1,18 +1,46 @@ using System.Threading; using System.Threading.Tasks; +using Microsoft.Extensions.Configuration; +using Orleans; using Turbo.Messages.Registry; using Turbo.Primitives.Messages.Incoming.FriendList; +using Turbo.Primitives.Messages.Outgoing.FriendList; +using Turbo.Primitives.Orleans; namespace Turbo.PacketHandlers.FriendList; -public class GetMessengerHistoryMessageHandler : IMessageHandler +public class GetMessengerHistoryMessageHandler( + IGrainFactory grainFactory, + IConfiguration configuration +) : IMessageHandler { + private readonly IGrainFactory _grainFactory = grainFactory; + private readonly IConfiguration _configuration = configuration; + public async ValueTask HandleAsync( GetMessengerHistoryMessage message, MessageContext ctx, CancellationToken ct ) { - await ValueTask.CompletedTask.ConfigureAwait(false); + if (ctx.PlayerId <= 0) + return; + + var pageSize = _configuration.GetValue("Turbo:Messenger:MessageHistoryPageSize"); + + var messengerGrain = _grainFactory.GetMessengerGrain(ctx.PlayerId); + var history = await messengerGrain + .GetMessageHistoryAsync(message.ChatId, message.Message, pageSize, ct) + .ConfigureAwait(false); + + await ctx.SendComposerAsync( + new ConsoleMessageHistoryMessageComposer + { + ChatId = message.ChatId, + Messages = history, + }, + ct + ) + .ConfigureAwait(false); } } diff --git a/Turbo.PacketHandlers/FriendList/HabboSearchMessageHandler.cs b/Turbo.PacketHandlers/FriendList/HabboSearchMessageHandler.cs index 2d13448d..27b1cb93 100644 --- a/Turbo.PacketHandlers/FriendList/HabboSearchMessageHandler.cs +++ b/Turbo.PacketHandlers/FriendList/HabboSearchMessageHandler.cs @@ -1,18 +1,40 @@ using System.Threading; using System.Threading.Tasks; +using Microsoft.Extensions.Configuration; +using Orleans; using Turbo.Messages.Registry; using Turbo.Primitives.Messages.Incoming.FriendList; +using Turbo.Primitives.Messages.Outgoing.FriendList; +using Turbo.Primitives.Orleans; namespace Turbo.PacketHandlers.FriendList; -public class HabboSearchMessageHandler : IMessageHandler +public class HabboSearchMessageHandler(IGrainFactory grainFactory, IConfiguration configuration) + : IMessageHandler { + private readonly IGrainFactory _grainFactory = grainFactory; + private readonly IConfiguration _configuration = configuration; + public async ValueTask HandleAsync( HabboSearchMessage message, MessageContext ctx, CancellationToken ct ) { - await ValueTask.CompletedTask.ConfigureAwait(false); + if (ctx.PlayerId <= 0) + return; + + var searchLimit = _configuration.GetValue("Turbo:Messenger:SearchResultLimit"); + + var messengerGrain = _grainFactory.GetMessengerGrain(ctx.PlayerId); + var (friends, others) = await messengerGrain + .SearchPlayersAsync(message.SearchQuery, searchLimit, ct) + .ConfigureAwait(false); + + await ctx.SendComposerAsync( + new HabboSearchResultMessageComposer { Friends = friends, Others = others }, + ct + ) + .ConfigureAwait(false); } } diff --git a/Turbo.PacketHandlers/FriendList/MessengerInitMessageHandler.cs b/Turbo.PacketHandlers/FriendList/MessengerInitMessageHandler.cs index dd8ddf4f..7d0884d0 100644 --- a/Turbo.PacketHandlers/FriendList/MessengerInitMessageHandler.cs +++ b/Turbo.PacketHandlers/FriendList/MessengerInitMessageHandler.cs @@ -1,29 +1,82 @@ +using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; +using Microsoft.Extensions.Configuration; +using Orleans; using Turbo.Messages.Registry; using Turbo.Primitives.Messages.Incoming.FriendList; using Turbo.Primitives.Messages.Outgoing.FriendList; +using Turbo.Primitives.Orleans; +using Turbo.Primitives.Snapshots.FriendList; namespace Turbo.PacketHandlers.FriendList; -public class MessengerInitMessageHandler : IMessageHandler +public class MessengerInitMessageHandler(IConfiguration configuration, IGrainFactory grainFactory) + : IMessageHandler { + private readonly IConfiguration _configuration = configuration; + private readonly IGrainFactory _grainFactory = grainFactory; + public async ValueTask HandleAsync( MessengerInitMessage message, MessageContext ctx, CancellationToken ct ) { + if (ctx.PlayerId <= 0) + return; + + var userFriendLimit = _configuration.GetValue("Turbo:FriendList:UserFriendLimit"); + var normalFriendLimit = _configuration.GetValue("Turbo:FriendList:NormalFriendLimit"); + var extendedFriendLimit = _configuration.GetValue( + "Turbo:FriendList:ExtendedFriendLimit" + ); + + var messengerGrain = _grainFactory.GetMessengerGrain(ctx.PlayerId); + var categories = await messengerGrain.GetCategoriesAsync(ct).ConfigureAwait(false); + var friends = await messengerGrain.GetFriendsAsync(ct).ConfigureAwait(false); + + // Send MessengerInit await ctx.SendComposerAsync( new MessengerInitMessageComposer { - UserFriendLimit = 0, - NormalFriendLimit = 0, - ExtendedFriendLimit = 0, - FriendCategories = [], + UserFriendLimit = userFriendLimit, + NormalFriendLimit = normalFriendLimit, + ExtendedFriendLimit = extendedFriendLimit, + FriendCategories = categories, }, ct ) .ConfigureAwait(false); + + // Send friend list as fragments (max 500 per fragment) + const int fragmentSize = 500; + var totalFragments = + friends.Count == 0 ? 1 : (friends.Count + fragmentSize - 1) / fragmentSize; + + for (var i = 0; i < totalFragments; i++) + { + var fragment = friends.GetRange( + i * fragmentSize, + System.Math.Min(fragmentSize, friends.Count - i * fragmentSize) + ); + + await ctx.SendComposerAsync( + new FriendListFragmentMessageComposer + { + TotalFragments = totalFragments, + FragmentIndex = i, + Fragment = fragment, + }, + ct + ) + .ConfigureAwait(false); + } + + // Notify friends we're online + await messengerGrain.NotifyOnlineAsync(ct).ConfigureAwait(false); + + // Deliver any offline messages that were stored while we were away + await messengerGrain.DeliverOfflineMessagesAsync(ct).ConfigureAwait(false); } } diff --git a/Turbo.PacketHandlers/FriendList/RemoveFriendMessageHandler.cs b/Turbo.PacketHandlers/FriendList/RemoveFriendMessageHandler.cs index 3c3af004..92e5d246 100644 --- a/Turbo.PacketHandlers/FriendList/RemoveFriendMessageHandler.cs +++ b/Turbo.PacketHandlers/FriendList/RemoveFriendMessageHandler.cs @@ -1,18 +1,27 @@ using System.Threading; using System.Threading.Tasks; +using Orleans; using Turbo.Messages.Registry; using Turbo.Primitives.Messages.Incoming.FriendList; +using Turbo.Primitives.Orleans; namespace Turbo.PacketHandlers.FriendList; -public class RemoveFriendMessageHandler : IMessageHandler +public class RemoveFriendMessageHandler(IGrainFactory grainFactory) + : IMessageHandler { + private readonly IGrainFactory _grainFactory = grainFactory; + public async ValueTask HandleAsync( RemoveFriendMessage message, MessageContext ctx, CancellationToken ct ) { - await ValueTask.CompletedTask.ConfigureAwait(false); + if (ctx.PlayerId <= 0) + return; + + var messengerGrain = _grainFactory.GetMessengerGrain(ctx.PlayerId); + await messengerGrain.RemoveFriendsAsync(message.FriendIds, ct).ConfigureAwait(false); } } diff --git a/Turbo.PacketHandlers/FriendList/RequestFriendMessageHandler.cs b/Turbo.PacketHandlers/FriendList/RequestFriendMessageHandler.cs index cec10f39..ef6655b5 100644 --- a/Turbo.PacketHandlers/FriendList/RequestFriendMessageHandler.cs +++ b/Turbo.PacketHandlers/FriendList/RequestFriendMessageHandler.cs @@ -1,18 +1,66 @@ using System.Threading; using System.Threading.Tasks; +using Microsoft.Extensions.Configuration; +using Orleans; using Turbo.Messages.Registry; using Turbo.Primitives.Messages.Incoming.FriendList; +using Turbo.Primitives.Messages.Outgoing.FriendList; +using Turbo.Primitives.Orleans; namespace Turbo.PacketHandlers.FriendList; -public class RequestFriendMessageHandler : IMessageHandler +public class RequestFriendMessageHandler(IGrainFactory grainFactory, IConfiguration configuration) + : IMessageHandler { + private readonly IGrainFactory _grainFactory = grainFactory; + private readonly IConfiguration _configuration = configuration; + public async ValueTask HandleAsync( RequestFriendMessage message, MessageContext ctx, CancellationToken ct ) { - await ValueTask.CompletedTask.ConfigureAwait(false); + if (ctx.PlayerId <= 0) + return; + + // Resolve target player ID by name + var playerDirectory = _grainFactory.GetPlayerDirectoryGrain(); + var targetId = await playerDirectory + .GetPlayerIdAsync(message.PlayerName, ct) + .ConfigureAwait(false); + + if (targetId is null) + return; + + var friendLimit = _configuration.GetValue("Turbo:FriendList:UserFriendLimit"); + + // Get sender info + var senderGrain = _grainFactory.GetPlayerGrain(ctx.PlayerId); + var senderSummary = await senderGrain.GetSummaryAsync(ct).ConfigureAwait(false); + + var messengerGrain = _grainFactory.GetMessengerGrain(ctx.PlayerId); + var error = await messengerGrain + .SendFriendRequestAsync( + targetId.Value, + senderSummary.Name, + senderSummary.Figure, + friendLimit, + ct + ) + .ConfigureAwait(false); + + if (error is not null) + { + await ctx.SendComposerAsync( + new MessengerErrorMessageComposer + { + ClientMessageId = 0, + ErrorCode = error.Value, + }, + ct + ) + .ConfigureAwait(false); + } } } diff --git a/Turbo.PacketHandlers/FriendList/SendMsgMessageHandler.cs b/Turbo.PacketHandlers/FriendList/SendMsgMessageHandler.cs index ce65282e..da9c060a 100644 --- a/Turbo.PacketHandlers/FriendList/SendMsgMessageHandler.cs +++ b/Turbo.PacketHandlers/FriendList/SendMsgMessageHandler.cs @@ -1,18 +1,109 @@ using System.Threading; using System.Threading.Tasks; +using Orleans; using Turbo.Messages.Registry; +using Turbo.Primitives.FriendList.Enums; using Turbo.Primitives.Messages.Incoming.FriendList; +using Turbo.Primitives.Messages.Outgoing.FriendList; +using Turbo.Primitives.Orleans; +using Turbo.Primitives.Players; namespace Turbo.PacketHandlers.FriendList; -public class SendMsgMessageHandler : IMessageHandler +public class SendMsgMessageHandler(IGrainFactory grainFactory) : IMessageHandler { + private readonly IGrainFactory _grainFactory = grainFactory; + public async ValueTask HandleAsync( SendMsgMessage message, MessageContext ctx, CancellationToken ct ) { - await ValueTask.CompletedTask.ConfigureAwait(false); + if (ctx.PlayerId <= 0) + return; + + var recipientId = PlayerId.Parse(message.ChatId); + var messengerGrain = _grainFactory.GetMessengerGrain(ctx.PlayerId); + + // Validate friendship + if (!await messengerGrain.IsFriendAsync(recipientId).ConfigureAwait(false)) + { + await ctx.SendComposerAsync( + new InstantMessageErrorMessageComposer + { + ErrorCode = InstantMessageErrorCodeType.NotFriend, + PlayerId = recipientId, + Message = string.Empty, + }, + ct + ) + .ConfigureAwait(false); + return; + } + + // Check if either user has blocked the other + if (await messengerGrain.IsBlockedAsync(recipientId).ConfigureAwait(false)) + { + await ctx.SendComposerAsync( + new InstantMessageErrorMessageComposer + { + ErrorCode = InstantMessageErrorCodeType.NotFriend, + PlayerId = recipientId, + Message = string.Empty, + }, + ct + ) + .ConfigureAwait(false); + return; + } + + var recipientGrain = _grainFactory.GetMessengerGrain(recipientId); + if (await recipientGrain.IsBlockedAsync(ctx.PlayerId).ConfigureAwait(false)) + { + await ctx.SendComposerAsync( + new InstantMessageErrorMessageComposer + { + ErrorCode = InstantMessageErrorCodeType.NotFriend, + PlayerId = recipientId, + Message = string.Empty, + }, + ct + ) + .ConfigureAwait(false); + return; + } + + // Get sender info + var senderGrain = _grainFactory.GetPlayerGrain(ctx.PlayerId); + var senderSummary = await senderGrain.GetSummaryAsync(ct).ConfigureAwait(false); + + var messageId = await messengerGrain + .SendMessageAsync( + recipientId, + message.Message, + message.ConfirmationId, + senderSummary.Name, + senderSummary.Figure, + ct + ) + .ConfigureAwait(false); + + // Send confirmation to sender + await ctx.SendComposerAsync( + new NewConsoleMessageMessageComposer + { + ChatId = message.ChatId, + Message = message.Message, + SecondsSinceSent = 0, + MessageId = messageId, + ConfirmationId = message.ConfirmationId, + SenderId = ctx.PlayerId, + SenderName = senderSummary.Name, + SenderFigure = senderSummary.Figure, + }, + ct + ) + .ConfigureAwait(false); } } diff --git a/Turbo.PacketHandlers/FriendList/SendRoomInviteMessageHandler.cs b/Turbo.PacketHandlers/FriendList/SendRoomInviteMessageHandler.cs index 11522433..c73e536d 100644 --- a/Turbo.PacketHandlers/FriendList/SendRoomInviteMessageHandler.cs +++ b/Turbo.PacketHandlers/FriendList/SendRoomInviteMessageHandler.cs @@ -1,18 +1,61 @@ +using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; +using Orleans; using Turbo.Messages.Registry; using Turbo.Primitives.Messages.Incoming.FriendList; +using Turbo.Primitives.Messages.Outgoing.FriendList; +using Turbo.Primitives.Orleans; +using Turbo.Primitives.Players; namespace Turbo.PacketHandlers.FriendList; -public class SendRoomInviteMessageHandler : IMessageHandler +public class SendRoomInviteMessageHandler(IGrainFactory grainFactory) + : IMessageHandler { + private readonly IGrainFactory _grainFactory = grainFactory; + public async ValueTask HandleAsync( SendRoomInviteMessage message, MessageContext ctx, CancellationToken ct ) { - await ValueTask.CompletedTask.ConfigureAwait(false); + if (ctx.PlayerId <= 0 || ctx.RoomId <= 0) + return; + + var messengerGrain = _grainFactory.GetMessengerGrain(ctx.PlayerId); + var failedRecipients = new List(); + + foreach (var friendId in message.FriendIds) + { + var friendPlayerId = PlayerId.Parse(friendId); + + // Validate friendship + if (!await messengerGrain.IsFriendAsync(friendPlayerId).ConfigureAwait(false)) + { + failedRecipients.Add(friendId); + continue; + } + + // Send invite via the friend's messenger grain + var friendGrain = _grainFactory.GetMessengerGrain(friendPlayerId); + await friendGrain + .ReceiveRoomInviteAsync(ctx.PlayerId, message.Message) + .ConfigureAwait(false); + } + + if (failedRecipients.Count > 0) + { + await ctx.SendComposerAsync( + new RoomInviteErrorMessageComposer + { + ErrorCode = 1, + FailedRecipients = failedRecipients, + }, + ct + ) + .ConfigureAwait(false); + } } } diff --git a/Turbo.PacketHandlers/FriendList/SetRelationshipStatusMessageHandler.cs b/Turbo.PacketHandlers/FriendList/SetRelationshipStatusMessageHandler.cs index cada1876..5a286407 100644 --- a/Turbo.PacketHandlers/FriendList/SetRelationshipStatusMessageHandler.cs +++ b/Turbo.PacketHandlers/FriendList/SetRelationshipStatusMessageHandler.cs @@ -1,18 +1,29 @@ using System.Threading; using System.Threading.Tasks; +using Orleans; using Turbo.Messages.Registry; using Turbo.Primitives.Messages.Incoming.FriendList; +using Turbo.Primitives.Orleans; namespace Turbo.PacketHandlers.FriendList; -public class SetRelationshipStatusMessageHandler : IMessageHandler +public class SetRelationshipStatusMessageHandler(IGrainFactory grainFactory) + : IMessageHandler { + private readonly IGrainFactory _grainFactory = grainFactory; + public async ValueTask HandleAsync( SetRelationshipStatusMessage message, MessageContext ctx, CancellationToken ct ) { - await ValueTask.CompletedTask.ConfigureAwait(false); + if (ctx.PlayerId <= 0) + return; + + var messengerGrain = _grainFactory.GetMessengerGrain(ctx.PlayerId); + await messengerGrain + .SetRelationshipStatusAsync(message.FriendUserId, message.RelationType, ct) + .ConfigureAwait(false); } } diff --git a/Turbo.PacketHandlers/FriendList/VisitUserMessageHandler.cs b/Turbo.PacketHandlers/FriendList/VisitUserMessageHandler.cs index d800f797..f4abc939 100644 --- a/Turbo.PacketHandlers/FriendList/VisitUserMessageHandler.cs +++ b/Turbo.PacketHandlers/FriendList/VisitUserMessageHandler.cs @@ -1,18 +1,44 @@ using System.Threading; using System.Threading.Tasks; +using Orleans; using Turbo.Messages.Registry; using Turbo.Primitives.Messages.Incoming.FriendList; +using Turbo.Primitives.Orleans; +using Turbo.Primitives.Players; namespace Turbo.PacketHandlers.FriendList; -public class VisitUserMessageHandler : IMessageHandler +public class VisitUserMessageHandler(IGrainFactory grainFactory) : IMessageHandler { + private readonly IGrainFactory _grainFactory = grainFactory; + public async ValueTask HandleAsync( VisitUserMessage message, MessageContext ctx, CancellationToken ct ) { - await ValueTask.CompletedTask.ConfigureAwait(false); + if (ctx.PlayerId <= 0) + return; + + // Resolve target player ID by name + var playerDirectory = _grainFactory.GetPlayerDirectoryGrain(); + var targetId = await playerDirectory + .GetPlayerIdAsync(message.PlayerName, ct) + .ConfigureAwait(false); + + if (targetId is null) + return; + + // Get their active room + var targetPresence = _grainFactory.GetPlayerPresenceGrain(targetId.Value); + var activeRoom = await targetPresence.GetActiveRoomAsync().ConfigureAwait(false); + + if (activeRoom.RoomId <= 0) + return; + + // Forward the requester to that room + var myPresence = _grainFactory.GetPlayerPresenceGrain(ctx.PlayerId); + await myPresence.SetPendingRoomAsync(activeRoom.RoomId, true).ConfigureAwait(false); } } diff --git a/Turbo.PacketHandlers/Navigator/ForwardToSomeRoomMessageHandler.cs b/Turbo.PacketHandlers/Navigator/ForwardToSomeRoomMessageHandler.cs index a63db93f..8b3caa20 100644 --- a/Turbo.PacketHandlers/Navigator/ForwardToSomeRoomMessageHandler.cs +++ b/Turbo.PacketHandlers/Navigator/ForwardToSomeRoomMessageHandler.cs @@ -1,18 +1,39 @@ using System.Threading; using System.Threading.Tasks; +using Orleans; using Turbo.Messages.Registry; using Turbo.Primitives.Messages.Incoming.Navigator; +using Turbo.Primitives.Messages.Outgoing.Room.Session; +using Turbo.Primitives.Orleans; namespace Turbo.PacketHandlers.Navigator; -public class ForwardToSomeRoomMessageHandler : IMessageHandler +public class ForwardToSomeRoomMessageHandler(IGrainFactory grainFactory) + : IMessageHandler { + private readonly IGrainFactory _grainFactory = grainFactory; + public async ValueTask HandleAsync( ForwardToSomeRoomMessage message, MessageContext ctx, CancellationToken ct ) { - await ValueTask.CompletedTask.ConfigureAwait(false); + if (ctx.PlayerId <= 0) + return; + + var roomDirectory = _grainFactory.GetRoomDirectoryGrain(); + var randomRoomId = await roomDirectory + .GetRandomPopulatedRoomAsync(ct) + .ConfigureAwait(false); + + if (randomRoomId is not null && randomRoomId.Value > 0) + { + await ctx.SendComposerAsync( + new RoomForwardMessageComposer { RoomId = randomRoomId.Value }, + ct + ) + .ConfigureAwait(false); + } } } diff --git a/Turbo.PacketHandlers/Users/BlockListInitMessageHandler.cs b/Turbo.PacketHandlers/Users/BlockListInitMessageHandler.cs new file mode 100644 index 00000000..3fa74e58 --- /dev/null +++ b/Turbo.PacketHandlers/Users/BlockListInitMessageHandler.cs @@ -0,0 +1,34 @@ +using System.Threading; +using System.Threading.Tasks; +using Orleans; +using Turbo.Messages.Registry; +using Turbo.Primitives.Messages.Incoming.Users; +using Turbo.Primitives.Messages.Outgoing.Users; +using Turbo.Primitives.Orleans; + +namespace Turbo.PacketHandlers.Users; + +public class BlockListInitMessageHandler(IGrainFactory grainFactory) + : IMessageHandler +{ + private readonly IGrainFactory _grainFactory = grainFactory; + + public async ValueTask HandleAsync( + BlockListInitMessage message, + MessageContext ctx, + CancellationToken ct + ) + { + if (ctx.PlayerId <= 0) + return; + + var messengerGrain = _grainFactory.GetMessengerGrain(ctx.PlayerId); + var blockedIds = await messengerGrain.GetBlockedUserIdsAsync(ct).ConfigureAwait(false); + + await ctx.SendComposerAsync( + new BlockListMessageComposer { BlockedUserIds = blockedIds }, + ct + ) + .ConfigureAwait(false); + } +} diff --git a/Turbo.PacketHandlers/Users/BlockUserMessageHandler.cs b/Turbo.PacketHandlers/Users/BlockUserMessageHandler.cs new file mode 100644 index 00000000..c31837c3 --- /dev/null +++ b/Turbo.PacketHandlers/Users/BlockUserMessageHandler.cs @@ -0,0 +1,37 @@ +using System.Threading; +using System.Threading.Tasks; +using Orleans; +using Turbo.Messages.Registry; +using Turbo.Primitives.Messages.Incoming.Users; +using Turbo.Primitives.Messages.Outgoing.Users; +using Turbo.Primitives.Orleans; + +namespace Turbo.PacketHandlers.Users; + +public class BlockUserMessageHandler(IGrainFactory grainFactory) : IMessageHandler +{ + private readonly IGrainFactory _grainFactory = grainFactory; + + public async ValueTask HandleAsync( + BlockUserMessage message, + MessageContext ctx, + CancellationToken ct + ) + { + if (ctx.PlayerId <= 0) + return; + + var messengerGrain = _grainFactory.GetMessengerGrain(ctx.PlayerId); + await messengerGrain.BlockUserAsync(message.PlayerId, ct).ConfigureAwait(false); + + await ctx.SendComposerAsync( + new BlockUserUpdateMessageComposer + { + Result = 1, // Blocked + UserId = message.PlayerId, + }, + ct + ) + .ConfigureAwait(false); + } +} diff --git a/Turbo.PacketHandlers/Users/GetIgnoredUsersMessageHandler.cs b/Turbo.PacketHandlers/Users/GetIgnoredUsersMessageHandler.cs index 0d45a512..4fb426f4 100644 --- a/Turbo.PacketHandlers/Users/GetIgnoredUsersMessageHandler.cs +++ b/Turbo.PacketHandlers/Users/GetIgnoredUsersMessageHandler.cs @@ -1,18 +1,34 @@ using System.Threading; using System.Threading.Tasks; +using Orleans; using Turbo.Messages.Registry; using Turbo.Primitives.Messages.Incoming.Users; +using Turbo.Primitives.Messages.Outgoing.Users; +using Turbo.Primitives.Orleans; namespace Turbo.PacketHandlers.Users; -public class GetIgnoredUsersMessageHandler : IMessageHandler +public class GetIgnoredUsersMessageHandler(IGrainFactory grainFactory) + : IMessageHandler { + private readonly IGrainFactory _grainFactory = grainFactory; + public async ValueTask HandleAsync( GetIgnoredUsersMessage message, MessageContext ctx, CancellationToken ct ) { - await ValueTask.CompletedTask.ConfigureAwait(false); + if (ctx.PlayerId <= 0) + return; + + var messengerGrain = _grainFactory.GetMessengerGrain(ctx.PlayerId); + var ignoredIds = await messengerGrain.GetIgnoredUserIdsAsync(ct).ConfigureAwait(false); + + await ctx.SendComposerAsync( + new IgnoredUsersMessageComposer { IgnoredUserIds = ignoredIds }, + ct + ) + .ConfigureAwait(false); } } diff --git a/Turbo.PacketHandlers/Users/GetRelationshipStatusInfoMessageHandler.cs b/Turbo.PacketHandlers/Users/GetRelationshipStatusInfoMessageHandler.cs index c99a407c..f29f70fb 100644 --- a/Turbo.PacketHandlers/Users/GetRelationshipStatusInfoMessageHandler.cs +++ b/Turbo.PacketHandlers/Users/GetRelationshipStatusInfoMessageHandler.cs @@ -1,19 +1,38 @@ using System.Threading; using System.Threading.Tasks; +using Orleans; using Turbo.Messages.Registry; using Turbo.Primitives.Messages.Incoming.Users; +using Turbo.Primitives.Messages.Outgoing.Users; +using Turbo.Primitives.Orleans; namespace Turbo.PacketHandlers.Users; -public class GetRelationshipStatusInfoMessageHandler +public class GetRelationshipStatusInfoMessageHandler(IGrainFactory grainFactory) : IMessageHandler { + private readonly IGrainFactory _grainFactory = grainFactory; + public async ValueTask HandleAsync( GetRelationshipStatusInfoMessage message, MessageContext ctx, CancellationToken ct ) { - await ValueTask.CompletedTask.ConfigureAwait(false); + if (ctx.PlayerId <= 0) + return; + + var targetGrain = _grainFactory.GetMessengerGrain(message.PlayerId); + var entries = await targetGrain.GetRelationshipStatusInfoAsync(ct).ConfigureAwait(false); + + await ctx.SendComposerAsync( + new RelationshipStatusInfoEventMessageComposer + { + UserId = message.PlayerId, + Entries = entries, + }, + ct + ) + .ConfigureAwait(false); } } diff --git a/Turbo.PacketHandlers/Users/IgnoreUserMessageHandler.cs b/Turbo.PacketHandlers/Users/IgnoreUserMessageHandler.cs index d7e6c802..ffc77a64 100644 --- a/Turbo.PacketHandlers/Users/IgnoreUserMessageHandler.cs +++ b/Turbo.PacketHandlers/Users/IgnoreUserMessageHandler.cs @@ -1,18 +1,43 @@ using System.Threading; using System.Threading.Tasks; +using Microsoft.Extensions.Configuration; +using Orleans; using Turbo.Messages.Registry; using Turbo.Primitives.Messages.Incoming.Users; +using Turbo.Primitives.Messages.Outgoing.Users; +using Turbo.Primitives.Orleans; +using Turbo.Primitives.Players; namespace Turbo.PacketHandlers.Users; -public class IgnoreUserMessageHandler : IMessageHandler +public class IgnoreUserMessageHandler(IGrainFactory grainFactory, IConfiguration configuration) + : IMessageHandler { + private readonly IGrainFactory _grainFactory = grainFactory; + private readonly IConfiguration _configuration = configuration; + public async ValueTask HandleAsync( IgnoreUserMessage message, MessageContext ctx, CancellationToken ct ) { - await ValueTask.CompletedTask.ConfigureAwait(false); + if (ctx.PlayerId <= 0) + return; + + var targetId = PlayerId.Parse(message.PlayerId); + + var maxIgnoreCapacity = _configuration.GetValue("Turbo:Messenger:IgnoreListLimit"); + + var messengerGrain = _grainFactory.GetMessengerGrain(ctx.PlayerId); + var result = await messengerGrain + .IgnoreUserAsync(targetId, maxIgnoreCapacity, ct) + .ConfigureAwait(false); + + await ctx.SendComposerAsync( + new IgnoreResultMessageComposer { Result = result, PlayerName = string.Empty }, + ct + ) + .ConfigureAwait(false); } } diff --git a/Turbo.PacketHandlers/Users/UnblockUserMessageHandler.cs b/Turbo.PacketHandlers/Users/UnblockUserMessageHandler.cs new file mode 100644 index 00000000..768d5a9a --- /dev/null +++ b/Turbo.PacketHandlers/Users/UnblockUserMessageHandler.cs @@ -0,0 +1,38 @@ +using System.Threading; +using System.Threading.Tasks; +using Orleans; +using Turbo.Messages.Registry; +using Turbo.Primitives.Messages.Incoming.Users; +using Turbo.Primitives.Messages.Outgoing.Users; +using Turbo.Primitives.Orleans; + +namespace Turbo.PacketHandlers.Users; + +public class UnblockUserMessageHandler(IGrainFactory grainFactory) + : IMessageHandler +{ + private readonly IGrainFactory _grainFactory = grainFactory; + + public async ValueTask HandleAsync( + UnblockUserMessage message, + MessageContext ctx, + CancellationToken ct + ) + { + if (ctx.PlayerId <= 0) + return; + + var messengerGrain = _grainFactory.GetMessengerGrain(ctx.PlayerId); + await messengerGrain.UnblockUserAsync(message.PlayerId, ct).ConfigureAwait(false); + + await ctx.SendComposerAsync( + new BlockUserUpdateMessageComposer + { + Result = 0, // Unblocked + UserId = message.PlayerId, + }, + ct + ) + .ConfigureAwait(false); + } +} diff --git a/Turbo.PacketHandlers/Users/UnignoreUserMessageHandler.cs b/Turbo.PacketHandlers/Users/UnignoreUserMessageHandler.cs index 4d59db82..05d94dda 100644 --- a/Turbo.PacketHandlers/Users/UnignoreUserMessageHandler.cs +++ b/Turbo.PacketHandlers/Users/UnignoreUserMessageHandler.cs @@ -1,18 +1,41 @@ using System.Threading; using System.Threading.Tasks; +using Orleans; using Turbo.Messages.Registry; using Turbo.Primitives.Messages.Incoming.Users; +using Turbo.Primitives.Messages.Outgoing.Users; +using Turbo.Primitives.Orleans; +using Turbo.Primitives.Players; namespace Turbo.PacketHandlers.Users; -public class UnignoreUserMessageHandler : IMessageHandler +public class UnignoreUserMessageHandler(IGrainFactory grainFactory) + : IMessageHandler { + private readonly IGrainFactory _grainFactory = grainFactory; + public async ValueTask HandleAsync( UnignoreUserMessage message, MessageContext ctx, CancellationToken ct ) { - await ValueTask.CompletedTask.ConfigureAwait(false); + if (ctx.PlayerId <= 0) + return; + + var targetId = PlayerId.Parse(message.PlayerId); + + var messengerGrain = _grainFactory.GetMessengerGrain(ctx.PlayerId); + await messengerGrain.UnignoreUserAsync(targetId, ct).ConfigureAwait(false); + + await ctx.SendComposerAsync( + new IgnoreResultMessageComposer + { + Result = 3, // Unignored + PlayerName = string.Empty, + }, + ct + ) + .ConfigureAwait(false); } } diff --git a/Turbo.Players/Grains/MessengerGrain.cs b/Turbo.Players/Grains/MessengerGrain.cs new file mode 100644 index 00000000..1074bf74 --- /dev/null +++ b/Turbo.Players/Grains/MessengerGrain.cs @@ -0,0 +1,1381 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; +using Orleans; +using Turbo.Database.Context; +using Turbo.Database.Entities.Messenger; +using Turbo.Primitives.FriendList.Enums; +using Turbo.Primitives.FriendList.Grains; +using Turbo.Primitives.Messages.Outgoing.FriendList; +using Turbo.Primitives.Orleans; +using Turbo.Primitives.Players; +using Turbo.Primitives.Snapshots.FriendList; + +namespace Turbo.Players.Grains; + +internal sealed class MessengerGrain( + IDbContextFactory dbCtxFactory, + IGrainFactory grainFactory, + ILogger logger +) : Grain, IMessengerGrain +{ + private PlayerId _playerId; + + // In-memory state loaded from DB on activation + private readonly Dictionary _friends = []; + private readonly Dictionary _incomingRequests = []; + private readonly HashSet _blockedUserIds = []; + private readonly HashSet _ignoredUserIds = []; + private readonly List _categories = []; + private bool _isLoaded; + + // Session-based message history — cleared on deactivation, never persisted + private const int MaxSessionMessagesPerConversation = 200; + private readonly Dictionary> _sessionMessages = []; + private int _nextSessionMessageId = 1; + + // Tracks whether this player has an active session (set by NotifyOnline/OfflineAsync) + private bool _isOnline; + + // Batched delivered-flag queue — flushed periodically to avoid per-message DB writes + private readonly HashSet _pendingDeliveredIds = []; + + public override async Task OnActivateAsync(CancellationToken ct) + { + _playerId = PlayerId.Parse((int)this.GetPrimaryKeyLong()); + + this.RegisterGrainTimer( + static async (self, ct) => + await ((MessengerGrain)self!).FlushDeliveredMessagesAsync(ct), + this, + TimeSpan.FromSeconds(10), + TimeSpan.FromSeconds(10) + ); + + await LoadFromDatabaseAsync(ct); + } + + public override async Task OnDeactivateAsync(DeactivationReason reason, CancellationToken ct) + { + await FlushDeliveredMessagesAsync(ct); + } + + private async Task LoadFromDatabaseAsync(CancellationToken ct) + { + if (_isLoaded) + return; + + await using var dbCtx = await dbCtxFactory.CreateDbContextAsync(ct); + var playerId = (int)_playerId; + + // Load friends (without online checks — those are done in parallel below) + var friendEntities = await dbCtx + .MessengerFriends.AsNoTracking() + .Include(f => f.FriendPlayerEntity) + .Where(f => f.PlayerEntityId == playerId) + .ToListAsync(ct); + + foreach (var entity in friendEntities) + { + _friends[entity.FriendPlayerEntityId] = new MessengerFriendSnapshot + { + PlayerId = PlayerId.Parse(entity.FriendPlayerEntityId), + Name = entity.FriendPlayerEntity.Name, + Gender = entity.FriendPlayerEntity.Gender, + Online = false, + FollowingAllowed = true, + Figure = entity.FriendPlayerEntity.Figure, + CategoryId = entity.MessengerCategoryEntityId ?? 0, + Motto = entity.FriendPlayerEntity.Motto ?? string.Empty, + LastAccess = entity.FriendPlayerEntity.UpdatedAt.ToString("dd-MM-yyyy HH:mm:ss"), + RealName = string.Empty, + FacebookId = string.Empty, + PersistedMessageUser = true, + VipMember = false, + PocketHabboUser = false, + RelationshipStatus = (short)entity.RelationType, + }; + } + + // Load incoming friend requests + var requestEntities = await dbCtx + .MessengerRequests.AsNoTracking() + .Include(r => r.PlayerEntity) + .Where(r => r.RequestedPlayerEntityId == playerId) + .ToListAsync(ct); + + foreach (var entity in requestEntities) + { + _incomingRequests[entity.PlayerEntityId] = new FriendRequestSnapshot + { + RequestId = entity.PlayerEntityId, + RequesterName = entity.PlayerEntity.Name, + FigureString = entity.PlayerEntity.Figure, + RequesterUserId = PlayerId.Parse(entity.PlayerEntityId), + }; + } + + // Load blocked users + var blockedEntities = await dbCtx + .MessengerBlocked.AsNoTracking() + .Where(b => b.PlayerEntityId == playerId) + .Select(b => b.BlockedPlayerEntityId) + .ToListAsync(ct); + + foreach (var id in blockedEntities) + _blockedUserIds.Add(id); + + // Load ignored users + var ignoredEntities = await dbCtx + .MessengerIgnored.AsNoTracking() + .Where(i => i.PlayerEntityId == playerId) + .Select(i => i.IgnoredPlayerEntityId) + .ToListAsync(ct); + + foreach (var id in ignoredEntities) + _ignoredUserIds.Add(id); + + // Load categories + var categoryEntities = await dbCtx + .MessengerCategories.AsNoTracking() + .Where(c => c.PlayerEntityId == playerId) + .ToListAsync(ct); + + foreach (var entity in categoryEntities) + _categories.Add(new FriendCategorySnapshot { Id = entity.Id, Name = entity.Name }); + + // Batch-check online status for all friends in parallel (avoids N+1 sequential grain calls) + if (_friends.Count > 0) + { + var onlineTasks = _friends + .Keys.Select(async friendId => + { + var isOnline = await IsPlayerOnlineAsync(PlayerId.Parse(friendId)); + return (friendId, isOnline); + }) + .ToList(); + + var results = await Task.WhenAll(onlineTasks); + + foreach (var (friendId, isOnline) in results) + { + if (isOnline && _friends.TryGetValue(friendId, out var friend)) + _friends[friendId] = friend with { Online = true }; + } + } + + _isLoaded = true; + } + + #region Initialization + + public Task> GetFriendsAsync(CancellationToken ct) => + Task.FromResult(_friends.Values.ToList()); + + public Task> GetCategoriesAsync(CancellationToken ct) => + Task.FromResult(_categories.ToList()); + + public Task> GetFriendRequestsAsync(CancellationToken ct) => + Task.FromResult(_incomingRequests.Values.ToList()); + + public Task> GetBlockedUserIdsAsync(CancellationToken ct) => + Task.FromResult(_blockedUserIds.ToList()); + + public Task> GetIgnoredUserIdsAsync(CancellationToken ct) => + Task.FromResult(_ignoredUserIds.ToList()); + + #endregion + + #region Friend Requests + + public async Task SendFriendRequestAsync( + PlayerId targetPlayerId, + string senderName, + string senderFigure, + int friendLimit, + CancellationToken ct + ) + { + var myId = (int)_playerId; + var targetId = (int)targetPlayerId; + + // Check own friend limit + if (_friends.Count >= friendLimit) + return FriendListErrorCodeType.YouHitFriendLimit; + + // Check if blocked by them + var targetGrain = grainFactory.GetMessengerGrain(targetPlayerId); + if (await targetGrain.IsBlockedAsync(_playerId)) + return FriendListErrorCodeType.BlockedByThem; + + // Check if we blocked them + if (_blockedUserIds.Contains(targetId)) + return FriendListErrorCodeType.BlockedByYou; + + // Check existing friendship + if (_friends.ContainsKey(targetId)) + return null; // Already friends, silently ignore + + // Check target friend limit + var targetFriendCount = await targetGrain.GetFriendCountAsync(); + if (targetFriendCount >= friendLimit) + return FriendListErrorCodeType.TheyHitFriendLimit; + + // Check existing pending request in either direction + await using var dbCtx = await dbCtxFactory.CreateDbContextAsync(ct); + + var existingRequest = await dbCtx + .MessengerRequests.AsNoTracking() + .AnyAsync( + r => + (r.PlayerEntityId == myId && r.RequestedPlayerEntityId == targetId) + || (r.PlayerEntityId == targetId && r.RequestedPlayerEntityId == myId), + ct + ); + + if (existingRequest) + return null; // Already a pending request, silently ignore + + // Create the request + dbCtx.MessengerRequests.Add( + new MessengerRequestEntity + { + PlayerEntityId = myId, + RequestedPlayerEntityId = targetId, + PlayerEntity = null!, + RequestedPlayerEntity = null!, + } + ); + + await dbCtx.SaveChangesAsync(ct); + + // Notify the target + var requestSnapshot = new FriendRequestSnapshot + { + RequestId = myId, + RequesterName = senderName, + FigureString = senderFigure, + RequesterUserId = _playerId, + }; + + await targetGrain.ReceiveFriendRequestAsync(requestSnapshot); + + return null; // Success + } + + public async Task ReceiveFriendRequestAsync(FriendRequestSnapshot request) + { + _incomingRequests[request.RequesterUserId] = request; + + // Send notification to the player if online + try + { + var presence = grainFactory.GetPlayerPresenceGrain(_playerId); + await presence.SendComposerAsync( + new NewFriendRequestMessageComposer { Request = request } + ); + } + catch (Exception ex) + { + logger.LogDebug( + ex, + "Failed to send friend request notification to player {PlayerId}", + _playerId + ); + } + } + + public async Task<( + List Failures, + List Updates + )> AcceptFriendRequestsAsync(List requestIds, int friendLimit, CancellationToken ct) + { + var failures = new List(); + var updates = new List(); + var myId = (int)_playerId; + + // Fetch our own summary once, outside the loop — it's the same for every iteration + var myPlayerGrain = grainFactory.GetPlayerGrain(_playerId); + var mySummary = await myPlayerGrain.GetSummaryAsync(ct); + + foreach (var requesterId in requestIds) + { + // Check if we actually have this request + if (!_incomingRequests.ContainsKey(requesterId)) + { + failures.Add( + new AcceptFriendFailureSnapshot + { + SenderId = requesterId, + ErrorCode = FriendListErrorCodeType.FriendRequestNotFound, + } + ); + continue; + } + + // Check our friend limit + if (_friends.Count >= friendLimit) + { + failures.Add( + new AcceptFriendFailureSnapshot + { + SenderId = requesterId, + ErrorCode = FriendListErrorCodeType.YouHitFriendLimit, + } + ); + continue; + } + + // Check their friend limit + var requesterGrain = grainFactory.GetMessengerGrain(requesterId); + var requesterFriendCount = await requesterGrain.GetFriendCountAsync(); + if (requesterFriendCount >= friendLimit) + { + failures.Add( + new AcceptFriendFailureSnapshot + { + SenderId = requesterId, + ErrorCode = FriendListErrorCodeType.TheyHitFriendLimit, + } + ); + continue; + } + + await using var dbCtx = await dbCtxFactory.CreateDbContextAsync(ct); + + // Track-remove the request so delete + friendship insert go through one SaveChanges + var requestEntity = await dbCtx.MessengerRequests.FirstOrDefaultAsync( + r => r.PlayerEntityId == requesterId && r.RequestedPlayerEntityId == myId, + ct + ); + + if (requestEntity != null) + dbCtx.MessengerRequests.Remove(requestEntity); + + // Create bidirectional friendship + dbCtx.MessengerFriends.Add( + new MessengerFriendEntity + { + PlayerEntityId = myId, + FriendPlayerEntityId = requesterId, + RelationType = MessengerFriendRelationType.Zero, + PlayerEntity = null!, + FriendPlayerEntity = null!, + } + ); + + dbCtx.MessengerFriends.Add( + new MessengerFriendEntity + { + PlayerEntityId = requesterId, + FriendPlayerEntityId = myId, + RelationType = MessengerFriendRelationType.Zero, + PlayerEntity = null!, + FriendPlayerEntity = null!, + } + ); + + await dbCtx.SaveChangesAsync(ct); + + // Remove from local request cache + _incomingRequests.Remove(requesterId); + + // Get requester's player info + online status in parallel (independent grain calls) + var requesterPlayerGrain = grainFactory.GetPlayerGrain(PlayerId.Parse(requesterId)); + var summaryTask = requesterPlayerGrain.GetSummaryAsync(ct); + var onlineTask = IsPlayerOnlineAsync(PlayerId.Parse(requesterId)); + await Task.WhenAll(summaryTask, onlineTask); + + var requesterSummary = await summaryTask; + var requesterIsOnline = await onlineTask; + + // Add to our local friends cache + var friendSnapshotForUs = new MessengerFriendSnapshot + { + PlayerId = PlayerId.Parse(requesterId), + Name = requesterSummary.Name, + Gender = requesterSummary.Gender, + Online = requesterIsOnline, + FollowingAllowed = true, + Figure = requesterSummary.Figure, + CategoryId = 0, + Motto = requesterSummary.Motto, + LastAccess = requesterSummary.CreatedAt.ToString("dd-MM-yyyy HH:mm:ss"), + RealName = string.Empty, + FacebookId = string.Empty, + PersistedMessageUser = true, + VipMember = false, + PocketHabboUser = false, + RelationshipStatus = 0, + }; + _friends[requesterId] = friendSnapshotForUs; + + // Collect update for the caller (handler will send it to the client) + updates.Add( + new FriendListUpdateSnapshot + { + ActionType = FriendListUpdateActionType.Added, + FriendId = requesterId, + Friend = friendSnapshotForUs, + } + ); + + // Build snapshot of ourselves for the requester (using pre-fetched summary) + var friendSnapshotForThem = new MessengerFriendSnapshot + { + PlayerId = _playerId, + Name = mySummary.Name, + Gender = mySummary.Gender, + Online = true, // We must be online to accept + FollowingAllowed = true, + Figure = mySummary.Figure, + CategoryId = 0, + Motto = mySummary.Motto, + LastAccess = mySummary.CreatedAt.ToString("dd-MM-yyyy HH:mm:ss"), + RealName = string.Empty, + FacebookId = string.Empty, + PersistedMessageUser = true, + VipMember = false, + PocketHabboUser = false, + RelationshipStatus = 0, + }; + + // Notify the requester of the new friendship + LogAndForget( + requesterGrain.OnFriendAcceptedAsync(friendSnapshotForThem), + nameof(OnFriendAcceptedAsync), + requesterId + ); + } + + return (failures, updates); + } + + public async Task DeclineFriendRequestsAsync( + List? requestIds, + bool declineAll, + CancellationToken ct + ) + { + var myId = (int)_playerId; + + await using var dbCtx = await dbCtxFactory.CreateDbContextAsync(ct); + + if (declineAll) + { + await dbCtx + .MessengerRequests.Where(r => r.RequestedPlayerEntityId == myId) + .ExecuteDeleteAsync(ct); + + _incomingRequests.Clear(); + } + else if (requestIds is { Count: > 0 }) + { + await dbCtx + .MessengerRequests.Where(r => + r.RequestedPlayerEntityId == myId && requestIds.Contains(r.PlayerEntityId) + ) + .ExecuteDeleteAsync(ct); + + foreach (var id in requestIds) + _incomingRequests.Remove(id); + } + } + + #endregion + + #region Friend Management + + public async Task RemoveFriendsAsync(List friendIds, CancellationToken ct) + { + var myId = (int)_playerId; + + // Filter to only friends we actually have + var validFriendIds = friendIds.Where(id => _friends.ContainsKey(id)).ToList(); + + if (validFriendIds.Count == 0) + return; + + // Batch-delete all friendship rows in a single query instead of N+1 DB calls + await using var dbCtx = await dbCtxFactory.CreateDbContextAsync(ct); + await dbCtx + .MessengerFriends.Where(f => + (f.PlayerEntityId == myId && validFriendIds.Contains(f.FriendPlayerEntityId)) + || (validFriendIds.Contains(f.PlayerEntityId) && f.FriendPlayerEntityId == myId) + ) + .ExecuteDeleteAsync(ct); + + // Build all removal updates and update local cache + var removeUpdates = new List(); + foreach (var friendId in validFriendIds) + { + _friends.Remove(friendId); + + removeUpdates.Add( + new FriendListUpdateSnapshot + { + ActionType = FriendListUpdateActionType.Removed, + FriendId = friendId, + } + ); + + // Notify the other side (fire-and-forget) + var friendGrain = grainFactory.GetMessengerGrain(friendId); + LogAndForget( + friendGrain.OnFriendRemovedAsync(_playerId), + nameof(OnFriendRemovedAsync), + friendId + ); + } + + // Send all removal updates to ourselves in a single composer call + try + { + var presence = grainFactory.GetPlayerPresenceGrain(_playerId); + await presence.SendComposerAsync( + new FriendListUpdateMessageComposer + { + FriendCategories = [], + Updates = removeUpdates, + } + ); + } + catch (Exception ex) + { + logger.LogDebug( + ex, + "Failed to send friend removal updates to player {PlayerId}", + _playerId + ); + } + } + + public async Task SetRelationshipStatusAsync(int friendId, int status, CancellationToken ct) + { + if (!_friends.ContainsKey(friendId)) + return; + + var myId = (int)_playerId; + var relationType = (MessengerFriendRelationType)status; + + await using var dbCtx = await dbCtxFactory.CreateDbContextAsync(ct); + + await dbCtx + .MessengerFriends.Where(f => + f.PlayerEntityId == myId && f.FriendPlayerEntityId == friendId + ) + .ExecuteUpdateAsync(up => up.SetProperty(f => f.RelationType, relationType), ct); + + // Update local cache + if (_friends.TryGetValue(friendId, out var existing)) + { + _friends[friendId] = existing with { RelationshipStatus = (short)status }; + } + } + + public Task> GetRelationshipStatusInfoAsync( + CancellationToken ct + ) + { + var entries = new List(); + + var grouped = _friends + .Values.Where(f => f.RelationshipStatus > 0) + .GroupBy(f => f.RelationshipStatus); + + foreach (var group in grouped) + { + var friends = group.ToList(); + var random = friends[Random.Shared.Next(friends.Count)]; + + entries.Add( + new RelationshipStatusEntrySnapshot + { + RelationshipStatusType = group.Key, + FriendCount = friends.Count, + RandomFriendId = random.PlayerId, + RandomFriendName = random.Name, + RandomFriendFigure = random.Figure, + } + ); + } + + return Task.FromResult(entries); + } + + #endregion + + #region Friend State Queries + + public Task IsFriendAsync(PlayerId playerId) => + Task.FromResult(_friends.ContainsKey(playerId)); + + public Task IsFriendRequestSentAsync(PlayerId playerId) => + Task.FromResult(_incomingRequests.ContainsKey(playerId)); + + public Task GetFriendCountAsync() => Task.FromResult(_friends.Count); + + #endregion + + #region Blocking + + public async Task BlockUserAsync(PlayerId targetId, CancellationToken ct) + { + var myId = (int)_playerId; + var target = (int)targetId; + + if (_blockedUserIds.Contains(target)) + return; + + await using var dbCtx = await dbCtxFactory.CreateDbContextAsync(ct); + + dbCtx.MessengerBlocked.Add( + new MessengerBlockedEntity + { + PlayerEntityId = myId, + BlockedPlayerEntityId = target, + PlayerEntity = null!, + BlockedPlayerEntity = null!, + } + ); + + await dbCtx.SaveChangesAsync(ct); + _blockedUserIds.Add(target); + + // If the blocked user is a friend, remove the friendship + if (_friends.ContainsKey(target)) + await RemoveFriendsAsync([target], ct); + } + + public async Task UnblockUserAsync(PlayerId targetId, CancellationToken ct) + { + var myId = (int)_playerId; + var target = (int)targetId; + + if (!_blockedUserIds.Contains(target)) + return; + + await using var dbCtx = await dbCtxFactory.CreateDbContextAsync(ct); + + await dbCtx + .MessengerBlocked.Where(b => + b.PlayerEntityId == myId && b.BlockedPlayerEntityId == target + ) + .ExecuteDeleteAsync(ct); + + _blockedUserIds.Remove(target); + } + + public Task IsBlockedAsync(PlayerId targetId) => + Task.FromResult(_blockedUserIds.Contains(targetId)); + + public Task IsBlockedByAsync(PlayerId targetId) => + // This checks if the OTHER player has blocked US + // We need to ask the other player's grain + Task.FromResult(false); // Handled via grain-to-grain call + #endregion + + #region Ignoring + + public async Task IgnoreUserAsync( + PlayerId targetId, + int maxIgnoreCapacity, + CancellationToken ct + ) + { + var myId = (int)_playerId; + var target = (int)targetId; + + if (_ignoredUserIds.Contains(target)) + return 0; // Already ignored, fail + + int result; + + await using var dbCtx = await dbCtxFactory.CreateDbContextAsync(ct); + + if (_ignoredUserIds.Count >= maxIgnoreCapacity) + { + // Evict the oldest entry + var oldest = await dbCtx + .MessengerIgnored.Where(i => i.PlayerEntityId == myId) + .OrderBy(i => i.Id) + .FirstOrDefaultAsync(ct); + + if (oldest is not null) + { + dbCtx.MessengerIgnored.Remove(oldest); + _ignoredUserIds.Remove(oldest.IgnoredPlayerEntityId); + } + + result = 2; // Success, oldest evicted + } + else + { + result = 1; // Success + } + + dbCtx.MessengerIgnored.Add( + new MessengerIgnoredEntity + { + PlayerEntityId = myId, + IgnoredPlayerEntityId = target, + PlayerEntity = null!, + IgnoredPlayerEntity = null!, + } + ); + + await dbCtx.SaveChangesAsync(ct); + _ignoredUserIds.Add(target); + + return result; + } + + public async Task UnignoreUserAsync(PlayerId targetId, CancellationToken ct) + { + var myId = (int)_playerId; + var target = (int)targetId; + + if (!_ignoredUserIds.Contains(target)) + return; + + await using var dbCtx = await dbCtxFactory.CreateDbContextAsync(ct); + + await dbCtx + .MessengerIgnored.Where(i => + i.PlayerEntityId == myId && i.IgnoredPlayerEntityId == target + ) + .ExecuteDeleteAsync(ct); + + _ignoredUserIds.Remove(target); + } + + #endregion + + #region Messaging + + public async Task SendMessageAsync( + PlayerId recipientId, + string message, + int confirmationId, + string senderName, + string senderFigure, + CancellationToken ct + ) + { + var myId = (int)_playerId; + var targetId = (int)recipientId; + + // Store message in DB (for offline delivery if recipient is offline) + await using var dbCtx = await dbCtxFactory.CreateDbContextAsync(ct); + + var now = DateTime.UtcNow; + + var messageEntity = new MessengerMessageEntity + { + SenderPlayerEntityId = myId, + ReceiverPlayerEntityId = targetId, + Message = message, + Timestamp = now, + SenderPlayerEntity = null!, + ReceiverPlayerEntity = null!, + }; + + dbCtx.MessengerMessages.Add(messageEntity); + await dbCtx.SaveChangesAsync(ct); + + var sessionMsgId = (_nextSessionMessageId++).ToString(); + + // Record in sender's session history + AddToSessionHistory( + targetId, + new MessageHistoryEntrySnapshot + { + SenderId = _playerId, + SenderName = senderName, + SenderFigure = senderFigure, + Message = message, + SecondsSinceSent = 0, + MessageId = sessionMsgId, + } + ); + + // Send to recipient (fire-and-forget to avoid cross-grain deadlock) + // confirmationId must be 0 for the recipient — the client uses confirmationId > 0 + // to route the packet to onConfirmOwnChatMessage (sender echo) instead of + // recordChatMessage (actual incoming message). + var recipientGrain = grainFactory.GetMessengerGrain(recipientId); + LogAndForget( + recipientGrain.ReceiveMessageAsync( + myId, // chatId = sender's ID for the recipient + message, + 0, + sessionMsgId, + 0, // confirmationId = 0 → client treats this as a received message + myId, + senderName, + senderFigure, + messageEntity.Id // DB id so recipient can delete after delivery + ), + nameof(ReceiveMessageAsync), + targetId + ); + + return sessionMsgId; + } + + public async Task ReceiveMessageAsync( + int chatId, + string messageText, + int secondsSinceSent, + string messageId, + int confirmationId, + int senderId, + string senderName, + string senderFigure, + int dbMessageId = 0 + ) + { + // If we're not online, the message stays in DB for delivery on next login + if (!_isOnline) + return; + + // Assign a session-local messageId for the recipient's history + var sessionMsgId = (_nextSessionMessageId++).ToString(); + + // Record in recipient's (our) session history + AddToSessionHistory( + senderId, + new MessageHistoryEntrySnapshot + { + SenderId = PlayerId.Parse(senderId), + SenderName = senderName, + SenderFigure = senderFigure, + Message = messageText, + SecondsSinceSent = secondsSinceSent, + MessageId = sessionMsgId, + } + ); + + var presence = grainFactory.GetPlayerPresenceGrain(_playerId); + await presence.SendComposerAsync( + new NewConsoleMessageMessageComposer + { + ChatId = chatId, + Message = messageText, + SecondsSinceSent = secondsSinceSent, + MessageId = sessionMsgId, + ConfirmationId = confirmationId, + SenderId = senderId, + SenderName = senderName, + SenderFigure = senderFigure, + } + ); + + // Queue delivered-flag update — flushed periodically by timer to avoid per-message DB writes + if (dbMessageId > 0) + _pendingDeliveredIds.Add(dbMessageId); + } + + public Task> GetMessageHistoryAsync( + int chatPartnerId, + string lastMessageId, + int pageSize, + CancellationToken ct + ) + { + // Session-based history — only messages from this login session + if (!_sessionMessages.TryGetValue(chatPartnerId, out var entries) || entries.Count == 0) + return Task.FromResult(new List()); + + IEnumerable result; + + // Cursor-based pagination: return entries before the given messageId + if (!string.IsNullOrEmpty(lastMessageId) && int.TryParse(lastMessageId, out var lastId)) + { + // Session message IDs are sequential ints starting from 1 + // Find the index of the entry with this messageId and return entries before it + var idx = entries.FindIndex(e => e.MessageId == lastMessageId); + if (idx > 0) + result = entries.Take(idx); + else + return Task.FromResult(new List()); + } + else + { + // No cursor — return the most recent entries + result = entries; + } + + // Return up to pageSize entries, newest first (reverse order from the end) + return Task.FromResult(result.Reverse().Take(pageSize).Reverse().ToList()); + } + + /// + /// Delivers unread offline messages from DB and marks them as delivered. + /// Called during messenger init after friend list is sent. + /// + public async Task DeliverOfflineMessagesAsync(CancellationToken ct) + { + var myId = (int)_playerId; + + await using var dbCtx = await dbCtxFactory.CreateDbContextAsync(ct); + + var offlineMessages = await dbCtx + .MessengerMessages.Include(m => m.SenderPlayerEntity) + .Where(m => m.ReceiverPlayerEntityId == myId && !m.Delivered) + .OrderBy(m => m.Id) + .ToListAsync(ct); + + if (offlineMessages.Count == 0) + return; + + var now = DateTime.UtcNow; + var presence = grainFactory.GetPlayerPresenceGrain(_playerId); + + foreach (var msg in offlineMessages) + { + var secondsSince = (int)(now - msg.Timestamp).TotalSeconds; + var sessionMsgId = (_nextSessionMessageId++).ToString(); + + // Add to session history + AddToSessionHistory( + msg.SenderPlayerEntityId, + new MessageHistoryEntrySnapshot + { + SenderId = PlayerId.Parse(msg.SenderPlayerEntityId), + SenderName = msg.SenderPlayerEntity.Name, + SenderFigure = msg.SenderPlayerEntity.Figure, + Message = msg.Message, + SecondsSinceSent = secondsSince, + MessageId = sessionMsgId, + } + ); + + // Push to client as NewConsoleMessage + await presence.SendComposerAsync( + new NewConsoleMessageMessageComposer + { + ChatId = msg.SenderPlayerEntityId, + Message = msg.Message, + SecondsSinceSent = secondsSince, + MessageId = sessionMsgId, + ConfirmationId = 0, + SenderId = msg.SenderPlayerEntityId, + SenderName = msg.SenderPlayerEntity.Name, + SenderFigure = msg.SenderPlayerEntity.Figure, + } + ); + } + + // Queue delivered-flag updates — flushed by the periodic timer + foreach (var msg in offlineMessages) + _pendingDeliveredIds.Add(msg.Id); + } + + private void AddToSessionHistory(int chatPartnerId, MessageHistoryEntrySnapshot entry) + { + if (!_sessionMessages.TryGetValue(chatPartnerId, out var list)) + { + list = []; + _sessionMessages[chatPartnerId] = list; + } + + list.Add(entry); + + // Evict oldest entries when conversation exceeds the limit + if (list.Count > MaxSessionMessagesPerConversation) + list.RemoveAt(0); + } + + #endregion + + #region Room Invites + + public async Task ReceiveRoomInviteAsync(int senderId, string message) + { + try + { + var presence = grainFactory.GetPlayerPresenceGrain(_playerId); + await presence.SendComposerAsync( + new RoomInviteMessageComposer { SenderId = senderId, Message = message } + ); + } + catch (Exception ex) + { + logger.LogDebug( + ex, + "Failed to deliver room invite from {SenderId} to player {PlayerId}", + senderId, + _playerId + ); + } + } + + #endregion + + #region Follow + + public async Task<( + bool Success, + int RoomId, + FollowFriendErrorCodeType? Error + )> FollowFriendAsync(PlayerId targetId, CancellationToken ct) + { + if (!_friends.ContainsKey((int)targetId)) + return (false, 0, FollowFriendErrorCodeType.NotFriend); + + // Check if target is online + var isOnline = await IsPlayerOnlineAsync(targetId); + if (!isOnline) + return (false, 0, FollowFriendErrorCodeType.Offline); + + // Check if target is in a room + var targetPresence = grainFactory.GetPlayerPresenceGrain(targetId); + var activeRoom = await targetPresence.GetActiveRoomAsync(); + + if (activeRoom.RoomId <= 0) + return (false, 0, FollowFriendErrorCodeType.HotelView); + + return (true, (int)activeRoom.RoomId.Value, null); + } + + #endregion + + #region Online/Offline Presence + + public async Task NotifyOnlineAsync(CancellationToken ct) + { + _isOnline = true; + + // Clear any stale session state from a previous login + _sessionMessages.Clear(); + _nextSessionMessageId = 1; + + var myPlayerGrain = grainFactory.GetPlayerGrain(_playerId); + var mySummary = await myPlayerGrain.GetSummaryAsync(ct); + + var mySnapshot = new MessengerFriendSnapshot + { + PlayerId = _playerId, + Name = mySummary.Name, + Gender = mySummary.Gender, + Online = true, + FollowingAllowed = true, + Figure = mySummary.Figure, + CategoryId = 0, + Motto = mySummary.Motto, + LastAccess = mySummary.CreatedAt.ToString("dd-MM-yyyy HH:mm:ss"), + RealName = string.Empty, + FacebookId = string.Empty, + PersistedMessageUser = true, + VipMember = false, + PocketHabboUser = false, + RelationshipStatus = 0, + }; + + var update = new FriendListUpdateSnapshot + { + ActionType = FriendListUpdateActionType.Updated, + FriendId = _playerId, + Friend = mySnapshot, + }; + + foreach (var friendId in _friends.Keys) + { + var friendGrain = grainFactory.GetMessengerGrain(friendId); + LogAndForget( + friendGrain.ReceiveFriendUpdateAsync(update), + nameof(ReceiveFriendUpdateAsync), + friendId + ); + } + } + + public async Task NotifyOfflineAsync(CancellationToken ct) + { + _isOnline = false; + + // Clear session-based message history + _sessionMessages.Clear(); + _nextSessionMessageId = 1; + + var myPlayerGrain = grainFactory.GetPlayerGrain(_playerId); + var mySummary = await myPlayerGrain.GetSummaryAsync(ct); + + var mySnapshot = new MessengerFriendSnapshot + { + PlayerId = _playerId, + Name = mySummary.Name, + Gender = mySummary.Gender, + Online = false, + FollowingAllowed = true, + Figure = mySummary.Figure, + CategoryId = 0, + Motto = mySummary.Motto, + LastAccess = mySummary.CreatedAt.ToString("dd-MM-yyyy HH:mm:ss"), + RealName = string.Empty, + FacebookId = string.Empty, + PersistedMessageUser = true, + VipMember = false, + PocketHabboUser = false, + RelationshipStatus = 0, + }; + + var update = new FriendListUpdateSnapshot + { + ActionType = FriendListUpdateActionType.Updated, + FriendId = _playerId, + Friend = mySnapshot, + }; + + foreach (var friendId in _friends.Keys) + { + var friendGrain = grainFactory.GetMessengerGrain(friendId); + LogAndForget( + friendGrain.ReceiveFriendUpdateAsync(update), + nameof(ReceiveFriendUpdateAsync), + friendId + ); + } + } + + #endregion + + #region Friend Updates + + public async Task ReceiveFriendUpdateAsync(FriendListUpdateSnapshot update) + { + // Update local cache if this is an update for an existing friend + if ( + update is { ActionType: FriendListUpdateActionType.Updated, Friend: not null } + && _friends.ContainsKey(update.FriendId) + ) + { + // Preserve our local category/relationship settings + var existing = _friends[update.FriendId]; + _friends[update.FriendId] = update.Friend with + { + CategoryId = existing.CategoryId, + RelationshipStatus = existing.RelationshipStatus, + }; + } + + // Forward to the player's client + try + { + var presence = grainFactory.GetPlayerPresenceGrain(_playerId); + await presence.SendComposerAsync( + new FriendListUpdateMessageComposer { FriendCategories = [], Updates = [update] } + ); + } + catch (Exception ex) + { + logger.LogDebug(ex, "Failed to forward friend update to player {PlayerId}", _playerId); + } + } + + public async Task OnFriendRemovedAsync(PlayerId friendId) + { + _friends.Remove((int)friendId); + + var removeUpdate = new FriendListUpdateSnapshot + { + ActionType = FriendListUpdateActionType.Removed, + FriendId = friendId, + }; + + try + { + var presence = grainFactory.GetPlayerPresenceGrain(_playerId); + await presence.SendComposerAsync( + new FriendListUpdateMessageComposer + { + FriendCategories = [], + Updates = [removeUpdate], + } + ); + } + catch (Exception ex) + { + logger.LogDebug( + ex, + "Failed to send friend removal notification to player {PlayerId}", + _playerId + ); + } + } + + public async Task OnFriendAcceptedAsync(MessengerFriendSnapshot friendSnapshot) + { + _friends[friendSnapshot.PlayerId] = friendSnapshot; + + var addedUpdate = new FriendListUpdateSnapshot + { + ActionType = FriendListUpdateActionType.Added, + FriendId = friendSnapshot.PlayerId, + Friend = friendSnapshot, + }; + + try + { + var presence = grainFactory.GetPlayerPresenceGrain(_playerId); + await presence.SendComposerAsync( + new FriendListUpdateMessageComposer + { + FriendCategories = [], + Updates = [addedUpdate], + } + ); + } + catch (Exception ex) + { + logger.LogDebug( + ex, + "Failed to send friend accepted notification to player {PlayerId}", + _playerId + ); + } + } + + #endregion + + #region Habbo Search + + public async Task<( + List Friends, + List Others + )> SearchPlayersAsync(string query, int searchLimit, CancellationToken ct) + { + var friends = new List(); + var others = new List(); + + if (string.IsNullOrWhiteSpace(query)) + return (friends, others); + + await using var dbCtx = await dbCtxFactory.CreateDbContextAsync(ct); + + var results = await dbCtx + .Players.AsNoTracking() + .Where(p => EF.Functions.Like(p.Name, $"%{query}%")) + .Take(searchLimit) + .Select(p => new + { + p.Id, + p.Name, + p.Motto, + p.Figure, + p.Gender, + }) + .ToListAsync(ct); + + var filtered = results.Where(player => player.Id != (int)_playerId).ToList(); + + // Batch-check online status in parallel instead of sequential N+1 + var onlineTasks = filtered + .Select(async p => + { + var isOnline = await IsPlayerOnlineAsync(PlayerId.Parse(p.Id)); + return (p, isOnline); + }) + .ToList(); + var onlineResults = await Task.WhenAll(onlineTasks); + + foreach (var (player, isOnline) in onlineResults) + { + var snapshot = new MessengerSearchResultSnapshot + { + PlayerId = PlayerId.Parse(player.Id), + Name = player.Name, + Motto = player.Motto ?? string.Empty, + Online = isOnline, + FollowingAllowed = false, + UnknownString = string.Empty, + Gender = player.Gender, + Figure = player.Figure, + RealName = string.Empty, + }; + + if (_friends.ContainsKey(player.Id)) + { + friends.Add(snapshot with { FollowingAllowed = true }); + } + else + { + others.Add(snapshot); + } + } + + return (friends, others); + } + + #endregion + + #region Persistence + + private async Task FlushDeliveredMessagesAsync(CancellationToken ct) + { + if (_pendingDeliveredIds.Count == 0) + return; + + var batch = _pendingDeliveredIds.ToList(); + _pendingDeliveredIds.Clear(); + + try + { + await using var dbCtx = await dbCtxFactory.CreateDbContextAsync(ct); + await dbCtx + .MessengerMessages.Where(m => batch.Contains(m.Id)) + .ExecuteUpdateAsync(s => s.SetProperty(m => m.Delivered, true), ct); + } + catch (Exception ex) + { + logger.LogWarning( + ex, + "Failed to flush {Count} delivered message flags for player {PlayerId}", + batch.Count, + _playerId + ); + } + } + + #endregion + + #region Helpers + + private async Task IsPlayerOnlineAsync(PlayerId playerId) + { + try + { + var presence = grainFactory.GetPlayerPresenceGrain(playerId); + return await presence.HasActiveSessionAsync(); + } + catch (Exception ex) + { + logger.LogDebug(ex, "Failed to check online status for player {PlayerId}", playerId); + return false; + } + } + + private void LogAndForget(Task task, string operation, int targetId) + { + _ = task.ContinueWith( + t => + logger.LogDebug( + t.Exception?.GetBaseException(), + "Fire-and-forget {Operation} failed for target {TargetId} from player {PlayerId}", + operation, + targetId, + _playerId + ), + CancellationToken.None, + TaskContinuationOptions.OnlyOnFaulted, + TaskScheduler.Default + ); + } + + #endregion +} diff --git a/Turbo.Players/Grains/PlayerPresenceGrain.cs b/Turbo.Players/Grains/PlayerPresenceGrain.cs index c7c45c06..9e384b94 100644 --- a/Turbo.Players/Grains/PlayerPresenceGrain.cs +++ b/Turbo.Players/Grains/PlayerPresenceGrain.cs @@ -6,7 +6,9 @@ using Orleans.Streams; using Turbo.Players.Grains.Modules; using Turbo.Primitives.Networking; +using Turbo.Primitives.Orleans; using Turbo.Primitives.Orleans.Observers; +using Turbo.Primitives.Players; using Turbo.Primitives.Players.Grains; using Turbo.Primitives.Rooms.Snapshots; @@ -61,9 +63,16 @@ public async Task UnregisterSessionObserverAsync(CancellationToken ct) { await ClearActiveRoomAsync(ct); + // Notify friends that we went offline + var playerId = (PlayerId)this.GetPrimaryKeyLong(); + var messengerGrain = _grainFactory.GetMessengerGrain(playerId); + messengerGrain.NotifyOfflineAsync(ct).Ignore(); + _sessionObserver = null; } + public Task HasActiveSessionAsync() => Task.FromResult(_sessionObserver is not null); + public Task SendComposerAsync(IComposer composer) { if (composer is not null) diff --git a/Turbo.Primitives/FriendList/Enums/FriendListErrorCodeType.cs b/Turbo.Primitives/FriendList/Enums/FriendListErrorCodeType.cs index 9bdf0ebe..12253233 100644 --- a/Turbo.Primitives/FriendList/Enums/FriendListErrorCodeType.cs +++ b/Turbo.Primitives/FriendList/Enums/FriendListErrorCodeType.cs @@ -6,4 +6,6 @@ public enum FriendListErrorCodeType TheyHitFriendLimit = 2, FriendRequestsDisabled = 3, FriendRequestNotFound = 4, + BlockedByThem = 7, + BlockedByYou = 8, } diff --git a/Turbo.Primitives/FriendList/Grains/IMessengerGrain.cs b/Turbo.Primitives/FriendList/Grains/IMessengerGrain.cs new file mode 100644 index 00000000..6808b29b --- /dev/null +++ b/Turbo.Primitives/FriendList/Grains/IMessengerGrain.cs @@ -0,0 +1,119 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Orleans; +using Turbo.Primitives.FriendList.Enums; +using Turbo.Primitives.Players; +using Turbo.Primitives.Snapshots.FriendList; + +namespace Turbo.Primitives.FriendList.Grains; + +public interface IMessengerGrain : IGrainWithIntegerKey +{ + // Initialization + Task> GetFriendsAsync(CancellationToken ct); + Task> GetCategoriesAsync(CancellationToken ct); + Task> GetFriendRequestsAsync(CancellationToken ct); + Task> GetBlockedUserIdsAsync(CancellationToken ct); + Task> GetIgnoredUserIdsAsync(CancellationToken ct); + + // Friend Requests + Task SendFriendRequestAsync( + PlayerId targetPlayerId, + string senderName, + string senderFigure, + int friendLimit, + CancellationToken ct + ); + + Task ReceiveFriendRequestAsync(FriendRequestSnapshot request); + + Task<( + List Failures, + List Updates + )> AcceptFriendRequestsAsync(List requestIds, int friendLimit, CancellationToken ct); + + Task DeclineFriendRequestsAsync(List? requestIds, bool declineAll, CancellationToken ct); + + // Friend Management + Task RemoveFriendsAsync(List friendIds, CancellationToken ct); + Task SetRelationshipStatusAsync(int friendId, int status, CancellationToken ct); + Task> GetRelationshipStatusInfoAsync( + CancellationToken ct + ); + + // Friend state queries + Task IsFriendAsync(PlayerId playerId); + Task IsFriendRequestSentAsync(PlayerId playerId); + Task GetFriendCountAsync(); + + // Blocking + Task BlockUserAsync(PlayerId targetId, CancellationToken ct); + Task UnblockUserAsync(PlayerId targetId, CancellationToken ct); + Task IsBlockedAsync(PlayerId targetId); + Task IsBlockedByAsync(PlayerId targetId); + + // Ignoring + Task IgnoreUserAsync(PlayerId targetId, int maxIgnoreCapacity, CancellationToken ct); + Task UnignoreUserAsync(PlayerId targetId, CancellationToken ct); + + // Messaging + Task SendMessageAsync( + PlayerId recipientId, + string message, + int confirmationId, + string senderName, + string senderFigure, + CancellationToken ct + ); + + Task ReceiveMessageAsync( + int chatId, + string messageText, + int secondsSinceSent, + string messageId, + int confirmationId, + int senderId, + string senderName, + string senderFigure, + int dbMessageId = 0 + ); + + Task> GetMessageHistoryAsync( + int chatPartnerId, + string lastMessageId, + int pageSize, + CancellationToken ct + ); + + // Offline message delivery + Task DeliverOfflineMessagesAsync(CancellationToken ct); + + // Room Invites + Task ReceiveRoomInviteAsync(int senderId, string message); + + // Follow + Task<(bool Success, int RoomId, FollowFriendErrorCodeType? Error)> FollowFriendAsync( + PlayerId targetId, + CancellationToken ct + ); + + // Online/Offline presence + Task NotifyOnlineAsync(CancellationToken ct); + Task NotifyOfflineAsync(CancellationToken ct); + + // Friend list updates push + Task ReceiveFriendUpdateAsync(FriendListUpdateSnapshot update); + + // Habbo search + Task<( + List Friends, + List Others + )> SearchPlayersAsync(string query, int searchLimit, CancellationToken ct); + + // Remove friendship (called by the other side) + Task OnFriendRemovedAsync(PlayerId friendId); + + // Accept friendship (called by the other side) + Task OnFriendAcceptedAsync(MessengerFriendSnapshot friendSnapshot); +} diff --git a/Turbo.Primitives/Messages/Incoming/FriendList/SetRelationshipStatusMessage.cs b/Turbo.Primitives/Messages/Incoming/FriendList/SetRelationshipStatusMessage.cs index 03041caf..3fa052ca 100644 --- a/Turbo.Primitives/Messages/Incoming/FriendList/SetRelationshipStatusMessage.cs +++ b/Turbo.Primitives/Messages/Incoming/FriendList/SetRelationshipStatusMessage.cs @@ -2,4 +2,8 @@ namespace Turbo.Primitives.Messages.Incoming.FriendList; -public record SetRelationshipStatusMessage : IMessageEvent { } +public record SetRelationshipStatusMessage : IMessageEvent +{ + public required int FriendUserId { get; init; } + public required int RelationType { get; init; } +} diff --git a/Turbo.Primitives/Messages/Incoming/Users/BlockUserMessage.cs b/Turbo.Primitives/Messages/Incoming/Users/BlockUserMessage.cs new file mode 100644 index 00000000..e9b9b57e --- /dev/null +++ b/Turbo.Primitives/Messages/Incoming/Users/BlockUserMessage.cs @@ -0,0 +1,9 @@ +using Turbo.Primitives.Networking; +using Turbo.Primitives.Players; + +namespace Turbo.Primitives.Messages.Incoming.Users; + +public record BlockUserMessage : IMessageEvent +{ + public required PlayerId PlayerId { get; init; } +} diff --git a/Turbo.Primitives/Messages/Incoming/Users/GetRelationshipStatusInfoMessage.cs b/Turbo.Primitives/Messages/Incoming/Users/GetRelationshipStatusInfoMessage.cs index eca9756a..7ec66c20 100644 --- a/Turbo.Primitives/Messages/Incoming/Users/GetRelationshipStatusInfoMessage.cs +++ b/Turbo.Primitives/Messages/Incoming/Users/GetRelationshipStatusInfoMessage.cs @@ -1,5 +1,9 @@ using Turbo.Primitives.Networking; +using Turbo.Primitives.Players; namespace Turbo.Primitives.Messages.Incoming.Users; -public record GetRelationshipStatusInfoMessage : IMessageEvent { } +public record GetRelationshipStatusInfoMessage : IMessageEvent +{ + public required PlayerId PlayerId { get; init; } +} diff --git a/Turbo.Primitives/Messages/Incoming/Users/IgnoreUserMessage.cs b/Turbo.Primitives/Messages/Incoming/Users/IgnoreUserMessage.cs index a93bd533..e90d30a9 100644 --- a/Turbo.Primitives/Messages/Incoming/Users/IgnoreUserMessage.cs +++ b/Turbo.Primitives/Messages/Incoming/Users/IgnoreUserMessage.cs @@ -2,4 +2,7 @@ namespace Turbo.Primitives.Messages.Incoming.Users; -public record IgnoreUserMessage : IMessageEvent { } +public record IgnoreUserMessage : IMessageEvent +{ + public required int PlayerId { get; init; } +} diff --git a/Turbo.Primitives/Messages/Incoming/Users/UnblockUserMessage.cs b/Turbo.Primitives/Messages/Incoming/Users/UnblockUserMessage.cs new file mode 100644 index 00000000..37d5d3d3 --- /dev/null +++ b/Turbo.Primitives/Messages/Incoming/Users/UnblockUserMessage.cs @@ -0,0 +1,9 @@ +using Turbo.Primitives.Networking; +using Turbo.Primitives.Players; + +namespace Turbo.Primitives.Messages.Incoming.Users; + +public record UnblockUserMessage : IMessageEvent +{ + public required PlayerId PlayerId { get; init; } +} diff --git a/Turbo.Primitives/Messages/Incoming/Users/UnignoreUserMessage.cs b/Turbo.Primitives/Messages/Incoming/Users/UnignoreUserMessage.cs index e93a8117..1f04f657 100644 --- a/Turbo.Primitives/Messages/Incoming/Users/UnignoreUserMessage.cs +++ b/Turbo.Primitives/Messages/Incoming/Users/UnignoreUserMessage.cs @@ -2,4 +2,7 @@ namespace Turbo.Primitives.Messages.Incoming.Users; -public record UnignoreUserMessage : IMessageEvent { } +public record UnignoreUserMessage : IMessageEvent +{ + public required int PlayerId { get; init; } +} diff --git a/Turbo.Primitives/Messages/Outgoing/FriendList/FindFriendsProcessResultMessageComposer.cs b/Turbo.Primitives/Messages/Outgoing/FriendList/FindFriendsProcessResultMessageComposer.cs new file mode 100644 index 00000000..e83700aa --- /dev/null +++ b/Turbo.Primitives/Messages/Outgoing/FriendList/FindFriendsProcessResultMessageComposer.cs @@ -0,0 +1,11 @@ +using Orleans; +using Turbo.Primitives.Networking; + +namespace Turbo.Primitives.Messages.Outgoing.FriendList; + +[GenerateSerializer, Immutable] +public sealed record FindFriendsProcessResultMessageComposer : IComposer +{ + [Id(0)] + public required bool Success { get; init; } +} diff --git a/Turbo.Primitives/Messages/Outgoing/Users/BlockListMessageComposer.cs b/Turbo.Primitives/Messages/Outgoing/Users/BlockListMessageComposer.cs new file mode 100644 index 00000000..73cb1257 --- /dev/null +++ b/Turbo.Primitives/Messages/Outgoing/Users/BlockListMessageComposer.cs @@ -0,0 +1,12 @@ +using System.Collections.Generic; +using Orleans; +using Turbo.Primitives.Networking; + +namespace Turbo.Primitives.Messages.Outgoing.Users; + +[GenerateSerializer, Immutable] +public sealed record BlockListMessageComposer : IComposer +{ + [Id(0)] + public required List BlockedUserIds { get; init; } +} diff --git a/Turbo.Primitives/Messages/Outgoing/Users/BlockUserUpdateMessageComposer.cs b/Turbo.Primitives/Messages/Outgoing/Users/BlockUserUpdateMessageComposer.cs new file mode 100644 index 00000000..ecc8196c --- /dev/null +++ b/Turbo.Primitives/Messages/Outgoing/Users/BlockUserUpdateMessageComposer.cs @@ -0,0 +1,14 @@ +using Orleans; +using Turbo.Primitives.Networking; + +namespace Turbo.Primitives.Messages.Outgoing.Users; + +[GenerateSerializer, Immutable] +public sealed record BlockUserUpdateMessageComposer : IComposer +{ + [Id(0)] + public required int Result { get; init; } + + [Id(1)] + public required int UserId { get; init; } +} diff --git a/Turbo.Primitives/Messages/Outgoing/Users/IgnoreResultMessageComposer.cs b/Turbo.Primitives/Messages/Outgoing/Users/IgnoreResultMessageComposer.cs index 2545b1f1..f766e7ac 100644 --- a/Turbo.Primitives/Messages/Outgoing/Users/IgnoreResultMessageComposer.cs +++ b/Turbo.Primitives/Messages/Outgoing/Users/IgnoreResultMessageComposer.cs @@ -6,5 +6,9 @@ namespace Turbo.Primitives.Messages.Outgoing.Users; [GenerateSerializer, Immutable] public sealed record IgnoreResultMessageComposer : IComposer { - // TODO: add properties if/when identified + [Id(0)] + public required int Result { get; init; } + + [Id(1)] + public required string PlayerName { get; init; } } diff --git a/Turbo.Primitives/Messages/Outgoing/Users/RelationshipStatusInfoEventMessageComposer.cs b/Turbo.Primitives/Messages/Outgoing/Users/RelationshipStatusInfoEventMessageComposer.cs index da9daa5c..aee53432 100644 --- a/Turbo.Primitives/Messages/Outgoing/Users/RelationshipStatusInfoEventMessageComposer.cs +++ b/Turbo.Primitives/Messages/Outgoing/Users/RelationshipStatusInfoEventMessageComposer.cs @@ -1,10 +1,17 @@ +using System.Collections.Generic; using Orleans; using Turbo.Primitives.Networking; +using Turbo.Primitives.Players; +using Turbo.Primitives.Snapshots.FriendList; namespace Turbo.Primitives.Messages.Outgoing.Users; [GenerateSerializer, Immutable] public sealed record RelationshipStatusInfoEventMessageComposer : IComposer { - // TODO: add properties if/when identified + [Id(0)] + public required PlayerId UserId { get; init; } + + [Id(1)] + public required List Entries { get; init; } } diff --git a/Turbo.Primitives/Orleans/GrainFactoryExtensions.cs b/Turbo.Primitives/Orleans/GrainFactoryExtensions.cs index 11ecc5d8..8f258436 100644 --- a/Turbo.Primitives/Orleans/GrainFactoryExtensions.cs +++ b/Turbo.Primitives/Orleans/GrainFactoryExtensions.cs @@ -1,5 +1,6 @@ using Orleans; using Turbo.Primitives.Catalog.Grains; +using Turbo.Primitives.FriendList.Grains; using Turbo.Primitives.Grains.Players; using Turbo.Primitives.Inventory.Grains; using Turbo.Primitives.Players; @@ -65,4 +66,12 @@ public static ICatalogPurchaseGrain GetCatalogPurchaseGrain( this IGrainFactory factory, long playerId ) => factory.GetGrain(playerId); + + public static IMessengerGrain GetMessengerGrain( + this IGrainFactory factory, + PlayerId playerId + ) => factory.GetGrain(playerId.Value); + + public static IMessengerGrain GetMessengerGrain(this IGrainFactory factory, long playerId) => + factory.GetGrain(playerId); } diff --git a/Turbo.Primitives/Players/Grains/IPlayerPresenceGrain.cs b/Turbo.Primitives/Players/Grains/IPlayerPresenceGrain.cs index 5303139a..3e9ce09b 100644 --- a/Turbo.Primitives/Players/Grains/IPlayerPresenceGrain.cs +++ b/Turbo.Primitives/Players/Grains/IPlayerPresenceGrain.cs @@ -12,4 +12,5 @@ public partial interface IPlayerPresenceGrain : IGrainWithIntegerKey public Task UnregisterSessionObserverAsync(CancellationToken ct); public Task SendComposerAsync(IComposer composer); public Task SendComposerAsync(params IComposer[] composers); + public Task HasActiveSessionAsync(); } diff --git a/Turbo.Primitives/Rooms/Grains/IRoomDirectoryGrain.cs b/Turbo.Primitives/Rooms/Grains/IRoomDirectoryGrain.cs index 4aaca561..2ebd7de2 100644 --- a/Turbo.Primitives/Rooms/Grains/IRoomDirectoryGrain.cs +++ b/Turbo.Primitives/Rooms/Grains/IRoomDirectoryGrain.cs @@ -15,4 +15,5 @@ public interface IRoomDirectoryGrain : IGrainWithStringKey public Task RemoveActiveRoomAsync(RoomId roomId); public Task AddPlayerToRoomAsync(PlayerId playerId, RoomId roomId, CancellationToken ct); public Task RemovePlayerFromRoomAsync(PlayerId playerId, RoomId roomId, CancellationToken ct); + public Task GetRandomPopulatedRoomAsync(CancellationToken ct); } diff --git a/Turbo.Primitives/Snapshots/FriendList/MessengerFriendSnapshot.cs b/Turbo.Primitives/Snapshots/FriendList/MessengerFriendSnapshot.cs index f4874371..f6966c72 100644 --- a/Turbo.Primitives/Snapshots/FriendList/MessengerFriendSnapshot.cs +++ b/Turbo.Primitives/Snapshots/FriendList/MessengerFriendSnapshot.cs @@ -31,6 +31,9 @@ public record MessengerFriendSnapshot [Id(7)] public required string Motto { get; init; } + [Id(14)] + public required string LastAccess { get; init; } + [Id(8)] public required string RealName { get; init; } diff --git a/Turbo.Primitives/Snapshots/FriendList/RelationshipStatusEntrySnapshot.cs b/Turbo.Primitives/Snapshots/FriendList/RelationshipStatusEntrySnapshot.cs new file mode 100644 index 00000000..770683a1 --- /dev/null +++ b/Turbo.Primitives/Snapshots/FriendList/RelationshipStatusEntrySnapshot.cs @@ -0,0 +1,23 @@ +using Orleans; +using Turbo.Primitives.Players; + +namespace Turbo.Primitives.Snapshots.FriendList; + +[GenerateSerializer, Immutable] +public record RelationshipStatusEntrySnapshot +{ + [Id(0)] + public required int RelationshipStatusType { get; init; } + + [Id(1)] + public required int FriendCount { get; init; } + + [Id(2)] + public required PlayerId RandomFriendId { get; init; } + + [Id(3)] + public required string RandomFriendName { get; init; } + + [Id(4)] + public required string RandomFriendFigure { get; init; } +} diff --git a/Turbo.Revisions/Revision20260112/Parsers/FriendList/SetRelationshipStatusMessageParser.cs b/Turbo.Revisions/Revision20260112/Parsers/FriendList/SetRelationshipStatusMessageParser.cs index 608f67da..68f7b557 100644 --- a/Turbo.Revisions/Revision20260112/Parsers/FriendList/SetRelationshipStatusMessageParser.cs +++ b/Turbo.Revisions/Revision20260112/Parsers/FriendList/SetRelationshipStatusMessageParser.cs @@ -6,5 +6,10 @@ namespace Turbo.Revisions.Revision20260112.Parsers.FriendList; internal class SetRelationshipStatusMessageParser : IParser { - public IMessageEvent Parse(IClientPacket packet) => new SetRelationshipStatusMessage(); + public IMessageEvent Parse(IClientPacket packet) => + new SetRelationshipStatusMessage + { + FriendUserId = packet.PopInt(), + RelationType = packet.PopInt(), + }; } diff --git a/Turbo.Revisions/Revision20260112/Parsers/Users/BlockUserMessageParser.cs b/Turbo.Revisions/Revision20260112/Parsers/Users/BlockUserMessageParser.cs new file mode 100644 index 00000000..8195d8e3 --- /dev/null +++ b/Turbo.Revisions/Revision20260112/Parsers/Users/BlockUserMessageParser.cs @@ -0,0 +1,11 @@ +using Turbo.Primitives.Messages.Incoming.Users; +using Turbo.Primitives.Networking; +using Turbo.Primitives.Packets; + +namespace Turbo.Revisions.Revision20260112.Parsers.Users; + +internal class BlockUserMessageParser : IParser +{ + public IMessageEvent Parse(IClientPacket packet) => + new BlockUserMessage { PlayerId = packet.PopInt() }; +} diff --git a/Turbo.Revisions/Revision20260112/Parsers/Users/GetRelationshipStatusInfoMessageParser.cs b/Turbo.Revisions/Revision20260112/Parsers/Users/GetRelationshipStatusInfoMessageParser.cs index 23ca25e5..792210a2 100644 --- a/Turbo.Revisions/Revision20260112/Parsers/Users/GetRelationshipStatusInfoMessageParser.cs +++ b/Turbo.Revisions/Revision20260112/Parsers/Users/GetRelationshipStatusInfoMessageParser.cs @@ -6,5 +6,6 @@ namespace Turbo.Revisions.Revision20260112.Parsers.Users; internal class GetRelationshipStatusInfoMessageParser : IParser { - public IMessageEvent Parse(IClientPacket packet) => new GetRelationshipStatusInfoMessage(); + public IMessageEvent Parse(IClientPacket packet) => + new GetRelationshipStatusInfoMessage { PlayerId = packet.PopInt() }; } diff --git a/Turbo.Revisions/Revision20260112/Parsers/Users/IgnoreUserMessageParser.cs b/Turbo.Revisions/Revision20260112/Parsers/Users/IgnoreUserMessageParser.cs index 4f7e44e0..184afcfd 100644 --- a/Turbo.Revisions/Revision20260112/Parsers/Users/IgnoreUserMessageParser.cs +++ b/Turbo.Revisions/Revision20260112/Parsers/Users/IgnoreUserMessageParser.cs @@ -6,5 +6,6 @@ namespace Turbo.Revisions.Revision20260112.Parsers.Users; internal class IgnoreUserMessageParser : IParser { - public IMessageEvent Parse(IClientPacket packet) => new IgnoreUserMessage(); + public IMessageEvent Parse(IClientPacket packet) => + new IgnoreUserMessage { PlayerId = packet.PopInt() }; } diff --git a/Turbo.Revisions/Revision20260112/Parsers/Users/UnblockUserMessageParser.cs b/Turbo.Revisions/Revision20260112/Parsers/Users/UnblockUserMessageParser.cs new file mode 100644 index 00000000..e991c3eb --- /dev/null +++ b/Turbo.Revisions/Revision20260112/Parsers/Users/UnblockUserMessageParser.cs @@ -0,0 +1,11 @@ +using Turbo.Primitives.Messages.Incoming.Users; +using Turbo.Primitives.Networking; +using Turbo.Primitives.Packets; + +namespace Turbo.Revisions.Revision20260112.Parsers.Users; + +internal class UnblockUserMessageParser : IParser +{ + public IMessageEvent Parse(IClientPacket packet) => + new UnblockUserMessage { PlayerId = packet.PopInt() }; +} diff --git a/Turbo.Revisions/Revision20260112/Parsers/Users/UnignoreUserMessageParser.cs b/Turbo.Revisions/Revision20260112/Parsers/Users/UnignoreUserMessageParser.cs index d328292d..37a8d645 100644 --- a/Turbo.Revisions/Revision20260112/Parsers/Users/UnignoreUserMessageParser.cs +++ b/Turbo.Revisions/Revision20260112/Parsers/Users/UnignoreUserMessageParser.cs @@ -6,5 +6,6 @@ namespace Turbo.Revisions.Revision20260112.Parsers.Users; internal class UnignoreUserMessageParser : IParser { - public IMessageEvent Parse(IClientPacket packet) => new UnignoreUserMessage(); + public IMessageEvent Parse(IClientPacket packet) => + new UnignoreUserMessage { PlayerId = packet.PopInt() }; } diff --git a/Turbo.Revisions/Revision20260112/Revision20260112.cs b/Turbo.Revisions/Revision20260112/Revision20260112.cs index 1f86dd1a..c465e9a5 100644 --- a/Turbo.Revisions/Revision20260112/Revision20260112.cs +++ b/Turbo.Revisions/Revision20260112/Revision20260112.cs @@ -373,6 +373,10 @@ public class Revision20260112 : IRevision { MessageEvent.RequestFriendMessageEvent, new RequestFriendMessageParser() }, { MessageEvent.SendMsgMessageEvent, new SendMsgMessageParser() }, { MessageEvent.SendRoomInviteMessageEvent, new SendRoomInviteMessageParser() }, + { + MessageEvent.SetRelationshipStatusMessageEvent, + new SetRelationshipStatusMessageParser() + }, { MessageEvent.VisitUserMessageEvent, new VisitUserMessageParser() }, #endregion @@ -1339,6 +1343,7 @@ public class Revision20260112 : IRevision new GetHabboGroupDetailsMessageParser() }, { MessageEvent.BlockListInitEvent, new BlockListInitMessageParser() }, + { MessageEvent.BlockUserMessageEvent, new BlockUserMessageParser() }, { MessageEvent.GetIgnoredUsersMessageEvent, new GetIgnoredUsersMessageParser() }, { MessageEvent.GetMemberGuildItemCountMessageEvent, @@ -1372,6 +1377,7 @@ public class Revision20260112 : IRevision new SelectFavouriteHabboGroupMessageParser() }, { MessageEvent.UnblockGroupMemberMessageEvent, new UnblockGroupMemberMessageParser() }, + { MessageEvent.UnblockUserMessageEvent, new UnblockUserMessageParser() }, { MessageEvent.UnignoreUserMessageEvent, new UnignoreUserMessageParser() }, { MessageEvent.UpdateGuildBadgeMessageEvent, new UpdateGuildBadgeMessageParser() }, { MessageEvent.UpdateGuildColorsMessageEvent, new UpdateGuildColorsMessageParser() }, @@ -1874,6 +1880,12 @@ public class Revision20260112 : IRevision typeof(RoomInviteMessageComposer), new RoomInviteMessageSerializer(MessageComposer.RoomInviteComposer) }, + { + typeof(FindFriendsProcessResultMessageComposer), + new FindFriendsProcessResultMessageSerializer( + MessageComposer.FindFriendsProcessResultComposer + ) + }, #endregion #region Groupforums @@ -3169,6 +3181,28 @@ public class Revision20260112 : IRevision MessageComposer.IgnoredUsersMessageComposer ) }, + { + typeof(IgnoreResultMessageComposer), + new IgnoreResultMessageComposerSerializer( + MessageComposer.IgnoreResultMessageComposer + ) + }, + { + typeof(BlockListMessageComposer), + new BlockListMessageComposerSerializer(MessageComposer.BlockListMessageComposer) + }, + { + typeof(BlockUserUpdateMessageComposer), + new BlockUserUpdateMessageComposerSerializer( + MessageComposer.BlockUserUpdateMessageComposer + ) + }, + { + typeof(RelationshipStatusInfoEventMessageComposer), + new RelationshipStatusInfoEventMessageComposerSerializer( + MessageComposer.RelationshipStatusInfoComposer + ) + }, { typeof(ScrSendUserInfoMessageComposer), new ScrSendUserInfoMessageSerializer(MessageComposer.ScrSendUserInfoComposer) diff --git a/Turbo.Revisions/Revision20260112/Serializers/FriendList/FindFriendsProcessResultMessageSerializer.cs b/Turbo.Revisions/Revision20260112/Serializers/FriendList/FindFriendsProcessResultMessageSerializer.cs new file mode 100644 index 00000000..f63f1111 --- /dev/null +++ b/Turbo.Revisions/Revision20260112/Serializers/FriendList/FindFriendsProcessResultMessageSerializer.cs @@ -0,0 +1,16 @@ +using Turbo.Primitives.Messages.Outgoing.FriendList; +using Turbo.Primitives.Packets; + +namespace Turbo.Revisions.Revision20260112.Serializers.FriendList; + +internal class FindFriendsProcessResultMessageSerializer(int header) + : AbstractSerializer(header) +{ + protected override void Serialize( + IServerPacket packet, + FindFriendsProcessResultMessageComposer message + ) + { + packet.WriteBoolean(message.Success); + } +} diff --git a/Turbo.Revisions/Revision20260112/Serializers/FriendList/Snapshots/MessengerFriendSnapshotSerializer.cs b/Turbo.Revisions/Revision20260112/Serializers/FriendList/Snapshots/MessengerFriendSnapshotSerializer.cs index 6d40e8e9..f9d4463d 100644 --- a/Turbo.Revisions/Revision20260112/Serializers/FriendList/Snapshots/MessengerFriendSnapshotSerializer.cs +++ b/Turbo.Revisions/Revision20260112/Serializers/FriendList/Snapshots/MessengerFriendSnapshotSerializer.cs @@ -15,6 +15,8 @@ public static void Serialize(IServerPacket packet, MessengerFriendSnapshot messa packet.WriteString(message.Figure); packet.WriteInteger(message.CategoryId); packet.WriteString(message.Motto); + packet.WriteString(message.LastAccess); + packet.WriteString(message.RealName); packet.WriteString(message.FacebookId); packet.WriteBoolean(message.PersistedMessageUser); packet.WriteBoolean(message.VipMember); diff --git a/Turbo.Revisions/Revision20260112/Serializers/Users/BlockListMessageComposerSerializer.cs b/Turbo.Revisions/Revision20260112/Serializers/Users/BlockListMessageComposerSerializer.cs new file mode 100644 index 00000000..96902819 --- /dev/null +++ b/Turbo.Revisions/Revision20260112/Serializers/Users/BlockListMessageComposerSerializer.cs @@ -0,0 +1,17 @@ +using Turbo.Primitives.Messages.Outgoing.Users; +using Turbo.Primitives.Packets; + +namespace Turbo.Revisions.Revision20260112.Serializers.Users; + +internal class BlockListMessageComposerSerializer(int header) + : AbstractSerializer(header) +{ + protected override void Serialize(IServerPacket packet, BlockListMessageComposer message) + { + packet.WriteInteger(message.BlockedUserIds.Count); + foreach (var userId in message.BlockedUserIds) + { + packet.WriteInteger(userId); + } + } +} diff --git a/Turbo.Revisions/Revision20260112/Serializers/Users/BlockUserUpdateMessageComposerSerializer.cs b/Turbo.Revisions/Revision20260112/Serializers/Users/BlockUserUpdateMessageComposerSerializer.cs new file mode 100644 index 00000000..ef47c70a --- /dev/null +++ b/Turbo.Revisions/Revision20260112/Serializers/Users/BlockUserUpdateMessageComposerSerializer.cs @@ -0,0 +1,14 @@ +using Turbo.Primitives.Messages.Outgoing.Users; +using Turbo.Primitives.Packets; + +namespace Turbo.Revisions.Revision20260112.Serializers.Users; + +internal class BlockUserUpdateMessageComposerSerializer(int header) + : AbstractSerializer(header) +{ + protected override void Serialize(IServerPacket packet, BlockUserUpdateMessageComposer message) + { + packet.WriteInteger(message.Result); + packet.WriteInteger(message.UserId); + } +} diff --git a/Turbo.Revisions/Revision20260112/Serializers/Users/IgnoreResultMessageComposerSerializer.cs b/Turbo.Revisions/Revision20260112/Serializers/Users/IgnoreResultMessageComposerSerializer.cs index 270b4acd..7286c1d4 100644 --- a/Turbo.Revisions/Revision20260112/Serializers/Users/IgnoreResultMessageComposerSerializer.cs +++ b/Turbo.Revisions/Revision20260112/Serializers/Users/IgnoreResultMessageComposerSerializer.cs @@ -8,6 +8,7 @@ internal class IgnoreResultMessageComposerSerializer(int header) { protected override void Serialize(IServerPacket packet, IgnoreResultMessageComposer message) { - // + packet.WriteInteger(message.Result); + packet.WriteString(message.PlayerName); } } diff --git a/Turbo.Revisions/Revision20260112/Serializers/Users/RelationshipStatusInfoEventMessageComposerSerializer.cs b/Turbo.Revisions/Revision20260112/Serializers/Users/RelationshipStatusInfoEventMessageComposerSerializer.cs index 482d7205..ff3a70db 100644 --- a/Turbo.Revisions/Revision20260112/Serializers/Users/RelationshipStatusInfoEventMessageComposerSerializer.cs +++ b/Turbo.Revisions/Revision20260112/Serializers/Users/RelationshipStatusInfoEventMessageComposerSerializer.cs @@ -11,6 +11,15 @@ protected override void Serialize( RelationshipStatusInfoEventMessageComposer message ) { - // + packet.WriteInteger(message.UserId); + packet.WriteInteger(message.Entries.Count); + foreach (var entry in message.Entries) + { + packet.WriteInteger(entry.RelationshipStatusType); + packet.WriteInteger(entry.FriendCount); + packet.WriteInteger(entry.RandomFriendId); + packet.WriteString(entry.RandomFriendName); + packet.WriteString(entry.RandomFriendFigure); + } } } diff --git a/Turbo.Rooms/Grains/RoomDirectoryGrain.cs b/Turbo.Rooms/Grains/RoomDirectoryGrain.cs index 76fa6bc0..b903fad5 100644 --- a/Turbo.Rooms/Grains/RoomDirectoryGrain.cs +++ b/Turbo.Rooms/Grains/RoomDirectoryGrain.cs @@ -122,6 +122,17 @@ public Task> GetActiveRoomsAsync() => public Task GetRoomPopulationAsync(RoomId roomId) => Task.FromResult(_roomPopulations.TryGetValue(roomId, out var pop) ? pop : 0); + public Task GetRandomPopulatedRoomAsync(CancellationToken ct) + { + var populated = _roomPopulations.Where(kv => kv.Value > 0).Select(kv => kv.Key).ToArray(); + + if (populated.Length == 0) + return Task.FromResult(null); + + var random = Random.Shared.Next(populated.Length); + return Task.FromResult(populated[random]); + } + private Task UpdatePopulationAsync(RoomId roomId) { _roomPopulations[roomId] = _roomPlayers.TryGetValue(roomId, out var players) diff --git a/appsettings.json b/appsettings.json index 7510b947..8eed5f3e 100644 --- a/appsettings.json +++ b/appsettings.json @@ -50,6 +50,11 @@ }, "Game": {}, "Networking": {}, + "FriendList": { + "UserFriendLimit": 100, + "NormalFriendLimit": 100, + "ExtendedFriendLimit": 100 + }, "Crypto": { "KeySize": "3", "PublicKey": "86851DD364D5C5CECE3C883171CC6DDC5760779B992482BD1E20DD296888DF91B33B936A7B93F06D29E8870F703A216257DEC7C81DE0058FEA4CC5116F75E6EFC4E9113513E45357DC3FD43D4EFAB5963EF178B78BD61E81A14C603B24C8BCCE0A12230B320045498EDC29282FF0603BC7B7DAE8FC1B05B52B2F301A9DC783B7",