From 40a002b6f64f6f79f46c11f0606584fa3a0d1a01 Mon Sep 17 00:00:00 2001 From: Diddyy Date: Sun, 8 Feb 2026 21:18:58 +0000 Subject: [PATCH 1/4] feat(catalog): implement production-grade LTD raffle system - Added weighted selection based on badges, account age, achievement score, friend count, room count, and furniture count - Implemented 20s buffer window with automatic drawing logic - Added support for both randomized and sequential serial numbers - Implemented Landing View countdown timer (Header 2178) - Added 'One-Per-Customer' anti-hoarding policy - Refactored to lean, high-performance C# 12 patterns (Primary Constructors, Collection Expressions) --- .config/dotnet-tools.json | 9 +- Turbo.Catalog/CatalogService.cs | 45 +- Turbo.Catalog/Configuration/CatalogConfig.cs | 152 + Turbo.Catalog/Grains/LtdRaffleGrain.cs | 514 +++ .../Providers/CatalogSnapshotProvider.cs | 60 +- Turbo.Database/Context/TurboDbContext.cs | 4 + .../Entities/Catalog/CatalogProductEntity.cs | 8 - .../Entities/Catalog/LtdRaffleEntryEntity.cs | 65 + .../Entities/Catalog/LtdSeriesEntity.cs | 67 + .../Migrations/20260205185839_AddedModels.cs | 1319 ++++---- .../20260208101510_InitialCreate.cs | 2825 +++++++++++------ ...60208201115_AddLtdRaffleSystem.Designer.cs | 2530 +++++++++++++++ .../20260208201115_AddLtdRaffleSystem.cs | 193 ++ .../Migrations/TurboDbContextModelSnapshot.cs | 190 +- .../Grains/InventoryGrain.Furni.cs | 76 + Turbo.Inventory/Grains/InventoryGrain.cs | 6 +- ...LimitedOfferAppearingNextMessageHandler.cs | 38 +- .../PurchaseFromCatalogMessageHandler.cs | 174 +- .../Catalog/Enums/CatalogPurchaseErrorType.cs | 28 +- .../Catalog/Enums/LtdRaffleEntryError.cs | 16 + .../Catalog/Enums/LtdRaffleResultCode.cs | 29 + .../Catalog/Grains/ILtdRaffleGrain.cs | 36 + Turbo.Primitives/Catalog/ICatalogService.cs | 4 + .../Snapshots/CatalogProductSnapshot.cs | 6 + .../Catalog/Snapshots/LtdRaffleEntryResult.cs | 44 + .../Catalog/Snapshots/LtdSeriesSnapshot.cs | 47 + .../Catalog/Snapshots/UpcomingLtdSnapshot.cs | 22 + .../Inventory/Grains/IInventoryGrain.Furni.cs | 15 + ...imitedOfferAppearingNextMessageComposer.cs | 12 +- .../Catalog/PurchaseErrorMessageComposer.cs | 3 +- .../LtdRaffleEnteredMessageComposer.cs | 19 + .../LtdRaffleResultMessageComposer.cs | 28 + .../Orleans/GrainFactoryExtensions.cs | 3 + Turbo.Primitives/TurboErrorCodeEnum.cs | 3 + .../Revision20260112/Revision20260112.cs | 12 + ...rAppearingNextMessageComposerSerializer.cs | 5 +- .../PurchaseErrorMessageComposerSerializer.cs | 2 +- ...dRaffleEnteredMessageComposerSerializer.cs | 13 + ...tdRaffleResultMessageComposerSerializer.cs | 14 + appsettings.json | 51 +- 40 files changed, 7029 insertions(+), 1658 deletions(-) create mode 100644 Turbo.Catalog/Grains/LtdRaffleGrain.cs create mode 100644 Turbo.Database/Entities/Catalog/LtdRaffleEntryEntity.cs create mode 100644 Turbo.Database/Entities/Catalog/LtdSeriesEntity.cs create mode 100644 Turbo.Database/Migrations/20260208201115_AddLtdRaffleSystem.Designer.cs create mode 100644 Turbo.Database/Migrations/20260208201115_AddLtdRaffleSystem.cs create mode 100644 Turbo.Primitives/Catalog/Enums/LtdRaffleEntryError.cs create mode 100644 Turbo.Primitives/Catalog/Enums/LtdRaffleResultCode.cs create mode 100644 Turbo.Primitives/Catalog/Grains/ILtdRaffleGrain.cs create mode 100644 Turbo.Primitives/Catalog/Snapshots/LtdRaffleEntryResult.cs create mode 100644 Turbo.Primitives/Catalog/Snapshots/LtdSeriesSnapshot.cs create mode 100644 Turbo.Primitives/Catalog/Snapshots/UpcomingLtdSnapshot.cs create mode 100644 Turbo.Primitives/Messages/Outgoing/Collectibles/LtdRaffleEnteredMessageComposer.cs create mode 100644 Turbo.Primitives/Messages/Outgoing/Collectibles/LtdRaffleResultMessageComposer.cs create mode 100644 Turbo.Revisions/Revision20260112/Serializers/Collectibles/LtdRaffleEnteredMessageComposerSerializer.cs create mode 100644 Turbo.Revisions/Revision20260112/Serializers/Collectibles/LtdRaffleResultMessageComposerSerializer.cs diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 8d74243d..87686faf 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -8,6 +8,13 @@ "csharpier" ], "rollForward": false + }, + "dotnet-ef": { + "version": "9.0.8", + "commands": [ + "dotnet-ef" + ], + "rollForward": false } } -} +} \ No newline at end of file diff --git a/Turbo.Catalog/CatalogService.cs b/Turbo.Catalog/CatalogService.cs index 92944b98..8bcdabdb 100644 --- a/Turbo.Catalog/CatalogService.cs +++ b/Turbo.Catalog/CatalogService.cs @@ -1,5 +1,10 @@ using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; +using Turbo.Database.Context; using Turbo.Primitives.Catalog; using Turbo.Primitives.Catalog.Enums; using Turbo.Primitives.Catalog.Providers; @@ -10,12 +15,14 @@ namespace Turbo.Catalog; public sealed class CatalogService( ILogger logger, - ICatalogSnapshotProvider normalCatalogProvider + ICatalogSnapshotProvider normalCatalogProvider, + IDbContextFactory dbCtxFactory ) : ICatalogService { private readonly ILogger _logger = logger; private readonly ICatalogSnapshotProvider _normalCatalogProvider = normalCatalogProvider; + private readonly IDbContextFactory _dbCtxFactory = dbCtxFactory; public CatalogSnapshot GetCatalogSnapshot(CatalogType catalogType) { @@ -28,4 +35,40 @@ public CatalogSnapshot GetCatalogSnapshot(CatalogType catalogType) _ => throw new NotSupportedException($"Catalog type {catalogType} is not supported."), }; } + + public async Task GetUpcomingLtdAsync(CancellationToken ct) + { + await using var dbCtx = await _dbCtxFactory.CreateDbContextAsync(ct); + + // Find the nearest upcoming active LTD drop + var now = DateTime.UtcNow; + var nextSeries = await dbCtx + .LtdSeries.AsNoTracking() + .Where(s => s.IsActive && s.StartsAt > now) + .OrderBy(s => s.StartsAt) + .FirstOrDefaultAsync(ct); + + if (nextSeries == null) + return null; + + var catalogSnap = GetCatalogSnapshot(CatalogType.Normal); + var product = catalogSnap.ProductsById.Values.FirstOrDefault(p => + p.LtdSeriesId == nextSeries.Id + ); + + if (product == null) + return null; + + // Resolve PageId from Offer + if (!catalogSnap.OffersById.TryGetValue(product.OfferId, out var offer)) + return null; + + return new UpcomingLtdSnapshot + { + SecondsUntil = (int)(nextSeries.StartsAt!.Value - now).TotalSeconds, + PageId = offer.PageId, + OfferId = offer.Id, + ClassName = product.ClassName, + }; + } } diff --git a/Turbo.Catalog/Configuration/CatalogConfig.cs b/Turbo.Catalog/Configuration/CatalogConfig.cs index 49873f37..50a1c477 100644 --- a/Turbo.Catalog/Configuration/CatalogConfig.cs +++ b/Turbo.Catalog/Configuration/CatalogConfig.cs @@ -1,6 +1,158 @@ +using System; + namespace Turbo.Catalog.Configuration; public class CatalogConfig { public const string SECTION_NAME = "Turbo:Catalog"; + + /// + /// Configuration for LTD raffle weighting criteria. + /// + public LtdRaffleWeightConfig LtdRaffle { get; set; } = new(); +} + +/// +/// Configuration for LTD raffle weighting criteria. +/// Hotel owners can tune these values to define their own fairness criteria. +/// +public class LtdRaffleWeightConfig +{ + /// + /// Base weight given to all participants (ensures everyone has a chance). + /// + public double BaseWeight { get; set; } = 1.0; + + /// + /// Default buffer window in seconds. + /// + public int DefaultBufferSeconds { get; set; } = 20; + + /// + /// If true, uses pure random selection (equal chance for all). + /// Set to true to disable all weighting. + /// + public bool UsePureRandom { get; set; } = false; + + /// + /// If true, serial numbers are assigned randomly (Habbo style). + /// If false, they are assigned sequentially (1, 2, 3...). + /// + public bool RandomizeSerials { get; set; } = true; + + /// + /// If true, each player can only win one item per LTD series. + /// If false, players can buy as many as they want (if stock permits). + /// + public bool LimitOnePerCustomer { get; set; } = true; + + /// + /// Badge count weighting configuration. + /// + public WeightCriterion BadgeCount { get; set; } = + new() + { + Enabled = true, + BonusPerUnit = 0.02, + MaxBonus = 1.0, + }; + + /// + /// Account age (days) weighting configuration. + /// + public WeightCriterion AccountAgeDays { get; set; } = + new() + { + Enabled = true, + BonusPerUnit = 0.00137, // ~0.5 per year + MaxBonus = 0.5, + }; + + /// + /// Online time (minutes) weighting configuration. + /// Note: Requires online time tracking to be implemented. + /// + public WeightCriterion OnlineTimeMinutes { get; set; } = + new() + { + Enabled = false, + BonusPerUnit = 0.00005, + MaxBonus = 0.5, + }; + + /// + /// Room count weighting configuration. + /// + public WeightCriterion RoomCount { get; set; } = + new() + { + Enabled = false, + BonusPerUnit = 0.05, + MaxBonus = 0.5, + }; + + /// + /// Furniture count weighting configuration. + /// + public WeightCriterion FurnitureCount { get; set; } = + new() + { + Enabled = false, + BonusPerUnit = 0.001, + MaxBonus = 0.5, + }; + + /// + /// Friend count weighting configuration. + /// + public WeightCriterion FriendCount { get; set; } = + new() + { + Enabled = false, + BonusPerUnit = 0.01, + MaxBonus = 0.5, + }; + + /// + /// Respects earned (from other users) weighting configuration. + /// + public WeightCriterion RespectsReceived { get; set; } = + new() + { + Enabled = false, + BonusPerUnit = 0.005, + MaxBonus = 0.5, + }; + + /// + /// Achievement score weighting configuration. + /// + public WeightCriterion AchievementScore { get; set; } = + new() + { + Enabled = false, + BonusPerUnit = 0.0001, + MaxBonus = 0.5, + }; +} + +/// +/// Configuration for a single weighting criterion. +/// +public class WeightCriterion +{ + /// + /// Whether this criterion is used in weight calculation. + /// + public bool Enabled { get; set; } + + /// + /// Bonus weight added per unit of this criterion. + /// + public double BonusPerUnit { get; set; } + + /// + /// Maximum bonus that can be gained from this criterion. + /// + public double MaxBonus { get; set; } } diff --git a/Turbo.Catalog/Grains/LtdRaffleGrain.cs b/Turbo.Catalog/Grains/LtdRaffleGrain.cs new file mode 100644 index 00000000..94aeaf24 --- /dev/null +++ b/Turbo.Catalog/Grains/LtdRaffleGrain.cs @@ -0,0 +1,514 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Orleans; +using Turbo.Catalog.Configuration; +using Turbo.Database.Context; +using Turbo.Database.Entities.Catalog; +using Turbo.Primitives.Catalog; +using Turbo.Primitives.Catalog.Enums; +using Turbo.Primitives.Catalog.Grains; +using Turbo.Primitives.Catalog.Snapshots; +using Turbo.Primitives.Messages.Outgoing.Collectibles; +using Turbo.Primitives.Orleans; +using Turbo.Primitives.Players; +using Turbo.Primitives.Players.Enums.Wallet; +using Turbo.Primitives.Players.Wallet; + +namespace Turbo.Catalog.Grains; + +public sealed class LtdRaffleGrain( + IGrainFactory grainFactory, + IDbContextFactory dbCtxFactory, + ILogger logger, + ICatalogService catalogService, + IOptions config +) : Grain, ILtdRaffleGrain +{ + private readonly IGrainFactory _grainFactory = grainFactory; + private readonly IDbContextFactory _dbCtxFactory = dbCtxFactory; + private readonly ILogger _logger = logger; + private readonly ICatalogService _catalogService = catalogService; + private readonly CatalogConfig _config = config.Value; + + private readonly Dictionary _currentBatchEntries = []; + + private LtdSeriesSnapshot? _series; + private string? _currentBatchId; + private bool _isInBufferPeriod; + private bool _raffleFinished; + private IDisposable? _raffleTimer; + + public override async Task OnActivateAsync(CancellationToken ct) + { + await ReloadSeriesAsync(ct); + + if (_series != null) + _raffleFinished = _series.IsRaffleFinished; + } + + public async Task EnterRaffleAsync(int playerId, CancellationToken ct) + { + if (_series is not { IsAvailable: true }) + { + return LtdRaffleEntryResult.Failed( + _series?.RemainingQuantity <= 0 + ? LtdRaffleEntryError.SoldOut + : LtdRaffleEntryError.SeriesNotFound + ); + } + + var snap = _catalogService.GetCatalogSnapshot(CatalogType.Normal); + var product = snap.ProductsById.Values.FirstOrDefault(p => p.LtdSeriesId == _series.Id); + + if (product == null || !snap.OffersById.TryGetValue(product.OfferId, out var offer)) + return LtdRaffleEntryResult.Failed(LtdRaffleEntryError.None); + + var walletGrain = _grainFactory.GetPlayerWalletGrain(playerId); + var credits = await walletGrain.GetAmountForCurrencyAsync( + new CurrencyKind { CurrencyType = CurrencyType.Credits }, + ct + ); + var activityPoints = await walletGrain.GetActivityPointsAsync(ct); + + var hasInsufficientCredits = offer.CostCredits > credits; + var hasInsufficientActivityPoints = + offer is { CostCurrency: > 0, CurrencyTypeId: not null } + && activityPoints.GetValueOrDefault(offer.CurrencyTypeId.Value) < offer.CostCurrency; + + if (hasInsufficientCredits || hasInsufficientActivityPoints) + { + return LtdRaffleEntryResult.Failed( + LtdRaffleEntryError.InsufficientFunds, + new CatalogBalanceFailure + { + NotEnoughCredits = hasInsufficientCredits, + NotEnoughActivityPoints = hasInsufficientActivityPoints, + ActivityPointType = offer.CurrencyTypeId ?? 0, + } + ); + } + + var buffer = + _series.RaffleWindowSeconds > 0 + ? _series.RaffleWindowSeconds + : _config.LtdRaffle.DefaultBufferSeconds; + + if (buffer <= 0 || _raffleFinished) + { + var instantWin = await TryFinalizeWinnerAsync(playerId, null, false); + return instantWin + ? LtdRaffleEntryResult.Succeeded("instant") + : LtdRaffleEntryResult.Failed(LtdRaffleEntryError.SoldOut); + } + + if (_config.LtdRaffle.LimitOnePerCustomer) + { + await using var dbCtx = await _dbCtxFactory.CreateDbContextAsync(ct); + + var alreadyWon = await dbCtx.LtdRaffleEntries.AnyAsync( + e => + e.SeriesEntityId == _series.Id + && e.PlayerEntityId == playerId + && e.Result == "won", + ct + ); + + if (alreadyWon) + return LtdRaffleEntryResult.Failed(LtdRaffleEntryError.AlreadyWon); + } + + if (_currentBatchEntries.ContainsKey(playerId)) + return LtdRaffleEntryResult.Failed(LtdRaffleEntryError.AlreadyInQueue); + + if (_currentBatchId == null) + { + _currentBatchId = Guid.NewGuid().ToString(); + _isInBufferPeriod = true; + _raffleTimer = this.RegisterGrainTimer( + async _ => await ExecuteRaffleAsync(), + null, + TimeSpan.FromSeconds(buffer), + Timeout.InfiniteTimeSpan + ); + } + + if (!_isInBufferPeriod) + return LtdRaffleEntryResult.Failed(LtdRaffleEntryError.RaffleProcessing); + + _currentBatchEntries[playerId] = await CalculateWeightAsync(playerId, ct); + await PersistEntryAsync(playerId, _currentBatchId, ct); + + await _grainFactory + .GetPlayerPresenceGrain(playerId) + .SendComposerAsync( + new LtdRaffleEnteredMessageComposer { ClassName = product.ClassName ?? "LTD" } + ); + + return LtdRaffleEntryResult.Succeeded(_currentBatchId); + } + + private async Task ExecuteRaffleAsync() + { + if (_currentBatchId == null || _currentBatchEntries.Count == 0) + { + _isInBufferPeriod = false; + return; + } + + var batchId = _currentBatchId; + var entries = _currentBatchEntries.ToList(); + + _currentBatchId = null; + _currentBatchEntries.Clear(); + _isInBufferPeriod = false; + _raffleFinished = true; + _raffleTimer = null; + + await PersistFinishedAsync(); + await ReloadSeriesAsync(CancellationToken.None); + + var winnersCount = Math.Min(entries.Count, _series?.RemainingQuantity ?? 0); + var winners = _config.LtdRaffle.UsePureRandom + ? [.. entries.OrderBy(_ => Random.Shared.Next()).Take(winnersCount).Select(e => e.Key)] + : SelectWeighted(entries, winnersCount); + + foreach (var entry in entries) + { + if (winners.Contains(entry.Key)) + await TryFinalizeWinnerAsync(entry.Key, batchId, true); + else + await NotifyLoserAsync(entry.Key, LtdRaffleResultCode.Lost); + } + + await ReloadSeriesAsync(CancellationToken.None); + } + + private async Task TryFinalizeWinnerAsync(int playerId, string? batchId, bool isRaffle) + { + await using var dbCtx = await _dbCtxFactory.CreateDbContextAsync(); + await using var tx = await dbCtx.Database.BeginTransactionAsync(); + + try + { + var series = await dbCtx + .LtdSeries.FromSqlRaw( + "SELECT * FROM ltd_series WHERE id = {0} FOR UPDATE", + (int)this.GetPrimaryKeyLong() + ) + .OrderBy(x => x.Id) + .FirstOrDefaultAsync(); + + if (series == null || series.RemainingQuantity <= 0) + return false; + + var snap = _catalogService.GetCatalogSnapshot(CatalogType.Normal); + var prod = snap.ProductsById.Values.First(p => p.LtdSeriesId == series.Id); + var offer = snap.OffersById[prod.OfferId]; + + var debitResult = await _grainFactory + .GetPlayerWalletGrain(playerId) + .TryDebitAsync(BuildDebits(offer), CancellationToken.None); + + if (!debitResult.Succeeded) + { + await NotifyLoserAsync(playerId, LtdRaffleResultCode.Lost); + return false; + } + + var serial = _config.LtdRaffle.RandomizeSerials + ? (await GetAvailableSerialsAsync(dbCtx, series))[ + Random.Shared.Next(series.RemainingQuantity) + ] + : (series.TotalQuantity - series.RemainingQuantity) + 1; + + series.RemainingQuantity--; + + if (batchId != null) + { + var entry = await dbCtx + .LtdRaffleEntries.OrderBy(e => e.Id) + .FirstOrDefaultAsync(e => e.BatchId == batchId && e.PlayerEntityId == playerId); + + if (entry != null) + { + entry.Result = "won"; + entry.SerialNumber = serial; + entry.ProcessedAt = DateTime.UtcNow; + } + } + + await dbCtx.SaveChangesAsync(); + await tx.CommitAsync(); + + await _grainFactory + .GetInventoryGrain(playerId) + .GrantLtdFurnitureAsync( + series.CatalogProductEntityId, + serial, + series.TotalQuantity, + CancellationToken.None + ); + + if (isRaffle) + { + await _grainFactory + .GetPlayerPresenceGrain(playerId) + .SendComposerAsync( + new LtdRaffleResultMessageComposer + { + ClassName = prod.ClassName ?? "LTD", + ResultCode = LtdRaffleResultCode.Won, + } + ); + } + + return true; + } + catch (Exception ex) + { + _logger.LogError( + ex, + "Failed to finalize LTD raffle winner for player {PlayerId} in series {SeriesId}", + playerId, + this.GetPrimaryKeyLong() + ); + + await tx.RollbackAsync(); + return false; + } + } + + private async Task> GetAvailableSerialsAsync(TurboDbContext db, LtdSeriesEntity s) + { + var usedSerials = await db + .LtdRaffleEntries.Where(e => + e.SeriesEntityId == s.Id && e.Result == "won" && e.SerialNumber != null + ) + .Select(e => e.SerialNumber!.Value) + .ToListAsync(); + + return [.. Enumerable.Range(1, s.TotalQuantity).Except(usedSerials)]; + } + + private static List BuildDebits(CatalogOfferSnapshot offer) + { + var debits = new List(); + + if (offer.CostCredits > 0) + { + debits.Add( + new WalletDebitRequest + { + CurrencyKind = new CurrencyKind { CurrencyType = CurrencyType.Credits }, + Amount = offer.CostCredits, + } + ); + } + + if (offer.CostCurrency > 0) + { + debits.Add( + new WalletDebitRequest + { + CurrencyKind = new CurrencyKind + { + CurrencyType = CurrencyType.ActivityPoints, + ActivityPointType = offer.CurrencyTypeId, + }, + Amount = offer.CostCurrency, + } + ); + } + + return debits; + } + + private async Task CalculateWeightAsync(int playerId, CancellationToken ct) + { + var playerGrain = _grainFactory.GetPlayerGrain(PlayerId.Parse(playerId)); + var summary = await playerGrain.GetSummaryAsync(ct); + var profile = await playerGrain.GetExtendedProfileSnapshotAsync(ct); + + var cfg = _config.LtdRaffle; + var weight = cfg.BaseWeight; + + // TODO: Replace DB queries with snapshot-based lookups once PlayerGrain exposes + // badge count, room count, and furniture count in PlayerSummarySnapshot or a dedicated snapshot. + var needsDbQuery = + cfg.BadgeCount.Enabled || cfg.RoomCount.Enabled || cfg.FurnitureCount.Enabled; + + if (needsDbQuery) + { + await using var db = await _dbCtxFactory.CreateDbContextAsync(ct); + + if (cfg.BadgeCount.Enabled) + { + var badgeCount = await db.PlayerBadges.CountAsync( + b => b.PlayerEntityId == playerId, + ct + ); + weight += Math.Min( + badgeCount * cfg.BadgeCount.BonusPerUnit, + cfg.BadgeCount.MaxBonus + ); + } + + if (cfg.RoomCount.Enabled) + { + var roomCount = await db.Rooms.CountAsync(r => r.PlayerEntityId == playerId, ct); + weight += Math.Min(roomCount * cfg.RoomCount.BonusPerUnit, cfg.RoomCount.MaxBonus); + } + + if (cfg.FurnitureCount.Enabled) + { + var furniCount = await db.Furnitures.CountAsync( + f => f.PlayerEntityId == playerId, + ct + ); + weight += Math.Min( + furniCount * cfg.FurnitureCount.BonusPerUnit, + cfg.FurnitureCount.MaxBonus + ); + } + } + + // Snapshot-based weighting (no DB round-trip needed) + if (cfg.AccountAgeDays.Enabled) + weight += Math.Min( + (DateTime.UtcNow - summary.CreatedAt).Days * cfg.AccountAgeDays.BonusPerUnit, + cfg.AccountAgeDays.MaxBonus + ); + + if (cfg.AchievementScore.Enabled) + weight += Math.Min( + profile.AchievementScore * cfg.AchievementScore.BonusPerUnit, + cfg.AchievementScore.MaxBonus + ); + + if (cfg.FriendCount.Enabled) + weight += Math.Min( + profile.FriendCount * cfg.FriendCount.BonusPerUnit, + cfg.FriendCount.MaxBonus + ); + + if (cfg.RespectsReceived.Enabled) + weight += Math.Min( + profile.StarGemCount * cfg.RespectsReceived.BonusPerUnit, + cfg.RespectsReceived.MaxBonus + ); + + return weight; + } + + private static HashSet SelectWeighted(List> entries, int count) + { + var winners = new HashSet(); + var pool = entries.ToList(); + + for (var i = 0; i < count && pool.Count > 0; i++) + { + var total = pool.Sum(e => e.Value); + var roll = Random.Shared.NextDouble() * total; + var current = 0.0; + + foreach (var entry in pool) + { + current += entry.Value; + + if (roll <= current) + { + winners.Add(entry.Key); + pool.Remove(entry); + break; + } + } + } + + return winners; + } + + public async Task ReloadSeriesAsync(CancellationToken ct) + { + await using var db = await _dbCtxFactory.CreateDbContextAsync(ct); + + var entity = await db + .LtdSeries.AsNoTracking() + .OrderBy(s => s.Id) + .FirstOrDefaultAsync(s => s.Id == (int)this.GetPrimaryKeyLong(), ct); + + if (entity != null) + { + _series = new LtdSeriesSnapshot + { + Id = entity.Id, + CatalogProductId = entity.CatalogProductEntityId, + TotalQuantity = entity.TotalQuantity, + RemainingQuantity = entity.RemainingQuantity, + RaffleWindowSeconds = entity.RaffleWindowSeconds, + IsActive = entity.IsActive, + IsRaffleFinished = entity.IsRaffleFinished, + StartsAt = entity.StartsAt, + EndsAt = entity.EndsAt, + }; + } + } + + private async Task PersistFinishedAsync() + { + await using var db = await _dbCtxFactory.CreateDbContextAsync(); + + await db + .LtdSeries.Where(s => s.Id == (int)this.GetPrimaryKeyLong()) + .ExecuteUpdateAsync(u => u.SetProperty(s => s.IsRaffleFinished, true)); + } + + private async Task NotifyLoserAsync(int playerId, LtdRaffleResultCode resultCode) + { + var product = _catalogService + .GetCatalogSnapshot(CatalogType.Normal) + .ProductsById.Values.FirstOrDefault(p => p.LtdSeriesId == _series?.Id); + + await _grainFactory + .GetPlayerPresenceGrain(playerId) + .SendComposerAsync( + new LtdRaffleResultMessageComposer + { + ClassName = product?.ClassName ?? "LTD", + ResultCode = resultCode, + } + ); + } + + private async Task PersistEntryAsync(int playerId, string batchId, CancellationToken ct) + { + await using var db = await _dbCtxFactory.CreateDbContextAsync(ct); + + db.LtdRaffleEntries.Add( + new LtdRaffleEntryEntity + { + SeriesEntityId = (int)this.GetPrimaryKeyLong(), + PlayerEntityId = playerId, + BatchId = batchId, + EnteredAt = DateTime.UtcNow, + Result = "pending", + } + ); + + await db.SaveChangesAsync(ct); + } + + public Task GetSeriesSnapshotAsync(CancellationToken ct) => + Task.FromResult(_series); + + public async Task ForceRunRaffleAsync(CancellationToken ct) + { + _raffleTimer?.Dispose(); + await ExecuteRaffleAsync(); + } +} diff --git a/Turbo.Catalog/Providers/CatalogSnapshotProvider.cs b/Turbo.Catalog/Providers/CatalogSnapshotProvider.cs index 963a7edb..2028a831 100644 --- a/Turbo.Catalog/Providers/CatalogSnapshotProvider.cs +++ b/Turbo.Catalog/Providers/CatalogSnapshotProvider.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Threading; @@ -5,6 +6,7 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using Turbo.Database.Context; +using Turbo.Database.Entities.Catalog; using Turbo.Primitives.Catalog; using Turbo.Primitives.Catalog.Enums; using Turbo.Primitives.Catalog.Providers; @@ -55,6 +57,21 @@ public async Task ReloadAsync(CancellationToken ct) .CatalogProducts.AsNoTracking() .ToListAsync(ct) .ConfigureAwait(false); + var allSeries = await dbCtx + .LtdSeries.AsNoTracking() + .ToListAsync(ct) + .ConfigureAwait(false); + + // Group by product and pick the most relevant series (Active > Newest) + var series = allSeries + .GroupBy(s => s.CatalogProductEntityId) + .ToDictionary( + g => g.Key, + g => + g.OrderByDescending(s => s.IsActive) + .ThenByDescending(s => s.StartsAt ?? s.CreatedAt) + .First() + ); var pageChildrenIds = pages .GroupBy(p => p.ParentEntityId ?? -1) @@ -76,23 +93,34 @@ public async Task ReloadAsync(CancellationToken ct) .ToImmutableDictionary(g => g.Key, g => g.Select(x => x.Id).ToImmutableArray()); var productsById = products - .Select(x => new CatalogProductSnapshot + .Select(x => { - Id = x.Id, - OfferId = x.CatalogOfferEntityId, - ProductType = x.ProductType, - FurniDefinitionId = x.FurnitureDefinitionEntityId ?? -1, - SpriteId = - x.FurnitureDefinitionEntityId != null - ? _furnitureProvider - .TryGetDefinition(x.FurnitureDefinitionEntityId.Value) - ?.SpriteId - ?? -1 - : -1, - ExtraParam = x.ExtraParam, - Quantity = x.Quantity, - UniqueSize = x.UniqueSize, - UniqueRemaining = x.UniqueRemaining, + var productSeries = series.GetValueOrDefault(x.Id); + return new CatalogProductSnapshot + { + Id = x.Id, + OfferId = x.CatalogOfferEntityId, + ProductType = x.ProductType, + FurniDefinitionId = x.FurnitureDefinitionEntityId ?? -1, + SpriteId = + x.FurnitureDefinitionEntityId != null + ? _furnitureProvider + .TryGetDefinition(x.FurnitureDefinitionEntityId.Value) + ?.SpriteId + ?? -1 + : -1, + ExtraParam = x.ExtraParam, + Quantity = x.Quantity, + UniqueSize = productSeries?.TotalQuantity ?? 0, + UniqueRemaining = productSeries?.RemainingQuantity ?? 0, + LtdSeriesId = productSeries?.Id, + ClassName = + x.FurnitureDefinitionEntityId != null + ? _furnitureProvider + .TryGetDefinition(x.FurnitureDefinitionEntityId.Value) + ?.Name + : null, + }; }) .ToImmutableDictionary(x => x.Id); diff --git a/Turbo.Database/Context/TurboDbContext.cs b/Turbo.Database/Context/TurboDbContext.cs index 0fc87e40..af554f42 100644 --- a/Turbo.Database/Context/TurboDbContext.cs +++ b/Turbo.Database/Context/TurboDbContext.cs @@ -58,6 +58,10 @@ public class TurboDbContext(DbContextOptions options) public DbSet PlayerFavouriteRooms { get; init; } + public DbSet LtdSeries { get; init; } + + public DbSet LtdRaffleEntries { get; init; } + protected override void OnModelCreating(ModelBuilder mb) { base.OnModelCreating(mb); diff --git a/Turbo.Database/Entities/Catalog/CatalogProductEntity.cs b/Turbo.Database/Entities/Catalog/CatalogProductEntity.cs index 7d2f405f..9145639e 100644 --- a/Turbo.Database/Entities/Catalog/CatalogProductEntity.cs +++ b/Turbo.Database/Entities/Catalog/CatalogProductEntity.cs @@ -25,14 +25,6 @@ public class CatalogProductEntity : TurboEntity [DefaultValue(1)] public required int Quantity { get; set; } - [Column("unique_size")] - [DefaultValue(0)] - public required int UniqueSize { get; set; } - - [Column("unique_remaining")] - [DefaultValue(0)] - public required int UniqueRemaining { get; set; } - [ForeignKey(nameof(CatalogOfferEntityId))] public required CatalogOfferEntity Offer { get; set; } diff --git a/Turbo.Database/Entities/Catalog/LtdRaffleEntryEntity.cs b/Turbo.Database/Entities/Catalog/LtdRaffleEntryEntity.cs new file mode 100644 index 00000000..205b49bc --- /dev/null +++ b/Turbo.Database/Entities/Catalog/LtdRaffleEntryEntity.cs @@ -0,0 +1,65 @@ +using System; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using Turbo.Database.Entities.Players; + +namespace Turbo.Database.Entities.Catalog; + +/// +/// An entry in an LTD raffle queue. +/// +[Table("ltd_raffle_entries")] +public class LtdRaffleEntryEntity : TurboEntity +{ + /// + /// The LTD series this entry is for. + /// + [Column("series_id")] + public required int SeriesEntityId { get; set; } + + /// + /// The player who entered the raffle. + /// + [Column("player_id")] + public required int PlayerEntityId { get; set; } + + /// + /// UUID for the raffle batch this entry belongs to. + /// + [Column("batch_id")] + [MaxLength(36)] + public required string BatchId { get; set; } + + /// + /// When the player entered the raffle. + /// + [Column("entered_at")] + public required DateTime EnteredAt { get; set; } + + /// + /// The result of the raffle: 'pending', 'won', 'lost'. + /// + [Column("result")] + [MaxLength(20)] + [DefaultValue("pending")] + public required string Result { get; set; } = "pending"; + + /// + /// The serial number assigned (if won). + /// + [Column("serial_number")] + public int? SerialNumber { get; set; } + + /// + /// When the raffle was processed. + /// + [Column("processed_at")] + public DateTime? ProcessedAt { get; set; } + + [ForeignKey(nameof(SeriesEntityId))] + public LtdSeriesEntity? Series { get; set; } + + [ForeignKey(nameof(PlayerEntityId))] + public PlayerEntity? Player { get; set; } +} diff --git a/Turbo.Database/Entities/Catalog/LtdSeriesEntity.cs b/Turbo.Database/Entities/Catalog/LtdSeriesEntity.cs new file mode 100644 index 00000000..8c2e7606 --- /dev/null +++ b/Turbo.Database/Entities/Catalog/LtdSeriesEntity.cs @@ -0,0 +1,67 @@ +using System; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Turbo.Database.Entities.Catalog; + +/// +/// Configuration for an LTD (Limited Edition) series. +/// +[Table("ltd_series")] +public class LtdSeriesEntity : TurboEntity +{ + /// + /// Links to the catalog product this LTD series is for. + /// + [Column("catalog_product_id")] + public required int CatalogProductEntityId { get; set; } + + /// + /// Total items in this series (e.g., 500). + /// + [Column("total_quantity")] + public required int TotalQuantity { get; set; } + + /// + /// How many items are left to mint. + /// + [Column("remaining_quantity")] + public required int RemainingQuantity { get; set; } + + /// + /// How long the raffle queue is open (in seconds) before drawing. + /// + [Column("raffle_window_seconds")] + [DefaultValue(30)] + public required int RaffleWindowSeconds { get; set; } = 30; + + /// + /// Whether this series is currently active. + /// + [Column("is_active")] + [DefaultValue(true)] + public required bool IsActive { get; set; } = true; + + /// + /// Whether the initial 20s raffle draw has already occurred. + /// + [Column("has_raffle_finished")] + [DefaultValue(false)] + public bool IsRaffleFinished { get; set; } = false; + + /// + /// When this LTD becomes available (null = immediately). + /// + [Column("starts_at")] + public DateTime? StartsAt { get; set; } + + /// + /// When this LTD sale ends (null = never). + /// + [Column("ends_at")] + public DateTime? EndsAt { get; set; } + + [ForeignKey(nameof(CatalogProductEntityId))] + public CatalogProductEntity? CatalogProduct { 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/20260208201115_AddLtdRaffleSystem.Designer.cs b/Turbo.Database/Migrations/20260208201115_AddLtdRaffleSystem.Designer.cs new file mode 100644 index 00000000..13943336 --- /dev/null +++ b/Turbo.Database/Migrations/20260208201115_AddLtdRaffleSystem.Designer.cs @@ -0,0 +1,2530 @@ +// +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("20260208201115_AddLtdRaffleSystem")] + partial class AddLtdRaffleSystem + { + /// + 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("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.Catalog.LtdRaffleEntryEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("BatchId") + .IsRequired() + .HasMaxLength(36) + .HasColumnType("varchar(36)") + .HasColumnName("batch_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("EnteredAt") + .HasColumnType("datetime(6)") + .HasColumnName("entered_at"); + + b.Property("PlayerEntityId") + .HasColumnType("int") + .HasColumnName("player_id"); + + b.Property("ProcessedAt") + .HasColumnType("datetime(6)") + .HasColumnName("processed_at"); + + b.Property("Result") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("pending") + .HasColumnName("result"); + + b.Property("SerialNumber") + .HasColumnType("int") + .HasColumnName("serial_number"); + + b.Property("SeriesEntityId") + .HasColumnType("int") + .HasColumnName("series_id"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("UpdatedAt")); + + b.HasKey("Id"); + + b.HasIndex("PlayerEntityId"); + + b.HasIndex("SeriesEntityId"); + + b.ToTable("ltd_raffle_entries"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Catalog.LtdSeriesEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CatalogProductEntityId") + .HasColumnType("int") + .HasColumnName("catalog_product_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("EndsAt") + .HasColumnType("datetime(6)") + .HasColumnName("ends_at"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true) + .HasColumnName("is_active"); + + b.Property("IsRaffleFinished") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false) + .HasColumnName("has_raffle_finished"); + + b.Property("RaffleWindowSeconds") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(30) + .HasColumnName("raffle_window_seconds"); + + b.Property("RemainingQuantity") + .HasColumnType("int") + .HasColumnName("remaining_quantity"); + + b.Property("StartsAt") + .HasColumnType("datetime(6)") + .HasColumnName("starts_at"); + + b.Property("TotalQuantity") + .HasColumnType("int") + .HasColumnName("total_quantity"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("UpdatedAt")); + + b.HasKey("Id"); + + b.HasIndex("CatalogProductEntityId"); + + b.ToTable("ltd_series"); + }); + + 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.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.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.Catalog.LtdRaffleEntryEntity", b => + { + b.HasOne("Turbo.Database.Entities.Players.PlayerEntity", "Player") + .WithMany() + .HasForeignKey("PlayerEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Turbo.Database.Entities.Catalog.LtdSeriesEntity", "Series") + .WithMany() + .HasForeignKey("SeriesEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Player"); + + b.Navigation("Series"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Catalog.LtdSeriesEntity", b => + { + b.HasOne("Turbo.Database.Entities.Catalog.CatalogProductEntity", "CatalogProduct") + .WithMany() + .HasForeignKey("CatalogProductEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("CatalogProduct"); + }); + + 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.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.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("MessengerCategories"); + + b.Navigation("MessengerFriends"); + + 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/20260208201115_AddLtdRaffleSystem.cs b/Turbo.Database/Migrations/20260208201115_AddLtdRaffleSystem.cs new file mode 100644 index 00000000..5c94ca99 --- /dev/null +++ b/Turbo.Database/Migrations/20260208201115_AddLtdRaffleSystem.cs @@ -0,0 +1,193 @@ +using System; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Turbo.Database.Migrations +{ + /// + public partial class AddLtdRaffleSystem : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn(name: "unique_remaining", table: "catalog_products"); + + migrationBuilder.DropColumn(name: "unique_size", table: "catalog_products"); + + migrationBuilder + .CreateTable( + name: "ltd_series", + columns: table => new + { + id = table + .Column(type: "int", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.IdentityColumn + ), + catalog_product_id = table.Column(type: "int", nullable: false), + total_quantity = table.Column(type: "int", nullable: false), + remaining_quantity = table.Column(type: "int", nullable: false), + raffle_window_seconds = table.Column( + type: "int", + nullable: false, + defaultValue: 30 + ), + is_active = table.Column( + type: "tinyint(1)", + nullable: false, + defaultValue: true + ), + has_raffle_finished = table.Column( + type: "tinyint(1)", + nullable: false, + defaultValue: false + ), + starts_at = table.Column(type: "datetime(6)", nullable: true), + ends_at = table.Column(type: "datetime(6)", 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_ltd_series", x => x.id); + table.ForeignKey( + name: "FK_ltd_series_catalog_products_catalog_product_id", + column: x => x.catalog_product_id, + principalTable: "catalog_products", + principalColumn: "id", + onDelete: ReferentialAction.Cascade + ); + } + ) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder + .CreateTable( + name: "ltd_raffle_entries", + columns: table => new + { + id = table + .Column(type: "int", nullable: false) + .Annotation( + "MySql:ValueGenerationStrategy", + MySqlValueGenerationStrategy.IdentityColumn + ), + series_id = table.Column(type: "int", nullable: false), + player_id = table.Column(type: "int", nullable: false), + batch_id = table + .Column(type: "varchar(36)", maxLength: 36, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + entered_at = table.Column(type: "datetime(6)", nullable: false), + result = table + .Column( + type: "varchar(20)", + maxLength: 20, + nullable: false, + defaultValue: "pending" + ) + .Annotation("MySql:CharSet", "utf8mb4"), + serial_number = table.Column(type: "int", nullable: true), + processed_at = table.Column(type: "datetime(6)", 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_ltd_raffle_entries", x => x.id); + table.ForeignKey( + name: "FK_ltd_raffle_entries_ltd_series_series_id", + column: x => x.series_id, + principalTable: "ltd_series", + principalColumn: "id", + onDelete: ReferentialAction.Cascade + ); + table.ForeignKey( + name: "FK_ltd_raffle_entries_players_player_id", + column: x => x.player_id, + principalTable: "players", + principalColumn: "id", + onDelete: ReferentialAction.Cascade + ); + } + ) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateIndex( + name: "IX_ltd_raffle_entries_player_id", + table: "ltd_raffle_entries", + column: "player_id" + ); + + migrationBuilder.CreateIndex( + name: "IX_ltd_raffle_entries_series_id", + table: "ltd_raffle_entries", + column: "series_id" + ); + + migrationBuilder.CreateIndex( + name: "IX_ltd_series_catalog_product_id", + table: "ltd_series", + column: "catalog_product_id" + ); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable(name: "ltd_raffle_entries"); + + migrationBuilder.DropTable(name: "ltd_series"); + + migrationBuilder.AddColumn( + name: "unique_remaining", + table: "catalog_products", + type: "int", + nullable: false, + defaultValue: 0 + ); + + migrationBuilder.AddColumn( + name: "unique_size", + table: "catalog_products", + type: "int", + nullable: false, + defaultValue: 0 + ); + } + } +} diff --git a/Turbo.Database/Migrations/TurboDbContextModelSnapshot.cs b/Turbo.Database/Migrations/TurboDbContextModelSnapshot.cs index 5f1460df..90a78eed 100644 --- a/Turbo.Database/Migrations/TurboDbContextModelSnapshot.cs +++ b/Turbo.Database/Migrations/TurboDbContextModelSnapshot.cs @@ -245,18 +245,6 @@ protected override void BuildModel(ModelBuilder modelBuilder) .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)") @@ -327,6 +315,154 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("currency_types"); }); + modelBuilder.Entity("Turbo.Database.Entities.Catalog.LtdRaffleEntryEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("BatchId") + .IsRequired() + .HasMaxLength(36) + .HasColumnType("varchar(36)") + .HasColumnName("batch_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("EnteredAt") + .HasColumnType("datetime(6)") + .HasColumnName("entered_at"); + + b.Property("PlayerEntityId") + .HasColumnType("int") + .HasColumnName("player_id"); + + b.Property("ProcessedAt") + .HasColumnType("datetime(6)") + .HasColumnName("processed_at"); + + b.Property("Result") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .HasColumnType("varchar(20)") + .HasDefaultValue("pending") + .HasColumnName("result"); + + b.Property("SerialNumber") + .HasColumnType("int") + .HasColumnName("serial_number"); + + b.Property("SeriesEntityId") + .HasColumnType("int") + .HasColumnName("series_id"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("UpdatedAt")); + + b.HasKey("Id"); + + b.HasIndex("PlayerEntityId"); + + b.HasIndex("SeriesEntityId"); + + b.ToTable("ltd_raffle_entries"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Catalog.LtdSeriesEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("CatalogProductEntityId") + .HasColumnType("int") + .HasColumnName("catalog_product_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("EndsAt") + .HasColumnType("datetime(6)") + .HasColumnName("ends_at"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true) + .HasColumnName("is_active"); + + b.Property("IsRaffleFinished") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false) + .HasColumnName("has_raffle_finished"); + + b.Property("RaffleWindowSeconds") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(30) + .HasColumnName("raffle_window_seconds"); + + b.Property("RemainingQuantity") + .HasColumnType("int") + .HasColumnName("remaining_quantity"); + + b.Property("StartsAt") + .HasColumnType("datetime(6)") + .HasColumnName("starts_at"); + + b.Property("TotalQuantity") + .HasColumnType("int") + .HasColumnName("total_quantity"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("UpdatedAt")); + + b.HasKey("Id"); + + b.HasIndex("CatalogProductEntityId"); + + b.ToTable("ltd_series"); + }); + modelBuilder.Entity("Turbo.Database.Entities.Furniture.FurnitureDefinitionEntity", b => { b.Property("Id") @@ -1981,6 +2117,36 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("Offer"); }); + modelBuilder.Entity("Turbo.Database.Entities.Catalog.LtdRaffleEntryEntity", b => + { + b.HasOne("Turbo.Database.Entities.Players.PlayerEntity", "Player") + .WithMany() + .HasForeignKey("PlayerEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Turbo.Database.Entities.Catalog.LtdSeriesEntity", "Series") + .WithMany() + .HasForeignKey("SeriesEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Player"); + + b.Navigation("Series"); + }); + + modelBuilder.Entity("Turbo.Database.Entities.Catalog.LtdSeriesEntity", b => + { + b.HasOne("Turbo.Database.Entities.Catalog.CatalogProductEntity", "CatalogProduct") + .WithMany() + .HasForeignKey("CatalogProductEntityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("CatalogProduct"); + }); + modelBuilder.Entity("Turbo.Database.Entities.Furniture.FurnitureEntity", b => { b.HasOne("Turbo.Database.Entities.Furniture.FurnitureDefinitionEntity", "FurnitureDefinitionEntity") diff --git a/Turbo.Inventory/Grains/InventoryGrain.Furni.cs b/Turbo.Inventory/Grains/InventoryGrain.Furni.cs index 44179458..87cc7a1d 100644 --- a/Turbo.Inventory/Grains/InventoryGrain.Furni.cs +++ b/Turbo.Inventory/Grains/InventoryGrain.Furni.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Linq; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Orleans; @@ -9,6 +11,7 @@ using Turbo.Inventory.Furniture; using Turbo.Logging; using Turbo.Primitives; +using Turbo.Primitives.Catalog.Enums; using Turbo.Primitives.Catalog.Snapshots; using Turbo.Primitives.Furniture.Enums; using Turbo.Primitives.Furniture.StuffData; @@ -143,4 +146,77 @@ CancellationToken ct public Task> GetAllItemSnapshotsAsync( CancellationToken ct ) => _furniModule.GetAllItemSnapshotsAsync(ct); + + public async Task GrantLtdFurnitureAsync( + int catalogProductId, + int serialNumber, + int seriesSize, + CancellationToken ct + ) + { + // Find the product in the catalog snapshot + var snapshot = _catalogService.GetCatalogSnapshot(CatalogType.Normal); + var product = snapshot.ProductsById.Values.FirstOrDefault(p => p.Id == catalogProductId); + + if (product == null) + throw new TurboException(TurboErrorCodeEnum.CatalogProductNotFound); + + var def = + _furnitureDefinitionProvider.TryGetDefinition(product.FurniDefinitionId) + ?? throw new TurboException(TurboErrorCodeEnum.FurnitureDefinitionNotFound); + + // Build ExtraData JSON with LTD serial info in the stuff section + var extraDataJson = JsonSerializer.Serialize( + new + { + stuff = new + { + UniqueNumber = serialNumber, + UniqueSeries = seriesSize, + Data = "0", + }, + } + ); + + var dbCtx = await _dbCtxFactory.CreateDbContextAsync(ct).ConfigureAwait(false); + + try + { + // Create furniture entity - LTD data is stored in ExtraData JSON + var entity = new FurnitureEntity + { + PlayerEntityId = (int)this.GetPrimaryKeyLong(), + FurnitureDefinitionEntityId = def.Id, + ExtraData = extraDataJson, + }; + + dbCtx.Add(entity); + await dbCtx.SaveChangesAsync(ct); + + // Create stuff data from the ExtraData - this properly loads the UniqueNumber/UniqueSeries + var extraData = new ExtraData(extraDataJson); + var stuffData = _stuffDataFactory.CreateStuffDataFromExtraData( + StuffDataType.LegacyKey, + extraData + ); + + // Add to inventory + await AddFurnitureAsync( + new FurnitureItem() + { + ItemId = entity.Id, + OwnerId = entity.PlayerEntityId, + OwnerName = string.Empty, + Definition = def, + ExtraData = extraData, + StuffData = stuffData, + }, + ct + ); + } + finally + { + await dbCtx.DisposeAsync().ConfigureAwait(false); + } + } } diff --git a/Turbo.Inventory/Grains/InventoryGrain.cs b/Turbo.Inventory/Grains/InventoryGrain.cs index 03a1fe8f..e382b4a2 100644 --- a/Turbo.Inventory/Grains/InventoryGrain.cs +++ b/Turbo.Inventory/Grains/InventoryGrain.cs @@ -6,6 +6,7 @@ using Turbo.Database.Context; using Turbo.Inventory.Configuration; using Turbo.Inventory.Grains.Modules; +using Turbo.Primitives.Catalog; using Turbo.Primitives.Furniture.Providers; using Turbo.Primitives.Inventory.Factories; using Turbo.Primitives.Inventory.Grains; @@ -20,6 +21,7 @@ public sealed partial class InventoryGrain : Grain, IInventoryGrain private readonly IFurnitureDefinitionProvider _furnitureDefinitionProvider; private readonly IInventoryFurnitureLoader _furnitureItemsLoader; private readonly IStuffDataFactory _stuffDataFactory; + private readonly ICatalogService _catalogService; private readonly InventoryLiveState _state; private readonly InventoryFurniModule _furniModule; @@ -30,7 +32,8 @@ public InventoryGrain( IGrainFactory grainFactory, IFurnitureDefinitionProvider furnitureDefinitionProvider, IInventoryFurnitureLoader furnitureItemsLoader, - IStuffDataFactory stuffDataFactory + IStuffDataFactory stuffDataFactory, + ICatalogService catalogService ) { _dbCtxFactory = dbContextFactory; @@ -39,6 +42,7 @@ IStuffDataFactory stuffDataFactory _furnitureDefinitionProvider = furnitureDefinitionProvider; _furnitureItemsLoader = furnitureItemsLoader; _stuffDataFactory = stuffDataFactory; + _catalogService = catalogService; _state = new(); _furniModule = new InventoryFurniModule(this, _state, _furnitureItemsLoader); diff --git a/Turbo.PacketHandlers/Catalog/GetLimitedOfferAppearingNextMessageHandler.cs b/Turbo.PacketHandlers/Catalog/GetLimitedOfferAppearingNextMessageHandler.cs index d6ac2d69..9f7070d8 100644 --- a/Turbo.PacketHandlers/Catalog/GetLimitedOfferAppearingNextMessageHandler.cs +++ b/Turbo.PacketHandlers/Catalog/GetLimitedOfferAppearingNextMessageHandler.cs @@ -1,19 +1,53 @@ using System.Threading; using System.Threading.Tasks; using Turbo.Messages.Registry; +using Turbo.Primitives.Catalog; using Turbo.Primitives.Messages.Incoming.Catalog; +using Turbo.Primitives.Messages.Outgoing.Catalog; namespace Turbo.PacketHandlers.Catalog; -public class GetLimitedOfferAppearingNextMessageHandler +public class GetLimitedOfferAppearingNextMessageHandler(ICatalogService catalogService) : IMessageHandler { + private readonly ICatalogService _catalogService = catalogService; + public async ValueTask HandleAsync( GetLimitedOfferAppearingNextMessage message, MessageContext ctx, CancellationToken ct ) { - await ValueTask.CompletedTask.ConfigureAwait(false); + var upcoming = await _catalogService.GetUpcomingLtdAsync(ct).ConfigureAwait(false); + + if (upcoming != null) + { + await ctx.SendComposerAsync( + new LimitedOfferAppearingNextMessageComposer + { + AppearsInSeconds = upcoming.SecondsUntil, + PageId = upcoming.PageId, + OfferId = upcoming.OfferId, + ProductClassName = upcoming.ClassName ?? "", + }, + ct + ) + .ConfigureAwait(false); + } + else + { + // Habbo standard: send -1 if no upcoming LTD + await ctx.SendComposerAsync( + new LimitedOfferAppearingNextMessageComposer + { + AppearsInSeconds = -1, + PageId = -1, + OfferId = -1, + ProductClassName = "", + }, + ct + ) + .ConfigureAwait(false); + } } } diff --git a/Turbo.PacketHandlers/Catalog/PurchaseFromCatalogMessageHandler.cs b/Turbo.PacketHandlers/Catalog/PurchaseFromCatalogMessageHandler.cs index 2eafcb08..0c2b6aaa 100644 --- a/Turbo.PacketHandlers/Catalog/PurchaseFromCatalogMessageHandler.cs +++ b/Turbo.PacketHandlers/Catalog/PurchaseFromCatalogMessageHandler.cs @@ -1,19 +1,26 @@ +using System.Linq; using System.Threading; using System.Threading.Tasks; using Orleans; using Turbo.Catalog.Exceptions; using Turbo.Messages.Registry; +using Turbo.Primitives.Catalog; using Turbo.Primitives.Catalog.Enums; +using Turbo.Primitives.Catalog.Grains; +using Turbo.Primitives.Catalog.Snapshots; using Turbo.Primitives.Messages.Incoming.Catalog; using Turbo.Primitives.Messages.Outgoing.Catalog; using Turbo.Primitives.Orleans; namespace Turbo.PacketHandlers.Catalog; -public class PurchaseFromCatalogMessageHandler(IGrainFactory grainFactory) - : IMessageHandler +public class PurchaseFromCatalogMessageHandler( + IGrainFactory grainFactory, + ICatalogService catalogService +) : IMessageHandler { private readonly IGrainFactory _grainFactory = grainFactory; + private readonly ICatalogService _catalogService = catalogService; public async ValueTask HandleAsync( PurchaseFromCatalogMessage message, @@ -24,10 +31,25 @@ CancellationToken ct if (ctx.PlayerId <= 0) return; + // 1. Get current catalog snapshot to check for LTDs + var snapshot = _catalogService.GetCatalogSnapshot(CatalogType.Normal); + + if (snapshot.OffersById.TryGetValue(message.OfferId, out var offer)) + { + // Detect if any product in this offer is an LTD (UniqueSize > 0) + var ltdProduct = offer.Products.FirstOrDefault(p => p.UniqueSize > 0); + + if (ltdProduct != null) + { + await HandleLtdPurchaseAsync(ctx, ltdProduct, ct).ConfigureAwait(false); + return; + } + } + try { var purchaseGrain = _grainFactory.GetCatalogPurchaseGrain(ctx.PlayerId); - var offer = await purchaseGrain + var resultOffer = await purchaseGrain .PurchaseOfferFromCatalogAsync( CatalogType.Normal, message.OfferId, @@ -37,7 +59,7 @@ CancellationToken ct ) .ConfigureAwait(false); - await ctx.SendComposerAsync(new PurchaseOKMessageComposer { Offer = offer }, ct) + await ctx.SendComposerAsync(new PurchaseOKMessageComposer { Offer = resultOffer }, ct) .ConfigureAwait(false); } catch (CatalogPurchaseException ex) @@ -57,11 +79,153 @@ await ctx.SendComposerAsync( return; } + // If error code is < 100, it belongs to the dynamic "PurchaseError" (930) packet + if ((int)ex.ErrorType < 100) + { + await ctx.SendComposerAsync( + new PurchaseErrorMessageComposer { ErrorCode = (int)ex.ErrorType }, + ct + ) + .ConfigureAwait(false); + } + else + { + // Otherwise use the static "NotAllowed" (1872) packet + // Map internal RequiresHabboClub (101) to client code 1 + var errorCode = + ex.ErrorType == CatalogPurchaseErrorType.RequiresHabboClub + ? 1 + : (int)ex.ErrorType; + await ctx.SendComposerAsync( + new PurchaseNotAllowedMessageComposer + { + ErrorType = (CatalogPurchaseErrorType)errorCode, + }, + ct + ) + .ConfigureAwait(false); + } + } + } + + private async Task HandleLtdPurchaseAsync( + MessageContext ctx, + CatalogProductSnapshot ltdProduct, + CancellationToken ct + ) + { + // Use the series ID if available (Perfect Design), otherwise fallback to Product ID + var seriesId = ltdProduct.LtdSeriesId ?? ltdProduct.Id; + var ltdRaffleGrain = _grainFactory.GetGrain(seriesId); + + var result = await ltdRaffleGrain.EnterRaffleAsync(ctx.PlayerId, ct).ConfigureAwait(false); + + if (result.Success) + { + // If it was an instant buy (FCFS), send standard PurchaseOK + if (result.BatchId == "instant") + { + var snapshot = _catalogService.GetCatalogSnapshot(CatalogType.Normal); + if (snapshot.OffersById.TryGetValue(ltdProduct.OfferId, out var offer)) + { + await ctx.SendComposerAsync(new PurchaseOKMessageComposer { Offer = offer }, ct) + .ConfigureAwait(false); + } + } + return; + } + + // 1. Check for specialized balance alerts first + if (result.BalanceFailure != null) + { await ctx.SendComposerAsync( - new PurchaseNotAllowedMessageComposer { ErrorType = ex.ErrorType }, + new NotEnoughBalanceMessageComposer + { + NotEnoughCredits = result.BalanceFailure.NotEnoughCredits, + NotEnoughActivityPoints = result.BalanceFailure.NotEnoughActivityPoints, + ActivityPointType = result.BalanceFailure.ActivityPointType, + }, ct ) .ConfigureAwait(false); + return; + } + + // 2. Map other errors to correct packet types + switch (result.Error) + { + case LtdRaffleEntryError.AlreadyWon: + // Error 6: "LTD item purchases are limited..." needs PurchaseErrorMessage (930) + await ctx.SendComposerAsync( + new PurchaseErrorMessageComposer + { + ErrorCode = (int)CatalogPurchaseErrorType.LtdPurchasesLimited, + }, + ct + ) + .ConfigureAwait(false); + break; + + case LtdRaffleEntryError.RaffleProcessing: + // Error 12: "Frank is handling..." needs PurchaseErrorMessage (930) + await ctx.SendComposerAsync( + new PurchaseErrorMessageComposer + { + ErrorCode = (int)CatalogPurchaseErrorType.RaffleOngoing, + }, + ct + ) + .ConfigureAwait(false); + break; + + case LtdRaffleEntryError.AlreadyInQueue: + // Silent failure or generic error + await ctx.SendComposerAsync( + new PurchaseNotAllowedMessageComposer + { + ErrorType = CatalogPurchaseErrorType.PurchaseFailed, + }, + ct + ) + .ConfigureAwait(false); + break; + + case LtdRaffleEntryError.SoldOut: + // Map to static PurchaseNotAllowed (1872) using internal ID for OfferNotFound + await ctx.SendComposerAsync( + new PurchaseNotAllowedMessageComposer + { + ErrorType = (CatalogPurchaseErrorType) + (int)CatalogPurchaseErrorType.OfferNotFound, + }, + ct + ) + .ConfigureAwait(false); + break; + + case LtdRaffleEntryError.InsufficientFunds: + // Fallback: If 3883 failed or balancefailure null, send standard NotEnoughCredits via 1872 logic + // Map to client case 102 internally + await ctx.SendComposerAsync( + new PurchaseNotAllowedMessageComposer + { + ErrorType = CatalogPurchaseErrorType.NotEnoughCredits, + }, + ct + ) + .ConfigureAwait(false); + break; + + default: + await ctx.SendComposerAsync( + new PurchaseNotAllowedMessageComposer + { + ErrorType = CatalogPurchaseErrorType.PurchaseFailed, + }, + ct + ) + .ConfigureAwait(false); + break; } } } diff --git a/Turbo.Primitives/Catalog/Enums/CatalogPurchaseErrorType.cs b/Turbo.Primitives/Catalog/Enums/CatalogPurchaseErrorType.cs index 176f60af..d5214717 100644 --- a/Turbo.Primitives/Catalog/Enums/CatalogPurchaseErrorType.cs +++ b/Turbo.Primitives/Catalog/Enums/CatalogPurchaseErrorType.cs @@ -3,10 +3,26 @@ namespace Turbo.Primitives.Catalog.Enums; public enum CatalogPurchaseErrorType { None = 0, - RequiresHabboClub = 1, - OfferNotFound = 2, - NotEnoughCredits = 3, - OfferMisconfigured = 4, - PurchaseFailed = 5, - NotEnoughActivityPoints = 6, + + // Mapped to catalog.alert.purchaseerror.description.{ID} (Packet 930) + BadgeOwned = 1, + NotEnoughCredits = 2, + TradeLocked = 3, + NotEnoughActivityPoints = 4, + EffectOwned = 5, + LtdPurchasesLimited = 6, + GroupRequired = 7, + SafetyLocked = 8, + InvalidGiftMessage = 9, + InventoryFull = 10, + ChatbubbleOwned = 11, + RaffleOngoing = 12, + BlockedByReceiver = 13, + ReceiverNotFound = 14, + + // Static Description Mapping (1872 Packet) + RequiresHabboClub = 101, // Handled as case 1 in client + OfferNotFound = 102, + OfferMisconfigured = 103, + PurchaseFailed = 104, } diff --git a/Turbo.Primitives/Catalog/Enums/LtdRaffleEntryError.cs b/Turbo.Primitives/Catalog/Enums/LtdRaffleEntryError.cs new file mode 100644 index 00000000..2691edb5 --- /dev/null +++ b/Turbo.Primitives/Catalog/Enums/LtdRaffleEntryError.cs @@ -0,0 +1,16 @@ +namespace Turbo.Primitives.Catalog.Enums; + +/// +/// Error codes for LTD raffle entry failures. +/// +public enum LtdRaffleEntryError +{ + None = 0, + SeriesNotFound = 1, + SeriesNotActive = 2, + SoldOut = 3, + AlreadyInQueue = 4, + InsufficientFunds = 5, + RaffleProcessing = 6, + AlreadyWon = 7, +} diff --git a/Turbo.Primitives/Catalog/Enums/LtdRaffleResultCode.cs b/Turbo.Primitives/Catalog/Enums/LtdRaffleResultCode.cs new file mode 100644 index 00000000..574bfc43 --- /dev/null +++ b/Turbo.Primitives/Catalog/Enums/LtdRaffleResultCode.cs @@ -0,0 +1,29 @@ +namespace Turbo.Primitives.Catalog.Enums; + +/// +/// Result codes for LTD raffle outcomes. +/// +public enum LtdRaffleResultCode : byte +{ + /// + /// Player won the raffle and received the item. + /// Triggers notification.raffle.won in client. + /// + Won = 0, + + /// + /// Player lost the raffle. + /// Triggers notification.raffle.lost in client. + /// + Lost = 1, + + /// + /// Player lost because stock ran out during raffle. + /// + LostNoStock = 2, + + /// + /// Player lost due to an error. + /// + LostError = 3, +} diff --git a/Turbo.Primitives/Catalog/Grains/ILtdRaffleGrain.cs b/Turbo.Primitives/Catalog/Grains/ILtdRaffleGrain.cs new file mode 100644 index 00000000..f9fa7069 --- /dev/null +++ b/Turbo.Primitives/Catalog/Grains/ILtdRaffleGrain.cs @@ -0,0 +1,36 @@ +using System.Threading; +using System.Threading.Tasks; +using Orleans; +using Turbo.Primitives.Catalog.Snapshots; + +namespace Turbo.Primitives.Catalog.Grains; + +/// +/// Grain that manages the LTD raffle system for a specific series. +/// Keyed by LTD Series ID. +/// +public interface ILtdRaffleGrain : IGrainWithIntegerKey +{ + /// + /// Player enters the raffle for this LTD series. + /// + /// The player attempting to enter. + /// Cancellation token. + /// Result indicating success or failure reason. + Task EnterRaffleAsync(int playerId, CancellationToken ct); + + /// + /// Get the current series snapshot. + /// + Task GetSeriesSnapshotAsync(CancellationToken ct); + + /// + /// Admin: Force run the raffle for the current batch immediately. + /// + Task ForceRunRaffleAsync(CancellationToken ct); + + /// + /// Admin: Reload series configuration from database. + /// + Task ReloadSeriesAsync(CancellationToken ct); +} diff --git a/Turbo.Primitives/Catalog/ICatalogService.cs b/Turbo.Primitives/Catalog/ICatalogService.cs index a26766f5..0107015a 100644 --- a/Turbo.Primitives/Catalog/ICatalogService.cs +++ b/Turbo.Primitives/Catalog/ICatalogService.cs @@ -1,3 +1,5 @@ +using System.Threading; +using System.Threading.Tasks; using Turbo.Primitives.Catalog.Enums; using Turbo.Primitives.Catalog.Snapshots; @@ -6,4 +8,6 @@ namespace Turbo.Primitives.Catalog; public interface ICatalogService { public CatalogSnapshot GetCatalogSnapshot(CatalogType catalogType); + + public Task GetUpcomingLtdAsync(CancellationToken ct); } diff --git a/Turbo.Primitives/Catalog/Snapshots/CatalogProductSnapshot.cs b/Turbo.Primitives/Catalog/Snapshots/CatalogProductSnapshot.cs index 5ea56b16..2117c0f9 100644 --- a/Turbo.Primitives/Catalog/Snapshots/CatalogProductSnapshot.cs +++ b/Turbo.Primitives/Catalog/Snapshots/CatalogProductSnapshot.cs @@ -32,4 +32,10 @@ public sealed record CatalogProductSnapshot [Id(8)] public required int UniqueRemaining { get; init; } + + [Id(9)] + public int? LtdSeriesId { get; init; } + + [Id(10)] + public required string? ClassName { get; init; } } diff --git a/Turbo.Primitives/Catalog/Snapshots/LtdRaffleEntryResult.cs b/Turbo.Primitives/Catalog/Snapshots/LtdRaffleEntryResult.cs new file mode 100644 index 00000000..fa1db042 --- /dev/null +++ b/Turbo.Primitives/Catalog/Snapshots/LtdRaffleEntryResult.cs @@ -0,0 +1,44 @@ +using Orleans; +using Turbo.Primitives.Catalog.Enums; + +namespace Turbo.Primitives.Catalog.Snapshots; + +/// +/// Result of attempting to enter an LTD raffle. +/// +[GenerateSerializer, Immutable] +public sealed record LtdRaffleEntryResult +{ + [Id(0)] + public required bool Success { get; init; } + + [Id(1)] + public string? BatchId { get; init; } + + [Id(2)] + public LtdRaffleEntryError? Error { get; init; } + + [Id(3)] + public CatalogBalanceFailure? BalanceFailure { get; init; } + + public static LtdRaffleEntryResult Succeeded(string batchId) => + new() + { + Success = true, + BatchId = batchId, + Error = null, + BalanceFailure = null, + }; + + public static LtdRaffleEntryResult Failed( + LtdRaffleEntryError error, + CatalogBalanceFailure? balanceFailure = null + ) => + new() + { + Success = false, + BatchId = null, + Error = error, + BalanceFailure = balanceFailure, + }; +} diff --git a/Turbo.Primitives/Catalog/Snapshots/LtdSeriesSnapshot.cs b/Turbo.Primitives/Catalog/Snapshots/LtdSeriesSnapshot.cs new file mode 100644 index 00000000..2cd797fd --- /dev/null +++ b/Turbo.Primitives/Catalog/Snapshots/LtdSeriesSnapshot.cs @@ -0,0 +1,47 @@ +using System; +using Orleans; + +namespace Turbo.Primitives.Catalog.Snapshots; + +/// +/// Snapshot of an LTD (Limited Edition) series configuration. +/// +[GenerateSerializer, Immutable] +public sealed record LtdSeriesSnapshot +{ + [Id(0)] + public required int Id { get; init; } + + [Id(1)] + public required int CatalogProductId { get; init; } + + [Id(3)] + public required int TotalQuantity { get; init; } + + [Id(4)] + public required int RemainingQuantity { get; init; } + + [Id(6)] + public required int RaffleWindowSeconds { get; init; } + + [Id(7)] + public required bool IsActive { get; init; } + + [Id(8)] + public required bool IsRaffleFinished { get; init; } + + [Id(9)] + public DateTime? StartsAt { get; init; } + + [Id(10)] + public DateTime? EndsAt { get; init; } + + /// + /// Whether this LTD series is currently available for purchase. + /// + public bool IsAvailable => + IsActive + && RemainingQuantity > 0 + && (StartsAt == null || StartsAt <= DateTime.UtcNow) + && (EndsAt == null || EndsAt >= DateTime.UtcNow); +} diff --git a/Turbo.Primitives/Catalog/Snapshots/UpcomingLtdSnapshot.cs b/Turbo.Primitives/Catalog/Snapshots/UpcomingLtdSnapshot.cs new file mode 100644 index 00000000..384e793a --- /dev/null +++ b/Turbo.Primitives/Catalog/Snapshots/UpcomingLtdSnapshot.cs @@ -0,0 +1,22 @@ +using Orleans; + +namespace Turbo.Primitives.Catalog.Snapshots; + +/// +/// Data required for the Landing View LTD countdown. +/// +[GenerateSerializer, Immutable] +public sealed record UpcomingLtdSnapshot +{ + [Id(0)] + public required int SecondsUntil { get; init; } + + [Id(1)] + public required int PageId { get; init; } + + [Id(2)] + public required int OfferId { get; init; } + + [Id(3)] + public required string? ClassName { get; init; } +} diff --git a/Turbo.Primitives/Inventory/Grains/IInventoryGrain.Furni.cs b/Turbo.Primitives/Inventory/Grains/IInventoryGrain.Furni.cs index 2366dd47..eddf9ef2 100644 --- a/Turbo.Primitives/Inventory/Grains/IInventoryGrain.Furni.cs +++ b/Turbo.Primitives/Inventory/Grains/IInventoryGrain.Furni.cs @@ -23,6 +23,21 @@ public Task GrantCatalogOfferAsync( int quantity, CancellationToken ct ); + + /// + /// Grant an LTD furniture item with serial number to the player's inventory. + /// + /// The catalog product ID. + /// The unique serial number (e.g., 123). + /// The total series size (e.g., 500). + /// Cancellation token. + public Task GrantLtdFurnitureAsync( + int catalogProductId, + int serialNumber, + int seriesSize, + CancellationToken ct + ); + public Task GetItemSnapshotAsync( RoomObjectId itemId, CancellationToken ct diff --git a/Turbo.Primitives/Messages/Outgoing/Catalog/LimitedOfferAppearingNextMessageComposer.cs b/Turbo.Primitives/Messages/Outgoing/Catalog/LimitedOfferAppearingNextMessageComposer.cs index 1cfa8e4f..9063ae76 100644 --- a/Turbo.Primitives/Messages/Outgoing/Catalog/LimitedOfferAppearingNextMessageComposer.cs +++ b/Turbo.Primitives/Messages/Outgoing/Catalog/LimitedOfferAppearingNextMessageComposer.cs @@ -6,5 +6,15 @@ namespace Turbo.Primitives.Messages.Outgoing.Catalog; [GenerateSerializer, Immutable] public sealed record LimitedOfferAppearingNextMessageComposer : IComposer { - // TODO: add properties if/when identified + [Id(0)] + public required int AppearsInSeconds { get; init; } + + [Id(1)] + public required int PageId { get; init; } + + [Id(2)] + public required int OfferId { get; init; } + + [Id(3)] + public required string ProductClassName { get; init; } } diff --git a/Turbo.Primitives/Messages/Outgoing/Catalog/PurchaseErrorMessageComposer.cs b/Turbo.Primitives/Messages/Outgoing/Catalog/PurchaseErrorMessageComposer.cs index 999ceabd..bc10da6d 100644 --- a/Turbo.Primitives/Messages/Outgoing/Catalog/PurchaseErrorMessageComposer.cs +++ b/Turbo.Primitives/Messages/Outgoing/Catalog/PurchaseErrorMessageComposer.cs @@ -6,5 +6,6 @@ namespace Turbo.Primitives.Messages.Outgoing.Catalog; [GenerateSerializer, Immutable] public sealed record PurchaseErrorMessageComposer : IComposer { - // TODO: add properties if/when identified + [Id(0)] + public required int ErrorCode { get; init; } } diff --git a/Turbo.Primitives/Messages/Outgoing/Collectibles/LtdRaffleEnteredMessageComposer.cs b/Turbo.Primitives/Messages/Outgoing/Collectibles/LtdRaffleEnteredMessageComposer.cs new file mode 100644 index 00000000..08e69c59 --- /dev/null +++ b/Turbo.Primitives/Messages/Outgoing/Collectibles/LtdRaffleEnteredMessageComposer.cs @@ -0,0 +1,19 @@ +using Orleans; +using Turbo.Primitives.Networking; + +namespace Turbo.Primitives.Messages.Outgoing.Collectibles; + +/// +/// Sent when a player successfully enters the LTD raffle queue. +/// Shows "entering raffle" UI with dots animation in purchase confirmation. +/// Header ID: 1221 +/// +[GenerateSerializer, Immutable] +public sealed record LtdRaffleEnteredMessageComposer : IComposer +{ + /// + /// The product name/type for which the user entered the raffle. + /// + [Id(0)] + public required string ClassName { get; init; } +} diff --git a/Turbo.Primitives/Messages/Outgoing/Collectibles/LtdRaffleResultMessageComposer.cs b/Turbo.Primitives/Messages/Outgoing/Collectibles/LtdRaffleResultMessageComposer.cs new file mode 100644 index 00000000..9a641cd0 --- /dev/null +++ b/Turbo.Primitives/Messages/Outgoing/Collectibles/LtdRaffleResultMessageComposer.cs @@ -0,0 +1,28 @@ +using Orleans; +using Turbo.Primitives.Catalog.Enums; +using Turbo.Primitives.Networking; + +namespace Turbo.Primitives.Messages.Outgoing.Collectibles; + +/// +/// Sent when the LTD raffle completes with the player's result. +/// Header ID: 785 +/// ResultCode 0 = Won (triggers notification.raffle.won) +/// ResultCode 1-3 = Lost (triggers notification.raffle.lost) +/// +[GenerateSerializer, Immutable] +public sealed record LtdRaffleResultMessageComposer : IComposer +{ + /// + /// The product name. + /// + [Id(0)] + public required string ClassName { get; init; } + + /// + /// The raffle result code. + /// 0 = Won, 1+ = Lost + /// + [Id(1)] + public required LtdRaffleResultCode ResultCode { get; init; } +} diff --git a/Turbo.Primitives/Orleans/GrainFactoryExtensions.cs b/Turbo.Primitives/Orleans/GrainFactoryExtensions.cs index 11ecc5d8..c1492fb6 100644 --- a/Turbo.Primitives/Orleans/GrainFactoryExtensions.cs +++ b/Turbo.Primitives/Orleans/GrainFactoryExtensions.cs @@ -65,4 +65,7 @@ public static ICatalogPurchaseGrain GetCatalogPurchaseGrain( this IGrainFactory factory, long playerId ) => factory.GetGrain(playerId); + + public static ILtdRaffleGrain GetLtdRaffleGrain(this IGrainFactory factory, int ltdSeriesId) => + factory.GetGrain(ltdSeriesId); } diff --git a/Turbo.Primitives/TurboErrorCodeEnum.cs b/Turbo.Primitives/TurboErrorCodeEnum.cs index d9cc1d71..790ac806 100644 --- a/Turbo.Primitives/TurboErrorCodeEnum.cs +++ b/Turbo.Primitives/TurboErrorCodeEnum.cs @@ -12,6 +12,7 @@ public enum TurboErrorCodeEnum FloorItemNotFound, WallItemNotFound, FurnitureDefinitionNotFound, + CatalogProductNotFound, InvalidLogic, InvalidWired, InvalidFurnitureProductType, @@ -36,6 +37,8 @@ public static string ToDefaultMessage(this TurboErrorCodeEnum code) => TurboErrorCodeEnum.WallItemNotFound => "The specified wall item could not be found.", TurboErrorCodeEnum.FurnitureDefinitionNotFound => "The specified furniture definition could not be found.", + TurboErrorCodeEnum.CatalogProductNotFound => + "The specified catalog product could not be found.", TurboErrorCodeEnum.InvalidLogic => "The logic is not valid.", TurboErrorCodeEnum.InvalidWired => "The wired definition is not valid.", TurboErrorCodeEnum.InvalidFurnitureProductType => diff --git a/Turbo.Revisions/Revision20260112/Revision20260112.cs b/Turbo.Revisions/Revision20260112/Revision20260112.cs index 1f86dd1a..e020a909 100644 --- a/Turbo.Revisions/Revision20260112/Revision20260112.cs +++ b/Turbo.Revisions/Revision20260112/Revision20260112.cs @@ -1795,6 +1795,18 @@ public class Revision20260112 : IRevision MessageComposer.UserNftChatStylesMessageComposer ) }, + { + typeof(LtdRaffleEnteredMessageComposer), + new LtdRaffleEnteredMessageComposerSerializer( + MessageComposer.LtdRaffleEnteredMessageComposer + ) + }, + { + typeof(LtdRaffleResultMessageComposer), + new LtdRaffleResultMessageComposerSerializer( + MessageComposer.LtdRaffleResultMessageComposer + ) + }, #endregion #region FriendList diff --git a/Turbo.Revisions/Revision20260112/Serializers/Catalog/LimitedOfferAppearingNextMessageComposerSerializer.cs b/Turbo.Revisions/Revision20260112/Serializers/Catalog/LimitedOfferAppearingNextMessageComposerSerializer.cs index 8a3c63b6..2ba72cbc 100644 --- a/Turbo.Revisions/Revision20260112/Serializers/Catalog/LimitedOfferAppearingNextMessageComposerSerializer.cs +++ b/Turbo.Revisions/Revision20260112/Serializers/Catalog/LimitedOfferAppearingNextMessageComposerSerializer.cs @@ -11,6 +11,9 @@ protected override void Serialize( LimitedOfferAppearingNextMessageComposer message ) { - // + packet.WriteInteger(message.AppearsInSeconds); + packet.WriteInteger(message.PageId); + packet.WriteInteger(message.OfferId); + packet.WriteString(message.ProductClassName); } } diff --git a/Turbo.Revisions/Revision20260112/Serializers/Catalog/PurchaseErrorMessageComposerSerializer.cs b/Turbo.Revisions/Revision20260112/Serializers/Catalog/PurchaseErrorMessageComposerSerializer.cs index a73509be..be3930e7 100644 --- a/Turbo.Revisions/Revision20260112/Serializers/Catalog/PurchaseErrorMessageComposerSerializer.cs +++ b/Turbo.Revisions/Revision20260112/Serializers/Catalog/PurchaseErrorMessageComposerSerializer.cs @@ -8,6 +8,6 @@ internal class PurchaseErrorMessageComposerSerializer(int header) { protected override void Serialize(IServerPacket packet, PurchaseErrorMessageComposer message) { - // + packet.WriteInteger(message.ErrorCode); } } diff --git a/Turbo.Revisions/Revision20260112/Serializers/Collectibles/LtdRaffleEnteredMessageComposerSerializer.cs b/Turbo.Revisions/Revision20260112/Serializers/Collectibles/LtdRaffleEnteredMessageComposerSerializer.cs new file mode 100644 index 00000000..9e8ab861 --- /dev/null +++ b/Turbo.Revisions/Revision20260112/Serializers/Collectibles/LtdRaffleEnteredMessageComposerSerializer.cs @@ -0,0 +1,13 @@ +using Turbo.Primitives.Messages.Outgoing.Collectibles; +using Turbo.Primitives.Packets; + +namespace Turbo.Revisions.Revision20260112.Serializers.Collectibles; + +internal class LtdRaffleEnteredMessageComposerSerializer(int header) + : AbstractSerializer(header) +{ + protected override void Serialize(IServerPacket packet, LtdRaffleEnteredMessageComposer message) + { + packet.WriteString(message.ClassName); + } +} diff --git a/Turbo.Revisions/Revision20260112/Serializers/Collectibles/LtdRaffleResultMessageComposerSerializer.cs b/Turbo.Revisions/Revision20260112/Serializers/Collectibles/LtdRaffleResultMessageComposerSerializer.cs new file mode 100644 index 00000000..18c4f266 --- /dev/null +++ b/Turbo.Revisions/Revision20260112/Serializers/Collectibles/LtdRaffleResultMessageComposerSerializer.cs @@ -0,0 +1,14 @@ +using Turbo.Primitives.Messages.Outgoing.Collectibles; +using Turbo.Primitives.Packets; + +namespace Turbo.Revisions.Revision20260112.Serializers.Collectibles; + +internal class LtdRaffleResultMessageComposerSerializer(int header) + : AbstractSerializer(header) +{ + protected override void Serialize(IServerPacket packet, LtdRaffleResultMessageComposer message) + { + packet.WriteString(message.ClassName); + packet.WriteByte((byte)message.ResultCode); + } +} diff --git a/appsettings.json b/appsettings.json index 7510b947..efc361ee 100644 --- a/appsettings.json +++ b/appsettings.json @@ -50,6 +50,55 @@ }, "Game": {}, "Networking": {}, + "Catalog": { + "LtdRaffle": { + "BaseWeight": 1.0, + "DefaultBufferSeconds": 20, + "UsePureRandom": false, + "RandomizeSerials": true, + "LimitOnePerCustomer": false, + "BadgeCount": { + "Enabled": true, + "BonusPerUnit": 0.02, + "MaxBonus": 1.0 + }, + "AccountAgeDays": { + "Enabled": true, + "BonusPerUnit": 0.00137, + "MaxBonus": 0.5 + }, + "OnlineTimeMinutes": { + "Enabled": false, + "BonusPerUnit": 0.00005, + "MaxBonus": 0.5 + }, + "RoomCount": { + "Enabled": false, + "BonusPerUnit": 0.05, + "MaxBonus": 0.5 + }, + "FurnitureCount": { + "Enabled": false, + "BonusPerUnit": 0.001, + "MaxBonus": 0.5 + }, + "FriendCount": { + "Enabled": false, + "BonusPerUnit": 0.01, + "MaxBonus": 0.5 + }, + "RespectsReceived": { + "Enabled": false, + "BonusPerUnit": 0.005, + "MaxBonus": 0.5 + }, + "AchievementScore": { + "Enabled": false, + "BonusPerUnit": 0.0001, + "MaxBonus": 0.5 + } + } + }, "Crypto": { "KeySize": "3", "PublicKey": "86851DD364D5C5CECE3C883171CC6DDC5760779B992482BD1E20DD296888DF91B33B936A7B93F06D29E8870F703A216257DEC7C81DE0058FEA4CC5116F75E6EFC4E9113513E45357DC3FD43D4EFAB5963EF178B78BD61E81A14C603B24C8BCCE0A12230B320045498EDC29282FF0603BC7B7DAE8FC1B05B52B2F301A9DC783B7", @@ -57,4 +106,4 @@ "EnableServerToClientEncryption": true } } -} \ No newline at end of file +} From c1d7cf828efb96a04a4aa45ae595fad65c116d90 Mon Sep 17 00:00:00 2001 From: Diddyy Date: Mon, 9 Feb 2026 08:43:34 +0000 Subject: [PATCH 2/4] refactor(LtdRaffleGrain): simplify dependency usage --- Turbo.Catalog/Grains/LtdRaffleGrain.cs | 40 ++++++++++++-------------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/Turbo.Catalog/Grains/LtdRaffleGrain.cs b/Turbo.Catalog/Grains/LtdRaffleGrain.cs index 94aeaf24..a928db80 100644 --- a/Turbo.Catalog/Grains/LtdRaffleGrain.cs +++ b/Turbo.Catalog/Grains/LtdRaffleGrain.cs @@ -30,10 +30,6 @@ public sealed class LtdRaffleGrain( IOptions config ) : Grain, ILtdRaffleGrain { - private readonly IGrainFactory _grainFactory = grainFactory; - private readonly IDbContextFactory _dbCtxFactory = dbCtxFactory; - private readonly ILogger _logger = logger; - private readonly ICatalogService _catalogService = catalogService; private readonly CatalogConfig _config = config.Value; private readonly Dictionary _currentBatchEntries = []; @@ -63,13 +59,13 @@ public async Task EnterRaffleAsync(int playerId, Cancellat ); } - var snap = _catalogService.GetCatalogSnapshot(CatalogType.Normal); + var snap = catalogService.GetCatalogSnapshot(CatalogType.Normal); var product = snap.ProductsById.Values.FirstOrDefault(p => p.LtdSeriesId == _series.Id); if (product == null || !snap.OffersById.TryGetValue(product.OfferId, out var offer)) return LtdRaffleEntryResult.Failed(LtdRaffleEntryError.None); - var walletGrain = _grainFactory.GetPlayerWalletGrain(playerId); + var walletGrain = grainFactory.GetPlayerWalletGrain(playerId); var credits = await walletGrain.GetAmountForCurrencyAsync( new CurrencyKind { CurrencyType = CurrencyType.Credits }, ct @@ -109,7 +105,7 @@ public async Task EnterRaffleAsync(int playerId, Cancellat if (_config.LtdRaffle.LimitOnePerCustomer) { - await using var dbCtx = await _dbCtxFactory.CreateDbContextAsync(ct); + await using var dbCtx = await dbCtxFactory.CreateDbContextAsync(ct); var alreadyWon = await dbCtx.LtdRaffleEntries.AnyAsync( e => @@ -144,7 +140,7 @@ public async Task EnterRaffleAsync(int playerId, Cancellat _currentBatchEntries[playerId] = await CalculateWeightAsync(playerId, ct); await PersistEntryAsync(playerId, _currentBatchId, ct); - await _grainFactory + await grainFactory .GetPlayerPresenceGrain(playerId) .SendComposerAsync( new LtdRaffleEnteredMessageComposer { ClassName = product.ClassName ?? "LTD" } @@ -191,7 +187,7 @@ private async Task ExecuteRaffleAsync() private async Task TryFinalizeWinnerAsync(int playerId, string? batchId, bool isRaffle) { - await using var dbCtx = await _dbCtxFactory.CreateDbContextAsync(); + await using var dbCtx = await dbCtxFactory.CreateDbContextAsync(); await using var tx = await dbCtx.Database.BeginTransactionAsync(); try @@ -204,14 +200,14 @@ private async Task TryFinalizeWinnerAsync(int playerId, string? batchId, b .OrderBy(x => x.Id) .FirstOrDefaultAsync(); - if (series == null || series.RemainingQuantity <= 0) + if (series is not { RemainingQuantity: > 0 }) return false; - var snap = _catalogService.GetCatalogSnapshot(CatalogType.Normal); + var snap = catalogService.GetCatalogSnapshot(CatalogType.Normal); var prod = snap.ProductsById.Values.First(p => p.LtdSeriesId == series.Id); var offer = snap.OffersById[prod.OfferId]; - var debitResult = await _grainFactory + var debitResult = await grainFactory .GetPlayerWalletGrain(playerId) .TryDebitAsync(BuildDebits(offer), CancellationToken.None); @@ -246,7 +242,7 @@ private async Task TryFinalizeWinnerAsync(int playerId, string? batchId, b await dbCtx.SaveChangesAsync(); await tx.CommitAsync(); - await _grainFactory + await grainFactory .GetInventoryGrain(playerId) .GrantLtdFurnitureAsync( series.CatalogProductEntityId, @@ -257,7 +253,7 @@ await _grainFactory if (isRaffle) { - await _grainFactory + await grainFactory .GetPlayerPresenceGrain(playerId) .SendComposerAsync( new LtdRaffleResultMessageComposer @@ -272,7 +268,7 @@ await _grainFactory } catch (Exception ex) { - _logger.LogError( + logger.LogError( ex, "Failed to finalize LTD raffle winner for player {PlayerId} in series {SeriesId}", playerId, @@ -331,7 +327,7 @@ private static List BuildDebits(CatalogOfferSnapshot offer) private async Task CalculateWeightAsync(int playerId, CancellationToken ct) { - var playerGrain = _grainFactory.GetPlayerGrain(PlayerId.Parse(playerId)); + var playerGrain = grainFactory.GetPlayerGrain(PlayerId.Parse(playerId)); var summary = await playerGrain.GetSummaryAsync(ct); var profile = await playerGrain.GetExtendedProfileSnapshotAsync(ct); @@ -345,7 +341,7 @@ private async Task CalculateWeightAsync(int playerId, CancellationToken if (needsDbQuery) { - await using var db = await _dbCtxFactory.CreateDbContextAsync(ct); + await using var db = await dbCtxFactory.CreateDbContextAsync(ct); if (cfg.BadgeCount.Enabled) { @@ -435,7 +431,7 @@ private static HashSet SelectWeighted(List> entri public async Task ReloadSeriesAsync(CancellationToken ct) { - await using var db = await _dbCtxFactory.CreateDbContextAsync(ct); + await using var db = await dbCtxFactory.CreateDbContextAsync(ct); var entity = await db .LtdSeries.AsNoTracking() @@ -461,7 +457,7 @@ public async Task ReloadSeriesAsync(CancellationToken ct) private async Task PersistFinishedAsync() { - await using var db = await _dbCtxFactory.CreateDbContextAsync(); + await using var db = await dbCtxFactory.CreateDbContextAsync(); await db .LtdSeries.Where(s => s.Id == (int)this.GetPrimaryKeyLong()) @@ -470,11 +466,11 @@ await db private async Task NotifyLoserAsync(int playerId, LtdRaffleResultCode resultCode) { - var product = _catalogService + var product = catalogService .GetCatalogSnapshot(CatalogType.Normal) .ProductsById.Values.FirstOrDefault(p => p.LtdSeriesId == _series?.Id); - await _grainFactory + await grainFactory .GetPlayerPresenceGrain(playerId) .SendComposerAsync( new LtdRaffleResultMessageComposer @@ -487,7 +483,7 @@ await _grainFactory private async Task PersistEntryAsync(int playerId, string batchId, CancellationToken ct) { - await using var db = await _dbCtxFactory.CreateDbContextAsync(ct); + await using var db = await dbCtxFactory.CreateDbContextAsync(ct); db.LtdRaffleEntries.Add( new LtdRaffleEntryEntity From 4a7f71f9ac03b6f4fdd9db205b84e3ab6efaa47c Mon Sep 17 00:00:00 2001 From: Diddyy Date: Mon, 9 Feb 2026 09:10:45 +0000 Subject: [PATCH 3/4] feat(LtdRaffleGrain): enhance deactivation process and update loser handling --- Turbo.Catalog/Grains/LtdRaffleGrain.cs | 42 +++++++++++++++++++ .../PurchaseFromCatalogMessageHandler.cs | 2 +- 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/Turbo.Catalog/Grains/LtdRaffleGrain.cs b/Turbo.Catalog/Grains/LtdRaffleGrain.cs index a928db80..7dbfaa93 100644 --- a/Turbo.Catalog/Grains/LtdRaffleGrain.cs +++ b/Turbo.Catalog/Grains/LtdRaffleGrain.cs @@ -48,6 +48,28 @@ public override async Task OnActivateAsync(CancellationToken ct) _raffleFinished = _series.IsRaffleFinished; } + public override async Task OnDeactivateAsync(DeactivationReason reason, CancellationToken ct) + { + _raffleTimer?.Dispose(); + _raffleTimer = null; + + if (_currentBatchEntries.Count > 0) + { + try + { + await ExecuteRaffleAsync(); + } + catch (Exception ex) + { + logger.LogError( + ex, + "Failed to execute raffle during deactivation for series {SeriesId}", + this.GetPrimaryKeyLong() + ); + } + } + } + public async Task EnterRaffleAsync(int playerId, CancellationToken ct) { if (_series is not { IsAvailable: true }) @@ -164,6 +186,7 @@ private async Task ExecuteRaffleAsync() _currentBatchEntries.Clear(); _isInBufferPeriod = false; _raffleFinished = true; + _raffleTimer?.Dispose(); _raffleTimer = null; await PersistFinishedAsync(); @@ -174,12 +197,31 @@ private async Task ExecuteRaffleAsync() ? [.. entries.OrderBy(_ => Random.Shared.Next()).Take(winnersCount).Select(e => e.Key)] : SelectWeighted(entries, winnersCount); + var loserIds = new List(); + foreach (var entry in entries) { if (winners.Contains(entry.Key)) await TryFinalizeWinnerAsync(entry.Key, batchId, true); else + { + loserIds.Add(entry.Key); await NotifyLoserAsync(entry.Key, LtdRaffleResultCode.Lost); + } + } + + if (loserIds.Count > 0) + { + await using var db = await dbCtxFactory.CreateDbContextAsync(CancellationToken.None); + + await db + .LtdRaffleEntries.Where(e => + e.BatchId == batchId && loserIds.Contains(e.PlayerEntityId) + ) + .ExecuteUpdateAsync(u => + u.SetProperty(e => e.Result, "lost") + .SetProperty(e => e.ProcessedAt, DateTime.UtcNow) + ); } await ReloadSeriesAsync(CancellationToken.None); diff --git a/Turbo.PacketHandlers/Catalog/PurchaseFromCatalogMessageHandler.cs b/Turbo.PacketHandlers/Catalog/PurchaseFromCatalogMessageHandler.cs index 0c2b6aaa..e94045fd 100644 --- a/Turbo.PacketHandlers/Catalog/PurchaseFromCatalogMessageHandler.cs +++ b/Turbo.PacketHandlers/Catalog/PurchaseFromCatalogMessageHandler.cs @@ -116,7 +116,7 @@ CancellationToken ct { // Use the series ID if available (Perfect Design), otherwise fallback to Product ID var seriesId = ltdProduct.LtdSeriesId ?? ltdProduct.Id; - var ltdRaffleGrain = _grainFactory.GetGrain(seriesId); + var ltdRaffleGrain = _grainFactory.GetLtdRaffleGrain(seriesId); var result = await ltdRaffleGrain.EnterRaffleAsync(ctx.PlayerId, ct).ConfigureAwait(false); From 345198bd56dd015cb1b1fa91a50dd2283dc3c0f0 Mon Sep 17 00:00:00 2001 From: Diddyy Date: Mon, 9 Feb 2026 12:02:56 +0000 Subject: [PATCH 4/4] feat(LtdRaffleGrain): add max entries per batch limit and optimize loser notifications --- Turbo.Catalog/Configuration/CatalogConfig.cs | 6 +++ Turbo.Catalog/Grains/LtdRaffleGrain.cs | 38 +++++++++++++------ .../PurchaseFromCatalogMessageHandler.cs | 12 ------ 3 files changed, 32 insertions(+), 24 deletions(-) diff --git a/Turbo.Catalog/Configuration/CatalogConfig.cs b/Turbo.Catalog/Configuration/CatalogConfig.cs index 50a1c477..8c26e573 100644 --- a/Turbo.Catalog/Configuration/CatalogConfig.cs +++ b/Turbo.Catalog/Configuration/CatalogConfig.cs @@ -46,6 +46,12 @@ public class LtdRaffleWeightConfig /// public bool LimitOnePerCustomer { get; set; } = true; + /// + /// Maximum number of entries accepted per raffle batch. + /// Prevents unbounded memory growth during the buffer window. + /// + public int MaxEntriesPerBatch { get; set; } = 5000; + /// /// Badge count weighting configuration. /// diff --git a/Turbo.Catalog/Grains/LtdRaffleGrain.cs b/Turbo.Catalog/Grains/LtdRaffleGrain.cs index 7dbfaa93..170dd826 100644 --- a/Turbo.Catalog/Grains/LtdRaffleGrain.cs +++ b/Turbo.Catalog/Grains/LtdRaffleGrain.cs @@ -14,6 +14,7 @@ using Turbo.Primitives.Catalog.Enums; using Turbo.Primitives.Catalog.Grains; using Turbo.Primitives.Catalog.Snapshots; +using Turbo.Primitives.Messages.Outgoing.Catalog; using Turbo.Primitives.Messages.Outgoing.Collectibles; using Turbo.Primitives.Orleans; using Turbo.Primitives.Players; @@ -159,6 +160,9 @@ public async Task EnterRaffleAsync(int playerId, Cancellat if (!_isInBufferPeriod) return LtdRaffleEntryResult.Failed(LtdRaffleEntryError.RaffleProcessing); + if (_currentBatchEntries.Count >= _config.LtdRaffle.MaxEntriesPerBatch) + return LtdRaffleEntryResult.Failed(LtdRaffleEntryError.RaffleProcessing); + _currentBatchEntries[playerId] = await CalculateWeightAsync(playerId, ct); await PersistEntryAsync(playerId, _currentBatchId, ct); @@ -199,15 +203,21 @@ private async Task ExecuteRaffleAsync() var loserIds = new List(); + // Winners must be sequential (row lock + quantity decrement per winner) foreach (var entry in entries) { if (winners.Contains(entry.Key)) await TryFinalizeWinnerAsync(entry.Key, batchId, true); else - { loserIds.Add(entry.Key); - await NotifyLoserAsync(entry.Key, LtdRaffleResultCode.Lost); - } + } + + // Loser notifications go to different presence grains — parallelize + if (loserIds.Count > 0) + { + await Task.WhenAll( + loserIds.Select(id => NotifyLoserAsync(id, LtdRaffleResultCode.Lost)) + ); } if (loserIds.Count > 0) @@ -293,17 +303,21 @@ await grainFactory CancellationToken.None ); + var presence = grainFactory.GetPlayerPresenceGrain(playerId); + if (isRaffle) { - await grainFactory - .GetPlayerPresenceGrain(playerId) - .SendComposerAsync( - new LtdRaffleResultMessageComposer - { - ClassName = prod.ClassName ?? "LTD", - ResultCode = LtdRaffleResultCode.Won, - } - ); + await presence.SendComposerAsync( + new LtdRaffleResultMessageComposer + { + ClassName = prod.ClassName ?? "LTD", + ResultCode = LtdRaffleResultCode.Won, + } + ); + } + else + { + await presence.SendComposerAsync(new PurchaseOKMessageComposer { Offer = offer }); } return true; diff --git a/Turbo.PacketHandlers/Catalog/PurchaseFromCatalogMessageHandler.cs b/Turbo.PacketHandlers/Catalog/PurchaseFromCatalogMessageHandler.cs index e94045fd..27453494 100644 --- a/Turbo.PacketHandlers/Catalog/PurchaseFromCatalogMessageHandler.cs +++ b/Turbo.PacketHandlers/Catalog/PurchaseFromCatalogMessageHandler.cs @@ -121,19 +121,7 @@ CancellationToken ct var result = await ltdRaffleGrain.EnterRaffleAsync(ctx.PlayerId, ct).ConfigureAwait(false); if (result.Success) - { - // If it was an instant buy (FCFS), send standard PurchaseOK - if (result.BatchId == "instant") - { - var snapshot = _catalogService.GetCatalogSnapshot(CatalogType.Normal); - if (snapshot.OffersById.TryGetValue(ltdProduct.OfferId, out var offer)) - { - await ctx.SendComposerAsync(new PurchaseOKMessageComposer { Offer = offer }, ct) - .ConfigureAwait(false); - } - } return; - } // 1. Check for specialized balance alerts first if (result.BalanceFailure != null)