How do I replicate an inventory?

Hello. I’ve been trying to get my inventory to replicate, but with no luck.

I declare my item class like this:


UCLASS(Blueprintable, Abstract)
class UItemBase : public UObject
{
	GENERATED_BODY()

public:
	UPROPERTY(BlueprintReadOnly, Category = Item)
		FString itemName;

	UPROPERTY(BlueprintReadOnly, Category = Item)
		FString itemDescription;

	UPROPERTY(BlueprintReadOnly, Category = Item)
		UTexture2D* itemIcon;

	UPROPERTY(BlueprintReadOnly, Category = Item)
		UStaticMesh* itemMesh;

	UPROPERTY(BlueprintReadOnly, Category = Item)
		int32 itemLevel;

	UPROPERTY(BlueprintReadOnly, Category = Item)
		EItemRarity itemRarity;

	UPROPERTY(BlueprintReadOnly, Category = Item)
		int32 itemID;

	UPROPERTY(BlueprintReadOnly, Category = Item)
		EItemSubclass itemSubclass;
};

I tried to do replicatesubobject, and I couldn’t make it work (I also used the Replicated on each property etc (I used this as guidance: A new, community-hosted Unreal Engine Wiki - Announcements and Releases - Unreal Engine Forums)).
I also tried inheriting from AActor, but then I couldn’t do for example this:


UItemWeapon* weapon = NewObject<UItemWeapon>();

My UItemWeapon class just inherits from UItemBase with additional properties.

Any advice?

Well, you probably want to have a TArray of these UItems or something like that. You can store it on your Pawn, for instance, and make this property replicated. Then, you can replicate it either on this character only (if you don’t want other players to be aware of your inventory) or to everybody.

And don’t forget to add this variable to the replicated variables list:



void YourClass::GetLifetimeReplicatedProps(TArray<FLifetimeProperty> &OutLifetimeProps) const
{
    Super::GetLifetimeReplicatedProps(OutLifetimeProps);

    DOREPLIFETIME_CONDITION(ATPSCharacter, YourInventory, COND_OwnerOnly);
}


Though I would probably just use a struct for such things. It is much easier to manage around.

And mark them with UPROPERTY(Replicated).

But it’s inherited from UObject, and correct me if I’m wrong, but UObjects don’t replicate, right?

As far as I know, it is true only if it’s the ‘last-in-chain’ object. Meaning, it doesn’t have an Actor as its ‘outer’ object. But I would really just go with structs for your case.

UObjects dont Replicate. The first thing that has this capability is the AActor. However you could replicate a TArray<TSubclassOf<MyItemObject>>.

Could you walk me through how to do this? I really appreciate your help guys. :slight_smile:

Check out the ShooterGameExample.
Last time I checked, it used an array for its inventory.
Though based on actors, it’ll give you an idea on what to do.

Well, I would have something like this:



USTRUCT()
struct FInventoryItem
{
    // Some UPROPERTY()s like name, description and etc.
}

class AMyCharacter : public ACharacter
{
    ...
    UPROPERTY(Replicated)
    TArray<FInventoryItem> Inventory;
}


This is the most basic approach.

That simple, really? Well, thanks. :slight_smile:

You don’t want to replicate whole objects (it’s expensive bandwidth wise). Instead you want to create a Datatable somewhere that has all your items(each containing a unique ID), then your inventory is just an array of those unique IDs:



    UPROPERTY(Replicated)
    TArray<uint32> Inventory;


Then just use that ID to look up the item stats/description/etc, so you’re only ever replicating n * 4 bytes rather than n * sizeof(FInventoryItem).

And if you don’t want to mess with data tables, you can also save asset references (FStringAssetReference) serialized to string. A little more bandwidth but more flexible and you don’t need to maintain this table.

Ah, sweet. Thanks for the tips!

It’s potentially a lot of bandwidth given you are transmitting the full string path (which could be using wchar). If you don’t want to maintain the table, you could hash the FStringAssetReference, and then at load time just iterate through your known directory and build up a hash to Asset map. The problem with just doing that is if you move an asset, you break the hash so items you previously had won’t stay with you. The Datatable lets you avoid that at the overhead of maintaining that (or you build a unique ID into each asset you want to carry,i.e. some GUID, and replicate that), etc.

Lots of ways to do it. Just depends on your bandwidth/persistence limitations.

Alright, so I tried just setting my TArray<UItemBase*> inventory; to replicated, but it doesn’t replicate. I made a call to the server to add an item from an ID, so it should replicate to the clients that it has an item in the inventory, but it’s just null on everyone but the server.

So uh, as I said before, UObjects won’t replicate. :frowning:

EDIT: I also added this to the .cpp file:


void AMyProjectCharacter::GetLifetimeReplicatedProps(TArray< FLifetimeProperty > & OutLifetimeProps) const
{
	Super::GetLifetimeReplicatedProps(OutLifetimeProps);

	DOREPLIFETIME(AMyProjectCharacter, inventory);
}

Hey i do not think this approach will work.
As you say UObject do not replicate, so if the Object is not replicated the reference you potential store in your inventory array.
Will not be valid client side after you add it server side, and it will be null.

In order for the UObject to replicate it needs a Actor as the outer parent.
So you are better of defining a base item class derived from AActor::.

An item is surely something that is in the world at one point so making it a UObject makes little sense to me.

That makes sense.

I tried it though, and when I wanted to make a new instance of an item(AItemBase* item = NewObject<AItemBase>()) I got an error, so I don’t think you can use NewObject with an actor.
Are there any alternatives, or should I go about it in a different way?

Well you should spawn the Item in world (create a instance of it) and then store it in the array.
Using a Server RPC to add the item server side, and have it replicated to client.

Now in my opinion having a inventory with references to actors for a networked game.
Is not something i see as a generally good idea, if you think about having a lot of players.
And all of them are carrying a bunch of items, that are referenced and must be located some where in the world but hidden or something as they are in a backpack or what have you.

A struct with a item id defining the type of item and filling in the data as needed.
e.g: Icons for UI using the ID.

Also have a look at conditonal replication, for my players inventory my condition is to Owner only like this.


// replicating this ONLY to owning player.
DOREPLIFETIME_CONDITION(AMyPlayerClass, InventoryContainer, COND_OwnerOnly);

This will save you a lot of badwidth.

Now am not saying its the only way to do it and depending on your game design so 4th but this is preferred approach by many.

I’d really like to avoid having all items spawned in the world, and a struct is not something I see as a viable option unless I can make my item subclasses inherit from the base item struct (which I don’t think is possible, or is it?).

And thanks for the tip, I’ll make sure to put it to good use. :slight_smile:

Yes you can inherit structs like you do with a class.


USTRUCT()
struct FMyBaseStruct
{
     GENERATED_USTRUCT_BODY()
};

USTRUCT()
struct FMyChildStruct : public FMyBaseStruct 
{
     GENERATED_USTRUCT_BODY()
};