Replication for inventory, transferring items

The goal is quite common - to transfer some item from one container to another one. I can have two chests, or chest and inventory, or even transferring inside of the same container (to another slot).

I simulated with 100ms time delay for replication, it means 10 replications for a second.
If for all operations we wait for an answer from the Server, it takes a time.
It means that I cannot take an item before Server allows it. And I cannot put it back without permission also.
It’s definitely wrong.

If I don’t wait for an answer from the Server and hope that next replication fixes all issues, it’s wrong as well.
For example, I make change 1 and send it to the Server. While I wait for replication, I do second change. Finally, I get the response from the Server with change 1, which overwrites my second change. Of course, next replication fixes this issue, however, I have wrong data for some time.

This task even more complicated if a second player wants to take the same item at the same time.

I can see just one solution: to have a revision for each change and every time check it in the client before using. If I have some data from the server with old revision, I wait for next replication to update my data. If a revision is the same or newer (if somebody else did some change also), I can update the data.

However, I feel that this method is also wrong.
Because if I play with slots and don’t stop, the Server never overtakes me.

The question is - how to do it correctly to avoid time delay for users?

Would it work better to only have the inventory exist on the server, not on the clients? Or at least only do inventory change operations on Server. Any time Client changes something on their end, it doesn’t actually happen on their end until the server replies. That way there can be no incorrect unsynchronized changes. I know there would be a visible delay though.

I know there would be a visible delay though.

Exactly! I have to add - a significant delay. You’ll never play this game :slight_smile:

The fact is that the REAL truth of the reality is on the Server, and there will always be a delay over network. The best you can do is to hide the delay by making the client look like the transaction already happened, or show some kind of animation to explain the delay in a way that looks cool. The delay should not be more than 200ms most of the time though unless you’re on a really bad network.

making the client look like the
transaction already happened

I have asked about the way how to do it properly.
If you transfer something between slots, an animation is not the answer.

Sorry, but it looks like:

I ask you: how to make the white colour for painting the ceiling?

And you answer: for painting ceiling you have to use a white colour.

Sorry I don’t mean to be redundant.

The server SHOULD overwrite what happens on the client. You cannot avoid a time delay.
But you CAN prevent another change from occurring while the Server and Client are synchronizing.

I’m sorry I don’t know the “proper” way to do this but a few ideas come to mind.
One is to set a boolean on the Inventory actor or Chest or whatever it is, called “BusyTransferring”, and make it a RepNotify. Then on the Client whenever a transfer of items occurs, set BusyTransferring to true, and make it so nobody can start another one (and show this by graying out the inventory UI until BusyTransferring is false again) on that Chest or other Inventory actor.

Once the Server receives the item via the Server RPC from the Client, then it can set BusyTransferring to true and then immediately set it to False again to trigger the Notify on all Clients, which will signal them that the Chest can be used again. I don’t know if this will work but that’s something I would try.

The basic idea is to block it from doing anything more until everyone has been signaled that the first change has taken place. Then you don’t get out-of-sync states between clients.

Unfortunately, this method gives delay. If a client wants to take two items, the client can click both of them much faster, than permission from the server comes to the client.

I’m still thinking about the answer and I can see just one way: to make a revision for each change.
It can be int32 (revision or version) which should be increased by 1 every time when Client or Server do some changes.
Replicated data cannot be used. I have to create a duplicate for the client, which will be synchronized just in case of the revision (or version) of Server (we can see it in the replicated data on the Client) is equal or more than the revision (or version) of the duplicate.

Could you extend your answer in the next part

The server has to validate the action

How it should be implemented?
RPC from server or replication of something?

To hide the delay the client needs to be able to predict the outcome as in have all the information required cached at the beginning. The server has to validate the actions each client makes and if a client has made an invalid action the server has to send that client a correction. A very simple explanation to a complicated topic but I hope it gets you going.

  • The idea is that the client do the action immediately and let the server know what it has done
  • Then later the server receives what the client has done and if it accepts it it lets the other clients know what happened if necessary via an RPC or a replicated variable.
  • If the server doesn’t accept it (another client did something first or the server changed something that the client has not received yet). Then the server has to send an RPC to that specific client with the correction.
  • This will only work well if the client can correct itself gracefully.

Well… It means that I have to use RPC from the server in case of declining changes… It should be not replication but RPC. Ok. Thanks.

Finally, I did it using just RPC from Client to Server and replication from Server to Client.
The client has two sets of data: replicated and non-replicated.
The client uses the non-replicated dataset.
Server and Client have counters. For the client, there is two: non-replicated and replicated.

In case of some changes, non-replicated counter takes +1. The same thing Server does as well.

If the replicated counter right after replication more or equal the non-replicated counter, the non-replicated dataset takes the replicated dataset.

If there is no replication more than 1 second after the last request from the client, the non-replicated dataset takes the last replicated dataset as well.

If the non-replicated dataset does not equal the last replicated dataset, the data is not fully valid.

This method works very well for multiplayer if several players try to change one dataset.