Best practices to replicate an inventory (multiplayer)

Hi guys,

I am sure you can help me out. I am currently working on an basic inventory system backend, which I would like to share or publish for the community later on. Unfortunately I miss a few basic information or best practices how I can realize certain points, as it should support multiplayer. So far I have already created a “MasterItemActor”, which can already be created or destroyed by the server and replicated to the clients. This is working so far.
After reading and watching the networking tutorials, also of Tom Looman, I am a bit confused about the different ways I could take. Currently I am searching for a best practice + I got some questions. I would be very happy if you guys could help me a bit.

I plan creating a class (Actor/UObject/Whatever???) which holds the basic functions like “AddToInventory”, “RemoveFromInventory” and s*o on. *It should also contains an array of all the items in inventory. What is the best way replicating this inventory and/or where should I store this Actor/Class ?

For an entry point these are the first questions.

Thank you really much for your time. Maybe you can also share some information how you realized something like this.

Greetings 3HMonkey

There is many ways to make an inventory and the “best” way depends on the game. Generally speaking you would probably only replicate the inventory state to the owner of that inventory.

Personally, I think you’re going about it the wrong way. If it where me, I would do something like this (extremely simplified):

Create a class called UItem derived from UDataAsset
Add all the properties an item has to the UItem class (ID, Name, Icon, Damage, etc)
Add a new UItem object for every item you need

On game startup somewhere (must be called in a Constructor somewhere), lookup UObjectLibrary and load all your UItems into a TMap<int, UItem> where the key is the ID of the UItem

I would then create some Client RPCs that the server can call like Client_SetItem(int id) and Client_DeleteItem(int position) or something like that, that way you don’t need to replicate your entire inventory for one small change.

On the client side, since you get the ID, you can lookup that ID in your TMap of UItems.

I hope that makes sense kind of.

Hey and thank you for your reply,

I like the idea of a TMap/Array etc. I quickly blueprinted something.

  1. I created an inventory actor which I spawn on the server inside the character on begin play event. (working)
  2. This actor class then creates an empty array of item actors for the server (working)
  3. (Adding to Inventory) I am using RPCs, so the character triggers a line trace event on the server to call the delete actor function (working because my usable actors are replicated) and add the item struct to the array, so every client has its own array on the server (working)

Doing it this way completely prevents the replication of the whole array/inventory. But how can I access that variable on the client for example for updating UI like HUD? I could add functions like “getInventory” or “removeFromInventory” inside the inventory actor class and return the array of items. Do you think its possible or am I wrong?

Greetings

UE4 only replicates the changes to an array, so replicating the inventory to its owner - and provided its not too big, everyone else - is fine.

You should do any collision / trace line checks on the client, then just verify the transform of the player and target item instead of running the trace again on the server.

Hey Kris,

thank you for your response. I made it this way, but I really don’t know if it is a adequate common solution.

Here is my setup:

  • Variable “ItemReference” holds the item actor itself (replicated that the server is allowed to spawn or delete this actor)
  • Variable “InventoryActorReference” is the inventory actor which is spawned on begin play and holds all the functions for adding or removing items to the inventory item array
  • Variable “InventoryItems” is the inventory item array (replicated/repNotify to owning client that the client can work with this)

Here is the workflow process:

  • For example when I hit the “Tab” button I fire the whole process on server as you guys can see on the picture
  • I added a function called “LineTrace” to set the item reference variable
  • Afterwards I check if its a valid one and pass this to “AddItem” function (nothing special, just adding an entry to the array)
  • Then the server destroys the actor of the item
  • Finally it sets the replicated inventory items variable

That also seems pretty easy to me and I cannot imagine that it is enough to create a basis for the system or?

What do you guys think?

Greetings and thank you for your time

That Event makes no sense at all to me.
DoOnServer is meaningless if you don’t also explain what it is doing.
LineTrace is also a poor name although better since it indicates a LineTrace is happening but why?
SwitchHasAuthority is not needed since a RunOnServer event are never executed on a remote client.
AddItem makes sense but then you immediately destroy the actor which means that Reference is now invalid (also on the client).
Finally you get the struct item array and set it to itself?

Sorry but I don’t understand what it is you are trying to achieve with this event.

@GarnerP57 Yes/no. In context, it somewhat makes sense. Coming at it for the first time it would be a tad off putting.

Line trace to what? From where?
Do what on the server?
And so on.
Bit ambiguous :slight_smile:

@3HMonkey Provided LineTrace is doing what I assume it is doing - running quick trace from viewpoint to item location and setting the item reference if its ok - then yes, seems basically ok.
I’d still do the trace to the item on the client first.
If you can’t reach the item, why bother making the server do any work?
Only if the trace succeeds should you bother asking the server to have a go :stuck_out_tongue:

I apologize if I sound a bit harsh, it is not my intention. I am genuinely confused about the intent of that event.

Hey hey,

you guys are right. Since I am just blueprinting and this is more trial and error, I used generic names for testing purpose. Afterwords I go and translate that to C++ and plan distributing better names. Well, my bad. I have been programming for 15 years, I shpuld have known better. Show nothing that you would not understand yourself :slight_smile:

@Kris Yes, its just a line trace to set the item reference. I go switch it over to the client. I thought about eXi’s posts, who always likes to outsource stuff to the server

I’ll give you a feedback when I changed that.
THX again,

Greetings

Some people built inventory using Actor class; I think that’s an absurd, waste of data (Actor class is heavy and slow to spawn)

Is there a possibility to create RPCs inside an Actor class instead using custom events in player character class? I think it’s better if the inventory actor class could handle everything, so people would be able to use this system by drag and drop.

Greetings

You could always use an ActorComponent.
Still drag and drop, be replicated etc.

This is the approach I would go for.

That way you can add 1 or more inventories to any actor in your environment and interact with them in a consistent way.

Just in case anyone comes across this, the way I handle this type of system is more of a modular approach.

  1. Create an Actor Component for the Inventory (UInventoryComponent) and add #2.
  2. Create a UObject to act as a Item Container (USpatialInventoryContainer).
  3. Create a UObject for UItemObject_Base.
  4. Add TArray of type UItemObject_Base.
  5. Create all of your Item Objects parented to UItemObject_Base.
  6. Make a function in the Inventory Component to Add Item, and instead
    of passing in the UItemObject_Base child, you serialize the item into a struct with a uint8 byte array, and Item Class (Classes are always the same on the Client as the Server).
  7. Have the Client build the Item from the Serialized byte array.

Server only handles the transfer of the Byte array. Not pointers, and uses the
Inventory Component as a wrapper to the Owning Actors network channel.

This method can also be called to Synchronize any existing UObject, but not every frame.

For example, on a network test I can create 10,000 Items in 1500ms, 60 in about 9-14ms (on LAN). Please do not try this in blueprint. The serialization isn’t exposed by default, and since it is virtualized, it will make the server calls hang at scale. So so long as you control the flow to only replicate what is necessary, you shouldn’t have a problem with performance. (We use item stacking, not just for Play convenience, but to reduce bandwidth on the server.)

Be cautious about Inventory size.