ixikos
ixikos2w ago

I have an inventory that I want to sync, each slot has an item id as well as quantity

I have an inventory that I want to sync, each slot has an item id as well as quantity. Would I use two arrays to do this ? Is there a way to do this with a single data structure to prevent race conditions on data synchronization
5 Replies
kapa.ai
kapa.ai2w ago
Hi @ixikos. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- To synchronize an inventory where each slot has both an item ID and a quantity, you should avoid using two separate arrays (e.g., one for item IDs and one for quantities). Using two arrays can lead to race conditions and data mismatches, especially in a real-time, multi-client environment. Instead, the recommended approach in Normcore is to use a single data structure for each inventory slot—such as a custom RealtimeModel that contains both the item ID and quantity fields. Then, you can store these slot models in a supported collection, such as a RealtimeArray, RealtimeSet, or StringKeyDictionary, depending on your needs: - RealtimeArray: Good for a fixed-size, ordered inventory where you only add items (removal by index is not supported). - RealtimeSet: Good for unordered collections where items can be added or removed at runtime. - StringKeyDictionary: Good for key-value access with transactional guarantees (e.g., slot index as key). For example, you could define a model like this:
[RealtimeModel]
public partial class InventorySlotModel {
[RealtimeProperty(1, true)] private int _itemId;
[RealtimeProperty(2, true)] private int _quantity;
}
[RealtimeModel]
public partial class InventorySlotModel {
[RealtimeProperty(1, true)] private int _itemId;
[RealtimeProperty(2, true)] private int _quantity;
}
Then, use a collection of InventorySlotModel (e.g., RealtimeArray<InventorySlotModel> or StringKeyDictionary<InventorySlotModel>) to represent the inventory. This ensures that each slot's data is kept together and synchronized atomically, preventing race conditions and data mismatches. Normcore collections are designed to synchronize only the minimal changes and handle multi-client updates robustly. Using a single model per slot and a supported collection is the best practice for your use case. Avoid using two arrays, as Normcore cannot track changes between them efficiently and cannot guarantee atomic updates across both arrays, which can lead to synchronization issues and race conditions Supported Primitives | Collections. If you need transactional guarantees (e.g., to prevent conflicts when multiple clients update the same slot), use StringKeyDictionary, which only applies changes after server confirmation and rejects conflicting updates. Summary:
- Use a single model per inventory slot (with both item ID and quantity). - Store these models in a supported Normcore collection. - Do not use two separate arrays for item IDs and quantities. This approach will keep your inventory data consistent and synchronized across all clients. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai2w ago
<@346532823143809046> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
maxweisel
maxweisel2w ago
can you provide more info on what race condition you’re worried about here? normcore batches all updates that occur in one frame into a single packet.
ixikos
ixikosOP2w ago
yea i think i was initially thinking about this as two arrays unecessarly, it was mostly setting the quantity and item type , and deciding when to update the view per client. So this approach where there is a RealtimeSet or RealtimeArray with that model containing both id and quantity makes sense, now just sorting out which of those two makes more sense here these are for things like storage chests in world, that have an inventory, so index does matter snippets, trying to sort out the events now
public partial class InventoryModel
{
[RealtimeProperty(1, true, true, false)]
private RealtimeArray<InventorySlotModel> _inventorySlots;
}
public partial class InventoryModel
{
[RealtimeProperty(1, true, true, false)]
private RealtimeArray<InventorySlotModel> _inventorySlots;
}
public partial class InventorySlotModel {
[RealtimeProperty(1, true)]
private int _itemId;

[RealtimeProperty(2, true)]
private int _quantity;
}
public partial class InventorySlotModel {
[RealtimeProperty(1, true)]
private int _itemId;

[RealtimeProperty(2, true)]
private int _quantity;
}
public class InventoryRealtime : RealtimeComponent<InventoryModel>
{
protected override void OnRealtimeModelReplaced(InventoryModel previousModel, InventoryModel currentModel)
{
if (previousModel != null)
{
// Unregister from events
}

if (currentModel != null)
{
if (currentModel.isFreshModel)
{
// Initialize the model
}

// Register for events
model.inventorySlotsDidChange += InventorySlotsDidChange; // doesnt seem to exist
}
}
}
public class InventoryRealtime : RealtimeComponent<InventoryModel>
{
protected override void OnRealtimeModelReplaced(InventoryModel previousModel, InventoryModel currentModel)
{
if (previousModel != null)
{
// Unregister from events
}

if (currentModel != null)
{
if (currentModel.isFreshModel)
{
// Initialize the model
}

// Register for events
model.inventorySlotsDidChange += InventorySlotsDidChange; // doesnt seem to exist
}
}
}
maxweisel
maxweisel2w ago
looks good to me. if index matters maybe use RealtimeDictionary

Did you find this page helpful?