Multiplayer Inventory System for Unity + Mirror — Version 1.0. A fully network-authoritative inventory where every item transaction runs through the server.
NetInventory Pro is a fully network-authoritative inventory system built for Unity with the Mirror networking library. Every item transaction — adding, removing, swapping, splitting, merging, dropping, and picking up — runs through the server, making it safe for multiplayer games where client authority would be a cheat risk.
| Dependency | Version | Notes |
|---|---|---|
| Unity | 2022.3 LTS or newer | |
| Mirror | Latest stable release | Available on the Asset Store or GitHub |
| TextMeshPro | Any version | Included with Unity |
| Render Pipeline | Built-In or URP | HDRP not tested |
| Scripting Backend | Mono or IL2CPP |
This asset requires Mirror Networking. It will not compile or function without Mirror installed. Install Mirror before importing this package.
Open the Unity Asset Store window and import Mirror into your project. Alternatively, install it via the Package Manager using the Mirror GitHub URL.
In the Unity Editor, go to Assets → Import Package → Custom Package and select the NetInventoryPro.unitypackage file. Import all assets.
If prompted, click Import TMP Essentials in the TextMeshPro setup window. The UI prefabs require TMP to display item names and stack counts.
Navigate to Assets/Scenes/ and open DEMO.unity. Press Play to verify everything is working before integrating into your own project.
After import, the following folders will be present in your project:
| Path | Contents |
|---|---|
Script/Inventory/ | All inventory C# scripts |
Script/Player/ | PlayerMovement script |
Prefabs/ | SlotPrefab, DroppedItem, InventoryUI prefabs |
Resources/Items/ | Item ScriptableObject assets (Wood, Stone, etc.) |
Resources/Player.prefab | Player prefab with all components pre-configured |
Scenes/DEMO.unity | Demo scene — open this first to verify setup |
Item assets must remain inside a Resources/ folder so they can be loaded at runtime with Resources.Load(). Do not move them outside Resources/.
The fastest way to get started is to use the pre-configured Player prefab:
Drag a Mirror NetworkManager into your scene if you do not already have one.
Open Resources/Player.prefab and assign it as the Player Prefab in your NetworkManager's Registered Spawnable Prefabs list, and in the Player Prefab field.
Drag the Prefabs/InventoryUI prefab into your scene. Assign the references (gridParent, hotbarParent, tooltipPanel, etc.) if not already set.
Add Prefabs/DroppedItem to the NetworkManager's Registered Spawnable Prefabs list.
Start a host from the NetworkManager HUD. Your player will spawn with a fully functional inventory.
Items are defined as ScriptableObject assets. Each asset represents one item type (e.g. Wood, Iron Sword, Health Potion). You do not need to write any code to add new items.
| Field | Type | Description |
|---|---|---|
itemName | string | Display name shown in tooltips and UI |
icon | Sprite | Icon displayed in inventory slots and when held |
description | string | Short description shown in the tooltip |
stackable | bool | Whether multiple units share a slot |
maxStack | int | Maximum units per stack (default: 64) |
itemType | ItemType | Resource / Tool / Weapon / Consumable / Seed / Food |
toolType | ToolType | Only relevant when itemType is Tool |
damageMultiplier | int | Hits removed per swing (for tools/weapons) |
The system searches the following paths inside Resources/ when loading items at runtime:
Items/Items/Resource/Items/Food/Items/Consumable/Items/Tool/Items/Weapon/Items/Station/The item id used in code is the asset filename, not the itemName field. For example, an asset named 'IronSword' is referenced as AddItem("IronSword", 1).
If you are using your own player prefab instead of the provided one, add the following components to your player GameObject:
| Component | Purpose |
|---|---|
Inventory | Core inventory data and network sync |
InventoryLinker | Links the Inventory to the InventoryUI when the local player spawns |
InventorySaveManager | Handles save and load of inventory state on the server |
ItemDropper | Handles dropping items and proximity pickup |
HeldItemDisplay | Syncs and displays the currently held item sprite |
PlayerMovement | Top-down movement with UI-aware input blocking |
InventoryLinker connects the local player's Inventory component to the singleton InventoryUI when the player object starts on the client. Without this component the UI will not display the correct inventory.
If you have UI panels outside of the inventory system, you can block player movement while they are open by assigning a delegate to PlayerMovement.IsUIOpen:
// In your UI script or game manager:
PlayerMovement.IsUIOpen = () => MyCustomUI.Instance.IsOpen;
The Inventory component is the central authority for all item data. It runs on the player GameObject and all public methods that modify state are server-only. Clients interact with it exclusively through Commands.
| Constant | Value | Description |
|---|---|---|
TOTAL_SLOTS | 40 | Total number of inventory slots |
HOTBAR_SLOTS | 8 | Slots 0–7, displayed in the hotbar |
GRID_SLOTS | 32 | Slots 8–39, displayed in the grid panel |
| Method | Description |
|---|---|
AddItem(string itemId, int amount = 1) | Adds items to the inventory, filling existing stacks first. Returns true if at least one unit was added. |
RemoveItem(string itemId, int amount = 1) | Removes items by id, searching from the last slot backwards. Returns true if at least one was removed. |
RemoveSlot(int slotIndex, int amount = 1) | Removes a specific amount from a single slot by index. Returns true on success. |
| Command | Description |
|---|---|
CmdAddItem(string itemId, int amount) | Client-callable wrapper for AddItem. |
CmdRemoveSlot(int slotIndex, int amount) | Client-callable wrapper for RemoveSlot. |
CmdSwapSlots(int fromIndex, int toIndex) | Swaps two slots. Merges stacks if both hold the same stackable item. |
CmdTransferOne(int fromIndex, int toIndex) | Moves exactly one unit between slots (right-click split). |
CmdDistributeAcrossSlots(...) | Spreads a stack evenly across multiple target slots (right-drag). |
CmdMergeStacks(int fromIndex) | Gathers all stacks of the same item into the source slot (double right-click). |
CmdSelectHotbar(int index) | Changes the active hotbar slot (0–7). |
// From a server-side script (e.g. a chest or resource node):
Inventory inv = player.GetComponent<Inventory>();
inv.AddItem("Wood", 5);
// From a client-side script:
Inventory inv = GetComponent<Inventory>();
inv.CmdAddItem("Stone", 3);
InventoryUI is a singleton MonoBehaviour that manages all UI elements. It instantiates slot prefabs, handles input for opening/closing, scroll wheel hotbar cycling, number key selection, tooltip display, and slot swapping.
| Field | Type | Purpose |
|---|---|---|
inventoryPanel | GameObject | Root panel shown when the inventory is open |
gridParent | Transform | Parent for the 32 grid slot objects |
hotbarParent | Transform | Parent for the 8 hotbar slot objects |
slotPrefab | GameObject | Prefab instantiated for each slot |
tooltipPanel | GameObject | Root of the tooltip popup |
tooltipName | TMP_Text | Item name label |
tooltipType | TMP_Text | Item type label (colour-coded) |
tooltipDescription | TMP_Text | Item description label |
tooltipStack | TMP_Text | Max stack size label |
| Gesture | Action |
|---|---|
| Left-drag to another slot | Swap the two slots (merges stacks if same item) |
| Left-drag outside panel | Drop the entire stack into the world |
| Right-drag across slots | Distribute the stack evenly across all visited slots |
| Right-click (single) | Starts timer for double-click detection |
| Right-click (double) | Merge all stacks of the same item into this slot |
| Right-click outside panel | Drop one unit into the world |
| Right-click during left-drag | Transfer one unit from the drag source to this slot |
| Hover (grid slots only) | Show tooltip |
InventorySaveManager saves and loads each player's inventory as a JSON file on the server. Save files are stored in Application.persistentDataPath by default, with one file per player.
{saveFolderPath}/inventory_{playerId}.json
To provide a custom save folder or player id (e.g. SteamID), call Configure() before OnStartServer runs:
InventorySaveManager saveManager = player.GetComponent<InventorySaveManager>();
saveManager.Configure("/path/to/saves", steamId.ToString());
Edit the GiveStarterItems() method inside InventorySaveManager.cs:
void GiveStarterItems()
{
inventory.AddItem("Wood", 10);
inventory.AddItem("Stone", 10);
// Add more items here
}
| Key / Input | Action |
|---|---|
E | Open / close inventory panel |
Q | Drop one unit of the selected hotbar item |
Ctrl + Q | Drop the entire stack of the selected hotbar item |
1 – 8 | Select hotbar slot directly |
Scroll Wheel | Cycle through hotbar slots |
WASD / Arrows | Move the player (blocked when inventory is open) |
Open ItemData.cs and add a new value to the ItemType enum:
public enum ItemType
{
Resource, Tool, Weapon, Consumable, Seed, Food,
Quest // your new type
}
Then add a matching colour entry in InventoryUI.GetTypeColor() and SlotUI.GetCategoryColor().
Add a new value to the ToolType enum in ItemData.cs:
public enum ToolType
{
None, Axe, Pickaxe, Hoe, WateringCan, Sword,
Shovel // your new tool
}
Edit the constants at the top of Inventory.cs:
public const int TOTAL_SLOTS = 40; // hotbar + grid
public const int HOTBAR_SLOTS = 8;
public const int GRID_SLOTS = 32;
If you change these values, also update the UI layout (grid columns/rows) to match.
Any server-side script can drop items into the world using ItemDropper:
// e.g. a tree falls and drops logs:
ItemDropper dropper = player.GetComponent<ItemDropper>();
dropper.SpawnDroppedItem("Wood", 3, treePosition);
Mirror is not installed. Install Mirror first, then re-import the package.
Make sure InventoryLinker is on the player prefab and that InventoryUI exists in the scene. Check that the player prefab is registered in the NetworkManager.
Verify that each ItemData asset has a Sprite assigned to the Icon field. Also confirm the asset is inside a Resources/ subfolder.
The item id passed to AddItem or LoadItem does not match any asset filename. Item ids are case-sensitive and must match the asset filename exactly (e.g. "Wood" not "wood").
SaveInventory() must be called explicitly (e.g. on player disconnect). It is not called automatically on exit. Hook it to your disconnect event.
Ensure the DroppedItem prefab is added to the NetworkManager's Registered Spawnable Prefabs list.
Check that InventoryUI.Instance is not null and that the IsOpen property returns true when the panel is visible.
Right-drag requires the source slot to have more than one unit. The drag threshold is 8 pixels — move the mouse further from the starting point.
NetInventory Pro — Version 1.0 · CywerStudio · Thank you for using this asset. If you have questions or feedback, please leave a review on the Asset Store.