using System; using System.Linq; using System.Threading.Tasks; namespace GameCult.Networking { /// /// Bridges schema-v0 simulation observation messages into a local observation hub. /// public sealed class CultNetSimulationObservationServer : IDisposable { private readonly Server _server; private readonly CultNetSimulationObservationHub _hub; private readonly Func _observationHandler; private bool _disposed; /// /// Creates and attaches a simulation observation bridge. /// public CultNetSimulationObservationServer( Server server, CultNetSimulationObservationHub hub) { _server = server ?? throw new ArgumentNullException(nameof(server)); _hub = hub ?? throw new ArgumentNullException(nameof(hub)); _observationHandler = HandleObservationAsync; _server.OnCultNet(_observationHandler); } /// /// Gets the observation hub used by this bridge. /// public CultNetSimulationObservationHub Hub => _hub; /// /// Applies an observation message and returns current candidate messages for the same subject. /// public CultNetSimulationConsensusCandidateMessage[] CreateCandidateMessages( CultNetSimulationObservationMessage message) { if (message == null) throw new ArgumentNullException(nameof(message)); return _hub.Submit(message) .Select(candidate => CultNetSimulationConsensusCandidateMessage.FromCandidate( message.MessageId, candidate)) .ToArray(); } /// public void Dispose() { if (_disposed) { return; } _disposed = true; _server.RemoveCultNetMessageListener(_observationHandler); } private Task HandleObservationAsync(CultNetSimulationObservationMessage message, CultNetServerPeer peer) { try { foreach (var candidate in CreateCandidateMessages(message)) { peer.SendCultNet(candidate); } } catch (Exception ex) { peer.SendCultNet(new CultNetErrorMessage { Error = ex.Message }); } return Task.CompletedTask; } } }