BridgeTownCrier handles inbound towncrier.add / towncrier.remove, pushing
website-published news into GlobalTownCrierEntryList so every town crier
announces it until it expires. Both run on the Core thread (required: AddEntry
mutates a shared list and the criers send packets). An id maps to the created
TownCrierEntry so a later remove can pull it, and re-adding an id replaces the
prior entry.
Caps are enforced before touching the shared list -- line count, line length,
active-entry count, duration -- as defense in depth on top of the loopback trust
boundary: a buggy or compromised sidecar still cannot flood the criers or pin a
message forever. Config: Bridge.TownCrierMax{Lines,LineLength,Active,DurationSec}.
Verified with a sending stub and a probe that logs the actual crier list. Replies
and game state agree: add n1 -> towncrier.ok and the entry appears with the exact
lines; add n2 (8 lines over the cap of 6) -> towncrier.error and never enters the
list; remove n1 -> towncrier.ok and the entry is gone; remove unknown ->
towncrier.error. Evidence in docs/PLAN.md §16.
Adds BridgeJson.GetStringList for JSON string arrays, tools/stub_sidecar_crier.ps1,
and tools/scaffolding/BridgeCrierProbe.cs. This closes the pure-plugin inbound
work; only the PlayerVendorSale core edit (Phase 7) remains on the ServUO side.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
67 lines
2.5 KiB
C#
67 lines
2.5 KiB
C#
using System;
|
|
|
|
namespace Server.Custom.Bridge
|
|
{
|
|
/// <summary>
|
|
/// Tunables from Config/Bridge.cfg. Key scope is the filename, so `Port=7788` there
|
|
/// reads as "Bridge.Port" here.
|
|
///
|
|
/// Loaded in Configure(), which ScriptCompiler invokes before World.Load.
|
|
/// </summary>
|
|
public static class BridgeConfig
|
|
{
|
|
public static string Host { get; private set; }
|
|
public static int Port { get; private set; }
|
|
public static int QueueCap { get; private set; }
|
|
|
|
public static int StatSweepSeconds { get; private set; }
|
|
public static int DecaySweepSeconds { get; private set; }
|
|
public static int EconomySweepSeconds { get; private set; }
|
|
|
|
public static string LinkUrl { get; private set; }
|
|
|
|
public static int TownCrierMaxLines { get; private set; }
|
|
public static int TownCrierMaxLineLength { get; private set; }
|
|
public static int TownCrierMaxActive { get; private set; }
|
|
public static int TownCrierMaxDurationSec { get; private set; }
|
|
|
|
public static bool Enabled { get; private set; }
|
|
|
|
public static void Configure()
|
|
{
|
|
Load();
|
|
}
|
|
|
|
/// <summary>Re-readable at runtime via `[bridge reload`.</summary>
|
|
public static void Load()
|
|
{
|
|
Enabled = Config.Get("Bridge.Enabled", true);
|
|
|
|
Host = Config.Get("Bridge.Host", "127.0.0.1");
|
|
Port = Config.Get("Bridge.Port", 7788);
|
|
QueueCap = Config.Get("Bridge.QueueCap", 10000);
|
|
|
|
StatSweepSeconds = Config.Get("Bridge.StatSweepSeconds", 30);
|
|
DecaySweepSeconds = Config.Get("Bridge.DecaySweepSeconds", 60);
|
|
EconomySweepSeconds = Config.Get("Bridge.EconomySweepSeconds", 300);
|
|
|
|
LinkUrl = Config.Get("Bridge.LinkUrl", "https://yoursite/link");
|
|
|
|
TownCrierMaxLines = Config.Get("Bridge.TownCrierMaxLines", 6);
|
|
TownCrierMaxLineLength = Config.Get("Bridge.TownCrierMaxLineLength", 200);
|
|
TownCrierMaxActive = Config.Get("Bridge.TownCrierMaxActive", 20);
|
|
TownCrierMaxDurationSec = Config.Get("Bridge.TownCrierMaxDurationSec", 86400);
|
|
|
|
if (QueueCap < 16)
|
|
QueueCap = 16;
|
|
}
|
|
|
|
public static string Describe()
|
|
{
|
|
return String.Format(
|
|
"enabled={0} endpoint={1}:{2} queueCap={3} sweeps(stat={4}s decay={5}s econ={6}s)",
|
|
Enabled, Host, Port, QueueCap, StatSweepSeconds, DecaySweepSeconds, EconomySweepSeconds);
|
|
}
|
|
}
|
|
}
|