Download

Replicating actors that reference static data

This is a follow up to a question I asked earlier…

So I have some static data in a game. Say it’s a card game, and my data structures look like…



UCLASS()
class CardDefinition : public UDataAsset
{
  // Some data including art, base stats, abilities, etc.
  // This class is potentially large
}

USTRUCT()
struct CardInstanceData
{
  UPROPERTY()
  CardDefinition* Definition;

  // Some other data, including current health, any temporary effects, etc.
}

UCLASS()
class CardInstance : public AActor
{
  UPROPERTY()
  CardInstanceData* InstanceData;

  // Other stuff, mesh, etc.
}


CardInstance needs to be replicated from server to client. CardInstance has a construction script; when this script is called on the server, InstanceData’s Definition is set to what I expect. On the client, however, the Definition is null. Is there something I need to do to cause a CardDefinition reference to replicate properly to the client?

As an even better alternative, CardDefinition assets are all static and should be identical between the client and the server. It seems like it would be much more efficient to just pass around references to assets. I’ve tried making CardInstanceData.Definition a TAssetPtr<CardDefinition>. However, I can’t load a TAssetPtr from a construction script (makes sense, loading is asynchronous). I’m going to try adding a preload step for all of the CardDefinitions that are being used in the current game, but I’m not confident that this will solve my issue.

Am I going about this the right way, or am I completely barking up the wrong tree? What’s the standard way for a multiplayer game to reference static data-only assets that are not part of a level?

Thanks!

Good news: data asset pointers can be passed around between server and client very efficiently and they always point to the same static data. So in this case your idea about using CardDefinition is right, but you can simply pass a data asset around as a CardDefinition* variable!



UFUNCTION(NetMulticast, Reliable)
void MyRPC(CardDefinition* SomeData);


Double posting because I forgot some critical info for you. Replicating data asset references as a actor variable is of course also possible. To replicate any actor variable you have to flag them as a replicated UPROPERTY:



UPROPERTY(Replicated)
CardDefinition* SomeData;


That will cause the data to copy from server to client. Be aware that on the client the value will stay null until it received data from the server. This can take a few frames. If you want to respond directly to the value becoming non-null on the client, specify a function to call via ReplicatedUsing:



UFUNCTION()
void OnRep_SomeData();

UPROPERTY(ReplicatedUsing=OnRep_SomeData)
CardDefinition* SomeData;


OnRep_SomeData will automatically be called as soon as ‘SomeData’ changes value client-side.

Thanks. It looks like my problem was I was trying to read a variable value from a construction script, not a replication callback. The references to static data are in fact replicating properly; they just hadn’t replicated yet. It’s kind of weird to me though that the actor would get replicated to the client before the properties. It feels like the actor is in a limbo state that isn’t safe to use until all of the values get replicated.

In any case, this addresses the problem I’m having right now (or at least one of them). Thanks!