ItemLibraryAsset — Skill Libraries and XP Storage
Overview
ItemLibraryAsset extends ItemBarricadeAsset and represents placeable library or kiosk structures. Libraries serve as NPC quest integration points where players deposit items in exchange for XP or quest progress. They function as intermediary storage containers tied to the NPC economy: players place items into the library, NPC quests or skill systems withdraw from it, and a configurable tax rate takes a percentage of the transaction.
Despite its name, the library is not limited to books or skill-related items. Any item can be deposited and withdrawn, making libraries a generalized item-exchange kiosk for the NPC and quest pipeline.
Inheritance Chain
Asset
└── ItemAsset
└── ItemBarricadeAsset
└── ItemLibraryAssetItemLibraryAsset sits on the barricade chain, meaning libraries are placeable structures that occupy world space. They inherit barricade properties: health, salvageable items, placement requirements, ownership, and the barricade networking pipeline. Libraries cannot be picked up once placed (standard barricade behavior) and are subject to the same destruction/salvage rules as other barricades.
Fields
Capacity (_capacity)
csharp
protected uint _capacity;
public uint capacity => _capacity;The maximum number of item stacks that can be stored in the library. This is a count of distinct item slots, not a weight or volume limit. A library with Capacity 10 can hold 10 different item types, each with its own stack amount.
Parsed from the .dat key Capacity:
csharp
_capacity = p.data.ParseUInt32("Capacity");If Capacity is absent from the .dat, ParseUInt32 returns 0 — a library with zero capacity cannot hold any items. This is effectively a non-functional library, but it still loads without error. Modders must always specify a positive capacity.
Tax (_tax)
csharp
protected byte _tax;
public byte tax => _tax;A tax rate applied to item exchanges through the library. Stored as a byte with range 0-255. The interpretation of this value depends on the game mode or plugin consuming it — in vanilla NPC systems, the tax represents a percentage of items consumed during the exchange.
Parsed from the .dat key Tax:
csharp
_tax = p.data.ParseUInt8("Tax");Tax Range and Overflow
Because _tax is a byte, values above 255 wrap around:
- 0: No tax. All items pass through.
- 50: 50% tax. Half the items are consumed.
- 100: 100% tax. All items are consumed (the library destroys items, nothing passes to the NPC).
- 200: Stored as 200 (
byterange allows it). - 256: Overflows to 0 (256 % 256 = 0). Appears as no tax.
- 300: Overflows to 44 (300 % 256 = 44). Appears as 44% tax.
Values above 100 can produce unexpected behavior in game modes that interpret the tax as a percentage. A 150% tax (byte 150) might consume all input items and attempt to consume additional items, or behave as a ceiling-clamped 100%.
Modders should keep tax values in the range 0-100 to avoid overflow and semantics issues.
State Serialization (getState)
Barricade assets define a getState method that returns a byte array representing the barricade's mutable state for network replication:
csharp
public override byte[] getState(EItemOrigin origin)
{
return new byte[20];
}The library returns a fixed 20-byte array. The layout (inferred from barricade conventions):
| Offset | Size | Field |
|---|---|---|
| 0 | 8 bytes | Owner player ID (ulong) |
| 8 | 8 bytes | Group ID (ulong) |
| 16 | 4 bytes | Item count or library-specific data |
The first 16 bytes are the standard barricade ownership header: which player placed the library and which Steam group it belongs to. The remaining 4 bytes store the library's item count or other mutable state.
The EItemOrigin parameter distinguishes between server-authoritative state (EItemOrigin.WORLD) and client-predicted state (EItemOrigin.CRAFT), but the library returns the same 20 bytes regardless — it does not maintain separate server/client state views.
Ownership Bytes
The two ulong values encode:
- Owner: The 64-bit Steam ID of the player who placed the library. A value of 0 means the library has no owner (rare, typically indicates a bug or admin-placed object).
- Group: The 64-bit Steam group ID. If the library is placed for a group, this is the group's ID. If placed for personal use, this is typically 0 or matches the player's personal group.
Data Bytes
The final 4 bytes can encode:
- The number of items currently stored in the library (up to
_capacity). - A bitmask of occupied slots.
- A checksum or version counter for the library's content.
The exact encoding is determined by the game mode or plugin that processes library interactions. The library asset itself only defines the buffer size; the meaning of bytes 16-19 is a runtime concern.
Runtime Behavior: NPC Quest Integration
Libraries are primarily used as intermediary storage for NPC item turn-in quests. The flow:
Deposit Flow
- Player interacts with a placed library barricade.
- A UI opens showing the library's storage slots (up to
_capacityslots). - The player drags items from their inventory into the library storage.
- The server validates the transfer: checks capacity, removes items from player inventory, adds to library state.
- NPC quests can now query the library for deposited items.
Withdrawal Flow (Quest Turn-In)
- Player interacts with an NPC.
- The NPC quest condition checks the library for required items.
- If sufficient items are present: the library applies the tax rate.
- Taxed items are destroyed (removed from library storage).
- Remaining items are "turned in" — the quest progresses, NPC grants rewards.
- The player receives XP, items, or quest advancement.
Tax Application
The tax is applied during withdrawal. If the library has Tax 20 and the player deposits 10 items, the NPC withdrawal:
- The NPC needs 10 items from the library.
- The tax takes 20%: 2 items are destroyed.
- 8 items count toward the quest.
- The player must deposit 10 items to get credit for 8 (assuming the NPC needs exactly 10, they'd need to deposit 13:
ceil(10 / 0.8) = 13).
If Tax is 0, all deposited items count fully toward quest progress.
Item Storage Mechanics
Slot Model
The library uses a slot-based storage model. Each slot can hold one item type with a stack amount. The _capacity field is the total number of slots, not a weight or volume cap.
A library with Capacity 5 can hold up to 5 different item types. Each slot can hold a stack up to the item's maximum stack size. For example:
- Slot 1: 50 logs (stackable)
- Slot 2: 1 rifle (non-stackable)
- Slot 3: 20 bandages
- Slot 4: empty
- Slot 5: empty
Full Capacity Behavior
When the library is full (all slots occupied), the player cannot deposit additional items. The UI should show slots as occupied and reject new item drags. If the player has items that match an existing slot's item type, they can add to that stack if it hasn't reached max stack size.
Item Removal
The NPC quest system removes items from the library during withdrawal. The library state is updated to reflect the reduced stacks. If a stack reaches zero, the slot is freed for new deposits.
Per-Frame and Network Behavior
Network Replication
Library state is replicated to all clients in range via the barricade networking system. The 20-byte state buffer is sent whenever the library's content changes (item deposited, item withdrawn, ownership changed). The update is sent to the barricade's relevancy region — typically all clients within a configurable radius.
Clients receive state updates and update their local UI to reflect the current library contents. The server is authoritative for all state changes; clients send item transfer requests that the server validates before applying.
Save/Load Persistence
Libraries persist across server restarts. The barricade save system serializes the library's state buffer along with its position, rotation, health, and ownership. On server restart, the library is reconstructed with its previous contents intact.
Cargo Data Export
ItemLibraryAsset overrides BuildCargoData to export library-specific fields to the Library Cargo table:
csharp
internal override void BuildCargoData(CargoBuilder builder)
{
base.BuildCargoData(builder);
CargoDeclaration data = builder.GetOrAddDeclaration("Library");
data.Append("GUID", GUID); // Key
data.Append("Capacity", capacity);
data.Append("Tax", tax);
}The Library Cargo table exports three fields:
GUID: The asset's GUID (primary key). Links to the base item Cargo table.Capacity: Maximum item stacks.Tax: Tax rate (0-255).
The base class BuildCargoData is called first, which exports the barricade-level and item-level fields to their respective Cargo tables.
Comparison with ItemStorageAsset
Libraries and storage containers (ItemStorageAsset) share the barricade storage pattern but serve different purposes:
| Feature | ItemLibraryAsset | ItemStorageAsset |
|---|---|---|
| Base class | ItemBarricadeAsset | ItemBarricadeAsset |
| Capacity field | _capacity (uint) | Width × Height grid |
| Storage model | Slot-based (item count) | Grid-based (inventory grid) |
| Tax | Yes (_tax) | No |
| Primary purpose | NPC quest turn-in | Player storage |
| State buffer | 20 bytes | Configurable |
| UI | Quest-integrated UI | Inventory-style grid UI |
Libraries are designed for automated NPC interactions; storage containers are designed for manual player interactions. Libraries have tax mechanics; storage containers don't. Libraries use slot-based capacity; storage containers use grid-based capacity.
Modding Guide
Creating a Library
Minimum .dat:
ID 58100
ItemName "Quest Library"
Rarity Uncommon
Size_X 2
Size_Y 2
Slot None
Capacity 10
Tax 5This creates a library with 10 storage slots and a 5% tax rate. The item is 2×2 in the inventory (when unplaced) and becomes a barricade when placed in the world.
Library with No Tax
Tax 0Or omit the Tax key entirely (defaults to 0 since ParseUInt8("Tax") with no key returns 0).
High-Capacity Tax Shelter
Capacity 50
Tax 1A large library with minimal tax for servers that want item turn-in without significant tax burden.
Integrating with NPC Quests
Libraries integrate with NPC quests through the quest condition system. An NPC condition of type "Library_Has_Items" can check whether a placed library within range contains specific items. The NPC dialogue can then trigger a withdrawal.
The exact condition keys depend on the game mode's NPC system, but typically involve:
Library_ID: The item ID of the library to check.Required_Item_ID: The item that must be in the library.Required_Amount: The quantity needed (before tax).Search_Radius: How far from the NPC to search for libraries.
Common Pitfalls
Zero capacity: Omitting
Capacityresults in a library that can't store anything. Always specify a positive capacity.Tax overflow: Values above 255 wrap around. Keep tax in 0-100 for predictable behavior.
Tax = 100 means no items pass through: A 100% tax consumes all deposited items. The NPC receives nothing. The library becomes a black hole for items. Use tax rates below 100 for functional quest turn-in.
State buffer size: The 20-byte state buffer is fixed. If a mod or plugin needs more state (e.g., tracking individual item qualities or metadata), it must extend the barricade state system, not the
getStatemethod.Library placement restrictions: As a barricade, libraries are subject to placement restrictions (on floor, not on walls, within buildable area). Libraries placed outside buildable zones are destroyed on server cleanup.
Multiple libraries per player: A player can place multiple libraries. NPC quests typically query the nearest library, not all libraries. Placing libraries at multiple quest hubs can optimize turn-in routes.
Library destruction: If a library is destroyed (by zombies, raiders, or decay), all stored items are lost. The barricade destruction system drops salvage items based on the barricade's salvage table, not the library's stored items. Contents are not recoverable.
State Serialization Deep Dive
Byte Layout of getState()
The 20-byte state buffer returned by getState follows the barricade state convention. While the SDK only defines the buffer size, the conventional layout is:
Offset Size Field
0 8 Owner Steam ID (ulong), little-endian
8 8 Group Steam ID (ulong), little-endian
16 1 Item count (byte), number of occupied slots
17 1 Flags byte: bit 0 = has owner, bit 1 = has group
18 2 Reserved / paddingSerialization at Save Time
When the server saves barricade state to disk, it calls getState(EItemOrigin.WORLD) and serializes the returned byte array. On load, the byte array is passed back to the barricade's deserialization logic. The library's 20-byte state is always the same size, so the save system can use fixed-size record storage for efficiency.
Client vs. Server State
The EItemOrigin parameter distinguishes between server-authoritative state (EItemOrigin.WORLD) and client-predicted state (EItemOrigin.CRAFT). For libraries:
WORLDorigin: Current actual state, used for networking and saving.CRAFTorigin: Client-side prediction state, used during placement preview.
The library returns the same byte array for both origins — it does not maintain divergent client/server views. This is appropriate because library state changes only occur via server-authoritative transactions (item deposit/withdrawal), never via client prediction.
State Buffer Size Constraints
The 20-byte buffer is the maximum state size that fits in a single barricade network update packet alongside position, rotation, health, and metadata. If a mod requires more library state (e.g., per-item metadata, timestamps, deposit history), the options are:
- Extend getState: Increase the buffer size (requires code modification).
- External database: Store extended state in a separate database keyed by barricade instance ID.
- Custom barricade component: Attach a custom network component that sends additional state on a separate channel.
NPC Quest Integration Patterns
Condition Types
NPC quest systems use several condition types to interact with libraries:
Item_Stored: Checks whether a specific item exists in any library within range. Useful for "deposit N items" quests.
Item_Withdrawn: Checks whether a specific item has been withdrawn from a library. Tracks quest progress.
Library_Value: Checks the total value of items stored, using a ItemCurrencyAsset for valuation. Useful for "donate $1000 worth of items" quests.
Library_Capacity_Used: Checks what percentage of the library's capacity is filled. Useful for storage management quests.
Tax Interaction with Quests
When a quest requires depositing 10 items and the library has a 20% tax:
- Player deposits 10 items.
- Quest checks:
storedItems.count >= requiredCount→ 10 >= 10 → condition met. - NPC withdrawal: library processes the withdrawal, applying 20% tax.
- 2 items are destroyed (tax). 8 items are transferred to the NPC.
- The quest progresses to the next stage.
The quest checks raw deposited count, NOT post-tax count. The tax is applied at withdrawal time, which may be a separate quest stage from the deposit stage. A quest designer must account for this: if the NPC needs 8 items, the player must deposit 10 (10 - 20% = 8).
Cross-Library Quests
NPCs can query multiple libraries simultaneously if they're within range. A quest condition might be:
- "Any library within 50 meters has 5 bandages" → satisfied if any single library has them.
- "The total across all libraries within 50 meters is 20 bandages" → satisfied by aggregated counts.
The library search radius is configurable per quest condition. A radius of 0 means the NPC only checks the single nearest library. A large radius checks all libraries in the area.
Economy and Balancing
Tax as an Economic Sink
Libraries with non-zero tax serve as item sinks — they remove items from the economy. In a server with many players depositing items into libraries, the tax consumes a percentage of all deposits permanently. This helps combat inflation in economies where items are generated faster than they're consumed.
The tax rate should be calibrated to the server's item generation rate:
- High spawn rate, generous loot tables → higher tax (15-25%) to sink excess items.
- Low spawn rate, scarce resources → lower tax (0-5%) to avoid starving players.
- Quest-critical items → zero tax to avoid blocking progression.
Capacity vs. Server Load
Large-capacity libraries (50+ slots) store more items, reducing the need for players to craft storage containers. However, each library instance on the map consumes server memory proportional to its capacity. A server with 100 players, each with 3 libraries of 50 slots, stores 15,000 item slots in libraries alone.
Item storage in barricades is serialized as individual Item instances (C# objects), each with metadata (quality, durability, attachments). At ~200 bytes per item, 15,000 items consume ~3 MB — modest for modern hardware but grows with server population and library density.
Tax Revenue Tracking
Some game mode configs track tax revenue for leaderboards or faction competition. Each library withdrawal logs the tax amount, the item types consumed, and the player/NPC involved. This data feeds into:
- Faction tax income statistics.
- NPC vendor restocking calculations (taxed items replenish vendor inventory).
- Economy health dashboards.
Comparison: Library vs. ItemStorageAsset vs. Vehicle Storage
| Feature | Library | Storage Crate | Vehicle Storage |
|---|---|---|---|
| Base class | ItemBarricadeAsset | ItemBarricadeAsset | VehicleAsset (+ storage component) |
| Capacity model | Slot count (uint) | Width × Height grid | Width × Height grid |
| Tax | Supported | No | No |
| NPC integration | Primary purpose | Not integrated | Not integrated |
| Ownership | Owner + Group | Owner + Group | Vehicle owner + lock |
| Access control | None (any NPC can query) | Owner/group only | Vehicle lock system |
| Destruction loot | Salvage table only | Drops stored items | Drops stored items |
| State buffer | 20 bytes | Variable (grid size dependent) | Variable |
| Primary use case | Quest turn-in | Player item storage | Mobile item transport |
.dat Reference Table
ItemLibraryAsset Complete .dat Schema
| Key | Type | Default | Required | Description |
|---|---|---|---|---|
ID | ushort | — | Yes | Unique item ID |
ItemName | string | — | Yes | Display name |
ItemDescription | string | "" | No | Tooltip description |
Rarity | EItemRarity | Common | No | Item rarity tier |
Size_X | byte | 1 | No | Inventory slot width (when unplaced) |
Size_Y | byte | 1 | No | Inventory slot height (when unplaced) |
Slot | ESlotType | None | No | Equipment slot |
Capacity | uint | 0 | Yes | Maximum item stacks storable |
Tax | byte | 0 | No | Tax rate (0-100 recommended) |
Barricade Base Class Fields (Inherited)
| Key | Type | Default | Description |
|---|---|---|---|
Health | ushort | 100 | Barricade hit points |
Salvage_Item_ID | ushort | 0 | Item received when salvaged |
Salvage_Item_Count | byte | 0 | Number of salvage items |
Placement_Min_Angle | float | 0 | Minimum placement slope angle |
Placement_Max_Angle | float | 90 | Maximum placement slope angle |
Allow_Placement_On_Walls | bool | false | Can place on vertical surfaces |
Buildable | EBuildableType | All | Where placement is allowed |
Debugging and Troubleshooting
Symptom: "Library doesn't accept items"
Check in order:
- Verify
Capacityis set to a positive value. - Verify the library is placed (barricade state is active).
- Verify the library is not full (occupied slots < capacity).
- Verify the item is not prohibited by the NPC quest config.
- Verify the player is within interaction range.
- Check server logs for transfer rejection messages.
Symptom: "NPC quest doesn't detect deposited items"
Check:
- Verify the NPC has a quest condition referencing the library item type.
- Verify the library is within the NPC's search radius.
- Verify the deposited item ID matches the quest condition's required item ID.
- Verify the quest condition checks deposited count, not post-tax count.
- Verify no other plugin is consuming items from the library before the quest check.
- Enable verbose NPC quest logging to trace condition evaluation.
Symptom: "Tax consumes all items (100% tax)"
Check:
- Verify the
Taxvalue in the library .dat is below 100. - Verify the game mode config isn't applying an additional tax modifier.
- Check if a plugin is modifying the tax rate at runtime.
- Review server logs for tax calculation values.
Symptom: "Items vanish after server restart"
- Verify the barricade save system is enabled and functioning.
- Check if the barricade is in a restricted area cleaned up on restart.
- Verify the library's state buffer (20 bytes) is being serialized correctly.
- Check for plugin interference with barricade save/load hooks.
Library Placement Strategies
Quest Hub Design
Place libraries near NPC quest givers for ergonomic turn-in:
NPC Area Layout:
[Quest NPC] ← 5m → [Library] ← 5m → [Storage Crate]
Player flow: Get quest → Gather items → Deposit in library → Talk to NPCThe library should be within the NPC's default interaction radius (typically 10-20m) for automatic detection.
Multi-Library Networks
For large quest areas with multiple NPCs:
[Library A] ← NPC 1 (medical quests)
[Library B] ← NPC 2 (construction quests)
[Library C] ← NPC 3 (food quests)Each library is themed to its NPC's quest type. Players deposit medical items in Library A, construction materials in Library B, etc. This keeps quest progress organized and prevents cross-contamination.
Library Protection
Libraries hold valuable items and are targets for raiders:
- Place inside fortified structures.
- Surround with other barricades for additional HP buffer.
- Place away from high-traffic zombie paths to avoid accidental destruction.
- Use the barricade ownership system to restrict who can interact (if supported by game mode).
Cargo Data Field Reference
Library Cargo Table
| Field | Type | Description |
|---|---|---|
GUID | string | Asset GUID (primary key, links to base item table) |
Capacity | uint | Maximum storable item stacks |
Tax | byte | Tax rate (0-255, raw byte value) |
Inherited Cargo Tables
The library participates in multiple Cargo tables through inheritance:
| Table | Source Class | Key Fields |
|---|---|---|
Item | ItemAsset | GUID, ID, Rarity, Size_X, Size_Y |
Barricade | ItemBarricadeAsset | GUID, Health, Salvage_Item_ID |
Library | ItemLibraryAsset | GUID, Capacity, Tax |
The Cargo data wiki uses GUID-based joins to link records across these tables.
