add: MessageBus
This commit is contained in:
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
22
src/Agents/KnowledgeRender.cs
Normal file
22
src/Agents/KnowledgeRender.cs
Normal 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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
|
||||
}
|
||||
|
||||
42
src/DataStructures/EventDictionary.cs
Normal file
42
src/DataStructures/EventDictionary.cs
Normal 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;
|
||||
|
||||
}
|
||||
12
src/Extensions/MessageClientExtension.cs
Normal file
12
src/Extensions/MessageClientExtension.cs
Normal 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)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
15
src/Interfaces/IMessageClient.cs
Normal file
15
src/Interfaces/IMessageClient.cs
Normal 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);
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
using Polonium.MessageManager;
|
||||
using Polonium.Resources;
|
||||
|
||||
namespace Polonium.Interfaces;
|
||||
|
||||
public interface IPoloniumFactory
|
||||
{
|
||||
public SignalHeader CreateSignalHeader();
|
||||
public MessageHeader CreateMessageHeader();
|
||||
}
|
||||
61
src/MessageManager/MessageBus.cs
Normal file
61
src/MessageManager/MessageBus.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
12
src/MessageManager/MessageHeader.cs
Normal file
12
src/MessageManager/MessageHeader.cs
Normal 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;
|
||||
}
|
||||
8
src/MessageManager/PoloniumMessage.cs
Normal file
8
src/MessageManager/PoloniumMessage.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
using Godot;
|
||||
|
||||
namespace Polonium.MessageManager;
|
||||
|
||||
public abstract partial class PoloniumMessage : Resource
|
||||
{
|
||||
public MessageHeader Header { get; set; }
|
||||
}
|
||||
@@ -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()
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user