Skip to main content

Overview

Hooks allow you to intercept and modify data flowing between the game server and client. Lich provides two types of hooks:
  • DownstreamHook - Intercepts data from the game server to the client
  • UpstreamHook - Intercepts data from the client to the game server
Hooks are powerful but can impact system performance. Keep hook processing fast and efficient.

DownstreamHook

Intercept and modify game output before it reaches your client.

Adding a Hook

# Basic downstream hook
DownstreamHook.add("my_hook") do |server_string|
  # Modify the string
  if server_string =~ /troll/
    server_string.gsub("troll", "TROLL (DANGER!)")
  else
    server_string
  end
end
Hooks must return the modified string or nil to suppress output entirely.

Hook Examples

Highlighting Important Text

DownstreamHook.add("highlight_danger") do |line|
  if line =~ /You are stunned|You are webbed/
    line = "*** #{line.upcase} ***"
  end
  line
end

Monster Counter

monster_count = 0

DownstreamHook.add("monster_counter") do |line|
  if line =~ /Also here: .*(troll|ogre|giant)/
    monster_count += 1
    echo "Monsters encountered: #{monster_count}"
  end
  line
end

Filter Spam

DownstreamHook.add("filter_spam") do |line|
  # Return nil to suppress output
  if line =~ /^\[.*?\]: /
    nil  # Suppress channel messages
  else
    line
  end
end

Modify Game Output

DownstreamHook.add("modify_exits") do |line|
  if line =~ /^Obvious exits: (.+)$/
    exits = $1
    line = "Available Directions: #{exits.upcase}"
  end
  line
end

XML Handling

DownstreamHook.add("track_room_changes") do |line|
  # Line includes XML tags in raw form
  if line =~ /<nav rm='(\d+)'/
    room_id = $1
    echo "Entered room: #{room_id}"
  end
  line
end

UpstreamHook

Intercept and modify commands before they reach the game server.

Adding a Hook

# Basic upstream hook
UpstreamHook.add("my_hook") do |client_string|
  # Modify or log the command
  echo "Command sent: #{client_string}"
  client_string
end

Hook Examples

Command Alias System

UpstreamHook.add("aliases") do |command|
  case command
  when /^aa$/
    "attack"
  when /^la$/
    "look at"
  when /^gg (.+)$/
    "go go #{$1}"
  else
    command
  end
end

Command Logging

log_file = File.open("commands.log", "a")

UpstreamHook.add("command_logger") do |command|
  log_file.puts "[#{Time.now}] #{command}"
  log_file.flush
  command
end

before_dying {
  log_file.close
}

Auto-correct Common Typos

UpstreamHook.add("typo_fixer") do |command|
  command.gsub(/attak/, "attack")
         .gsub(/attakc/, "attack")
         .gsub(/loook/, "look")
end

Block Dangerous Commands

UpstreamHook.add("safety") do |command|
  if command =~ /^drop (my|.*? shield|.*? armor)/
    echo "Blocked dangerous drop command"
    nil  # Suppress command
  else
    command
  end
end

Command Metrics

command_counts = Hash.new(0)

UpstreamHook.add("metrics") do |command|
  base_command = command.split.first
  command_counts[base_command] += 1
  command
end

before_dying {
  echo "Command usage:"
  command_counts.sort_by { |k, v| -v }.first(10).each do |cmd, count|
    echo "  #{cmd}: #{count}"
  end
}

Hook Management

Removing Hooks

# Remove a specific hook
DownstreamHook.remove("my_hook")
UpstreamHook.remove("my_hook")

# Always remove hooks when script exits
before_dying {
  DownstreamHook.remove("my_hook")
  UpstreamHook.remove("my_hook")
}
Always remove your hooks in a before_dying callback to prevent them from persisting after your script exits.

Listing Active Hooks

# List all downstream hooks
DownstreamHook.list.each do |name|
  echo "Downstream: #{name}"
end

# List all upstream hooks
UpstreamHook.list.each do |name|
  echo "Upstream: #{name}"
end

# See hook sources (which script added them)
DownstreamHook.sources
UpstreamHook.sources

Hook Execution Order

Hooks are executed in the order they were added:
DownstreamHook.add("first") do |line|
  echo "First hook"
  line
end

DownstreamHook.add("second") do |line|
  echo "Second hook"
  line
end

# Output will be:
# First hook
# Second hook

Advanced Patterns

Conditional Hook

enabled = true

DownstreamHook.add("conditional") do |line|
  if enabled && line =~ /monster/
    line.gsub("monster", "MONSTER")
  else
    line
  end
end

# Toggle via command
match "toggle", /^toggle filter$/i
matchwait
enabled = !enabled
echo "Filter #{enabled ? 'enabled' : 'disabled'}"

Multi-line Buffer

buffer = []

DownstreamHook.add("multi_line") do |line|
  buffer << line
  
  # Keep last 5 lines
  buffer.shift if buffer.length > 5
  
  # Check for pattern across multiple lines
  if buffer.join(" ") =~ /important.*sequence/
    echo "Pattern detected!"
  end
  
  line
end

State Machine Hook

state = :idle

DownstreamHook.add("state_machine") do |line|
  case state
  when :idle
    if line =~ /combat begins/
      state = :combat
      echo "Entering combat mode"
    end
  when :combat
    if line =~ /combat ends/
      state = :idle
      echo "Exiting combat mode"
    end
  end
  line
end

Hook with External Data

# Load configuration
keywords = ["troll", "ogre", "giant"]

DownstreamHook.add("keyword_highlighter") do |line|
  keywords.each do |keyword|
    line.gsub!(keyword, "*** #{keyword.upcase} ***")
  end
  line
end

# Update keywords dynamically
keywords << "dragon"

Complete Example

Combined upstream and downstream hooks:
# Script: hook_example.lic
# version: 1.0.0

echo "Installing hooks..."

# Track commands sent
command_history = []

UpstreamHook.add("command_tracker") do |command|
  command_history << command
  command_history.shift if command_history.length > 100
  command
end

# Highlight important game output
DownstreamHook.add("highlighter") do |line|
  if line =~ /You (take|suffer)/
    line = "!!! #{line.upcase} !!!"
  end
  line
end

# Monitor for specific trigger
DownstreamHook.add("trigger") do |line|
  if line =~ /the monster flees/
    echo "Monster escaped!"
    # Could trigger other actions
  end
  line
end

echo "Hooks installed. Script running..."

# Keep script alive
loop do
  pause 60
end

# Cleanup
before_dying {
  echo "Removing hooks..."
  UpstreamHook.remove("command_tracker")
  DownstreamHook.remove("highlighter")
  DownstreamHook.remove("trigger")
  
  echo "Last 5 commands:"
  command_history.last(5).each { |cmd| echo "  #{cmd}" }
}

Best Practices

Keep Processing Fast

Hooks run on every line. Avoid expensive operations.

Always Clean Up

Remove hooks in before_dying callbacks.

Return Correctly

Always return the string (or nil to suppress).

Test Thoroughly

Hooks can break game output if not careful.

Next Steps

Threading

Create multi-threaded scripts for complex operations

Global Methods

Learn about available script methods

Build docs developers (and LLMs) love