Skip to main content

Overview

The Buffer module provides a thread-safe circular buffer implementation for managing multiple streams of game data. Each thread maintains its own read position, allowing concurrent access to the shared buffer without blocking. Location: lib/common/buffer.rb

Stream Constants

The Buffer uses bitmask flags to identify different types of data streams:
ConstantValueDescription
DOWNSTREAM_STRIPPED1Game output with ANSI/formatting stripped
DOWNSTREAM_RAW2Raw game output with all formatting
DOWNSTREAM_MOD4Modified downstream data (post-hook)
UPSTREAM8Commands sent to the game server
UPSTREAM_MOD16Modified upstream commands (post-hook)
SCRIPT_OUTPUT32Output from scripts

Module Methods

gets

Blocking read that waits for the next line matching the current thread’s stream filter.
Buffer.getsLine
return
Line
The next line object from the buffer matching the current stream filter. Blocks until a matching line is available.
Behavior:
  • Automatically initializes the thread’s read position if first access
  • Blocks (sleeps 0.05s intervals) while waiting for data
  • Filters lines based on the thread’s stream mask
  • Returns the first line where (line.stream & thread_streams) != 0

gets?

Non-blocking read that returns the next available line or nil.
Buffer.gets?Line or nil
return
Line | nil
The next line object matching the stream filter, or nil if no data is available
Behavior:
  • Same as gets but returns nil immediately if no matching data is available
  • Does not block or sleep
  • Useful for polling or checking for data without waiting

clear

Read and remove all pending lines for the current thread.
Buffer.clearArray<Line>
return
Array<Line>
Array of all unread lines matching the current thread’s stream filter
Behavior:
  • Advances the thread’s read position to the end of the buffer
  • Returns all matching lines that were pending
  • Useful for catching up or discarding old data

rewind

Reset the current thread’s read position to the beginning of the buffer.
Buffer.rewindBuffer
return
Buffer
Returns self for method chaining
Behavior:
  • Moves the thread’s index back to the buffer offset (oldest available line)
  • Subsequent reads will re-read from the beginning
  • Does not affect other threads’ positions

update

Add a new line to the buffer (typically called by core framework).
Buffer.update(line, stream = nil) → Buffer
line
Line
required
Line object to add to the buffer
stream
Integer
Optional stream bitmask to set on the line. If not provided, uses the line’s existing stream value.
return
Buffer
Returns self for method chaining
Behavior:
  • Duplicates and freezes the line before storing
  • Maintains circular buffer by removing oldest lines when max size (3000) is exceeded
  • Thread-safe via mutex synchronization

streams

Get the current thread’s stream filter mask.
Buffer.streamsInteger
return
Integer
Current stream bitmask for the thread (default: DOWNSTREAM_STRIPPED)

streams=

Set the current thread’s stream filter mask.
Buffer.streams = value
value
Integer
required
Bitmask specifying which streams to read. Must be a valid integer with at least one of the 6 stream bits set.
Behavior:
  • Validates that the value is an integer with valid stream bits
  • Raises an error if the mask is invalid (0 or outside valid range)
  • Only affects the current thread’s reads

cleanup

Remove thread indexes for terminated threads (housekeeping).
Buffer.cleanupBuffer
return
Buffer
Returns self for method chaining
Behavior:
  • Removes dead thread entries from internal indexes
  • Called periodically by the framework
  • Prevents memory leaks from thread churn

Examples

Basic Reading

# Read the next line (blocks if needed)
line = Buffer.gets
puts line

Non-Blocking Check

# Check for data without blocking
if line = Buffer.gets?
  puts "Got: #{line}"
else
  puts "No data available"
end

Stream Filtering

# Only read raw downstream data
Buffer.streams = Buffer::DOWNSTREAM_RAW

while line = Buffer.gets
  puts line  # Only raw game output
end

Multiple Streams

# Read both stripped output and script output
Buffer.streams = Buffer::DOWNSTREAM_STRIPPED | Buffer::SCRIPT_OUTPUT

while line = Buffer.gets
  puts line  # Either game output or script messages
end

Clearing Old Data

# Discard all pending lines
old_lines = Buffer.clear
puts "Cleared #{old_lines.length} lines"

# Now only read new data
line = Buffer.gets

Re-reading Data

# Read some lines
line1 = Buffer.gets
line2 = Buffer.gets

# Go back to the beginning
Buffer.rewind

# Read again from the start
line1_again = Buffer.gets  # Same as line1

Thread Safety

The Buffer module is fully thread-safe:
  • Each thread maintains its own read position
  • Multiple threads can read simultaneously without interference
  • Internal state is protected by a mutex
  • Lines are frozen before storage to prevent modification

Buffer Size

The buffer maintains a maximum of 3000 lines. When this limit is exceeded:
  • Oldest lines are automatically removed
  • The global offset is incremented
  • Thread positions are adjusted to stay valid
  • Threads that fall too far behind are reset to the current offset

Use Cases

Script Pattern Matching

loop do
  line = Buffer.gets
  if line =~ /You feel stronger/
    echo "Buff received!"
    break
  end
end

Monitoring Multiple Streams

# Watch both game output and commands
Buffer.streams = Buffer::DOWNSTREAM_STRIPPED | Buffer::UPSTREAM

loop do
  line = Buffer.gets
  if (line.stream & Buffer::UPSTREAM) != 0
    echo "Command sent: #{line}"
  else
    echo "Game output: #{line}"
  end
end

Non-Blocking Poll Loop

loop do
  while line = Buffer.gets?
    process_line(line)
  end
  
  # Do other work
  perform_other_tasks()
  
  sleep 0.1
end

Build docs developers (and LLMs) love