diff --git a/src/GameLogic/PlugIns/ChatCommands/CastleSiegePhaseChatCommandPlugIn.cs b/src/GameLogic/PlugIns/ChatCommands/CastleSiegePhaseChatCommandPlugIn.cs
new file mode 100644
index 0000000..3ea4095
--- /dev/null
+++ b/src/GameLogic/PlugIns/ChatCommands/CastleSiegePhaseChatCommandPlugIn.cs
@@ -0,0 +1,49 @@
+//
+// Licensed under the MIT License. See LICENSE file in the project root for full license information.
+//
+
+namespace MUnique.OpenMU.GameLogic.PlugIns.ChatCommands;
+
+using System.Runtime.InteropServices;
+using MUnique.OpenMU.GameLogic.CastleSiege;
+using MUnique.OpenMU.GameLogic.PlugIns.PeriodicTasks;
+using MUnique.OpenMU.GameLogic.Views;
+using MUnique.OpenMU.Interfaces;
+using MUnique.OpenMU.PlugIns;
+
+/// Forces a specific Castle Siege phase. GM only. Usage: /csphase Siege.
+[Guid("A1B2C3D4-0003-4E5F-9A0B-CA5710000003")]
+[PlugIn]
+[Display(Name = "Castle Siege Phase", Description = "GM command: /csphase ")]
+[ChatCommandHelp(Command, CharacterStatus.GameMaster)]
+public class CastleSiegePhaseChatCommandPlugIn : IChatCommandPlugIn
+{
+ private const string Command = "/csphase";
+
+ ///
+ public string Key => Command;
+
+ ///
+ public CharacterStatus MinCharacterStatusRequirement => CharacterStatus.GameMaster;
+
+ ///
+ public async ValueTask HandleCommandAsync(Player player, string command)
+ {
+ var parts = command.Split(' ', StringSplitOptions.RemoveEmptyEntries);
+ if (parts.Length < 2 || !Enum.TryParse(parts[1], true, out var phase))
+ {
+ await player.InvokeViewPlugInAsync(p => p.ShowMessageAsync("Usage: /csphase ", MessageType.BlueNormal)).ConfigureAwait(false);
+ return;
+ }
+
+ var context = CastleSiegeEventPlugIn.TryGetContext(player.GameContext);
+ if (context is null)
+ {
+ await player.InvokeViewPlugInAsync(p => p.ShowMessageAsync("Castle Siege plugin not active.", MessageType.BlueNormal)).ConfigureAwait(false);
+ return;
+ }
+
+ await context.ForcePhaseAsync(phase, DateTime.UtcNow).ConfigureAwait(false);
+ await player.InvokeViewPlugInAsync(p => p.ShowMessageAsync($"Castle Siege: phase set to {phase}.", MessageType.BlueNormal)).ConfigureAwait(false);
+ }
+}
diff --git a/src/GameLogic/PlugIns/ChatCommands/CastleSiegeResetChatCommandPlugIn.cs b/src/GameLogic/PlugIns/ChatCommands/CastleSiegeResetChatCommandPlugIn.cs
new file mode 100644
index 0000000..96e01b5
--- /dev/null
+++ b/src/GameLogic/PlugIns/ChatCommands/CastleSiegeResetChatCommandPlugIn.cs
@@ -0,0 +1,41 @@
+//
+// Licensed under the MIT License. See LICENSE file in the project root for full license information.
+//
+
+namespace MUnique.OpenMU.GameLogic.PlugIns.ChatCommands;
+
+using System.Runtime.InteropServices;
+using MUnique.OpenMU.GameLogic.PlugIns.PeriodicTasks;
+using MUnique.OpenMU.GameLogic.Views;
+using MUnique.OpenMU.Interfaces;
+using MUnique.OpenMU.PlugIns;
+
+/// Resets Castle Siege to the ownership phase and clears registrations. GM only.
+[Guid("A1B2C3D4-0005-4E5F-9A0B-CA5710000005")]
+[PlugIn]
+[Display(Name = "Castle Siege Reset", Description = "GM command: /csreset")]
+[ChatCommandHelp(Command, CharacterStatus.GameMaster)]
+public class CastleSiegeResetChatCommandPlugIn : IChatCommandPlugIn
+{
+ private const string Command = "/csreset";
+
+ ///
+ public string Key => Command;
+
+ ///
+ public CharacterStatus MinCharacterStatusRequirement => CharacterStatus.GameMaster;
+
+ ///
+ public async ValueTask HandleCommandAsync(Player player, string command)
+ {
+ var context = CastleSiegeEventPlugIn.TryGetContext(player.GameContext);
+ if (context is null)
+ {
+ await player.InvokeViewPlugInAsync(p => p.ShowMessageAsync("Castle Siege plugin not active.", MessageType.BlueNormal)).ConfigureAwait(false);
+ return;
+ }
+
+ await context.ResetAsync(DateTime.UtcNow).ConfigureAwait(false);
+ await player.InvokeViewPlugInAsync(p => p.ShowMessageAsync("Castle Siege reset to ownership phase.", MessageType.BlueNormal)).ConfigureAwait(false);
+ }
+}
diff --git a/src/GameLogic/PlugIns/ChatCommands/CastleSiegeSetOwnerChatCommandPlugIn.cs b/src/GameLogic/PlugIns/ChatCommands/CastleSiegeSetOwnerChatCommandPlugIn.cs
new file mode 100644
index 0000000..5f1e4e6
--- /dev/null
+++ b/src/GameLogic/PlugIns/ChatCommands/CastleSiegeSetOwnerChatCommandPlugIn.cs
@@ -0,0 +1,44 @@
+//
+// Licensed under the MIT License. See LICENSE file in the project root for full license information.
+//
+
+namespace MUnique.OpenMU.GameLogic.PlugIns.ChatCommands;
+
+using System.Runtime.InteropServices;
+using MUnique.OpenMU.GameLogic.PlugIns.PeriodicTasks;
+using MUnique.OpenMU.GameLogic.Views;
+using MUnique.OpenMU.Interfaces;
+using MUnique.OpenMU.PlugIns;
+
+/// Sets the Castle Siege owner guild by name. GM only. Usage: /cssetowner GuildName (empty clears).
+[Guid("A1B2C3D4-0004-4E5F-9A0B-CA5710000004")]
+[PlugIn]
+[Display(Name = "Castle Siege Set Owner", Description = "GM command: /cssetowner ")]
+[ChatCommandHelp(Command, CharacterStatus.GameMaster)]
+public class CastleSiegeSetOwnerChatCommandPlugIn : IChatCommandPlugIn
+{
+ private const string Command = "/cssetowner";
+
+ ///
+ public string Key => Command;
+
+ ///
+ public CharacterStatus MinCharacterStatusRequirement => CharacterStatus.GameMaster;
+
+ ///
+ public async ValueTask HandleCommandAsync(Player player, string command)
+ {
+ var parts = command.Split(' ', 2, StringSplitOptions.RemoveEmptyEntries);
+ var owner = parts.Length >= 2 ? parts[1].Trim() : null;
+
+ var context = CastleSiegeEventPlugIn.TryGetContext(player.GameContext);
+ if (context is null)
+ {
+ await player.InvokeViewPlugInAsync(p => p.ShowMessageAsync("Castle Siege plugin not active.", MessageType.BlueNormal)).ConfigureAwait(false);
+ return;
+ }
+
+ context.SetOwner(string.IsNullOrWhiteSpace(owner) ? null : owner);
+ await player.InvokeViewPlugInAsync(p => p.ShowMessageAsync($"Castle Siege owner set to {owner ?? "(none)"}.", MessageType.BlueNormal)).ConfigureAwait(false);
+ }
+}
diff --git a/src/GameLogic/PlugIns/ChatCommands/CastleSiegeStartChatCommandPlugIn.cs b/src/GameLogic/PlugIns/ChatCommands/CastleSiegeStartChatCommandPlugIn.cs
new file mode 100644
index 0000000..5ddc3fe
--- /dev/null
+++ b/src/GameLogic/PlugIns/ChatCommands/CastleSiegeStartChatCommandPlugIn.cs
@@ -0,0 +1,41 @@
+//
+// Licensed under the MIT License. See LICENSE file in the project root for full license information.
+//
+
+namespace MUnique.OpenMU.GameLogic.PlugIns.ChatCommands;
+
+using System.Runtime.InteropServices;
+using MUnique.OpenMU.GameLogic.PlugIns.PeriodicTasks;
+using MUnique.OpenMU.GameLogic.Views;
+using MUnique.OpenMU.Interfaces;
+using MUnique.OpenMU.PlugIns;
+
+/// Forces the Castle Siege into the registration phase. GM only.
+[Guid("A1B2C3D4-0002-4E5F-9A0B-CA5710000002")]
+[PlugIn]
+[Display(Name = "Castle Siege Start", Description = "GM command: /csstart")]
+[ChatCommandHelp(Command, CharacterStatus.GameMaster)]
+public class CastleSiegeStartChatCommandPlugIn : IChatCommandPlugIn
+{
+ private const string Command = "/csstart";
+
+ ///
+ public string Key => Command;
+
+ ///
+ public CharacterStatus MinCharacterStatusRequirement => CharacterStatus.GameMaster;
+
+ ///
+ public async ValueTask HandleCommandAsync(Player player, string command)
+ {
+ var context = CastleSiegeEventPlugIn.TryGetContext(player.GameContext);
+ if (context is null)
+ {
+ await player.InvokeViewPlugInAsync(p => p.ShowMessageAsync("Castle Siege plugin not active.", MessageType.BlueNormal)).ConfigureAwait(false);
+ return;
+ }
+
+ await context.ForceStartRegistrationAsync(DateTime.UtcNow).ConfigureAwait(false);
+ await player.InvokeViewPlugInAsync(p => p.ShowMessageAsync("Castle Siege: registration started.", MessageType.BlueNormal)).ConfigureAwait(false);
+ }
+}
diff --git a/src/GameLogic/PlugIns/ChatCommands/CastleSiegeStatusChatCommandPlugIn.cs b/src/GameLogic/PlugIns/ChatCommands/CastleSiegeStatusChatCommandPlugIn.cs
new file mode 100644
index 0000000..566125f
--- /dev/null
+++ b/src/GameLogic/PlugIns/ChatCommands/CastleSiegeStatusChatCommandPlugIn.cs
@@ -0,0 +1,35 @@
+//
+// Licensed under the MIT License. See LICENSE file in the project root for full license information.
+//
+
+namespace MUnique.OpenMU.GameLogic.PlugIns.ChatCommands;
+
+using System.Runtime.InteropServices;
+using MUnique.OpenMU.GameLogic.PlugIns.PeriodicTasks;
+using MUnique.OpenMU.GameLogic.Views;
+using MUnique.OpenMU.Interfaces;
+using MUnique.OpenMU.PlugIns;
+
+/// Shows the current Castle Siege status. GM only.
+[Guid("A1B2C3D4-0001-4E5F-9A0B-CA5710000001")]
+[PlugIn]
+[Display(Name = "Castle Siege Status", Description = "GM command: /csstatus")]
+[ChatCommandHelp(Command, CharacterStatus.GameMaster)]
+public class CastleSiegeStatusChatCommandPlugIn : IChatCommandPlugIn
+{
+ private const string Command = "/csstatus";
+
+ ///
+ public string Key => Command;
+
+ ///
+ public CharacterStatus MinCharacterStatusRequirement => CharacterStatus.GameMaster;
+
+ ///
+ public async ValueTask HandleCommandAsync(Player player, string command)
+ {
+ var context = CastleSiegeEventPlugIn.TryGetContext(player.GameContext);
+ var text = context?.GetStatusText() ?? "Castle Siege plugin not active.";
+ await player.InvokeViewPlugInAsync(p => p.ShowMessageAsync(text, MessageType.BlueNormal)).ConfigureAwait(false);
+ }
+}