channeldorg / channeld-ue-plugin

Enables distributed simulation with Unreal Engine's dedicated servers. 为虚幻引擎专用服务器提供分布式模拟能力的开源插件.
Apache License 2.0
132 stars 38 forks source link

Question about struct FCopy_XXX generation #22

Closed ganibc closed 1 year ago

ganibc commented 1 year ago

Hi, not sure if I should put this in discussion or here. I have question about code generation for struct FCopy_XXXXX. What is the purpose of making FCopy instead of using the original structure? I believe the generation is also problematic since the code generation relies on Reflection system, and UE's reflection system will only recognize the UPROPERTY member variables. For example, this is one of the struct generated for Gameplay Ability System.


struct FCopy_FGameplayAbilitySpec
{
    FCopy_FGameplayAbilitySpecHandle Handle;
TObjectPtr<UGameplayAbility> Ability;
int32 Level;
int32 InputID;
uint8 ActiveCount;
bool InputPressed;
bool RemoveAfterActivation;
bool PendingRemove;
bool bActivateOnce;
FCopy_FGameplayAbilityActivationInfo ActivationInfo;
FCopy_FGameplayTagContainer DynamicAbilityTags;
TArray<TObjectPtr<UGameplayAbility>> NonReplicatedInstances;
TArray<TObjectPtr<UGameplayAbility>> ReplicatedInstances;
FCopy_FActiveGameplayEffectHandle GameplayEffectHandle;

};

But FGameplayAbilitySpec have other variable that are not using any UPROPERTY specifier.

    /** Handle to GE that granted us (usually invalid) */
    UPROPERTY(NotReplicated)
    FActiveGameplayEffectHandle GameplayEffectHandle;

    /** Passed on SetByCaller magnitudes if this ability was granted by a GE */
    TMap<FGameplayTag, float> SetByCallerTagMagnitudes;

Probably it's not an issue in this particular struct since SetByCallerTagMagnitudes is located at the end of the struct, but if it's in between other UPROPERTIES, then it will cause the memory offset for all properties after SetByCallerTagMagnitudes to be wrong, and the value will be invalid/corrupted.

Is my understanding is correct? Am I missing some details?

Augkit commented 1 year ago

The reason why generating FCopy is used to replace the original struct is because the original struct is probably not defined with API_xxx exported module, and the header file of the structure needs to be imported in the generated code.

Augkit commented 1 year ago

About generating properties with UPROPERTY macros only for FCopy. First of all, it should be clear that FCopy is only used for RPC parameters, it is recommended to use channeldUE when using channeldUE to use all C++ defined properties marked with the structure of the UPROPERTY macro or use the Blueprint struct as RPC parameter. However, errors caused by memory offset of properties do occur.

ganibc commented 1 year ago

I see. Basically the FCopy_XX should only apply for RPC params that defined in .gen.cpp file. This make sense, because all RPC params should exists in the Reflection. I guess the generation for non-RPC params struct is a bug.

laughxing commented 1 year ago

About generating properties with UPROPERTY macros only for FCopy. First of all, it should be clear that FCopy is only used for RPC parameters, it is recommended to use channeldUE when using channeldUE to use all C++ defined properties marked with the structure of the UPROPERTY macro or use the Blueprint struct as RPC parameter. However, errors caused by memory offset of properties do occur.

@Augkit why we need to do this, is there any limitation to use the original ue rpc serialization?

Augkit commented 1 year ago

I see. Basically the FCopy_XX should only apply for RPC params that defined in .gen.cpp file. This make sense, because all RPC params should exists in the Reflection. I guess the generation for non-RPC params struct is a bug.

@ganibc At this stage, we treat RPC's parameter structures as the same as the usual structures, so the corresponding generated code needs to have the same capabilities. But the generated code can be confusing, and this will be factored into account when refactoring later.

Augkit commented 1 year ago

About generating properties with UPROPERTY macros only for FCopy. First of all, it should be clear that FCopy is only used for RPC parameters, it is recommended to use channeldUE when using channeldUE to use all C++ defined properties marked with the structure of the UPROPERTY macro or use the Blueprint struct as RPC parameter. However, errors caused by memory offset of properties do occur.

@Augkit why we need to do this, is there any limitation to use the original ue rpc serialization?

@laughxing If we want to use channeld to forward RPC, we need to package the RPC parameters into the Protobuf object for serialization, so we need to re-implement the serialization and deserialization of RPC. As another note, UE implements the serialization and deserialization of RPC parameters by defining an internal struct in the gen.cpp.

laughxing commented 1 year ago

correct me if i'm wrong. the internel struct generated by UE is used as a memory buffer to hold the params when deserializing the rpc param. if we use the unreal serialization through the reflection info of UFunction, we can pack the rpc params in to a string, transfer to the other end using Protobuf, then use the UFunction reflection info to decode the string buffer to the internal struct in the gen.cpp then call the ProcessEvent.

indiest commented 1 year ago

if we use the unreal serialization through the reflection info of UFunction, we can pack the rpc params in to a string

Sounds like a way to make life easier :) Do you know which functions we can use to pack/unpack UFunction's parameter buffer?

laughxing commented 1 year ago

RepLayout::SendPropertiesForRPC FRepLayout::ReceivePropertiesForRPC can we use these?

indiest commented 1 year ago

Looks promising. Still need some test though - not sure if calling UActorChannel::WriteFieldHeaderAndPayload is needed after RepLayout::SendPropertiesForRPC

indiest commented 1 year ago

Created task #36