Skip to content

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
              └── ItemLibraryAsset

ItemLibraryAsset 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 (byte range 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):

OffsetSizeField
08 bytesOwner player ID (ulong)
88 bytesGroup ID (ulong)
164 bytesItem 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

  1. Player interacts with a placed library barricade.
  2. A UI opens showing the library's storage slots (up to _capacity slots).
  3. The player drags items from their inventory into the library storage.
  4. The server validates the transfer: checks capacity, removes items from player inventory, adds to library state.
  5. NPC quests can now query the library for deposited items.

Withdrawal Flow (Quest Turn-In)

  1. Player interacts with an NPC.
  2. The NPC quest condition checks the library for required items.
  3. If sufficient items are present: the library applies the tax rate.
  4. Taxed items are destroyed (removed from library storage).
  5. Remaining items are "turned in" — the quest progresses, NPC grants rewards.
  6. 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:

  1. The NPC needs 10 items from the library.
  2. The tax takes 20%: 2 items are destroyed.
  3. 8 items count toward the quest.
  4. 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:

FeatureItemLibraryAssetItemStorageAsset
Base classItemBarricadeAssetItemBarricadeAsset
Capacity field_capacity (uint)Width × Height grid
Storage modelSlot-based (item count)Grid-based (inventory grid)
TaxYes (_tax)No
Primary purposeNPC quest turn-inPlayer storage
State buffer20 bytesConfigurable
UIQuest-integrated UIInventory-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 5

This 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 0

Or omit the Tax key entirely (defaults to 0 since ParseUInt8("Tax") with no key returns 0).

High-Capacity Tax Shelter

Capacity 50
Tax 1

A 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

  1. Zero capacity: Omitting Capacity results in a library that can't store anything. Always specify a positive capacity.

  2. Tax overflow: Values above 255 wrap around. Keep tax in 0-100 for predictable behavior.

  3. 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.

  4. 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 getState method.

  5. 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.

  6. 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.

  7. 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 / padding

Serialization 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:

  • WORLD origin: Current actual state, used for networking and saving.
  • CRAFT origin: 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:

  1. Extend getState: Increase the buffer size (requires code modification).
  2. External database: Store extended state in a separate database keyed by barricade instance ID.
  3. 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:

  1. Player deposits 10 items.
  2. Quest checks: storedItems.count >= requiredCount → 10 >= 10 → condition met.
  3. NPC withdrawal: library processes the withdrawal, applying 20% tax.
  4. 2 items are destroyed (tax). 8 items are transferred to the NPC.
  5. 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

FeatureLibraryStorage CrateVehicle Storage
Base classItemBarricadeAssetItemBarricadeAssetVehicleAsset (+ storage component)
Capacity modelSlot count (uint)Width × Height gridWidth × Height grid
TaxSupportedNoNo
NPC integrationPrimary purposeNot integratedNot integrated
OwnershipOwner + GroupOwner + GroupVehicle owner + lock
Access controlNone (any NPC can query)Owner/group onlyVehicle lock system
Destruction lootSalvage table onlyDrops stored itemsDrops stored items
State buffer20 bytesVariable (grid size dependent)Variable
Primary use caseQuest turn-inPlayer item storageMobile item transport

.dat Reference Table

ItemLibraryAsset Complete .dat Schema

KeyTypeDefaultRequiredDescription
IDushortYesUnique item ID
ItemNamestringYesDisplay name
ItemDescriptionstring""NoTooltip description
RarityEItemRarityCommonNoItem rarity tier
Size_Xbyte1NoInventory slot width (when unplaced)
Size_Ybyte1NoInventory slot height (when unplaced)
SlotESlotTypeNoneNoEquipment slot
Capacityuint0YesMaximum item stacks storable
Taxbyte0NoTax rate (0-100 recommended)

Barricade Base Class Fields (Inherited)

KeyTypeDefaultDescription
Healthushort100Barricade hit points
Salvage_Item_IDushort0Item received when salvaged
Salvage_Item_Countbyte0Number of salvage items
Placement_Min_Anglefloat0Minimum placement slope angle
Placement_Max_Anglefloat90Maximum placement slope angle
Allow_Placement_On_WallsboolfalseCan place on vertical surfaces
BuildableEBuildableTypeAllWhere placement is allowed

Debugging and Troubleshooting

Symptom: "Library doesn't accept items"

Check in order:

  1. Verify Capacity is set to a positive value.
  2. Verify the library is placed (barricade state is active).
  3. Verify the library is not full (occupied slots < capacity).
  4. Verify the item is not prohibited by the NPC quest config.
  5. Verify the player is within interaction range.
  6. Check server logs for transfer rejection messages.

Symptom: "NPC quest doesn't detect deposited items"

Check:

  1. Verify the NPC has a quest condition referencing the library item type.
  2. Verify the library is within the NPC's search radius.
  3. Verify the deposited item ID matches the quest condition's required item ID.
  4. Verify the quest condition checks deposited count, not post-tax count.
  5. Verify no other plugin is consuming items from the library before the quest check.
  6. Enable verbose NPC quest logging to trace condition evaluation.

Symptom: "Tax consumes all items (100% tax)"

Check:

  1. Verify the Tax value in the library .dat is below 100.
  2. Verify the game mode config isn't applying an additional tax modifier.
  3. Check if a plugin is modifying the tax rate at runtime.
  4. Review server logs for tax calculation values.

Symptom: "Items vanish after server restart"

  1. Verify the barricade save system is enabled and functioning.
  2. Check if the barricade is in a restricted area cleaned up on restart.
  3. Verify the library's state buffer (20 bytes) is being serialized correctly.
  4. 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 NPC

The 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

FieldTypeDescription
GUIDstringAsset GUID (primary key, links to base item table)
CapacityuintMaximum storable item stacks
TaxbyteTax rate (0-255, raw byte value)

Inherited Cargo Tables

The library participates in multiple Cargo tables through inheritance:

TableSource ClassKey Fields
ItemItemAssetGUID, ID, Rarity, Size_X, Size_Y
BarricadeItemBarricadeAssetGUID, Health, Salvage_Item_ID
LibraryItemLibraryAssetGUID, Capacity, Tax

The Cargo data wiki uses GUID-based joins to link records across these tables.