From 684763f0ccd5be399f1fc97057451919a36c27cc Mon Sep 17 00:00:00 2001 From: hzhang Date: Mon, 3 Mar 2025 11:08:25 +0000 Subject: [PATCH] add: MessageBus --- src/Agents/Agent.cs | 22 +++------ src/Agents/Knowledge.cs | 16 +++++-- src/Agents/KnowledgeRender.cs | 22 +++++++++ src/Agents/World.cs | 26 ++++------ src/Agents/WorldModel.cs | 11 ++--- src/DataStructures/EventDictionary.cs | 42 ++++++++++++++++ src/Extensions/MessageClientExtension.cs | 12 +++++ src/Interfaces/IMessageClient.cs | 15 ++++++ src/Interfaces/IPoloniumFactory.cs | 3 +- src/MessageManager/MessageBus.cs | 61 ++++++++++++++++++++++++ src/MessageManager/MessageHeader.cs | 12 +++++ src/MessageManager/PoloniumMessage.cs | 8 ++++ src/PoloniumRegistry.cs | 3 ++ src/Resources/KnowledgePatch.cs | 7 +-- src/Resources/SignalHeader.cs | 15 ------ 15 files changed, 211 insertions(+), 64 deletions(-) create mode 100644 src/Agents/KnowledgeRender.cs create mode 100644 src/DataStructures/EventDictionary.cs create mode 100644 src/Extensions/MessageClientExtension.cs create mode 100644 src/Interfaces/IMessageClient.cs create mode 100644 src/MessageManager/MessageBus.cs create mode 100644 src/MessageManager/MessageHeader.cs create mode 100644 src/MessageManager/PoloniumMessage.cs delete mode 100644 src/Resources/SignalHeader.cs diff --git a/src/Agents/Agent.cs b/src/Agents/Agent.cs index da84ea1..43ed975 100644 --- a/src/Agents/Agent.cs +++ b/src/Agents/Agent.cs @@ -6,15 +6,12 @@ namespace Polonium.Agents; public abstract partial class Agent : Node { - [Signal] - public delegate void ActionExecutedEventHandler(SignalHeader header, ActionBody actionBody); - protected Knowledge Knowledge { get; set; } + public Knowledge Knowledge { get; set; } public ActionSet ActionSet { get; set; } public virtual void __Ready() { - Knowledge = GetNode("Knowledge"); - ActionSet = GetNode("ActionSet"); + } public sealed override void _Ready() @@ -25,21 +22,16 @@ public abstract partial class Agent : Node base._Ready(); } - public void UpdateKnowledge(KnowledgePatch update) => update.Patch(Knowledge); - - public virtual void ExecuteAction(ActionBody actionBody) - { - SignalHeader header = PoloniumRegistry.Instance.PoloniumFactory.CreateSignalHeader(); - header.SetSender(this); - EmitSignalActionExecuted(header, actionBody); - } - public void Act(AgentAction.Parameter parameter) where TAction : AgentAction { AgentAction.Template template = ActionSet.Templates .FirstOrDefault(t => t.ActionName == PoloniumRegistry.Action.Name); - + if(template is null) + return; + AgentAction act = template.Get; + act.SetParameter(parameter); + act.Execute(); } diff --git a/src/Agents/Knowledge.cs b/src/Agents/Knowledge.cs index 335d6f9..bf20ff6 100644 --- a/src/Agents/Knowledge.cs +++ b/src/Agents/Knowledge.cs @@ -1,12 +1,22 @@ using Godot; +using Polonium.Interfaces; +using Polonium.MessageManager; using Polonium.Resources; namespace Polonium.Agents; -public abstract partial class Knowledge : Node +public abstract partial class Knowledge : Node, IMessageClient { - [Signal] - public delegate void KnowledgeUpdatedEventHandler(KnowledgePatch update); public abstract void UpdateKnowledge(KnowledgePatch update); + + public string PostCode { get; set; } + public virtual void ReceiveMessage(PoloniumMessage msg) + { + if(msg is KnowledgePatch update) + UpdateKnowledge(update); + } + + public event IMessageClient.MessageSentEventHandler MessageSent; + public void SendMessage(PoloniumMessage msg) => MessageSent?.Invoke(msg); } diff --git a/src/Agents/KnowledgeRender.cs b/src/Agents/KnowledgeRender.cs new file mode 100644 index 0000000..1447dde --- /dev/null +++ b/src/Agents/KnowledgeRender.cs @@ -0,0 +1,22 @@ +using Godot; +using Polonium.Interfaces; +using Polonium.MessageManager; +using Polonium.Resources; + +namespace Polonium.Agents; + +public abstract partial class KnowledgeRender : Node, IMessageClient +{ + public abstract void RenderKnowledgePatch(KnowledgePatch update); + public string PostCode { get; set; } + public virtual void ReceiveMessage(PoloniumMessage msg) + { + if(msg is KnowledgePatch p) + RenderKnowledgePatch(p); + } + + public event IMessageClient.MessageSentEventHandler MessageSent; + public void SendMessage(PoloniumMessage msg) => MessageSent?.Invoke(msg); +} + + diff --git a/src/Agents/World.cs b/src/Agents/World.cs index 53e6c01..e549b97 100644 --- a/src/Agents/World.cs +++ b/src/Agents/World.cs @@ -1,27 +1,27 @@ -using Godot; -using Polonium.Resources; +using Polonium.MessageManager; namespace Polonium.Agents; -public abstract partial class World : Node +public abstract partial class World : MessageBus { protected WorldModel Model { get; set; } - public HashSet Agents { get; } = new(); public Knowledge CommonKnowledge { get; set; } + public override void _Ready() { Model = GetNode("WorldModel"); + Model.WorldKnowledge.PostCode = "WorldKnowledge"; + Register(Model.WorldKnowledge); CommonKnowledge = GetNode("CommonKnowledge"); - Model.CommonKnowledgeUpdated += CommonKnowledgeUpdated; - Model.PrivateKnowledgeUpdated += ForwardPrivateKnowledgeUpdated; + CommonKnowledge.PostCode = "CommonKnowledge"; + Register(CommonKnowledge); base._Ready(); } - public void RegisterAgent(Agent agent) + public void Register(Agent agent) { - Agents.Add(agent); - agent.ActionExecuted += Model.ActionExecuted; + Register(agent.Knowledge); } public virtual void Enter() @@ -34,12 +34,4 @@ public abstract partial class World : Node PoloniumRegistry.Instance.CurrentWorld = null; } - public void CommonKnowledgeUpdated(KnowledgePatch update) => update.Patch(CommonKnowledge); - - public void ForwardPrivateKnowledgeUpdated(SignalHeader header, KnowledgePatch update) - { - foreach (Agent receiver in header.ResolveReceivers()) - receiver.UpdateKnowledge(update); - } - } \ No newline at end of file diff --git a/src/Agents/WorldModel.cs b/src/Agents/WorldModel.cs index d578347..d287e33 100644 --- a/src/Agents/WorldModel.cs +++ b/src/Agents/WorldModel.cs @@ -5,18 +5,13 @@ namespace Polonium.Agents; public abstract partial class WorldModel : Node { - - [Signal] - public delegate void CommonKnowledgeUpdatedEventHandler(KnowledgePatch update); - [Signal] - public delegate void PrivateKnowledgeUpdatedEventHandler(SignalHeader header, KnowledgePatch update); - - protected Knowledge WorldKnowledge { get; set; } + public Knowledge WorldKnowledge { get; set; } public override void _Ready() { WorldKnowledge = GetNode("WorldKnowledge"); base._Ready(); } - public abstract void ActionExecuted(SignalHeader header, ActionBody actionBody); + + } diff --git a/src/DataStructures/EventDictionary.cs b/src/DataStructures/EventDictionary.cs new file mode 100644 index 0000000..b7978df --- /dev/null +++ b/src/DataStructures/EventDictionary.cs @@ -0,0 +1,42 @@ +namespace Polonium.DataStructures; + +public class EventDictionary : Dictionary +{ + public new TValue this[TKey a] + { + get => base[a]; + set + { + base[a] = value; + Edited?.Invoke(a, value); + Updated?.Invoke(a); + } + } + + public new void Remove(TKey key) + { + base.Remove(key); + Removed?.Invoke(key); + Updated?.Invoke(key); + } + + public new void Clear() + { + base.Clear(); + Cleared?.Invoke(); + } + + + public delegate void EditedEventHandler(TKey key, TValue value); + public event EditedEventHandler Edited; + + public delegate void RemovedEventHandler(TKey key); + public event RemovedEventHandler Removed; + + public delegate void ClearedEventHandler(); + public event ClearedEventHandler Cleared; + + public delegate void UpdatedEventHandler(TKey key); + public event UpdatedEventHandler Updated; + +} diff --git a/src/Extensions/MessageClientExtension.cs b/src/Extensions/MessageClientExtension.cs new file mode 100644 index 0000000..89fa4c2 --- /dev/null +++ b/src/Extensions/MessageClientExtension.cs @@ -0,0 +1,12 @@ +using Polonium.Interfaces; +using Polonium.MessageManager; + +namespace Polonium.Extensions; + +public static class MessageClientExtension +{ + public static void SendMessage(this IMessageClient client, PoloniumMessage msg) + { + + } +} diff --git a/src/Interfaces/IMessageClient.cs b/src/Interfaces/IMessageClient.cs new file mode 100644 index 0000000..a09c35c --- /dev/null +++ b/src/Interfaces/IMessageClient.cs @@ -0,0 +1,15 @@ +using Godot; +using Polonium.MessageManager; + +namespace Polonium.Interfaces; + +public interface IMessageClient +{ + string PostCode { get; set; } + void ReceiveMessage(PoloniumMessage msg); + + delegate void MessageSentEventHandler(PoloniumMessage msg); + event MessageSentEventHandler MessageSent; + + public void SendMessage(PoloniumMessage msg); +} diff --git a/src/Interfaces/IPoloniumFactory.cs b/src/Interfaces/IPoloniumFactory.cs index f3c5e24..0418f20 100644 --- a/src/Interfaces/IPoloniumFactory.cs +++ b/src/Interfaces/IPoloniumFactory.cs @@ -1,8 +1,9 @@ +using Polonium.MessageManager; using Polonium.Resources; namespace Polonium.Interfaces; public interface IPoloniumFactory { - public SignalHeader CreateSignalHeader(); + public MessageHeader CreateMessageHeader(); } \ No newline at end of file diff --git a/src/MessageManager/MessageBus.cs b/src/MessageManager/MessageBus.cs new file mode 100644 index 0000000..e98a189 --- /dev/null +++ b/src/MessageManager/MessageBus.cs @@ -0,0 +1,61 @@ +using Godot; +using Polonium.Interfaces; + +namespace Polonium.MessageManager; + +public abstract partial class MessageBus : Node +{ + public Dictionary> Clients { get; } = new(); + + public abstract string NewPostCode(); + + public void Register(IMessageClient client) + { + if(!Clients.ContainsKey(client.PostCode)) + Clients[client.PostCode] = new HashSet(); + Clients[client.PostCode].Add(client); + client.MessageSent += Collect; + } + public Queue Messages { get; } = new(); + public Queue DeadMessages { get; } = new(); + public void Collect(PoloniumMessage message) => Messages.Enqueue(message); + + public override void _Ready() + { + PoloniumRegistry.Instance.MessageBus = this; + base._Ready(); + } + + public override void _Process(double delta) + { + if(DeadMessages.Count > 0) + Messages.Enqueue(DeadMessages.Dequeue()); + if (Messages.Count == 0) + return; + PoloniumMessage msg = Messages.Dequeue(); + HashSet errored = new(); + foreach (string postcode in msg.Header.Receivers) + { + + try + { + foreach(IMessageClient client in Clients[postcode]) + client.ReceiveMessage(msg); + } + catch (Exception e) + { + errored.Add(postcode); + } + } + if (errored.Count > 0) + { + msg.Header.Retried++; + if(msg.Header.Retried <= msg.Header.MaxRetries) + { + msg.Header.Receivers = errored; + DeadMessages.Enqueue(msg); + } + } + } + +} diff --git a/src/MessageManager/MessageHeader.cs b/src/MessageManager/MessageHeader.cs new file mode 100644 index 0000000..7d03376 --- /dev/null +++ b/src/MessageManager/MessageHeader.cs @@ -0,0 +1,12 @@ +using Godot; + +namespace Polonium.MessageManager; + +public abstract partial class MessageHeader : Resource +{ + public HashSet Receivers { get; set; } + public string Sender { get; set; } + + public int Retried { get; set; } = 0; + public int MaxRetries { get; set; } = 0; +} diff --git a/src/MessageManager/PoloniumMessage.cs b/src/MessageManager/PoloniumMessage.cs new file mode 100644 index 0000000..82ce22f --- /dev/null +++ b/src/MessageManager/PoloniumMessage.cs @@ -0,0 +1,8 @@ +using Godot; + +namespace Polonium.MessageManager; + +public abstract partial class PoloniumMessage : Resource +{ + public MessageHeader Header { get; set; } +} \ No newline at end of file diff --git a/src/PoloniumRegistry.cs b/src/PoloniumRegistry.cs index 0f7a386..7f4a1e2 100644 --- a/src/PoloniumRegistry.cs +++ b/src/PoloniumRegistry.cs @@ -2,6 +2,7 @@ using Godot; using Polonium.Agents; using Polonium.Attributes; using Polonium.Interfaces; +using Polonium.MessageManager; using Polonium.Resources; namespace Polonium; @@ -19,6 +20,8 @@ public class PoloniumRegistry [RegistryPassThrough] public IPoloniumFactory PoloniumFactory { get; set; } + [RegistryPassThrough] + public MessageBus MessageBus { get; set; } public static void Prepare() diff --git a/src/Resources/KnowledgePatch.cs b/src/Resources/KnowledgePatch.cs index b8b20a3..1789799 100644 --- a/src/Resources/KnowledgePatch.cs +++ b/src/Resources/KnowledgePatch.cs @@ -1,11 +1,8 @@ -using Godot; -using Polonium.Agents; +using Polonium.MessageManager; namespace Polonium.Resources; -public abstract partial class KnowledgePatch : Resource +public abstract partial class KnowledgePatch : PoloniumMessage { - public SignalHeader Header { get; set; } - public abstract void Patch(Knowledge knowledge); } \ No newline at end of file diff --git a/src/Resources/SignalHeader.cs b/src/Resources/SignalHeader.cs deleted file mode 100644 index 9189a00..0000000 --- a/src/Resources/SignalHeader.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Godot; -using Polonium.Agents; - -namespace Polonium.Resources; - -public abstract partial class SignalHeader : Resource -{ - - public abstract IEnumerable ResolveReceivers(); - public abstract Agent ResolveSender(); - - public abstract void SetSender(Agent agent); - public abstract void AddReceiver(Agent agent); - -}