diff --git a/src/GameLogic/CastleSiege/CastleSiegeConfiguration.cs b/src/GameLogic/CastleSiege/CastleSiegeConfiguration.cs index 4a54510..dfe54a0 100644 --- a/src/GameLogic/CastleSiege/CastleSiegeConfiguration.cs +++ b/src/GameLogic/CastleSiege/CastleSiegeConfiguration.cs @@ -87,6 +87,13 @@ public class CastleSiegeConfiguration /// public int RegistrationFee { get; set; } = 100000; + /// + /// Gets or sets the game server (SW) id on which the Castle Siege runs. With multiple game servers each + /// has its own map instances, so the siege must happen on ONE designated server; the others skip it (they + /// only honor the shared castle owner for the rewards). Players must be on this server to take part. + /// + public byte CastleSiegeServerId { get; set; } + // --- Runtime fields (hidden from the AdminPanel; proxied by the friendly properties above) --- /// Gets or sets the days of week registration auto-opens (UTC). Empty = every day. diff --git a/src/GameLogic/CastleSiege/CastleSiegeContext.cs b/src/GameLogic/CastleSiege/CastleSiegeContext.cs index 77361eb..4a5ef2e 100644 --- a/src/GameLogic/CastleSiege/CastleSiegeContext.cs +++ b/src/GameLogic/CastleSiege/CastleSiegeContext.cs @@ -158,6 +158,16 @@ public class CastleSiegeContext this._dirty = true; } + /// + /// Mirrors the shared castle owner from the configuration. Used on game servers that do NOT host the + /// siege, so their hunting-map gate and castle flag still reflect the current owner. Does not mark the + /// state dirty (these servers never persist). + /// + public void SyncOwnerFromConfig() + { + this.OwnerGuildName = this.Configuration.PersistedOwnerGuildName; + } + /// /// Sets the weekly auto-schedule (days of week + UTC time) into the configuration and marks the state /// dirty for persistence. Empty days disables auto-start (manual only). The configuration is the single diff --git a/src/GameLogic/CastleSiege/CastleSiegeGuardsmanTalkPlugIn.cs b/src/GameLogic/CastleSiege/CastleSiegeGuardsmanTalkPlugIn.cs index 962a47b..a9e5a48 100644 --- a/src/GameLogic/CastleSiege/CastleSiegeGuardsmanTalkPlugIn.cs +++ b/src/GameLogic/CastleSiege/CastleSiegeGuardsmanTalkPlugIn.cs @@ -43,6 +43,12 @@ public class CastleSiegeGuardsmanTalkPlugIn : IPlayerTalkToNpcPlugIn return; } + if (!CastleSiegeEventPlugIn.IsCastleSiegeServer(player.GameContext)) + { + await ShowAsync(player, $"The Castle Siege runs on server {context.Configuration.CastleSiegeServerId}. Switch to that server to register and fight.").ConfigureAwait(false); + return; + } + if (context.Phase != CastleSiegePhase.Registration) { await ShowAsync(player, "Castle Siege registration is not open right now.").ConfigureAwait(false); diff --git a/src/GameLogic/PlugIns/PeriodicTasks/CastleSiegeEventPlugIn.cs b/src/GameLogic/PlugIns/PeriodicTasks/CastleSiegeEventPlugIn.cs index 54823dc..93c65d3 100644 --- a/src/GameLogic/PlugIns/PeriodicTasks/CastleSiegeEventPlugIn.cs +++ b/src/GameLogic/PlugIns/PeriodicTasks/CastleSiegeEventPlugIn.cs @@ -83,6 +83,22 @@ public sealed class CastleSiegeEventPlugIn : IPeriodicTaskPlugIn, ISupportCustom public static CastleSiegeContext? TryGetContext(IGameContext gameContext) => Contexts.TryGetValue(gameContext, out var context) ? context : null; + /// + /// Returns whether the given game context is the designated Castle Siege server (the siege and registration + /// run only there). Non-server contexts (e.g. tests) or an uninitialized context count as the siege server. + /// + /// The game context. + public static bool IsCastleSiegeServer(IGameContext gameContext) + { + if (gameContext is not IGameServerContext serverContext + || TryGetContext(gameContext) is not { } context) + { + return true; + } + + return serverContext.Id == context.Configuration.CastleSiegeServerId; + } + /// public object CreateDefaultConfig() => new CastleSiegeConfiguration(); @@ -110,6 +126,20 @@ public sealed class CastleSiegeEventPlugIn : IPeriodicTaskPlugIn, ISupportCustom context.UpdateConfiguration(liveConfig); } + // With multiple game servers each has its own map instances, so the siege must run on ONE designated + // server (CastleSiegeServerId). Other servers skip the siege entirely — they only mirror the shared + // castle owner from the config so the hunting-map gate + castle flag rewards still work everywhere. + if (!IsCastleSiegeServer(gameContext)) + { + context.SyncOwnerFromConfig(); + if (DateTime.UtcNow.Second % 15 == 0) + { + await this.BroadcastCastleFlagAsync(gameContext, context).ConfigureAwait(false); + } + + return; + } + await context.TickAsync(DateTime.UtcNow).ConfigureAwait(false); // During the siege, evaluate the Crown Switches (held by standing on them) and the throne capture every tick.