Implementing a session persisted Inventory

We are creating a multiplayer party game. In each session you start from scratch and so we have no need to persistence after a match. Any data we need stored is stored locally.

The game is 2D and turned based. Most of the game’s data and state are stored on the players. So the player’s stats, their inventories, perks, curses, etc.

Inventories follow this structure:

public class Inventory
{
    List<InventorySlot> InventorySlots;
}

public class InventorySlot
{
    InventoryItem Item;
    SlotType Type;
}

public class InventoryItem
{
    string ItemId;
    int StackCount;
    int Durability;
}

Each player might have multiple different Inventories. They have their personal inventory, private storage and then there might be public inventories or containers (that all players can access).

Note that the game is turn based and action point based. So opponents will be watching others players’ actions as if they were playing the game locally on the same screen.

We’ve pondered ways of sending information about the state and changes of inventories between players. We were hoping we might just convert the state to a byte array and add a OnValueSyncedAttribute to that but then discovered that attribute is only supported for value types.

We could use Commands to send changes to an inventory to other players with the minimum amount of data required to perform that actions. So RemovedItemFromIndex with the index of the inventory or AddedItem with the InventoryItem info serialized to a byte array and slot index.

The problem there is if a player is disconnected, we want that player to be able to rejoin the game and continue on as if nothing happened. This means that the player needs to get all of their state data when rejoining. As I understand it, calls are not allowed to be larger than 512 bytes. We could serialize the player data into a byte array, split it into chunks and send it over that way… but that seems like we’re just creating our own packet system…

One solution is to create an object that looks something like this:

public class InventorySlot : Monobehavior
{
    SlotType Type;
    string ItemId;
    int StackCount;
    int Durability;
}

And for each inventory slot that exists in the game we create a synced GameObject that stores its state. That seems to be a bit excessive… that means that if we have 10 players with 10 inventories and each inventory has 10 slots we need to create 10x10x10 = 1.000 GameObjects for the slots.

This problems continues for other data that is stored in the player in lists. Each player has a list of perks, curses, queued notifications and more (which are shown at the start of a players turn but might accrue over the turn before). So if the player ever disconnects, that state is gone unless other players also have knowledge of that state.

So to summarize, we have a turn based, board-game-esque video game that is reliant on a lot of list based data for each player. We need to not only pass the changes to other players but also be able to easily allow disconnected players to rejoin matches, keeping their previous state. Matches might take more than an hour and so the ability to rejoin is paramount.

1 Like

You’ve laid out the challenges and possible approaches well, and this is a commonly requested system that would benefit from a higher level of abstraction from the coherence SDK. We’re striving to strike the right balance between Developer Experience and keeping our SDK solution agnostic. We are in active discussions on how best to fit this abstraction into the SDK, but I don’t have a timeline for when it will be in.

One option regarding player-specific data like inventory: if you’re using our authentication system you can use our player Key Value store:

This is player specific data, so as a player’s inventory changes, you can serialize to a K/V that’s specific to the game session. When they drop and return you can use this K/V to restore their inventory.

For communicating in real time during a game session, serializing state changes into commands is the recommended approach, yes.

Just to add to Brit’s answer - syncing lists, including those with size over 512 bytes, is on our High Priority list :slight_smile: