diff --git a/src/GameLogic/PlugIns/PeriodicTasks/CastleSiegeEventPlugIn.cs b/src/GameLogic/PlugIns/PeriodicTasks/CastleSiegeEventPlugIn.cs new file mode 100644 index 0000000..d0304f5 --- /dev/null +++ b/src/GameLogic/PlugIns/PeriodicTasks/CastleSiegeEventPlugIn.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.PlugIns.PeriodicTasks; + +using System.Collections.Concurrent; +using System.Runtime.InteropServices; +using MUnique.OpenMU.GameLogic.CastleSiege; +using MUnique.OpenMU.PlugIns; + +/// +/// Drives the Castle Siege phase state machine: ticks it every second and carries its configuration. +/// State is per- and kept in memory (P1: no persistence). +/// +[PlugIn] +[Display(Name = nameof(CastleSiegeEventPlugIn), Description = "Castle Siege event (P1 skeleton: phase state machine + scheduling).")] +[Guid("6E2C8B41-9A4D-4C2E-9E7B-1F2A3B4C5D60")] +public sealed class CastleSiegeEventPlugIn : IPeriodicTaskPlugIn, ISupportCustomConfiguration, ISupportDefaultCustomConfiguration +{ + private static readonly ConcurrentDictionary Contexts = new(); + + /// + public CastleSiegeConfiguration? Configuration { get; set; } + + /// + /// Gets the Castle Siege context for a game context, if the periodic tick has initialized it. + /// + /// The game context. + /// The context, or null if not yet initialized. + /// Static so GM chat commands can reach the state machine without a plugin instance + /// (GetKnownPlugInsOf returns Types, not instances). The tick runs every second, so the + /// context exists within ~1s of server start. + public static CastleSiegeContext? TryGetContext(IGameContext gameContext) + => Contexts.TryGetValue(gameContext, out var context) ? context : null; + + /// + public object CreateDefaultConfig() => new CastleSiegeConfiguration(); + + /// + public async ValueTask ExecuteTaskAsync(GameContext gameContext) + { + var context = Contexts.GetOrAdd(gameContext, _ => new CastleSiegeContext(this.Configuration ?? new CastleSiegeConfiguration())); + await context.TickAsync(DateTime.UtcNow).ConfigureAwait(false); + } + + /// + public void ForceStart() + { + // Force-start applies to every active game context's siege. + foreach (var context in Contexts.Values) + { + _ = context.ForceStartRegistrationAsync(DateTime.UtcNow); + } + } +}