feat(CS): run the siege on ONE designated server (CastleSiegeServerId)
Some checks failed
.NET Core / build (push) Has been cancelled
Some checks failed
.NET Core / build (push) Has been cancelled
With multiple game servers each has its own map instances, so the siege ran independently on all of them - a guild could win uncontested on an empty server's Valley of Loren. Now the siege (tick/spawn/battle/registration) runs only on the server whose Id == config.CastleSiegeServerId (AdminPanel-editable). Other servers skip the siege and just mirror the shared castle owner from config so the hunting-map gate + castle flag rewards still work everywhere. The Guardsman on non-siege servers tells players which server to switch to.
This commit is contained in:
@@ -87,6 +87,13 @@ public class CastleSiegeConfiguration
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public int RegistrationFee { get; set; } = 100000;
|
public int RegistrationFee { get; set; } = 100000;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 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.
|
||||||
|
/// </summary>
|
||||||
|
public byte CastleSiegeServerId { get; set; }
|
||||||
|
|
||||||
// --- Runtime fields (hidden from the AdminPanel; proxied by the friendly properties above) ---
|
// --- Runtime fields (hidden from the AdminPanel; proxied by the friendly properties above) ---
|
||||||
|
|
||||||
/// <summary>Gets or sets the days of week registration auto-opens (UTC). Empty = every day.</summary>
|
/// <summary>Gets or sets the days of week registration auto-opens (UTC). Empty = every day.</summary>
|
||||||
|
|||||||
@@ -158,6 +158,16 @@ public class CastleSiegeContext
|
|||||||
this._dirty = true;
|
this._dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 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).
|
||||||
|
/// </summary>
|
||||||
|
public void SyncOwnerFromConfig()
|
||||||
|
{
|
||||||
|
this.OwnerGuildName = this.Configuration.PersistedOwnerGuildName;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets the weekly auto-schedule (days of week + UTC time) into the configuration and marks the state
|
/// 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
|
/// dirty for persistence. Empty days disables auto-start (manual only). The configuration is the single
|
||||||
|
|||||||
@@ -43,6 +43,12 @@ public class CastleSiegeGuardsmanTalkPlugIn : IPlayerTalkToNpcPlugIn
|
|||||||
return;
|
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)
|
if (context.Phase != CastleSiegePhase.Registration)
|
||||||
{
|
{
|
||||||
await ShowAsync(player, "Castle Siege registration is not open right now.").ConfigureAwait(false);
|
await ShowAsync(player, "Castle Siege registration is not open right now.").ConfigureAwait(false);
|
||||||
|
|||||||
@@ -83,6 +83,22 @@ public sealed class CastleSiegeEventPlugIn : IPeriodicTaskPlugIn, ISupportCustom
|
|||||||
public static CastleSiegeContext? TryGetContext(IGameContext gameContext)
|
public static CastleSiegeContext? TryGetContext(IGameContext gameContext)
|
||||||
=> Contexts.TryGetValue(gameContext, out var context) ? context : null;
|
=> Contexts.TryGetValue(gameContext, out var context) ? context : null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 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.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="gameContext">The game context.</param>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public object CreateDefaultConfig() => new CastleSiegeConfiguration();
|
public object CreateDefaultConfig() => new CastleSiegeConfiguration();
|
||||||
|
|
||||||
@@ -110,6 +126,20 @@ public sealed class CastleSiegeEventPlugIn : IPeriodicTaskPlugIn, ISupportCustom
|
|||||||
context.UpdateConfiguration(liveConfig);
|
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);
|
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.
|
// During the siege, evaluate the Crown Switches (held by standing on them) and the throne capture every tick.
|
||||||
|
|||||||
Reference in New Issue
Block a user