add: MessageBus

This commit is contained in:
h z
2025-03-03 11:08:25 +00:00
parent d2f0ab8153
commit 684763f0cc
15 changed files with 211 additions and 64 deletions

View File

@@ -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>("Knowledge");
ActionSet = GetNode<ActionSet>("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<TAction>(AgentAction.Parameter parameter)
where TAction : AgentAction
{
AgentAction.Template template = ActionSet.Templates
.FirstOrDefault(t => t.ActionName == PoloniumRegistry.Action<TAction>.Name);
if(template is null)
return;
AgentAction act = template.Get;
act.SetParameter(parameter);
act.Execute();
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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<Agent> Agents { get; } = new();
public Knowledge CommonKnowledge { get; set; }
public override void _Ready()
{
Model = GetNode<WorldModel>("WorldModel");
Model.WorldKnowledge.PostCode = "WorldKnowledge";
Register(Model.WorldKnowledge);
CommonKnowledge = GetNode<Knowledge>("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);
}
}

View File

@@ -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<Knowledge>("WorldKnowledge");
base._Ready();
}
public abstract void ActionExecuted(SignalHeader header, ActionBody actionBody);
}

View File

@@ -0,0 +1,42 @@
namespace Polonium.DataStructures;
public class EventDictionary<TKey, TValue> : Dictionary<TKey, TValue>
{
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;
}

View File

@@ -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)
{
}
}

View File

@@ -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);
}

View File

@@ -1,8 +1,9 @@
using Polonium.MessageManager;
using Polonium.Resources;
namespace Polonium.Interfaces;
public interface IPoloniumFactory
{
public SignalHeader CreateSignalHeader();
public MessageHeader CreateMessageHeader();
}

View File

@@ -0,0 +1,61 @@
using Godot;
using Polonium.Interfaces;
namespace Polonium.MessageManager;
public abstract partial class MessageBus : Node
{
public Dictionary<string, HashSet<IMessageClient>> Clients { get; } = new();
public abstract string NewPostCode();
public void Register(IMessageClient client)
{
if(!Clients.ContainsKey(client.PostCode))
Clients[client.PostCode] = new HashSet<IMessageClient>();
Clients[client.PostCode].Add(client);
client.MessageSent += Collect;
}
public Queue<PoloniumMessage> Messages { get; } = new();
public Queue<PoloniumMessage> 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<string> 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);
}
}
}
}

View File

@@ -0,0 +1,12 @@
using Godot;
namespace Polonium.MessageManager;
public abstract partial class MessageHeader : Resource
{
public HashSet<string> Receivers { get; set; }
public string Sender { get; set; }
public int Retried { get; set; } = 0;
public int MaxRetries { get; set; } = 0;
}

View File

@@ -0,0 +1,8 @@
using Godot;
namespace Polonium.MessageManager;
public abstract partial class PoloniumMessage : Resource
{
public MessageHeader Header { get; set; }
}

View File

@@ -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()

View File

@@ -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);
}

View File

@@ -1,15 +0,0 @@
using Godot;
using Polonium.Agents;
namespace Polonium.Resources;
public abstract partial class SignalHeader : Resource
{
public abstract IEnumerable<Agent> ResolveReceivers();
public abstract Agent ResolveSender();
public abstract void SetSender(Agent agent);
public abstract void AddReceiver(Agent agent);
}