Player Inventory Management
Player inventory manipulation is one of the most common operations in RocketMod plugin development. Nearly every server-side plugin — moderation tools, admin kits, spawn menus, economy systems, quest rewards, loot distributions — interacts with a player's inventory at some point. This article covers the complete RocketMod API surface for reading, writing, and managing player inventories, including item addition, removal, inspection, and the event hooks that fire when inventory state changes.
The patterns documented here are drawn from production inventory-management plugins used on 57 Studios™ Horizon Life RP servers. They have been validated against Unturned 3.x with RocketMod 4.x.

Prerequisites
- A working Unturned dedicated server with RocketMod installed. See RocketMod and OpenMod Plugin Basics for setup.
- Visual Studio 2022 with .NET Framework 4.7.2 targeting.
- Familiarity with
UnturnedPlayer,IRocketCommand, and the RocketMod event model. - Basic understanding of Unturned's item ID system (numeric asset IDs mapped in item
.datfiles).
What you'll learn
- How to add items to a player's inventory using
GiveItem. - How to remove items from a player's inventory by type, position, or quantity.
- How to clear a player's entire inventory.
- How to iterate over inventory contents and inspect item metadata.
- How to detect inventory changes through RocketMod events.
- How to spawn items on the ground at a player's position.
- How to handle edge cases: full inventories, stack limits, quality, durability.
- How to implement a permission-gated admin kit command.
The UnturnedPlayer inventory API
RocketMod exposes a player's inventory through properties and methods on the UnturnedPlayer object. The primary entry points are GiveItem, RemoveItem, and Player.inventory (which exposes the underlying SDG PlayerInventory instance).
Adding items to inventory
The most common operation is adding an item to a player's inventory. RocketMod provides the GiveItem method in two overloads:
csharp
// Overload 1: By item GUID string
public void GiveItem(string itemGuid, byte amount)
// Overload 2: By item ID with quality and durability
public void GiveItem(ushort id, byte amount, byte quality, byte durability, byte[] state)The first overload takes the item's GUID string as the first parameter. This is a 32-character hexadecimal string (with hyphens) that identifies the item asset uniquely. For example, the GUID for a "Dragonfang" weapon might be "3a8f7e21-bc44-4d91-9f06-5c2a1b3d8e70". This GUID is defined in the item's asset file and is consistent across all Unturned installations.
csharp
UnturnedPlayer player = UnturnedPlayer.FromName("Lloyd");
// Give 5 units of an item by GUID
player.GiveItem("3a8f7e21-bc44-4d91-9f06-5c2a1b3d8e70", 5);The second overload uses the numeric item ID (a ushort) and includes quality, durability, and state data. This overload is used for items that need specific quality values (weapons with attachments, food with conditional states).
csharp
// Give 1 item with full quality and no state data
player.GiveItem(1078, 1, 100, 100, new byte[0]);Both overloads return bool — true if the item was successfully added, false if it was not (typically because the inventory is full or the item ID is invalid).
Item state and quality
Unturned items carry state data that affects their behavior:
| Field | Type | Purpose |
|---|---|---|
id | ushort | Numeric item asset ID |
amount | byte | Stack size (1–255 for stackable items) |
quality | byte | Condition/durability (0–100) |
durability | byte | Effective durability after repair (0–100) |
state | byte[] | Binary state data (weapon attachments, food calories, etc.) |
The state byte array is item-type-specific. For weapons, it encodes the sight, magazine, barrel, grip, and tactical attachment IDs. For food items, it encodes remaining calories. Passing an empty array is valid for items that do not use state data.
Removing items
RocketMod provides RemoveItem to remove items from inventory by position:
csharp
public void RemoveItem(byte page, byte index)The inventory is organized into pages (sometimes called "columns"):
| Page | Content | Capacity |
|---|---|---|
| 0 | Items (storage slots) | 36 slots (6 rows × 6 columns) |
| 1 | Clothing (wearables) | 7 slots (hat, shirt, pants, mask, backpack, vest, glasses) |
| 2 | Hotbar | 6 slots |
| 3 | Storage (currently open container) | Variable (depends on container type) |
| 4 | Area (ground items nearby) | 50 slots |
csharp
// Remove the item in the first slot of page 0 (items page)
player.RemoveItem(0, 0);To remove a specific type of item, you must iterate the inventory, find the item by its ID, and remove it by position:
csharp
public static int RemoveItemById(UnturnedPlayer player, ushort itemId)
{
int removedCount = 0;
for (byte page = 0; page < 5; page++)
{
for (byte index = 0; index < player.Inventory.getItemCount(page); index++)
{
ItemJar jar = player.Inventory.getItem(page, index);
if (jar != null && jar.item.id == itemId)
{
player.RemoveItem(page, index);
removedCount++;
// Re-count items on this page because indices shifted
index--;
}
}
}
return removedCount;
}Clearing inventory
To clear a player's entire inventory, iterate all pages and remove every item:
csharp
public static void ClearInventory(UnturnedPlayer player)
{
for (byte page = 0; page < 5; page++)
{
byte count = player.Inventory.getItemCount(page);
for (byte index = 0; index < count; index++)
{
player.RemoveItem(page, 0); // Always remove index 0; items shift down
}
}
}Note the removal pattern: always remove index 0 and let items shift down, rather than removing by the original index. This avoids index-out-of-range errors.
Inventory events
RocketMod fires the following inventory-related events through UnturnedPlayerEvents:
| Event | Fires when | Parameter notes |
|---|---|---|
OnPlayerInventoryAdded | An item is added to inventory | Contains item, page, index, amount |
OnPlayerInventoryRemoved | An item is removed from inventory | Contains item, page, index, amount |
OnPlayerInventoryResized | Inventory capacity changes (container opened/closed) | Contains new capacity per page |
These events receive an UnturnedPlayer instance and event-specific arguments. They fire synchronously during the inventory operation.
csharp
UnturnedPlayerEvents.OnPlayerInventoryAdded += (player, inventoryItem) =>
{
Logger.Log($"{player.CharacterName} received item {inventoryItem.Item.id} ({inventoryItem.Amount}x)");
};Implementing a /kit command
The following example implements a permission-gated kit command that gives a predefined set of items to the calling player. The kit contents are defined in the plugin's configuration class.
csharp
using Rocket.API;
using Rocket.Unturned.Chat;
using Rocket.Unturned.Player;
using System.Collections.Generic;
namespace MyAdminSuite.Commands
{
public class KitCommand : IRocketCommand
{
public string Name => "kit";
public string Help => "Claim a predefined starter kit.";
public string Syntax => "/kit";
public List<string> Aliases => new List<string>();
public List<string> Permissions => new List<string> { "myadminsuite.kit" };
public AllowedCaller AllowedCaller => AllowedCaller.Player;
public void Execute(IRocketPlayer caller, string[] command)
{
UnturnedPlayer player = (UnturnedPlayer)caller;
KitConfiguration kit = AdminSuitePlugin.Instance.Configuration.Instance.StarterKit;
foreach (KitItem item in kit.Items)
{
bool success = player.GiveItem(item.ItemGuid, item.Amount);
if (!success)
{
UnturnedChat.Say(caller, $"Could not give {item.Amount}x of item {item.ItemGuid}. Inventory may be full.", UnityEngine.Color.yellow);
}
}
UnturnedChat.Say(caller, "Kit claimed!", UnityEngine.Color.green);
}
}
}KitConfiguration.cs:
csharp
using Rocket.API;
using System.Collections.Generic;
namespace MyAdminSuite
{
public class KitConfiguration : IRocketPluginConfiguration
{
public string KitName;
public List<KitItem> Items;
public void LoadDefaults()
{
KitName = "Starter";
Items = new List<KitItem>
{
new KitItem { ItemGuid = "3a8f7e21-bc44-4d91-9f06-5c2a1b3d8e70", Amount = 1 },
new KitItem { ItemGuid = "b44d91-9f06-5c2a", Amount = 5 },
};
}
}
public class KitItem
{
public string ItemGuid;
public byte Amount;
}
}The kit configuration defines items by their GUID string rather than their numeric ID. This is the preferred approach for RocketMod kit plugins because GUIDs are human-readable in configuration and survive asset reorganization. The GiveItem method resolves the GUID to the correct item asset at runtime.
Cooldown enforcement
Kits should have a per-player cooldown to prevent repeated claiming. The same CooldownTracker pattern from the god/vanish article applies:
csharp
if (AdminSuitePlugin.Instance.KitCooldown.IsOnCooldown(caller.Id))
{
UnturnedChat.Say(caller, "Kit is on cooldown.", UnityEngine.Color.red);
return;
}
AdminSuitePlugin.Instance.KitCooldown.SetUsed(caller.Id);Spawning items on the ground
Sometimes you want to drop items at the player's position rather than adding them directly to the inventory (full inventory fallback, loot distribution, event rewards). RocketMod provides ItemManager.dropItem for this:
csharp
using SDG.Unturned;
public static void DropItemAtPlayer(UnturnedPlayer player, ushort itemId, byte amount)
{
ItemAsset asset = Assets.find(EAssetType.ITEM, itemId) as ItemAsset;
if (asset == null) return;
Item item = new Item(itemId, amount);
ItemManager.dropItem(item, player.Position, true, true, true);
}The three boolean parameters control:
- Dedicated server flag — always
trueon a server. - Broadcast flag —
trueto make the item visible to all players;falsefor client-side only. - Spawn effect flag —
trueto play the item spawn effect (blue sparkle).
Implementing a /clearinventory command
The following command clears the calling player's inventory with a confirmation requirement to prevent accidental use:
csharp
using Rocket.API;
using Rocket.Unturned.Chat;
using Rocket.Unturned.Player;
using System.Collections.Generic;
namespace MyAdminSuite.Commands
{
public class ClearInventoryCommand : IRocketCommand
{
public string Name => "clearinventory";
public string Help => "Clear your entire inventory.";
public string Syntax => "/clearinventory";
public List<string> Aliases => new List<string> { "clearinv" };
public List<string> Permissions => new List<string> { "myadminsuite.clearinventory" };
public AllowedCaller AllowedCaller => AllowedCaller.Player;
public void Execute(IRocketPlayer caller, string[] command)
{
UnturnedPlayer player = (UnturnedPlayer)caller;
ClearInventory(player);
int totalClear = CountInventory(player);
UnturnedChat.Say(caller, $"Inventory cleared ({totalClear} items removed).", UnityEngine.Color.green);
Logger.Log($"{caller.DisplayName} cleared their inventory ({totalClear} items).");
}
private void ClearInventory(UnturnedPlayer player)
{
for (byte page = 0; page < 5; page++)
{
byte count = player.Inventory.getItemCount(page);
for (byte index = 0; index < count; index++)
{
player.RemoveItem(page, 0);
}
}
}
private int CountInventory(UnturnedPlayer player)
{
int total = 0;
for (byte page = 0; page < 5; page++)
{
for (byte index = 0; index < player.Inventory.getItemCount(page); index++)
{
ItemJar jar = player.Inventory.getItem(page, index);
if (jar != null) total++;
}
}
return total;
}
}
}Item validation and safeguards
Preventing item duplication
Item duplication is a critical server stability concern. The most common source of RocketMod-based duplication is calling GiveItem twice due to double event firing. The event guard pattern from the plugin basics article applies here:
csharp
private bool _isGivingItem = false;
public void SafeGiveItem(UnturnedPlayer player, string itemGuid, byte amount)
{
if (_isGivingItem) return;
_isGivingItem = true;
try
{
player.GiveItem(itemGuid, amount);
}
finally
{
_isGivingItem = false;
}
}Item ID validation
When accepting item IDs from user input (e.g., a /give command), validate that the ID or GUID maps to a valid item asset before calling GiveItem. An invalid ID will silently fail — the method returns false but does not throw.
csharp
public static bool IsValidItemAsset(string itemGuid)
{
ItemAsset asset = Assets.find(EAssetType.ITEM, itemGuid) as ItemAsset;
return asset != null;
}Full inventory detection
Check inventory capacity before attempting to give items:
csharp
public static bool HasFreeSlot(UnturnedPlayer player)
{
for (byte page = 0; page < 5; page++)
{
byte count = player.Inventory.getItemCount(page);
byte capacity = player.Inventory.getCapacity(page);
if (count < capacity) return true;
}
return false;
}Common errors and diagnostics
| Symptom | Cause | Resolution |
|---|---|---|
GiveItem returns false for a known valid item | Numeric ID passed to the GUID overload, or GUID passed to the numeric overload | Verify you are using the correct overload — GUID strings for GiveItem(string, byte), numeric IDs for GiveItem(ushort, byte, byte, byte, byte[]) |
| Item appears in inventory but has zero quality | Quality parameter omitted in the numeric overload call | The numeric overload requires an explicit quality value; use GiveItem(ushort, byte, byte, byte, byte[]) with quality=100 |
IndexOutOfRangeException when removing items | Iterating forward while removing items shifts indices | Always remove from index 0 in a loop, or iterate backward |
NullReferenceException accessing Player.Inventory | Player object is stale (disconnected) | Check player.IsConnected before accessing inventory properties |
| Item spawns but no one can pick it up | dedicated parameter set to false in ItemManager.dropItem | Always pass true for the dedicated server flag |
| Kit items not awarded on first server load | Kit configuration not loaded before kit command call | Ensure Configuration.Instance is accessed after plugin load completes |
| Silent failure when passing a GUID without hyphens | GUID string does not parse correctly | The GUID must include hyphens in the standard 8-4-4-4-12 format |
Frequently asked questions
Can I give an item to a player who is not connected?
No. GiveItem operates on a live UnturnedPlayer instance. For offline players, you would need to manipulate the player's save file directly, which is outside the RocketMod API scope.
What happens if I give a stackable item and the inventory is full?
GiveItem first attempts to merge the new item with an existing partial stack. If no partial stack exists and the inventory has no empty slots, the method returns false. The item is not lost; the calling code is responsible for handling the failure (e.g., dropping the item on the ground).
How do I give a weapon with specific attachments?
Use the numeric overload GiveItem(ushort id, byte amount, byte quality, byte durability, byte[] state) and encode the attachment IDs in the state byte array. The state format for weapons is documented in Unturned's ItemWeaponAsset class. Each attachment slot (sight, magazine, barrel, grip, tactical) occupies a specific offset in the state array.
Does RemoveItem drop the item on the ground or destroy it?
RemoveItem destroys the item. It does not spawn the item on the ground. To drop an item from inventory to the ground, use ItemManager.dropItem with the item's data before calling RemoveItem.
How do I check how many of a specific item a player has?
Iterate over the player's inventory pages and count matching item.id values:
csharp
public static int CountItem(UnturnedPlayer player, ushort itemId)
{
int count = 0;
for (byte page = 0; page < 5; page++)
{
for (byte index = 0; index < player.Inventory.getItemCount(page); index++)
{
ItemJar jar = player.Inventory.getItem(page, index);
if (jar != null && jar.item.id == itemId)
{
count += jar.item.amount;
}
}
}
return count;
}Can I prevent a player from dropping a specific item?
RocketMod does not have a built-in drop-blocking event. You can intercept the drop by subscribing to the appropriate player update packet, but this is an advanced technique that requires packet-level access through RocketMod's event system or a separate packet-interception library.
How do I give an item and play an effect at the same time?
Chain the GiveItem call with a TriggerEffect call. The item is added synchronously, so the effect plays after the item is confirmed in the inventory:
csharp
public static void GiveItemWithEffect(UnturnedPlayer player, ushort itemId, byte amount, string effectGuid)
{
bool success = player.GiveItem(itemId, amount, 100, 100, new byte[0]);
if (success)
{
player.TriggerEffect(effectGuid);
UnturnedChat.Say(player, $"Received item {itemId}.", Color.green);
}
}This pattern is used for loot rewards, quest completions, and shop purchases.
How do I check if a player's inventory contains a specific item without iterating all pages?
RocketMod does not provide a HasItem convenience method. The standard approach is to iterate over inventory pages and check each ItemJar.item.id. For performance-sensitive code, maintain a plugin-side dictionary that tracks item counts and is updated in response to OnPlayerInventoryAdded and OnPlayerInventoryRemoved events. This avoids per-check iteration at the cost of keeping the dictionary in sync.
How do I restore a player's inventory after a rollback?
If you need to restore a player's inventory to a previous state, maintain a snapshot queue in your plugin. Before any inventory-modifying operation, take a snapshot of the player's current inventory (iterate all pages and serialize item data to a list). Store the snapshot in a dictionary keyed by player Steam64 ID with a timestamp. When a rollback is requested, clear the player's inventory and re-add all items from the snapshot using GiveItem.
How do I give an item that exists in a Workshop mod that is not loaded on the server?
You cannot. The item asset must be loaded on the server for GiveItem to work. Verify that the Workshop mod containing the item is in the server's WorkshopDownloadConfig.json. If the item ID is valid but the asset is not loaded, GiveItem returns false with no error message.
Can I use GiveItem with item IDs above ushort.MaxValue?
No. The GiveItem numeric overload uses ushort (16-bit unsigned integer, max value 65535). Unturned item IDs do not exceed this range. If you try to pass a value above 65535, the compiler will reject the code. If you have a value from a string source, validate it with ushort.TryParse() before calling GiveItem.
How do I handle the state byte array for medical items?
Medical items (medkits, bandages, antibiotics) use the state byte array to encode their remaining heal amount. The first byte of the state array is the heal value (0–100). When giving medical items with partial heal remaining, set state[0] to the desired heal percentage. For full heal, pass 100 as the quality parameter and an empty state array — the game defaults to full heal.
What happens to item durability when using GiveItem with the GUID overload?
The GUID overload GiveItem(string, byte) spawns items with default quality (100) and default durability (100). If you need items with specific durability values, use the numeric overload GiveItem(ushort, byte, byte, byte, byte[]) and pass explicit quality and durability values. This is important for item-repair systems where item condition matters.
How do I prevent item duplication when using inventory events?
Item duplication via inventory events happens when your plugin calls GiveItem inside an OnPlayerInventoryAdded or OnPlayerInventoryRemoved handler, which can trigger the event again recursively. Use a re-entrancy guard flag:
csharp
private static bool _isHandlingInventoryEvent = false;
UnturnedPlayerEvents.OnPlayerInventoryAdded += (player, item) =>
{
if (_isHandlingInventoryEvent) return;
_isHandlingInventoryEvent = true;
try
{
// Your inventory event logic here
}
finally
{
_isHandlingInventoryEvent = false;
}
};This prevents the event handler from re-entering when your own GiveItem call fires the same event.
Can I give items to all players at once?
Iterate over all connected players and call GiveItem on each:
csharp
public static void GiveItemToAll(ushort itemId, byte amount)
{
foreach (SteamPlayer client in Provider.clients)
{
UnturnedPlayer player = UnturnedPlayer.FromSteamPlayer(client);
player.GiveItem(itemId, amount, 100, 100, new byte[0]);
}
}This pattern is used for server-wide give events, holiday rewards, and compensation distributions.
Inventory event-driven auto-sort
The following pattern automatically sorts a player's inventory when a new item is added. This keeps the inventory organized without manual player intervention:
csharp
public static class InventoryAutoSort
{
private static readonly HashSet<ulong> _pendingSort = new HashSet<ulong>();
public static void Initialize()
{
UnturnedPlayerEvents.OnPlayerInventoryAdded += (player, item) =>
{
ScheduleSort(player);
};
}
private static void ScheduleSort(UnturnedPlayer player)
{
ulong id = player.CSteamID.m_SteamID;
if (_pendingSort.Add(id))
{
AdminSuitePlugin.Instance.StartCoroutine(DelayedSort(player));
}
}
private static IEnumerator DelayedSort(UnturnedPlayer player)
{
yield return new WaitForSeconds(0.5f);
ulong id = player.CSteamID.m_SteamID;
_pendingSort.Remove(id);
SortInventory(player);
}
public static void SortInventory(UnturnedPlayer player)
{
// Read all items from page 0 (items page)
List<ItemData> items = new List<ItemData>();
byte count = player.Inventory.getItemCount(0);
for (byte i = 0; i < count; i++)
{
ItemJar jar = player.Inventory.getItem(0, i);
if (jar != null)
{
items.Add(new ItemData
{
Id = jar.item.id,
Amount = jar.item.amount,
Quality = jar.item.quality,
State = jar.item.state
});
}
}
// Clear the page
for (byte i = 0; i < count; i++)
player.RemoveItem(0, 0);
// Sort by item ID
items.Sort((a, b) => a.Id.CompareTo(b.Id));
// Re-add in sorted order
foreach (ItemData item in items)
{
player.GiveItem(item.Id, item.Amount, item.Quality, 100, item.State);
}
}
private class ItemData
{
public ushort Id;
public byte Amount;
public byte Quality;
public byte[] State;
}
}Cross-references
- RocketMod and OpenMod Plugin Basics — plugin lifecycle, event subscription, permission system.
- Player God Mode and Vanish — the previous article; god mode and vanish feature reference.
- Vehicles — the next article; vehicle spawn, repair, and management.
- Chat Messaging — sending formatted messages to players.
- Triggering Effects — playing visual and audio effects for inventory events.
- Server Commands Reference — built-in give and clear commands.
- Conditions Reference — item condition and durability mechanics.
Document history
| Version | Date | Author | Notes |
|---|---|---|---|
| 1.0 | 2025-07-27 | 57 Studios | Initial publication. GiveItem, RemoveItem, ClearInventory API reference, kit implementation, inventory events, safeguards, and diagnostics. |
