Recently, I’ve been working on prototyping a multiplayer game in UE5 and have gained a solid understanding of Unreal’s multiplayer code. As I move forward with developing the game, I want to ensure that my code is well-structured and easy to read. However, I’ve found that UE can be limiting when it comes to structuring my code the way I want.
Specifically, I’m trying to avoid having my Actor, Pawn, or Character classes become overly complex and difficult to manage. Instead, I want to split up the logic and behavior into smaller, more concise classes that are responsible for performing specific tasks. While components can be helpful for this purpose, they’re not always the best solution.
Currently, I’m facing an issue with defining RPCs on custom UObject classes. To explain further, I have a pawn that can exhibit different networked behavior based on its type. To prevent the Pawn class from becoming overloaded with different RPC functions, I want to define the RPCs in their different respective classes for better readability. To achieve this, I’ve created a base class that inherits from UObject and attempts to set up the necessary code for enabling RPCs for child classes. However, because RPCs must be called on their owning Actors, I’ve taken the following approach:
I override the following function to essentialy route it through the Actor.
bool CallRemoteFunction(UFunction* Function, void* Parameters, FOutParmRec* OutParms, FFrame* Stack)
{
AActor* actor = Cast<AActor>(GetOuter());
check(actor);
UNetDriver* netDriver = actor->GetNetDriver();
if (!netDriver->ShouldReplicateFunction(mActor, Function))
{
return false;
}
netDriver->ProcessRemoteFunction(mActor, Function, Parameters, OutParms, Stack, this);
return true;
}
I also override this function to use the Actor’s function callspace.
// Use the owning actor's logic
int32 GetFunctionCallspace(UFunction* Function, FFrame* Stack) override
{
AActor* actor = Cast<AActor>(GetOuter());
check(actor);
return actor->GetFunctionCallspace(Function, Stack);
}
Then I define an RPC in the UObject class which will be called from an internal function that is called from the owning actor.
UFUNCTION(Server, Unreliable)
void TestRPCFunc()
{
UE_LOG(LogTemp, Warning, TEXT("Test RPC"));
}
I’m attempting to route everything through the owning actor, but this doesn’t work for some reason. When jumping through the callstack, I noticed that the NetDriver’s FClassNetCache doesn’t contain an FFieldNetCache that corresponds to the RPC function (Found in UNetDriver::InternalProcessRemoteFunctionPrivate). By the looks of it, some extra setup work needs to be done in order to setup the object’s NetCache.
Any idea on how to properly set something like this up? Also, if anyone has any tips to avoid long inheritance hierarchy chains and god classes in unreal, I’d love to hear what you have to say.
Thanks!