Communication System Specification - wwestlake/Steamforge GitHub Wiki
This document specifies the generalized Communication System designed to facilitate dynamic, decoupled communication between game objects in Steamforge.
The Communication System enables game objects such as items, NPCs, machinery, spells, and environmental systems to communicate through flexible, dynamically created messaging channels. This ensures loose coupling, scalability, and clear communication modes among various entities.
- Generalization: Allow interaction between any game objects without hard dependencies.
- Performance Efficiency: Maintain minimal overhead to ensure high performance.
- Dynamic Channel Management: Support runtime creation and discovery of messaging channels.
- Flexible Communication Modes: Clearly specify send-only, receive-only, or send-and-receive modes.
A structured unit carrying data between sender and receiver(s).
Logical messaging endpoint identified by a unique name (string or GUID). Objects send messages to or listen for messages from channels.
Centralized repository managing registration and discovery of communication channels and associated listeners.
struct FGameMessage
{
FName ChannelName; // Unique identifier of the target channel
UObject* Sender; // Reference to the sending object
FString MessageType; // Identifier for message classification
TMap<FString, FString> Payload; // Key-value pair data payload
};
Field Definitions:
-
ChannelName
: The name identifying the communication channel. -
Sender
: The object initiating the message. -
MessageType
: Used to distinguish message intent. -
Payload
: Flexible data dictionary for arbitrary message content.
A singleton class managing active channels and listeners.
class UMessageRegistry : public UObject
{
GENERATED_BODY()
private:
TMap<FName, TSet<TWeakObjectPtr<UObject>>> ChannelListeners;
public:
void RegisterListener(FName Channel, UObject* Listener);
void UnregisterListener(FName Channel, UObject* Listener);
TSet<TWeakObjectPtr<UObject>> GetListeners(FName Channel) const;
};
- RegisterListener: Adds an object as a listener to the specified channel.
- UnregisterListener: Removes an object from the specified channel.
- GetListeners: Retrieves all listeners associated with a specific channel.
Objects clearly specify their mode of interaction:
Mode | Description |
---|---|
Send-only | Object can only send messages. |
Receive-only | Object can only receive messages. |
Send-and-Receive | Object can both send and receive messages. |
void IMessageSender::SendMessage(const FGameMessage& Message)
{
auto& Registry = GetMessageRegistrySingleton();
auto Listeners = Registry.GetListeners(Message.ChannelName);
for (auto& ListenerWeakPtr : Listeners)
{
if (ListenerWeakPtr.IsValid())
{
auto Listener = ListenerWeakPtr.Get();
IMessageReceiverInterface* Receiver = Cast<IMessageReceiverInterface>(Listener);
if (Receiver)
{
Receiver->OnMessageReceived(Message);
}
}
}
}
UINTERFACE()
class UMessageReceiverInterface : public UInterface
{
GENERATED_BODY()
};
class IMessageReceiverInterface
{
GENERATED_BODY()
public:
virtual void OnMessageReceived(const FGameMessage& Message) = 0;
};
Scenario:
A magical crystal (MagicCrystal
) sends magical energy pulses. Nearby steam-powered lights (SteamPoweredLight
) illuminate when receiving a pulse.
// MagicCrystal.cpp (Sender Example)
void AMagicCrystal::EmitMagicPulse()
{
FGameMessage PulseMessage;
PulseMessage.ChannelName = "MagicEnergy";
PulseMessage.Sender = this;
PulseMessage.MessageType = "Pulse";
PulseMessage.Payload.Add("Intensity", "High");
SendMessage(PulseMessage);
}
// SteamPoweredLight.cpp (Receiver Example)
void ASteamPoweredLight::OnMessageReceived(const FGameMessage& Message)
{
if (Message.MessageType == "Pulse" && Message.Payload["Intensity"] == "High")
{
Illuminate();
}
}
void ASteamPoweredLight::BeginPlay()
{
Super::BeginPlay();
GetMessageRegistrySingleton().RegisterListener("MagicEnergy", this);
}
void ASteamPoweredLight::EndPlay(const EEndPlayReason::Type Reason)
{
Super::EndPlay(Reason);
GetMessageRegistrySingleton().UnregisterListener("MagicEnergy", this);
}
- Lazy Initialization of channels.
- Use Weak Pointers (
TWeakObjectPtr
) to prevent unnecessary object references. - Optional Message Batching for reducing update frequency.
- Queued Messages: Schedule messages for delayed dispatch.
- Conditional Filtering: Allow receivers to filter messages.
- Event-driven Binding: Enable specific event handling within channels.
This specification provides the foundation for the Communication System, supporting robust and flexible interactions within the Steamforge game environment.