Communication System Specification - wwestlake/Steamforge GitHub Wiki

Communication System Specification

This document specifies the generalized Communication System designed to facilitate dynamic, decoupled communication between game objects in Steamforge.


Overview

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.


Objectives

  • 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.

Key Concepts

Message

A structured unit carrying data between sender and receiver(s).

Channel

Logical messaging endpoint identified by a unique name (string or GUID). Objects send messages to or listen for messages from channels.

Registry

Centralized repository managing registration and discovery of communication channels and associated listeners.


Message Structure

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.

Channel Registry

A singleton class managing active channels and listeners.

Registry Class Definition:

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

Functional Overview:

  • 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.

Communication Modes

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.

Message Broadcasting and Receiving

Broadcasting Example:

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

Receiving Example (Receiver Interface):

UINTERFACE()
class UMessageReceiverInterface : public UInterface
{
    GENERATED_BODY()
};

class IMessageReceiverInterface
{
    GENERATED_BODY()

public:
    virtual void OnMessageReceived(const FGameMessage& Message) = 0;
};

Use Case Example

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

Optimization Considerations

  • Lazy Initialization of channels.
  • Use Weak Pointers (TWeakObjectPtr) to prevent unnecessary object references.
  • Optional Message Batching for reducing update frequency.

Potential Enhancements

  • 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.

⚠️ **GitHub.com Fallback** ⚠️