From 07af825e855f9729c8f1c06fdefc3e61518b9766 Mon Sep 17 00:00:00 2001 From: Mosch0512 Date: Fri, 13 Nov 2026 15:52:28 +0100 Subject: [PATCH 1/4] feat(persistence): characters get a list of items which wait for space in the inventory A WaitingItem has the item, the reason why it waits (for now only NoSpaceWhenLoaded) and when it was added. It owns its item like a quest reward owns its item, so no other item changes how it's linked. Migration AddWaitingItems, compiled models of the EntityDataContext, AccountContext and TradeContext, and the account data source of the admin panel knows the new type. Captions and descriptions for the admin panel in English, German and Chinese. --- src/DataModel/Entities/Character.cs | 6 + src/DataModel/Entities/WaitingItem.cs | 34 + src/DataModel/Entities/WaitingItemReason.cs | 17 + .../Properties/ModelResources.de.resx | 45 + src/DataModel/Properties/ModelResources.resx | 45 + .../Properties/ModelResources.zh-CN.resx | 45 + src/Persistence/AccountDataSource.cs | 3 +- .../BasicModel/Character.Generated.cs | 21 + .../BasicModel/WaitingItem.Generated.cs | 67 + .../AccountContextModelBuilder.cs | 6 +- .../AccountContext/CharacterEntityType.cs | 2 +- .../AccountContext/WaitingItemEntityType.cs | 135 + .../EntityDataContext/CharacterEntityType.cs | 2 +- .../EntityDataContextModelBuilder.cs | 6 +- .../WaitingItemEntityType.cs | 135 + .../TradeContext/CharacterEntityType.cs | 2 +- .../TradeContext/TradeContextModelBuilder.cs | 6 +- .../TradeContext/WaitingItemEntityType.cs | 135 + ...20261114172314_AddWaitingItems.Designer.cs | 6938 +++++++++++++++++ .../20261114172314_AddWaitingItems.cs | 66 + .../EntityDataContextModelSnapshot.cs | 45 + .../Model/BasicModelConverter.Generated.cs | 25 + .../Model/Character.Generated.cs | 9 + .../Model/ExtendedTypeContext.Generated.cs | 3 + .../Model/WaitingItem.Generated.cs | 77 + 25 files changed, 7868 insertions(+), 7 deletions(-) create mode 100644 src/DataModel/Entities/WaitingItem.cs create mode 100644 src/DataModel/Entities/WaitingItemReason.cs create mode 100644 src/Persistence/BasicModel/WaitingItem.Generated.cs create mode 100644 src/Persistence/EntityFramework/CompiledModels/AccountContext/WaitingItemEntityType.cs create mode 100644 src/Persistence/EntityFramework/CompiledModels/EntityDataContext/WaitingItemEntityType.cs create mode 100644 src/Persistence/EntityFramework/CompiledModels/TradeContext/WaitingItemEntityType.cs create mode 100644 src/Persistence/EntityFramework/Migrations/20261114172314_AddWaitingItems.Designer.cs create mode 100644 src/Persistence/EntityFramework/Migrations/20261114172314_AddWaitingItems.cs create mode 100644 src/Persistence/EntityFramework/Model/WaitingItem.Generated.cs diff --git a/src/DataModel/Entities/Character.cs b/src/DataModel/Entities/Character.cs index 52851639d4..31475f9e56 100644 --- a/src/DataModel/Entities/Character.cs +++ b/src/DataModel/Entities/Character.cs @@ -263,6 +263,12 @@ public class Character [MemberOfAggregate] public virtual ICollection QuestStates { get; protected set; } = null!; + /// + /// Gets or sets the items which wait for space in the . + /// + [MemberOfAggregate] + public virtual ICollection WaitingItems { get; protected set; } = null!; + /// public override string ToString() { diff --git a/src/DataModel/Entities/WaitingItem.cs b/src/DataModel/Entities/WaitingItem.cs new file mode 100644 index 0000000000..ec6c3894e2 --- /dev/null +++ b/src/DataModel/Entities/WaitingItem.cs @@ -0,0 +1,34 @@ +// +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace MUnique.OpenMU.DataModel.Entities; + +/// +/// An item of a character which waits for space in the inventory. +/// The game server puts it into the inventory as soon as it fits there. +/// +public class WaitingItem +{ + /// + /// Gets or sets the item. + /// + [MemberOfAggregate] + public virtual Item? Item { get; set; } + + /// + /// Gets or sets the reason why the item waits. + /// + public WaitingItemReason Reason { get; set; } + + /// + /// Gets or sets the time when the item was added to the waiting items. + /// + public DateTime AddedAt { get; set; } + + /// + public override string ToString() + { + return $"{this.Item} ({this.Reason})"; + } +} diff --git a/src/DataModel/Entities/WaitingItemReason.cs b/src/DataModel/Entities/WaitingItemReason.cs new file mode 100644 index 0000000000..dcfb1ff4ba --- /dev/null +++ b/src/DataModel/Entities/WaitingItemReason.cs @@ -0,0 +1,17 @@ +// +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace MUnique.OpenMU.DataModel.Entities; + +/// +/// The reason why a waits for space in the inventory. +/// +public enum WaitingItemReason +{ + /// + /// The item found no free place when its storage was loaded, + /// e.g. because the size of its definition was changed in the data. + /// + NoSpaceWhenLoaded, +} diff --git a/src/DataModel/Properties/ModelResources.de.resx b/src/DataModel/Properties/ModelResources.de.resx index a2d44c47bb..51b1b16cff 100644 --- a/src/DataModel/Properties/ModelResources.de.resx +++ b/src/DataModel/Properties/ModelResources.de.resx @@ -3621,4 +3621,49 @@ Ob Spieler diesen Warp verwenden können. Er schränkt Spieler nur ein, wenn er ausgeschaltet ist: Dann ist der Warp nicht in der Warp-Liste des Clients, und die Warp-Anfrage eines Spielers wird abgelehnt; Game Master können ihn weiterhin verwenden. + + Wartende Gegenstände + + + Gegenstände, die in ihrem Lagerplatz keinen Platz fanden und auf Platz im Inventar warten. Sie werden hineingelegt, sobald sie passen. + + + Wartender Gegenstand + + + Wartende Gegenstände + + + Ein Gegenstand, der auf Platz im Inventar eines Charakters wartet. + + + Gegenstand + + + Der wartende Gegenstand. + + + Grund + + + Warum der Gegenstand wartet. + + + Hinzugefügt am + + + Seit wann der Gegenstand wartet (UTC). Die ältesten Gegenstände werden zuerst ins Inventar gelegt. + + + Grund für wartenden Gegenstand + + + Warum ein Gegenstand auf Platz im Inventar wartet. + + + Kein Platz beim Laden + + + Der Gegenstand fand beim Laden seines Lagerplatzes keinen Platz, z. B. weil die Größe seiner Definition gewachsen ist. + diff --git a/src/DataModel/Properties/ModelResources.resx b/src/DataModel/Properties/ModelResources.resx index 43bf68d905..3ca400b045 100644 --- a/src/DataModel/Properties/ModelResources.resx +++ b/src/DataModel/Properties/ModelResources.resx @@ -6529,4 +6529,49 @@ Whether players can use this warp. It only restricts players when it's off: then the warp isn't in the warp list of the client and the warp request of a player is refused, while game masters can still use it. + + Waiting Items + + + Items which found no place in their storage and wait for space in the inventory. They are put into it as soon as they fit. + + + Waiting Item + + + Waiting Items + + + An item which waits for space in the inventory of a character. + + + Item + + + The item which waits. + + + Reason + + + Why the item waits. + + + Added At + + + When the item started to wait (UTC). The oldest items are put into the inventory first. + + + Waiting Item Reason + + + Why an item waits for space in the inventory. + + + No Space When Loaded + + + The item found no place when its storage was loaded, e.g. because the size of its definition grew. + diff --git a/src/DataModel/Properties/ModelResources.zh-CN.resx b/src/DataModel/Properties/ModelResources.zh-CN.resx index 4b808ffc04..cf1d1574ce 100644 --- a/src/DataModel/Properties/ModelResources.zh-CN.resx +++ b/src/DataModel/Properties/ModelResources.zh-CN.resx @@ -6460,4 +6460,49 @@ 此公式适用的物品需求属性。 + + 等待中的物品 + + + 在其存放位置找不到空间、正在等待背包空间的物品。有足够空间时会自动放入背包。 + + + 等待中的物品 + + + 等待中的物品 + + + 正在等待角色背包空间的物品。 + + + 物品 + + + 正在等待的物品。 + + + 原因 + + + 物品等待的原因。 + + + 添加时间 + + + 物品开始等待的时间(UTC)。最早的物品最先放入背包。 + + + 物品等待原因 + + + 物品等待背包空间的原因。 + + + 加载时无空间 + + + 加载存放位置时物品找不到空间,例如其定义的尺寸变大了。 + diff --git a/src/Persistence/AccountDataSource.cs b/src/Persistence/AccountDataSource.cs index 829e6f357e..61ce9cee8c 100644 --- a/src/Persistence/AccountDataSource.cs +++ b/src/Persistence/AccountDataSource.cs @@ -21,12 +21,13 @@ public sealed class AccountDataSource : DataSourceBase { typeof(Account), a => Enumerable.Repeat(a, 1) }, { typeof(Character), a => a.Characters }, { typeof(ItemStorage), a => a.Characters.Select(c => c.Inventory).Concat(Enumerable.Repeat(a.Vault, 1)).Where(i => i is not null) }, - { typeof(Item), a => a.Characters.SelectMany(c => c.Inventory?.Items ?? Enumerable.Empty()).Concat(a.Vault?.Items ?? Enumerable.Empty()) }, + { typeof(Item), a => a.Characters.SelectMany(c => c.Inventory?.Items ?? Enumerable.Empty()).Concat(a.Vault?.Items ?? Enumerable.Empty()).Concat(a.Characters.SelectMany(c => c.WaitingItems).Select(w => w.Item).OfType()) }, { typeof(StatAttribute), a => a.Characters.SelectMany(c => c.Attributes) }, { typeof(LetterHeader), a => a.Characters.SelectMany(c => c.Letters) }, { typeof(SkillEntry), a => a.Characters.SelectMany(c => c.LearnedSkills) }, { typeof(CharacterQuestState), a => a.Characters.SelectMany(c => c.QuestStates) }, { typeof(QuestMonsterKillRequirementState), a => a.Characters.SelectMany(c => c.QuestStates).SelectMany(q => q.RequirementStates) }, + { typeof(WaitingItem), a => a.Characters.SelectMany(c => c.WaitingItems) }, }.AsReadOnly(); private readonly IDataSource _gameConfigurationSource; diff --git a/src/Persistence/BasicModel/Character.Generated.cs b/src/Persistence/BasicModel/Character.Generated.cs index a854766a33..c7a5d38e03 100644 --- a/src/Persistence/BasicModel/Character.Generated.cs +++ b/src/Persistence/BasicModel/Character.Generated.cs @@ -127,6 +127,27 @@ protected set } } + /// + /// Gets the raw collection of . + /// + [System.Text.Json.Serialization.JsonPropertyName("waitingItems")] + public ICollection RawWaitingItems { get; } = new List(); + + /// + [System.Text.Json.Serialization.JsonIgnore] + public override ICollection WaitingItems + { + get => base.WaitingItems ??= new CollectionAdapter(this.RawWaitingItems); + protected set + { + this.WaitingItems.Clear(); + foreach (var item in value) + { + this.WaitingItems.Add(item); + } + } + } + /// /// Gets the raw object of . /// diff --git a/src/Persistence/BasicModel/WaitingItem.Generated.cs b/src/Persistence/BasicModel/WaitingItem.Generated.cs new file mode 100644 index 0000000000..a5fb837998 --- /dev/null +++ b/src/Persistence/BasicModel/WaitingItem.Generated.cs @@ -0,0 +1,67 @@ +// +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +//------------------------------------------------------------------------------ +// +// This source code was auto-generated by a roslyn code generator. +// +//------------------------------------------------------------------------------ + +// ReSharper disable All + +namespace MUnique.OpenMU.Persistence.BasicModel; + +using MUnique.OpenMU.Persistence.Json; + +/// +/// A plain implementation of . +/// +public partial class WaitingItem : MUnique.OpenMU.DataModel.Entities.WaitingItem, IIdentifiable, IConvertibleTo +{ + + /// + /// Gets or sets the identifier of this instance. + /// + public Guid Id { get; set; } + + /// + /// Gets the raw object of . + /// + [System.Text.Json.Serialization.JsonPropertyName("item")] + public Item RawItem + { + get => base.Item as Item; + set => base.Item = value; + } + + /// + [System.Text.Json.Serialization.JsonIgnore] + public override MUnique.OpenMU.DataModel.Entities.Item Item + { + get => base.Item; + set => base.Item = value; + } + + + /// + public override bool Equals(object obj) + { + var baseObject = obj as IIdentifiable; + if (baseObject != null) + { + return baseObject.Id == this.Id; + } + + return base.Equals(obj); + } + + /// + public override int GetHashCode() + { + return this.Id.GetHashCode(); + } + + /// + public WaitingItem Convert() => this; +} diff --git a/src/Persistence/EntityFramework/CompiledModels/AccountContext/AccountContextModelBuilder.cs b/src/Persistence/EntityFramework/CompiledModels/AccountContext/AccountContextModelBuilder.cs index 0a10a6b467..2fe3def969 100644 --- a/src/Persistence/EntityFramework/CompiledModels/AccountContext/AccountContextModelBuilder.cs +++ b/src/Persistence/EntityFramework/CompiledModels/AccountContext/AccountContextModelBuilder.cs @@ -12,7 +12,7 @@ namespace MUnique.OpenMU.Persistence.EntityFramework.CompiledModels.ForAccountCo public partial class AccountContextModel { private AccountContextModel() - : base(skipDetectChanges: false, modelId: new Guid("58d27c62-721e-444e-a4f1-8bdbbd4d4ad7"), entityTypeCount: 49) + : base(skipDetectChanges: false, modelId: new Guid("bd8cf6b1-c27c-4863-a680-567e553dc2f8"), entityTypeCount: 50) { } @@ -67,6 +67,7 @@ partial void Initialize() var skillCharacterClass = SkillCharacterClassEntityType.Create(this); var skillEntry = SkillEntryEntityType.Create(this); var statAttribute = StatAttributeEntityType.Create(this); + var waitingItem = WaitingItemEntityType.Create(this); AccountEntityType.CreateForeignKey1(account, itemStorage); AccountAttributeGrantEntityType.CreateForeignKey1(accountAttributeGrant, account); @@ -99,6 +100,8 @@ partial void Initialize() SkillEntryEntityType.CreateForeignKey1(skillEntry, character); StatAttributeEntityType.CreateForeignKey1(statAttribute, account); StatAttributeEntityType.CreateForeignKey2(statAttribute, character); + WaitingItemEntityType.CreateForeignKey1(waitingItem, character); + WaitingItemEntityType.CreateForeignKey2(waitingItem, item); AccountEntityType.CreateAnnotations(account); AccountAttributeGrantEntityType.CreateAnnotations(accountAttributeGrant); @@ -149,6 +152,7 @@ partial void Initialize() SkillCharacterClassEntityType.CreateAnnotations(skillCharacterClass); SkillEntryEntityType.CreateAnnotations(skillEntry); StatAttributeEntityType.CreateAnnotations(statAttribute); + WaitingItemEntityType.CreateAnnotations(waitingItem); AddAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); AddAnnotation("ProductVersion", "10.0.2"); diff --git a/src/Persistence/EntityFramework/CompiledModels/AccountContext/CharacterEntityType.cs b/src/Persistence/EntityFramework/CompiledModels/AccountContext/CharacterEntityType.cs index b3ca2acf65..47252b8987 100644 --- a/src/Persistence/EntityFramework/CompiledModels/AccountContext/CharacterEntityType.cs +++ b/src/Persistence/EntityFramework/CompiledModels/AccountContext/CharacterEntityType.cs @@ -24,7 +24,7 @@ public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType bas typeof(MUnique.OpenMU.Persistence.EntityFramework.Model.Character), baseEntityType, propertyCount: 26, - navigationCount: 6, + navigationCount: 7, foreignKeyCount: 2, unnamedIndexCount: 3, keyCount: 1); diff --git a/src/Persistence/EntityFramework/CompiledModels/AccountContext/WaitingItemEntityType.cs b/src/Persistence/EntityFramework/CompiledModels/AccountContext/WaitingItemEntityType.cs new file mode 100644 index 0000000000..beab9efe7f --- /dev/null +++ b/src/Persistence/EntityFramework/CompiledModels/AccountContext/WaitingItemEntityType.cs @@ -0,0 +1,135 @@ +// +using System; +using System.Collections.Generic; +using System.Reflection; +using MUnique.OpenMU.DataModel.Entities; +using MUnique.OpenMU.Persistence.EntityFramework.Model; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#pragma warning disable 219, 612, 618 +#nullable disable + +namespace MUnique.OpenMU.Persistence.EntityFramework.CompiledModels.ForAccountContext +{ + [EntityFrameworkInternal] + public partial class WaitingItemEntityType + { + public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType baseEntityType = null) + { + var runtimeEntityType = model.AddEntityType( + "MUnique.OpenMU.Persistence.EntityFramework.Model.WaitingItem", + typeof(MUnique.OpenMU.Persistence.EntityFramework.Model.WaitingItem), + baseEntityType, + propertyCount: 5, + navigationCount: 1, + foreignKeyCount: 2, + unnamedIndexCount: 2, + keyCount: 1); + + var id = runtimeEntityType.AddProperty( + "Id", + typeof(Guid), + propertyInfo: typeof(MUnique.OpenMU.Persistence.EntityFramework.Model.WaitingItem).GetProperty("Id", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(MUnique.OpenMU.Persistence.EntityFramework.Model.WaitingItem).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + valueGenerated: ValueGenerated.OnAdd, + afterSaveBehavior: PropertySaveBehavior.Throw, + valueGeneratorFactory: new MUnique.OpenMU.Persistence.EntityFramework.GuidV7ValueGeneratorFactory().Create, + sentinel: new Guid("00000000-0000-0000-0000-000000000000")); + id.AddAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.None); + + var addedAt = runtimeEntityType.AddProperty( + "AddedAt", + typeof(DateTime), + propertyInfo: typeof(MUnique.OpenMU.DataModel.Entities.WaitingItem).GetProperty("AddedAt", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(MUnique.OpenMU.DataModel.Entities.WaitingItem).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + sentinel: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)); + addedAt.AddAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.None); + + var characterId = runtimeEntityType.AddProperty( + "CharacterId", + typeof(Guid?), + nullable: true); + characterId.AddAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.None); + + var itemId = runtimeEntityType.AddProperty( + "ItemId", + typeof(Guid?), + propertyInfo: typeof(MUnique.OpenMU.Persistence.EntityFramework.Model.WaitingItem).GetProperty("ItemId", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(MUnique.OpenMU.Persistence.EntityFramework.Model.WaitingItem).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); + itemId.AddAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.None); + + var reason = runtimeEntityType.AddProperty( + "Reason", + typeof(MUnique.OpenMU.DataModel.Entities.WaitingItemReason), + propertyInfo: typeof(MUnique.OpenMU.DataModel.Entities.WaitingItem).GetProperty("Reason", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(MUnique.OpenMU.DataModel.Entities.WaitingItem).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)); + reason.SetSentinelFromProviderValue(0); + reason.AddAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.None); + + var key = runtimeEntityType.AddKey( + new[] { id }); + runtimeEntityType.SetPrimaryKey(key); + + var index = runtimeEntityType.AddIndex( + new[] { characterId }); + + var index0 = runtimeEntityType.AddIndex( + new[] { itemId }, + unique: true); + + return runtimeEntityType; + } + + public static RuntimeForeignKey CreateForeignKey1(RuntimeEntityType declaringEntityType, RuntimeEntityType principalEntityType) + { + var runtimeForeignKey = declaringEntityType.AddForeignKey(new[] { declaringEntityType.FindProperty("CharacterId") }, + principalEntityType.FindKey(new[] { principalEntityType.FindProperty("Id") }), + principalEntityType, + deleteBehavior: DeleteBehavior.Cascade); + + var rawWaitingItems = principalEntityType.AddNavigation("RawWaitingItems", + runtimeForeignKey, + onDependent: false, + typeof(System.Collections.Generic.ICollection), + propertyInfo: typeof(MUnique.OpenMU.Persistence.EntityFramework.Model.Character).GetProperty("RawWaitingItems", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(MUnique.OpenMU.Persistence.EntityFramework.Model.Character).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)); + + return runtimeForeignKey; + } + + public static RuntimeForeignKey CreateForeignKey2(RuntimeEntityType declaringEntityType, RuntimeEntityType principalEntityType) + { + var runtimeForeignKey = declaringEntityType.AddForeignKey(new[] { declaringEntityType.FindProperty("ItemId") }, + principalEntityType.FindKey(new[] { principalEntityType.FindProperty("Id") }), + principalEntityType, + deleteBehavior: DeleteBehavior.Cascade, + unique: true); + + var rawItem = declaringEntityType.AddNavigation("RawItem", + runtimeForeignKey, + onDependent: true, + typeof(MUnique.OpenMU.Persistence.EntityFramework.Model.Item), + propertyInfo: typeof(MUnique.OpenMU.Persistence.EntityFramework.Model.WaitingItem).GetProperty("RawItem", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)); + + return runtimeForeignKey; + } + + public static void CreateAnnotations(RuntimeEntityType runtimeEntityType) + { + runtimeEntityType.AddAnnotation("Relational:FunctionName", null); + runtimeEntityType.AddAnnotation("Relational:Schema", "data"); + runtimeEntityType.AddAnnotation("Relational:SqlQuery", null); + runtimeEntityType.AddAnnotation("Relational:TableName", "WaitingItem"); + runtimeEntityType.AddAnnotation("Relational:ViewName", null); + runtimeEntityType.AddAnnotation("Relational:ViewSchema", null); + + Customize(runtimeEntityType); + } + + static partial void Customize(RuntimeEntityType runtimeEntityType); + } +} diff --git a/src/Persistence/EntityFramework/CompiledModels/EntityDataContext/CharacterEntityType.cs b/src/Persistence/EntityFramework/CompiledModels/EntityDataContext/CharacterEntityType.cs index ec260c68cd..4e18f30672 100644 --- a/src/Persistence/EntityFramework/CompiledModels/EntityDataContext/CharacterEntityType.cs +++ b/src/Persistence/EntityFramework/CompiledModels/EntityDataContext/CharacterEntityType.cs @@ -24,7 +24,7 @@ public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType bas typeof(MUnique.OpenMU.Persistence.EntityFramework.Model.Character), baseEntityType, propertyCount: 26, - navigationCount: 8, + navigationCount: 9, foreignKeyCount: 4, unnamedIndexCount: 5, keyCount: 1); diff --git a/src/Persistence/EntityFramework/CompiledModels/EntityDataContext/EntityDataContextModelBuilder.cs b/src/Persistence/EntityFramework/CompiledModels/EntityDataContext/EntityDataContextModelBuilder.cs index 4d3231ed53..a6c53da2a6 100644 --- a/src/Persistence/EntityFramework/CompiledModels/EntityDataContext/EntityDataContextModelBuilder.cs +++ b/src/Persistence/EntityFramework/CompiledModels/EntityDataContext/EntityDataContextModelBuilder.cs @@ -12,7 +12,7 @@ namespace MUnique.OpenMU.Persistence.EntityFramework.CompiledModels.ForEntityDat public partial class EntityDataContextModel { private EntityDataContextModel() - : base(skipDetectChanges: false, modelId: new Guid("d56bc8cd-52a2-42a0-a760-fd4e9e56ff02"), entityTypeCount: 129) + : base(skipDetectChanges: false, modelId: new Guid("265f0f2e-638f-4ec5-8ec3-a189197d916a"), entityTypeCount: 130) { } @@ -146,6 +146,7 @@ partial void Initialize() var statAttribute = StatAttributeEntityType.Create(this); var statAttributeDefinition = StatAttributeDefinitionEntityType.Create(this); var systemConfiguration = SystemConfigurationEntityType.Create(this); + var waitingItem = WaitingItemEntityType.Create(this); var warpInfo = WarpInfoEntityType.Create(this); AccountEntityType.CreateForeignKey1(account, itemStorage); @@ -401,6 +402,8 @@ partial void Initialize() StatAttributeEntityType.CreateForeignKey3(statAttribute, attributeDefinition); StatAttributeDefinitionEntityType.CreateForeignKey1(statAttributeDefinition, attributeDefinition); StatAttributeDefinitionEntityType.CreateForeignKey2(statAttributeDefinition, characterClass); + WaitingItemEntityType.CreateForeignKey1(waitingItem, character); + WaitingItemEntityType.CreateForeignKey2(waitingItem, item); WarpInfoEntityType.CreateForeignKey1(warpInfo, gameConfiguration); WarpInfoEntityType.CreateForeignKey2(warpInfo, exitGate); @@ -532,6 +535,7 @@ partial void Initialize() StatAttributeEntityType.CreateAnnotations(statAttribute); StatAttributeDefinitionEntityType.CreateAnnotations(statAttributeDefinition); SystemConfigurationEntityType.CreateAnnotations(systemConfiguration); + WaitingItemEntityType.CreateAnnotations(waitingItem); WarpInfoEntityType.CreateAnnotations(warpInfo); AddAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); diff --git a/src/Persistence/EntityFramework/CompiledModels/EntityDataContext/WaitingItemEntityType.cs b/src/Persistence/EntityFramework/CompiledModels/EntityDataContext/WaitingItemEntityType.cs new file mode 100644 index 0000000000..645ef1e0a1 --- /dev/null +++ b/src/Persistence/EntityFramework/CompiledModels/EntityDataContext/WaitingItemEntityType.cs @@ -0,0 +1,135 @@ +// +using System; +using System.Collections.Generic; +using System.Reflection; +using MUnique.OpenMU.DataModel.Entities; +using MUnique.OpenMU.Persistence.EntityFramework.Model; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#pragma warning disable 219, 612, 618 +#nullable disable + +namespace MUnique.OpenMU.Persistence.EntityFramework.CompiledModels.ForEntityDataContext +{ + [EntityFrameworkInternal] + public partial class WaitingItemEntityType + { + public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType baseEntityType = null) + { + var runtimeEntityType = model.AddEntityType( + "MUnique.OpenMU.Persistence.EntityFramework.Model.WaitingItem", + typeof(MUnique.OpenMU.Persistence.EntityFramework.Model.WaitingItem), + baseEntityType, + propertyCount: 5, + navigationCount: 1, + foreignKeyCount: 2, + unnamedIndexCount: 2, + keyCount: 1); + + var id = runtimeEntityType.AddProperty( + "Id", + typeof(Guid), + propertyInfo: typeof(MUnique.OpenMU.Persistence.EntityFramework.Model.WaitingItem).GetProperty("Id", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(MUnique.OpenMU.Persistence.EntityFramework.Model.WaitingItem).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + valueGenerated: ValueGenerated.OnAdd, + afterSaveBehavior: PropertySaveBehavior.Throw, + valueGeneratorFactory: new MUnique.OpenMU.Persistence.EntityFramework.GuidV7ValueGeneratorFactory().Create, + sentinel: new Guid("00000000-0000-0000-0000-000000000000")); + id.AddAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.None); + + var addedAt = runtimeEntityType.AddProperty( + "AddedAt", + typeof(DateTime), + propertyInfo: typeof(MUnique.OpenMU.DataModel.Entities.WaitingItem).GetProperty("AddedAt", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(MUnique.OpenMU.DataModel.Entities.WaitingItem).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + sentinel: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)); + addedAt.AddAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.None); + + var characterId = runtimeEntityType.AddProperty( + "CharacterId", + typeof(Guid?), + nullable: true); + characterId.AddAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.None); + + var itemId = runtimeEntityType.AddProperty( + "ItemId", + typeof(Guid?), + propertyInfo: typeof(MUnique.OpenMU.Persistence.EntityFramework.Model.WaitingItem).GetProperty("ItemId", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(MUnique.OpenMU.Persistence.EntityFramework.Model.WaitingItem).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); + itemId.AddAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.None); + + var reason = runtimeEntityType.AddProperty( + "Reason", + typeof(MUnique.OpenMU.DataModel.Entities.WaitingItemReason), + propertyInfo: typeof(MUnique.OpenMU.DataModel.Entities.WaitingItem).GetProperty("Reason", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(MUnique.OpenMU.DataModel.Entities.WaitingItem).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)); + reason.SetSentinelFromProviderValue(0); + reason.AddAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.None); + + var key = runtimeEntityType.AddKey( + new[] { id }); + runtimeEntityType.SetPrimaryKey(key); + + var index = runtimeEntityType.AddIndex( + new[] { characterId }); + + var index0 = runtimeEntityType.AddIndex( + new[] { itemId }, + unique: true); + + return runtimeEntityType; + } + + public static RuntimeForeignKey CreateForeignKey1(RuntimeEntityType declaringEntityType, RuntimeEntityType principalEntityType) + { + var runtimeForeignKey = declaringEntityType.AddForeignKey(new[] { declaringEntityType.FindProperty("CharacterId") }, + principalEntityType.FindKey(new[] { principalEntityType.FindProperty("Id") }), + principalEntityType, + deleteBehavior: DeleteBehavior.Cascade); + + var rawWaitingItems = principalEntityType.AddNavigation("RawWaitingItems", + runtimeForeignKey, + onDependent: false, + typeof(System.Collections.Generic.ICollection), + propertyInfo: typeof(MUnique.OpenMU.Persistence.EntityFramework.Model.Character).GetProperty("RawWaitingItems", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(MUnique.OpenMU.Persistence.EntityFramework.Model.Character).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)); + + return runtimeForeignKey; + } + + public static RuntimeForeignKey CreateForeignKey2(RuntimeEntityType declaringEntityType, RuntimeEntityType principalEntityType) + { + var runtimeForeignKey = declaringEntityType.AddForeignKey(new[] { declaringEntityType.FindProperty("ItemId") }, + principalEntityType.FindKey(new[] { principalEntityType.FindProperty("Id") }), + principalEntityType, + deleteBehavior: DeleteBehavior.Cascade, + unique: true); + + var rawItem = declaringEntityType.AddNavigation("RawItem", + runtimeForeignKey, + onDependent: true, + typeof(MUnique.OpenMU.Persistence.EntityFramework.Model.Item), + propertyInfo: typeof(MUnique.OpenMU.Persistence.EntityFramework.Model.WaitingItem).GetProperty("RawItem", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)); + + return runtimeForeignKey; + } + + public static void CreateAnnotations(RuntimeEntityType runtimeEntityType) + { + runtimeEntityType.AddAnnotation("Relational:FunctionName", null); + runtimeEntityType.AddAnnotation("Relational:Schema", "data"); + runtimeEntityType.AddAnnotation("Relational:SqlQuery", null); + runtimeEntityType.AddAnnotation("Relational:TableName", "WaitingItem"); + runtimeEntityType.AddAnnotation("Relational:ViewName", null); + runtimeEntityType.AddAnnotation("Relational:ViewSchema", null); + + Customize(runtimeEntityType); + } + + static partial void Customize(RuntimeEntityType runtimeEntityType); + } +} diff --git a/src/Persistence/EntityFramework/CompiledModels/TradeContext/CharacterEntityType.cs b/src/Persistence/EntityFramework/CompiledModels/TradeContext/CharacterEntityType.cs index 6f187b7724..2a8fe8eefd 100644 --- a/src/Persistence/EntityFramework/CompiledModels/TradeContext/CharacterEntityType.cs +++ b/src/Persistence/EntityFramework/CompiledModels/TradeContext/CharacterEntityType.cs @@ -24,7 +24,7 @@ public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType bas typeof(MUnique.OpenMU.Persistence.EntityFramework.Model.Character), baseEntityType, propertyCount: 26, - navigationCount: 5, + navigationCount: 6, foreignKeyCount: 1, unnamedIndexCount: 2, keyCount: 1); diff --git a/src/Persistence/EntityFramework/CompiledModels/TradeContext/TradeContextModelBuilder.cs b/src/Persistence/EntityFramework/CompiledModels/TradeContext/TradeContextModelBuilder.cs index 011f132bff..75952e1660 100644 --- a/src/Persistence/EntityFramework/CompiledModels/TradeContext/TradeContextModelBuilder.cs +++ b/src/Persistence/EntityFramework/CompiledModels/TradeContext/TradeContextModelBuilder.cs @@ -12,7 +12,7 @@ namespace MUnique.OpenMU.Persistence.EntityFramework.CompiledModels.ForTradeCont public partial class TradeContextModel { private TradeContextModel() - : base(skipDetectChanges: false, modelId: new Guid("3354f79e-6c11-4196-b706-28c658530ea2"), entityTypeCount: 48) + : base(skipDetectChanges: false, modelId: new Guid("cf84136b-2f3c-45cd-bdba-12d159157a17"), entityTypeCount: 49) { } @@ -66,6 +66,7 @@ partial void Initialize() var skillCharacterClass = SkillCharacterClassEntityType.Create(this); var skillEntry = SkillEntryEntityType.Create(this); var statAttribute = StatAttributeEntityType.Create(this); + var waitingItem = WaitingItemEntityType.Create(this); AccountAttributeGrantEntityType.CreateForeignKey1(accountAttributeGrant, account); AccountCharacterClassEntityType.CreateForeignKey1(accountCharacterClass, account); @@ -95,6 +96,8 @@ partial void Initialize() SkillEntryEntityType.CreateForeignKey1(skillEntry, character); StatAttributeEntityType.CreateForeignKey1(statAttribute, account); StatAttributeEntityType.CreateForeignKey2(statAttribute, character); + WaitingItemEntityType.CreateForeignKey1(waitingItem, character); + WaitingItemEntityType.CreateForeignKey2(waitingItem, item); AccountEntityType.CreateAnnotations(account); AccountAttributeGrantEntityType.CreateAnnotations(accountAttributeGrant); @@ -144,6 +147,7 @@ partial void Initialize() SkillCharacterClassEntityType.CreateAnnotations(skillCharacterClass); SkillEntryEntityType.CreateAnnotations(skillEntry); StatAttributeEntityType.CreateAnnotations(statAttribute); + WaitingItemEntityType.CreateAnnotations(waitingItem); AddAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); AddAnnotation("ProductVersion", "10.0.2"); diff --git a/src/Persistence/EntityFramework/CompiledModels/TradeContext/WaitingItemEntityType.cs b/src/Persistence/EntityFramework/CompiledModels/TradeContext/WaitingItemEntityType.cs new file mode 100644 index 0000000000..36018bc3c6 --- /dev/null +++ b/src/Persistence/EntityFramework/CompiledModels/TradeContext/WaitingItemEntityType.cs @@ -0,0 +1,135 @@ +// +using System; +using System.Collections.Generic; +using System.Reflection; +using MUnique.OpenMU.DataModel.Entities; +using MUnique.OpenMU.Persistence.EntityFramework.Model; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#pragma warning disable 219, 612, 618 +#nullable disable + +namespace MUnique.OpenMU.Persistence.EntityFramework.CompiledModels.ForTradeContext +{ + [EntityFrameworkInternal] + public partial class WaitingItemEntityType + { + public static RuntimeEntityType Create(RuntimeModel model, RuntimeEntityType baseEntityType = null) + { + var runtimeEntityType = model.AddEntityType( + "MUnique.OpenMU.Persistence.EntityFramework.Model.WaitingItem", + typeof(MUnique.OpenMU.Persistence.EntityFramework.Model.WaitingItem), + baseEntityType, + propertyCount: 5, + navigationCount: 1, + foreignKeyCount: 2, + unnamedIndexCount: 2, + keyCount: 1); + + var id = runtimeEntityType.AddProperty( + "Id", + typeof(Guid), + propertyInfo: typeof(MUnique.OpenMU.Persistence.EntityFramework.Model.WaitingItem).GetProperty("Id", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(MUnique.OpenMU.Persistence.EntityFramework.Model.WaitingItem).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + valueGenerated: ValueGenerated.OnAdd, + afterSaveBehavior: PropertySaveBehavior.Throw, + valueGeneratorFactory: new MUnique.OpenMU.Persistence.EntityFramework.GuidV7ValueGeneratorFactory().Create, + sentinel: new Guid("00000000-0000-0000-0000-000000000000")); + id.AddAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.None); + + var addedAt = runtimeEntityType.AddProperty( + "AddedAt", + typeof(DateTime), + propertyInfo: typeof(MUnique.OpenMU.DataModel.Entities.WaitingItem).GetProperty("AddedAt", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(MUnique.OpenMU.DataModel.Entities.WaitingItem).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + sentinel: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)); + addedAt.AddAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.None); + + var characterId = runtimeEntityType.AddProperty( + "CharacterId", + typeof(Guid?), + nullable: true); + characterId.AddAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.None); + + var itemId = runtimeEntityType.AddProperty( + "ItemId", + typeof(Guid?), + propertyInfo: typeof(MUnique.OpenMU.Persistence.EntityFramework.Model.WaitingItem).GetProperty("ItemId", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(MUnique.OpenMU.Persistence.EntityFramework.Model.WaitingItem).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly), + nullable: true); + itemId.AddAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.None); + + var reason = runtimeEntityType.AddProperty( + "Reason", + typeof(MUnique.OpenMU.DataModel.Entities.WaitingItemReason), + propertyInfo: typeof(MUnique.OpenMU.DataModel.Entities.WaitingItem).GetProperty("Reason", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(MUnique.OpenMU.DataModel.Entities.WaitingItem).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)); + reason.SetSentinelFromProviderValue(0); + reason.AddAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.None); + + var key = runtimeEntityType.AddKey( + new[] { id }); + runtimeEntityType.SetPrimaryKey(key); + + var index = runtimeEntityType.AddIndex( + new[] { characterId }); + + var index0 = runtimeEntityType.AddIndex( + new[] { itemId }, + unique: true); + + return runtimeEntityType; + } + + public static RuntimeForeignKey CreateForeignKey1(RuntimeEntityType declaringEntityType, RuntimeEntityType principalEntityType) + { + var runtimeForeignKey = declaringEntityType.AddForeignKey(new[] { declaringEntityType.FindProperty("CharacterId") }, + principalEntityType.FindKey(new[] { principalEntityType.FindProperty("Id") }), + principalEntityType, + deleteBehavior: DeleteBehavior.Cascade); + + var rawWaitingItems = principalEntityType.AddNavigation("RawWaitingItems", + runtimeForeignKey, + onDependent: false, + typeof(System.Collections.Generic.ICollection), + propertyInfo: typeof(MUnique.OpenMU.Persistence.EntityFramework.Model.Character).GetProperty("RawWaitingItems", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly), + fieldInfo: typeof(MUnique.OpenMU.Persistence.EntityFramework.Model.Character).GetField("k__BackingField", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)); + + return runtimeForeignKey; + } + + public static RuntimeForeignKey CreateForeignKey2(RuntimeEntityType declaringEntityType, RuntimeEntityType principalEntityType) + { + var runtimeForeignKey = declaringEntityType.AddForeignKey(new[] { declaringEntityType.FindProperty("ItemId") }, + principalEntityType.FindKey(new[] { principalEntityType.FindProperty("Id") }), + principalEntityType, + deleteBehavior: DeleteBehavior.Cascade, + unique: true); + + var rawItem = declaringEntityType.AddNavigation("RawItem", + runtimeForeignKey, + onDependent: true, + typeof(MUnique.OpenMU.Persistence.EntityFramework.Model.Item), + propertyInfo: typeof(MUnique.OpenMU.Persistence.EntityFramework.Model.WaitingItem).GetProperty("RawItem", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)); + + return runtimeForeignKey; + } + + public static void CreateAnnotations(RuntimeEntityType runtimeEntityType) + { + runtimeEntityType.AddAnnotation("Relational:FunctionName", null); + runtimeEntityType.AddAnnotation("Relational:Schema", "data"); + runtimeEntityType.AddAnnotation("Relational:SqlQuery", null); + runtimeEntityType.AddAnnotation("Relational:TableName", "WaitingItem"); + runtimeEntityType.AddAnnotation("Relational:ViewName", null); + runtimeEntityType.AddAnnotation("Relational:ViewSchema", null); + + Customize(runtimeEntityType); + } + + static partial void Customize(RuntimeEntityType runtimeEntityType); + } +} diff --git a/src/Persistence/EntityFramework/Migrations/20261114172314_AddWaitingItems.Designer.cs b/src/Persistence/EntityFramework/Migrations/20261114172314_AddWaitingItems.Designer.cs new file mode 100644 index 0000000000..a6ab2546f8 --- /dev/null +++ b/src/Persistence/EntityFramework/Migrations/20261114172314_AddWaitingItems.Designer.cs @@ -0,0 +1,6938 @@ +// +using System; +using MUnique.OpenMU.Persistence.EntityFramework; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace MUnique.OpenMU.Persistence.EntityFramework.Migrations +{ + [DbContext(typeof(MUnique.OpenMU.Persistence.EntityFramework.EntityDataContext))] + [Migration("20261114172314_AddWaitingItems")] + partial class AddWaitingItems + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.2") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.Account", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ChatBanUntil") + .HasColumnType("timestamp with time zone"); + + b.Property("EMail") + .IsRequired() + .HasColumnType("text"); + + b.Property("GoblinPoints") + .HasColumnType("integer"); + + b.Property("IsBot") + .HasColumnType("boolean"); + + b.Property("IsNetworkObservationActive") + .HasColumnType("boolean"); + + b.Property("IsTemplate") + .HasColumnType("boolean"); + + b.Property("IsVaultExtended") + .HasColumnType("boolean"); + + b.Property("LanguageIsoCode") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(3) + .HasColumnType("character varying(3)") + .HasDefaultValue("en"); + + b.Property("LoginName") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("PasswordHash") + .IsRequired() + .HasColumnType("text"); + + b.Property("RegistrationDate") + .HasColumnType("timestamp with time zone"); + + b.Property("SecurityCode") + .IsRequired() + .HasColumnType("text"); + + b.Property("State") + .HasColumnType("integer"); + + b.Property("TimeZone") + .HasColumnType("smallint"); + + b.Property("VaultId") + .HasColumnType("uuid"); + + b.Property("VaultPassword") + .IsRequired() + .HasColumnType("text"); + + b.Property("WCoinC") + .HasColumnType("integer"); + + b.Property("WCoinP") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("LoginName") + .IsUnique(); + + b.HasIndex("VaultId") + .IsUnique(); + + b.ToTable("Account", "data"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.AccountAttributeGrant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AccountId") + .HasColumnType("uuid"); + + b.Property("AttributeId") + .HasColumnType("uuid"); + + b.Property("ValidUntil") + .HasColumnType("timestamp with time zone"); + + b.Property("Value") + .HasColumnType("real"); + + b.HasKey("Id"); + + b.HasIndex("AccountId"); + + b.HasIndex("AttributeId"); + + b.ToTable("AccountAttributeGrant", "data"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.AccountCharacterClass", b => + { + b.Property("AccountId") + .HasColumnType("uuid"); + + b.Property("CharacterClassId") + .HasColumnType("uuid"); + + b.HasKey("AccountId", "CharacterClassId"); + + b.HasIndex("CharacterClassId"); + + b.ToTable("AccountCharacterClass", "data"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.AccountExternalLink", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AccountId") + .HasColumnType("uuid"); + + b.Property("CharacterName") + .HasColumnType("text"); + + b.Property("CodeExpiresAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CodeHash") + .HasColumnType("text"); + + b.Property("ExternalUserId") + .HasColumnType("text"); + + b.Property("ExternalUserName") + .HasColumnType("text"); + + b.Property("LinkedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Notifications") + .HasColumnType("integer"); + + b.Property("Provider") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("CodeHash"); + + b.HasIndex("AccountId", "Provider") + .IsUnique(); + + b.HasIndex("Provider", "ExternalUserId") + .IsUnique(); + + b.ToTable("AccountExternalLink", "data"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.AppearanceData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CharacterClassId") + .HasColumnType("uuid"); + + b.Property("FullAncientSetEquipped") + .HasColumnType("boolean"); + + b.Property("Pose") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("CharacterClassId"); + + b.ToTable("AppearanceData", "data"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.AreaSkillSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DelayBetweenHits") + .HasColumnType("interval"); + + b.Property("DelayPerOneDistance") + .HasColumnType("interval"); + + b.Property("EffectRange") + .HasColumnType("integer"); + + b.Property("FrustumDistance") + .HasColumnType("real"); + + b.Property("FrustumEndWidth") + .HasColumnType("real"); + + b.Property("FrustumStartWidth") + .HasColumnType("real"); + + b.Property("HitChancePerDistanceMultiplier") + .HasColumnType("real"); + + b.Property("MaximumNumberOfHitsPerAttack") + .HasColumnType("integer"); + + b.Property("MaximumNumberOfHitsPerTarget") + .HasColumnType("integer"); + + b.Property("MinimumNumberOfHitsPerAttack") + .HasColumnType("integer"); + + b.Property("MinimumNumberOfHitsPerTarget") + .HasColumnType("integer"); + + b.Property("ProjectileCount") + .HasColumnType("integer"); + + b.Property("TargetAreaDiameter") + .HasColumnType("real"); + + b.Property("UseDeferredHits") + .HasColumnType("boolean"); + + b.Property("UseFrustumFilter") + .HasColumnType("boolean"); + + b.Property("UseTargetAreaFilter") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.ToTable("AreaSkillSettings", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.AttributeDefinition", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("Designation") + .HasColumnType("text"); + + b.Property("GameConfigurationId") + .HasColumnType("uuid"); + + b.Property("MaximumValue") + .HasColumnType("real"); + + b.HasKey("Id"); + + b.HasIndex("GameConfigurationId"); + + b.ToTable("AttributeDefinition", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.AttributeRelationship", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AggregateType") + .HasColumnType("integer"); + + b.Property("CharacterClassId") + .HasColumnType("uuid"); + + b.Property("GameConfigurationId") + .HasColumnType("uuid"); + + b.Property("InputAttributeId") + .HasColumnType("uuid"); + + b.Property("InputOperand") + .HasColumnType("real"); + + b.Property("InputOperator") + .HasColumnType("integer"); + + b.Property("OperandAttributeId") + .HasColumnType("uuid"); + + b.Property("PowerUpDefinitionValueId") + .HasColumnType("uuid"); + + b.Property("SkillId") + .HasColumnType("uuid"); + + b.Property("TargetAttributeId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("CharacterClassId"); + + b.HasIndex("GameConfigurationId"); + + b.HasIndex("InputAttributeId"); + + b.HasIndex("OperandAttributeId"); + + b.HasIndex("PowerUpDefinitionValueId"); + + b.HasIndex("SkillId"); + + b.HasIndex("TargetAttributeId"); + + b.ToTable("AttributeRelationship", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.AttributeRequirement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AttributeId") + .HasColumnType("uuid"); + + b.Property("GameMapDefinitionId") + .HasColumnType("uuid"); + + b.Property("GameServerDefinitionId") + .HasColumnType("uuid"); + + b.Property("ItemDefinitionId") + .HasColumnType("uuid"); + + b.Property("MinimumItemLevel") + .HasColumnType("smallint"); + + b.Property("MinimumValue") + .HasColumnType("integer"); + + b.Property("SkillId") + .HasColumnType("uuid"); + + b.Property("SkillId1") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("AttributeId"); + + b.HasIndex("GameMapDefinitionId"); + + b.HasIndex("GameServerDefinitionId"); + + b.HasIndex("ItemDefinitionId"); + + b.HasIndex("SkillId"); + + b.HasIndex("SkillId1"); + + b.ToTable("AttributeRequirement", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.BattleZoneDefinition", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("GroundId") + .HasColumnType("uuid"); + + b.Property("LeftGoalId") + .HasColumnType("uuid"); + + b.Property("LeftTeamSpawnPointX") + .HasColumnType("smallint"); + + b.Property("LeftTeamSpawnPointY") + .HasColumnType("smallint"); + + b.Property("RightGoalId") + .HasColumnType("uuid"); + + b.Property("RightTeamSpawnPointX") + .HasColumnType("smallint"); + + b.Property("RightTeamSpawnPointY") + .HasColumnType("smallint"); + + b.Property("Type") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("GroundId") + .IsUnique(); + + b.HasIndex("LeftGoalId") + .IsUnique(); + + b.HasIndex("RightGoalId") + .IsUnique(); + + b.ToTable("BattleZoneDefinition", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.Buff", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("MagicEffectDefinitionId") + .HasColumnType("uuid"); + + b.Property("MaximumLevel") + .HasColumnType("integer"); + + b.Property("MinimumLevel") + .HasColumnType("integer"); + + b.Property("MonsterDefinitionId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("MagicEffectDefinitionId") + .IsUnique(); + + b.HasIndex("MonsterDefinitionId"); + + b.ToTable("Buff", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CashShopCoinGrant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AccountId") + .HasColumnType("uuid"); + + b.Property("Amount") + .HasColumnType("integer"); + + b.Property("AppliedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CoinType") + .HasColumnType("integer"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("GrantedBy") + .HasColumnType("text"); + + b.Property("Reason") + .HasColumnType("text"); + + b.Property("Reference") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Reference") + .IsUnique(); + + b.HasIndex("AccountId", "AppliedAt"); + + b.ToTable("CashShopCoinGrant", "data"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CashShopConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BannerSaleZone") + .HasColumnType("smallint"); + + b.Property("BannerYear") + .HasColumnType("smallint"); + + b.Property("BannerYearId") + .HasColumnType("smallint"); + + b.Property("ScriptSaleZone") + .HasColumnType("smallint"); + + b.Property("ScriptYear") + .HasColumnType("smallint"); + + b.Property("ScriptYearId") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.ToTable("CashShopConfiguration", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CashShopPackage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CashShopConfigurationId") + .HasColumnType("uuid"); + + b.Property("CoinType") + .HasColumnType("integer"); + + b.Property("IsBundle") + .HasColumnType("boolean"); + + b.Property("IsForSale") + .HasColumnType("boolean"); + + b.Property("IsGiftable") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("PackageSequence") + .HasColumnType("integer"); + + b.Property("Price") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("CashShopConfigurationId"); + + b.ToTable("CashShopPackage", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CashShopProduct", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CashShopPackageId") + .HasColumnType("uuid"); + + b.Property("Duration") + .HasColumnType("interval"); + + b.Property("ItemDefinitionId") + .HasColumnType("uuid"); + + b.Property("ItemLevel") + .HasColumnType("smallint"); + + b.Property("Price") + .HasColumnType("integer"); + + b.Property("PriceSequence") + .HasColumnType("integer"); + + b.Property("ProductSequence") + .HasColumnType("integer"); + + b.Property("Quantity") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("CashShopPackageId"); + + b.HasIndex("ItemDefinitionId"); + + b.ToTable("CashShopProduct", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CashShopStorageItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AccountId") + .HasColumnType("uuid"); + + b.Property("AddedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("GiftMessage") + .HasColumnType("text"); + + b.Property("GiftSenderName") + .HasColumnType("text"); + + b.Property("IsGift") + .HasColumnType("boolean"); + + b.Property("PriceSequence") + .HasColumnType("integer"); + + b.Property("ProductSequence") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("AccountId"); + + b.ToTable("CashShopStorageItem", "data"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CastleSiegeConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AttackRespawnAreaId") + .HasColumnType("uuid"); + + b.Property("CastleSiegeMapDefinitionId") + .HasColumnType("uuid"); + + b.Property("CrownHoldTimeSeconds") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(30); + + b.Property("DefenseRespawnAreaId") + .HasColumnType("uuid"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("GateBuyPrice") + .HasColumnType("integer"); + + b.Property("GateRepairCostPerHealthPoint") + .HasColumnType("integer"); + + b.Property("GuildScoreCastleSiege") + .HasColumnType("integer"); + + b.Property("GuildScoreCastleSiegeMembers") + .HasColumnType("integer"); + + b.Property("LandOfTrialsMapDefinitionId") + .HasColumnType("uuid"); + + b.Property("MaxAttackingGuilds") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(3); + + b.Property("ParticipantRewardMinSeconds") + .HasColumnType("integer"); + + b.Property("RegisterMinLevel") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(200); + + b.Property("RegisterMinMembers") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(20); + + b.Property("RepairCostPerUpgradeLevel") + .HasColumnType("integer"); + + b.Property("RewardItemDefinitionId") + .HasColumnType("uuid"); + + b.Property("SignOfLordItemDefinitionId") + .HasColumnType("uuid"); + + b.Property("SignOfLordItemLevel") + .ValueGeneratedOnAdd() + .HasColumnType("smallint") + .HasDefaultValue((byte)3); + + b.Property("StatueBuyPrice") + .HasColumnType("integer"); + + b.Property("StatueRepairCostPerHealthPoint") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("AttackRespawnAreaId") + .IsUnique(); + + b.HasIndex("CastleSiegeMapDefinitionId"); + + b.HasIndex("DefenseRespawnAreaId") + .IsUnique(); + + b.HasIndex("LandOfTrialsMapDefinitionId"); + + b.HasIndex("RewardItemDefinitionId"); + + b.HasIndex("SignOfLordItemDefinitionId"); + + b.ToTable("CastleSiegeConfiguration", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CastleSiegeData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("IsHuntZoneEnabled") + .HasColumnType("boolean"); + + b.Property("IsOccupied") + .HasColumnType("boolean"); + + b.Property("OwnerGuildId") + .HasColumnType("uuid"); + + b.Property("TaxChaos") + .HasColumnType("smallint"); + + b.Property("TaxHunt") + .HasColumnType("integer"); + + b.Property("TaxStore") + .HasColumnType("smallint"); + + b.Property("TributeMoney") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("OwnerGuildId"); + + b.ToTable("CastleSiegeData", "data"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CastleSiegeGuild", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CastleSiegeDataId") + .HasColumnType("uuid"); + + b.Property("GuildId") + .HasColumnType("uuid"); + + b.Property("GuildName") + .IsRequired() + .HasMaxLength(8) + .HasColumnType("character varying(8)"); + + b.Property("IsAllianceMaster") + .HasColumnType("boolean"); + + b.Property("Score") + .HasColumnType("integer"); + + b.Property("Side") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("CastleSiegeDataId"); + + b.HasIndex("GuildId") + .IsUnique(); + + b.ToTable("CastleSiegeGuild", "data"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CastleSiegeGuildRegistration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("GuildId") + .HasColumnType("uuid"); + + b.Property("GuildName") + .IsRequired() + .HasMaxLength(8) + .HasColumnType("character varying(8)"); + + b.Property("Marks") + .HasColumnType("integer"); + + b.Property("RegistrationOrder") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("GuildId") + .IsUnique(); + + b.ToTable("CastleSiegeGuildRegistration", "data"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CastleSiegeNpcDefinition", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CastleSiegeConfigurationId") + .HasColumnType("uuid"); + + b.Property("DefaultSide") + .HasColumnType("smallint"); + + b.Property("Direction") + .HasColumnType("integer"); + + b.Property("InstanceId") + .HasColumnType("smallint"); + + b.Property("IsPersistedToDatabase") + .HasColumnType("boolean"); + + b.Property("MonsterDefinitionId") + .HasColumnType("uuid"); + + b.Property("SpawnX") + .HasColumnType("smallint"); + + b.Property("SpawnY") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("CastleSiegeConfigurationId"); + + b.HasIndex("MonsterDefinitionId", "InstanceId"); + + b.ToTable("CastleSiegeNpcDefinition", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CastleSiegeNpcState", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CastleSiegeDataId") + .HasColumnType("uuid"); + + b.Property("CurrentHp") + .HasColumnType("integer"); + + b.Property("DefenseLevel") + .HasColumnType("smallint"); + + b.Property("InstanceId") + .HasColumnType("smallint"); + + b.Property("LifeLevel") + .HasColumnType("smallint"); + + b.Property("MonsterNumber") + .HasColumnType("smallint"); + + b.Property("RegenLevel") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("CastleSiegeDataId"); + + b.HasIndex("MonsterNumber", "InstanceId") + .IsUnique(); + + b.ToTable("CastleSiegeNpcState", "data"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CastleSiegePendingReward", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CharacterId") + .HasColumnType("uuid"); + + b.Property("ItemDefinitionId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("CharacterId"); + + b.HasIndex("ItemDefinitionId"); + + b.ToTable("CastleSiegePendingReward", "data"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CastleSiegeStateScheduleEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CastleSiegeConfigurationId") + .HasColumnType("uuid"); + + b.Property("DayOfWeek") + .HasColumnType("integer"); + + b.Property("Hour") + .HasColumnType("smallint"); + + b.Property("Minute") + .HasColumnType("smallint"); + + b.Property("State") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("CastleSiegeConfigurationId"); + + b.ToTable("CastleSiegeStateScheduleEntry", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CastleSiegeUpgradeDefinition", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CastleSiegeConfigurationId") + .HasColumnType("uuid"); + + b.Property("CastleSiegeConfigurationId1") + .HasColumnType("uuid"); + + b.Property("CastleSiegeConfigurationId2") + .HasColumnType("uuid"); + + b.Property("CastleSiegeConfigurationId3") + .HasColumnType("uuid"); + + b.Property("CastleSiegeConfigurationId4") + .HasColumnType("uuid"); + + b.Property("Level") + .HasColumnType("smallint"); + + b.Property("RequiredJewelOfGuardianCount") + .HasColumnType("integer"); + + b.Property("RequiredZen") + .HasColumnType("integer"); + + b.Property("Value") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("CastleSiegeConfigurationId"); + + b.HasIndex("CastleSiegeConfigurationId1"); + + b.HasIndex("CastleSiegeConfigurationId2"); + + b.HasIndex("CastleSiegeConfigurationId3"); + + b.HasIndex("CastleSiegeConfigurationId4"); + + b.ToTable("CastleSiegeUpgradeDefinition", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CastleSiegeZoneDefinition", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CastleSiegeConfigurationId") + .HasColumnType("uuid"); + + b.Property("CastleSiegeConfigurationId1") + .HasColumnType("uuid"); + + b.Property("X1") + .HasColumnType("smallint"); + + b.Property("X2") + .HasColumnType("smallint"); + + b.Property("Y1") + .HasColumnType("smallint"); + + b.Property("Y2") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("CastleSiegeConfigurationId"); + + b.HasIndex("CastleSiegeConfigurationId1"); + + b.ToTable("CastleSiegeZoneDefinition", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.Character", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AccountId") + .HasColumnType("uuid"); + + b.Property("CharacterClassId") + .HasColumnType("uuid"); + + b.Property("CharacterSlot") + .HasColumnType("smallint"); + + b.Property("CharacterStatus") + .HasColumnType("integer"); + + b.Property("CreateDate") + .HasColumnType("timestamp with time zone"); + + b.Property("CurrentMapId") + .HasColumnType("uuid"); + + b.Property("Experience") + .HasColumnType("bigint"); + + b.Property("InventoryExtensions") + .HasColumnType("integer"); + + b.Property("InventoryId") + .HasColumnType("uuid"); + + b.Property("IsStoreOpened") + .HasColumnType("boolean"); + + b.Property("KeyConfiguration") + .HasColumnType("bytea"); + + b.Property("LevelUpPoints") + .HasColumnType("integer"); + + b.Property("MasterExperience") + .HasColumnType("bigint"); + + b.Property("MasterLevelUpPoints") + .HasColumnType("integer"); + + b.Property("MuHelperConfiguration") + .HasColumnType("bytea"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("character varying(10)"); + + b.Property("PlayerKillCount") + .HasColumnType("integer"); + + b.Property("Pose") + .HasColumnType("smallint"); + + b.Property("PositionX") + .HasColumnType("smallint"); + + b.Property("PositionY") + .HasColumnType("smallint"); + + b.Property("State") + .HasColumnType("integer"); + + b.Property("StateRemainingSeconds") + .HasColumnType("integer"); + + b.Property("StoreName") + .HasColumnType("text"); + + b.Property("UsedFruitPoints") + .HasColumnType("integer"); + + b.Property("UsedNegFruitPoints") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("AccountId"); + + b.HasIndex("CharacterClassId"); + + b.HasIndex("CurrentMapId"); + + b.HasIndex("InventoryId") + .IsUnique(); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("Character", "data"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CharacterClass", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CanGetCreated") + .HasColumnType("boolean"); + + b.Property("ComboDefinitionId") + .HasColumnType("uuid"); + + b.Property("CreationAllowedFlag") + .HasColumnType("smallint"); + + b.Property("FruitCalculation") + .HasColumnType("integer"); + + b.Property("GameConfigurationId") + .HasColumnType("uuid"); + + b.Property("HomeMapId") + .HasColumnType("uuid"); + + b.Property("IntroducedIn") + .HasColumnType("integer"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsMasterClass") + .HasColumnType("boolean"); + + b.Property("LevelRequirementByCreation") + .HasColumnType("smallint"); + + b.Property("LevelWarpRequirementReductionPercent") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("NextGenerationClassId") + .HasColumnType("uuid"); + + b.Property("Number") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("ComboDefinitionId") + .IsUnique(); + + b.HasIndex("GameConfigurationId"); + + b.HasIndex("HomeMapId"); + + b.HasIndex("NextGenerationClassId"); + + b.ToTable("CharacterClass", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CharacterDropItemGroup", b => + { + b.Property("CharacterId") + .HasColumnType("uuid"); + + b.Property("DropItemGroupId") + .HasColumnType("uuid"); + + b.HasKey("CharacterId", "DropItemGroupId"); + + b.HasIndex("DropItemGroupId"); + + b.ToTable("CharacterDropItemGroup", "data"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CharacterQuestState", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ActiveQuestId") + .HasColumnType("uuid"); + + b.Property("CharacterId") + .HasColumnType("uuid"); + + b.Property("ClientActionPerformed") + .HasColumnType("boolean"); + + b.Property("Group") + .HasColumnType("smallint"); + + b.Property("LastFinishedQuestId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ActiveQuestId"); + + b.HasIndex("CharacterId"); + + b.HasIndex("LastFinishedQuestId"); + + b.ToTable("CharacterQuestState", "data"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ChatServerDefinition", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ClientCleanUpInterval") + .HasColumnType("interval"); + + b.Property("ClientTimeout") + .HasColumnType("interval"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("MaximumConnections") + .HasColumnType("integer"); + + b.Property("RoomCleanUpInterval") + .HasColumnType("interval"); + + b.Property("ServerId") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.ToTable("ChatServerDefinition", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ChatServerEndpoint", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ChatServerDefinitionId") + .HasColumnType("uuid"); + + b.Property("ClientId") + .HasColumnType("uuid"); + + b.Property("NetworkPort") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("ChatServerDefinitionId"); + + b.HasIndex("ClientId"); + + b.ToTable("ChatServerEndpoint", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CombinationBonusRequirement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ItemOptionCombinationBonusId") + .HasColumnType("uuid"); + + b.Property("MinimumCount") + .HasColumnType("integer"); + + b.Property("OptionTypeId") + .HasColumnType("uuid"); + + b.Property("SubOptionType") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("ItemOptionCombinationBonusId"); + + b.HasIndex("OptionTypeId"); + + b.ToTable("CombinationBonusRequirement", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ConfigurationUpdate", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("InstalledAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Key") + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Version") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.ToTable("ConfigurationUpdate", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ConfigurationUpdateState", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("InitializationKey") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("ConfigurationUpdateState", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ConnectServerDefinition", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CheckMaxConnectionsPerAddress") + .HasColumnType("boolean"); + + b.Property("ClientId") + .HasColumnType("uuid"); + + b.Property("ClientListenerPort") + .HasColumnType("integer"); + + b.Property("CurrentPatchVersion") + .HasColumnType("bytea"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("DisconnectOnUnknownPacket") + .HasColumnType("boolean"); + + b.Property("ListenerBacklog") + .HasColumnType("integer"); + + b.Property("MaxConnections") + .HasColumnType("integer"); + + b.Property("MaxConnectionsPerAddress") + .HasColumnType("integer"); + + b.Property("MaxFtpRequests") + .HasColumnType("integer"); + + b.Property("MaxIpRequests") + .HasColumnType("integer"); + + b.Property("MaxServerListRequests") + .HasColumnType("integer"); + + b.Property("MaximumReceiveSize") + .HasColumnType("smallint"); + + b.Property("PatchAddress") + .IsRequired() + .HasColumnType("text"); + + b.Property("ServerId") + .HasColumnType("smallint"); + + b.Property("Timeout") + .HasColumnType("interval"); + + b.HasKey("Id"); + + b.HasIndex("ClientId"); + + b.ToTable("ConnectServerDefinition", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ConstValueAttribute", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AggregateType") + .HasColumnType("integer"); + + b.Property("CharacterClassId") + .HasColumnType("uuid"); + + b.Property("DefinitionId") + .HasColumnType("uuid"); + + b.Property("GameConfigurationId") + .HasColumnType("uuid"); + + b.Property("Value") + .HasColumnType("real"); + + b.HasKey("Id"); + + b.HasIndex("CharacterClassId"); + + b.HasIndex("DefinitionId"); + + b.HasIndex("GameConfigurationId"); + + b.ToTable("ConstValueAttribute", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CrywolfData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("IsOccupied") + .HasColumnType("boolean"); + + b.Property("IsWarRunning") + .HasColumnType("boolean"); + + b.Property("LastBattleEnd") + .HasColumnType("timestamp with time zone"); + + b.Property("WarStart") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.ToTable("CrywolfData", "data"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.DropItemGroup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Chance") + .HasColumnType("double precision"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("ExcellentOptionsAddChance") + .HasColumnType("real"); + + b.Property("ExcellentOptionsMaximumOptionsPerItem") + .HasColumnType("integer"); + + b.Property("GameConfigurationId") + .HasColumnType("uuid"); + + b.Property("ItemLevel") + .HasColumnType("smallint"); + + b.Property("ItemType") + .HasColumnType("integer"); + + b.Property("MaximumMonsterLevel") + .HasColumnType("smallint"); + + b.Property("MinimumMonsterLevel") + .HasColumnType("smallint"); + + b.Property("MonsterId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("GameConfigurationId"); + + b.HasIndex("MonsterId"); + + b.ToTable("DropItemGroup", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.DropItemGroupItemDefinition", b => + { + b.Property("DropItemGroupId") + .HasColumnType("uuid"); + + b.Property("ItemDefinitionId") + .HasColumnType("uuid"); + + b.HasKey("DropItemGroupId", "ItemDefinitionId"); + + b.HasIndex("ItemDefinitionId"); + + b.ToTable("DropItemGroupItemDefinition", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.DuelArea", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DuelConfigurationId") + .HasColumnType("uuid"); + + b.Property("FirstPlayerGateId") + .HasColumnType("uuid"); + + b.Property("Index") + .HasColumnType("smallint"); + + b.Property("SecondPlayerGateId") + .HasColumnType("uuid"); + + b.Property("SpectatorsGateId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DuelConfigurationId"); + + b.HasIndex("FirstPlayerGateId"); + + b.HasIndex("SecondPlayerGateId"); + + b.HasIndex("SpectatorsGateId"); + + b.ToTable("DuelArea", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.DuelConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("EntranceFee") + .HasColumnType("integer"); + + b.Property("ExitId") + .HasColumnType("uuid"); + + b.Property("MaximumScore") + .HasColumnType("integer"); + + b.Property("MaximumSpectatorsPerDuelRoom") + .HasColumnType("integer"); + + b.Property("MinimumCharacterLevel") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("ExitId"); + + b.ToTable("DuelConfiguration", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.EnterGate", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("GameMapDefinitionId") + .HasColumnType("uuid"); + + b.Property("LevelRequirement") + .HasColumnType("smallint"); + + b.Property("Number") + .HasColumnType("smallint"); + + b.Property("TargetGateId") + .HasColumnType("uuid"); + + b.Property("X1") + .HasColumnType("smallint"); + + b.Property("X2") + .HasColumnType("smallint"); + + b.Property("Y1") + .HasColumnType("smallint"); + + b.Property("Y2") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("GameMapDefinitionId"); + + b.HasIndex("TargetGateId"); + + b.ToTable("EnterGate", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ExitGate", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Direction") + .HasColumnType("integer"); + + b.Property("IsSpawnGate") + .HasColumnType("boolean"); + + b.Property("MapId") + .HasColumnType("uuid"); + + b.Property("X1") + .HasColumnType("smallint"); + + b.Property("X2") + .HasColumnType("smallint"); + + b.Property("Y1") + .HasColumnType("smallint"); + + b.Property("Y2") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("MapId"); + + b.ToTable("ExitGate", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.Friend", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Accepted") + .HasColumnType("boolean"); + + b.Property("CharacterId") + .HasColumnType("uuid"); + + b.Property("FriendId") + .HasColumnType("uuid"); + + b.Property("RequestOpen") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.HasAlternateKey("CharacterId", "FriendId"); + + b.ToTable("Friend", "friend"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.GameClientDefinition", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("Episode") + .HasColumnType("smallint"); + + b.Property("Language") + .HasColumnType("integer"); + + b.Property("Season") + .HasColumnType("smallint"); + + b.Property("Serial") + .HasColumnType("bytea"); + + b.Property("Version") + .HasColumnType("bytea"); + + b.HasKey("Id"); + + b.ToTable("GameClientDefinition", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.GameConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AncientDropLevelBonus") + .HasColumnType("smallint"); + + b.Property("AreaSkillHitsPlayer") + .HasColumnType("boolean"); + + b.Property("CashShopConfigurationId") + .HasColumnType("uuid"); + + b.Property("CastleSiegeConfigurationId") + .HasColumnType("uuid"); + + b.Property("CharacterNameRegex") + .HasColumnType("text"); + + b.Property("ClampMoneyOnPickup") + .HasColumnType("boolean"); + + b.Property("DamagePerOneItemDurability") + .HasColumnType("double precision"); + + b.Property("DamagePerOnePetDurability") + .HasColumnType("double precision"); + + b.Property("DuelConfigurationId") + .HasColumnType("uuid"); + + b.Property("ExcellentDropLevelBonus") + .HasColumnType("smallint"); + + b.Property("ExcellentItemDropLevelDelta") + .ValueGeneratedOnAdd() + .HasColumnType("smallint") + .HasDefaultValue((byte)25); + + b.Property("ExperienceFormula") + .ValueGeneratedOnAdd() + .HasColumnType("text") + .HasDefaultValue("if(level == 0, 0, if(level < 256, 10 * (level + 8) * (level - 1) * (level - 1), (10 * (level + 8) * (level - 1) * (level - 1)) + (1000 * (level - 247) * (level - 256) * (level - 256))))"); + + b.Property("ExperienceRate") + .HasColumnType("real"); + + b.Property("HitsPerOneItemDurability") + .HasColumnType("double precision"); + + b.Property("InfoRange") + .HasColumnType("smallint"); + + b.Property("ItemDropDuration") + .ValueGeneratedOnAdd() + .HasColumnType("interval") + .HasDefaultValue(new TimeSpan(0, 0, 1, 0, 0)); + + b.Property("LetterSendPrice") + .HasColumnType("integer"); + + b.Property("MasterExperienceFormula") + .ValueGeneratedOnAdd() + .HasColumnType("text") + .HasDefaultValue("(505 * level * level * level) + (35278500 * level) + (228045 * level * level)"); + + b.Property("MasterExperienceRate") + .HasColumnType("real"); + + b.Property("MaximumCharactersPerAccount") + .HasColumnType("smallint"); + + b.Property("MaximumInventoryMoney") + .HasColumnType("integer"); + + b.Property("MaximumItemOptionLevelDrop") + .HasColumnType("smallint"); + + b.Property("MaximumLetters") + .HasColumnType("integer"); + + b.Property("MaximumLevel") + .HasColumnType("smallint"); + + b.Property("MaximumMasterLevel") + .HasColumnType("smallint"); + + b.Property("MaximumPartySize") + .HasColumnType("smallint"); + + b.Property("MaximumPasswordLength") + .HasColumnType("integer"); + + b.Property("MaximumVaultMoney") + .HasColumnType("integer"); + + b.Property("MinimumMonsterLevelForMasterExperience") + .HasColumnType("smallint"); + + b.Property("PreventExperienceOverflow") + .HasColumnType("boolean"); + + b.Property("RecoveryInterval") + .HasColumnType("integer"); + + b.Property("ShouldDropMoney") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.HasIndex("CashShopConfigurationId") + .IsUnique(); + + b.HasIndex("CastleSiegeConfigurationId") + .IsUnique(); + + b.HasIndex("DuelConfigurationId") + .IsUnique(); + + b.ToTable("GameConfiguration", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.GameMapDefinition", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BattleZoneId") + .HasColumnType("uuid"); + + b.Property("Discriminator") + .HasColumnType("integer"); + + b.Property("ExpMultiplier") + .HasColumnType("double precision"); + + b.Property("GameConfigurationId") + .HasColumnType("uuid"); + + b.Property("IntroducedIn") + .HasColumnType("integer"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Number") + .HasColumnType("smallint"); + + b.Property("SafezoneMapId") + .HasColumnType("uuid"); + + b.Property("TerrainData") + .HasColumnType("bytea"); + + b.HasKey("Id"); + + b.HasIndex("BattleZoneId") + .IsUnique(); + + b.HasIndex("GameConfigurationId"); + + b.HasIndex("SafezoneMapId"); + + b.ToTable("GameMapDefinition", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.GameMapDefinitionDropItemGroup", b => + { + b.Property("GameMapDefinitionId") + .HasColumnType("uuid"); + + b.Property("DropItemGroupId") + .HasColumnType("uuid"); + + b.HasKey("GameMapDefinitionId", "DropItemGroupId"); + + b.HasIndex("DropItemGroupId"); + + b.ToTable("GameMapDefinitionDropItemGroup", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.GameMapTerrainVariant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("GameMapDefinitionId") + .HasColumnType("uuid"); + + b.Property("Number") + .HasColumnType("smallint"); + + b.Property("TerrainData") + .HasColumnType("bytea"); + + b.HasKey("Id"); + + b.HasIndex("GameMapDefinitionId"); + + b.ToTable("GameMapTerrainVariant", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.GameServerConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("MaximumPlayers") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.ToTable("GameServerConfiguration", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.GameServerConfigurationGameMapDefinition", b => + { + b.Property("GameServerConfigurationId") + .HasColumnType("uuid"); + + b.Property("GameMapDefinitionId") + .HasColumnType("uuid"); + + b.HasKey("GameServerConfigurationId", "GameMapDefinitionId"); + + b.HasIndex("GameMapDefinitionId"); + + b.ToTable("GameServerConfigurationGameMapDefinition", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.GameServerDefinition", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("ExperienceRate") + .HasColumnType("real"); + + b.Property("GameConfigurationId") + .HasColumnType("uuid"); + + b.Property("PvpEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("ServerConfigurationId") + .HasColumnType("uuid"); + + b.Property("ServerID") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("GameConfigurationId"); + + b.HasIndex("ServerConfigurationId"); + + b.ToTable("GameServerDefinition", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.GameServerEndpoint", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AlternativePublishedPort") + .HasColumnType("integer"); + + b.Property("ClientId") + .HasColumnType("uuid"); + + b.Property("GameServerDefinitionId") + .HasColumnType("uuid"); + + b.Property("NetworkPort") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("ClientId"); + + b.HasIndex("GameServerDefinitionId"); + + b.ToTable("GameServerEndpoint", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.GensAbuse", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("KillCount") + .HasColumnType("integer"); + + b.Property("KillerId") + .HasColumnType("uuid"); + + b.Property("LastKillAt") + .HasColumnType("timestamp with time zone"); + + b.Property("VictimId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("VictimId"); + + b.HasIndex("KillerId", "VictimId") + .IsUnique(); + + b.ToTable("GensAbuse", "data"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.GensMember", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CharacterId") + .HasColumnType("uuid"); + + b.Property("Contribution") + .HasColumnType("integer"); + + b.Property("Gens") + .HasColumnType("integer"); + + b.Property("JoinedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("LeftAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Rank") + .HasColumnType("smallint"); + + b.Property("RankingPosition") + .HasColumnType("integer"); + + b.Property("RewardClaimedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("CharacterId") + .IsUnique(); + + b.ToTable("GensMember", "data"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.Guild", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AllianceGuildId") + .HasColumnType("uuid"); + + b.Property("HostilityId") + .HasColumnType("uuid"); + + b.Property("Logo") + .HasColumnType("bytea"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(8) + .HasColumnType("character varying(8)"); + + b.Property("Notice") + .HasColumnType("text"); + + b.Property("Score") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("AllianceGuildId"); + + b.HasIndex("HostilityId"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("Guild", "guild"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.GuildChatBinding", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BoundAt") + .HasColumnType("timestamp with time zone"); + + b.Property("BoundBy") + .HasColumnType("text"); + + b.Property("ExternalChannelId") + .IsRequired() + .HasColumnType("text"); + + b.Property("ExternalServerId") + .IsRequired() + .HasColumnType("text"); + + b.Property("GuildId") + .HasColumnType("uuid"); + + b.Property("IsHosted") + .HasColumnType("boolean"); + + b.Property("Provider") + .IsRequired() + .HasColumnType("text"); + + b.Property("Scope") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("Provider", "ExternalChannelId") + .IsUnique(); + + b.HasIndex("GuildId", "Scope", "Provider") + .IsUnique(); + + b.ToTable("GuildChatBinding", "data"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.GuildMember", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("GuildId") + .HasColumnType("uuid"); + + b.Property("Status") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("GuildId"); + + b.ToTable("GuildMember", "guild"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.IncreasableItemOption", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ItemOptionDefinitionId") + .HasColumnType("uuid"); + + b.Property("LevelType") + .HasColumnType("integer"); + + b.Property("Number") + .HasColumnType("integer"); + + b.Property("OptionTypeId") + .HasColumnType("uuid"); + + b.Property("PowerUpDefinitionId") + .HasColumnType("uuid"); + + b.Property("SubOptionType") + .HasColumnType("integer"); + + b.Property("Weight") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("ItemOptionDefinitionId"); + + b.HasIndex("OptionTypeId"); + + b.HasIndex("PowerUpDefinitionId") + .IsUnique(); + + b.ToTable("IncreasableItemOption", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.Item", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DefinitionId") + .HasColumnType("uuid"); + + b.Property("Durability") + .HasColumnType("double precision"); + + b.Property("HasSkill") + .HasColumnType("boolean"); + + b.Property("ItemSlot") + .HasColumnType("smallint"); + + b.Property("ItemStorageId") + .HasColumnType("uuid"); + + b.Property("Level") + .HasColumnType("smallint"); + + b.Property("PetExperience") + .HasColumnType("integer"); + + b.Property("SocketCount") + .HasColumnType("integer"); + + b.Property("StorePrice") + .HasColumnType("integer"); + + b.Property("StorePriceCoinType") + .HasColumnType("integer"); + + b.Property("StorePriceItemDefinitionId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DefinitionId"); + + b.HasIndex("ItemStorageId"); + + b.HasIndex("StorePriceItemDefinitionId"); + + b.ToTable("Item", "data"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemAppearance", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AppearanceDataId") + .HasColumnType("uuid"); + + b.Property("DefinitionId") + .HasColumnType("uuid"); + + b.Property("ItemSlot") + .HasColumnType("smallint"); + + b.Property("Level") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("AppearanceDataId"); + + b.HasIndex("DefinitionId"); + + b.ToTable("ItemAppearance", "data"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemAppearanceItemOptionType", b => + { + b.Property("ItemAppearanceId") + .HasColumnType("uuid"); + + b.Property("ItemOptionTypeId") + .HasColumnType("uuid"); + + b.HasKey("ItemAppearanceId", "ItemOptionTypeId"); + + b.HasIndex("ItemOptionTypeId"); + + b.ToTable("ItemAppearanceItemOptionType", "data"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemBasePowerUpDefinition", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AggregateType") + .HasColumnType("integer"); + + b.Property("BaseValue") + .HasColumnType("real"); + + b.Property("BonusPerLevelTableId") + .HasColumnType("uuid"); + + b.Property("ItemDefinitionId") + .HasColumnType("uuid"); + + b.Property("TargetAttributeId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("BonusPerLevelTableId"); + + b.HasIndex("ItemDefinitionId"); + + b.HasIndex("TargetAttributeId"); + + b.ToTable("ItemBasePowerUpDefinition", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemCrafting", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ItemCraftingHandlerClassName") + .IsRequired() + .HasColumnType("text"); + + b.Property("MonsterDefinitionId") + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Number") + .HasColumnType("smallint"); + + b.Property("SimpleCraftingSettingsId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("MonsterDefinitionId"); + + b.HasIndex("SimpleCraftingSettingsId") + .IsUnique(); + + b.ToTable("ItemCrafting", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemCraftingRequiredItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AddPercentage") + .HasColumnType("smallint"); + + b.Property("FailResult") + .HasColumnType("integer"); + + b.Property("MaximumAmount") + .HasColumnType("smallint"); + + b.Property("MaximumItemLevel") + .HasColumnType("smallint"); + + b.Property("MinimumAmount") + .HasColumnType("smallint"); + + b.Property("MinimumItemLevel") + .HasColumnType("smallint"); + + b.Property("NpcPriceDivisor") + .HasColumnType("integer"); + + b.Property("Reference") + .HasColumnType("smallint"); + + b.Property("SimpleCraftingSettingsId") + .HasColumnType("uuid"); + + b.Property("SuccessResult") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("SimpleCraftingSettingsId"); + + b.ToTable("ItemCraftingRequiredItem", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemCraftingRequiredItemItemDefinition", b => + { + b.Property("ItemCraftingRequiredItemId") + .HasColumnType("uuid"); + + b.Property("ItemDefinitionId") + .HasColumnType("uuid"); + + b.HasKey("ItemCraftingRequiredItemId", "ItemDefinitionId"); + + b.HasIndex("ItemDefinitionId"); + + b.ToTable("ItemCraftingRequiredItemItemDefinition", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemCraftingRequiredItemItemOptionType", b => + { + b.Property("ItemCraftingRequiredItemId") + .HasColumnType("uuid"); + + b.Property("ItemOptionTypeId") + .HasColumnType("uuid"); + + b.HasKey("ItemCraftingRequiredItemId", "ItemOptionTypeId"); + + b.HasIndex("ItemOptionTypeId"); + + b.ToTable("ItemCraftingRequiredItemItemOptionType", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemCraftingResultItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AddLevel") + .HasColumnType("smallint"); + + b.Property("Durability") + .HasColumnType("smallint"); + + b.Property("ItemDefinitionId") + .HasColumnType("uuid"); + + b.Property("RandomMaximumLevel") + .HasColumnType("smallint"); + + b.Property("RandomMinimumLevel") + .HasColumnType("smallint"); + + b.Property("Reference") + .HasColumnType("smallint"); + + b.Property("SimpleCraftingSettingsId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("ItemDefinitionId"); + + b.HasIndex("SimpleCraftingSettingsId"); + + b.ToTable("ItemCraftingResultItem", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDefinition", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ConsumeEffectId") + .HasColumnType("uuid"); + + b.Property("DropLevel") + .HasColumnType("smallint"); + + b.Property("DropsFromMonsters") + .HasColumnType("boolean"); + + b.Property("Durability") + .HasColumnType("smallint"); + + b.Property("ExcellentBonus") + .HasColumnType("smallint"); + + b.Property("ExcellentRaisesLevel") + .HasColumnType("boolean"); + + b.Property("GameConfigurationId") + .HasColumnType("uuid"); + + b.Property("Group") + .HasColumnType("smallint"); + + b.Property("Height") + .HasColumnType("smallint"); + + b.Property("IntroducedIn") + .HasColumnType("integer"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsAmmunition") + .HasColumnType("boolean"); + + b.Property("IsBoundToCharacter") + .HasColumnType("boolean"); + + b.Property("IsDroppable") + .HasColumnType("boolean"); + + b.Property("IsPersonalStoreSellable") + .HasColumnType("boolean"); + + b.Property("IsQuestItem") + .HasColumnType("boolean"); + + b.Property("IsRepairable") + .HasColumnType("boolean"); + + b.Property("IsSellableToNpc") + .HasColumnType("boolean"); + + b.Property("IsStorable") + .HasColumnType("boolean"); + + b.Property("IsTradable") + .HasColumnType("boolean"); + + b.Property("ItemSlotId") + .HasColumnType("uuid"); + + b.Property("MaximumDropLevel") + .HasColumnType("smallint"); + + b.Property("MaximumItemLevel") + .HasColumnType("smallint"); + + b.Property("MaximumSockets") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Number") + .HasColumnType("smallint"); + + b.Property("PetExperienceFormula") + .HasColumnType("text"); + + b.Property("PriceDefinitionId") + .HasColumnType("uuid"); + + b.Property("SkillId") + .HasColumnType("uuid"); + + b.Property("StatProfileId") + .HasColumnType("uuid"); + + b.Property("StorageLimitPerCharacter") + .HasColumnType("integer"); + + b.Property("Value") + .HasColumnType("integer"); + + b.Property("Width") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("ConsumeEffectId"); + + b.HasIndex("GameConfigurationId"); + + b.HasIndex("ItemSlotId"); + + b.HasIndex("PriceDefinitionId"); + + b.HasIndex("SkillId"); + + b.HasIndex("StatProfileId"); + + b.ToTable("ItemDefinition", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDefinitionCharacterClass", b => + { + b.Property("ItemDefinitionId") + .HasColumnType("uuid"); + + b.Property("CharacterClassId") + .HasColumnType("uuid"); + + b.HasKey("ItemDefinitionId", "CharacterClassId"); + + b.HasIndex("CharacterClassId"); + + b.ToTable("ItemDefinitionCharacterClass", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDefinitionItemOptionDefinition", b => + { + b.Property("ItemDefinitionId") + .HasColumnType("uuid"); + + b.Property("ItemOptionDefinitionId") + .HasColumnType("uuid"); + + b.HasKey("ItemDefinitionId", "ItemOptionDefinitionId"); + + b.HasIndex("ItemOptionDefinitionId"); + + b.ToTable("ItemDefinitionItemOptionDefinition", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDefinitionItemSetGroup", b => + { + b.Property("ItemDefinitionId") + .HasColumnType("uuid"); + + b.Property("ItemSetGroupId") + .HasColumnType("uuid"); + + b.HasKey("ItemDefinitionId", "ItemSetGroupId"); + + b.HasIndex("ItemSetGroupId"); + + b.ToTable("ItemDefinitionItemSetGroup", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDropItemGroup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Chance") + .HasColumnType("double precision"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("DropEffect") + .HasColumnType("integer"); + + b.Property("ExcellentOptionsAddChance") + .HasColumnType("real"); + + b.Property("ExcellentOptionsMaximumOptionsPerItem") + .HasColumnType("integer"); + + b.Property("ItemDefinitionId") + .HasColumnType("uuid"); + + b.Property("ItemLevel") + .HasColumnType("smallint"); + + b.Property("ItemType") + .HasColumnType("integer"); + + b.Property("MaximumLevel") + .HasColumnType("smallint"); + + b.Property("MaximumMonsterLevel") + .HasColumnType("smallint"); + + b.Property("MinimumLevel") + .HasColumnType("smallint"); + + b.Property("MinimumMonsterLevel") + .HasColumnType("smallint"); + + b.Property("MoneyAmount") + .HasColumnType("integer"); + + b.Property("MonsterId") + .HasColumnType("uuid"); + + b.Property("RequiredCharacterLevel") + .HasColumnType("smallint"); + + b.Property("SourceItemLevel") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("ItemDefinitionId"); + + b.HasIndex("MonsterId"); + + b.ToTable("ItemDropItemGroup", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDropItemGroupItemDefinition", b => + { + b.Property("ItemDropItemGroupId") + .HasColumnType("uuid"); + + b.Property("ItemDefinitionId") + .HasColumnType("uuid"); + + b.HasKey("ItemDropItemGroupId", "ItemDefinitionId"); + + b.HasIndex("ItemDefinitionId"); + + b.ToTable("ItemDropItemGroupItemDefinition", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemItemOfItemSet", b => + { + b.Property("ItemId") + .HasColumnType("uuid"); + + b.Property("ItemOfItemSetId") + .HasColumnType("uuid"); + + b.HasKey("ItemId", "ItemOfItemSetId"); + + b.HasIndex("ItemOfItemSetId"); + + b.ToTable("ItemItemOfItemSet", "data"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemLevelBonusTable", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("GameConfigurationId") + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("GameConfigurationId"); + + b.ToTable("ItemLevelBonusTable", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemLevelPrice", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ItemPriceDefinitionId") + .HasColumnType("uuid"); + + b.Property("Level") + .HasColumnType("integer"); + + b.Property("Price") + .HasColumnType("bigint"); + + b.HasKey("Id"); + + b.HasIndex("ItemPriceDefinitionId"); + + b.ToTable("ItemLevelPrice", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemOfItemSet", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AncientSetDiscriminator") + .HasColumnType("integer"); + + b.Property("BonusOptionId") + .HasColumnType("uuid"); + + b.Property("ItemDefinitionId") + .HasColumnType("uuid"); + + b.Property("ItemSetGroupId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("BonusOptionId"); + + b.HasIndex("ItemDefinitionId"); + + b.HasIndex("ItemSetGroupId"); + + b.ToTable("ItemOfItemSet", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemOption", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Number") + .HasColumnType("integer"); + + b.Property("OptionTypeId") + .HasColumnType("uuid"); + + b.Property("PowerUpDefinitionId") + .HasColumnType("uuid"); + + b.Property("SubOptionType") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("OptionTypeId"); + + b.HasIndex("PowerUpDefinitionId") + .IsUnique(); + + b.ToTable("ItemOption", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemOptionCombinationBonus", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AppliesMultipleTimes") + .HasColumnType("boolean"); + + b.Property("BonusId") + .HasColumnType("uuid"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("GameConfigurationId") + .HasColumnType("uuid"); + + b.Property("Number") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("BonusId") + .IsUnique(); + + b.HasIndex("GameConfigurationId"); + + b.ToTable("ItemOptionCombinationBonus", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemOptionDefinition", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AddChance") + .HasColumnType("real"); + + b.Property("AddsRandomly") + .HasColumnType("boolean"); + + b.Property("GameConfigurationId") + .HasColumnType("uuid"); + + b.Property("MaximumOptionsPerItem") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("GameConfigurationId"); + + b.ToTable("ItemOptionDefinition", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemOptionLink", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Index") + .HasColumnType("integer"); + + b.Property("ItemId") + .HasColumnType("uuid"); + + b.Property("ItemOptionId") + .HasColumnType("uuid"); + + b.Property("Level") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("ItemId"); + + b.HasIndex("ItemOptionId"); + + b.ToTable("ItemOptionLink", "data"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemOptionOfLevel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("IncreasableItemOptionId") + .HasColumnType("uuid"); + + b.Property("Level") + .HasColumnType("integer"); + + b.Property("PowerUpDefinitionId") + .HasColumnType("uuid"); + + b.Property("RequiredItemLevel") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("IncreasableItemOptionId"); + + b.HasIndex("PowerUpDefinitionId") + .IsUnique(); + + b.ToTable("ItemOptionOfLevel", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemOptionType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("GameConfigurationId") + .HasColumnType("uuid"); + + b.Property("IsVisible") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("GameConfigurationId"); + + b.ToTable("ItemOptionType", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemPriceDefinition", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BasePriceFormula") + .HasColumnType("text"); + + b.Property("CraftingReferencePrice") + .HasColumnType("bigint"); + + b.Property("GameConfigurationId") + .HasColumnType("uuid"); + + b.Property("Modifiers") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("QuantityScaling") + .HasColumnType("integer"); + + b.Property("SellingPriceRounding") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("GameConfigurationId"); + + b.ToTable("ItemPriceDefinition", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemRequirementFormula", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Add") + .HasColumnType("integer"); + + b.Property("AttributeId") + .HasColumnType("uuid"); + + b.Property("ItemStatProfileId") + .HasColumnType("uuid"); + + b.Property("KeepBase") + .HasColumnType("boolean"); + + b.Property("LevelRequirementBasis") + .HasColumnType("boolean"); + + b.Property("LevelWeight") + .HasColumnType("integer"); + + b.Property("Percent") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("AttributeId"); + + b.HasIndex("ItemStatProfileId", "AttributeId") + .IsUnique(); + + b.ToTable("ItemRequirementFormula", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemSetGroup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AlwaysApplies") + .HasColumnType("boolean"); + + b.Property("CountDistinct") + .HasColumnType("boolean"); + + b.Property("GameConfigurationId") + .HasColumnType("uuid"); + + b.Property("MinimumItemCount") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("OptionsId") + .HasColumnType("uuid"); + + b.Property("SetLevel") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("GameConfigurationId"); + + b.HasIndex("OptionsId"); + + b.ToTable("ItemSetGroup", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemSlotType", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("GameConfigurationId") + .HasColumnType("uuid"); + + b.Property("RawItemSlots") + .HasColumnType("text") + .HasColumnName("ItemSlots") + .HasJsonPropertyName("itemSlots"); + + b.HasKey("Id"); + + b.HasIndex("GameConfigurationId"); + + b.ToTable("ItemSlotType", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemStatProfile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ExcellentLevelAdd") + .HasColumnType("integer"); + + b.Property("GameConfigurationId") + .HasColumnType("uuid"); + + b.Property("LevelPerItemLevel") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("StrengthPerOptionLevel") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("GameConfigurationId", "Name") + .IsUnique(); + + b.ToTable("ItemStatProfile", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemStatTerms", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AncientAdd") + .HasColumnType("integer"); + + b.Property("AncientAfterLevelBonus") + .HasColumnType("boolean"); + + b.Property("AncientDropLevelDivisor") + .HasColumnType("integer"); + + b.Property("AncientState") + .HasColumnType("integer"); + + b.Property("AncientTimes") + .HasColumnType("integer"); + + b.Property("AttributeId") + .HasColumnType("uuid"); + + b.Property("ExcellentAdd") + .HasColumnType("integer"); + + b.Property("ExcellentDropLevelDivisor") + .HasColumnType("integer"); + + b.Property("ExcellentState") + .HasColumnType("integer"); + + b.Property("ExcellentTimes") + .HasColumnType("integer"); + + b.Property("ItemStatProfileId") + .HasColumnType("uuid"); + + b.Property("PerLevelAfterHalving") + .HasColumnType("integer"); + + b.Property("TableId") + .HasColumnType("uuid"); + + b.Property("TableState") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("AttributeId"); + + b.HasIndex("TableId"); + + b.HasIndex("ItemStatProfileId", "AttributeId") + .IsUnique(); + + b.ToTable("ItemStatTerms", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemStorage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Money") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.ToTable("ItemStorage", "data"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.JewelMix", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("GameConfigurationId") + .HasColumnType("uuid"); + + b.Property("MixedJewelId") + .HasColumnType("uuid"); + + b.Property("Number") + .HasColumnType("smallint"); + + b.Property("SingleJewelId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("GameConfigurationId"); + + b.HasIndex("MixedJewelId"); + + b.HasIndex("SingleJewelId"); + + b.ToTable("JewelMix", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.LetterBody", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Animation") + .HasColumnType("smallint"); + + b.Property("HeaderId") + .HasColumnType("uuid"); + + b.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b.Property("Rotation") + .HasColumnType("smallint"); + + b.Property("SenderAppearanceId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("HeaderId"); + + b.HasIndex("SenderAppearanceId") + .IsUnique(); + + b.ToTable("LetterBody", "data"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.LetterHeader", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("LetterDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ReadFlag") + .HasColumnType("boolean"); + + b.Property("ReceiverId") + .HasColumnType("uuid"); + + b.Property("SenderName") + .HasColumnType("text"); + + b.Property("Subject") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ReceiverId"); + + b.ToTable("LetterHeader", "data"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.LevelBonus", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AdditionalValue") + .HasColumnType("real"); + + b.Property("ItemLevelBonusTableId") + .HasColumnType("uuid"); + + b.Property("Level") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("ItemLevelBonusTableId"); + + b.ToTable("LevelBonus", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MagicEffectDefinition", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ChanceId") + .HasColumnType("uuid"); + + b.Property("ChancePvpId") + .HasColumnType("uuid"); + + b.Property("DurationDependsOnTargetLevel") + .HasColumnType("boolean"); + + b.Property("DurationId") + .HasColumnType("uuid"); + + b.Property("DurationPvpId") + .HasColumnType("uuid"); + + b.Property("GameConfigurationId") + .HasColumnType("uuid"); + + b.Property("InformObservers") + .HasColumnType("boolean"); + + b.Property("MonsterTargetLevelDivisor") + .HasColumnType("real"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Number") + .HasColumnType("smallint"); + + b.Property("PlayerTargetLevelDivisor") + .HasColumnType("real"); + + b.Property("SendDuration") + .HasColumnType("boolean"); + + b.Property("StopByDeath") + .HasColumnType("boolean"); + + b.Property("SubType") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("ChanceId") + .IsUnique(); + + b.HasIndex("ChancePvpId") + .IsUnique(); + + b.HasIndex("DurationId") + .IsUnique(); + + b.HasIndex("DurationPvpId") + .IsUnique(); + + b.HasIndex("GameConfigurationId"); + + b.ToTable("MagicEffectDefinition", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MasterSkillDefinition", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Aggregation") + .HasColumnType("integer"); + + b.Property("DisplayValueFormula") + .IsRequired() + .HasColumnType("text"); + + b.Property("ExtendsDuration") + .HasColumnType("boolean"); + + b.Property("MaximumLevel") + .HasColumnType("smallint"); + + b.Property("MinimumLevel") + .HasColumnType("smallint"); + + b.Property("Rank") + .HasColumnType("smallint"); + + b.Property("ReplacedSkillId") + .HasColumnType("uuid"); + + b.Property("RootId") + .HasColumnType("uuid"); + + b.Property("TargetAttributeId") + .HasColumnType("uuid"); + + b.Property("ValueFormula") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ReplacedSkillId"); + + b.HasIndex("RootId"); + + b.HasIndex("TargetAttributeId"); + + b.ToTable("MasterSkillDefinition", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MasterSkillDefinitionSkill", b => + { + b.Property("MasterSkillDefinitionId") + .HasColumnType("uuid"); + + b.Property("SkillId") + .HasColumnType("uuid"); + + b.HasKey("MasterSkillDefinitionId", "SkillId"); + + b.HasIndex("SkillId"); + + b.ToTable("MasterSkillDefinitionSkill", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MasterSkillRoot", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("GameConfigurationId") + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("GameConfigurationId"); + + b.ToTable("MasterSkillRoot", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MiniGameChangeEvent", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("Index") + .HasColumnType("integer"); + + b.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b.Property("MiniGameDefinitionId") + .HasColumnType("uuid"); + + b.Property("MinimumTargetLevel") + .HasColumnType("smallint"); + + b.Property("MultiplyKillsByPlayers") + .HasColumnType("boolean"); + + b.Property("NumberOfKills") + .HasColumnType("smallint"); + + b.Property("SpawnAreaId") + .HasColumnType("uuid"); + + b.Property("Target") + .HasColumnType("integer"); + + b.Property("TargetDefinitionId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("MiniGameDefinitionId"); + + b.HasIndex("SpawnAreaId") + .IsUnique(); + + b.HasIndex("TargetDefinitionId"); + + b.ToTable("MiniGameChangeEvent", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MiniGameDefinition", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AllowParty") + .HasColumnType("boolean"); + + b.Property("ArePlayerKillersAllowedToEnter") + .HasColumnType("boolean"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("EnterDuration") + .HasColumnType("interval"); + + b.Property("EntranceFee") + .HasColumnType("integer"); + + b.Property("EntranceId") + .HasColumnType("uuid"); + + b.Property("ExitDuration") + .HasColumnType("interval"); + + b.Property("FreeTicketItemId") + .HasColumnType("uuid"); + + b.Property("GameConfigurationId") + .HasColumnType("uuid"); + + b.Property("GameDuration") + .HasColumnType("interval"); + + b.Property("GameLevel") + .HasColumnType("smallint"); + + b.Property("IntroducedIn") + .HasColumnType("integer"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("MapCreationPolicy") + .HasColumnType("integer"); + + b.Property("MaximumCharacterLevel") + .HasColumnType("integer"); + + b.Property("MaximumPlayerCount") + .HasColumnType("integer"); + + b.Property("MaximumSpecialCharacterLevel") + .HasColumnType("integer"); + + b.Property("MinimumCharacterLevel") + .HasColumnType("integer"); + + b.Property("MinimumPlayerCount") + .HasColumnType("integer"); + + b.Property("MinimumSpecialCharacterLevel") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("RequiresMasterClass") + .HasColumnType("boolean"); + + b.Property("SaveRankingStatistics") + .HasColumnType("boolean"); + + b.Property("TicketItemId") + .HasColumnType("uuid"); + + b.Property("TicketItemLevel") + .HasColumnType("integer"); + + b.Property("Type") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("EntranceId"); + + b.HasIndex("FreeTicketItemId"); + + b.HasIndex("GameConfigurationId"); + + b.HasIndex("TicketItemId"); + + b.ToTable("MiniGameDefinition", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MiniGameRankingEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CharacterId") + .HasColumnType("uuid"); + + b.Property("GameInstanceId") + .HasColumnType("uuid"); + + b.Property("MiniGameId") + .HasColumnType("uuid"); + + b.Property("Rank") + .HasColumnType("integer"); + + b.Property("Score") + .HasColumnType("integer"); + + b.Property("Timestamp") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("CharacterId"); + + b.HasIndex("MiniGameId"); + + b.ToTable("MiniGameRankingEntry", "data"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MiniGameReward", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ItemRewardId") + .HasColumnType("uuid"); + + b.Property("MiniGameDefinitionId") + .HasColumnType("uuid"); + + b.Property("Rank") + .HasColumnType("integer"); + + b.Property("RequiredKillId") + .HasColumnType("uuid"); + + b.Property("RequiredSuccess") + .HasColumnType("integer"); + + b.Property("RewardAmount") + .HasColumnType("integer"); + + b.Property("RewardType") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("ItemRewardId"); + + b.HasIndex("MiniGameDefinitionId"); + + b.HasIndex("RequiredKillId"); + + b.ToTable("MiniGameReward", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MiniGameSpawnWave", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("EndTime") + .HasColumnType("interval"); + + b.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b.Property("MiniGameDefinitionId") + .HasColumnType("uuid"); + + b.Property("StartTime") + .HasColumnType("interval"); + + b.Property("WaveNumber") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("MiniGameDefinitionId"); + + b.ToTable("MiniGameSpawnWave", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MiniGameTerrainChange", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("EndX") + .HasColumnType("smallint"); + + b.Property("EndY") + .HasColumnType("smallint"); + + b.Property("IsClientUpdateRequired") + .HasColumnType("boolean"); + + b.Property("MiniGameChangeEventId") + .HasColumnType("uuid"); + + b.Property("SetTerrainAttribute") + .HasColumnType("boolean"); + + b.Property("StartX") + .HasColumnType("smallint"); + + b.Property("StartY") + .HasColumnType("smallint"); + + b.Property("TerrainAttribute") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("MiniGameChangeEventId"); + + b.ToTable("MiniGameTerrainChange", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MonsterAttribute", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AttributeDefinitionId") + .HasColumnType("uuid"); + + b.Property("MonsterDefinitionId") + .HasColumnType("uuid"); + + b.Property("Value") + .HasColumnType("real"); + + b.HasKey("Id"); + + b.HasIndex("AttributeDefinitionId"); + + b.HasIndex("MonsterDefinitionId"); + + b.ToTable("MonsterAttribute", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MonsterDefinition", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AttackDelay") + .HasColumnType("interval"); + + b.Property("AttackRange") + .HasColumnType("smallint"); + + b.Property("AttackSkillId") + .HasColumnType("uuid"); + + b.Property("Attribute") + .HasColumnType("smallint"); + + b.Property("Designation") + .IsRequired() + .HasColumnType("text"); + + b.Property("GameConfigurationId") + .HasColumnType("uuid"); + + b.Property("IntelligenceTypeName") + .HasColumnType("text"); + + b.Property("IntroducedIn") + .HasColumnType("integer"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("MerchantStoreId") + .HasColumnType("uuid"); + + b.Property("MoveDelay") + .HasColumnType("interval"); + + b.Property("MoveRange") + .HasColumnType("smallint"); + + b.Property("NpcWindow") + .HasColumnType("integer"); + + b.Property("Number") + .HasColumnType("smallint"); + + b.Property("NumberOfMaximumItemDrops") + .HasColumnType("integer"); + + b.Property("ObjectKind") + .HasColumnType("integer"); + + b.Property("RespawnDelay") + .HasColumnType("interval"); + + b.Property("ViewRange") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("AttackSkillId"); + + b.HasIndex("GameConfigurationId"); + + b.HasIndex("MerchantStoreId") + .IsUnique(); + + b.ToTable("MonsterDefinition", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MonsterDefinitionDropItemGroup", b => + { + b.Property("MonsterDefinitionId") + .HasColumnType("uuid"); + + b.Property("DropItemGroupId") + .HasColumnType("uuid"); + + b.HasKey("MonsterDefinitionId", "DropItemGroupId"); + + b.HasIndex("DropItemGroupId"); + + b.ToTable("MonsterDefinitionDropItemGroup", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MonsterSpawnArea", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Direction") + .HasColumnType("integer"); + + b.Property("GameMapId") + .HasColumnType("uuid"); + + b.Property("MaximumHealthOverride") + .HasColumnType("integer"); + + b.Property("MonsterDefinitionId") + .HasColumnType("uuid"); + + b.Property("Quantity") + .HasColumnType("smallint"); + + b.Property("SpawnTrigger") + .HasColumnType("integer"); + + b.Property("WaveNumber") + .HasColumnType("smallint"); + + b.Property("X1") + .HasColumnType("smallint"); + + b.Property("X2") + .HasColumnType("smallint"); + + b.Property("Y1") + .HasColumnType("smallint"); + + b.Property("Y2") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("GameMapId"); + + b.HasIndex("MonsterDefinitionId"); + + b.ToTable("MonsterSpawnArea", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.PlugInConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CustomConfiguration") + .HasColumnType("text"); + + b.Property("CustomPlugInSource") + .HasColumnType("text"); + + b.Property("ExternalAssemblyName") + .HasColumnType("text"); + + b.Property("GameConfigurationId") + .HasColumnType("uuid"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("TypeId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("GameConfigurationId"); + + b.ToTable("PlugInConfiguration", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.PowerUpDefinition", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BoostId") + .HasColumnType("uuid"); + + b.Property("GameMapDefinitionId") + .HasColumnType("uuid"); + + b.Property("MagicEffectDefinitionId") + .HasColumnType("uuid"); + + b.Property("MagicEffectDefinitionId1") + .HasColumnType("uuid"); + + b.Property("MasterSkillDefinitionId") + .HasColumnType("uuid"); + + b.Property("TargetAttributeId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("BoostId") + .IsUnique(); + + b.HasIndex("GameMapDefinitionId"); + + b.HasIndex("MagicEffectDefinitionId"); + + b.HasIndex("MagicEffectDefinitionId1"); + + b.HasIndex("MasterSkillDefinitionId"); + + b.HasIndex("TargetAttributeId"); + + b.ToTable("PowerUpDefinition", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.PowerUpDefinitionValue", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AggregateType") + .HasColumnType("integer"); + + b.Property("MaximumValue") + .HasColumnType("real"); + + b.Property("Value") + .HasColumnType("real"); + + b.HasKey("Id"); + + b.ToTable("PowerUpDefinitionValue", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.QuestDefinition", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Group") + .HasColumnType("smallint"); + + b.Property("MaximumCharacterLevel") + .HasColumnType("integer"); + + b.Property("MinimumCharacterLevel") + .HasColumnType("integer"); + + b.Property("MonsterDefinitionId") + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Number") + .HasColumnType("smallint"); + + b.Property("QualifiedCharacterId") + .HasColumnType("uuid"); + + b.Property("QuestGiverId") + .HasColumnType("uuid"); + + b.Property("RefuseNumber") + .HasColumnType("smallint"); + + b.Property("Repeatable") + .HasColumnType("boolean"); + + b.Property("RequiredStartMoney") + .HasColumnType("integer"); + + b.Property("RequiresClientAction") + .HasColumnType("boolean"); + + b.Property("StartingNumber") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("MonsterDefinitionId"); + + b.HasIndex("QualifiedCharacterId"); + + b.HasIndex("QuestGiverId"); + + b.ToTable("QuestDefinition", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.QuestItemRequirement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("DropItemGroupId") + .HasColumnType("uuid"); + + b.Property("ItemId") + .HasColumnType("uuid"); + + b.Property("MinimumNumber") + .HasColumnType("integer"); + + b.Property("QuestDefinitionId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("DropItemGroupId"); + + b.HasIndex("ItemId"); + + b.HasIndex("QuestDefinitionId"); + + b.ToTable("QuestItemRequirement", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.QuestMonsterKillRequirement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("MinimumNumber") + .HasColumnType("integer"); + + b.Property("MonsterId") + .HasColumnType("uuid"); + + b.Property("QuestDefinitionId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("MonsterId"); + + b.HasIndex("QuestDefinitionId"); + + b.ToTable("QuestMonsterKillRequirement", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.QuestMonsterKillRequirementState", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CharacterQuestStateId") + .HasColumnType("uuid"); + + b.Property("KillCount") + .HasColumnType("integer"); + + b.Property("RequirementId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("CharacterQuestStateId"); + + b.HasIndex("RequirementId"); + + b.ToTable("QuestMonsterKillRequirementState", "data"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.QuestReward", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AttributeRewardId") + .HasColumnType("uuid"); + + b.Property("ItemRewardId") + .HasColumnType("uuid"); + + b.Property("QuestDefinitionId") + .HasColumnType("uuid"); + + b.Property("RewardType") + .HasColumnType("integer"); + + b.Property("SkillRewardId") + .HasColumnType("uuid"); + + b.Property("Value") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("AttributeRewardId"); + + b.HasIndex("ItemRewardId") + .IsUnique(); + + b.HasIndex("QuestDefinitionId"); + + b.HasIndex("SkillRewardId"); + + b.ToTable("QuestReward", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.Rectangle", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("X1") + .HasColumnType("smallint"); + + b.Property("X2") + .HasColumnType("smallint"); + + b.Property("Y1") + .HasColumnType("smallint"); + + b.Property("Y2") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.ToTable("Rectangle", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.SimpleCraftingSettings", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("MaximumSuccessPercent") + .HasColumnType("smallint"); + + b.Property("Money") + .HasColumnType("integer"); + + b.Property("MoneyPerFinalSuccessPercentage") + .HasColumnType("integer"); + + b.Property("MultipleAllowed") + .HasColumnType("boolean"); + + b.Property("NpcPriceDivisor") + .HasColumnType("integer"); + + b.Property("ResultItemExcellentOptionChance") + .HasColumnType("smallint"); + + b.Property("ResultItemLuckOptionChance") + .HasColumnType("smallint"); + + b.Property("ResultItemMaxExcOptionCount") + .HasColumnType("smallint"); + + b.Property("ResultItemSelect") + .HasColumnType("integer"); + + b.Property("ResultItemSkillChance") + .HasColumnType("smallint"); + + b.Property("SuccessPercent") + .HasColumnType("smallint"); + + b.Property("SuccessPercentageAdditionForAncientItem") + .HasColumnType("integer"); + + b.Property("SuccessPercentageAdditionForExcellentItem") + .HasColumnType("integer"); + + b.Property("SuccessPercentageAdditionForGuardianItem") + .HasColumnType("integer"); + + b.Property("SuccessPercentageAdditionForLuck") + .HasColumnType("integer"); + + b.Property("SuccessPercentageAdditionForSocketItem") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.ToTable("SimpleCraftingSettings", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.Skill", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AreaSkillSettingsId") + .HasColumnType("uuid"); + + b.Property("AttackDamage") + .HasColumnType("integer"); + + b.Property("DamageType") + .HasColumnType("integer"); + + b.Property("ElementalModifierTargetId") + .HasColumnType("uuid"); + + b.Property("GameConfigurationId") + .HasColumnType("uuid"); + + b.Property("ImplicitTargetRange") + .HasColumnType("smallint"); + + b.Property("MagicEffectDefId") + .HasColumnType("uuid"); + + b.Property("MasterDefinitionId") + .HasColumnType("uuid"); + + b.Property("MovesTarget") + .HasColumnType("boolean"); + + b.Property("MovesToTarget") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Number") + .HasColumnType("smallint"); + + b.Property("NumberOfHitsPerAttack") + .HasColumnType("smallint"); + + b.Property("Range") + .HasColumnType("smallint"); + + b.Property("SkillType") + .HasColumnType("integer"); + + b.Property("SkipElementalModifier") + .HasColumnType("boolean"); + + b.Property("Target") + .HasColumnType("integer"); + + b.Property("TargetRestriction") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("AreaSkillSettingsId") + .IsUnique(); + + b.HasIndex("ElementalModifierTargetId"); + + b.HasIndex("GameConfigurationId"); + + b.HasIndex("MagicEffectDefId"); + + b.HasIndex("MasterDefinitionId") + .IsUnique(); + + b.ToTable("Skill", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.SkillCharacterClass", b => + { + b.Property("SkillId") + .HasColumnType("uuid"); + + b.Property("CharacterClassId") + .HasColumnType("uuid"); + + b.HasKey("SkillId", "CharacterClassId"); + + b.HasIndex("CharacterClassId"); + + b.ToTable("SkillCharacterClass", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.SkillComboDefinition", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("MaximumCompletionTime") + .HasColumnType("interval"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("SkillComboDefinition", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.SkillComboStep", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("IsFinalStep") + .HasColumnType("boolean"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("SkillComboDefinitionId") + .HasColumnType("uuid"); + + b.Property("SkillId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("SkillComboDefinitionId"); + + b.HasIndex("SkillId"); + + b.ToTable("SkillComboStep", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.SkillEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CharacterId") + .HasColumnType("uuid"); + + b.Property("Level") + .HasColumnType("integer"); + + b.Property("SkillId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("CharacterId"); + + b.HasIndex("SkillId"); + + b.ToTable("SkillEntry", "data"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.StatAttribute", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AccountId") + .HasColumnType("uuid"); + + b.Property("CharacterId") + .HasColumnType("uuid"); + + b.Property("DefinitionId") + .HasColumnType("uuid"); + + b.Property("Value") + .HasColumnType("real"); + + b.HasKey("Id"); + + b.HasIndex("AccountId"); + + b.HasIndex("CharacterId"); + + b.HasIndex("DefinitionId"); + + b.ToTable("StatAttribute", "data"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.StatAttributeDefinition", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AttributeId") + .HasColumnType("uuid"); + + b.Property("BaseValue") + .HasColumnType("real"); + + b.Property("CharacterClassId") + .HasColumnType("uuid"); + + b.Property("IncreasableByPlayer") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.HasIndex("AttributeId"); + + b.HasIndex("CharacterClassId"); + + b.ToTable("StatAttributeDefinition", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.SystemConfiguration", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AutoStart") + .HasColumnType("boolean"); + + b.Property("AutoUpdateSchema") + .HasColumnType("boolean"); + + b.Property("IpResolver") + .HasColumnType("integer"); + + b.Property("IpResolverParameter") + .HasColumnType("text"); + + b.Property("NetworkAnalyzerLiveBufferSize") + .HasColumnType("integer"); + + b.Property("NetworkObservationArchivePath") + .HasColumnType("text"); + + b.Property("NetworkObservationMaxSessionSizeMb") + .HasColumnType("integer"); + + b.Property("NetworkObservationMaxTotalSizeMb") + .HasColumnType("integer"); + + b.Property("NetworkObservationRetentionDays") + .HasColumnType("integer"); + + b.Property("ReadConsoleInput") + .HasColumnType("boolean"); + + b.Property("TimeZoneId") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("SystemConfiguration", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.WaitingItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AddedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CharacterId") + .HasColumnType("uuid"); + + b.Property("ItemId") + .HasColumnType("uuid"); + + b.Property("Reason") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("CharacterId"); + + b.HasIndex("ItemId") + .IsUnique(); + + b.ToTable("WaitingItem", "data"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.WarpInfo", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Costs") + .HasColumnType("integer"); + + b.Property("GameConfigurationId") + .HasColumnType("uuid"); + + b.Property("GateId") + .HasColumnType("uuid"); + + b.Property("Index") + .HasColumnType("integer"); + + b.Property("IsWarpable") + .HasColumnType("boolean"); + + b.Property("LevelRequirement") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("GameConfigurationId"); + + b.HasIndex("GateId"); + + b.ToTable("WarpInfo", "config"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.Account", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemStorage", "RawVault") + .WithOne() + .HasForeignKey("MUnique.OpenMU.Persistence.EntityFramework.Model.Account", "VaultId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("RawVault"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.AccountAttributeGrant", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Account", null) + .WithMany("RawAttributeGrants") + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.AttributeDefinition", "RawAttribute") + .WithMany() + .HasForeignKey("AttributeId"); + + b.Navigation("RawAttribute"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.AccountCharacterClass", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Account", "Account") + .WithMany("JoinedUnlockedCharacterClasses") + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.CharacterClass", "CharacterClass") + .WithMany() + .HasForeignKey("CharacterClassId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Account"); + + b.Navigation("CharacterClass"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.AccountExternalLink", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Account", null) + .WithMany() + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.AppearanceData", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.CharacterClass", "RawCharacterClass") + .WithMany() + .HasForeignKey("CharacterClassId"); + + b.Navigation("RawCharacterClass"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.AttributeDefinition", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameConfiguration", null) + .WithMany("RawAttributes") + .HasForeignKey("GameConfigurationId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.AttributeRelationship", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.CharacterClass", null) + .WithMany("RawAttributeCombinations") + .HasForeignKey("CharacterClassId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameConfiguration", null) + .WithMany("RawGlobalAttributeCombinations") + .HasForeignKey("GameConfigurationId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.AttributeDefinition", "RawInputAttribute") + .WithMany() + .HasForeignKey("InputAttributeId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.AttributeDefinition", "RawOperandAttribute") + .WithMany() + .HasForeignKey("OperandAttributeId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.PowerUpDefinitionValue", null) + .WithMany("RawRelatedValues") + .HasForeignKey("PowerUpDefinitionValueId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Skill", null) + .WithMany("RawAttributeRelationships") + .HasForeignKey("SkillId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.AttributeDefinition", "RawTargetAttribute") + .WithMany() + .HasForeignKey("TargetAttributeId"); + + b.Navigation("RawInputAttribute"); + + b.Navigation("RawOperandAttribute"); + + b.Navigation("RawTargetAttribute"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.AttributeRequirement", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.AttributeDefinition", "RawAttribute") + .WithMany() + .HasForeignKey("AttributeId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameMapDefinition", null) + .WithMany("RawMapRequirements") + .HasForeignKey("GameMapDefinitionId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameServerDefinition", null) + .WithMany("RawJoinRequirements") + .HasForeignKey("GameServerDefinitionId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDefinition", null) + .WithMany("RawRequirements") + .HasForeignKey("ItemDefinitionId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Skill", null) + .WithMany("RawConsumeRequirements") + .HasForeignKey("SkillId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Skill", null) + .WithMany("RawRequirements") + .HasForeignKey("SkillId1") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("RawAttribute"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.BattleZoneDefinition", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Rectangle", "RawGround") + .WithOne() + .HasForeignKey("MUnique.OpenMU.Persistence.EntityFramework.Model.BattleZoneDefinition", "GroundId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Rectangle", "RawLeftGoal") + .WithOne() + .HasForeignKey("MUnique.OpenMU.Persistence.EntityFramework.Model.BattleZoneDefinition", "LeftGoalId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Rectangle", "RawRightGoal") + .WithOne() + .HasForeignKey("MUnique.OpenMU.Persistence.EntityFramework.Model.BattleZoneDefinition", "RightGoalId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("RawGround"); + + b.Navigation("RawLeftGoal"); + + b.Navigation("RawRightGoal"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.Buff", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.MagicEffectDefinition", "RawMagicEffectDefinition") + .WithOne() + .HasForeignKey("MUnique.OpenMU.Persistence.EntityFramework.Model.Buff", "MagicEffectDefinitionId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.MonsterDefinition", null) + .WithMany("RawBuffs") + .HasForeignKey("MonsterDefinitionId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("RawMagicEffectDefinition"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CashShopCoinGrant", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Account", null) + .WithMany() + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CashShopPackage", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.CashShopConfiguration", null) + .WithMany("RawPackages") + .HasForeignKey("CashShopConfigurationId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CashShopProduct", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.CashShopPackage", null) + .WithMany("RawProducts") + .HasForeignKey("CashShopPackageId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDefinition", "RawItemDefinition") + .WithMany() + .HasForeignKey("ItemDefinitionId"); + + b.Navigation("RawItemDefinition"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CashShopStorageItem", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Account", null) + .WithMany() + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CastleSiegeConfiguration", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.CastleSiegeZoneDefinition", "RawAttackRespawnArea") + .WithOne() + .HasForeignKey("MUnique.OpenMU.Persistence.EntityFramework.Model.CastleSiegeConfiguration", "AttackRespawnAreaId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameMapDefinition", "RawCastleSiegeMapDefinition") + .WithMany() + .HasForeignKey("CastleSiegeMapDefinitionId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.CastleSiegeZoneDefinition", "RawDefenseRespawnArea") + .WithOne() + .HasForeignKey("MUnique.OpenMU.Persistence.EntityFramework.Model.CastleSiegeConfiguration", "DefenseRespawnAreaId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameMapDefinition", "RawLandOfTrialsMapDefinition") + .WithMany() + .HasForeignKey("LandOfTrialsMapDefinitionId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDefinition", "RawRewardItemDefinition") + .WithMany() + .HasForeignKey("RewardItemDefinitionId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDefinition", "RawSignOfLordItemDefinition") + .WithMany() + .HasForeignKey("SignOfLordItemDefinitionId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("RawAttackRespawnArea"); + + b.Navigation("RawCastleSiegeMapDefinition"); + + b.Navigation("RawDefenseRespawnArea"); + + b.Navigation("RawLandOfTrialsMapDefinition"); + + b.Navigation("RawRewardItemDefinition"); + + b.Navigation("RawSignOfLordItemDefinition"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CastleSiegeData", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Guild", null) + .WithMany() + .HasForeignKey("OwnerGuildId") + .OnDelete(DeleteBehavior.SetNull); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CastleSiegeGuild", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.CastleSiegeData", null) + .WithMany("RawGuilds") + .HasForeignKey("CastleSiegeDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Guild", null) + .WithMany() + .HasForeignKey("GuildId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CastleSiegeGuildRegistration", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Guild", null) + .WithMany() + .HasForeignKey("GuildId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CastleSiegeNpcDefinition", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.CastleSiegeConfiguration", null) + .WithMany("RawNpcDefinitions") + .HasForeignKey("CastleSiegeConfigurationId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.MonsterDefinition", "RawMonsterDefinition") + .WithMany() + .HasForeignKey("MonsterDefinitionId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("RawMonsterDefinition"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CastleSiegeNpcState", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.CastleSiegeData", null) + .WithMany("RawNpcStates") + .HasForeignKey("CastleSiegeDataId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CastleSiegePendingReward", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Character", null) + .WithMany() + .HasForeignKey("CharacterId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDefinition", null) + .WithMany() + .HasForeignKey("ItemDefinitionId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CastleSiegeStateScheduleEntry", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.CastleSiegeConfiguration", null) + .WithMany("RawStateSchedule") + .HasForeignKey("CastleSiegeConfigurationId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CastleSiegeUpgradeDefinition", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.CastleSiegeConfiguration", null) + .WithMany("RawGateDefenseUpgrades") + .HasForeignKey("CastleSiegeConfigurationId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.CastleSiegeConfiguration", null) + .WithMany("RawGateLifeUpgrades") + .HasForeignKey("CastleSiegeConfigurationId1") + .OnDelete(DeleteBehavior.Cascade) + .HasConstraintName("FK_CastleSiegeUpgradeDefinition_CastleSiegeConfiguration_Cast~1"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.CastleSiegeConfiguration", null) + .WithMany("RawStatueDefenseUpgrades") + .HasForeignKey("CastleSiegeConfigurationId2") + .OnDelete(DeleteBehavior.Cascade) + .HasConstraintName("FK_CastleSiegeUpgradeDefinition_CastleSiegeConfiguration_Cast~2"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.CastleSiegeConfiguration", null) + .WithMany("RawStatueLifeUpgrades") + .HasForeignKey("CastleSiegeConfigurationId3") + .OnDelete(DeleteBehavior.Cascade) + .HasConstraintName("FK_CastleSiegeUpgradeDefinition_CastleSiegeConfiguration_Cast~3"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.CastleSiegeConfiguration", null) + .WithMany("RawStatueRegenUpgrades") + .HasForeignKey("CastleSiegeConfigurationId4") + .OnDelete(DeleteBehavior.Cascade) + .HasConstraintName("FK_CastleSiegeUpgradeDefinition_CastleSiegeConfiguration_Cast~4"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CastleSiegeZoneDefinition", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.CastleSiegeConfiguration", null) + .WithMany("RawAttackMachineZones") + .HasForeignKey("CastleSiegeConfigurationId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.CastleSiegeConfiguration", null) + .WithMany("RawDefenseMachineZones") + .HasForeignKey("CastleSiegeConfigurationId1") + .OnDelete(DeleteBehavior.Cascade) + .HasConstraintName("FK_CastleSiegeZoneDefinition_CastleSiegeConfiguration_CastleS~1"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.Character", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Account", null) + .WithMany("RawCharacters") + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.CharacterClass", "RawCharacterClass") + .WithMany() + .HasForeignKey("CharacterClassId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameMapDefinition", "RawCurrentMap") + .WithMany() + .HasForeignKey("CurrentMapId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemStorage", "RawInventory") + .WithOne() + .HasForeignKey("MUnique.OpenMU.Persistence.EntityFramework.Model.Character", "InventoryId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("RawCharacterClass"); + + b.Navigation("RawCurrentMap"); + + b.Navigation("RawInventory"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CharacterClass", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.SkillComboDefinition", "RawComboDefinition") + .WithOne() + .HasForeignKey("MUnique.OpenMU.Persistence.EntityFramework.Model.CharacterClass", "ComboDefinitionId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameConfiguration", null) + .WithMany("RawCharacterClasses") + .HasForeignKey("GameConfigurationId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameMapDefinition", "RawHomeMap") + .WithMany() + .HasForeignKey("HomeMapId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.CharacterClass", "RawNextGenerationClass") + .WithMany() + .HasForeignKey("NextGenerationClassId"); + + b.Navigation("RawComboDefinition"); + + b.Navigation("RawHomeMap"); + + b.Navigation("RawNextGenerationClass"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CharacterDropItemGroup", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Character", "Character") + .WithMany("JoinedDropItemGroups") + .HasForeignKey("CharacterId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.DropItemGroup", "DropItemGroup") + .WithMany() + .HasForeignKey("DropItemGroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Character"); + + b.Navigation("DropItemGroup"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CharacterQuestState", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.QuestDefinition", "RawActiveQuest") + .WithMany() + .HasForeignKey("ActiveQuestId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Character", null) + .WithMany("RawQuestStates") + .HasForeignKey("CharacterId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.QuestDefinition", "RawLastFinishedQuest") + .WithMany() + .HasForeignKey("LastFinishedQuestId"); + + b.Navigation("RawActiveQuest"); + + b.Navigation("RawLastFinishedQuest"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ChatServerEndpoint", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ChatServerDefinition", null) + .WithMany("RawEndpoints") + .HasForeignKey("ChatServerDefinitionId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameClientDefinition", "RawClient") + .WithMany() + .HasForeignKey("ClientId"); + + b.Navigation("RawClient"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CombinationBonusRequirement", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemOptionCombinationBonus", null) + .WithMany("RawRequirements") + .HasForeignKey("ItemOptionCombinationBonusId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemOptionType", "RawOptionType") + .WithMany() + .HasForeignKey("OptionTypeId"); + + b.Navigation("RawOptionType"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ConnectServerDefinition", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameClientDefinition", "RawClient") + .WithMany() + .HasForeignKey("ClientId"); + + b.Navigation("RawClient"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ConstValueAttribute", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.CharacterClass", "CharacterClass") + .WithMany("RawBaseAttributeValues") + .HasForeignKey("CharacterClassId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.AttributeDefinition", "RawDefinition") + .WithMany() + .HasForeignKey("DefinitionId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameConfiguration", "GameConfiguration") + .WithMany("RawGlobalBaseAttributeValues") + .HasForeignKey("GameConfigurationId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("CharacterClass"); + + b.Navigation("GameConfiguration"); + + b.Navigation("RawDefinition"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.DropItemGroup", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameConfiguration", null) + .WithMany("RawDropItemGroups") + .HasForeignKey("GameConfigurationId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.MonsterDefinition", "RawMonster") + .WithMany() + .HasForeignKey("MonsterId"); + + b.Navigation("RawMonster"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.DropItemGroupItemDefinition", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.DropItemGroup", "DropItemGroup") + .WithMany("JoinedPossibleItems") + .HasForeignKey("DropItemGroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDefinition", "ItemDefinition") + .WithMany() + .HasForeignKey("ItemDefinitionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("DropItemGroup"); + + b.Navigation("ItemDefinition"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.DuelArea", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.DuelConfiguration", null) + .WithMany("RawDuelAreas") + .HasForeignKey("DuelConfigurationId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ExitGate", "RawFirstPlayerGate") + .WithMany() + .HasForeignKey("FirstPlayerGateId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ExitGate", "RawSecondPlayerGate") + .WithMany() + .HasForeignKey("SecondPlayerGateId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ExitGate", "RawSpectatorsGate") + .WithMany() + .HasForeignKey("SpectatorsGateId"); + + b.Navigation("RawFirstPlayerGate"); + + b.Navigation("RawSecondPlayerGate"); + + b.Navigation("RawSpectatorsGate"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.DuelConfiguration", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ExitGate", "RawExit") + .WithMany() + .HasForeignKey("ExitId"); + + b.Navigation("RawExit"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.EnterGate", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameMapDefinition", null) + .WithMany("RawEnterGates") + .HasForeignKey("GameMapDefinitionId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ExitGate", "RawTargetGate") + .WithMany() + .HasForeignKey("TargetGateId"); + + b.Navigation("RawTargetGate"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ExitGate", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameMapDefinition", "RawMap") + .WithMany("RawExitGates") + .HasForeignKey("MapId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("RawMap"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.GameConfiguration", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.CashShopConfiguration", "RawCashShopConfiguration") + .WithOne() + .HasForeignKey("MUnique.OpenMU.Persistence.EntityFramework.Model.GameConfiguration", "CashShopConfigurationId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.CastleSiegeConfiguration", "RawCastleSiegeConfiguration") + .WithOne() + .HasForeignKey("MUnique.OpenMU.Persistence.EntityFramework.Model.GameConfiguration", "CastleSiegeConfigurationId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.DuelConfiguration", "RawDuelConfiguration") + .WithOne() + .HasForeignKey("MUnique.OpenMU.Persistence.EntityFramework.Model.GameConfiguration", "DuelConfigurationId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("RawCashShopConfiguration"); + + b.Navigation("RawCastleSiegeConfiguration"); + + b.Navigation("RawDuelConfiguration"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.GameMapDefinition", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.BattleZoneDefinition", "RawBattleZone") + .WithOne() + .HasForeignKey("MUnique.OpenMU.Persistence.EntityFramework.Model.GameMapDefinition", "BattleZoneId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameConfiguration", null) + .WithMany("RawMaps") + .HasForeignKey("GameConfigurationId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameMapDefinition", "RawSafezoneMap") + .WithMany() + .HasForeignKey("SafezoneMapId"); + + b.Navigation("RawBattleZone"); + + b.Navigation("RawSafezoneMap"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.GameMapDefinitionDropItemGroup", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.DropItemGroup", "DropItemGroup") + .WithMany() + .HasForeignKey("DropItemGroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameMapDefinition", "GameMapDefinition") + .WithMany("JoinedDropItemGroups") + .HasForeignKey("GameMapDefinitionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("DropItemGroup"); + + b.Navigation("GameMapDefinition"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.GameMapTerrainVariant", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameMapDefinition", null) + .WithMany("RawTerrainVariants") + .HasForeignKey("GameMapDefinitionId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.GameServerConfigurationGameMapDefinition", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameMapDefinition", "GameMapDefinition") + .WithMany() + .HasForeignKey("GameMapDefinitionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameServerConfiguration", "GameServerConfiguration") + .WithMany("JoinedMaps") + .HasForeignKey("GameServerConfigurationId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("GameMapDefinition"); + + b.Navigation("GameServerConfiguration"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.GameServerDefinition", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameConfiguration", "RawGameConfiguration") + .WithMany() + .HasForeignKey("GameConfigurationId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameServerConfiguration", "RawServerConfiguration") + .WithMany() + .HasForeignKey("ServerConfigurationId"); + + b.Navigation("RawGameConfiguration"); + + b.Navigation("RawServerConfiguration"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.GameServerEndpoint", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameClientDefinition", "RawClient") + .WithMany() + .HasForeignKey("ClientId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameServerDefinition", null) + .WithMany("RawEndpoints") + .HasForeignKey("GameServerDefinitionId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("RawClient"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.GensAbuse", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Character", null) + .WithMany() + .HasForeignKey("KillerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Character", null) + .WithMany() + .HasForeignKey("VictimId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.GensMember", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Character", null) + .WithMany() + .HasForeignKey("CharacterId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.Guild", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Guild", "RawAllianceGuild") + .WithMany() + .HasForeignKey("AllianceGuildId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Guild", "RawHostility") + .WithMany() + .HasForeignKey("HostilityId"); + + b.Navigation("RawAllianceGuild"); + + b.Navigation("RawHostility"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.GuildChatBinding", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Guild", null) + .WithMany() + .HasForeignKey("GuildId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.GuildMember", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Guild", null) + .WithMany("RawMembers") + .HasForeignKey("GuildId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Character", "Character") + .WithMany() + .HasForeignKey("Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Character"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.IncreasableItemOption", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemOptionDefinition", null) + .WithMany("RawPossibleOptions") + .HasForeignKey("ItemOptionDefinitionId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemOptionType", "RawOptionType") + .WithMany() + .HasForeignKey("OptionTypeId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.PowerUpDefinition", "RawPowerUpDefinition") + .WithOne() + .HasForeignKey("MUnique.OpenMU.Persistence.EntityFramework.Model.IncreasableItemOption", "PowerUpDefinitionId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("RawOptionType"); + + b.Navigation("RawPowerUpDefinition"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.Item", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDefinition", "RawDefinition") + .WithMany() + .HasForeignKey("DefinitionId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemStorage", "RawItemStorage") + .WithMany("RawItems") + .HasForeignKey("ItemStorageId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDefinition", "RawStorePriceItemDefinition") + .WithMany() + .HasForeignKey("StorePriceItemDefinitionId"); + + b.Navigation("RawDefinition"); + + b.Navigation("RawItemStorage"); + + b.Navigation("RawStorePriceItemDefinition"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemAppearance", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.AppearanceData", null) + .WithMany("RawEquippedItems") + .HasForeignKey("AppearanceDataId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDefinition", "RawDefinition") + .WithMany() + .HasForeignKey("DefinitionId"); + + b.Navigation("RawDefinition"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemAppearanceItemOptionType", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemAppearance", "ItemAppearance") + .WithMany("JoinedVisibleOptions") + .HasForeignKey("ItemAppearanceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemOptionType", "ItemOptionType") + .WithMany() + .HasForeignKey("ItemOptionTypeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ItemAppearance"); + + b.Navigation("ItemOptionType"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemBasePowerUpDefinition", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemLevelBonusTable", "RawBonusPerLevelTable") + .WithMany() + .HasForeignKey("BonusPerLevelTableId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDefinition", null) + .WithMany("RawBasePowerUpAttributes") + .HasForeignKey("ItemDefinitionId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.AttributeDefinition", "RawTargetAttribute") + .WithMany() + .HasForeignKey("TargetAttributeId"); + + b.Navigation("RawBonusPerLevelTable"); + + b.Navigation("RawTargetAttribute"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemCrafting", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.MonsterDefinition", null) + .WithMany("RawItemCraftings") + .HasForeignKey("MonsterDefinitionId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.SimpleCraftingSettings", "RawSimpleCraftingSettings") + .WithOne() + .HasForeignKey("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemCrafting", "SimpleCraftingSettingsId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("RawSimpleCraftingSettings"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemCraftingRequiredItem", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.SimpleCraftingSettings", null) + .WithMany("RawRequiredItems") + .HasForeignKey("SimpleCraftingSettingsId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemCraftingRequiredItemItemDefinition", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemCraftingRequiredItem", "ItemCraftingRequiredItem") + .WithMany("JoinedPossibleItems") + .HasForeignKey("ItemCraftingRequiredItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDefinition", "ItemDefinition") + .WithMany() + .HasForeignKey("ItemDefinitionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ItemCraftingRequiredItem"); + + b.Navigation("ItemDefinition"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemCraftingRequiredItemItemOptionType", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemCraftingRequiredItem", "ItemCraftingRequiredItem") + .WithMany("JoinedRequiredItemOptions") + .HasForeignKey("ItemCraftingRequiredItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemOptionType", "ItemOptionType") + .WithMany() + .HasForeignKey("ItemOptionTypeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ItemCraftingRequiredItem"); + + b.Navigation("ItemOptionType"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemCraftingResultItem", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDefinition", "RawItemDefinition") + .WithMany() + .HasForeignKey("ItemDefinitionId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.SimpleCraftingSettings", null) + .WithMany("RawResultItems") + .HasForeignKey("SimpleCraftingSettingsId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("RawItemDefinition"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDefinition", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.MagicEffectDefinition", "RawConsumeEffect") + .WithMany() + .HasForeignKey("ConsumeEffectId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameConfiguration", null) + .WithMany("RawItems") + .HasForeignKey("GameConfigurationId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemSlotType", "RawItemSlot") + .WithMany() + .HasForeignKey("ItemSlotId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemPriceDefinition", "RawPriceDefinition") + .WithMany() + .HasForeignKey("PriceDefinitionId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Skill", "RawSkill") + .WithMany() + .HasForeignKey("SkillId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemStatProfile", "RawStatProfile") + .WithMany() + .HasForeignKey("StatProfileId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("RawConsumeEffect"); + + b.Navigation("RawItemSlot"); + + b.Navigation("RawPriceDefinition"); + + b.Navigation("RawSkill"); + + b.Navigation("RawStatProfile"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDefinitionCharacterClass", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.CharacterClass", "CharacterClass") + .WithMany() + .HasForeignKey("CharacterClassId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDefinition", "ItemDefinition") + .WithMany("JoinedQualifiedCharacters") + .HasForeignKey("ItemDefinitionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("CharacterClass"); + + b.Navigation("ItemDefinition"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDefinitionItemOptionDefinition", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDefinition", "ItemDefinition") + .WithMany("JoinedPossibleItemOptions") + .HasForeignKey("ItemDefinitionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemOptionDefinition", "ItemOptionDefinition") + .WithMany() + .HasForeignKey("ItemOptionDefinitionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ItemDefinition"); + + b.Navigation("ItemOptionDefinition"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDefinitionItemSetGroup", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDefinition", "ItemDefinition") + .WithMany("JoinedPossibleItemSetGroups") + .HasForeignKey("ItemDefinitionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemSetGroup", "ItemSetGroup") + .WithMany() + .HasForeignKey("ItemSetGroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ItemDefinition"); + + b.Navigation("ItemSetGroup"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDropItemGroup", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDefinition", null) + .WithMany("RawDropItems") + .HasForeignKey("ItemDefinitionId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.MonsterDefinition", "RawMonster") + .WithMany() + .HasForeignKey("MonsterId"); + + b.Navigation("RawMonster"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDropItemGroupItemDefinition", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDefinition", "ItemDefinition") + .WithMany() + .HasForeignKey("ItemDefinitionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDropItemGroup", "ItemDropItemGroup") + .WithMany("JoinedPossibleItems") + .HasForeignKey("ItemDropItemGroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ItemDefinition"); + + b.Navigation("ItemDropItemGroup"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemItemOfItemSet", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Item", "Item") + .WithMany("JoinedItemSetGroups") + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemOfItemSet", "ItemOfItemSet") + .WithMany() + .HasForeignKey("ItemOfItemSetId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Item"); + + b.Navigation("ItemOfItemSet"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemLevelBonusTable", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameConfiguration", null) + .WithMany("RawItemLevelBonusTables") + .HasForeignKey("GameConfigurationId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemLevelPrice", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemPriceDefinition", null) + .WithMany("RawPricePerLevel") + .HasForeignKey("ItemPriceDefinitionId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemOfItemSet", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.IncreasableItemOption", "RawBonusOption") + .WithMany() + .HasForeignKey("BonusOptionId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDefinition", "RawItemDefinition") + .WithMany() + .HasForeignKey("ItemDefinitionId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemSetGroup", "RawItemSetGroup") + .WithMany("RawItems") + .HasForeignKey("ItemSetGroupId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("RawBonusOption"); + + b.Navigation("RawItemDefinition"); + + b.Navigation("RawItemSetGroup"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemOption", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemOptionType", "RawOptionType") + .WithMany() + .HasForeignKey("OptionTypeId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.PowerUpDefinition", "RawPowerUpDefinition") + .WithOne() + .HasForeignKey("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemOption", "PowerUpDefinitionId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("RawOptionType"); + + b.Navigation("RawPowerUpDefinition"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemOptionCombinationBonus", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.PowerUpDefinition", "RawBonus") + .WithOne() + .HasForeignKey("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemOptionCombinationBonus", "BonusId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameConfiguration", null) + .WithMany("RawItemOptionCombinationBonuses") + .HasForeignKey("GameConfigurationId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("RawBonus"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemOptionDefinition", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameConfiguration", null) + .WithMany("RawItemOptions") + .HasForeignKey("GameConfigurationId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemOptionLink", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Item", null) + .WithMany("RawItemOptions") + .HasForeignKey("ItemId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.IncreasableItemOption", "RawItemOption") + .WithMany() + .HasForeignKey("ItemOptionId"); + + b.Navigation("RawItemOption"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemOptionOfLevel", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.IncreasableItemOption", null) + .WithMany("RawLevelDependentOptions") + .HasForeignKey("IncreasableItemOptionId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.PowerUpDefinition", "RawPowerUpDefinition") + .WithOne() + .HasForeignKey("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemOptionOfLevel", "PowerUpDefinitionId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("RawPowerUpDefinition"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemOptionType", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameConfiguration", null) + .WithMany("RawItemOptionTypes") + .HasForeignKey("GameConfigurationId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemPriceDefinition", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameConfiguration", null) + .WithMany("RawItemPriceDefinitions") + .HasForeignKey("GameConfigurationId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemRequirementFormula", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.AttributeDefinition", "RawAttribute") + .WithMany() + .HasForeignKey("AttributeId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemStatProfile", null) + .WithMany("RawRequirementFormulas") + .HasForeignKey("ItemStatProfileId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("RawAttribute"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemSetGroup", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameConfiguration", null) + .WithMany("RawItemSetGroups") + .HasForeignKey("GameConfigurationId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemOptionDefinition", "RawOptions") + .WithMany() + .HasForeignKey("OptionsId"); + + b.Navigation("RawOptions"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemSlotType", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameConfiguration", null) + .WithMany("RawItemSlotTypes") + .HasForeignKey("GameConfigurationId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemStatProfile", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameConfiguration", null) + .WithMany("RawItemStatProfiles") + .HasForeignKey("GameConfigurationId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemStatTerms", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.AttributeDefinition", "RawAttribute") + .WithMany() + .HasForeignKey("AttributeId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemStatProfile", null) + .WithMany("RawStatTerms") + .HasForeignKey("ItemStatProfileId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemLevelBonusTable", "RawTable") + .WithMany() + .HasForeignKey("TableId"); + + b.Navigation("RawAttribute"); + + b.Navigation("RawTable"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.JewelMix", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameConfiguration", null) + .WithMany("RawJewelMixes") + .HasForeignKey("GameConfigurationId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDefinition", "RawMixedJewel") + .WithMany() + .HasForeignKey("MixedJewelId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDefinition", "RawSingleJewel") + .WithMany() + .HasForeignKey("SingleJewelId"); + + b.Navigation("RawMixedJewel"); + + b.Navigation("RawSingleJewel"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.LetterBody", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.LetterHeader", "RawHeader") + .WithMany() + .HasForeignKey("HeaderId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.AppearanceData", "RawSenderAppearance") + .WithOne() + .HasForeignKey("MUnique.OpenMU.Persistence.EntityFramework.Model.LetterBody", "SenderAppearanceId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("RawHeader"); + + b.Navigation("RawSenderAppearance"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.LetterHeader", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Character", "Receiver") + .WithMany("RawLetters") + .HasForeignKey("ReceiverId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Receiver"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.LevelBonus", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemLevelBonusTable", null) + .WithMany("RawBonusPerLevel") + .HasForeignKey("ItemLevelBonusTableId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MagicEffectDefinition", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.PowerUpDefinitionValue", "RawChance") + .WithOne() + .HasForeignKey("MUnique.OpenMU.Persistence.EntityFramework.Model.MagicEffectDefinition", "ChanceId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.PowerUpDefinitionValue", "RawChancePvp") + .WithOne() + .HasForeignKey("MUnique.OpenMU.Persistence.EntityFramework.Model.MagicEffectDefinition", "ChancePvpId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.PowerUpDefinitionValue", "RawDuration") + .WithOne() + .HasForeignKey("MUnique.OpenMU.Persistence.EntityFramework.Model.MagicEffectDefinition", "DurationId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.PowerUpDefinitionValue", "RawDurationPvp") + .WithOne() + .HasForeignKey("MUnique.OpenMU.Persistence.EntityFramework.Model.MagicEffectDefinition", "DurationPvpId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameConfiguration", null) + .WithMany("RawMagicEffects") + .HasForeignKey("GameConfigurationId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("RawChance"); + + b.Navigation("RawChancePvp"); + + b.Navigation("RawDuration"); + + b.Navigation("RawDurationPvp"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MasterSkillDefinition", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Skill", "RawReplacedSkill") + .WithMany() + .HasForeignKey("ReplacedSkillId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.MasterSkillRoot", "RawRoot") + .WithMany() + .HasForeignKey("RootId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.AttributeDefinition", "RawTargetAttribute") + .WithMany() + .HasForeignKey("TargetAttributeId"); + + b.Navigation("RawReplacedSkill"); + + b.Navigation("RawRoot"); + + b.Navigation("RawTargetAttribute"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MasterSkillDefinitionSkill", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.MasterSkillDefinition", "MasterSkillDefinition") + .WithMany("JoinedRequiredMasterSkills") + .HasForeignKey("MasterSkillDefinitionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Skill", "Skill") + .WithMany() + .HasForeignKey("SkillId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MasterSkillDefinition"); + + b.Navigation("Skill"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MasterSkillRoot", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameConfiguration", null) + .WithMany("RawMasterSkillRoots") + .HasForeignKey("GameConfigurationId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MiniGameChangeEvent", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.MiniGameDefinition", null) + .WithMany("RawChangeEvents") + .HasForeignKey("MiniGameDefinitionId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.MonsterSpawnArea", "RawSpawnArea") + .WithOne() + .HasForeignKey("MUnique.OpenMU.Persistence.EntityFramework.Model.MiniGameChangeEvent", "SpawnAreaId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.MonsterDefinition", "RawTargetDefinition") + .WithMany() + .HasForeignKey("TargetDefinitionId"); + + b.Navigation("RawSpawnArea"); + + b.Navigation("RawTargetDefinition"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MiniGameDefinition", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ExitGate", "RawEntrance") + .WithMany() + .HasForeignKey("EntranceId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDefinition", "RawFreeTicketItem") + .WithMany() + .HasForeignKey("FreeTicketItemId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameConfiguration", null) + .WithMany("RawMiniGameDefinitions") + .HasForeignKey("GameConfigurationId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDefinition", "RawTicketItem") + .WithMany() + .HasForeignKey("TicketItemId"); + + b.Navigation("RawEntrance"); + + b.Navigation("RawFreeTicketItem"); + + b.Navigation("RawTicketItem"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MiniGameRankingEntry", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Character", "RawCharacter") + .WithMany() + .HasForeignKey("CharacterId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.MiniGameDefinition", "RawMiniGame") + .WithMany() + .HasForeignKey("MiniGameId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("RawCharacter"); + + b.Navigation("RawMiniGame"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MiniGameReward", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.DropItemGroup", "RawItemReward") + .WithMany() + .HasForeignKey("ItemRewardId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.MiniGameDefinition", null) + .WithMany("RawRewards") + .HasForeignKey("MiniGameDefinitionId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.MonsterDefinition", "RawRequiredKill") + .WithMany() + .HasForeignKey("RequiredKillId"); + + b.Navigation("RawItemReward"); + + b.Navigation("RawRequiredKill"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MiniGameSpawnWave", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.MiniGameDefinition", null) + .WithMany("RawSpawnWaves") + .HasForeignKey("MiniGameDefinitionId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MiniGameTerrainChange", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.MiniGameChangeEvent", null) + .WithMany("RawTerrainChanges") + .HasForeignKey("MiniGameChangeEventId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MonsterAttribute", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.AttributeDefinition", "RawAttributeDefinition") + .WithMany() + .HasForeignKey("AttributeDefinitionId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.MonsterDefinition", null) + .WithMany("RawAttributes") + .HasForeignKey("MonsterDefinitionId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("RawAttributeDefinition"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MonsterDefinition", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Skill", "RawAttackSkill") + .WithMany() + .HasForeignKey("AttackSkillId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameConfiguration", null) + .WithMany("RawMonsters") + .HasForeignKey("GameConfigurationId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemStorage", "RawMerchantStore") + .WithOne() + .HasForeignKey("MUnique.OpenMU.Persistence.EntityFramework.Model.MonsterDefinition", "MerchantStoreId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("RawAttackSkill"); + + b.Navigation("RawMerchantStore"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MonsterDefinitionDropItemGroup", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.DropItemGroup", "DropItemGroup") + .WithMany() + .HasForeignKey("DropItemGroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.MonsterDefinition", "MonsterDefinition") + .WithMany("JoinedDropItemGroups") + .HasForeignKey("MonsterDefinitionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("DropItemGroup"); + + b.Navigation("MonsterDefinition"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MonsterSpawnArea", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameMapDefinition", "RawGameMap") + .WithMany("RawMonsterSpawns") + .HasForeignKey("GameMapId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.MonsterDefinition", "RawMonsterDefinition") + .WithMany() + .HasForeignKey("MonsterDefinitionId"); + + b.Navigation("RawGameMap"); + + b.Navigation("RawMonsterDefinition"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.PlugInConfiguration", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameConfiguration", null) + .WithMany("RawPlugInConfigurations") + .HasForeignKey("GameConfigurationId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.PowerUpDefinition", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.PowerUpDefinitionValue", "RawBoost") + .WithOne() + .HasForeignKey("MUnique.OpenMU.Persistence.EntityFramework.Model.PowerUpDefinition", "BoostId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameMapDefinition", null) + .WithMany("RawCharacterPowerUpDefinitions") + .HasForeignKey("GameMapDefinitionId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.MagicEffectDefinition", null) + .WithMany("RawPowerUpDefinitions") + .HasForeignKey("MagicEffectDefinitionId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.MagicEffectDefinition", null) + .WithMany("RawPowerUpDefinitionsPvp") + .HasForeignKey("MagicEffectDefinitionId1") + .OnDelete(DeleteBehavior.Cascade) + .HasConstraintName("FK_PowerUpDefinition_MagicEffectDefinition_MagicEffectDefinit~1"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.MasterSkillDefinition", null) + .WithMany("RawPassivePowerUps") + .HasForeignKey("MasterSkillDefinitionId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.AttributeDefinition", "RawTargetAttribute") + .WithMany() + .HasForeignKey("TargetAttributeId"); + + b.Navigation("RawBoost"); + + b.Navigation("RawTargetAttribute"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.QuestDefinition", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.MonsterDefinition", null) + .WithMany("RawQuests") + .HasForeignKey("MonsterDefinitionId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.CharacterClass", "RawQualifiedCharacter") + .WithMany() + .HasForeignKey("QualifiedCharacterId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.MonsterDefinition", "RawQuestGiver") + .WithMany() + .HasForeignKey("QuestGiverId"); + + b.Navigation("RawQualifiedCharacter"); + + b.Navigation("RawQuestGiver"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.QuestItemRequirement", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.DropItemGroup", "RawDropItemGroup") + .WithMany() + .HasForeignKey("DropItemGroupId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDefinition", "RawItem") + .WithMany() + .HasForeignKey("ItemId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.QuestDefinition", null) + .WithMany("RawRequiredItems") + .HasForeignKey("QuestDefinitionId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("RawDropItemGroup"); + + b.Navigation("RawItem"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.QuestMonsterKillRequirement", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.MonsterDefinition", "RawMonster") + .WithMany() + .HasForeignKey("MonsterId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.QuestDefinition", null) + .WithMany("RawRequiredMonsterKills") + .HasForeignKey("QuestDefinitionId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("RawMonster"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.QuestMonsterKillRequirementState", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.CharacterQuestState", null) + .WithMany("RawRequirementStates") + .HasForeignKey("CharacterQuestStateId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.QuestMonsterKillRequirement", "RawRequirement") + .WithMany() + .HasForeignKey("RequirementId"); + + b.Navigation("RawRequirement"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.QuestReward", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.AttributeDefinition", "RawAttributeReward") + .WithMany() + .HasForeignKey("AttributeRewardId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Item", "RawItemReward") + .WithOne() + .HasForeignKey("MUnique.OpenMU.Persistence.EntityFramework.Model.QuestReward", "ItemRewardId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.QuestDefinition", null) + .WithMany("RawRewards") + .HasForeignKey("QuestDefinitionId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Skill", "RawSkillReward") + .WithMany() + .HasForeignKey("SkillRewardId"); + + b.Navigation("RawAttributeReward"); + + b.Navigation("RawItemReward"); + + b.Navigation("RawSkillReward"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.Skill", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.AreaSkillSettings", "RawAreaSkillSettings") + .WithOne() + .HasForeignKey("MUnique.OpenMU.Persistence.EntityFramework.Model.Skill", "AreaSkillSettingsId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.AttributeDefinition", "RawElementalModifierTarget") + .WithMany() + .HasForeignKey("ElementalModifierTargetId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameConfiguration", null) + .WithMany("RawSkills") + .HasForeignKey("GameConfigurationId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.MagicEffectDefinition", "RawMagicEffectDef") + .WithMany() + .HasForeignKey("MagicEffectDefId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.MasterSkillDefinition", "RawMasterDefinition") + .WithOne() + .HasForeignKey("MUnique.OpenMU.Persistence.EntityFramework.Model.Skill", "MasterDefinitionId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("RawAreaSkillSettings"); + + b.Navigation("RawElementalModifierTarget"); + + b.Navigation("RawMagicEffectDef"); + + b.Navigation("RawMasterDefinition"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.SkillCharacterClass", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.CharacterClass", "CharacterClass") + .WithMany() + .HasForeignKey("CharacterClassId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Skill", "Skill") + .WithMany("JoinedQualifiedCharacters") + .HasForeignKey("SkillId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("CharacterClass"); + + b.Navigation("Skill"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.SkillComboStep", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.SkillComboDefinition", null) + .WithMany("RawSteps") + .HasForeignKey("SkillComboDefinitionId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Skill", "RawSkill") + .WithMany() + .HasForeignKey("SkillId"); + + b.Navigation("RawSkill"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.SkillEntry", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Character", null) + .WithMany("RawLearnedSkills") + .HasForeignKey("CharacterId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Skill", "RawSkill") + .WithMany() + .HasForeignKey("SkillId"); + + b.Navigation("RawSkill"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.StatAttribute", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Account", null) + .WithMany("RawAttributes") + .HasForeignKey("AccountId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Character", null) + .WithMany("RawAttributes") + .HasForeignKey("CharacterId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.AttributeDefinition", "RawDefinition") + .WithMany() + .HasForeignKey("DefinitionId"); + + b.Navigation("RawDefinition"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.StatAttributeDefinition", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.AttributeDefinition", "RawAttribute") + .WithMany() + .HasForeignKey("AttributeId"); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.CharacterClass", null) + .WithMany("RawStatAttributes") + .HasForeignKey("CharacterClassId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("RawAttribute"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.WaitingItem", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Character", null) + .WithMany("RawWaitingItems") + .HasForeignKey("CharacterId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Item", "RawItem") + .WithOne() + .HasForeignKey("MUnique.OpenMU.Persistence.EntityFramework.Model.WaitingItem", "ItemId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("RawItem"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.WarpInfo", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameConfiguration", null) + .WithMany("RawWarpList") + .HasForeignKey("GameConfigurationId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.ExitGate", "RawGate") + .WithMany() + .HasForeignKey("GateId"); + + b.Navigation("RawGate"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.Account", b => + { + b.Navigation("JoinedUnlockedCharacterClasses"); + + b.Navigation("RawAttributeGrants"); + + b.Navigation("RawAttributes"); + + b.Navigation("RawCharacters"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.AppearanceData", b => + { + b.Navigation("RawEquippedItems"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CashShopConfiguration", b => + { + b.Navigation("RawPackages"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CashShopPackage", b => + { + b.Navigation("RawProducts"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CastleSiegeConfiguration", b => + { + b.Navigation("RawAttackMachineZones"); + + b.Navigation("RawDefenseMachineZones"); + + b.Navigation("RawGateDefenseUpgrades"); + + b.Navigation("RawGateLifeUpgrades"); + + b.Navigation("RawNpcDefinitions"); + + b.Navigation("RawStateSchedule"); + + b.Navigation("RawStatueDefenseUpgrades"); + + b.Navigation("RawStatueLifeUpgrades"); + + b.Navigation("RawStatueRegenUpgrades"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CastleSiegeData", b => + { + b.Navigation("RawGuilds"); + + b.Navigation("RawNpcStates"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.Character", b => + { + b.Navigation("JoinedDropItemGroups"); + + b.Navigation("RawAttributes"); + + b.Navigation("RawLearnedSkills"); + + b.Navigation("RawLetters"); + + b.Navigation("RawQuestStates"); + + b.Navigation("RawWaitingItems"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CharacterClass", b => + { + b.Navigation("RawAttributeCombinations"); + + b.Navigation("RawBaseAttributeValues"); + + b.Navigation("RawStatAttributes"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CharacterQuestState", b => + { + b.Navigation("RawRequirementStates"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ChatServerDefinition", b => + { + b.Navigation("RawEndpoints"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.DropItemGroup", b => + { + b.Navigation("JoinedPossibleItems"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.DuelConfiguration", b => + { + b.Navigation("RawDuelAreas"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.GameConfiguration", b => + { + b.Navigation("RawAttributes"); + + b.Navigation("RawCharacterClasses"); + + b.Navigation("RawDropItemGroups"); + + b.Navigation("RawGlobalAttributeCombinations"); + + b.Navigation("RawGlobalBaseAttributeValues"); + + b.Navigation("RawItemLevelBonusTables"); + + b.Navigation("RawItemOptionCombinationBonuses"); + + b.Navigation("RawItemOptionTypes"); + + b.Navigation("RawItemOptions"); + + b.Navigation("RawItemPriceDefinitions"); + + b.Navigation("RawItemSetGroups"); + + b.Navigation("RawItemSlotTypes"); + + b.Navigation("RawItemStatProfiles"); + + b.Navigation("RawItems"); + + b.Navigation("RawJewelMixes"); + + b.Navigation("RawMagicEffects"); + + b.Navigation("RawMaps"); + + b.Navigation("RawMasterSkillRoots"); + + b.Navigation("RawMiniGameDefinitions"); + + b.Navigation("RawMonsters"); + + b.Navigation("RawPlugInConfigurations"); + + b.Navigation("RawSkills"); + + b.Navigation("RawWarpList"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.GameMapDefinition", b => + { + b.Navigation("JoinedDropItemGroups"); + + b.Navigation("RawCharacterPowerUpDefinitions"); + + b.Navigation("RawEnterGates"); + + b.Navigation("RawExitGates"); + + b.Navigation("RawMapRequirements"); + + b.Navigation("RawMonsterSpawns"); + + b.Navigation("RawTerrainVariants"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.GameServerConfiguration", b => + { + b.Navigation("JoinedMaps"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.GameServerDefinition", b => + { + b.Navigation("RawEndpoints"); + + b.Navigation("RawJoinRequirements"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.Guild", b => + { + b.Navigation("RawMembers"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.IncreasableItemOption", b => + { + b.Navigation("RawLevelDependentOptions"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.Item", b => + { + b.Navigation("JoinedItemSetGroups"); + + b.Navigation("RawItemOptions"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemAppearance", b => + { + b.Navigation("JoinedVisibleOptions"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemCraftingRequiredItem", b => + { + b.Navigation("JoinedPossibleItems"); + + b.Navigation("JoinedRequiredItemOptions"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDefinition", b => + { + b.Navigation("JoinedPossibleItemOptions"); + + b.Navigation("JoinedPossibleItemSetGroups"); + + b.Navigation("JoinedQualifiedCharacters"); + + b.Navigation("RawBasePowerUpAttributes"); + + b.Navigation("RawDropItems"); + + b.Navigation("RawRequirements"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemDropItemGroup", b => + { + b.Navigation("JoinedPossibleItems"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemLevelBonusTable", b => + { + b.Navigation("RawBonusPerLevel"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemOptionCombinationBonus", b => + { + b.Navigation("RawRequirements"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemOptionDefinition", b => + { + b.Navigation("RawPossibleOptions"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemPriceDefinition", b => + { + b.Navigation("RawPricePerLevel"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemSetGroup", b => + { + b.Navigation("RawItems"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemStatProfile", b => + { + b.Navigation("RawRequirementFormulas"); + + b.Navigation("RawStatTerms"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.ItemStorage", b => + { + b.Navigation("RawItems"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MagicEffectDefinition", b => + { + b.Navigation("RawPowerUpDefinitions"); + + b.Navigation("RawPowerUpDefinitionsPvp"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MasterSkillDefinition", b => + { + b.Navigation("JoinedRequiredMasterSkills"); + + b.Navigation("RawPassivePowerUps"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MiniGameChangeEvent", b => + { + b.Navigation("RawTerrainChanges"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MiniGameDefinition", b => + { + b.Navigation("RawChangeEvents"); + + b.Navigation("RawRewards"); + + b.Navigation("RawSpawnWaves"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.MonsterDefinition", b => + { + b.Navigation("JoinedDropItemGroups"); + + b.Navigation("RawAttributes"); + + b.Navigation("RawBuffs"); + + b.Navigation("RawItemCraftings"); + + b.Navigation("RawQuests"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.PowerUpDefinitionValue", b => + { + b.Navigation("RawRelatedValues"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.QuestDefinition", b => + { + b.Navigation("RawRequiredItems"); + + b.Navigation("RawRequiredMonsterKills"); + + b.Navigation("RawRewards"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.SimpleCraftingSettings", b => + { + b.Navigation("RawRequiredItems"); + + b.Navigation("RawResultItems"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.Skill", b => + { + b.Navigation("JoinedQualifiedCharacters"); + + b.Navigation("RawAttributeRelationships"); + + b.Navigation("RawConsumeRequirements"); + + b.Navigation("RawRequirements"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.SkillComboDefinition", b => + { + b.Navigation("RawSteps"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Persistence/EntityFramework/Migrations/20261114172314_AddWaitingItems.cs b/src/Persistence/EntityFramework/Migrations/20261114172314_AddWaitingItems.cs new file mode 100644 index 0000000000..79ecb9e764 --- /dev/null +++ b/src/Persistence/EntityFramework/Migrations/20261114172314_AddWaitingItems.cs @@ -0,0 +1,66 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace MUnique.OpenMU.Persistence.EntityFramework.Migrations +{ + /// + public partial class AddWaitingItems : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "WaitingItem", + schema: "data", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + ItemId = table.Column(type: "uuid", nullable: true), + CharacterId = table.Column(type: "uuid", nullable: true), + Reason = table.Column(type: "integer", nullable: false), + AddedAt = table.Column(type: "timestamp with time zone", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_WaitingItem", x => x.Id); + table.ForeignKey( + name: "FK_WaitingItem_Character_CharacterId", + column: x => x.CharacterId, + principalSchema: "data", + principalTable: "Character", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_WaitingItem_Item_ItemId", + column: x => x.ItemId, + principalSchema: "data", + principalTable: "Item", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_WaitingItem_CharacterId", + schema: "data", + table: "WaitingItem", + column: "CharacterId"); + + migrationBuilder.CreateIndex( + name: "IX_WaitingItem_ItemId", + schema: "data", + table: "WaitingItem", + column: "ItemId", + unique: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "WaitingItem", + schema: "data"); + } + } +} diff --git a/src/Persistence/EntityFramework/Migrations/EntityDataContextModelSnapshot.cs b/src/Persistence/EntityFramework/Migrations/EntityDataContextModelSnapshot.cs index bebb3c3b34..5c3e8927a1 100644 --- a/src/Persistence/EntityFramework/Migrations/EntityDataContextModelSnapshot.cs +++ b/src/Persistence/EntityFramework/Migrations/EntityDataContextModelSnapshot.cs @@ -4604,6 +4604,34 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("SystemConfiguration", "config"); }); + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.WaitingItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AddedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CharacterId") + .HasColumnType("uuid"); + + b.Property("ItemId") + .HasColumnType("uuid"); + + b.Property("Reason") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("CharacterId"); + + b.HasIndex("ItemId") + .IsUnique(); + + b.ToTable("WaitingItem", "data"); + }); + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.WarpInfo", b => { b.Property("Id") @@ -6533,6 +6561,21 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("RawAttribute"); }); + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.WaitingItem", b => + { + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Character", null) + .WithMany("RawWaitingItems") + .HasForeignKey("CharacterId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.Item", "RawItem") + .WithOne() + .HasForeignKey("MUnique.OpenMU.Persistence.EntityFramework.Model.WaitingItem", "ItemId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("RawItem"); + }); + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.WarpInfo", b => { b.HasOne("MUnique.OpenMU.Persistence.EntityFramework.Model.GameConfiguration", null) @@ -6612,6 +6655,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("RawLetters"); b.Navigation("RawQuestStates"); + + b.Navigation("RawWaitingItems"); }); modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CharacterClass", b => diff --git a/src/Persistence/EntityFramework/Model/BasicModelConverter.Generated.cs b/src/Persistence/EntityFramework/Model/BasicModelConverter.Generated.cs index a65eb01d99..94e50a896f 100644 --- a/src/Persistence/EntityFramework/Model/BasicModelConverter.Generated.cs +++ b/src/Persistence/EntityFramework/Model/BasicModelConverter.Generated.cs @@ -155,6 +155,7 @@ public static TBasic Convert(object source) SkillEntry value => this.Convert(value), StatAttributeDefinition value => this.Convert(value), SystemConfiguration value => this.Convert(value), + WaitingItem value => this.Convert(value), WarpInfo value => this.Convert(value), _ => source, }; @@ -867,6 +868,14 @@ private BasicModel.Character Convert(Character source) } } + if (source.WaitingItems is { } waitingItemsItems) + { + foreach (var item in waitingItemsItems) + { + target.WaitingItems.Add((MUnique.OpenMU.DataModel.Entities.WaitingItem)this.ConvertObject(item)!); + } + } + return target; } @@ -3278,6 +3287,22 @@ private BasicModel.SystemConfiguration Convert(SystemConfiguration source) return target; } + private BasicModel.WaitingItem Convert(WaitingItem source) + { + if (this._converted.TryGetValue(source, out var converted)) + { + return (BasicModel.WaitingItem)converted; + } + + var target = new BasicModel.WaitingItem(); + this._converted.Add(source, target); + target.Id = source.Id; + target.Item = (MUnique.OpenMU.DataModel.Entities.Item)this.ConvertObject(source.Item)!; + target.Reason = source.Reason; + target.AddedAt = source.AddedAt; + return target; + } + private BasicModel.WarpInfo Convert(WarpInfo source) { if (this._converted.TryGetValue(source, out var converted)) diff --git a/src/Persistence/EntityFramework/Model/Character.Generated.cs b/src/Persistence/EntityFramework/Model/Character.Generated.cs index e8dc4b1f7e..68709b93e8 100644 --- a/src/Persistence/EntityFramework/Model/Character.Generated.cs +++ b/src/Persistence/EntityFramework/Model/Character.Generated.cs @@ -65,6 +65,15 @@ public Character() [NotMapped] public override ICollection QuestStates => base.QuestStates ??= new CollectionAdapter(this.RawQuestStates); + /// + /// Gets the raw collection of . + /// + public ICollection RawWaitingItems { get; } = new EntityFramework.List(); + + /// + [NotMapped] + public override ICollection WaitingItems => base.WaitingItems ??= new CollectionAdapter(this.RawWaitingItems); + /// /// Gets or sets the identifier of . /// diff --git a/src/Persistence/EntityFramework/Model/ExtendedTypeContext.Generated.cs b/src/Persistence/EntityFramework/Model/ExtendedTypeContext.Generated.cs index f252cfa18c..4b83b97040 100644 --- a/src/Persistence/EntityFramework/Model/ExtendedTypeContext.Generated.cs +++ b/src/Persistence/EntityFramework/Model/ExtendedTypeContext.Generated.cs @@ -51,6 +51,7 @@ protected override void OnModelCreating(Microsoft.EntityFrameworkCore.ModelBuild modelBuilder.Ignore(); modelBuilder.Ignore(); modelBuilder.Ignore(); + modelBuilder.Ignore(); modelBuilder.Ignore(); modelBuilder.Ignore(); modelBuilder.Ignore(); @@ -153,10 +154,12 @@ protected override void OnModelCreating(Microsoft.EntityFrameworkCore.ModelBuild modelBuilder.Entity().HasMany(entity => entity.RawLearnedSkills).WithOne().OnDelete(DeleteBehavior.Cascade); modelBuilder.Entity().HasOne(entity => entity.RawInventory).WithOne().OnDelete(DeleteBehavior.Cascade); modelBuilder.Entity().HasMany(entity => entity.RawQuestStates).WithOne().OnDelete(DeleteBehavior.Cascade); + modelBuilder.Entity().HasMany(entity => entity.RawWaitingItems).WithOne().OnDelete(DeleteBehavior.Cascade); modelBuilder.Entity().HasMany(entity => entity.RawRequirementStates).WithOne().OnDelete(DeleteBehavior.Cascade); modelBuilder.Entity().HasMany(entity => entity.RawItemOptions).WithOne().OnDelete(DeleteBehavior.Cascade); modelBuilder.Entity().HasMany(entity => entity.RawItems).WithOne().OnDelete(DeleteBehavior.Cascade); modelBuilder.Entity().HasOne(entity => entity.RawSenderAppearance).WithOne().OnDelete(DeleteBehavior.Cascade); + modelBuilder.Entity().HasOne(entity => entity.RawItem).WithOne().OnDelete(DeleteBehavior.Cascade); modelBuilder.Entity().HasOne(entity => entity.RawGround).WithOne().OnDelete(DeleteBehavior.Cascade); modelBuilder.Entity().HasOne(entity => entity.RawLeftGoal).WithOne().OnDelete(DeleteBehavior.Cascade); modelBuilder.Entity().HasOne(entity => entity.RawRightGoal).WithOne().OnDelete(DeleteBehavior.Cascade); diff --git a/src/Persistence/EntityFramework/Model/WaitingItem.Generated.cs b/src/Persistence/EntityFramework/Model/WaitingItem.Generated.cs new file mode 100644 index 0000000000..403bc5f07e --- /dev/null +++ b/src/Persistence/EntityFramework/Model/WaitingItem.Generated.cs @@ -0,0 +1,77 @@ +// +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +//------------------------------------------------------------------------------ +// +// This source code was auto-generated by a roslyn code generator. +// +//------------------------------------------------------------------------------ + +// ReSharper disable All + +namespace MUnique.OpenMU.Persistence.EntityFramework.Model; + +using System.ComponentModel.DataAnnotations.Schema; +using MUnique.OpenMU.Persistence; + +/// +/// The Entity Framework Core implementation of . +/// +[Table(nameof(WaitingItem), Schema = SchemaNames.AccountData)] +internal partial class WaitingItem : MUnique.OpenMU.DataModel.Entities.WaitingItem, IIdentifiable +{ + + + /// + /// Gets or sets the identifier of this instance. + /// + public Guid Id { get; set; } + + /// + /// Gets or sets the identifier of . + /// + public Guid? ItemId { get; set; } + + /// + /// Gets the raw object of . + /// + [ForeignKey(nameof(ItemId))] + public Item RawItem + { + get => base.Item as Item; + set => base.Item = value; + } + + /// + [NotMapped] + public override MUnique.OpenMU.DataModel.Entities.Item Item + { + get => base.Item;set + { + base.Item = value; + this.ItemId = this.RawItem?.Id; + } + } + + + /// + public override bool Equals(object obj) + { + var baseObject = obj as IIdentifiable; + if (baseObject != null) + { + return baseObject.Id == this.Id; + } + + return base.Equals(obj); + } + + /// + public override int GetHashCode() + { + return this.Id.GetHashCode(); + } + + +} From 324f06c2330eb5be28983b6e9038c55422d3c509 Mon Sep 17 00:00:00 2001 From: Mosch0512 Date: Fri, 13 Nov 2026 15:52:28 +0100 Subject: [PATCH 2/4] feat(items): an item which finds no place when its storage is loaded waits for space in the inventory Before, the storage threw and the character couldn't enter the game, e.g. when the size of an item definition grew. - The storage takes an optional function for such an item; when it takes the item, the item is removed from the item storage afterwards. The inventory (an item first tries the inventory extensions), the personal store and the vault put such items into the waiting items of the character. - The plug-in "Waiting items delivery" puts them into the inventory as soon as they fit: when the character enters the game or opens the vault, and then periodically while no dialog is open, holding the persistence lock of the player and the lock of the personal store. The oldest item comes first. Errors are logged, so they don't stop entering the game. Its interval and the number of named items are configurable. - The player is told which items wait, in short messages; the chat command /waiting lists them. - A character with waiting items can't be deleted, because an item from the vault belongs to the account. - A waiting item without an item definition is kept, but not delivered or listed. --- src/GameLogic/InventoryStorage.cs | 47 +- .../Character/DeleteCharacterAction.cs | 13 + src/GameLogic/PlayerActions/TalkNpcAction.cs | 11 +- src/GameLogic/PlayerStorages.cs | 3 +- .../WaitingItemsChatCommandPlugIn.cs | 62 ++ .../Properties/PlayerMessage.Designer.cs | 63 +++ .../Properties/PlayerMessage.de.resx | 21 + .../Properties/PlayerMessage.pt.resx | 21 + src/GameLogic/Properties/PlayerMessage.resx | 21 + .../Properties/PlayerMessage.zh-CN.resx | 21 + .../Properties/PlugInResources.Designer.cs | 90 +++ .../Properties/PlugInResources.de.resx | 30 + src/GameLogic/Properties/PlugInResources.resx | 30 + .../Properties/PlugInResources.zh-CN.resx | 30 + src/GameLogic/ShopStorage.cs | 5 +- src/GameLogic/Storage.cs | 50 +- .../WaitingItems/WaitingItemExtensions.cs | 175 ++++++ .../WaitingItemsDeliveryPlugIn.cs | 139 +++++ ...WaitingItemsDeliveryPlugInConfiguration.cs | 24 + .../MUnique.OpenMU.Tests/WaitingItemsTest.cs | 530 ++++++++++++++++++ 20 files changed, 1378 insertions(+), 8 deletions(-) create mode 100644 src/GameLogic/PlugIns/ChatCommands/WaitingItemsChatCommandPlugIn.cs create mode 100644 src/GameLogic/WaitingItems/WaitingItemExtensions.cs create mode 100644 src/GameLogic/WaitingItems/WaitingItemsDeliveryPlugIn.cs create mode 100644 src/GameLogic/WaitingItems/WaitingItemsDeliveryPlugInConfiguration.cs create mode 100644 tests/MUnique.OpenMU.Tests/WaitingItemsTest.cs diff --git a/src/GameLogic/InventoryStorage.cs b/src/GameLogic/InventoryStorage.cs index 4a38a197c8..6fdb514f05 100644 --- a/src/GameLogic/InventoryStorage.cs +++ b/src/GameLogic/InventoryStorage.cs @@ -7,6 +7,7 @@ namespace MUnique.OpenMU.GameLogic; using MUnique.OpenMU.DataModel; using MUnique.OpenMU.GameLogic.Attributes; using MUnique.OpenMU.GameLogic.Views.World; +using MUnique.OpenMU.GameLogic.WaitingItems; using MUnique.OpenMU.PlugIns; using static MUnique.OpenMU.DataModel.InventoryConstants; @@ -25,11 +26,26 @@ public class InventoryStorage : Storage, IInventoryStorage /// The player. /// The game context. public InventoryStorage(Player player, IGameContext context) + : this(player, context, []) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The player. + /// The game context. + /// + /// Collects the items which find no place in the main inventory or in their extension. They get another try + /// when all extensions exist, and only the ones which still find no place wait for space. + /// + private InventoryStorage(Player player, IGameContext context, List itemsWithoutPlace) : base( GetInventorySize(0), EquippableSlotsCount, 0, - new ItemStorageAdapter(player.SelectedCharacter?.Inventory ?? throw Error.NotInitializedProperty(player, "SelectedCharacter.Inventory"), FirstEquippableItemSlotIndex, player.InventorySize)) + new ItemStorageAdapter(player.SelectedCharacter?.Inventory ?? throw Error.NotInitializedProperty(player, "SelectedCharacter.Inventory"), FirstEquippableItemSlotIndex, player.InventorySize), + KeepItemWithoutPlace(itemsWithoutPlace)) { this._player = player; this.EquippedItemsChanged += async eventArgs => await this.UpdateItemsOnChangeAsync(eventArgs.Item, eventArgs.IsEquipped).ConfigureAwait(false); @@ -43,13 +59,25 @@ public InventoryStorage(Player player, IGameContext context) for (int i = 0; i < player.SelectedCharacter.InventoryExtensions; i++) { var offset = FirstExtensionItemSlotIndex + (i * sizePerExtension); - var extension = new Storage(sizePerExtension, 0, offset, this.ItemStorage); + var extension = new Storage(sizePerExtension, 0, offset, this.ItemStorage, KeepItemWithoutPlace(itemsWithoutPlace)); extensions.Add(extension); } this.Extensions = extensions; } + if (itemsWithoutPlace.Count > 0) + { + // The items are still in the item storage. Like in the Storage, each one is removed only after + // it was put aside, so that none is lost when putting it aside throws. + var putAside = player.PutAsideWhenLoaded(player.SelectedCharacter); + foreach (var item in itemsWithoutPlace.Where(item => !this.TryAddItemWithoutPlace(item)).ToList()) + { + putAside(item); + this.ItemStorage.Items.Remove(item); + } + } + this.InitializePowerUps(); } @@ -151,6 +179,21 @@ public async ValueTask RaiseEquippedItemsChangedAsync(Item item, bool isEquipped } } + /// + /// Creates the function which keeps an item, which finds no place when a storage is loaded, in the given list + /// instead of putting it aside, so that it gets another try when all extensions exist. + /// + /// The list of the items without place. + /// The function; it doesn't take the item, so it stays in the item storage. + private static Func KeepItemWithoutPlace(List itemsWithoutPlace) + { + return item => + { + itemsWithoutPlace.Add(item); + return false; + }; + } + private bool IsWearingSlot(int slot) { return slot >= FirstEquippableItemSlotIndex && slot <= LastEquippableItemSlotIndex; diff --git a/src/GameLogic/PlayerActions/Character/DeleteCharacterAction.cs b/src/GameLogic/PlayerActions/Character/DeleteCharacterAction.cs index a420b4ce9c..a4aa98bbb7 100644 --- a/src/GameLogic/PlayerActions/Character/DeleteCharacterAction.cs +++ b/src/GameLogic/PlayerActions/Character/DeleteCharacterAction.cs @@ -6,6 +6,7 @@ namespace MUnique.OpenMU.GameLogic.PlayerActions.Character; using MUnique.OpenMU.GameLogic.PlugIns; using MUnique.OpenMU.GameLogic.Views.Character; +using MUnique.OpenMU.GameLogic.WaitingItems; using MUnique.OpenMU.Interfaces; /// @@ -62,6 +63,18 @@ private async ValueTask DeleteCharacterRequestAsync(Playe return CharacterDeleteResult.Unsuccessful; } + // The waiting items are deleted with the character, but an item which waits because it found no place + // in the vault belongs to the account. An item without a definition is kept until the definition is back, + // so it prevents the deletion, too; only an admin can solve that. + if (character.WaitingItems is { Count: > 0 }) + { + var message = character.HasDeliverableWaitingItems() + ? nameof(PlayerMessage.CantDeleteCharacterWithWaitingItems) + : nameof(PlayerMessage.CantDeleteCharacterWithUndeliverableWaitingItems); + await player.ShowLocalizedBlueMessageAsync(message).ConfigureAwait(false); + return CharacterDeleteResult.Unsuccessful; + } + player.Account.Characters.Remove(character); player.GameContext.PlugInManager.GetPlugInPoint()?.CharacterDeleted(player, character); await player.PersistenceContext.DeleteAsync(character).ConfigureAwait(false); diff --git a/src/GameLogic/PlayerActions/TalkNpcAction.cs b/src/GameLogic/PlayerActions/TalkNpcAction.cs index 32fa414821..e83f2b6bdb 100644 --- a/src/GameLogic/PlayerActions/TalkNpcAction.cs +++ b/src/GameLogic/PlayerActions/TalkNpcAction.cs @@ -13,6 +13,7 @@ namespace MUnique.OpenMU.GameLogic.PlayerActions; using MUnique.OpenMU.GameLogic.Views.NPC; using MUnique.OpenMU.GameLogic.Views.Quest; using MUnique.OpenMU.GameLogic.Views.Vault; +using MUnique.OpenMU.GameLogic.WaitingItems; using MUnique.OpenMU.Interfaces; /// @@ -107,8 +108,16 @@ private async ValueTask ShowDialogOfOpenedNpcAsync(Player player) case NpcWindow.VaultStorage: player.Account!.Vault ??= player.PersistenceContext.CreateNew(); var warehouseSize = player.Account.IsVaultExtended ? InventoryConstants.WarehouseSize * 2 : InventoryConstants.WarehouseSize; - player.Vault = new Storage(warehouseSize, player.Account.Vault); + var character = player.SelectedCharacter!; + var waitingItemCount = character.WaitingItems?.Count ?? 0; + player.Vault = new Storage(warehouseSize, 0, 0, player.Account.Vault, player.PutAsideWhenLoaded(character)); await player.InvokeViewPlugInAsync(p => p.ShowVaultAsync()).ConfigureAwait(false); + if ((character.WaitingItems?.Count ?? 0) > waitingItemCount + && WaitingItemsDeliveryPlugIn.GetActive(player.GameContext) is { } delivery) + { + await delivery.DeliverAndShowAsync(player).ConfigureAwait(false); + } + break; case NpcWindow.GuildMaster: if (await this.IsPlayedAllowedToCreateGuildAsync(player).ConfigureAwait(false)) diff --git a/src/GameLogic/PlayerStorages.cs b/src/GameLogic/PlayerStorages.cs index 1b416aa52f..0732aea7ef 100644 --- a/src/GameLogic/PlayerStorages.cs +++ b/src/GameLogic/PlayerStorages.cs @@ -4,6 +4,7 @@ namespace MUnique.OpenMU.GameLogic; +using MUnique.OpenMU.GameLogic.WaitingItems; using MUnique.OpenMU.Persistence; /// @@ -54,7 +55,7 @@ public PlayerStorages(Player player) public void CreateForCharacter(Character character) { this.Inventory = new InventoryStorage(this._player, this._player.GameContext); - this.ShopStorage = new ShopStorage(character); + this.ShopStorage = new ShopStorage(character, this._player.PutAsideWhenLoaded(character)); this.TemporaryStorage = new Storage(InventoryConstants.TemporaryStorageSize, new TemporaryItemStorage()); this.Vault = null; // vault storage is getting set when vault npc is opened. } diff --git a/src/GameLogic/PlugIns/ChatCommands/WaitingItemsChatCommandPlugIn.cs b/src/GameLogic/PlugIns/ChatCommands/WaitingItemsChatCommandPlugIn.cs new file mode 100644 index 0000000000..8e274e198b --- /dev/null +++ b/src/GameLogic/PlugIns/ChatCommands/WaitingItemsChatCommandPlugIn.cs @@ -0,0 +1,62 @@ +// +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace MUnique.OpenMU.GameLogic.PlugIns.ChatCommands; + +using System.Runtime.InteropServices; +using MUnique.OpenMU.GameLogic.WaitingItems; +using MUnique.OpenMU.PlugIns; + +/// +/// A chat command which lists the items which wait for space in the inventory. +/// +[Guid("75B3625C-6E8E-4FFF-A0A4-3FA9C3895C70")] +[PlugIn] +[Display(Name = nameof(PlugInResources.WaitingItemsChatCommandPlugIn_Name), Description = nameof(PlugInResources.WaitingItemsChatCommandPlugIn_Description), ResourceType = typeof(PlugInResources))] +[ChatCommandHelp(Command, typeof(WaitingItemsChatCommandPlugIn.Arguments), CharacterStatus.Normal)] +public class WaitingItemsChatCommandPlugIn : ChatCommandPlugInBase, ISupportCustomConfiguration, ISupportDefaultCustomConfiguration +{ + /// + /// The command. + /// + internal const string Command = "/waiting"; + + /// + public override string Key => Command; + + /// + public override CharacterStatus MinCharacterStatusRequirement => CharacterStatus.Normal; + + /// + public WaitingItemsCommandConfiguration? Configuration { get; set; } + + /// + public object CreateDefaultConfig() => new WaitingItemsCommandConfiguration(); + + /// + protected override async ValueTask DoHandleCommandAsync(Player player, Arguments arguments) + { + var itemsNamed = this.Configuration?.ItemsNamed ?? new WaitingItemsCommandConfiguration().ItemsNamed; + await player.ShowWaitingItemsAsync(itemsNamed, true).ConfigureAwait(false); + } + + /// + /// The configuration of the command. + /// + public class WaitingItemsCommandConfiguration + { + /// + /// Gets or sets the number of waiting items which are named. + /// + [Display(ResourceType = typeof(PlugInResources), Name = nameof(PlugInResources.WaitingItemsCommandConfiguration_ItemsNamed_Name), Description = nameof(PlugInResources.WaitingItemsCommandConfiguration_ItemsNamed_Description))] + public int ItemsNamed { get; set; } = 10; + } + + /// + /// The arguments of the command; it has none. + /// + public class Arguments : ArgumentsBase + { + } +} diff --git a/src/GameLogic/Properties/PlayerMessage.Designer.cs b/src/GameLogic/Properties/PlayerMessage.Designer.cs index 451d767b67..44f5675574 100644 --- a/src/GameLogic/Properties/PlayerMessage.Designer.cs +++ b/src/GameLogic/Properties/PlayerMessage.Designer.cs @@ -751,6 +751,15 @@ public static string ImperialGuardianLevelTooLow { } } + /// + /// Looks up a localized string similar to {0} item(s) wait for space in your inventory. They are put into it as soon as they fit:. + /// + public static string ItemsWaitForSpace { + get { + return ResourceManager.GetString("ItemsWaitForSpace", resourceCulture); + } + } + /// /// Looks up a localized string similar to Your {0} has been destroyed! You are falling out of the Kanturu Refinery Tower.. /// @@ -949,6 +958,24 @@ public static string KanturuVictory { } } + /// + /// Looks up a localized string similar to Can't delete a character while items wait for it whose item type doesn't exist anymore. Please ask an admin.. + /// + public static string CantDeleteCharacterWithUndeliverableWaitingItems { + get { + return ResourceManager.GetString("CantDeleteCharacterWithUndeliverableWaitingItems", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Can't delete a character while items wait for space in its inventory. Enter the game with it and make room first.. + /// + public static string CantDeleteCharacterWithWaitingItems { + get { + return ResourceManager.GetString("CantDeleteCharacterWithWaitingItems", resourceCulture); + } + } + /// /// Looks up a localized string similar to Can't delete a guild member. Remove the character from guild first.. /// @@ -1642,6 +1669,33 @@ public static string KillAllMonstersFormat { } } + /// + /// Looks up a localized string similar to ... and {0} more. /waiting lists more of them.. + /// + public static string MoreWaitingItems { + get { + return ResourceManager.GetString("MoreWaitingItems", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to ... and {0} more.. + /// + public static string MoreWaitingItemsNotNamed { + get { + return ResourceManager.GetString("MoreWaitingItemsNotNamed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No items wait for space in your inventory.. + /// + public static string NoWaitingItems { + get { + return ResourceManager.GetString("NoWaitingItems", resourceCulture); + } + } + /// /// Looks up a localized string similar to Close your store to change its prices.. /// @@ -2947,6 +3001,15 @@ public static string UsingThisItemNotImplemented { } } + /// + /// Looks up a localized string similar to {0} was put into your inventory.. + /// + public static string WaitingItemDelivered { + get { + return ResourceManager.GetString("WaitingItemDelivered", resourceCulture); + } + } + /// /// Looks up a localized string similar to This warp is only available to game masters.. /// diff --git a/src/GameLogic/Properties/PlayerMessage.de.resx b/src/GameLogic/Properties/PlayerMessage.de.resx index c6b035c784..58c5c473e8 100644 --- a/src/GameLogic/Properties/PlayerMessage.de.resx +++ b/src/GameLogic/Properties/PlayerMessage.de.resx @@ -1021,4 +1021,25 @@ Inventarerweiterungen: {18} Dieser Warp ist nur für Game Master verfügbar. + + {0} Gegenstand/Gegenstände warten auf Platz in Ihrem Inventar. Sie werden hineingelegt, sobald sie passen: + + + Keine Gegenstände warten auf Platz in Ihrem Inventar. + + + {0} wurde in Ihr Inventar gelegt. + + + ... und {0} weitere. /waiting listet weitere auf. + + + Ein Charakter, für den Gegenstände auf Platz im Inventar warten, kann nicht gelöscht werden. Betreten Sie zuerst mit ihm das Spiel und schaffen Sie Platz. + + + ... und {0} weitere. + + + Ein Charakter, für den Gegenstände warten, deren Gegenstandsart es nicht mehr gibt, kann nicht gelöscht werden. Bitte wenden Sie sich an einen Admin. + diff --git a/src/GameLogic/Properties/PlayerMessage.pt.resx b/src/GameLogic/Properties/PlayerMessage.pt.resx index 3f21ddfa66..cac6f6def0 100644 --- a/src/GameLogic/Properties/PlayerMessage.pt.resx +++ b/src/GameLogic/Properties/PlayerMessage.pt.resx @@ -1174,4 +1174,25 @@ Extensões de inventário: {18} Este warp está disponível apenas para game masters. + + {0} item(ns) aguardam espaço no seu inventário. Eles serão colocados nele assim que couberem: + + + Nenhum item aguarda espaço no seu inventário. + + + {0} foi colocado no seu inventário. + + + ... e mais {0}. /waiting lista mais deles. + + + Não é possível excluir um personagem enquanto itens aguardam espaço no inventário dele. Entre no jogo com ele e libere espaço primeiro. + + + ... e mais {0}. + + + Não é possível excluir um personagem enquanto aguardam itens cujo tipo de item não existe mais. Peça ajuda a um administrador. + diff --git a/src/GameLogic/Properties/PlayerMessage.resx b/src/GameLogic/Properties/PlayerMessage.resx index 95b8eb4d9c..92a9b72f69 100644 --- a/src/GameLogic/Properties/PlayerMessage.resx +++ b/src/GameLogic/Properties/PlayerMessage.resx @@ -1174,4 +1174,25 @@ Inventory Extensions: {18} This warp is only available to game masters. + + {0} item(s) wait for space in your inventory. They are put into it as soon as they fit: + + + No items wait for space in your inventory. + + + {0} was put into your inventory. + + + ... and {0} more. /waiting lists more of them. + + + Can't delete a character while items wait for space in its inventory. Enter the game with it and make room first. + + + ... and {0} more. + + + Can't delete a character while items wait for it whose item type doesn't exist anymore. Please ask an admin. + diff --git a/src/GameLogic/Properties/PlayerMessage.zh-CN.resx b/src/GameLogic/Properties/PlayerMessage.zh-CN.resx index ae40bd8140..1e00e637d8 100644 --- a/src/GameLogic/Properties/PlayerMessage.zh-CN.resx +++ b/src/GameLogic/Properties/PlayerMessage.zh-CN.resx @@ -15,4 +15,25 @@ 红名玩家无法进入! 参加此活动需要入场券。 无法在卡利玛创建卡利玛之门。 + + 有 {0} 件物品正在等待背包空间,有足够空间时会自动放入背包: + + + 没有等待背包空间的物品。 + + + {0} 已放入你的背包。 + + + ……还有 {0} 件。输入 /waiting 查看更多。 + + + 该角色有物品正在等待背包空间,无法删除。请先用该角色进入游戏并腾出空间。 + + + ……还有 {0} 件。 + + + 该角色有等待中的物品,但其物品类型已不存在,无法删除。请联系管理员。 + diff --git a/src/GameLogic/Properties/PlugInResources.Designer.cs b/src/GameLogic/Properties/PlugInResources.Designer.cs index afe520fe7a..da87619d43 100644 --- a/src/GameLogic/Properties/PlugInResources.Designer.cs +++ b/src/GameLogic/Properties/PlugInResources.Designer.cs @@ -7702,6 +7702,96 @@ public static string WorldChatChatCommandPlugIn_Description { } } + /// + /// Looks up a localized string similar to Handles the chat command '/waiting'. Lists the items which wait for space in the inventory.. + /// + public static string WaitingItemsChatCommandPlugIn_Description { + get { + return ResourceManager.GetString("WaitingItemsChatCommandPlugIn_Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Waiting items chat command. + /// + public static string WaitingItemsChatCommandPlugIn_Name { + get { + return ResourceManager.GetString("WaitingItemsChatCommandPlugIn_Name", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Check interval. + /// + public static string WaitingItemsDeliveryPlugInConfiguration_CheckInterval_Name { + get { + return ResourceManager.GetString("WaitingItemsDeliveryPlugInConfiguration_CheckInterval_Name", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to How often the waiting items are put into the inventories, as far as they fit there.. + /// + public static string WaitingItemsDeliveryPlugInConfiguration_CheckInterval_Description { + get { + return ResourceManager.GetString("WaitingItemsDeliveryPlugInConfiguration_CheckInterval_Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Named items. + /// + public static string WaitingItemsDeliveryPlugInConfiguration_ItemsNamed_Name { + get { + return ResourceManager.GetString("WaitingItemsDeliveryPlugInConfiguration_ItemsNamed_Name", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The number of waiting items which are named, one per message, when the player is told about them when entering the game or opening the vault.. + /// + public static string WaitingItemsDeliveryPlugInConfiguration_ItemsNamed_Description { + get { + return ResourceManager.GetString("WaitingItemsDeliveryPlugInConfiguration_ItemsNamed_Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Named items. + /// + public static string WaitingItemsCommandConfiguration_ItemsNamed_Name { + get { + return ResourceManager.GetString("WaitingItemsCommandConfiguration_ItemsNamed_Name", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The number of waiting items which are named, one per message.. + /// + public static string WaitingItemsCommandConfiguration_ItemsNamed_Description { + get { + return ResourceManager.GetString("WaitingItemsCommandConfiguration_ItemsNamed_Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Puts the items which wait for space in the inventory of a character into it as soon as they fit there: when the character enters the game or opens the vault, and then periodically. When it's deactivated, the items keep waiting.. + /// + public static string WaitingItemsDeliveryPlugIn_Description { + get { + return ResourceManager.GetString("WaitingItemsDeliveryPlugIn_Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Waiting items delivery. + /// + public static string WaitingItemsDeliveryPlugIn_Name { + get { + return ResourceManager.GetString("WaitingItemsDeliveryPlugIn_Name", resourceCulture); + } + } + /// /// Looks up a localized string similar to World chat command. /// diff --git a/src/GameLogic/Properties/PlugInResources.de.resx b/src/GameLogic/Properties/PlugInResources.de.resx index c476911ee9..075a6c4501 100644 --- a/src/GameLogic/Properties/PlugInResources.de.resx +++ b/src/GameLogic/Properties/PlugInResources.de.resx @@ -2016,4 +2016,34 @@ Teilt dem Client beim Betreten des Spiels mit, welche Warps der Spieler im Warp-Fenster auswählen kann. + + Zustellung wartender Gegenstände + + + Legt die Gegenstände, die auf Platz im Inventar eines Charakters warten, hinein, sobald sie passen: wenn der Charakter das Spiel betritt oder das Lager öffnet und danach regelmäßig. Ist es deaktiviert, warten die Gegenstände weiter. + + + Chatbefehl wartende Gegenstände + + + Verarbeitet den Chatbefehl '/waiting'. Listet die Gegenstände auf, die auf Platz im Inventar warten. + + + Prüfintervall + + + Wie oft die wartenden Gegenstände in die Inventare gelegt werden, soweit sie hineinpassen. + + + Genannte Gegenstände + + + Die Anzahl der wartenden Gegenstände, die genannt werden, einer pro Nachricht, wenn der Spieler beim Betreten des Spiels oder beim Öffnen des Lagers darüber informiert wird. + + + Genannte Gegenstände + + + Die Anzahl der wartenden Gegenstände, die genannt werden, einer pro Nachricht. + diff --git a/src/GameLogic/Properties/PlugInResources.resx b/src/GameLogic/Properties/PlugInResources.resx index e212c3adc8..cd2c42cb70 100644 --- a/src/GameLogic/Properties/PlugInResources.resx +++ b/src/GameLogic/Properties/PlugInResources.resx @@ -2061,4 +2061,34 @@ Tells the client which warps the player can select in the warp window, when the player enters the game. + + Waiting items delivery + + + Puts the items which wait for space in the inventory of a character into it as soon as they fit there: when the character enters the game or opens the vault, and then periodically. When it's deactivated, the items keep waiting. + + + Waiting items chat command + + + Handles the chat command '/waiting'. Lists the items which wait for space in the inventory. + + + Check interval + + + How often the waiting items are put into the inventories, as far as they fit there. + + + Named items + + + The number of waiting items which are named, one per message, when the player is told about them when entering the game or opening the vault. + + + Named items + + + The number of waiting items which are named, one per message. + diff --git a/src/GameLogic/Properties/PlugInResources.zh-CN.resx b/src/GameLogic/Properties/PlugInResources.zh-CN.resx index c744611a71..46a00199d7 100644 --- a/src/GameLogic/Properties/PlugInResources.zh-CN.resx +++ b/src/GameLogic/Properties/PlugInResources.zh-CN.resx @@ -1804,4 +1804,34 @@ 处理玩家与幻影寺院盟军物品仓库的交互。盟军将圣物送至此处可获得一分。 幻影寺院幻影物品仓库交互 处理玩家与幻影寺院幻影物品仓库的交互。幻影军将圣物送至此处可获得一分。 + + 等待物品发放 + + + 角色进入游戏或打开仓库时以及之后定期,将等待背包空间的物品放入背包(有足够空间时)。停用后物品会继续等待。 + + + 等待物品命令 + + + 处理 /waiting,列出等待背包空间的物品。 + + + 检查间隔 + + + 多久将等待的物品放入背包一次(有足够空间时)。 + + + 列出的物品数 + + + 进入游戏或打开仓库时告知玩家的等待物品数量,每条消息一件。 + + + 列出的物品数 + + + 列出的等待物品数量,每条消息一件。 + diff --git a/src/GameLogic/ShopStorage.cs b/src/GameLogic/ShopStorage.cs index 89e6da39ee..7a0a8a9b20 100644 --- a/src/GameLogic/ShopStorage.cs +++ b/src/GameLogic/ShopStorage.cs @@ -17,8 +17,9 @@ public class ShopStorage : Storage, IShopStorage /// Initializes a new instance of the class. /// /// The character. - public ShopStorage(Character character) - : base(InventoryConstants.StoreSize, 0, InventoryConstants.FirstStoreItemSlotIndex, new ItemStorageAdapter(character.Inventory ?? throw Error.NotInitializedProperty(character, nameof(Character.Inventory)), InventoryConstants.FirstStoreItemSlotIndex, InventoryConstants.StoreSize)) + /// Is called for each item which finds no free place, see . + public ShopStorage(Character character, Func? putAsideItemWithoutPlace = null) + : base(InventoryConstants.StoreSize, 0, InventoryConstants.FirstStoreItemSlotIndex, new ItemStorageAdapter(character.Inventory ?? throw Error.NotInitializedProperty(character, nameof(Character.Inventory)), InventoryConstants.FirstStoreItemSlotIndex, InventoryConstants.StoreSize), putAsideItemWithoutPlace) { this.StoreLock = new AsyncLock(); this._character = character; diff --git a/src/GameLogic/Storage.cs b/src/GameLogic/Storage.cs index 5b2ad29d32..c4871a8f63 100644 --- a/src/GameLogic/Storage.cs +++ b/src/GameLogic/Storage.cs @@ -35,7 +35,15 @@ public Storage(int numberOfSlots, ItemStorage itemStorage) /// The box offset, which is the first index where the 2-dimensional box starts. /// The slot offset, which means at which this instance starts. /// The item storage. - public Storage(int numberOfSlots, int boxOffset, int slotOffset, ItemStorage itemStorage) + /// + /// Is called for each item which finds no free place, e.g. because the size of its definition grew. + /// When it returns true, it took the item, and the item is removed from the + /// afterwards, so it isn't lost when the function throws. When it returns false, the item stays in the + /// , but not in this storage; then the caller must place or remove it, + /// otherwise another item may be placed over it and both are saved at the same place. + /// When it's null, such an item throws an . + /// + public Storage(int numberOfSlots, int boxOffset, int slotOffset, ItemStorage itemStorage, Func? putAsideItemWithoutPlace = null) { this.ItemArray = new Item?[numberOfSlots]; this._rows = (numberOfSlots - boxOffset) / InventoryConstants.RowSize; @@ -78,10 +86,23 @@ public Storage(int numberOfSlots, int boxOffset, int slotOffset, ItemStorage ite } } - if (unfittingItems.Count > 0) + if (unfittingItems.Count == 0) + { + return; + } + + if (putAsideItemWithoutPlace is null) { throw new ArgumentException($"Some items do not fit into the storage: {string.Join(';', unfittingItems)}"); } + + foreach (var item in unfittingItems) + { + if (putAsideItemWithoutPlace(item)) + { + this.ItemStorage.Items.Remove(item); + } + } } /// @@ -342,6 +363,31 @@ public bool ContainsSlot(byte slot) return true; } + /// + /// Puts an item, which found no free place when this storage or one of its extensions was loaded, + /// into the first free place of this storage or its extensions. + /// + /// The item, which still belongs to the item storage. + /// True, if the item found a place; Otherwise, false. + protected bool TryAddItemWithoutPlace(Item item) + { + if (this.CheckInvSpace(item) is not { } freeSlot) + { + return false; + } + + var storage = this.ContainsSlot(freeSlot) + ? this + : this.Extensions.OfType().FirstOrDefault(extension => extension.ContainsSlot(freeSlot)); + if (storage?.AddItemInternal((byte)(freeSlot - storage._slotOffset), item) is not true) + { + return false; + } + + item.ItemSlot = freeSlot; + return true; + } + /// /// Adds the item to the internal data structures. /// diff --git a/src/GameLogic/WaitingItems/WaitingItemExtensions.cs b/src/GameLogic/WaitingItems/WaitingItemExtensions.cs new file mode 100644 index 0000000000..47e73a8798 --- /dev/null +++ b/src/GameLogic/WaitingItems/WaitingItemExtensions.cs @@ -0,0 +1,175 @@ +// +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace MUnique.OpenMU.GameLogic.WaitingItems; + +using MUnique.OpenMU.GameLogic.Views.Inventory; +using MUnique.OpenMU.Persistence; + +/// +/// Extensions for the , the items which wait for space in the inventory. +/// +public static class WaitingItemExtensions +{ + /// + /// Adds the item to the waiting items of the character. + /// The item must not belong to an item storage anymore. + /// + /// The character. + /// The persistence context of the character. + /// The item. + /// The reason why the item waits. + public static void AddWaitingItem(this Character character, IContext context, Item item, WaitingItemReason reason) + { + var waitingItem = context.CreateNew(); + waitingItem.Item = item; + waitingItem.Reason = reason; + waitingItem.AddedAt = DateTime.UtcNow; + character.WaitingItems.Add(waitingItem); + } + + /// + /// Creates the function which puts an item of the character, which finds no place when its storage is loaded, + /// into the waiting items. It's passed to the constructor of a . + /// + /// The player. + /// The character which gets the waiting items. + /// The function; it always takes the item. + public static Func PutAsideWhenLoaded(this Player player, Character character) + { + return item => + { + player.Logger.LogWarning("Item {Item} of character {Character} finds no place in its storage, so it waits for space in the inventory.", item, character.Name); + character.AddWaitingItem(player.PersistenceContext, item, WaitingItemReason.NoSpaceWhenLoaded); + return true; + }; + } + + /// + /// Determines whether the selected character of the player has waiting items which can be put into the inventory + /// when they fit, which are the ones with an item definition. + /// + /// The player. + /// True, if the character has such waiting items; Otherwise, false. + public static bool HasDeliverableWaitingItems(this Player player) + { + return player.SelectedCharacter?.HasDeliverableWaitingItems() ?? false; + } + + /// + /// Determines whether the character has waiting items which can be put into the inventory when they fit, + /// which are the ones with an item definition. + /// + /// The character. + /// True, if the character has such waiting items; Otherwise, false. + public static bool HasDeliverableWaitingItems(this Character character) + { + return character.WaitingItems?.Any(w => w.Item?.Definition is not null) ?? false; + } + + /// + /// Puts the waiting items of the selected character into its inventory, as far as they fit there. + /// The oldest items come first; an item which doesn't fit doesn't stop a later, smaller one. + /// + /// The player. + /// The number of items which were put into the inventory. + public static async ValueTask DeliverWaitingItemsAsync(this Player player) + { + if (player.SelectedCharacter is not { WaitingItems: { Count: > 0 } } character + || player.Inventory is not { } inventory) + { + return 0; + } + + var delivered = 0; + foreach (var waitingItem in character.WaitingItems.OrderBy(w => w.AddedAt).ToList()) + { + if (waitingItem.Item is not { } item) + { + await player.RemoveWaitingItemAsync(character, waitingItem).ConfigureAwait(false); + continue; + } + + if (item.Definition is null) + { + // Like an inventory item without a definition, it's kept as it is, until the definition is back. + player.Logger.LogWarning("Waiting item {Item} of character {Character} has no item definition, so it can't be put into the inventory.", item, character.Name); + continue; + } + + if (!await inventory.AddItemAsync(item).ConfigureAwait(false)) + { + continue; + } + + // The item belongs to the inventory now, so deleting the waiting item must not delete it. + waitingItem.Item = null; + await player.RemoveWaitingItemAsync(character, waitingItem).ConfigureAwait(false); + + await player.InvokeViewPlugInAsync(p => p.ItemAppearAsync(item)).ConfigureAwait(false); + await player.ShowLocalizedBlueMessageAsync(nameof(PlayerMessage.WaitingItemDelivered), player.GetWaitingItemName(item)).ConfigureAwait(false); + delivered++; + } + + return delivered; + } + + /// + /// Tells the player which items wait for space in the inventory: one message with their number, + /// and one message per item, up to , so that a long list + /// doesn't end up in one long message. + /// + /// The player. + /// The maximum number of items which are named. + /// If set to true, the player is told when no item waits, too. + /// If set to true, the player is pointed to the chat command for the items which aren't named. + public static async ValueTask ShowWaitingItemsAsync(this Player player, int maximumNamedItems, bool evenWhenNone = false, bool pointToCommand = false) + { + var waitingItems = player.SelectedCharacter?.WaitingItems? + .Select(w => w.Item) + .OfType() + .Where(item => item.Definition is not null) + .ToList() ?? []; + if (waitingItems.Count == 0) + { + if (evenWhenNone) + { + await player.ShowLocalizedBlueMessageAsync(nameof(PlayerMessage.NoWaitingItems)).ConfigureAwait(false); + } + + return; + } + + await player.ShowLocalizedBlueMessageAsync(nameof(PlayerMessage.ItemsWaitForSpace), waitingItems.Count).ConfigureAwait(false); + foreach (var item in waitingItems.Take(maximumNamedItems)) + { + await player.ShowBlueMessageAsync($"- {player.GetWaitingItemName(item)}").ConfigureAwait(false); + } + + if (waitingItems.Count > maximumNamedItems) + { + var message = pointToCommand ? nameof(PlayerMessage.MoreWaitingItems) : nameof(PlayerMessage.MoreWaitingItemsNotNamed); + await player.ShowLocalizedBlueMessageAsync(message, waitingItems.Count - maximumNamedItems).ConfigureAwait(false); + } + } + + private static async ValueTask RemoveWaitingItemAsync(this Player player, Character character, WaitingItem waitingItem) + { + character.WaitingItems.Remove(waitingItem); + await player.PersistenceContext.DeleteAsync(waitingItem).ConfigureAwait(false); + } + + private static string GetWaitingItemName(this Player player, Item item) + { + var name = item.Definition?.Name.GetTranslation(player.Culture) ?? item.ToString(); + if (item.Level > 0) + { + name += $" +{item.Level}"; + } + + return item.Definition is { } definition + ? $"{name} ({definition.Width}x{definition.Height})" + : name; + } +} diff --git a/src/GameLogic/WaitingItems/WaitingItemsDeliveryPlugIn.cs b/src/GameLogic/WaitingItems/WaitingItemsDeliveryPlugIn.cs new file mode 100644 index 0000000000..8581d46d74 --- /dev/null +++ b/src/GameLogic/WaitingItems/WaitingItemsDeliveryPlugIn.cs @@ -0,0 +1,139 @@ +// +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace MUnique.OpenMU.GameLogic.WaitingItems; + +using System.Runtime.InteropServices; +using MUnique.OpenMU.GameLogic.PlugIns; +using MUnique.OpenMU.GameLogic.PlugIns.ChatCommands; +using MUnique.OpenMU.PlugIns; + +/// +/// Puts the waiting items of the characters into their inventories, as soon as they fit there: +/// when the character entered the game or opened the vault, and then periodically. +/// +/// +/// It checks periodically instead of reacting on each item action, because an action like moving +/// an item frees a place and takes it again; delivering in between would take the place away. +/// The delivery runs while holding the persistence lock of the player, so it doesn't run in the +/// middle of a packet handler, and the lock of the personal store, so it doesn't run in the middle of a purchase +/// in it. It only delivers while no dialog is open (e.g. trade, chaos machine). +/// When this plug-in is deactivated, the items keep waiting until it's activated again. +/// +[PlugIn] +[Display(Name = nameof(PlugInResources.WaitingItemsDeliveryPlugIn_Name), Description = nameof(PlugInResources.WaitingItemsDeliveryPlugIn_Description), ResourceType = typeof(PlugInResources))] +[Guid("112A4B4F-814B-415B-9A35-FB3C08C0AA87")] +public class WaitingItemsDeliveryPlugIn : IPeriodicTaskPlugIn, IPlayerStateChangedPlugIn, ISupportCustomConfiguration, ISupportDefaultCustomConfiguration +{ + private DateTime _nextRunUtc = DateTime.UtcNow; + + /// + public WaitingItemsDeliveryPlugInConfiguration? Configuration { get; set; } + + private WaitingItemsDeliveryPlugInConfiguration CurrentConfiguration => this.Configuration ??= new WaitingItemsDeliveryPlugInConfiguration(); + + /// + /// Gets the active instance of this plug-in of the game, if there is one. + /// + /// The game context. + /// The active instance, or null when it's deactivated. + public static WaitingItemsDeliveryPlugIn? GetActive(IGameContext gameContext) + { + return gameContext.PlugInManager.GetActivePlugInsOf().OfType().FirstOrDefault(); + } + + /// + public async ValueTask PlayerStateChangedAsync(Player player, State previousState, State currentState) + { + if (previousState != PlayerState.CharacterSelection + || currentState != PlayerState.EnteredWorld + || !player.HasDeliverableWaitingItems()) + { + return; + } + + await player.RunPersistenceExclusiveAsync(() => this.DeliverAndShowAsync(player)).ConfigureAwait(false); + } + + /// + /// Puts the waiting items of the player into the inventory, as far as they fit there, and tells the player + /// which items still wait: when the character entered the game, and when items were put aside when the vault was opened. + /// An error is logged instead of thrown, so that it doesn't stop the action which called it. + /// + /// The player. + public async ValueTask DeliverAndShowAsync(Player player) + { + try + { + // A purchase in the personal store changes the inventory of the seller while holding this lock, + // and the vault can be opened while the store is open. + using (await player.ShopStorage!.StoreLock.LockAsync().ConfigureAwait(false)) + { + var commandIsActive = player.GameContext.PlugInManager.GetStrategy(WaitingItemsChatCommandPlugIn.Command) is not null; + await player.DeliverWaitingItemsAsync().ConfigureAwait(false); + await player.ShowWaitingItemsAsync(this.CurrentConfiguration.ItemsNamed, pointToCommand: commandIsActive).ConfigureAwait(false); + } + } + catch (Exception ex) + { + player.Logger.LogError(ex, "Error when delivering the waiting items of player {Player}.", player); + } + } + + /// + public async ValueTask ExecuteTaskAsync(GameContext gameContext) + { + var now = DateTime.UtcNow; + if (now < this._nextRunUtc) + { + return; + } + + this._nextRunUtc = now + this.CurrentConfiguration.CheckInterval; + foreach (var player in await gameContext.GetPlayersAsync().ConfigureAwait(false)) + { + // Reading the count is safe without the lock; the items are only looked at while holding it, + // because opening the vault or a delivery may change them at the same time. + if (player.SelectedCharacter?.WaitingItems is not { Count: > 0 }) + { + continue; + } + + try + { + await player.RunPersistenceExclusiveAsync(async () => + { + if (player.PlayerState.CurrentState != PlayerState.EnteredWorld + || !player.HasDeliverableWaitingItems()) + { + return; + } + + // A purchase in the personal store changes the inventory of the seller while holding this lock, + // but not the persistence lock of the seller. + using (await player.ShopStorage!.StoreLock.LockAsync().ConfigureAwait(false)) + { + await player.DeliverWaitingItemsAsync().ConfigureAwait(false); + } + }).ConfigureAwait(false); + } + catch (Exception ex) + { + player.Logger.LogError(ex, "Error when delivering the waiting items of player {Player}.", player); + } + } + } + + /// + public void ForceStart() + { + this._nextRunUtc = DateTime.UtcNow; + } + + /// + public object CreateDefaultConfig() + { + return new WaitingItemsDeliveryPlugInConfiguration(); + } +} diff --git a/src/GameLogic/WaitingItems/WaitingItemsDeliveryPlugInConfiguration.cs b/src/GameLogic/WaitingItems/WaitingItemsDeliveryPlugInConfiguration.cs new file mode 100644 index 0000000000..fdd7ba92e9 --- /dev/null +++ b/src/GameLogic/WaitingItems/WaitingItemsDeliveryPlugInConfiguration.cs @@ -0,0 +1,24 @@ +// +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace MUnique.OpenMU.GameLogic.WaitingItems; + +/// +/// Configuration for the . +/// +public class WaitingItemsDeliveryPlugInConfiguration +{ + /// + /// Gets or sets the interval in which the waiting items are put into the inventories, as far as they fit there. + /// + [Display(ResourceType = typeof(PlugInResources), Name = nameof(PlugInResources.WaitingItemsDeliveryPlugInConfiguration_CheckInterval_Name), Description = nameof(PlugInResources.WaitingItemsDeliveryPlugInConfiguration_CheckInterval_Description))] + public TimeSpan CheckInterval { get; set; } = TimeSpan.FromSeconds(30); + + /// + /// Gets or sets the number of waiting items which are named when the player is told about them, + /// when entering the game or opening the vault. + /// + [Display(ResourceType = typeof(PlugInResources), Name = nameof(PlugInResources.WaitingItemsDeliveryPlugInConfiguration_ItemsNamed_Name), Description = nameof(PlugInResources.WaitingItemsDeliveryPlugInConfiguration_ItemsNamed_Description))] + public int ItemsNamed { get; set; } = 3; +} diff --git a/tests/MUnique.OpenMU.Tests/WaitingItemsTest.cs b/tests/MUnique.OpenMU.Tests/WaitingItemsTest.cs new file mode 100644 index 0000000000..22077f55f8 --- /dev/null +++ b/tests/MUnique.OpenMU.Tests/WaitingItemsTest.cs @@ -0,0 +1,530 @@ +// +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace MUnique.OpenMU.Tests; + +using Moq; +using MUnique.OpenMU.DataModel; +using MUnique.OpenMU.DataModel.Configuration; +using MUnique.OpenMU.DataModel.Configuration.Items; +using MUnique.OpenMU.DataModel.Entities; +using MUnique.OpenMU.GameLogic; +using MUnique.OpenMU.GameLogic.NPC; +using MUnique.OpenMU.GameLogic.PlayerActions; +using MUnique.OpenMU.GameLogic.PlayerActions.Character; +using MUnique.OpenMU.GameLogic.PlugIns.ChatCommands; +using MUnique.OpenMU.GameLogic.Views; +using MUnique.OpenMU.GameLogic.Views.Character; +using MUnique.OpenMU.GameLogic.Views.Inventory; +using MUnique.OpenMU.GameLogic.WaitingItems; +using MUnique.OpenMU.Interfaces; +using MUnique.OpenMU.PlugIns; +using BasicItem = MUnique.OpenMU.Persistence.BasicModel.Item; + +/// +/// Tests the waiting items: items which find no place when their storage is loaded wait for space in the inventory. +/// +[TestFixture] +public class WaitingItemsTest +{ + private const byte FirstInventorySlot = 12; + + private const int InventoryCells = 64; + + /// + /// Tests that a storage passes an item which finds no free place to the given action and removes it from the item storage. + /// + [Test] + public void StoragePutsAsideItemWithoutPlace() + { + var smallItems = Enumerable.Range(0, 16).Select(slot => CreateItem(1, 1, (byte)slot)).ToList(); + var grownItem = CreateItem(2, 2, 0); + var itemStorage = CreateItemStorage([.. smallItems, grownItem]); + var putAside = new List(); + + var storage = new Storage(16, 0, 0, itemStorage, TakeInto(putAside)); + + Assert.That(putAside, Is.EqualTo(new[] { grownItem })); + Assert.That(itemStorage.Items, Does.Not.Contain(grownItem)); + Assert.That(storage.Items, Is.EquivalentTo(smallItems)); + } + + /// + /// Tests that an item which finds no free place stays in the item storage when the action throws, so it isn't lost. + /// + [Test] + public void ItemStaysWhenPuttingAsideFails() + { + var grownItem = CreateItem(2, 2, 0); + var itemStorage = CreateItemStorage([.. Enumerable.Range(0, 16).Select(slot => CreateItem(1, 1, (byte)slot)), grownItem]); + + Assert.That(() => new Storage(16, 0, 0, itemStorage, _ => throw new InvalidOperationException()), Throws.InvalidOperationException); + Assert.That(itemStorage.Items, Does.Contain(grownItem)); + } + + /// + /// Tests that the personal store passes an item which finds no free place to the given action + /// and removes it from the inventory of the character. + /// + [Test] + public void ShopStoragePutsAsideItemWithoutPlace() + { + var character = PlayerTestHelper.CreateCharacter(GameContextTestHelper.CreateGameContext()); + var firstSlot = InventoryConstants.FirstStoreItemSlotIndex; + var smallItems = Enumerable.Range(firstSlot, InventoryConstants.StoreSize).Select(slot => CreateItem(1, 1, (byte)slot)).ToList(); + var grownItem = CreateItem(2, 2, firstSlot); + smallItems.ForEach(character.Inventory!.Items.Add); + character.Inventory.Items.Add(grownItem); + var putAside = new List(); + + var store = new ShopStorage(character, TakeInto(putAside)); + + Assert.That(putAside, Is.EqualTo(new[] { grownItem })); + Assert.That(character.Inventory.Items, Does.Not.Contain(grownItem)); + Assert.That(store.Items, Is.EquivalentTo(smallItems)); + } + + /// + /// Tests that a storage without such an action still throws when an item finds no free place. + /// + [Test] + public void StorageWithoutActionThrows() + { + var items = Enumerable.Range(0, 16).Select(slot => CreateItem(1, 1, (byte)slot)).Append(CreateItem(2, 2, 0)).ToArray(); + + Assert.That(() => new Storage(16, 0, 0, CreateItemStorage(items)), Throws.TypeOf()); + } + + /// + /// Tests that a character, whose inventory has an item which grew and finds no place, enters the game, + /// and that the item waits for space in the inventory. + /// + [Test] + public async ValueTask GrownItemWaitsWhenCharacterEntersAsync() + { + var (player, grownItem, _) = await CreatePlayerWithGrownItemAsync().ConfigureAwait(false); + + var waitingItem = player.SelectedCharacter!.WaitingItems.Single(); + Assert.That(waitingItem.Item, Is.SameAs(grownItem)); + Assert.That(waitingItem.Reason, Is.EqualTo(WaitingItemReason.NoSpaceWhenLoaded)); + Assert.That(player.SelectedCharacter.Inventory!.Items, Does.Not.Contain(grownItem)); + Assert.That(player.Inventory!.Items.Count(), Is.EqualTo(InventoryCells)); + } + + /// + /// Tests that an inventory item which finds no place stays in the inventory of the character when putting it + /// aside fails, so it isn't lost when the character is saved. + /// + [Test] + public async ValueTask InventoryItemStaysWhenPuttingAsideFailsAsync() + { + var gameContext = GameContextTestHelper.CreateGameContext(); + var player = await PlayerTestHelper.CreatePlayerAsync(gameContext).ConfigureAwait(false); + var character = PlayerTestHelper.CreateCharacter(gameContext); + Mock.Get(character).Setup(c => c.WaitingItems).Returns((ICollection)null!); + var grownItem = CreateItem(2, 2, FirstInventorySlot); + Enumerable.Range(FirstInventorySlot, InventoryCells).Select(slot => CreateItem(1, 1, (byte)slot)).ToList().ForEach(character.Inventory!.Items.Add); + character.Inventory.Items.Add(grownItem); + + Assert.That(async () => await PlayerTestHelper.EnterWorldAsync(player, character).ConfigureAwait(false), Throws.Exception); + Assert.That(character.Inventory.Items, Does.Contain(grownItem)); + } + + /// + /// Tests that an item which finds no place in the main inventory is put into an inventory extension + /// instead of waiting, when the extension has space. + /// + [Test] + public async ValueTask GrownItemIsPutIntoExtensionAsync() + { + var (player, grownItem, _) = await CreatePlayerWithGrownItemAsync(1).ConfigureAwait(false); + + Assert.That(player.SelectedCharacter!.WaitingItems, Is.Empty); + Assert.That(player.Inventory!.Items, Does.Contain(grownItem)); + Assert.That(grownItem.ItemSlot, Is.EqualTo(InventoryConstants.FirstExtensionItemSlotIndex)); + Assert.That(player.SelectedCharacter.Inventory!.Items, Does.Contain(grownItem)); + } + + /// + /// Tests that an item in the vault, which finds no place when the vault is opened, waits for space in the inventory. + /// + [Test] + public async ValueTask GrownVaultItemWaitsWhenVaultIsOpenedAsync() + { + var (player, _, _) = await CreatePlayerWithGrownItemAsync().ConfigureAwait(false); + Assert.That(await player.PlayerState.TryAdvanceToAsync(PlayerState.EnteredWorld).ConfigureAwait(false), Is.True); + var grownVaultItem = CreateItem(2, 2, 0); + var vault = CreateItemStorage([.. Enumerable.Range(0, InventoryConstants.WarehouseSize).Select(slot => CreateItem(1, 1, (byte)slot)), grownVaultItem]); + Mock.Get(player.Account!).Setup(a => a.Vault).Returns(vault); + var npc = new NonPlayerCharacter(null!, new Persistence.BasicModel.MonsterDefinition { NpcWindow = NpcWindow.VaultStorage }, null!); + + await new TalkNpcAction().TalkToNpcAsync(player, npc).ConfigureAwait(false); + + Assert.That(player.SelectedCharacter!.WaitingItems.Select(w => w.Item), Does.Contain(grownVaultItem)); + Assert.That(vault.Items, Does.Not.Contain(grownVaultItem)); + Assert.That(player.Vault!.Items.Count(), Is.EqualTo(InventoryConstants.WarehouseSize)); + } + + /// + /// Tests that a waiting item stays in the waiting items while it still doesn't fit. + /// + [Test] + public async ValueTask WaitingItemStaysWithoutSpaceAsync() + { + var (player, grownItem, _) = await CreatePlayerWithGrownItemAsync().ConfigureAwait(false); + + var delivered = await player.DeliverWaitingItemsAsync().ConfigureAwait(false); + + Assert.That(delivered, Is.Zero); + Assert.That(player.SelectedCharacter!.WaitingItems.Single().Item, Is.SameAs(grownItem)); + Assert.That(player.Inventory!.Items, Does.Not.Contain(grownItem)); + } + + /// + /// Tests that a waiting item is put into the inventory as soon as it fits there, and that the player is told. + /// + [Test] + public async ValueTask WaitingItemIsDeliveredWhenItFitsAsync() + { + var (player, grownItem, smallItems) = await CreatePlayerWithGrownItemAsync().ConfigureAwait(false); + await RemoveSmallItemsAsync(player, smallItems, 12, 13, 20, 21).ConfigureAwait(false); + + var delivered = await player.DeliverWaitingItemsAsync().ConfigureAwait(false); + + Assert.That(delivered, Is.EqualTo(1)); + Assert.That(player.SelectedCharacter!.WaitingItems, Is.Empty); + Assert.That(player.Inventory!.Items, Does.Contain(grownItem)); + Assert.That(player.SelectedCharacter.Inventory!.Items, Does.Contain(grownItem)); + Assert.That(grownItem.ItemSlot, Is.EqualTo(FirstInventorySlot)); + Mock.Get(player.ViewPlugIns.GetPlugIn()!).Verify(p => p.ItemAppearAsync(grownItem), Times.Once); + Mock.Get(player.ViewPlugIns.GetPlugIn()!).Verify(p => p.ShowMessageAsync("Scroll of Blood +3 (2x2) was put into your inventory.", MessageType.BlueNormal), Times.Once); + } + + /// + /// Tests that a smaller waiting item is delivered even when an older, bigger one doesn't fit yet. + /// + [Test] + public async ValueTask SmallerItemIsDeliveredBeforeBiggerOneAsync() + { + var (player, grownItem, smallItems) = await CreatePlayerWithGrownItemAsync().ConfigureAwait(false); + var character = player.SelectedCharacter!; + var smallWaitingItem = CreateItem(1, 1, 0); + character.AddWaitingItem(player.PersistenceContext, smallWaitingItem, WaitingItemReason.NoSpaceWhenLoaded); + character.WaitingItems.Last().AddedAt = character.WaitingItems.First().AddedAt.AddMinutes(1); + await RemoveSmallItemsAsync(player, smallItems, 75).ConfigureAwait(false); + + var delivered = await player.DeliverWaitingItemsAsync().ConfigureAwait(false); + + Assert.That(delivered, Is.EqualTo(1)); + Assert.That(player.Inventory!.Items, Does.Contain(smallWaitingItem)); + Assert.That(character.WaitingItems.Single().Item, Is.SameAs(grownItem)); + } + + /// + /// Tests that a waiting item without an item definition stays, and doesn't hold back the other waiting items. + /// + [Test] + public async ValueTask ItemWithoutDefinitionStaysAsync() + { + var (player, grownItem, smallItems) = await CreatePlayerWithGrownItemAsync().ConfigureAwait(false); + var character = player.SelectedCharacter!; + var waitingItem = character.WaitingItems.Single(); + var definition = grownItem.Definition; + grownItem.Definition = null; + var smallWaitingItem = CreateItem(1, 1, 0); + character.AddWaitingItem(player.PersistenceContext, smallWaitingItem, WaitingItemReason.NoSpaceWhenLoaded); + character.WaitingItems.Last().AddedAt = waitingItem.AddedAt.AddMinutes(1); + await RemoveSmallItemsAsync(player, smallItems, 12, 13, 20, 21).ConfigureAwait(false); + + var delivered = await player.DeliverWaitingItemsAsync().ConfigureAwait(false); + + Assert.That(delivered, Is.EqualTo(1)); + Assert.That(player.Inventory!.Items, Does.Contain(smallWaitingItem)); + Assert.That(character.WaitingItems.Single(), Is.SameAs(waitingItem)); + Assert.That(waitingItem.Item, Is.SameAs(grownItem)); + Assert.That(player.HasDeliverableWaitingItems(), Is.False); + + grownItem.Definition = definition; + Assert.That(player.HasDeliverableWaitingItems(), Is.True); + } + + /// + /// Tests that the delivery plug-in tells the player about the waiting items when the character entered the game, + /// and puts them into the inventory when they fit. + /// + [Test] + public async ValueTask PlugInDeliversWhenCharacterEnteredAsync() + { + var (player, grownItem, smallItems) = await CreatePlayerWithGrownItemAsync().ConfigureAwait(false); + var plugIn = new WaitingItemsDeliveryPlugIn(); + var messages = Mock.Get(player.ViewPlugIns.GetPlugIn()!); + + await plugIn.PlayerStateChangedAsync(player, PlayerState.CharacterSelection, PlayerState.EnteredWorld).ConfigureAwait(false); + + messages.Verify(p => p.ShowMessageAsync("1 item(s) wait for space in your inventory. They are put into it as soon as they fit:", MessageType.BlueNormal), Times.Once); + messages.Verify(p => p.ShowMessageAsync("- Scroll of Blood +3 (2x2)", MessageType.BlueNormal), Times.Once); + Assert.That(player.SelectedCharacter!.WaitingItems, Has.Count.EqualTo(1)); + + await RemoveSmallItemsAsync(player, smallItems, 12, 13, 20, 21).ConfigureAwait(false); + await plugIn.PlayerStateChangedAsync(player, PlayerState.CharacterSelection, PlayerState.EnteredWorld).ConfigureAwait(false); + + Assert.That(player.Inventory!.Items, Does.Contain(grownItem)); + Assert.That(player.SelectedCharacter.WaitingItems, Is.Empty); + } + + /// + /// Tests that the periodic delivery puts a waiting item into the inventory when it fits, + /// but only while no dialog is open. + /// + [Test] + public async ValueTask PeriodicDeliveryOnlyWithoutDialogAsync() + { + var (player, grownItem, smallItems) = await CreatePlayerWithGrownItemAsync().ConfigureAwait(false); + var gameContext = (GameContext)player.GameContext; + await gameContext.AddPlayerAsync(player).ConfigureAwait(false); + await player.PlayerState.TryAdvanceToAsync(PlayerState.EnteredWorld).ConfigureAwait(false); + await RemoveSmallItemsAsync(player, smallItems, 12, 13, 20, 21).ConfigureAwait(false); + var plugIn = new WaitingItemsDeliveryPlugIn(); + + Assert.That(await player.PlayerState.TryAdvanceToAsync(PlayerState.NpcDialogOpened).ConfigureAwait(false), Is.True); + await plugIn.ExecuteTaskAsync(gameContext).ConfigureAwait(false); + Assert.That(player.Inventory!.Items, Does.Not.Contain(grownItem)); + + Assert.That(await player.PlayerState.TryAdvanceToAsync(PlayerState.EnteredWorld).ConfigureAwait(false), Is.True); + plugIn.ForceStart(); + await plugIn.ExecuteTaskAsync(gameContext).ConfigureAwait(false); + Assert.That(player.Inventory!.Items, Does.Contain(grownItem)); + } + + /// + /// Tests that the periodic delivery waits while a purchase in the personal store holds its lock. + /// + [Test] + public async ValueTask PeriodicDeliveryWaitsForStoreLockAsync() + { + var (player, grownItem, smallItems) = await CreatePlayerWithGrownItemAsync().ConfigureAwait(false); + var gameContext = (GameContext)player.GameContext; + await gameContext.AddPlayerAsync(player).ConfigureAwait(false); + await player.PlayerState.TryAdvanceToAsync(PlayerState.EnteredWorld).ConfigureAwait(false); + await RemoveSmallItemsAsync(player, smallItems, 12, 13, 20, 21).ConfigureAwait(false); + + Task delivery; + using (await player.ShopStorage!.StoreLock.LockAsync().ConfigureAwait(false)) + { + delivery = new WaitingItemsDeliveryPlugIn().ExecuteTaskAsync(gameContext).AsTask(); + await Task.Delay(100).ConfigureAwait(false); + Assert.That(delivery.IsCompleted, Is.False); + Assert.That(player.Inventory!.Items, Does.Not.Contain(grownItem)); + } + + await delivery.ConfigureAwait(false); + Assert.That(player.Inventory!.Items, Does.Contain(grownItem)); + } + + /// + /// Tests that the delivery after opening the vault waits while a purchase in the personal store holds its lock. + /// + [Test] + public async ValueTask VaultDeliveryWaitsForStoreLockAsync() + { + var (player, grownItem, smallItems) = await CreatePlayerWithGrownItemAsync().ConfigureAwait(false); + await RemoveSmallItemsAsync(player, smallItems, 12, 13, 20, 21).ConfigureAwait(false); + + Task delivery; + using (await player.ShopStorage!.StoreLock.LockAsync().ConfigureAwait(false)) + { + delivery = new WaitingItemsDeliveryPlugIn().DeliverAndShowAsync(player).AsTask(); + await Task.Delay(100).ConfigureAwait(false); + Assert.That(delivery.IsCompleted, Is.False); + } + + await delivery.ConfigureAwait(false); + Assert.That(player.Inventory!.Items, Does.Contain(grownItem)); + } + + /// + /// Tests that an error in the delivery when the character entered the game is logged instead of thrown, + /// so that it doesn't stop entering the game. + /// + [Test] + public async ValueTask ErrorWhenCharacterEnteredDoesntThrowAsync() + { + var (player, _, _) = await CreatePlayerWithGrownItemAsync().ConfigureAwait(false); + Mock.Get(player.ViewPlugIns.GetPlugIn()!) + .Setup(p => p.ShowMessageAsync(It.IsAny(), It.IsAny())) + .ThrowsAsync(new InvalidOperationException()); + + await new WaitingItemsDeliveryPlugIn().PlayerStateChangedAsync(player, PlayerState.CharacterSelection, PlayerState.EnteredWorld).ConfigureAwait(false); + + Assert.That(player.SelectedCharacter!.WaitingItems, Has.Count.EqualTo(1)); + } + + /// + /// Tests that a character can't be deleted while items wait for it, because an item which found no place + /// in the vault belongs to the account and would be deleted with the character. + /// + [Test] + public async ValueTask CharacterWithWaitingItemsIsNotDeletedAsync() + { + var (player, _, _) = await CreatePlayerWithGrownItemAsync().ConfigureAwait(false); + var character = player.SelectedCharacter!; + character.Name = "Waiting"; + var accountMock = Mock.Get(player.Account!); + accountMock.Setup(a => a.Characters).Returns(new List { character }); + player.Account!.SecurityCode = "1234"; + await player.PlayerState.TryAdvanceToAsync(PlayerState.CharacterSelection).ConfigureAwait(false); + + await new DeleteCharacterAction().DeleteCharacterAsync(player, character.Name, "1234").ConfigureAwait(false); + + Assert.That(player.Account!.Characters, Does.Contain(character)); + Mock.Get(player.ViewPlugIns.GetPlugIn()!).Verify(p => p.ShowCharacterDeleteResponseAsync(CharacterDeleteResult.Unsuccessful), Times.Once); + } + + /// + /// Tests that a character whose only waiting item has no item definition can't be deleted either, + /// because the item is kept until the definition is back, and that the player is asked to contact an admin. + /// + [Test] + public async ValueTask CharacterWithUndeliverableWaitingItemIsNotDeletedAsync() + { + var (player, grownItem, _) = await CreatePlayerWithGrownItemAsync().ConfigureAwait(false); + grownItem.Definition = null; + var character = player.SelectedCharacter!; + character.Name = "Waiting"; + Mock.Get(player.Account!).Setup(a => a.Characters).Returns(new List { character }); + player.Account!.SecurityCode = "1234"; + await player.PlayerState.TryAdvanceToAsync(PlayerState.CharacterSelection).ConfigureAwait(false); + + await new DeleteCharacterAction().DeleteCharacterAsync(player, character.Name, "1234").ConfigureAwait(false); + + Assert.That(player.Account.Characters, Does.Contain(character)); + Mock.Get(player.ViewPlugIns.GetPlugIn()!).Verify(p => p.ShowMessageAsync(It.Is(m => m.Contains("admin")), MessageType.BlueNormal), Times.Once); + } + + /// + /// Tests that a long list of waiting items is told in short messages: one per named item, + /// and the number of the items which aren't named. + /// + [Test] + public async ValueTask ManyWaitingItemsAreToldInShortMessagesAsync() + { + var (player, _, _) = await CreatePlayerWithGrownItemAsync().ConfigureAwait(false); + for (var i = 0; i < 4; i++) + { + player.SelectedCharacter!.AddWaitingItem(player.PersistenceContext, CreateItem(2, 2, 0), WaitingItemReason.NoSpaceWhenLoaded); + } + + var messages = Mock.Get(player.ViewPlugIns.GetPlugIn()!); + + await player.ShowWaitingItemsAsync(3, pointToCommand: true).ConfigureAwait(false); + + messages.Verify(p => p.ShowMessageAsync("5 item(s) wait for space in your inventory. They are put into it as soon as they fit:", MessageType.BlueNormal), Times.Once); + messages.Verify(p => p.ShowMessageAsync(It.Is(m => m.StartsWith("- ")), MessageType.BlueNormal), Times.Exactly(3)); + messages.Verify(p => p.ShowMessageAsync("... and 2 more. /waiting lists more of them.", MessageType.BlueNormal), Times.Once); + } + + /// + /// Tests that the delivery points the player to /waiting for the items which aren't named + /// only while that chat command is active. + /// + /// If set to true, the chat command is active. + [TestCase(true)] + [TestCase(false)] + public async ValueTask DeliveryPointsToActiveCommandAsync(bool commandActive) + { + var plugInConfigurations = commandActive + ? null + : new[] { new PlugInConfiguration { TypeId = typeof(WaitingItemsChatCommandPlugIn).GUID, IsActive = false } }; + var (player, _, _) = await CreatePlayerWithGrownItemAsync(plugInConfigurations: plugInConfigurations).ConfigureAwait(false); + Assert.That(player.GameContext.PlugInManager.GetStrategy("/waiting") is not null, Is.EqualTo(commandActive)); + for (var i = 0; i < 4; i++) + { + player.SelectedCharacter!.AddWaitingItem(player.PersistenceContext, CreateItem(2, 2, 0), WaitingItemReason.NoSpaceWhenLoaded); + } + + var messages = Mock.Get(player.ViewPlugIns.GetPlugIn()!); + + await new WaitingItemsDeliveryPlugIn().DeliverAndShowAsync(player).ConfigureAwait(false); + + messages.Verify(p => p.ShowMessageAsync("... and 2 more. /waiting lists more of them.", MessageType.BlueNormal), Times.Exactly(commandActive ? 1 : 0)); + messages.Verify(p => p.ShowMessageAsync("... and 2 more.", MessageType.BlueNormal), Times.Exactly(commandActive ? 0 : 1)); + } + + /// + /// Tests that the player isn't pointed to the chat command when it's not wanted, + /// e.g. because it's deactivated or it's the chat command itself. + /// + [Test] + public async ValueTask NotPointedToCommandAsync() + { + var (player, _, _) = await CreatePlayerWithGrownItemAsync().ConfigureAwait(false); + for (var i = 0; i < 4; i++) + { + player.SelectedCharacter!.AddWaitingItem(player.PersistenceContext, CreateItem(2, 2, 0), WaitingItemReason.NoSpaceWhenLoaded); + } + + var messages = Mock.Get(player.ViewPlugIns.GetPlugIn()!); + + await player.ShowWaitingItemsAsync(3).ConfigureAwait(false); + + messages.Verify(p => p.ShowMessageAsync("... and 2 more.", MessageType.BlueNormal), Times.Once); + messages.Verify(p => p.ShowMessageAsync(It.Is(m => m.Contains("/waiting")), MessageType.BlueNormal), Times.Never); + } + + private static async ValueTask<(Player Player, Item GrownItem, List SmallItems)> CreatePlayerWithGrownItemAsync(int inventoryExtensions = 0, IEnumerable? plugInConfigurations = null) + { + var gameContext = GameContextTestHelper.CreateGameContext(plugInConfigurations); + var player = await PlayerTestHelper.CreatePlayerAsync(gameContext).ConfigureAwait(false); + var character = PlayerTestHelper.CreateCharacter(gameContext); + Mock.Get(character).Setup(c => c.WaitingItems).Returns(new List()); + character.InventoryExtensions = inventoryExtensions; + + // The small items come first, so the grown item is the one which finds no place. + var smallItems = Enumerable.Range(FirstInventorySlot, InventoryCells).Select(slot => CreateItem(1, 1, (byte)slot)).ToList(); + var grownItem = CreateItem(2, 2, FirstInventorySlot); + grownItem.Level = 3; + grownItem.Definition!.Name = "Scroll of Blood"; + smallItems.ForEach(character.Inventory!.Items.Add); + character.Inventory.Items.Add(grownItem); + + await PlayerTestHelper.EnterWorldAsync(player, character).ConfigureAwait(false); + return (player, grownItem, smallItems); + } + + private static async ValueTask RemoveSmallItemsAsync(Player player, List smallItems, params int[] slots) + { + foreach (var slot in slots) + { + await player.Inventory!.RemoveItemAsync(smallItems.First(i => i.ItemSlot == slot)).ConfigureAwait(false); + } + } + + private static Item CreateItem(byte width, byte height, byte slot) + { + // The items are compared by their identifier. + return new BasicItem + { + Id = Guid.NewGuid(), + Definition = new ItemDefinition { Width = width, Height = height, Durability = 1 }, + ItemSlot = slot, + Durability = 1, + }; + } + + private static Func TakeInto(List items) + { + return item => + { + items.Add(item); + return true; + }; + } + + private static ItemStorage CreateItemStorage(params Item[] items) + { + var storage = new Mock(); + storage.Setup(s => s.Items).Returns(items.ToList()); + return storage.Object; + } +} From c5ea63069b1301b9520db9663855ae4ff86f9b25 Mon Sep 17 00:00:00 2001 From: Mosch0512 Date: Fri, 13 Nov 2026 15:52:28 +0100 Subject: [PATCH 3/4] fix(admin-panel): adding an item to a character with an item which finds no place doesn't fail The placement check loads a detached copy of the inventory or vault, which threw for an item whose definition grew. In the game such an item waits for space in the inventory, so here it just takes no place. --- src/Web/AdminPanel.Operations/CharacterWriteOperations.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Web/AdminPanel.Operations/CharacterWriteOperations.cs b/src/Web/AdminPanel.Operations/CharacterWriteOperations.cs index f05c8c9e44..f4de947651 100644 --- a/src/Web/AdminPanel.Operations/CharacterWriteOperations.cs +++ b/src/Web/AdminPanel.Operations/CharacterWriteOperations.cs @@ -160,13 +160,17 @@ private async ValueTask ChangeAsync(string characterName, string pat } // Use detached storage/items for placement checks: Storage repairs malformed layouts in its constructor. + // An item which finds no place, e.g. because its definition grew, waits for space in the inventory + // when the storage is loaded in the game, so it takes no place here. var detached = new PreviewStorage(storage); - var grid = vault ? new Storage(InventoryConstants.WarehouseSize, detached) + Func ignoreItemWithoutPlace = _ => false; + var grid = vault ? new Storage(InventoryConstants.WarehouseSize, 0, 0, detached, ignoreItemWithoutPlace) : new Storage( InventoryConstants.GetInventorySize(0), InventoryConstants.EquippableSlotsCount, 0, - new ItemStorageAdapter(detached, 0, InventoryConstants.GetInventorySize(0))); + new ItemStorageAdapter(detached, 0, InventoryConstants.GetInventorySize(0)), + ignoreItemWithoutPlace); slot = grid.CheckInvSpace(new Item { Definition = definition, Level = level }); if (slot is null) { From bf2a9f6b0c43ad7ea73b3c6371d557af91debaa7 Mon Sep 17 00:00:00 2001 From: Mosch0512 Date: Fri, 13 Nov 2026 15:52:28 +0100 Subject: [PATCH 4/4] docs(items): page about the waiting items --- .../docs/server-features/waiting-items.md | 63 +++++++++++++++++++ docs-website/sidebars.js | 1 + 2 files changed, 64 insertions(+) create mode 100644 docs-website/docs/server-features/waiting-items.md diff --git a/docs-website/docs/server-features/waiting-items.md b/docs-website/docs/server-features/waiting-items.md new file mode 100644 index 0000000000..7ef366eb5a --- /dev/null +++ b/docs-website/docs/server-features/waiting-items.md @@ -0,0 +1,63 @@ +--- +title: Waiting items +sidebar_label: Waiting items +sidebar_position: 6 +description: Items which find no place in their storage wait for space in the inventory, instead of blocking the character. +--- + +When the size of an item definition grows, e.g. after an update of the game +configuration, an item of that kind may no longer fit where it is: in the +inventory, the personal store or the vault. Such an item doesn't stop the +character from entering the game. It's taken out of its storage and waits for +space in the inventory of the character. The server log gets a warning for each +of these items. + +An item in the inventory first tries the other places of the inventory, +including the inventory extensions, before it waits. + +## Delivery + +The plugin **Waiting items delivery** puts the waiting items into the inventory +as soon as they fit there: + +* when the character enters the game, +* when the character opens the vault and one of its items found no place, +* and then periodically, by default every 30 seconds, but only while no dialog + is open, e.g. no trade, chaos machine, merchant or vault. + +The oldest item comes first. An item which doesn't fit doesn't hold back a +later, smaller one. The client gets a delivered item like any other new item of +the inventory, so this works with every client. + +When the plugin is deactivated, the items keep waiting, and they are delivered +when it's activated again. + +### Settings + +| Setting | Default | Meaning | +|---|---|---| +| Check interval | 30 seconds | How often the waiting items are put into the inventories | +| Named items | 3 | How many waiting items are named when the player is told about them | + +## Messages + +When the character enters the game or opens the vault, the player is told which +items wait: one line with their number, one line per item (name, level and +size), and "... and N more." for the rest. Each delivered item is announced with +its own line. + +The chat command `/waiting` lists the waiting items, by default up to 10 (its +setting **Named items**). The automatic message points to it when the command is +active. + +## Deleting a character + +The waiting items belong to the character and are deleted with it. Because an +item from the vault belongs to the whole account, a character with waiting items +can't be deleted. The player is asked to enter the game with it and make room +first. + +An item whose item definition no longer exists isn't delivered or listed. It +stays in the database until the definition is back, like an inventory item +without a definition, and it prevents the deletion, too. Then, the player is +asked to contact an admin. diff --git a/docs-website/sidebars.js b/docs-website/sidebars.js index 2268090ad8..21a4fe4991 100644 --- a/docs-website/sidebars.js +++ b/docs-website/sidebars.js @@ -68,6 +68,7 @@ const sidebars = { "server-features/cash-shop", "server-features/discord", "server-features/player-shop-currencies", + "server-features/waiting-items", ], }, {