UnrealEngineDeepDive icon indicating copy to clipboard operation
UnrealEngineDeepDive copied to clipboard

very good content

Open KGB1st opened this issue 1 year ago • 5 comments

are you already working on this paper?

KGB1st avatar Feb 21 '24 07:02 KGB1st

Thank you, this encouraged me to add some more content to this:

https://github.com/SyedAman/UnrealEngineDeepDive/commit/2d3da4d30245e98f8499a316d41781398055a22e

I am working on my own game engine currently and have not been able to fill this out as much, but I will try to add to this. Is there any part you are particularly interested in?

islamhaqq avatar Feb 23 '24 03:02 islamhaqq

I'm interests in unreal engine architecture and more detail exploration how it works on real examples. I know a little bit about standalone classes such as game-play, state, pawn, etc.. but I need to get more information about plugin development. For example, I develop someapp and I not sure that code share its a good idea for my project. So I need know how I can write API for my plugin developers which can access to my main features in my app via that API in the runtime.

Running ue5App::API_Interface <=> Custom_Plugin (without direct accessing to ue5App src code, only via API methods based on mainApp signatures)

P.S. I'm not pro in C++, but I thinking it would be working.

For example this is custom plugin with event handler (where msgId arrowed to the signature from main call stack):

register_event(EventMsgId_or_SomethingElse:"ue5_Main_App_Event_client_connected", f():callbackF, Boolean:post_or_pre_hook_method)

// .. other stuff

public TheCustomPlugin::callbackF(const int msgId, ... other_params ) {
  // Do plugin logics based on msg signature
  //  Like msg with 98 id structure:
  //  1 param - userid
  //  2 param - hash
  //  and much more..
  // 
  // And return code (0 - continue base event without changes, 1 - handled but return 0 for base event, 2 - override event main params in source call stack)
}

p.s.s. looks like I need to share memory pointers to some main app structures )))

KGB1st avatar Feb 25 '24 12:02 KGB1st

Here are two solutions.

In your ue5 app:

// IAPI_Interface.h
#pragma once

#include "IAPI_Interface.generated.h"

UINTERFACE(MinimalAPI)
class UAPI_Interface : public UInterface
{
    GENERATED_BODY()
}

class IAPI_Interface
{
    GENERATED_BODY()

public:
    void RegisterClientConnectedEvent(const FName& EventName, const FDelegateHandle& DelegateHandle);
    void TriggerClientConnectedEvent(const FName& EventName, int32 ClientId, const FString& ClientInfo);
};
//MyAPIManager.h
#pragma once

#include "CoreMinimal.h"
#include "IAPI_Interface.h"
#include "MyAPIManager.generated.h"

UCLASS()
class UMyAPIManager : public UObject, public IAPI_Interface
{
    GENERATED_BODY()

public:
    TMap<FName, FClientConnectedDelegate> ClientConnectedEventMap;
    void RegisterClientConnectedEvent(const FName& EventName, const FDelegateHandle& DelegateHandle);
    void TriggerClientConnectedEvent(const FName& EventName, int32 ClientId, const FString& ClientInfo);
};
//MyAPIManager.cpp
#include "MyAPIManager.h"

void UMyAPIManager::RegisterClientConnectedEvent(const FName& EventName, const FDelegateHandle& DelegateHandle)
{
    if (!ClientConnectedEventMap.Contains(EventName)) {
        ClientConnectedEventMap.Add(EventName, FClientConnectedDelegate());
    }
    ClientConnectedEventMap[EventName].Add(DelegateHandle);
}

void UMyAPIManager::TriggerClientConnectedEvent(const FName& EventName, int32 ClientId, const FString& ClientInfo)
{
    if (ClientConnectedEventMap.Contains(EventName)) {
        ClientConnectedEventMap[EventName].Broadcast(ClientId, ClientInfo);
    }
}

PLUGIN

#include "PluginModule.h"
#include "Interfaces/IAPI_Interface.h"

