Skip to main content

Overview

The Settings module provides persistent storage for script configuration with automatic saving and scope-based organization. Settings are stored per-script and can be organized by character, game, or custom scopes. Settings automatically persist to SQLite database and use an internal caching system for performance.

Basic Usage

CharSettings

Character-specific settings (scoped to current character).
# Store character-specific data
CharSettings['hunting_area'] = 'kobolds'
CharSettings['last_login'] = Time.now

echo CharSettings['hunting_area']  # => 'kobolds'

GameSettings

Game-wide settings (shared across all characters in the same game).
# Store game-wide preferences
GameSettings['debug_mode'] = true
GameSettings['theme'] = 'dark'

echo GameSettings['debug_mode']  # => true

Settings

Direct Settings access (uses default scope - typically character-specific).
Settings['my_setting'] = 'value'
value = Settings['my_setting']

Data Types

Settings supports any Marshal-serializable Ruby object:
# Strings
CharSettings['name'] = 'Bob'

# Numbers
CharSettings['count'] = 42
CharSettings['ratio'] = 3.14

# Booleans
CharSettings['enabled'] = true

# Arrays
CharSettings['items'] = ['sword', 'shield', 'potion']

# Hashes
CharSettings['config'] = {
  mode: 'aggressive',
  speed: 'fast',
  loot: true
}

# Nested structures
CharSettings['database'] = {
  hunting: {
    kobolds: { count: 10, xp: 500 },
    trolls: { count: 5, xp: 1000 }
  }
}

Hash Operations

Settings values behave like normal Ruby objects:
# Initialize a hash
CharSettings['stats'] = {}

# Add values
CharSettings['stats']['kills'] = 0
CharSettings['stats']['deaths'] = 0

# Modify values
CharSettings['stats']['kills'] += 1

# Nested access
CharSettings['inventory'] = { weapons: {}, armor: {} }
CharSettings['inventory']['weapons']['sword'] = { damage: 10 }

Array Operations

# Initialize array
CharSettings['visited_rooms'] = []

# Add items
CharSettings['visited_rooms'] << 'Town Square'
CharSettings['visited_rooms'].push('Temple')

# Modify
CharSettings['visited_rooms'].concat(['Bank', 'Inn'])

# Access
first_room = CharSettings['visited_rooms'].first
room_count = CharSettings['visited_rooms'].length

Default Values

Use the ||= operator for defaults:
# Set default if not already set
CharSettings['hunting_count'] ||= 0

# Or use || for inline defaults
count = CharSettings['hunting_count'] || 0

# Complex defaults
CharSettings['config'] ||= {
  mode: 'safe',
  auto_loot: true,
  areas: ['kobolds', 'rats']
}

Checking Existence

# Check if a setting exists (and is not nil)
if CharSettings['hunting_area']
  echo "Hunting in #{CharSettings['hunting_area']}"
end

# Check for nil explicitly
if CharSettings['some_setting'].nil?
  echo 'Setting not configured'
end

# Use fetch for safe access
value = CharSettings.to_hash.fetch('key', 'default')

Advanced Features

to_hash

Get a snapshot of all settings as a plain Hash:
# Get all character settings
all_settings = CharSettings.to_hash

all_settings.each do |key, value|
  echo "#{key}: #{value}"
end

Custom Scopes

Create settings with custom scope strings:
# Create account-wide settings
account_scope = "account:#{XMLData.player_id}"
account_settings = Settings.root_proxy_for(account_scope)

account_settings['premium'] = true
account_settings['characters'] = ['Bob', 'Alice', 'Charlie']

to_h (Legacy)

Alias for to_hash:
settings_hash = CharSettings.to_h

Instance Settings (Advanced)

For multi-instance scripts that need separate settings per instance:
class HuntingScript
  def initialize(area)
    @settings = InstanceSettings.new("hunting:#{area}")
  end
  
  def configure
    @settings['enabled'] = true
    @settings['monsters'] = ['kobold', 'troll']
  end
end

script1 = HuntingScript.new('area1')
script2 = HuntingScript.new('area2')
# Each has independent settings

Persistence

Settings are automatically saved to database when modified. No manual save needed.

Manual Operations

# Force refresh from database (rarely needed)
Settings.load
CharSettings.load

# Legacy save (no-op, kept for compatibility)
Settings.save

Migration from Old Code

If migrating from older Lich versions:
# Old way (still works)
Settings['key'] = 'value'
Settings.save

# New way (save is automatic)
CharSettings['key'] = 'value'
# Already saved to database

# Old hash operations
Settings.to_hash['key'] = 'value'
Settings.save

# New way
CharSettings['key'] = 'value'
# Already saved

Common Patterns

Configuration Management

# Initialize script configuration
CharSettings['script_config'] ||= {
  enabled: true,
  mode: 'normal',
  areas: [],
  thresholds: { health: 50, mana: 30 }
}

# Access nested config
config = CharSettings['script_config']
if config['enabled']
  hunt_mode = config['mode']
  health_threshold = config['thresholds']['health']
end

Counter Tracking

# Initialize counters
CharSettings['kills'] ||= 0
CharSettings['deaths'] ||= 0
CharSettings['logins'] ||= 0

# Increment
CharSettings['kills'] += 1

# Display stats
kills = CharSettings['kills']
deaths = CharSettings['deaths']
ratio = kills.to_f / [deaths, 1].max
echo "K/D ratio: #{ratio.round(2)}"

List Management

# Initialize list
CharSettings['ignored_players'] ||= []

# Add item if not present
player = 'Bob'
unless CharSettings['ignored_players'].include?(player)
  CharSettings['ignored_players'] << player
end

# Remove item
CharSettings['ignored_players'].delete(player)

# Check membership
if CharSettings['ignored_players'].include?(player)
  echo "#{player} is ignored"
end

Timestamp Tracking

# Track last run time
CharSettings['last_hunting'] = Time.now

# Check time elapsed
last_hunt = CharSettings['last_hunting']
if last_hunt && Time.now - last_hunt < 3600
  echo 'Hunted recently, waiting...'
else
  echo 'Time to hunt!'
  CharSettings['last_hunting'] = Time.now
end

Debugging

# Enable debug logging
Settings.set_log_level(:debug)

# Levels: :none, :error, :info, :debug
Settings.set_log_level(:info)

# Disable logging
Settings.set_log_level(:none)

Scope Details

  • CharSettings: Scoped to "#{XMLData.game}:#{XMLData.name}"
  • GameSettings: Scoped to "#{XMLData.game}:"
  • Settings: Uses default scope ":" (typically character-specific)

Best Practices

  1. Use descriptive keys: CharSettings['hunting_area'] not CharSettings['ha']
  2. Initialize with defaults: Use ||= pattern
  3. Prefer CharSettings/GameSettings: Clearer scope than raw Settings
  4. Use appropriate scope: Character settings vs game-wide vs account-wide
  5. Avoid complex objects: Stick to basic types (Hash, Array, String, Number, Boolean)

See Also

  • Vars - Simple key-value variable storage
  • Script - Script management

Build docs developers (and LLMs) love