diff --git a/src/Dapr/GuildServer.Host/GuildServerController.cs b/src/Dapr/GuildServer.Host/GuildServerController.cs index 77c392fce8..3d008d697b 100644 --- a/src/Dapr/GuildServer.Host/GuildServerController.cs +++ b/src/Dapr/GuildServer.Host/GuildServerController.cs @@ -56,6 +56,28 @@ public ValueTask GuildExistsAsync([FromBody] string guildName) return this._guildServer.GetGuildAsync(guildId); } + /// + /// Gets the persistent identifier of a guild by its runtime identifier. + /// + /// The runtime guild identifier. + /// The persistent guild identifier, or if the guild was not found. + [HttpPost(nameof(IGuildServer.GetPersistentGuildIdAsync))] + public ValueTask GetPersistentGuildIdAsync([FromBody] uint guildId) + { + return this._guildServer.GetPersistentGuildIdAsync(guildId); + } + + /// + /// Gets the persistent alliance master identifier of a guild by its runtime identifier. + /// + /// The runtime guild identifier. + /// The persistent alliance master guild identifier, or if the guild was not found. + [HttpPost(nameof(IGuildServer.GetPersistentAllianceMasterGuildIdAsync))] + public ValueTask GetPersistentAllianceMasterGuildIdAsync([FromBody] uint guildId) + { + return this._guildServer.GetPersistentAllianceMasterGuildIdAsync(guildId); + } + /// /// Gets the guild id by the guild name. /// @@ -160,4 +182,4 @@ public ValueTask IncreaseGuildScoreAsync([FromBody] uint guildId) { return this._guildServer.IncreaseGuildScoreAsync(guildId); } -} \ No newline at end of file +} diff --git a/src/Dapr/ServerClients/GuildServer.cs b/src/Dapr/ServerClients/GuildServer.cs index 165b594baa..aa2b18e800 100644 --- a/src/Dapr/ServerClients/GuildServer.cs +++ b/src/Dapr/ServerClients/GuildServer.cs @@ -58,6 +58,34 @@ public async ValueTask GuildExistsAsync(string guildName) } } + /// + public async ValueTask GetPersistentGuildIdAsync(uint guildId) + { + try + { + return await this._daprClient.InvokeMethodAsync(this._targetAppId, nameof(this.GetPersistentGuildIdAsync), guildId).ConfigureAwait(false); + } + catch (Exception ex) + { + this._logger.LogError(ex, "Unexpected error when retrieving a persistent guild identifier."); + return null; + } + } + + /// + public async ValueTask GetPersistentAllianceMasterGuildIdAsync(uint guildId) + { + try + { + return await this._daprClient.InvokeMethodAsync(this._targetAppId, nameof(this.GetPersistentAllianceMasterGuildIdAsync), guildId).ConfigureAwait(false); + } + catch (Exception ex) + { + this._logger.LogError(ex, "Unexpected error when retrieving a persistent alliance master guild identifier."); + return null; + } + } + /// public async ValueTask GetGuildIdByNameAsync(string guildName) { @@ -263,4 +291,4 @@ public async ValueTask GetGuildRelationshipAsync(uint guild1, return GuildRelationship.None; } } -} \ No newline at end of file +} diff --git a/src/DataModel/Configuration/CastleSiegeConfiguration.cs b/src/DataModel/Configuration/CastleSiegeConfiguration.cs index 595d673559..651d46ad56 100644 --- a/src/DataModel/Configuration/CastleSiegeConfiguration.cs +++ b/src/DataModel/Configuration/CastleSiegeConfiguration.cs @@ -33,6 +33,16 @@ public partial class CastleSiegeConfiguration /// public int RegisterMinMembers { get; set; } = 20; + /// + /// Gets or sets the item definition used for Sign of Lord registration. + /// + public virtual ItemDefinition? SignOfLordItemDefinition { get; set; } + + /// + /// Gets or sets the required level of a Sign of Lord item. + /// + public byte SignOfLordItemLevel { get; set; } = 3; + /// /// Gets or sets the minimum number of seconds a participant must be present in the battle to be eligible for a reward. /// diff --git a/src/GameLogic/CastleSiege/Actions/CastleSiegeGuildReference.cs b/src/GameLogic/CastleSiege/Actions/CastleSiegeGuildReference.cs new file mode 100644 index 0000000000..0421851a3d --- /dev/null +++ b/src/GameLogic/CastleSiege/Actions/CastleSiegeGuildReference.cs @@ -0,0 +1,13 @@ +// +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace MUnique.OpenMU.GameLogic.CastleSiege.Actions; + +/// +/// A Castle Siege guild identity spanning runtime and persistent identifiers. +/// +/// The runtime guild identifier. +/// The persistent guild identifier. +/// The guild name. +internal sealed record CastleSiegeGuildReference(uint RuntimeId, Guid PersistentId, string Name); diff --git a/src/GameLogic/CastleSiege/Actions/CastleSiegeGuildResolver.cs b/src/GameLogic/CastleSiege/Actions/CastleSiegeGuildResolver.cs new file mode 100644 index 0000000000..42262e01cf --- /dev/null +++ b/src/GameLogic/CastleSiege/Actions/CastleSiegeGuildResolver.cs @@ -0,0 +1,70 @@ +// +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace MUnique.OpenMU.GameLogic.CastleSiege.Actions; + +using MUnique.OpenMU.GameLogic.Views.CastleSiege; +using MUnique.OpenMU.Interfaces; + +/// +/// Resolves runtime guild information to its persistent Castle Siege identity. +/// +internal static class CastleSiegeGuildResolver +{ + /// + /// Resolves a guild which is allowed to mutate a Castle Siege registration. + /// + /// The requesting player. + /// The resolved guild and validation result. + public static async ValueTask<(CastleSiegeGuildReference? Guild, CastleSiegeRegistrationResult Result)> ResolveAuthorizedGuildAsync( + Player player) + { + if (player.GuildStatus is not { } guildStatus) + { + return (null, CastleSiegeRegistrationResult.NoGuild); + } + + if (guildStatus.Position != GuildPosition.GuildMaster + || player.GameContext is not IGameServerContext gameServerContext) + { + return (null, CastleSiegeRegistrationResult.InvalidGuild); + } + + if (await gameServerContext.GuildServer.GetGuildAsync(guildStatus.GuildId).ConfigureAwait(false) is not { Name: not null } guild) + { + return (null, CastleSiegeRegistrationResult.InvalidGuild); + } + + if (guild.AllianceGuild is not null + && !await gameServerContext.GuildServer.IsAllianceMasterAsync(guildStatus.GuildId).ConfigureAwait(false)) + { + return (null, CastleSiegeRegistrationResult.InvalidGuild); + } + + if (await gameServerContext.GuildServer.GetPersistentGuildIdAsync(guildStatus.GuildId).ConfigureAwait(false) is not { } persistentGuildId) + { + return (null, CastleSiegeRegistrationResult.InvalidGuild); + } + + return (new(guildStatus.GuildId, persistentGuildId, guild.Name), CastleSiegeRegistrationResult.Success); + } + + /// + /// Resolves the registration identity visible to any member of a guild or alliance. + /// + /// The requesting player. + /// The persistent registration guild identifier, or . + public static async ValueTask ResolveRegistrationGuildIdAsync(Player player) + { + if (player.GuildStatus is not { } guildStatus + || player.GameContext is not IGameServerContext gameServerContext) + { + return null; + } + + return await gameServerContext.GuildServer + .GetPersistentAllianceMasterGuildIdAsync(guildStatus.GuildId) + .ConfigureAwait(false); + } +} diff --git a/src/GameLogic/CastleSiege/Actions/CastleSiegeRegisterGuildAction.cs b/src/GameLogic/CastleSiege/Actions/CastleSiegeRegisterGuildAction.cs new file mode 100644 index 0000000000..ef4e07bee8 --- /dev/null +++ b/src/GameLogic/CastleSiege/Actions/CastleSiegeRegisterGuildAction.cs @@ -0,0 +1,87 @@ +// +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace MUnique.OpenMU.GameLogic.CastleSiege.Actions; + +using MUnique.OpenMU.DataModel.Configuration; +using MUnique.OpenMU.GameLogic.Attributes; +using MUnique.OpenMU.GameLogic.Views.CastleSiege; + +/// +/// Validates and processes Castle Siege guild registrations. +/// +public class CastleSiegeRegisterGuildAction +{ + /// + /// Tries to register the player's guild or alliance for Castle Siege. + /// + /// The requesting player. + /// The Castle Siege context, if initialized. + /// A task which represents the asynchronous operation. + public async ValueTask RegisterAsync(Player player, CastleSiegeContext? context) + { + var (result, guildName) = await RegisterCoreAsync(player, context).ConfigureAwait(false); + await player.InvokeViewPlugInAsync( + view => view.ShowRegistrationResultAsync(result, guildName)).ConfigureAwait(false); + } + + private static async ValueTask<(CastleSiegeRegistrationResult Result, string GuildName)> RegisterCoreAsync( + Player player, + CastleSiegeContext? context) + { + if (context is not { Configuration.Enabled: true }) + { + return (CastleSiegeRegistrationResult.Failed, string.Empty); + } + + await context.ExecutionLock.WaitAsync().ConfigureAwait(false); + try + { + if (context.CurrentState != CastleSiegeState.RegisterGuild) + { + return (CastleSiegeRegistrationResult.NotRegistrationPeriod, string.Empty); + } + + var (guild, resolutionResult) = await CastleSiegeGuildResolver.ResolveAuthorizedGuildAsync(player).ConfigureAwait(false); + if (guild is null) + { + return (resolutionResult, string.Empty); + } + + var combinedLevel = player.Level + (int)(player.Attributes?[Stats.MasterLevel] ?? 0); + if (combinedLevel < context.Configuration.RegisterMinLevel) + { + return (CastleSiegeRegistrationResult.LevelInsufficient, guild.Name); + } + + if (player.GameContext is not IGameServerContext gameServerContext) + { + return (CastleSiegeRegistrationResult.InvalidGuild, guild.Name); + } + + var members = await gameServerContext.GuildServer.GetGuildListAsync(guild.RuntimeId).ConfigureAwait(false); + if (members.Count < context.Configuration.RegisterMinMembers) + { + return (CastleSiegeRegistrationResult.NotEnoughMembers, guild.Name); + } + + if (context.RegisteredGuilds.ContainsKey(guild.PersistentId)) + { + return (CastleSiegeRegistrationResult.AlreadyRegistered, guild.Name); + } + + if (context.SiegeData.OwnerGuildId == guild.PersistentId) + { + return (CastleSiegeRegistrationResult.IsDefender, guild.Name); + } + + await context.AddRegistrationAsync(guild.PersistentId, guild.Name).ConfigureAwait(false); + return (CastleSiegeRegistrationResult.Success, guild.Name); + } + finally + { + context.ExecutionLock.Release(); + } + } +} diff --git a/src/GameLogic/CastleSiege/Actions/CastleSiegeRegisterMarkAction.cs b/src/GameLogic/CastleSiege/Actions/CastleSiegeRegisterMarkAction.cs new file mode 100644 index 0000000000..aa129681c4 --- /dev/null +++ b/src/GameLogic/CastleSiege/Actions/CastleSiegeRegisterMarkAction.cs @@ -0,0 +1,77 @@ +// +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace MUnique.OpenMU.GameLogic.CastleSiege.Actions; + +using MUnique.OpenMU.DataModel.Configuration; +using MUnique.OpenMU.GameLogic.Views.CastleSiege; + +/// +/// Validates and processes Sign of Lord submissions. +/// +public class CastleSiegeRegisterMarkAction +{ + /// + /// Tries to submit the Sign of Lord from an inventory slot. + /// + /// The requesting player. + /// The Castle Siege context, if initialized. + /// The inventory slot. + /// A task which represents the asynchronous operation. + public async ValueTask RegisterMarkAsync(Player player, CastleSiegeContext? context, byte inventorySlot) + { + var (result, guildName, marks) = await RegisterMarkCoreAsync(player, context, inventorySlot).ConfigureAwait(false); + await player.InvokeViewPlugInAsync( + view => view.ShowMarkRegistrationResultAsync(result, guildName, marks)).ConfigureAwait(false); + } + + private static async ValueTask<(CastleSiegeMarkRegistrationResult Result, string GuildName, int Marks)> RegisterMarkCoreAsync( + Player player, + CastleSiegeContext? context, + byte inventorySlot) + { + if (context is not { Configuration.Enabled: true }) + { + return (CastleSiegeMarkRegistrationResult.Failed, string.Empty, 0); + } + + await context.ExecutionLock.WaitAsync().ConfigureAwait(false); + try + { + if (context.CurrentState != CastleSiegeState.RegisterMark) + { + return (CastleSiegeMarkRegistrationResult.Failed, string.Empty, 0); + } + + var (guild, _) = await CastleSiegeGuildResolver.ResolveAuthorizedGuildAsync(player).ConfigureAwait(false); + if (guild is null + || !context.RegisteredGuilds.TryGetValue(guild.PersistentId, out var registration)) + { + return (CastleSiegeMarkRegistrationResult.GuildNotRegistered, guild?.Name ?? string.Empty, 0); + } + + var signOfLord = player.Inventory?.GetItem(inventorySlot); + if (signOfLord is null + || signOfLord.Definition != context.Configuration.SignOfLordItemDefinition + || signOfLord.Level != context.Configuration.SignOfLordItemLevel) + { + return (CastleSiegeMarkRegistrationResult.IncorrectItem, guild.Name, registration.Marks); + } + + // Persist first so a failed registration never consumes the player's item. If item removal fails afterward, + // the durable mark is preferred over losing an item without receiving credit. + if (await context.IncrementMarksAsync(registration).ConfigureAwait(false) is not { } marks) + { + return (CastleSiegeMarkRegistrationResult.GuildNotRegistered, guild.Name, registration.Marks); + } + + await player.DestroyInventoryItemAsync(signOfLord).ConfigureAwait(false); + return (CastleSiegeMarkRegistrationResult.Success, guild.Name, marks); + } + finally + { + context.ExecutionLock.Release(); + } + } +} diff --git a/src/GameLogic/CastleSiege/Actions/CastleSiegeRegistrationStateAction.cs b/src/GameLogic/CastleSiege/Actions/CastleSiegeRegistrationStateAction.cs new file mode 100644 index 0000000000..ea08da07a3 --- /dev/null +++ b/src/GameLogic/CastleSiege/Actions/CastleSiegeRegistrationStateAction.cs @@ -0,0 +1,63 @@ +// +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace MUnique.OpenMU.GameLogic.CastleSiege.Actions; + +using MUnique.OpenMU.GameLogic.Views.CastleSiege; + +/// +/// Returns the Castle Siege registration state visible to a player. +/// +public class CastleSiegeRegistrationStateAction +{ + /// + /// Sends the player's guild or alliance registration state. + /// + /// The requesting player. + /// The Castle Siege context, if initialized. + /// A task which represents the asynchronous operation. + public async ValueTask ShowStateAsync(Player player, CastleSiegeContext? context) + { + var (result, guildName, marks, registrationRank) = await GetStateAsync(player, context).ConfigureAwait(false); + await player.InvokeViewPlugInAsync( + view => view.ShowRegistrationStateAsync(result, guildName, marks, false, registrationRank)).ConfigureAwait(false); + } + + private static async ValueTask<( + CastleSiegeRegistrationStateResult Result, + string GuildName, + int Marks, + byte RegistrationRank)> GetStateAsync( + Player player, + CastleSiegeContext? context) + { + if (context is not { Configuration.Enabled: true }) + { + return (CastleSiegeRegistrationStateResult.Unavailable, string.Empty, 0, 0); + } + + await context.ExecutionLock.WaitAsync().ConfigureAwait(false); + try + { + var guildId = await CastleSiegeGuildResolver.ResolveRegistrationGuildIdAsync(player).ConfigureAwait(false); + if (guildId is null + || !context.RegisteredGuilds.TryGetValue(guildId.Value, out var registration)) + { + return (CastleSiegeRegistrationStateResult.NotRegistered, string.Empty, 0, (byte)0); + } + + // MuMain carries the rank in one byte, so later registrations use the highest representable rank. + var registrationRank = (byte)Math.Min(registration.RegistrationOrder, byte.MaxValue); + return ( + CastleSiegeRegistrationStateResult.Registered, + registration.GuildName, + registration.Marks, + registrationRank); + } + finally + { + context.ExecutionLock.Release(); + } + } +} diff --git a/src/GameLogic/CastleSiege/Actions/CastleSiegeUnregisterGuildAction.cs b/src/GameLogic/CastleSiege/Actions/CastleSiegeUnregisterGuildAction.cs new file mode 100644 index 0000000000..196bfa401b --- /dev/null +++ b/src/GameLogic/CastleSiege/Actions/CastleSiegeUnregisterGuildAction.cs @@ -0,0 +1,69 @@ +// +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace MUnique.OpenMU.GameLogic.CastleSiege.Actions; + +using MUnique.OpenMU.DataModel.Configuration; +using MUnique.OpenMU.GameLogic.Views.CastleSiege; + +/// +/// Validates and processes Castle Siege guild unregistrations. +/// +public class CastleSiegeUnregisterGuildAction +{ + /// + /// Tries to unregister the player's guild or alliance. + /// + /// The requesting player. + /// The Castle Siege context, if initialized. + /// Whether the guild requests to give up its registration. + /// A task which represents the asynchronous operation. + public async ValueTask UnregisterAsync( + Player player, + CastleSiegeContext? context, + bool isGivingUp) + { + var (result, guildName) = await UnregisterCoreAsync(player, context).ConfigureAwait(false); + await player.InvokeViewPlugInAsync( + view => view.ShowUnregistrationResultAsync(result, isGivingUp, guildName)).ConfigureAwait(false); + } + + private static async ValueTask<(CastleSiegeUnregistrationResult Result, string GuildName)> UnregisterCoreAsync( + Player player, + CastleSiegeContext? context) + { + if (context is not { Configuration.Enabled: true }) + { + return (CastleSiegeUnregistrationResult.Failed, string.Empty); + } + + await context.ExecutionLock.WaitAsync().ConfigureAwait(false); + try + { + if (context.CurrentState != CastleSiegeState.RegisterGuild) + { + return (CastleSiegeUnregistrationResult.WrongState, string.Empty); + } + + var (guild, _) = await CastleSiegeGuildResolver.ResolveAuthorizedGuildAsync(player).ConfigureAwait(false); + if (guild is null) + { + // The client protocol has no separate NoGuild or InvalidGuild result for unregistration. + return (CastleSiegeUnregistrationResult.Failed, string.Empty); + } + + if (!context.RegisteredGuilds.TryGetValue(guild.PersistentId, out var registration)) + { + return (CastleSiegeUnregistrationResult.NotRegistered, guild.Name); + } + + await context.RemoveRegistrationAsync(registration).ConfigureAwait(false); + return (CastleSiegeUnregistrationResult.Success, guild.Name); + } + finally + { + context.ExecutionLock.Release(); + } + } +} diff --git a/src/GameLogic/CastleSiege/CastleSiegeContext.cs b/src/GameLogic/CastleSiege/CastleSiegeContext.cs index 4f1eeba933..69131d33fc 100644 --- a/src/GameLogic/CastleSiege/CastleSiegeContext.cs +++ b/src/GameLogic/CastleSiege/CastleSiegeContext.cs @@ -11,6 +11,10 @@ namespace MUnique.OpenMU.GameLogic.CastleSiege; /// /// Holds the runtime state of Castle Siege for one game context. /// +/// +/// Runtime registrations and are scoped to this game context. Deployments which run +/// multiple game-server processes require external coordination when registrations are changed concurrently. +/// public class CastleSiegeContext : IEventStateProvider { private readonly IGameContext _gameContext; @@ -254,6 +258,73 @@ public async ValueTask ClearRegistrationsAsync() this.RegisteredGuilds.Clear(); } + /// + /// Creates and persists a guild registration. + /// + /// The persistent guild identifier. + /// The guild name. + /// The created registration. + internal async ValueTask AddRegistrationAsync(Guid guildId, string guildName) + { + using var context = this._gameContext.PersistenceContextProvider.CreateNewTypedContext( + typeof(CastleSiegeGuildRegistration), + false, + this._gameContext.Configuration); + var registration = context.CreateNew(); + registration.GuildId = guildId; + registration.GuildName = guildName; + + // Registration orders stay monotonic for one cycle and are intentionally not compacted after unregistration. + registration.RegistrationOrder = this.RegisteredGuilds.IsEmpty + ? 1 + : this.RegisteredGuilds.Values.Max(entry => entry.RegistrationOrder) + 1; + await context.SaveChangesAsync().ConfigureAwait(false); + this.RegisteredGuilds[guildId] = registration; + return registration; + } + + /// + /// Deletes a persisted guild registration. + /// + /// The registration. + internal async ValueTask RemoveRegistrationAsync(CastleSiegeGuildRegistration registration) + { + using var context = this._gameContext.PersistenceContextProvider.CreateNewTypedContext( + typeof(CastleSiegeGuildRegistration), + false, + this._gameContext.Configuration); + if (await context.GetByIdAsync(registration.Id).ConfigureAwait(false) is { } persistentRegistration) + { + await context.DeleteAsync(persistentRegistration).ConfigureAwait(false); + await context.SaveChangesAsync().ConfigureAwait(false); + } + + this.RegisteredGuilds.TryRemove(registration.GuildId, out _); + } + + /// + /// Increments and persists the submitted mark count. + /// + /// The registration. + /// The updated mark count, or if the registration no longer exists. + internal async ValueTask IncrementMarksAsync(CastleSiegeGuildRegistration registration) + { + using var context = this._gameContext.PersistenceContextProvider.CreateNewTypedContext( + typeof(CastleSiegeGuildRegistration), + false, + this._gameContext.Configuration); + if (await context.GetByIdAsync(registration.Id).ConfigureAwait(false) is not { } persistentRegistration) + { + this.RegisteredGuilds.TryRemove(registration.GuildId, out _); + return null; + } + + persistentRegistration.Marks++; + await context.SaveChangesAsync().ConfigureAwait(false); + registration.Marks = persistentRegistration.Marks; + return registration.Marks; + } + /// /// Initializes the context at the state which contains . /// diff --git a/src/GameLogic/Views/CastleSiege/CastleSiegeMarkRegistrationResult.cs b/src/GameLogic/Views/CastleSiege/CastleSiegeMarkRegistrationResult.cs new file mode 100644 index 0000000000..4b58f03f40 --- /dev/null +++ b/src/GameLogic/Views/CastleSiege/CastleSiegeMarkRegistrationResult.cs @@ -0,0 +1,31 @@ +// +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace MUnique.OpenMU.GameLogic.Views.CastleSiege; + +/// +/// The result of a Sign of Lord registration request. +/// +public enum CastleSiegeMarkRegistrationResult : byte +{ + /// + /// The registration failed for an unspecified reason. + /// + Failed = 0, + + /// + /// The Sign of Lord was registered successfully. + /// + Success = 1, + + /// + /// The guild does not participate in the Castle Siege. + /// + GuildNotRegistered = 2, + + /// + /// The selected inventory item is not a Sign of Lord. + /// + IncorrectItem = 3, +} diff --git a/src/GameLogic/Views/CastleSiege/CastleSiegeRegistrationResult.cs b/src/GameLogic/Views/CastleSiege/CastleSiegeRegistrationResult.cs new file mode 100644 index 0000000000..39f3f5d1b1 --- /dev/null +++ b/src/GameLogic/Views/CastleSiege/CastleSiegeRegistrationResult.cs @@ -0,0 +1,56 @@ +// +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace MUnique.OpenMU.GameLogic.Views.CastleSiege; + +/// +/// The result of a Castle Siege guild registration request. +/// +public enum CastleSiegeRegistrationResult : byte +{ + /// + /// The request failed for an unspecified reason. + /// + Failed = 0, + + /// + /// The guild was registered successfully. + /// + Success = 1, + + /// + /// The guild is already registered. + /// + AlreadyRegistered = 2, + + /// + /// The guild is the current castle defender. + /// + IsDefender = 3, + + /// + /// The guild or the requesting guild member is invalid. + /// + InvalidGuild = 4, + + /// + /// The player's combined level is insufficient. + /// + LevelInsufficient = 5, + + /// + /// The player is not a guild member. + /// + NoGuild = 6, + + /// + /// Guild registration is currently closed. + /// + NotRegistrationPeriod = 7, + + /// + /// The guild has too few members. + /// + NotEnoughMembers = 8, +} diff --git a/src/GameLogic/Views/CastleSiege/CastleSiegeRegistrationStateResult.cs b/src/GameLogic/Views/CastleSiege/CastleSiegeRegistrationStateResult.cs new file mode 100644 index 0000000000..a50e7e8809 --- /dev/null +++ b/src/GameLogic/Views/CastleSiege/CastleSiegeRegistrationStateResult.cs @@ -0,0 +1,26 @@ +// +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace MUnique.OpenMU.GameLogic.Views.CastleSiege; + +/// +/// The result of a Castle Siege registration-state request. +/// +public enum CastleSiegeRegistrationStateResult : byte +{ + /// + /// The guild is not registered. + /// + NotRegistered, + + /// + /// The guild is registered. + /// + Registered, + + /// + /// The registration state is unavailable. + /// + Unavailable, +} diff --git a/src/GameLogic/Views/CastleSiege/CastleSiegeUnregistrationResult.cs b/src/GameLogic/Views/CastleSiege/CastleSiegeUnregistrationResult.cs new file mode 100644 index 0000000000..56e9a2e22b --- /dev/null +++ b/src/GameLogic/Views/CastleSiege/CastleSiegeUnregistrationResult.cs @@ -0,0 +1,31 @@ +// +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace MUnique.OpenMU.GameLogic.Views.CastleSiege; + +/// +/// The result of a Castle Siege guild-unregistration request. +/// +public enum CastleSiegeUnregistrationResult : byte +{ + /// + /// The request failed. + /// + Failed, + + /// + /// The guild was unregistered. + /// + Success, + + /// + /// The guild was not registered. + /// + NotRegistered, + + /// + /// Guilds cannot unregister during the current state. + /// + WrongState, +} diff --git a/src/GameLogic/Views/CastleSiege/ICastleSiegeMarkRegistrationResultPlugIn.cs b/src/GameLogic/Views/CastleSiege/ICastleSiegeMarkRegistrationResultPlugIn.cs new file mode 100644 index 0000000000..d0580b4af6 --- /dev/null +++ b/src/GameLogic/Views/CastleSiege/ICastleSiegeMarkRegistrationResultPlugIn.cs @@ -0,0 +1,20 @@ +// +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace MUnique.OpenMU.GameLogic.Views.CastleSiege; + +/// +/// A view which reports Sign of Lord submission results. +/// +public interface ICastleSiegeMarkRegistrationResultPlugIn : IViewPlugIn +{ + /// + /// Shows the mark registration result. + /// + /// The registration result. + /// The registered guild name. + /// The updated mark count. + /// A task which represents the asynchronous operation. + ValueTask ShowMarkRegistrationResultAsync(CastleSiegeMarkRegistrationResult result, string guildName, int marks); +} diff --git a/src/GameLogic/Views/CastleSiege/ICastleSiegeRegistrationResultPlugIn.cs b/src/GameLogic/Views/CastleSiege/ICastleSiegeRegistrationResultPlugIn.cs new file mode 100644 index 0000000000..44c3d6bf4e --- /dev/null +++ b/src/GameLogic/Views/CastleSiege/ICastleSiegeRegistrationResultPlugIn.cs @@ -0,0 +1,31 @@ +// +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace MUnique.OpenMU.GameLogic.Views.CastleSiege; + +/// +/// A view which reports Castle Siege guild registration results. +/// +public interface ICastleSiegeRegistrationResultPlugIn : IViewPlugIn +{ + /// + /// Shows a guild registration result. + /// + /// The result. + /// The guild name. + /// A task which represents the asynchronous operation. + ValueTask ShowRegistrationResultAsync(CastleSiegeRegistrationResult result, string guildName); + + /// + /// Shows a guild unregistration result. + /// + /// The result. + /// Whether the guild requested to give up its registration. + /// The guild name. + /// A task which represents the asynchronous operation. + ValueTask ShowUnregistrationResultAsync( + CastleSiegeUnregistrationResult result, + bool isGivingUp, + string guildName); +} diff --git a/src/GameLogic/Views/CastleSiege/ICastleSiegeRegistrationStatePlugIn.cs b/src/GameLogic/Views/CastleSiege/ICastleSiegeRegistrationStatePlugIn.cs new file mode 100644 index 0000000000..f74724221d --- /dev/null +++ b/src/GameLogic/Views/CastleSiege/ICastleSiegeRegistrationStatePlugIn.cs @@ -0,0 +1,27 @@ +// +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace MUnique.OpenMU.GameLogic.Views.CastleSiege; + +/// +/// A view which reports the registration state of a guild. +/// +public interface ICastleSiegeRegistrationStatePlugIn : IViewPlugIn +{ + /// + /// Shows the registration state. + /// + /// The query result. + /// The registered guild name, or an empty string. + /// The number of submitted Signs of Lord. + /// Whether the guild gave up its registration. + /// The registration rank, or zero when not registered. + /// A task which represents the asynchronous operation. + ValueTask ShowRegistrationStateAsync( + CastleSiegeRegistrationStateResult result, + string guildName, + int marks, + bool isGivingUp, + byte registrationRank); +} diff --git a/src/GameServer/MessageHandler/CastleSiege/CastleSiegeGroupHandlerPlugIn.cs b/src/GameServer/MessageHandler/CastleSiege/CastleSiegeGroupHandlerPlugIn.cs new file mode 100644 index 0000000000..d808bf432e --- /dev/null +++ b/src/GameServer/MessageHandler/CastleSiege/CastleSiegeGroupHandlerPlugIn.cs @@ -0,0 +1,40 @@ +// +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace MUnique.OpenMU.GameServer.MessageHandler.CastleSiege; + +using System.Runtime.InteropServices; +using Microsoft.Extensions.Logging; +using MUnique.OpenMU.PlugIns; + +/// +/// Packet handler for Castle Siege packets with the 0xB2 identifier. +/// +[PlugIn] +[Display(Name = nameof(PlugInResources.CastleSiegeGroupHandlerPlugIn_Name), Description = nameof(PlugInResources.CastleSiegeGroupHandlerPlugIn_Description), ResourceType = typeof(PlugInResources))] +[Guid("2438FBA6-4962-4BC7-8784-69F89AB53D8F")] +internal class CastleSiegeGroupHandlerPlugIn : GroupPacketHandlerPlugIn +{ + /// + /// The group key. + /// + internal const byte GroupKey = 0xB2; + + /// + /// Initializes a new instance of the class. + /// + /// The client version provider. + /// The plugin manager. + /// The logger factory. + public CastleSiegeGroupHandlerPlugIn(IClientVersionProvider clientVersionProvider, PlugInManager manager, ILoggerFactory loggerFactory) + : base(clientVersionProvider, manager, loggerFactory) + { + } + + /// + public override bool IsEncryptionExpected => false; + + /// + public override byte Key => GroupKey; +} diff --git a/src/GameServer/MessageHandler/CastleSiege/CastleSiegeHandlerContext.cs b/src/GameServer/MessageHandler/CastleSiege/CastleSiegeHandlerContext.cs new file mode 100644 index 0000000000..89dee2e9b8 --- /dev/null +++ b/src/GameServer/MessageHandler/CastleSiege/CastleSiegeHandlerContext.cs @@ -0,0 +1,29 @@ +// +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace MUnique.OpenMU.GameServer.MessageHandler.CastleSiege; + +using MUnique.OpenMU.GameLogic; +using MUnique.OpenMU.GameLogic.CastleSiege; +using MUnique.OpenMU.GameLogic.PlugIns; + +/// +/// Resolves the active Castle Siege context for packet handlers. +/// +internal static class CastleSiegeHandlerContext +{ + /// + /// Gets the initialized context for a player's game context. + /// + /// The player. + /// The Castle Siege context, or . + public static CastleSiegeContext? Get(Player player) + { + return player.GameContext.PlugInManager + .GetActivePlugInsOf() + .OfType() + .FirstOrDefault() + ?.GetContext(player.GameContext); + } +} diff --git a/src/GameServer/MessageHandler/CastleSiege/CastleSiegeMarkRegistrationHandlerPlugIn.cs b/src/GameServer/MessageHandler/CastleSiege/CastleSiegeMarkRegistrationHandlerPlugIn.cs new file mode 100644 index 0000000000..9de8c85b30 --- /dev/null +++ b/src/GameServer/MessageHandler/CastleSiege/CastleSiegeMarkRegistrationHandlerPlugIn.cs @@ -0,0 +1,66 @@ +// +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace MUnique.OpenMU.GameServer.MessageHandler.CastleSiege; + +using System.Runtime.InteropServices; +using MUnique.OpenMU.DataModel; +using MUnique.OpenMU.GameLogic; +using MUnique.OpenMU.GameLogic.CastleSiege.Actions; +using MUnique.OpenMU.Network.Packets.ClientToServer; +using MUnique.OpenMU.PlugIns; + +/// +/// Handles Sign of Lord registration requests. +/// +[PlugIn] +[Display(Name = nameof(PlugInResources.CastleSiegeMarkRegistrationHandlerPlugIn_Name), Description = nameof(PlugInResources.CastleSiegeMarkRegistrationHandlerPlugIn_Description), ResourceType = typeof(PlugInResources))] +[Guid("676913C3-502D-409D-84FA-6BA283ACF349")] +[BelongsToGroup(CastleSiegeGroupHandlerPlugIn.GroupKey)] +internal class CastleSiegeMarkRegistrationHandlerPlugIn : ISubPacketHandlerPlugIn +{ + private readonly CastleSiegeRegisterMarkAction _action = new(); + + /// + public bool IsEncryptionExpected => false; + + /// + public byte Key => CastleSiegeMarkRegistration.SubCode; + + /// + public ValueTask HandlePacketAsync(Player player, Memory packet) + { + if (packet.Length < CastleSiegeMarkRegistration.Length) + { + return ValueTask.CompletedTask; + } + + CastleSiegeMarkRegistration request = packet; + if (!TryGetInventorySlot(request.ItemIndex, out var inventorySlot)) + { + return ValueTask.CompletedTask; + } + + return this._action.RegisterMarkAsync(player, CastleSiegeHandlerContext.Get(player), inventorySlot); + } + + /// + /// Translates the client-side backpack-grid index to OpenMU's inventory slot, which includes equipment slots. + /// + /// The zero-based backpack-grid index sent by the client. + /// The translated OpenMU inventory slot. + /// if the translated slot fits into the packet slot range; otherwise, . + internal static bool TryGetInventorySlot(byte clientItemIndex, out byte inventorySlot) + { + var translatedSlot = clientItemIndex + InventoryConstants.EquippableSlotsCount; + if (translatedSlot > byte.MaxValue) + { + inventorySlot = default; + return false; + } + + inventorySlot = (byte)translatedSlot; + return true; + } +} diff --git a/src/GameServer/MessageHandler/CastleSiege/CastleSiegeRegistrationHandlerPlugIn.cs b/src/GameServer/MessageHandler/CastleSiege/CastleSiegeRegistrationHandlerPlugIn.cs new file mode 100644 index 0000000000..eca43f7c81 --- /dev/null +++ b/src/GameServer/MessageHandler/CastleSiege/CastleSiegeRegistrationHandlerPlugIn.cs @@ -0,0 +1,35 @@ +// +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace MUnique.OpenMU.GameServer.MessageHandler.CastleSiege; + +using System.Runtime.InteropServices; +using MUnique.OpenMU.GameLogic; +using MUnique.OpenMU.GameLogic.CastleSiege.Actions; +using MUnique.OpenMU.Network.Packets.ClientToServer; +using MUnique.OpenMU.PlugIns; + +/// +/// Handles Castle Siege guild registration requests. +/// +[PlugIn] +[Display(Name = nameof(PlugInResources.CastleSiegeRegistrationHandlerPlugIn_Name), Description = nameof(PlugInResources.CastleSiegeRegistrationHandlerPlugIn_Description), ResourceType = typeof(PlugInResources))] +[Guid("7315C1FD-EE79-490E-B9ED-E0ECC6E87F37")] +[BelongsToGroup(CastleSiegeGroupHandlerPlugIn.GroupKey)] +internal class CastleSiegeRegistrationHandlerPlugIn : ISubPacketHandlerPlugIn +{ + private readonly CastleSiegeRegisterGuildAction _action = new(); + + /// + public bool IsEncryptionExpected => false; + + /// + public byte Key => CastleSiegeRegistrationRequest.SubCode; + + /// + public ValueTask HandlePacketAsync(Player player, Memory packet) + { + return this._action.RegisterAsync(player, CastleSiegeHandlerContext.Get(player)); + } +} diff --git a/src/GameServer/MessageHandler/CastleSiege/CastleSiegeRegistrationStateHandlerPlugIn.cs b/src/GameServer/MessageHandler/CastleSiege/CastleSiegeRegistrationStateHandlerPlugIn.cs new file mode 100644 index 0000000000..424e2f7916 --- /dev/null +++ b/src/GameServer/MessageHandler/CastleSiege/CastleSiegeRegistrationStateHandlerPlugIn.cs @@ -0,0 +1,35 @@ +// +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace MUnique.OpenMU.GameServer.MessageHandler.CastleSiege; + +using System.Runtime.InteropServices; +using MUnique.OpenMU.GameLogic; +using MUnique.OpenMU.GameLogic.CastleSiege.Actions; +using MUnique.OpenMU.Network.Packets.ClientToServer; +using MUnique.OpenMU.PlugIns; + +/// +/// Handles Castle Siege registration-state requests. +/// +[PlugIn] +[Display(Name = nameof(PlugInResources.CastleSiegeRegistrationStateHandlerPlugIn_Name), Description = nameof(PlugInResources.CastleSiegeRegistrationStateHandlerPlugIn_Description), ResourceType = typeof(PlugInResources))] +[Guid("94E8D09A-7DBC-41B0-8A85-CA31F032B0FF")] +[BelongsToGroup(CastleSiegeGroupHandlerPlugIn.GroupKey)] +internal class CastleSiegeRegistrationStateHandlerPlugIn : ISubPacketHandlerPlugIn +{ + private readonly CastleSiegeRegistrationStateAction _action = new(); + + /// + public bool IsEncryptionExpected => false; + + /// + public byte Key => CastleSiegeRegistrationStateRequest.SubCode; + + /// + public ValueTask HandlePacketAsync(Player player, Memory packet) + { + return this._action.ShowStateAsync(player, CastleSiegeHandlerContext.Get(player)); + } +} diff --git a/src/GameServer/MessageHandler/CastleSiege/CastleSiegeUnregisterHandlerPlugIn.cs b/src/GameServer/MessageHandler/CastleSiege/CastleSiegeUnregisterHandlerPlugIn.cs new file mode 100644 index 0000000000..e7ee4f3c7c --- /dev/null +++ b/src/GameServer/MessageHandler/CastleSiege/CastleSiegeUnregisterHandlerPlugIn.cs @@ -0,0 +1,39 @@ +// +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace MUnique.OpenMU.GameServer.MessageHandler.CastleSiege; + +using System.Runtime.InteropServices; +using MUnique.OpenMU.GameLogic; +using MUnique.OpenMU.GameLogic.CastleSiege.Actions; +using MUnique.OpenMU.Network.Packets.ClientToServer; +using MUnique.OpenMU.PlugIns; + +/// +/// Handles Castle Siege guild unregistration requests. +/// +[PlugIn] +[Display(Name = nameof(PlugInResources.CastleSiegeUnregisterHandlerPlugIn_Name), Description = nameof(PlugInResources.CastleSiegeUnregisterHandlerPlugIn_Description), ResourceType = typeof(PlugInResources))] +[Guid("F8DFF1B0-1DDA-4988-AAC4-65004A7FF087")] +[BelongsToGroup(CastleSiegeGroupHandlerPlugIn.GroupKey)] +internal class CastleSiegeUnregisterHandlerPlugIn : ISubPacketHandlerPlugIn +{ + private readonly CastleSiegeUnregisterGuildAction _action = new(); + + /// + public bool IsEncryptionExpected => false; + + /// + public byte Key => CastleSiegeUnregisterRequest.SubCode; + + /// + public ValueTask HandlePacketAsync(Player player, Memory packet) + { + var request = new CastleSiegeUnregisterRequest(packet); + return this._action.UnregisterAsync( + player, + CastleSiegeHandlerContext.Get(player), + request.IsGivingUp); + } +} diff --git a/src/GameServer/Properties/PlugInResources.Designer.cs b/src/GameServer/Properties/PlugInResources.Designer.cs index 5f87604332..134bc7aeb1 100644 --- a/src/GameServer/Properties/PlugInResources.Designer.cs +++ b/src/GameServer/Properties/PlugInResources.Designer.cs @@ -617,6 +617,150 @@ public static string CancelGuildCreationHandlerPlugIn_Name { return ResourceManager.GetString("CancelGuildCreationHandlerPlugIn_Name", resourceCulture); } } + + /// + /// Looks up a localized string similar to Routes Castle Siege packet subcodes.. + /// + public static string CastleSiegeGroupHandlerPlugIn_Description { + get { + return ResourceManager.GetString("CastleSiegeGroupHandlerPlugIn_Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Castle Siege Packet Group Handler. + /// + public static string CastleSiegeGroupHandlerPlugIn_Name { + get { + return ResourceManager.GetString("CastleSiegeGroupHandlerPlugIn_Name", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Handles Castle Siege Sign of Lord registration requests.. + /// + public static string CastleSiegeMarkRegistrationHandlerPlugIn_Description { + get { + return ResourceManager.GetString("CastleSiegeMarkRegistrationHandlerPlugIn_Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Castle Siege Mark Registration Handler. + /// + public static string CastleSiegeMarkRegistrationHandlerPlugIn_Name { + get { + return ResourceManager.GetString("CastleSiegeMarkRegistrationHandlerPlugIn_Name", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Sends Sign of Lord registration results to the game client.. + /// + public static string CastleSiegeMarkRegistrationResultPlugIn_Description { + get { + return ResourceManager.GetString("CastleSiegeMarkRegistrationResultPlugIn_Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Castle Siege Mark Registration View. + /// + public static string CastleSiegeMarkRegistrationResultPlugIn_Name { + get { + return ResourceManager.GetString("CastleSiegeMarkRegistrationResultPlugIn_Name", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Handles Castle Siege guild registration requests.. + /// + public static string CastleSiegeRegistrationHandlerPlugIn_Description { + get { + return ResourceManager.GetString("CastleSiegeRegistrationHandlerPlugIn_Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Castle Siege Registration Handler. + /// + public static string CastleSiegeRegistrationHandlerPlugIn_Name { + get { + return ResourceManager.GetString("CastleSiegeRegistrationHandlerPlugIn_Name", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Sends Castle Siege registration results to the game client.. + /// + public static string CastleSiegeRegistrationResultPlugIn_Description { + get { + return ResourceManager.GetString("CastleSiegeRegistrationResultPlugIn_Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Castle Siege Registration Result View. + /// + public static string CastleSiegeRegistrationResultPlugIn_Name { + get { + return ResourceManager.GetString("CastleSiegeRegistrationResultPlugIn_Name", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Handles Castle Siege registration-state requests.. + /// + public static string CastleSiegeRegistrationStateHandlerPlugIn_Description { + get { + return ResourceManager.GetString("CastleSiegeRegistrationStateHandlerPlugIn_Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Castle Siege Registration State Handler. + /// + public static string CastleSiegeRegistrationStateHandlerPlugIn_Name { + get { + return ResourceManager.GetString("CastleSiegeRegistrationStateHandlerPlugIn_Name", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Sends Castle Siege registration state to the game client.. + /// + public static string CastleSiegeRegistrationStatePlugIn_Description { + get { + return ResourceManager.GetString("CastleSiegeRegistrationStatePlugIn_Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Castle Siege Registration State View. + /// + public static string CastleSiegeRegistrationStatePlugIn_Name { + get { + return ResourceManager.GetString("CastleSiegeRegistrationStatePlugIn_Name", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Handles Castle Siege guild unregistration requests.. + /// + public static string CastleSiegeUnregisterHandlerPlugIn_Description { + get { + return ResourceManager.GetString("CastleSiegeUnregisterHandlerPlugIn_Description", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Castle Siege Unregister Handler. + /// + public static string CastleSiegeUnregisterHandlerPlugIn_Name { + get { + return ResourceManager.GetString("CastleSiegeUnregisterHandlerPlugIn_Name", resourceCulture); + } + } /// /// Looks up a localized string similar to Handler for online state change packets.. diff --git a/src/GameServer/Properties/PlugInResources.resx b/src/GameServer/Properties/PlugInResources.resx index e420009985..8968bbd513 100644 --- a/src/GameServer/Properties/PlugInResources.resx +++ b/src/GameServer/Properties/PlugInResources.resx @@ -1947,6 +1947,54 @@ Packet handler for character packets (0xF3 identifier). + + Castle Siege Packet Group Handler + + + Routes Castle Siege packet subcodes. + + + Castle Siege Registration Handler + + + Handles Castle Siege guild registration requests. + + + Castle Siege Unregister Handler + + + Handles Castle Siege guild unregistration requests. + + + Castle Siege Registration State Handler + + + Handles Castle Siege registration-state requests. + + + Castle Siege Mark Registration Handler + + + Handles Castle Siege Sign of Lord registration requests. + + + Castle Siege Mark Registration View + + + Sends Sign of Lord registration results to the game client. + + + Castle Siege Registration Result View + + + Sends Castle Siege registration results to the game client. + + + Castle Siege Registration State View + + + Sends Castle Siege registration state to the game client. + Character - Key Configuration diff --git a/src/GameServer/RemoteView/CastleSiege/CastleSiegeMarkRegistrationResultPlugIn.cs b/src/GameServer/RemoteView/CastleSiege/CastleSiegeMarkRegistrationResultPlugIn.cs new file mode 100644 index 0000000000..839d79ec44 --- /dev/null +++ b/src/GameServer/RemoteView/CastleSiege/CastleSiegeMarkRegistrationResultPlugIn.cs @@ -0,0 +1,35 @@ +// +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace MUnique.OpenMU.GameServer.RemoteView.CastleSiege; + +using System.Runtime.InteropServices; +using MUnique.OpenMU.GameLogic.Views.CastleSiege; +using MUnique.OpenMU.Network.Packets.ServerToClient; +using MUnique.OpenMU.PlugIns; + +/// +/// The default implementation of the +/// which forwards mark registration results to the game client. +/// +[PlugIn] +[Display(Name = nameof(PlugInResources.CastleSiegeMarkRegistrationResultPlugIn_Name), Description = nameof(PlugInResources.CastleSiegeMarkRegistrationResultPlugIn_Description), ResourceType = typeof(PlugInResources))] +[Guid("12ED701D-B865-4D2B-B044-445E662BFE0E")] +public class CastleSiegeMarkRegistrationResultPlugIn : ICastleSiegeMarkRegistrationResultPlugIn +{ + private readonly RemotePlayer _player; + + /// + /// Initializes a new instance of the class. + /// + /// The player. + public CastleSiegeMarkRegistrationResultPlugIn(RemotePlayer player) => this._player = player; + + /// + public ValueTask ShowMarkRegistrationResultAsync(CastleSiegeMarkRegistrationResult result, string guildName, int marks) + => this._player.Connection.SendCastleSiegeMarkRegistrationResponseAsync( + (byte)result, + guildName, + checked((uint)marks)); +} diff --git a/src/GameServer/RemoteView/CastleSiege/CastleSiegeRegistrationResultPlugIn.cs b/src/GameServer/RemoteView/CastleSiege/CastleSiegeRegistrationResultPlugIn.cs new file mode 100644 index 0000000000..f47802c46c --- /dev/null +++ b/src/GameServer/RemoteView/CastleSiege/CastleSiegeRegistrationResultPlugIn.cs @@ -0,0 +1,42 @@ +// +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace MUnique.OpenMU.GameServer.RemoteView.CastleSiege; + +using System.Runtime.InteropServices; +using MUnique.OpenMU.GameLogic.Views.CastleSiege; +using MUnique.OpenMU.Network.Packets.ServerToClient; +using MUnique.OpenMU.PlugIns; + +/// +/// The default implementation of the +/// which forwards registration results to the game client. +/// +[PlugIn] +[Display(Name = nameof(PlugInResources.CastleSiegeRegistrationResultPlugIn_Name), Description = nameof(PlugInResources.CastleSiegeRegistrationResultPlugIn_Description), ResourceType = typeof(PlugInResources))] +[Guid("1FF48BAE-9F33-4316-B4C0-D6082653C383")] +public class CastleSiegeRegistrationResultPlugIn : ICastleSiegeRegistrationResultPlugIn +{ + private readonly RemotePlayer _player; + + /// + /// Initializes a new instance of the class. + /// + /// The player. + public CastleSiegeRegistrationResultPlugIn(RemotePlayer player) => this._player = player; + + /// + public ValueTask ShowRegistrationResultAsync(CastleSiegeRegistrationResult result, string guildName) + => this._player.Connection.SendCastleSiegeRegistrationResponseAsync((byte)result, guildName); + + /// + public ValueTask ShowUnregistrationResultAsync( + CastleSiegeUnregistrationResult result, + bool isGivingUp, + string guildName) + => this._player.Connection.SendCastleSiegeUnregisterResponseAsync( + (byte)result, + isGivingUp, + guildName); +} diff --git a/src/GameServer/RemoteView/CastleSiege/CastleSiegeRegistrationStatePlugIn.cs b/src/GameServer/RemoteView/CastleSiege/CastleSiegeRegistrationStatePlugIn.cs new file mode 100644 index 0000000000..a213b75ea3 --- /dev/null +++ b/src/GameServer/RemoteView/CastleSiege/CastleSiegeRegistrationStatePlugIn.cs @@ -0,0 +1,42 @@ +// +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace MUnique.OpenMU.GameServer.RemoteView.CastleSiege; + +using System.Runtime.InteropServices; +using MUnique.OpenMU.GameLogic.Views.CastleSiege; +using MUnique.OpenMU.Network.Packets.ServerToClient; +using MUnique.OpenMU.PlugIns; + +/// +/// The default implementation of the +/// which forwards registration state to the game client. +/// +[PlugIn] +[Display(Name = nameof(PlugInResources.CastleSiegeRegistrationStatePlugIn_Name), Description = nameof(PlugInResources.CastleSiegeRegistrationStatePlugIn_Description), ResourceType = typeof(PlugInResources))] +[Guid("1CE1AF15-92BB-4022-BC71-189C125F4531")] +public class CastleSiegeRegistrationStatePlugIn : ICastleSiegeRegistrationStatePlugIn +{ + private readonly RemotePlayer _player; + + /// + /// Initializes a new instance of the class. + /// + /// The player. + public CastleSiegeRegistrationStatePlugIn(RemotePlayer player) => this._player = player; + + /// + public ValueTask ShowRegistrationStateAsync( + CastleSiegeRegistrationStateResult result, + string guildName, + int marks, + bool isGivingUp, + byte registrationRank) + => this._player.Connection.SendCastleSiegeRegistrationStateResponseAsync( + (byte)result, + guildName, + checked((uint)marks), + isGivingUp, + registrationRank); +} diff --git a/src/GuildServer/GuildServer.cs b/src/GuildServer/GuildServer.cs index 40ec99c753..a98b4bff97 100644 --- a/src/GuildServer/GuildServer.cs +++ b/src/GuildServer/GuildServer.cs @@ -70,6 +70,29 @@ public async ValueTask GuildExistsAsync(string guildName) return null; } + /// + public ValueTask GetPersistentGuildIdAsync(uint guildId) + { + return ValueTask.FromResult( + this._guildDictionary.TryGetValue(guildId, out var guild) + ? (Guid?)guild.Guild.Id + : null); + } + + /// + public ValueTask GetPersistentAllianceMasterGuildIdAsync(uint guildId) + { + if (!this._guildDictionary.TryGetValue(guildId, out var guild)) + { + return ValueTask.FromResult(null); + } + + return ValueTask.FromResult( + guild.Guild.AllianceGuild is Guild allianceMaster + ? allianceMaster.Id + : guild.Guild.Id); + } + /// public async ValueTask GetGuildIdByNameAsync(string guildName) { @@ -784,4 +807,4 @@ private IReadOnlyList GetAllianceMemberIds(uint guildId) .Select(kvp => kvp.Key) .ToList(); } -} \ No newline at end of file +} diff --git a/src/Interfaces/IGuildServer.cs b/src/Interfaces/IGuildServer.cs index cf87570fcd..26992f1e13 100644 --- a/src/Interfaces/IGuildServer.cs +++ b/src/Interfaces/IGuildServer.cs @@ -97,6 +97,23 @@ public interface IGuildServer /// The guild. ValueTask GetGuildAsync(uint guildId); + /// + /// Gets the persistent identifier of a guild by its runtime identifier. + /// + /// The runtime guild identifier. + /// The persistent guild identifier, or if the guild was not found. + ValueTask GetPersistentGuildIdAsync(uint guildId); + + /// + /// Gets the persistent identifier under which a guild participates in an alliance event. + /// + /// The runtime guild identifier. + /// + /// The persistent identifier of the alliance master guild, the guild's own persistent identifier when it has no alliance, + /// or if the guild was not found. + /// + ValueTask GetPersistentAllianceMasterGuildIdAsync(uint guildId); + /// /// Gets the guild id by the guild name. /// @@ -243,4 +260,4 @@ public class GuildListEntry /// Gets or sets the players position in the guild. /// public GuildPosition PlayerPosition { get; set; } -} \ No newline at end of file +} diff --git a/src/Persistence/BasicModel/CastleSiegeConfiguration.Generated.cs b/src/Persistence/BasicModel/CastleSiegeConfiguration.Generated.cs index 54c37fd730..d187b25b72 100644 --- a/src/Persistence/BasicModel/CastleSiegeConfiguration.Generated.cs +++ b/src/Persistence/BasicModel/CastleSiegeConfiguration.Generated.cs @@ -214,6 +214,24 @@ protected set } } + /// + /// Gets the raw object of . + /// + [System.Text.Json.Serialization.JsonPropertyName("signOfLordItemDefinition")] + public ItemDefinition RawSignOfLordItemDefinition + { + get => base.SignOfLordItemDefinition as ItemDefinition; + set => base.SignOfLordItemDefinition = value; + } + + /// + [System.Text.Json.Serialization.JsonIgnore] + public override MUnique.OpenMU.DataModel.Configuration.Items.ItemDefinition SignOfLordItemDefinition + { + get => base.SignOfLordItemDefinition; + set => base.SignOfLordItemDefinition = value; + } + /// /// Gets the raw object of . /// diff --git a/src/Persistence/EntityFramework/Extensions/ModelBuilder/CastleSiegeExtensions.cs b/src/Persistence/EntityFramework/Extensions/ModelBuilder/CastleSiegeExtensions.cs index d01cf7d051..b49c57d021 100644 --- a/src/Persistence/EntityFramework/Extensions/ModelBuilder/CastleSiegeExtensions.cs +++ b/src/Persistence/EntityFramework/Extensions/ModelBuilder/CastleSiegeExtensions.cs @@ -22,6 +22,7 @@ public static void Apply(this EntityTypeBuilder builde builder.Property(configuration => configuration.CrownHoldTimeSeconds).HasDefaultValue(30); builder.Property(configuration => configuration.RegisterMinLevel).HasDefaultValue(200); builder.Property(configuration => configuration.RegisterMinMembers).HasDefaultValue(20); + builder.Property(configuration => configuration.SignOfLordItemLevel).HasDefaultValue((byte)3); builder.Property(configuration => configuration.MaxAttackingGuilds).HasDefaultValue(3); builder.HasOne(configuration => configuration.RawCastleSiegeMapDefinition) @@ -33,6 +34,9 @@ public static void Apply(this EntityTypeBuilder builde builder.HasOne(configuration => configuration.RawRewardItemDefinition) .WithMany() .OnDelete(DeleteBehavior.Restrict); + builder.HasOne(configuration => configuration.RawSignOfLordItemDefinition) + .WithMany() + .OnDelete(DeleteBehavior.Restrict); } /// diff --git a/src/Persistence/EntityFramework/Migrations/20260806161456_ConfigureCastleSiegeRegistration.Designer.cs b/src/Persistence/EntityFramework/Migrations/20260806161456_ConfigureCastleSiegeRegistration.Designer.cs new file mode 100644 index 0000000000..b1975ab842 --- /dev/null +++ b/src/Persistence/EntityFramework/Migrations/20260806161456_ConfigureCastleSiegeRegistration.Designer.cs @@ -0,0 +1,5805 @@ +// +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(EntityDataContext))] + [Migration("20260806161456_ConfigureCastleSiegeRegistration")] + partial class ConfigureCastleSiegeRegistration + { + /// + 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("IsBot") + .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.HasKey("Id"); + + b.HasIndex("LoginName") + .IsUnique(); + + b.HasIndex("VaultId") + .IsUnique(); + + b.ToTable("Account", "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.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("ItemDefinitionId") + .HasColumnType("uuid"); + + 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("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.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("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("RewardItemDefinitionId") + .HasColumnType("uuid"); + + b.Property("SignOfLordItemDefinitionId") + .HasColumnType("uuid"); + + b.Property("SignOfLordItemLevel") + .ValueGeneratedOnAdd() + .HasColumnType("smallint") + .HasDefaultValue((byte)3); + + b.Property("StatueBuyPrice") + .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.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.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("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("Name") + .IsRequired() + .HasColumnType("text"); + + 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("CurrentInstalledVersion") + .HasColumnType("integer"); + + 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("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.DropItemGroup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Chance") + .HasColumnType("double precision"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + 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("AreaSkillHitsPlayer") + .HasColumnType("boolean"); + + 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("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("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("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.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.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.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.HasKey("Id"); + + b.HasIndex("DefinitionId"); + + b.HasIndex("ItemStorageId"); + + 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("GameConfigurationId") + .HasColumnType("uuid"); + + b.Property("Group") + .HasColumnType("smallint"); + + b.Property("Height") + .HasColumnType("smallint"); + + b.Property("IsAmmunition") + .HasColumnType("boolean"); + + b.Property("IsBoundToCharacter") + .HasColumnType("boolean"); + + b.Property("IsQuestItem") + .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("SkillId") + .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("SkillId"); + + 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("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.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.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.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("GameConfigurationId") + .HasColumnType("uuid"); + + b.Property("GameDuration") + .HasColumnType("interval"); + + b.Property("GameLevel") + .HasColumnType("smallint"); + + 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("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("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("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("TargetAttributeId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("BoostId") + .IsUnique(); + + b.HasIndex("GameMapDefinitionId"); + + b.HasIndex("MagicEffectDefinitionId"); + + b.HasIndex("MagicEffectDefinitionId1"); + + 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("ReadConsoleInput") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.ToTable("SystemConfiguration", "config"); + }); + + 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("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.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.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.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.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.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.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.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("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.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.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.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.Navigation("RawDefinition"); + + b.Navigation("RawItemStorage"); + }); + + 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.Skill", "RawSkill") + .WithMany() + .HasForeignKey("SkillId"); + + b.Navigation("RawConsumeEffect"); + + b.Navigation("RawItemSlot"); + + b.Navigation("RawSkill"); + }); + + 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.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.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.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.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("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.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.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("RawAttributes"); + + b.Navigation("RawCharacters"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.AppearanceData", b => + { + b.Navigation("RawEquippedItems"); + }); + + 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("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"); + }); + + 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("RawItemSetGroups"); + + b.Navigation("RawItemSlotTypes"); + + 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"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.GameServerConfiguration", b => + { + b.Navigation("JoinedMaps"); + }); + + modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.GameServerDefinition", b => + { + b.Navigation("RawEndpoints"); + }); + + 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.ItemSetGroup", b => + { + b.Navigation("RawItems"); + }); + + 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"); + }); + + 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/20260806161456_ConfigureCastleSiegeRegistration.cs b/src/Persistence/EntityFramework/Migrations/20260806161456_ConfigureCastleSiegeRegistration.cs new file mode 100644 index 0000000000..7444b18ff2 --- /dev/null +++ b/src/Persistence/EntityFramework/Migrations/20260806161456_ConfigureCastleSiegeRegistration.cs @@ -0,0 +1,74 @@ +// +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +#nullable disable + +namespace MUnique.OpenMU.Persistence.EntityFramework.Migrations +{ + using System; + using Microsoft.EntityFrameworkCore.Migrations; + + /// + public partial class ConfigureCastleSiegeRegistration : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "SignOfLordItemDefinitionId", + schema: "config", + table: "CastleSiegeConfiguration", + type: "uuid", + nullable: true); + + migrationBuilder.AddColumn( + name: "SignOfLordItemLevel", + schema: "config", + table: "CastleSiegeConfiguration", + type: "smallint", + nullable: false, + defaultValue: (byte)3); + + migrationBuilder.CreateIndex( + name: "IX_CastleSiegeConfiguration_SignOfLordItemDefinitionId", + schema: "config", + table: "CastleSiegeConfiguration", + column: "SignOfLordItemDefinitionId"); + + migrationBuilder.AddForeignKey( + name: "FK_CastleSiegeConfiguration_ItemDefinition_SignOfLordItemDefin~", + schema: "config", + table: "CastleSiegeConfiguration", + column: "SignOfLordItemDefinitionId", + principalSchema: "config", + principalTable: "ItemDefinition", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_CastleSiegeConfiguration_ItemDefinition_SignOfLordItemDefin~", + schema: "config", + table: "CastleSiegeConfiguration"); + + migrationBuilder.DropIndex( + name: "IX_CastleSiegeConfiguration_SignOfLordItemDefinitionId", + schema: "config", + table: "CastleSiegeConfiguration"); + + migrationBuilder.DropColumn( + name: "SignOfLordItemDefinitionId", + schema: "config", + table: "CastleSiegeConfiguration"); + + migrationBuilder.DropColumn( + name: "SignOfLordItemLevel", + schema: "config", + table: "CastleSiegeConfiguration"); + } + } +} diff --git a/src/Persistence/EntityFramework/Migrations/EntityDataContextModelSnapshot.cs b/src/Persistence/EntityFramework/Migrations/EntityDataContextModelSnapshot.cs index b601d7b674..872549c451 100644 --- a/src/Persistence/EntityFramework/Migrations/EntityDataContextModelSnapshot.cs +++ b/src/Persistence/EntityFramework/Migrations/EntityDataContextModelSnapshot.cs @@ -434,6 +434,14 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("RewardItemDefinitionId") .HasColumnType("uuid"); + b.Property("SignOfLordItemDefinitionId") + .HasColumnType("uuid"); + + b.Property("SignOfLordItemLevel") + .ValueGeneratedOnAdd() + .HasColumnType("smallint") + .HasDefaultValue((byte)3); + b.Property("StatueBuyPrice") .HasColumnType("integer"); @@ -451,6 +459,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("RewardItemDefinitionId"); + b.HasIndex("SignOfLordItemDefinitionId"); + b.ToTable("CastleSiegeConfiguration", "config"); }); @@ -3994,6 +4004,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) .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"); @@ -4003,6 +4018,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("RawLandOfTrialsMapDefinition"); b.Navigation("RawRewardItemDefinition"); + + b.Navigation("RawSignOfLordItemDefinition"); }); modelBuilder.Entity("MUnique.OpenMU.Persistence.EntityFramework.Model.CastleSiegeData", b => diff --git a/src/Persistence/EntityFramework/Model/CastleSiegeConfiguration.Generated.cs b/src/Persistence/EntityFramework/Model/CastleSiegeConfiguration.Generated.cs index caa77f9dad..7ab01e0b81 100644 --- a/src/Persistence/EntityFramework/Model/CastleSiegeConfiguration.Generated.cs +++ b/src/Persistence/EntityFramework/Model/CastleSiegeConfiguration.Generated.cs @@ -109,6 +109,32 @@ internal partial class CastleSiegeConfiguration : MUnique.OpenMU.DataModel.Confi [NotMapped] public override ICollection DefenseMachineZones => base.DefenseMachineZones ??= new CollectionAdapter(this.RawDefenseMachineZones); + /// + /// Gets or sets the identifier of . + /// + public Guid? SignOfLordItemDefinitionId { get; set; } + + /// + /// Gets the raw object of . + /// + [ForeignKey(nameof(SignOfLordItemDefinitionId))] + public ItemDefinition RawSignOfLordItemDefinition + { + get => base.SignOfLordItemDefinition as ItemDefinition; + set => base.SignOfLordItemDefinition = value; + } + + /// + [NotMapped] + public override MUnique.OpenMU.DataModel.Configuration.Items.ItemDefinition SignOfLordItemDefinition + { + get => base.SignOfLordItemDefinition;set + { + base.SignOfLordItemDefinition = value; + this.SignOfLordItemDefinitionId = this.RawSignOfLordItemDefinition?.Id; + } + } + /// /// Gets or sets the identifier of . /// diff --git a/src/Persistence/Initialization/Updates/ConfigureCastleSiegeRegistrationUpdatePlugIn.cs b/src/Persistence/Initialization/Updates/ConfigureCastleSiegeRegistrationUpdatePlugIn.cs new file mode 100644 index 0000000000..fa414abbe7 --- /dev/null +++ b/src/Persistence/Initialization/Updates/ConfigureCastleSiegeRegistrationUpdatePlugIn.cs @@ -0,0 +1,56 @@ +// +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace MUnique.OpenMU.Persistence.Initialization.Updates; + +using System.Runtime.InteropServices; +using MUnique.OpenMU.DataModel.Configuration; +using MUnique.OpenMU.Persistence.Initialization.VersionSeasonSix.Events; +using MUnique.OpenMU.PlugIns; + +/// +/// Configures Sign of Lord registration for an existing Season 6 Castle Siege configuration. +/// +[PlugIn] +[Display(Name = PlugInName, Description = PlugInDescription)] +[Guid("D91757B1-0C3D-4336-8DEC-20438EDA7F09")] +public class ConfigureCastleSiegeRegistrationUpdatePlugIn : UpdatePlugInBase +{ + /// + /// The plug-in name. + /// + internal const string PlugInName = "Configure Castle Siege registration"; + + /// + /// The plug-in description. + /// + internal const string PlugInDescription = "This update configures the item used for Castle Siege Sign of Lord registration."; + + /// + public override string Name => PlugInName; + + /// + public override string Description => PlugInDescription; + + /// + public override UpdateVersion Version => UpdateVersion.ConfigureCastleSiegeRegistration; + + /// + public override string DataInitializationKey => VersionSeasonSix.DataInitialization.Id; + + /// + public override bool IsMandatory => true; + + /// + public override DateTime CreatedAt => new(2026, 08, 06, 14, 30, 0, DateTimeKind.Utc); + + /// + protected override ValueTask ApplyAsync(IContext context, GameConfiguration gameConfiguration) + { + var configuration = gameConfiguration.CastleSiegeConfiguration + ?? throw new InvalidOperationException("The Castle Siege configuration does not exist."); + new CastleSiegeInitializer(context, gameConfiguration).InitializeRegistration(configuration); + return ValueTask.CompletedTask; + } +} diff --git a/src/Persistence/Initialization/Updates/UpdateVersion.cs b/src/Persistence/Initialization/Updates/UpdateVersion.cs index ac883c8944..b0a9197f3c 100644 --- a/src/Persistence/Initialization/Updates/UpdateVersion.cs +++ b/src/Persistence/Initialization/Updates/UpdateVersion.cs @@ -513,4 +513,9 @@ public enum UpdateVersion /// The version of the . /// FinishSummonerMasterTree = 101, + + /// + /// The version of the . + /// + ConfigureCastleSiegeRegistration = 102, } diff --git a/src/Persistence/Initialization/VersionSeasonSix/Events/CastleSiegeInitializer.cs b/src/Persistence/Initialization/VersionSeasonSix/Events/CastleSiegeInitializer.cs index e2d3c0fc38..e04e983faa 100644 --- a/src/Persistence/Initialization/VersionSeasonSix/Events/CastleSiegeInitializer.cs +++ b/src/Persistence/Initialization/VersionSeasonSix/Events/CastleSiegeInitializer.cs @@ -15,6 +15,9 @@ internal sealed class CastleSiegeInitializer : InitializerBase { private const short GateMonsterNumber = 277; private const short StatueMonsterNumber = 283; + private const byte SignOfLordItemGroup = 14; + private const short SignOfLordItemNumber = 21; + private const byte SignOfLordItemLevel = 3; /// /// Initializes a new instance of the class. @@ -41,6 +44,7 @@ internal CastleSiegeConfiguration InitializeConfiguration() { if (this.GameConfiguration.CastleSiegeConfiguration is { } existingConfiguration) { + this.InitializeRegistration(existingConfiguration); return existingConfiguration; } @@ -49,6 +53,7 @@ internal CastleSiegeConfiguration InitializeConfiguration() configuration.CrownHoldTimeSeconds = 30; configuration.RegisterMinLevel = 200; configuration.RegisterMinMembers = 20; + this.InitializeRegistration(configuration); configuration.ParticipantRewardMinSeconds = 60; configuration.MaxAttackingGuilds = 3; configuration.GuildScoreCastleSiege = 0; @@ -69,6 +74,29 @@ internal CastleSiegeConfiguration InitializeConfiguration() return configuration; } + /// + /// Initializes the item configuration used for Sign of Lord registration. + /// + /// The Castle Siege configuration. + internal void InitializeRegistration(CastleSiegeConfiguration configuration) + { + if (configuration.SignOfLordItemDefinition is not null) + { + return; + } + + var itemDefinition = this.GameConfiguration.Items.SingleOrDefault( + item => item.Group == SignOfLordItemGroup && item.Number == SignOfLordItemNumber); + if (itemDefinition is null) + { + return; + } + + itemDefinition.MaximumItemLevel = Math.Max(itemDefinition.MaximumItemLevel, SignOfLordItemLevel); + configuration.SignOfLordItemDefinition = itemDefinition; + configuration.SignOfLordItemLevel = SignOfLordItemLevel; + } + /// /// Initializes the persistent Castle Siege state. /// diff --git a/tests/MUnique.OpenMU.Persistence.Initialization.Tests/TestInitializationWithEfCore.cs b/tests/MUnique.OpenMU.Persistence.Initialization.Tests/TestInitializationWithEfCore.cs index ee47aac7cc..ca09ffb544 100644 --- a/tests/MUnique.OpenMU.Persistence.Initialization.Tests/TestInitializationWithEfCore.cs +++ b/tests/MUnique.OpenMU.Persistence.Initialization.Tests/TestInitializationWithEfCore.cs @@ -206,6 +206,10 @@ private async Task AssertCastleSiegeDataAsync(IPersistenceContextProvider contex Assert.That(configuration.StatueBuyPrice, Is.EqualTo(4_500_000)); Assert.That(configuration.CastleSiegeMapDefinition?.Number, Is.EqualTo(30)); Assert.That(configuration.LandOfTrialsMapDefinition?.Number, Is.EqualTo(31)); + Assert.That(configuration.SignOfLordItemDefinition?.Group, Is.EqualTo(14)); + Assert.That(configuration.SignOfLordItemDefinition?.Number, Is.EqualTo(21)); + Assert.That(configuration.SignOfLordItemDefinition?.MaximumItemLevel, Is.GreaterThanOrEqualTo(3)); + Assert.That(configuration.SignOfLordItemLevel, Is.EqualTo(3)); Assert.That(configuration.DefenseRespawnArea, Is.Not.Null); Assert.That(configuration.AttackRespawnArea, Is.Not.Null); }); @@ -324,6 +328,47 @@ private async Task AssertCastleSiegeUpdatePlugInAsync(InMemoryPersistenceContext Assert.That(gameConfiguration.CastleSiegeConfiguration, Is.Not.Null); Assert.That(await context.GetAsync().ConfigureAwait(false), Has.Exactly(1).Items); + + var configuration = gameConfiguration.CastleSiegeConfiguration!; + var signOfLord = gameConfiguration.Items.Single(item => item.Group == 14 && item.Number == 21); + configuration.SignOfLordItemDefinition = null; + configuration.SignOfLordItemLevel = 0; + signOfLord.MaximumItemLevel = 0; + + var registrationUpdate = new ConfigureCastleSiegeRegistrationUpdatePlugIn(); + await registrationUpdate.ApplyUpdateAsync(context, gameConfiguration).ConfigureAwait(false); + await registrationUpdate.ApplyUpdateAsync(context, gameConfiguration).ConfigureAwait(false); + + Assert.Multiple(() => + { + Assert.That(configuration.SignOfLordItemDefinition, Is.SameAs(signOfLord)); + Assert.That(configuration.SignOfLordItemLevel, Is.EqualTo(3)); + Assert.That(signOfLord.MaximumItemLevel, Is.EqualTo(3)); + }); + + var customSignOfLord = gameConfiguration.Items.First(item => item != signOfLord); + configuration.SignOfLordItemDefinition = customSignOfLord; + configuration.SignOfLordItemLevel = 1; + await registrationUpdate.ApplyUpdateAsync(context, gameConfiguration).ConfigureAwait(false); + Assert.Multiple(() => + { + Assert.That(configuration.SignOfLordItemDefinition, Is.SameAs(customSignOfLord)); + Assert.That(configuration.SignOfLordItemLevel, Is.EqualTo(1)); + }); + + gameConfiguration.Items.Remove(signOfLord); + configuration.SignOfLordItemDefinition = null; + configuration.SignOfLordItemLevel = 0; + await registrationUpdate.ApplyUpdateAsync(context, gameConfiguration).ConfigureAwait(false); + Assert.Multiple(() => + { + Assert.That(configuration.SignOfLordItemDefinition, Is.Null); + Assert.That(configuration.SignOfLordItemLevel, Is.Zero); + }); + + gameConfiguration.Items.Add(signOfLord); + configuration.SignOfLordItemDefinition = signOfLord; + configuration.SignOfLordItemLevel = 3; } private void AssertUpgrades( diff --git a/tests/MUnique.OpenMU.Tests/CastleSiegeRegistrationRemoteViewTests.cs b/tests/MUnique.OpenMU.Tests/CastleSiegeRegistrationRemoteViewTests.cs new file mode 100644 index 0000000000..d898f44828 --- /dev/null +++ b/tests/MUnique.OpenMU.Tests/CastleSiegeRegistrationRemoteViewTests.cs @@ -0,0 +1,82 @@ +// +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace MUnique.OpenMU.Tests; + +using MUnique.OpenMU.GameLogic.Views.CastleSiege; +using MUnique.OpenMU.GameServer.RemoteView.CastleSiege; +using MUnique.OpenMU.Network.Packets.ServerToClient; + +/// +/// Tests Castle Siege registration remote-view packet serialization. +/// +[TestFixture] +public class CastleSiegeRegistrationRemoteViewTests +{ + /// + /// Verifies registration, unregistration, state and mark response packets. + /// + [Test] + public async ValueTask SerializeRegistrationResponsesAsync() + { + var (player, output) = CastleSiegeRemoteViewTestHelper.CreatePlayer(); + + await new CastleSiegeRegistrationResultPlugIn(player) + .ShowRegistrationResultAsync(CastleSiegeRegistrationResult.AlreadyRegistered, "GuildA") + .ConfigureAwait(false); + await new CastleSiegeRegistrationResultPlugIn(player) + .ShowUnregistrationResultAsync(CastleSiegeUnregistrationResult.Success, true, "GuildA") + .ConfigureAwait(false); + await new CastleSiegeRegistrationStatePlugIn(player) + .ShowRegistrationStateAsync( + CastleSiegeRegistrationStateResult.Registered, + "GuildA", + 21, + false, + 4) + .ConfigureAwait(false); + await new CastleSiegeMarkRegistrationResultPlugIn(player) + .ShowMarkRegistrationResultAsync(CastleSiegeMarkRegistrationResult.IncorrectItem, "GuildA", 22) + .ConfigureAwait(false); + + var data = output.ToArray().AsMemory(); + Assert.That( + data.Length, + Is.EqualTo( + CastleSiegeRegistrationResponse.Length + + CastleSiegeUnregisterResponse.Length + + CastleSiegeRegistrationStateResponse.Length + + CastleSiegeMarkRegistrationResponse.Length)); + + var registration = (CastleSiegeRegistrationResponse)data[..CastleSiegeRegistrationResponse.Length]; + Assert.That(registration.Result, Is.EqualTo((byte)CastleSiegeRegistrationResult.AlreadyRegistered)); + Assert.That(registration.GuildName, Is.EqualTo("GuildA")); + + var offset = CastleSiegeRegistrationResponse.Length; + var unregistration = (CastleSiegeUnregisterResponse)data.Slice( + offset, + CastleSiegeUnregisterResponse.Length); + Assert.That(unregistration.Result, Is.EqualTo((byte)CastleSiegeUnregistrationResult.Success)); + Assert.That(unregistration.IsGivingUp, Is.True); + Assert.That(unregistration.GuildName, Is.EqualTo("GuildA")); + + offset += CastleSiegeUnregisterResponse.Length; + var state = (CastleSiegeRegistrationStateResponse)data.Slice( + offset, + CastleSiegeRegistrationStateResponse.Length); + Assert.That(state.Result, Is.EqualTo((byte)CastleSiegeRegistrationStateResult.Registered)); + Assert.That(state.GuildName, Is.EqualTo("GuildA")); + Assert.That(state.GuildMarkCount, Is.EqualTo(21)); + Assert.That(state.IsGivingUp, Is.False); + Assert.That(state.RegistrationRank, Is.EqualTo(4)); + + offset += CastleSiegeRegistrationStateResponse.Length; + var mark = (CastleSiegeMarkRegistrationResponse)data.Slice( + offset, + CastleSiegeMarkRegistrationResponse.Length); + Assert.That(mark.Result, Is.EqualTo((byte)CastleSiegeMarkRegistrationResult.IncorrectItem)); + Assert.That(mark.GuildName, Is.EqualTo("GuildA")); + Assert.That(mark.GuildMarkCount, Is.EqualTo(22)); + } +} diff --git a/tests/MUnique.OpenMU.Tests/CastleSiegeRegistrationTests.cs b/tests/MUnique.OpenMU.Tests/CastleSiegeRegistrationTests.cs new file mode 100644 index 0000000000..51f6198948 --- /dev/null +++ b/tests/MUnique.OpenMU.Tests/CastleSiegeRegistrationTests.cs @@ -0,0 +1,482 @@ +// +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace MUnique.OpenMU.Tests; + +using System.Collections.Immutable; +using Microsoft.Extensions.Logging.Abstractions; +using Moq; +using MUnique.OpenMU.DataModel.Configuration; +using MUnique.OpenMU.DataModel.Entities; +using MUnique.OpenMU.GameLogic; +using MUnique.OpenMU.GameLogic.Attributes; +using MUnique.OpenMU.GameLogic.CastleSiege; +using MUnique.OpenMU.GameLogic.CastleSiege.Actions; +using MUnique.OpenMU.GameLogic.Views.CastleSiege; +using MUnique.OpenMU.GameServer; +using MUnique.OpenMU.GameServer.MessageHandler.CastleSiege; +using MUnique.OpenMU.Interfaces; +using MUnique.OpenMU.Network.Packets.ClientToServer; +using MUnique.OpenMU.Persistence; +using MUnique.OpenMU.Persistence.InMemory; +using MUnique.OpenMU.PlugIns; +using BasicModel = MUnique.OpenMU.Persistence.BasicModel; +using RuntimeGuild = MUnique.OpenMU.Interfaces.Guild; + +/// +/// Tests Castle Siege guild and Sign of Lord registration. +/// +[TestFixture] +public class CastleSiegeRegistrationTests +{ + private const uint RuntimeGuildId = 42; + private const string GuildName = "TestGuild"; + + /// + /// Verifies the protocol result values documented by the Castle Siege registration issue. + /// + [Test] + public void RegistrationResultValuesMatchProtocol() + { + Assert.Multiple(() => + { + Assert.That((byte)CastleSiegeRegistrationResult.Failed, Is.Zero); + Assert.That((byte)CastleSiegeRegistrationResult.Success, Is.EqualTo(1)); + Assert.That((byte)CastleSiegeRegistrationResult.AlreadyRegistered, Is.EqualTo(2)); + Assert.That((byte)CastleSiegeRegistrationResult.IsDefender, Is.EqualTo(3)); + Assert.That((byte)CastleSiegeRegistrationResult.InvalidGuild, Is.EqualTo(4)); + Assert.That((byte)CastleSiegeRegistrationResult.LevelInsufficient, Is.EqualTo(5)); + Assert.That((byte)CastleSiegeRegistrationResult.NoGuild, Is.EqualTo(6)); + Assert.That((byte)CastleSiegeRegistrationResult.NotRegistrationPeriod, Is.EqualTo(7)); + Assert.That((byte)CastleSiegeRegistrationResult.NotEnoughMembers, Is.EqualTo(8)); + Assert.That((byte)CastleSiegeMarkRegistrationResult.Failed, Is.Zero); + Assert.That((byte)CastleSiegeMarkRegistrationResult.Success, Is.EqualTo(1)); + Assert.That((byte)CastleSiegeMarkRegistrationResult.GuildNotRegistered, Is.EqualTo(2)); + Assert.That((byte)CastleSiegeMarkRegistrationResult.IncorrectItem, Is.EqualTo(3)); + }); + } + + /// + /// Verifies registration validation, persistence, and duplicate detection. + /// + [Test] + public async ValueTask RegistrationValidatesAndPersistsAsync() + { + var fixture = await CreateFixtureAsync().ConfigureAwait(false); + var view = fixture.Player.ViewPlugIns.GetPlugIn()!; + var action = new CastleSiegeRegisterGuildAction(); + + await action.RegisterAsync(fixture.Player, fixture.Context).ConfigureAwait(false); + Mock.Get(view).Verify( + plugIn => plugIn.ShowRegistrationResultAsync(CastleSiegeRegistrationResult.NoGuild, string.Empty), + Times.Once); + + fixture.Player.GuildStatus = new GuildMemberStatus(RuntimeGuildId, GuildPosition.NormalMember); + await action.RegisterAsync(fixture.Player, fixture.Context).ConfigureAwait(false); + Mock.Get(view).Verify( + plugIn => plugIn.ShowRegistrationResultAsync(CastleSiegeRegistrationResult.InvalidGuild, string.Empty), + Times.Once); + + fixture.Player.GuildStatus = new GuildMemberStatus(RuntimeGuildId, GuildPosition.GuildMaster); + await action.RegisterAsync(fixture.Player, fixture.Context).ConfigureAwait(false); + Mock.Get(view).Verify( + plugIn => plugIn.ShowRegistrationResultAsync(CastleSiegeRegistrationResult.LevelInsufficient, GuildName), + Times.Once); + + fixture.Player.Attributes![Stats.Level] = 400; + fixture.GuildMembers.Clear(); + await action.RegisterAsync(fixture.Player, fixture.Context).ConfigureAwait(false); + Mock.Get(view).Verify( + plugIn => plugIn.ShowRegistrationResultAsync(CastleSiegeRegistrationResult.NotEnoughMembers, GuildName), + Times.Once); + + fixture.GuildMembers.AddRange([new(), new()]); + await action.RegisterAsync(fixture.Player, fixture.Context).ConfigureAwait(false); + await action.RegisterAsync(fixture.Player, fixture.Context).ConfigureAwait(false); + Mock.Get(view).Verify( + plugIn => plugIn.ShowRegistrationResultAsync(CastleSiegeRegistrationResult.Success, GuildName), + Times.Once); + Mock.Get(view).Verify( + plugIn => plugIn.ShowRegistrationResultAsync(CastleSiegeRegistrationResult.AlreadyRegistered, GuildName), + Times.Once); + + Assert.That(fixture.Context.RegisteredGuilds, Contains.Key(fixture.PersistentGuildId)); + Assert.That(fixture.Context.RegisteredGuilds[fixture.PersistentGuildId].RegistrationOrder, Is.EqualTo(1)); + + using var persistenceContext = fixture.PersistenceContextProvider.CreateNewTypedContext( + typeof(CastleSiegeGuildRegistration), + false, + fixture.GameConfiguration); + var registration = (await persistenceContext.GetAsync().ConfigureAwait(false)).Single(); + Assert.That(registration.GuildId, Is.EqualTo(fixture.PersistentGuildId)); + Assert.That(registration.GuildName, Is.EqualTo(GuildName)); + + var restartedContext = new CastleSiegeContext(fixture.GameServerContext, fixture.CastleSiegeConfiguration); + await restartedContext.InitializeAsync(new DateTime(2026, 8, 3, 12, 0, 0, DateTimeKind.Utc)).ConfigureAwait(false); + Assert.That(restartedContext.RegisteredGuilds, Contains.Key(fixture.PersistentGuildId)); + } + + /// + /// Verifies the registration-period and defender validation results. + /// + [Test] + public async ValueTask RegistrationRejectsClosedPeriodAndDefenderAsync() + { + var fixture = await CreateFixtureAsync().ConfigureAwait(false); + var view = fixture.Player.ViewPlugIns.GetPlugIn()!; + var action = new CastleSiegeRegisterGuildAction(); + + fixture.CastleSiegeConfiguration.Enabled = false; + await action.RegisterAsync(fixture.Player, fixture.Context).ConfigureAwait(false); + Mock.Get(view).Verify( + plugIn => plugIn.ShowRegistrationResultAsync(CastleSiegeRegistrationResult.Failed, string.Empty), + Times.Once); + fixture.CastleSiegeConfiguration.Enabled = true; + + fixture.Player.GuildStatus = new GuildMemberStatus(RuntimeGuildId, GuildPosition.GuildMaster); + fixture.Player.Attributes![Stats.Level] = 400; + + fixture.Context.SetPeriod(fixture.Context.Schedule.GetCurrentPeriod(new DateTime(2026, 8, 4, 12, 0, 0, DateTimeKind.Utc))); + await action.RegisterAsync(fixture.Player, fixture.Context).ConfigureAwait(false); + Mock.Get(view).Verify( + plugIn => plugIn.ShowRegistrationResultAsync(CastleSiegeRegistrationResult.NotRegistrationPeriod, string.Empty), + Times.Once); + + fixture.Context.SetPeriod(fixture.Context.Schedule.GetCurrentPeriod(new DateTime(2026, 8, 3, 12, 0, 0, DateTimeKind.Utc))); + fixture.Context.SiegeData.OwnerGuildId = fixture.PersistentGuildId; + await action.RegisterAsync(fixture.Player, fixture.Context).ConfigureAwait(false); + Mock.Get(view).Verify( + plugIn => plugIn.ShowRegistrationResultAsync(CastleSiegeRegistrationResult.IsDefender, GuildName), + Times.Once); + } + + /// + /// Verifies that unregistration removes both runtime and persistent registration state. + /// + [Test] + public async ValueTask UnregistrationRemovesPersistentRegistrationAsync() + { + var fixture = await CreateRegisteredFixtureAsync().ConfigureAwait(false); + var view = fixture.Player.ViewPlugIns.GetPlugIn()!; + + await new CastleSiegeUnregisterGuildAction().UnregisterAsync(fixture.Player, fixture.Context, true).ConfigureAwait(false); + + Mock.Get(view).Verify( + plugIn => plugIn.ShowUnregistrationResultAsync( + CastleSiegeUnregistrationResult.Success, + true, + GuildName), + Times.Once); + Assert.That(fixture.Context.RegisteredGuilds, Is.Empty); + using var persistenceContext = fixture.PersistenceContextProvider.CreateNewTypedContext( + typeof(CastleSiegeGuildRegistration), + false, + fixture.GameConfiguration); + Assert.That(await persistenceContext.GetAsync().ConfigureAwait(false), Is.Empty); + } + + /// + /// Verifies Sign of Lord validation, consumption, mark persistence, and registration-state queries. + /// + [Test] + public async ValueTask MarkRegistrationConsumesValidSignOfLordAndPersistsCountAsync() + { + const byte itemSlot = 20; + var fixture = await CreateRegisteredFixtureAsync().ConfigureAwait(false); + fixture.Context.SetPeriod(fixture.Context.Schedule.GetCurrentPeriod(new DateTime(2026, 8, 4, 12, 0, 0, DateTimeKind.Utc))); + var markView = fixture.Player.ViewPlugIns.GetPlugIn()!; + var item = fixture.Player.PersistenceContext.CreateNew(); + item.Definition = fixture.CastleSiegeConfiguration.SignOfLordItemDefinition; + item.Level = 2; + await fixture.Player.Inventory!.AddItemAsync(itemSlot, item).ConfigureAwait(false); + + var action = new CastleSiegeRegisterMarkAction(); + await action.RegisterMarkAsync(fixture.Player, fixture.Context, itemSlot).ConfigureAwait(false); + Mock.Get(markView).Verify( + plugIn => plugIn.ShowMarkRegistrationResultAsync(CastleSiegeMarkRegistrationResult.IncorrectItem, GuildName, 0), + Times.Once); + Assert.That(fixture.Player.Inventory.GetItem(itemSlot), Is.SameAs(item)); + + item.Level = fixture.CastleSiegeConfiguration.SignOfLordItemLevel; + await action.RegisterMarkAsync(fixture.Player, fixture.Context, itemSlot).ConfigureAwait(false); + Mock.Get(markView).Verify( + plugIn => plugIn.ShowMarkRegistrationResultAsync(CastleSiegeMarkRegistrationResult.Success, GuildName, 1), + Times.Once); + Assert.That(fixture.Player.Inventory.GetItem(itemSlot), Is.Null); + Assert.That(fixture.Context.RegisteredGuilds[fixture.PersistentGuildId].Marks, Is.EqualTo(1)); + + using var persistenceContext = fixture.PersistenceContextProvider.CreateNewTypedContext( + typeof(CastleSiegeGuildRegistration), + false, + fixture.GameConfiguration); + var registration = (await persistenceContext.GetAsync().ConfigureAwait(false)).Single(); + Assert.That(registration.Marks, Is.EqualTo(1)); + + var stateView = fixture.Player.ViewPlugIns.GetPlugIn()!; + await new CastleSiegeRegistrationStateAction().ShowStateAsync(fixture.Player, fixture.Context).ConfigureAwait(false); + Mock.Get(stateView).Verify( + plugIn => plugIn.ShowRegistrationStateAsync( + CastleSiegeRegistrationStateResult.Registered, + GuildName, + 1, + false, + 1), + Times.Once); + } + + /// + /// Verifies that a Sign of Lord is preserved if the persistent guild registration disappeared. + /// + [Test] + public async ValueTask MarkRegistrationPreservesItemWhenRegistrationDisappearedAsync() + { + const byte itemSlot = 20; + var fixture = await CreateRegisteredFixtureAsync().ConfigureAwait(false); + fixture.Context.SetPeriod(fixture.Context.Schedule.GetCurrentPeriod(new DateTime(2026, 8, 4, 12, 0, 0, DateTimeKind.Utc))); + var cachedRegistration = fixture.Context.RegisteredGuilds[fixture.PersistentGuildId]; + using (var persistenceContext = fixture.PersistenceContextProvider.CreateNewTypedContext( + typeof(CastleSiegeGuildRegistration), + false, + fixture.GameConfiguration)) + { + var persistentRegistration = await persistenceContext.GetByIdAsync(cachedRegistration.Id).ConfigureAwait(false); + Assert.That(persistentRegistration, Is.Not.Null); + await persistenceContext.DeleteAsync(persistentRegistration!).ConfigureAwait(false); + await persistenceContext.SaveChangesAsync().ConfigureAwait(false); + } + + var signOfLord = fixture.Player.PersistenceContext.CreateNew(); + signOfLord.Definition = fixture.CastleSiegeConfiguration.SignOfLordItemDefinition; + signOfLord.Level = fixture.CastleSiegeConfiguration.SignOfLordItemLevel; + await fixture.Player.Inventory!.AddItemAsync(itemSlot, signOfLord).ConfigureAwait(false); + + await new CastleSiegeRegisterMarkAction().RegisterMarkAsync(fixture.Player, fixture.Context, itemSlot).ConfigureAwait(false); + + var view = fixture.Player.ViewPlugIns.GetPlugIn()!; + Mock.Get(view).Verify( + plugIn => plugIn.ShowMarkRegistrationResultAsync( + CastleSiegeMarkRegistrationResult.GuildNotRegistered, + GuildName, + 0), + Times.Once); + Assert.That(fixture.Player.Inventory.GetItem(itemSlot), Is.SameAs(signOfLord)); + Assert.That(fixture.Context.RegisteredGuilds, Does.Not.ContainKey(fixture.PersistentGuildId)); + } + + /// + /// Verifies that alliance members can query, but cannot mutate, the alliance master's registration. + /// + [Test] + public async ValueTask AllianceMemberUsesOfflineMasterRegistrationForQueriesOnlyAsync() + { + const uint allianceMemberGuildId = 43; + var fixture = await CreateFixtureAsync().ConfigureAwait(false); + var allianceMaster = new RuntimeGuild { Name = GuildName }; + allianceMaster.AllianceGuild = allianceMaster; + fixture.GuildServer + .Setup(server => server.GetGuildAsync(RuntimeGuildId)) + .Returns(new ValueTask(allianceMaster)); + fixture.Player.GuildStatus = new GuildMemberStatus(RuntimeGuildId, GuildPosition.GuildMaster); + fixture.Player.Attributes![Stats.Level] = 400; + + var registrationView = fixture.Player.ViewPlugIns.GetPlugIn()!; + await new CastleSiegeRegisterGuildAction().RegisterAsync(fixture.Player, fixture.Context).ConfigureAwait(false); + Mock.Get(registrationView).Verify( + plugIn => plugIn.ShowRegistrationResultAsync(CastleSiegeRegistrationResult.Success, GuildName), + Times.Once); + + var allianceMember = new RuntimeGuild { Name = "Member", AllianceGuild = allianceMaster }; + fixture.GuildServer + .Setup(server => server.GetGuildAsync(allianceMemberGuildId)) + .Returns(new ValueTask(allianceMember)); + fixture.GuildServer + .Setup(server => server.GetPersistentAllianceMasterGuildIdAsync(allianceMemberGuildId)) + .Returns(new ValueTask(fixture.PersistentGuildId)); + fixture.GuildServer + .Setup(server => server.IsAllianceMasterAsync(allianceMemberGuildId)) + .Returns(new ValueTask(false)); + fixture.Player.GuildStatus = new GuildMemberStatus(allianceMemberGuildId, GuildPosition.GuildMaster); + + await new CastleSiegeRegisterGuildAction().RegisterAsync(fixture.Player, fixture.Context).ConfigureAwait(false); + Mock.Get(registrationView).Verify( + plugIn => plugIn.ShowRegistrationResultAsync(CastleSiegeRegistrationResult.InvalidGuild, string.Empty), + Times.Once); + + fixture.GuildServer + .Setup(server => server.GetGuildAsync(RuntimeGuildId)) + .Returns(new ValueTask((RuntimeGuild?)null)); + var stateView = fixture.Player.ViewPlugIns.GetPlugIn()!; + await new CastleSiegeRegistrationStateAction().ShowStateAsync(fixture.Player, fixture.Context).ConfigureAwait(false); + Mock.Get(stateView).Verify( + plugIn => plugIn.ShowRegistrationStateAsync( + CastleSiegeRegistrationStateResult.Registered, + GuildName, + 0, + false, + 1), + Times.Once); + fixture.GuildServer.Verify(server => server.GetGuildIdByNameAsync(It.IsAny()), Times.Never); + } + + /// + /// Verifies the four client request subcodes handled by this issue. + /// + [Test] + public void RequestHandlersUseExpectedSubcodes() + { + Assert.Multiple(() => + { + Assert.That(new CastleSiegeRegistrationHandlerPlugIn().Key, Is.EqualTo(0x01)); + Assert.That(new CastleSiegeUnregisterHandlerPlugIn().Key, Is.EqualTo(0x02)); + Assert.That(new CastleSiegeRegistrationStateHandlerPlugIn().Key, Is.EqualTo(0x03)); + Assert.That(new CastleSiegeMarkRegistrationHandlerPlugIn().Key, Is.EqualTo(0x04)); + }); + } + + /// + /// Verifies that the zero-based backpack-grid index sent by MuMain is translated past the equipment slots. + /// + [Test] + public void MarkRegistrationHandlerTranslatesClientBackpackIndex() + { + Assert.Multiple(() => + { + Assert.That(CastleSiegeMarkRegistrationHandlerPlugIn.TryGetInventorySlot(8, out var inventorySlot), Is.True); + Assert.That(inventorySlot, Is.EqualTo(20)); + Assert.That(CastleSiegeMarkRegistrationHandlerPlugIn.TryGetInventorySlot(byte.MaxValue, out _), Is.False); + }); + } + + /// + /// Verifies that a truncated mark-registration packet is ignored. + /// + [Test] + public async ValueTask MarkRegistrationHandlerIgnoresTruncatedPacketAsync() + { + var fixture = await CreateFixtureAsync().ConfigureAwait(false); + var packet = new byte[CastleSiegeMarkRegistration.Length - 1]; + + await new CastleSiegeMarkRegistrationHandlerPlugIn() + .HandlePacketAsync(fixture.Player, packet) + .ConfigureAwait(false); + } + + private static async ValueTask CreateRegisteredFixtureAsync() + { + var fixture = await CreateFixtureAsync().ConfigureAwait(false); + fixture.Player.GuildStatus = new GuildMemberStatus(RuntimeGuildId, GuildPosition.GuildMaster); + fixture.Player.Attributes![Stats.Level] = 400; + await new CastleSiegeRegisterGuildAction().RegisterAsync(fixture.Player, fixture.Context).ConfigureAwait(false); + return fixture; + } + + private static async ValueTask CreateFixtureAsync() + { + var persistenceContextProvider = new InMemoryPersistenceContextProvider(); + BasicModel.GameConfiguration gameConfiguration; + BasicModel.CastleSiegeConfiguration castleSiegeConfiguration; + Guid persistentGuildId; + using (var persistenceContext = persistenceContextProvider.CreateNewContext()) + { + gameConfiguration = persistenceContext.CreateNew(); + gameConfiguration.Maps.Add(new BasicModel.GameMapDefinition()); + castleSiegeConfiguration = persistenceContext.CreateNew(); + castleSiegeConfiguration.Enabled = true; + castleSiegeConfiguration.RegisterMinLevel = 200; + castleSiegeConfiguration.RegisterMinMembers = 2; + var signOfLordDefinition = persistenceContext.CreateNew(); + signOfLordDefinition.Name = "Rena"; + signOfLordDefinition.Group = 14; + signOfLordDefinition.Number = 21; + signOfLordDefinition.MaximumItemLevel = 3; + signOfLordDefinition.Width = 1; + signOfLordDefinition.Height = 1; + gameConfiguration.Items.Add(signOfLordDefinition); + castleSiegeConfiguration.SignOfLordItemDefinition = signOfLordDefinition; + castleSiegeConfiguration.SignOfLordItemLevel = 3; + castleSiegeConfiguration.StateSchedule.Add(new BasicModel.CastleSiegeStateScheduleEntry + { + State = CastleSiegeState.RegisterGuild, + DayOfWeek = DayOfWeek.Monday, + }); + castleSiegeConfiguration.StateSchedule.Add(new BasicModel.CastleSiegeStateScheduleEntry + { + State = CastleSiegeState.RegisterMark, + DayOfWeek = DayOfWeek.Tuesday, + }); + gameConfiguration.CastleSiegeConfiguration = castleSiegeConfiguration; + persistenceContext.CreateNew(); + var persistentGuild = persistenceContext.CreateNew(); + persistentGuild.Name = GuildName; + persistentGuildId = persistentGuild.Id; + await persistenceContext.SaveChangesAsync().ConfigureAwait(false); + } + + var guildMembers = new List { new(), new() }; + var guildServer = new Mock(); + guildServer + .Setup(server => server.GetGuildAsync(RuntimeGuildId)) + .Returns(new ValueTask(new RuntimeGuild { Name = GuildName })); + guildServer + .Setup(server => server.GetGuildListAsync(RuntimeGuildId)) + .Returns(() => new ValueTask>(guildMembers.ToImmutableList())); + guildServer + .Setup(server => server.IsAllianceMasterAsync(RuntimeGuildId)) + .Returns(new ValueTask(true)); + guildServer + .Setup(server => server.GetGuildIdByNameAsync(GuildName)) + .Returns(new ValueTask(RuntimeGuildId)); + guildServer + .Setup(server => server.GetPersistentGuildIdAsync(RuntimeGuildId)) + .Returns(new ValueTask(persistentGuildId)); + guildServer + .Setup(server => server.GetPersistentAllianceMasterGuildIdAsync(RuntimeGuildId)) + .Returns(new ValueTask(persistentGuildId)); + + var mapInitializer = new MapInitializer(gameConfiguration, new NullLogger(), NullDropGenerator.Instance, null); + var gameServerContext = new GameServerContext( + new BasicModel.GameServerDefinition + { + GameConfiguration = gameConfiguration, + ServerConfiguration = new BasicModel.GameServerConfiguration(), + }, + guildServer.Object, + new Mock().Object, + new Mock().Object, + new Mock().Object, + persistenceContextProvider, + mapInitializer, + NullLoggerFactory.Instance, + new PlugInManager([], NullLoggerFactory.Instance, null, null), + NullDropGenerator.Instance, + new ConfigurationChangeMediator()); + mapInitializer.PlugInManager = gameServerContext.PlugInManager; + mapInitializer.PathFinderPool = gameServerContext.PathFinderPool; + + var player = await PlayerTestHelper.CreatePlayerAsync(gameServerContext).ConfigureAwait(false); + var castleSiegeContext = new CastleSiegeContext(gameServerContext, castleSiegeConfiguration); + await castleSiegeContext.InitializeAsync(new DateTime(2026, 8, 3, 12, 0, 0, DateTimeKind.Utc)).ConfigureAwait(false); + return new( + persistenceContextProvider, + gameConfiguration, + castleSiegeConfiguration, + gameServerContext, + castleSiegeContext, + player, + persistentGuildId, + guildMembers, + guildServer); + } + + private sealed record TestFixture( + InMemoryPersistenceContextProvider PersistenceContextProvider, + GameConfiguration GameConfiguration, + CastleSiegeConfiguration CastleSiegeConfiguration, + GameServerContext GameServerContext, + CastleSiegeContext Context, + Player Player, + Guid PersistentGuildId, + List GuildMembers, + Mock GuildServer); +} diff --git a/tests/MUnique.OpenMU.Tests/CastleSiegeRemoteViewTestHelper.cs b/tests/MUnique.OpenMU.Tests/CastleSiegeRemoteViewTestHelper.cs new file mode 100644 index 0000000000..fdde0c1e3d --- /dev/null +++ b/tests/MUnique.OpenMU.Tests/CastleSiegeRemoteViewTestHelper.cs @@ -0,0 +1,47 @@ +// +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace MUnique.OpenMU.Tests; + +using System.IO; +using System.IO.Pipelines; +using Microsoft.Extensions.Logging.Abstractions; +using Moq; +using MUnique.OpenMU.DataModel.Configuration; +using MUnique.OpenMU.GameLogic; +using MUnique.OpenMU.GameServer; +using MUnique.OpenMU.GameServer.RemoteView; +using MUnique.OpenMU.Network; +using MUnique.OpenMU.Persistence; +using MUnique.OpenMU.PlugIns; +using Nito.AsyncEx; + +/// +/// Creates remote players with an in-memory packet output for Castle Siege view tests. +/// +internal static class CastleSiegeRemoteViewTestHelper +{ + /// + /// Creates a remote player and its packet output stream. + /// + /// The remote player and output stream. + internal static (RemotePlayer Player, MemoryStream Output) CreatePlayer() + { + var output = new MemoryStream(); + var writer = PipeWriter.Create(output, new StreamPipeWriterOptions(leaveOpen: true)); + var connection = new Mock(); + connection.SetupGet(c => c.Connected).Returns(true); + connection.SetupGet(c => c.Output).Returns(writer); + connection.SetupGet(c => c.OutputLock).Returns(new AsyncLock()); + + var manager = new PlugInManager(null, new NullLoggerFactory(), null, null); + var gameContext = new Mock(); + gameContext.Setup(c => c.PersistenceContextProvider) + .Returns(new Mock().Object); + gameContext.Setup(c => c.Configuration).Returns(new GameConfiguration()); + gameContext.Setup(c => c.PlugInManager).Returns(manager); + gameContext.Setup(c => c.LoggerFactory).Returns(new NullLoggerFactory()); + return (new RemotePlayer(gameContext.Object, connection.Object, default), output); + } +} diff --git a/tests/MUnique.OpenMU.Tests/GuildAllianceTest.cs b/tests/MUnique.OpenMU.Tests/GuildAllianceTest.cs index 031957d308..e5339c9978 100644 --- a/tests/MUnique.OpenMU.Tests/GuildAllianceTest.cs +++ b/tests/MUnique.OpenMU.Tests/GuildAllianceTest.cs @@ -147,6 +147,26 @@ public async ValueTask IsAllianceMaster_MemberGuild_ReturnsFalse() Assert.That(isMaster, Is.False); } + /// + /// An online alliance member can resolve the persistent master guild after the master's last member went offline. + /// + [Test] + public async ValueTask GetPersistentAllianceMasterGuildId_MasterOffline_ReturnsMasterId() + { + await this.GuildServer.CreateAllianceAsync(this._firstGuildId, this._secondGuildId).ConfigureAwait(false); + var masterPersistentId = await this.GuildServer.GetPersistentGuildIdAsync(this._firstGuildId).ConfigureAwait(false); + + await this.GuildServer.GuildMemberLeftGameAsync(this._firstGuildId, this.GuildMaster.Id, 0).ConfigureAwait(false); + var masterGuildMember = (await this.GuildServer.GetGuildListAsync(this._firstGuildId).ConfigureAwait(false)).Single(); + var resolvedMasterId = await this.GuildServer.GetPersistentAllianceMasterGuildIdAsync(this._secondGuildId).ConfigureAwait(false); + + Assert.Multiple(() => + { + Assert.That(masterGuildMember.ServerId, Is.EqualTo(MUnique.OpenMU.GuildServer.GuildServer.OfflineServerId)); + Assert.That(resolvedMasterId, Is.EqualTo(masterPersistentId)); + }); + } + // ------------------------------------------------------------------------- // GetAllianceGuildsAsync // -------------------------------------------------------------------------