Overview
The Vars module provides persistent key-value storage that’s automatically saved and loaded per character. It’s simpler than Settings and ideal for storing script state, counters, and configuration values.
Vars are scoped to "#{XMLData.game}:#{XMLData.name}" (current game + character), stored in SQLite, and automatically saved every 5 minutes plus on program exit.
Vars is designed for simplicity - use Settings for more complex structured data or custom scoping.
Basic Usage
Bracket Notation
# Set a variable
Vars['hunting_area'] = 'kobolds'
# Get a variable
area = Vars['hunting_area'] # => 'kobolds'
# Delete a variable
Vars['hunting_area'] = nil
Method Syntax
Variables can also be accessed as methods:
# Set using method syntax
Vars.hunting_area = 'kobolds'
Vars.kill_count = 0
# Get using method syntax
echo Vars.hunting_area # => 'kobolds'
echo Vars.kill_count # => 0
# Delete
Vars.hunting_area = nil
Symbol Keys
Symbols are automatically converted to strings:
Vars[:hunting_area] = 'kobolds'
Vars['hunting_area'] # => 'kobolds' (same variable)
Vars.hunting_area # => 'kobolds' (same variable)
Data Types
Vars supports any Marshal-serializable Ruby object:
# Strings
Vars['name'] = 'Bob'
# Numbers
Vars['count'] = 42
Vars['ratio'] = 3.14
# Booleans
Vars['enabled'] = true
# Arrays
Vars['items'] = ['sword', 'shield', 'potion']
Vars['items'] << 'helmet' # Modifies and auto-saves
# Hashes
Vars['config'] = { mode: 'fast', loot: true }
Vars['config'][:mode] = 'slow' # Modifies and auto-saves
# Time objects
Vars['last_run'] = Time.now
Common Patterns
Default Values
# Set default if not already set
Vars['count'] ||= 0
Vars['config'] ||= { mode: 'normal' }
# Or use || for inline defaults
count = Vars['count'] || 0
mode = Vars.dig('config', 'mode') || 'normal'
Counters
# Initialize counter
Vars['kill_count'] ||= 0
# Increment
Vars['kill_count'] += 1
# Reset
Vars['kill_count'] = 0
echo "Total kills: #{Vars['kill_count']}"
Lists
# Initialize list
Vars['visited_rooms'] ||= []
# Add item
Vars['visited_rooms'] << Room.current.id
# Check membership
if Vars['visited_rooms'].include?(Room.current.id)
echo 'Already visited this room'
end
# Remove item
Vars['visited_rooms'].delete(Room.current.id)
Configuration
# Store script configuration
Vars['hunt_config'] ||= {
area: 'kobolds',
loot: true,
skin: true,
min_health: 50
}
# Access config
config = Vars['hunt_config']
if XMLData.health < config[:min_health]
echo 'Health too low!'
end
# Update config
Vars['hunt_config'][:area] = 'trolls'
Timestamps
# Track last execution
Vars['last_hunt'] = Time.now
# Check elapsed time
if Vars['last_hunt']
elapsed = Time.now - Vars['last_hunt']
echo "Last hunted #{(elapsed / 60).round} minutes ago"
end
# Cooldown check
if Vars['last_hunt'] && Time.now - Vars['last_hunt'] < 3600
echo 'On cooldown, please wait'
exit
end
Vars['last_hunt'] = Time.now
State Flags
# Track script state
Vars['hunting_active'] = true
Vars['auto_loot'] = true
Vars['auto_skin'] = false
# Check state
if Vars['hunting_active']
# Continue hunting
end
# Toggle state
Vars['auto_loot'] = !Vars['auto_loot']
echo "Auto-loot: #{Vars['auto_loot'] ? 'ON' : 'OFF'}"
Methods
Vars.list
Returns a duplicate Hash of all variables.
Hash with string keys containing all stored variables
all_vars = Vars.list
all_vars.each do |key, value|
echo "#{key}: #{value}"
end
# Check if a variable exists
if Vars.list.key?('hunting_area')
echo 'Hunting area is configured'
end
Vars.save
Force immediate save to database.
Vars are automatically saved every 5 minutes and on exit. Manual save is rarely needed.
# Make critical change and save immediately
Vars['important_data'] = critical_value
Vars.save # Force immediate save
Vars.[]
Get a variable value.
Variable value, or nil if not set
value = Vars['my_var']
value = Vars[:my_var] # Same as above
Vars.[]=
Set a variable value.
Value to store (or nil to delete)
Vars['my_var'] = 'value'
Vars[:my_var] = 'value' # Same as above
Vars['my_var'] = nil # Delete variable
Checking for Variables
# Simple existence check
if Vars['my_var']
echo "my_var is set to #{Vars['my_var']}"
end
# Explicit nil check
if Vars['my_var'].nil?
echo 'my_var is not set'
end
# Check with list
if Vars.list.key?('my_var')
echo 'my_var exists'
end
# Safe navigation
value = Vars['config']&.[]('setting') || 'default'
Automatic Persistence
Vars are automatically:
- Loaded on first access after login
- Saved every 5 minutes by background thread
- Saved when Lich exits normally
# These all automatically save eventually
Vars['count'] = 5
Vars['list'] << 'item'
Vars['hash'][:key] = 'value'
# Force immediate save if needed
Vars.save
Scoping
Vars are scoped to the current character:
- Scope:
"#{XMLData.game}:#{XMLData.name}"
- Each character has independent variables
- Variables persist across script runs
- Variables persist across Lich restarts
# Character "Bob" in GemStone IV
Vars['hunting_area'] = 'kobolds' # Saved for GSIV:Bob
# Switch to character "Alice"
Vars['hunting_area'] # => nil (different character)
Comparison with Settings
| Feature | Vars | Settings |
|---|
| Syntax | Simple bracket/method | CharSettings, GameSettings |
| Scoping | Character only | Character, Game, Custom |
| Complexity | Simple key-value | Nested structures |
| Use Case | Script state, counters | Configuration, complex data |
| Auto-save | Every 5 minutes | On every change |
# Vars - Simple script state
Vars['kill_count'] = 0
Vars['last_hunt'] = Time.now
# Settings - Complex configuration
CharSettings['hunt_config'] = {
areas: { kobolds: true, trolls: false },
loot_settings: { min_value: 1000 },
thresholds: { health: 50, mana: 30 }
}
Best Practices
- Use descriptive names:
Vars['hunting_area'] not Vars['ha']
- Initialize with defaults: Use
||= pattern
- Use for simple data: Complex nested data → use Settings
- Don’t store huge objects: Keep it lightweight
- Prefer Vars for counts/flags: Simple and efficient
Migration
If migrating from older code:
# Old pattern (still works)
if Vars['count'].nil?
Vars['count'] = 0
end
# Better pattern
Vars['count'] ||= 0
# Old explicit save
Vars['value'] = 123
Vars.save
# New (save is automatic)
Vars['value'] = 123
# Already saved (within 5 minutes)
Examples
Hunt Counter Script
# Initialize counters
Vars['kills'] ||= 0
Vars['deaths'] ||= 0
Vars['session_start'] ||= Time.now
# Track kills
Vars['kills'] += 1
echo "Total kills: #{Vars['kills']}"
# Track deaths
if XMLData.spirit == 0
Vars['deaths'] += 1
echo "Total deaths: #{Vars['deaths']}"
end
# Session stats
session_time = Time.now - Vars['session_start']
kph = (Vars['kills'].to_f / (session_time / 3600)).round(1)
echo "Kills per hour: #{kph}"
Room Tracking
# Track visited rooms
Vars['visited_rooms'] ||= []
current_room = Room.current.id
if Vars['visited_rooms'].include?(current_room)
echo 'Been here before'
else
Vars['visited_rooms'] << current_room
echo 'New room discovered!'
end
echo "Explored #{Vars['visited_rooms'].length} rooms"
Cooldown Management
# Track last cast time
Vars['last_spell_cast'] ||= {}
spell = '920'
cooldown = 60 # seconds
if Vars['last_spell_cast'][spell]
elapsed = Time.now - Vars['last_spell_cast'][spell]
if elapsed < cooldown
remaining = (cooldown - elapsed).ceil
echo "Spell on cooldown: #{remaining}s remaining"
exit
end
end
# Cast spell
fput "prep #{spell}"
Vars['last_spell_cast'][spell] = Time.now
See Also
- Settings - More complex configuration storage
- Script - Script management
- Script.db - SQLite database access for scripts