void FPluginModule::StartupModule()
{
    // Locate the API interface in the main application
    UMyAPIManager* APIManager = FindObject<UMyAPIManager>(ANY_PACKAGE, TEXT("MyAPIManager"), true);
    if (APIManager) {
        // Register to listen for the 'ClientConnected' event
        FClientConnectedDelegate::FDelegate ClientConnectedDelegate = FClientConnectedDelegate::FDelegate::CreateRaw(this, &FPluginModule::HandleClientConnected);
        FDelegateHandle DelegateHandle = APIManager->ClientConnectedEventMap.Add(FName("ClientConnected"), ClientConnectedDelegate);
    }
}

void FPluginModule::ShutdownModule()
{
    // Unregistering events, if necessary, would go here
}

void FPluginModule::HandleClientConnected(int32 ClientId, const FString& ClientInfo)
{
    UE_LOG(LogTemp, Log, TEXT("Client %d connected with info: %s"), ClientId, *ClientInfo);
}

Alternative solution that uses less Unreal Engine - specific syntax

// UE5App_API_Interface.h
#pragma once

#include "CoreMinimal.h"
#include "Modules/ModuleManager.h"

class IUE5AppAPIInterface
{
public:
    virtual ~IUE5AppAPIInterface() = default;

    virtual void RegisterEvent(FString EventName, std::function<void(int, TArray<FString>)> Callback, bool bPostEvent) = 0;
    virtual void UnregisterEvent(FString EventName) = 0;
};

// Expose the interface to other modules
extern "C" UE5APP_API IUE5AppAPIInterface* GetUE5AppAPI();


// UE5App_API_Interface.cpp (Implementation in main app)
#include "UE5App_API_Interface.h"

class FUE5AppAPI : public IUE5AppAPIInterface
{
public:
    virtual void RegisterEvent(FString EventName, std::function<void(int, TArray<FString>)> Callback, bool bPostEvent) override
    {
        // Implementation to register the event and callback in the main app
    }

    virtual void UnregisterEvent(FString EventName) override
    {
        // unregister the event
    }
};

// Global instance
static FUE5AppAPI UE5AppAPIInstance;

// Exported function
extern "C" UE5APP_API IUE5AppAPIInterface* GetUE5AppAPI()
{
    return &UE5AppAPIInstance;
}


// CustomPlugin.cpp (Usage in custom plugin)
#include "UE5App_API_Interface.h"

class FCustomPlugin
{
public:
    FCustomPlugin()
    {
        // Get the API interface from the main app
        IUE5AppAPIInterface* API = GetUE5AppAPI();
        if(API)
        {
            API->RegisterEvent(TEXT("ue5_Main_App_Event_client_connected"), 
                [this](int MsgId, TArray<FString> Params)
                {
                    this->EventHandler(MsgId, Params);
                }, 
                false);
        }
    }

    ~FCustomPlugin()
    {
        IUE5AppAPIInterface* API = GetUE5AppAPI();
        if(API)
        {
            API->UnregisterEvent(TEXT("ue5_Main_App_Event_client_connected"));
        }
    }

    void EventHandler(int MsgId, TArray<FString> Params)
    {
        // Handle the event, e.g. log message or manipulate parameters
        if(MsgId == 98)
        {
            // Custom logic for message with ID 98
        }
    }
};

// Declare a global instance of the plugin to ensure its constructor is called
static FCustomPlugin CustomPluginInstance;

If this makes sense for you, I might add it to the paper as example code for using Plugins

islamhaqq avatar Feb 25 '24 14:02 islamhaqq

Did that work for you? @KGB1st

islamhaqq avatar Mar 01 '24 18:03 islamhaqq

Hi , Appreciate your work ☺️☺️. It's a request to add some more information about unreal engine architecture, some of the information you have not added.

PhoenixDigitalFX avatar Jun 23 '24 09:06 PhoenixDigitalFX