@reldens/items-system
Version:
Reldens - Items System
657 lines (524 loc) • 21.7 kB
Markdown
# Items System - Detailed Architecture
This document provides in-depth technical documentation for the @reldens/items-system package.
## Class Hierarchy
```
ItemsServer
├── ItemsManager (extends Inventory)
│ └── uses ItemTypes
└── Sender
Inventory
├── ItemsManager
└── ItemGroup
ItemBase
├── ItemSingle
├── ItemEquipment
├── Usable
├── SingleEquipment (extends ItemSingle + Equipment behavior)
└── SingleUsable (extends ItemSingle + Usable behavior)
ExchangePlatform
├── RequirementsProcessor
│ └── RequirementsCollection
│ └── ExchangeRequirement[]
└── RewardsProcessor
└── RewardsCollection
└── ExchangeReward[]
```
## Core Classes Detail
### ItemsServer (`lib/server.js`)
Entry point for server-side item management. Wraps ItemsManager and Sender together.
**Constructor Props:**
- `owner` (required) - The entity that owns this items system
- `client` (optional) - Client connection for sending updates
- All ItemsManager props
**Properties:**
- `manager` - ItemsManager instance
- `client` - Sender instance (if client provided)
- `hasError` - Boolean flag for initialization errors
**Usage:**
```javascript
let itemsServer = new ItemsServer({
owner: player,
client: playerClient,
itemClasses: {...},
itemsModelData: {...}
});
```
### ItemsManager (`lib/manager.js`)
Extends Inventory with advanced features for managing items with model data.
**Constructor Props:**
- `owner` (required) - Owner entity
- `itemClasses` - Map of item key to item class
- `groupClasses` - Map of group configurations
- `itemsModelData` - Item model data from database
- `ownerIdProperty` - Property name for owner ID (default: 'id')
- `eventsPrefix` - Prefix for event names
- All Inventory props
**Properties:**
- `owner` - Owner entity reference
- `groups` - Object containing ItemGroup instances
- `types` - ItemTypes instance
- `eventsPrefix` - Event name prefix
- Inherits all Inventory properties
**Key Methods:**
- `setup(props)` - Async initialization with items and groups
- `createItemInstance(key, qty)` - Creates item instance(s) from model data
- `getOwnerId()` - Returns owner ID
- `getOwnerEventKey()` - Returns owner-specific event key
- `getOwnerUniqueEventKey(suffix)` - Returns unique event key with timestamp
**Events Fired:**
- `MANAGER_INIT` - On setup completion
### Inventory (`lib/item/inventory.js`)
Core inventory management with add/remove/modify operations.
**Constructor Props:**
- `eventsManager` - EventsManager instance (default: EventsManagerSingleton)
- `limitPerItem` - Max quantity per item (-1 = disabled)
- `itemsLimit` - Max total items (-1 = disabled)
- `applyModifiersAuto` - Auto-apply modifiers (default: true)
- `revertModifiersAuto` - Auto-revert modifiers (default: true)
- `eventsPrefix` - Event name prefix
**Properties:**
- `items` - Object containing item instances (key: inventoryId)
- `limitPerItem` - Quantity limit per item
- `itemsLimit` - Total items limit
- `locked` - Boolean, prevents modifications when true
- `lastError` - Last ItemsError instance
- `frozenItems` - Temporary item storage during exchanges
**Key Methods:**
- `validate(item)` - Async validation of item instance
- `addItem(item)` - Async add item with validation
- `addItems(itemsArray)` - Async add multiple items
- `removeItem(key)` - Async remove item by key
- `setItemQty(key, qty)` - Set item quantity
- `increaseItemQty(key, qty)` - Increase item quantity
- `decreaseItemQty(key, qty)` - Decrease item quantity
- `modifyItemQty(op, key, qty)` - Generic quantity modifier
- `setItems(items)` - Replace all items
- `setGroups(groups)` - Set groups object
- `findItemByKey(itemKey)` - Find item by key property
- `findItemsByPropertyValue(propertyKey, propertyValue)` - Find items by any property
- `fireEvent(eventName, ...args)` - Emit event
- `listenEvent(eventName, callback, removeKey, masterKey)` - Listen to event
**Events Fired:**
- `VALIDATE` - On item validation
- `ADD_ITEM_BEFORE` - Before adding item
- `ADD_ITEM` - After adding item
- `REMOVE_ITEM` - On removing item
- `MODIFY_ITEM_QTY` - On quantity modification
- `SET_ITEMS` - On setting items
- `SET_GROUPS` - On setting groups
**Error Codes:**
- `UNDEFINED_ITEM` - Item is undefined
- `UNDEFINED_METHOD_INVENTORY_ID` - Item missing getInventoryId method
- `UNDEFINED_ITEM_KEY` - Item missing key property
- `INVALID_ITEM_INSTANCE` - Item failed validation
- `LOCKED_FOR_ADD_ITEM` - Inventory locked, cannot add
- `MAX_TOTAL_REACHED_FOR_ADD_ITEM` - Items limit reached
- `ITEM_EXISTS_FOR_ADD_ITEM` - Item already exists (non-single items)
- `ITEM_LIMIT_EXCEEDED_FOR_ADD_ITEM` - Quantity exceeds limit
- `LOCKED_FOR_SET_ITEM` - Inventory locked, cannot set
- `LOCKED_FOR_REMOVE_ITEM` - Inventory locked, cannot remove
- `KEY_NOT_FOUND` - Item key not found
- `LOCKED_FOR_MODIFY_ITEM_QTY` - Inventory locked, cannot modify quantity
- `UNDEFINED_ITEM_KEY_FOR_OPERATION` - Missing item key for operation
- `QTY_NOT_A_NUMBER` - Quantity is not a number
- `ITEM_QTY_LIMIT_EXCEEDED` - Quantity exceeds per-item limit
- `LOCKED_FOR_SET_ITEMS` - Inventory locked, cannot set items
### ItemGroup (`lib/item/group.js`)
Extends Inventory for categorized item storage with separate limits.
**Constructor Props:**
- `id` (required) - Group ID
- `key` (required) - Group key
- `label` - Display label
- `description` - Group description
- `files_name` - Associated file name (e.g., for icons)
- `sort` - Sort order
- `items_limit` - Max items in this group
- `limit_per_item` - Max quantity per item in this group
- All Inventory props
**Properties:**
- All Inventory properties
- `id` - Group database ID
- `key` - Group unique key
- `label` - Display name
- `description` - Group description
- `files_name` - Icon/image file name
- `sort` - Sort order for display
**Use Cases:**
- Equipment bag (weapons, armor)
- Consumables bag (potions, food)
- Quest items bag
- Material storage
- Currency pouch
- Any categorized inventory with separate limits
## Item Type Classes
### ItemBase (`lib/item/type/item-base.js`)
Base class for all item types. Contains core properties and modifier handling.
**Constructor Props:**
- `key` (required) - Item type key
- `manager` (required) - ItemsManager instance
- `uid` - Unique instance ID (auto-generated if not provided)
- `id` - Inventory item instance ID (database)
- `item_id` - Item type ID (database)
- `label` - Display name
- `description` - Item description
- `type` - Item type constant
- `qty` - Item quantity
- `remaining_uses` - Remaining uses for consumables
- `is_active` - Active state (e.g., equipped)
- `group_id` - Associated group ID
- `qty_limit` - Quantity limit for this item
- `uses_limit` - Uses limit for this item
- `autoRemoveItemOnZeroQty` - Auto-remove when qty reaches 0 (default: true)
- `useTimeOut` - Timeout for use action
- `execTimeOut` - Timeout for execute action
- `modifiers` - Object of modifier instances
- `customData` - Custom data object (JSON or object)
**Properties:**
- `key` - Item type identifier
- `uid` - Unique instance identifier
- `id` - Database instance ID
- `item_id` - Database type ID
- `label` - Display name
- `description` - Description text
- `manager` - ItemsManager reference
- `type` - Type constant
- `qty` - Quantity
- `remaining_uses` - Uses remaining
- `is_active` - Active state
- `group_id` - Group association
- `qty_limit` - Max quantity
- `uses_limit` - Max uses
- `autoRemoveItemOnZeroQty` - Auto-remove flag
- `useTimeOut` - Use timeout value
- `execTimeOut` - Execute timeout value
- `modifiers` - Modifiers object
- `target` - Target entity for modifiers
- `singleInstance` - Single instance flag (default: false)
- `rawCustomData` - Raw custom data
- Custom properties from customData
**Key Methods:**
- `static isSingleInstance()` - Returns false (overridden in Single types)
- `getInventoryId()` - Returns key (single) or uid (unique)
- `applyModifiers()` - Async apply all modifiers to target
- `revertModifiers()` - Async revert all modifiers from target
- `changeModifiers(revert)` - Generic modifier change method
- `isType(type)` - Check if item is specific type
- `fireEvent(eventName, ...args)` - Fire event through manager
- `listenEvent(eventName, callback, removeKey, masterKey)` - Listen to event
**Events Fired:**
- `EQUIP_BEFORE+ApplyModifiers` - Before applying modifiers
- `EQUIP_BEFORE+RevertModifiers` - Before reverting modifiers
- `EQUIP+AppliedModifiers` - After applying modifiers
- `EQUIP+RevertedModifiers` - After reverting modifiers
### ItemSingle (`lib/item/type/single.js`)
Single-instance items where quantity is grouped in one inventory slot.
**Key Differences:**
- `static isSingleInstance()` - Returns true
- `singleInstance` property set to true in constructor
- Uses `key` as inventory ID instead of `uid`
- Multiple items with same key share one inventory slot
**Use Cases:**
- Stackable potions
- Arrows/ammunition
- Crafting materials
- Currency
### ItemEquipment (`lib/item/type/equipment.js`)
Equippable items that can apply/revert modifiers.
**Additional Methods:**
- `async equip(target, applyModifiers)` - Equip item to target
- `async unequip(target, revertModifiers)` - Unequip item from target
**Events Fired:**
- `EQUIP_ITEM` - On equip
- `UNEQUIP_ITEM` - On unequip
**Use Cases:**
- Weapons
- Armor
- Accessories
- Equipment that modifies stats
### Usable (`lib/item/type/usable.js`)
Consumable/usable items with execute functionality.
**Additional Methods:**
- `async execute(target)` - Execute item usage
- Custom execution logic can be added via modifiers or events
**Events Fired:**
- `EXECUTING_ITEM` - Before execution
- `EXECUTED_ITEM` - After execution
**Use Cases:**
- Health potions
- Mana potions
- Food items
- Scrolls
- One-time use items
### SingleEquipment (`lib/item/type/single-equipment.js`)
Combination of Single and Equipment behaviors. Stackable equippable items.
**Characteristics:**
- Single instance (stackable)
- Equippable
- Modifiers apply/revert
**Use Cases:**
- Stackable armor pieces
- Stackable weapons (rare but possible)
- Ammo that provides bonuses when equipped
### SingleUsable (`lib/item/type/single-usable.js`)
Combination of Single and Usable behaviors. Stackable consumable items.
**Characteristics:**
- Single instance (stackable)
- Usable/consumable
- Execute functionality
**Use Cases:**
- Stacked potions
- Stacked food items
- Stacked scrolls
- Any stackable consumable
### ItemTypes (`lib/item/item-types.js`)
Registry mapping type IDs to item classes.
**Type Constants:**
- `ITEM_BASE: 10` - Base item type
- `EQUIPMENT: 1` - Equipment items
- `USABLE: 2` - Usable/consumable items
- `SINGLE: 3` - Single-instance items
- `SINGLE_EQUIPMENT: 4` - Single-instance equipment
- `SINGLE_USABLE: 5` - Single-instance usable
**Methods:**
- `list()` - Returns all types object
- `classByTypeId(typeId)` - Returns class for type ID (defaults to ItemBase)
## Exchange System
### ExchangePlatform (`lib/exchange/exchange-platform.js`)
Trading/exchange system between two inventories.
**Constructor Props:**
- `eventsManager` - EventsManager instance
- `exchangeInitializerId` - Unique ID for this exchange
**Methods:**
- `initializeExchangeBetween(props)` - Initialize exchange
- `inventoryA` (required) - First inventory
- `inventoryB` (required) - Second inventory
- `exchangeRequirementsA` - Requirements for A
- `exchangeRequirementsB` - Requirements for B
- `exchangeRewardsA` - Rewards for A
- `exchangeRewardsB` - Rewards for B
- `dropExchangeA` - Don't add items to A (e.g., NPC selling)
- `dropExchangeB` - Don't add items to B
- `avoidExchangeDecreaseA` - Don't decrease items from A
- `avoidExchangeDecreaseB` - Don't decrease items from B
- `async pushForExchange(itemUid, qty, inventoryKey)` - Add item to exchange
- `async removeFromExchange(itemUid, inventoryKey)` - Remove item from exchange
- `async confirmExchange(inventoryKey)` - Confirm exchange for inventory
- `async disconfirmExchange(inventoryKey)` - Unconfirm exchange
- `async finalizeExchange()` - Execute the exchange
- `cancelExchange()` - Cancel and reset exchange
- `validateRequirements(inventoryKey)` - Validate requirements
- `validateRewards(inventoryKey)` - Validate rewards
- `lockInventories()` - Lock both inventories
- `unlockInventories()` - Unlock both inventories
**Exchange Flow:**
1. `initializeExchangeBetween()` - Setup inventories
2. `pushForExchange()` - Both parties add items
3. `confirmExchange()` - Both parties confirm
4. `finalizeExchange()` - Execute exchange if both confirmed
5. Inventories unlocked, exchange reset
**Events Fired:**
- `EXCHANGE.INITIALIZED` - On initialization
- `EXCHANGE.CANCELED` - On cancellation
- `EXCHANGE.INVALID_PUSH` - On invalid push attempt
- `EXCHANGE.ITEM_PUSHED` - On successful item push
- `EXCHANGE.ITEM_REMOVE` - On item removal
- `EXCHANGE.CONFIRM` - On confirmation
- `EXCHANGE.DISCONFIRM` - On disconfirmation
- `EXCHANGE.BEFORE_FINALIZE` - Before finalization
- `EXCHANGE.FINALIZED` - After successful finalization
**Error Codes:**
- `EXCHANGE.MISSING_CONFIRMATION` - Both parties must confirm
- `EXCHANGE.INVALID_PUSHED_QUANTITY` - Quantity exceeds available
- `EXCHANGE.INVALID_QUANTITY` - Invalid quantity (e.g., 0)
- `EXCHANGE.INVALID_EXCHANGE` - From and To are same
- `EXCHANGE.DECREASE_QUANTITY` - Failed to decrease quantity
- `EXCHANGE.ITEM_ADD` - Failed to add item
### Requirements System
**ExchangeRequirement** - Individual requirement item
**RequirementsCollection** - Collection of requirements
**RequirementsProcessor** - Validates and processes requirements
Requirements define what items must be present or traded for an exchange to complete.
**Validation:**
- Item exists in inventory
- Quantity available
- Item pushed for exchange (if required)
**Processing:**
- Remove non-exchanged required items
- Validate before removal
**Error Codes:**
- `REQUIREMENTS.ITEM_NOT_PRESENT` - Required item missing
- `REQUIREMENTS.QUANTITY_NOT_AVAILABLE` - Insufficient quantity
- `REQUIREMENTS.ITEM_NOT_PUSHED` - Required item not in exchange
- `REQUIREMENTS.ITEM_QUANTITY_NOT_PUSHED` - Insufficient quantity pushed
- `REQUIREMENTS.ITEM_DOES_NOT_EXISTS` - Item doesn't exist
- `REQUIREMENTS.ITEM_ADD` - Failed to add requirement item
### Rewards System
**ExchangeReward** - Individual reward item
**RewardsCollection** - Collection of rewards
**RewardsProcessor** - Validates and processes rewards
Rewards define what items are given as part of an exchange.
**Validation:**
- Reward exists in system
- Can be added to inventory
**Processing:**
- Add reward items to receiving inventory
- Create item instances
**Error Codes:**
- `REWARD.DOES_NOT_EXISTS` - Reward doesn't exist
- `REWARD.MISSING_ITEM` - Reward item missing
- `REWARD.ITEM_NOT_PRESENT` - Item not present
- `REWARD.QUANTITY_NOT_AVAILABLE` - Quantity not available
- `REWARD.MISSING_PUSHED` - Missing pushed items
- `REWARD.GET_ITEM_DOES_NOT_EXISTS` - Item doesn't exist
- `REWARD.PROCESS_ITEM` - Failed to process item
- `REWARD.PROCESS_INVENTORY` - Failed to process inventory
- `REWARD.ADD_ITEMS` - Failed to add items
- `REWARD.QUANTITY_OVERLOAD` - Quantity exceeds limits
## Communication
### Sender (`lib/server/sender.js`)
Server-side message sender for client updates.
**Constructor Props:**
- `manager` - ItemsManager instance
- `client` - Client connection
**Functionality:**
- Listens to manager events
- Sends item updates to client
- Formats messages for client consumption
### Receiver (`lib/client/receiver.js`)
Client-side message receiver for server updates.
**Functionality:**
- Receives item updates from server
- Updates client-side item state
- Triggers client-side events
## Data Generators
### ItemsDataGenerator (`lib/items-data-generator.js`)
Generates item data structures from raw data (typically database entities).
**Methods:**
- `static itemsListMappedData(inventoryClasses, itemsModelsList)` - Maps item models to class/data structures
- `static generateItemModifiers(itemModel)` - Converts modifier data to Modifier instances
**Functionality:**
- Transforms database items to item class instances
- Prepares item model data for ItemsManager
- Maps item keys to classes
- Processes item modifiers from `itemModel.related_items_item_modifiers` relation
- Converts modifier data to Modifier instances indexed by modifier ID
- Automatically converts numeric string values to numbers for non-set operations
- Falls back to type-based classes when custom classes not found
**Important:**
- Uses `related_items_item_modifiers` property (Reldens relation key pattern with `related_*` prefix)
- Modifiers are indexed by their ID in the returned object
- Numeric values are automatically converted for operations other than SET
### GroupsDataGenerator (`lib/groups-data-generator.js`)
Generates group data structures from raw data.
**Methods:**
- `static groupsListMappedData(inventoryClasses, groupsModelsList)` - Maps group models to structures
- `static addGroup(groupModel, groups, inventoryClasses)` - Adds individual group to collection
**Functionality:**
- Transforms database groups to ItemGroup instances
- Prepares group configurations
- Maps group keys to instances
- Creates groupList, groupBaseData, and groupModels collections
- Uses custom group classes when provided
## Supporting Classes
### ModelEntity (`lib/item/model-entity.js`)
Database model wrapper for items. Bridges ORM entities with the items system.
**Functionality:**
- Wraps database entity
- Provides item-system-compatible interface
- Handles entity-to-item transformation
### ItemsError (`lib/items-error.js`)
Custom error class for structured error handling.
**Constructor:**
- `message` - Error message
- `code` - Error code constant
- `data` - Additional error data
- `withError` - Nested error object
**Properties:**
- `message` - Error message
- `code` - Error code
- `data` - Error data
- `error` - Nested error (if provided)
### ItemsEvents (`lib/items-events.js`)
Event name constants for the entire system.
**Event Categories:**
- Manager events (MANAGER_INIT)
- Inventory events (ADD_ITEM, REMOVE_ITEM, MODIFY_ITEM_QTY, SET_ITEMS, SET_GROUPS)
- Equipment events (EQUIP_ITEM, UNEQUIP_ITEM, EQUIP_BEFORE, EQUIP)
- Exchange events (INITIALIZED, CANCELED, ITEM_PUSHED, ITEM_REMOVE, CONFIRM, FINALIZED, etc.)
- Item execution events (EXECUTING_ITEM, EXECUTED_ITEM)
- Validation events (VALIDATE)
### Constants (`lib/constants.js`)
System-wide constants.
**Categories:**
- `SET, INCREASE, DECREASE` - Quantity operations
- `ACTIONS_PREF` - Action prefix for messages
- `ACTION_*` - Action constants for client communication
- `BEHAVIOR_*` - Communication behaviors (SEND, BROADCAST, BOTH)
- `TYPES` - Item type constants
- `TRADE_ACTIONS` - Trade action types (BUY, SELL, TRADE)
- `ERROR_CODES` - Complete error code registry
## Common Patterns
### Creating an ItemsManager
```javascript
const ItemsManager = require('@reldens/items-system/lib/manager');
let itemsManager = new ItemsManager({
owner: player,
itemClasses: {
'health_potion': HealthPotionClass,
'sword': SwordClass
},
itemsModelData: {
'health_potion': {
class: SingleUsable,
data: {key: 'health_potion', label: 'Health Potion', qty: 10}
},
'sword': {
class: ItemEquipment,
data: {key: 'sword', label: 'Iron Sword', qty: 1}
}
},
limitPerItem: 99,
itemsLimit: 50
});
await itemsManager.setup({
items: {...},
groups: {...}
});
```
### Adding Items
```javascript
let potion = itemsManager.createItemInstance('health_potion', 5);
await itemsManager.addItem(potion);
```
### Setting Up an Exchange
```javascript
const ExchangePlatform = require('@reldens/items-system/lib/exchange/exchange-platform');
let exchange = new ExchangePlatform({});
exchange.initializeExchangeBetween({
inventoryA: player1.itemsManager,
inventoryB: player2.itemsManager
});
await exchange.pushForExchange(itemUid, 1, 'A');
await exchange.confirmExchange('A');
await exchange.confirmExchange('B');
await exchange.finalizeExchange();
```
### Using Item Groups
```javascript
const ItemGroup = require('@reldens/items-system/lib/item/group');
let equipmentBag = new ItemGroup({
id: 1,
key: 'equipment',
label: 'Equipment',
items_limit: 20,
limit_per_item: 1
});
await equipmentBag.addItem(sword);
```
## Integration with Reldens Platform
- Items entities are defined in main platform: `D:\dap\work\reldens\src\lib\inventory\server`
- This package provides the runtime item management
- Database models are loaded from main platform
- Events integrate with platform EventsManager
- Modifiers integrate with @reldens/modifiers package
- Utils use @reldens/utils package (Shortcuts, Logger, EventsManager)