Skip to main content

OSRS Bot Development SDK with game-native structure

Project description

Escape

A Python SDK for Old School RuneScape (OSRS) bot development that communicates with RuneLite via a high-performance bridge. The architecture mirrors the game's interface, making it intuitive for OSRS developers.

Requirements

  • Python 3.12+
  • Linux with inotify support (required for event system)
  • RuneLite with Escape plugin running

Features

  • Game-Native Structure: Directory layout mirrors OSRS client interface (tabs, interfaces, world)
  • Event-Driven Architecture: Zero-CPU inotify-based event system for real-time game state
  • Singleton Pattern: All modules are singletons with lazy initialization
  • Type-Safe: Full type hints with IDE autocomplete support
  • Auto-Generated Constants: ItemID, NpcID, ObjectID, InterfaceID, and more
  • 3D Projection: Convert local/world coordinates to screen coordinates

Installation

pip install -U escape-sdk

Development Installation

# Clone the repository
git clone https://github.com/OSEscape/escape_sdk.git
cd escape_sdk

# Install with development dependencies
pip install -e ".[dev]"

# Install pre-commit hooks
pre-commit install

Quick Start

from escape.client import client

# Client auto-connects on import and starts event consumer
# All modules are singletons - no instantiation needed

# Access inventory
items = client.tabs.inventory.getItems()
for item in items:
    print(f"{item.name}: {item.quantity}")

# Check player state
pos = client.player.position
print(f"Player at: {pos}")

# Use bank interface
if client.interfaces.bank.isOpen():
    client.interfaces.bank.depositAll()

# Direct module access (alternative pattern)
from escape.tabs.inventory import inventory
from escape.input.mouse import mouse

inventory.clickItem(995)  # Click coins
mouse.leftClick(100, 200)

Architecture

Module Structure

escape/
├── client.py           # Main singleton - auto-connects to RuneLite bridge
├── globals.py          # Global accessors (getClient, getApi, getEventCache)
│
├── tabs/               # Side panel tabs (14 tabs)
│   ├── inventory.py    # Inventory management
│   ├── equipment.py    # Worn equipment
│   ├── skills.py       # Skill levels and XP
│   ├── prayer.py       # Prayer activation
│   ├── magic.py        # Spellbook
│   ├── combat.py       # Combat options
│   └── ...             # friends, settings, logout, etc.
│
├── interfaces/         # Overlay windows
│   ├── bank.py         # Bank interface
│   └── ...             # GE, shop, dialogue (planned)
│
├── world/              # 3D world entities
│   ├── ground_items.py # Items on the ground
│   └── projection.py   # Coordinate projection (local → screen)
│
├── input/              # OS-level input
│   ├── mouse.py        # Mouse control
│   ├── keyboard.py     # Keyboard input
│   └── runelite.py     # RuneLite window management
│
├── interactions/       # Game interactions
│   └── menu.py         # Right-click menu handling
│
├── player/             # Player state
│   └── player.py       # Position, stats, status
│
├── navigation/         # Movement systems
│   └── pathfinder.py   # Pathfinding (planned)
│
├── types/              # Type definitions
│   ├── item.py         # Item dataclass
│   ├── widget.py       # Widget mask builder
│   ├── packed_position.py  # Coordinate packing
│   └── ...
│
├── utilities/          # Helper functions
│   ├── timing.py       # sleep, waitUntil
│   └── text.py         # Text utilities
│
└── _internal/          # Internal implementation
    ├── api.py          # RuneLite bridge API
    ├── query_builder.py # Fluent query interface
    ├── cache/          # Event cache and state builder
    ├── events/         # Inotify event consumer
    └── resources/      # Varps, objects database

Singleton Pattern

All modules use the singleton pattern with __new__ + _init():

# Two equivalent access patterns:

# Via client namespace
from escape.client import client
client.tabs.inventory.getItems()

# Direct import
from escape.tabs.inventory import inventory
inventory.getItems()

Event System

The SDK uses an inotify-based event system for zero-CPU-usage when idle:

from escape.client import client

# Access cached game state (updated automatically)
tick = client.cache.tick
energy = client.cache.energy
position = client.cache.position

# Get recent events
chats = client.cache.getRecentEvents('chat_message', n=10)
stats = client.cache.getRecentEvents('stat_changed', n=5)

# Access varps/varbits
quest_points = client.cache.getVarp(101)

# Check data freshness
if client.cache.isFresh(max_age=1.0):
    print(f"Data age: {client.cache.getAge():.2f}s")

Event Channels:

Channel Type Channels Description
Ring Buffer varbit_changed, chat_message, item_container_changed, stat_changed, animation_changed Guaranteed delivery, keeps history
Latest State gametick, camera_changed, world_view_loaded, world_entity, menu_open, ground_items Current state only

Projection System

Convert local/world coordinates to screen coordinates:

from escape.world.projection import projection

# Auto-configured from events - just use it
screen_pos = projection.localToCanvasSingle(localX, localY, plane)
if screen_pos:
    screenX, screenY = screen_pos
    print(f"Screen position: ({screenX}, {screenY})")

# Batch projection
import numpy as np
xs = np.array([1000, 2000, 3000])
ys = np.array([1000, 2000, 3000])
screenX, screenY, valid = projection.localToCanvas(xs, ys, plane=0)

Query Builder

Direct RuneLite API access with fluent interface:

from escape.client import client

# Build and execute queries
q = client.query()
result = q.execute({
    "inventory": q.client.getItemContainer(93),
    "position": q.localPlayer.getWorldLocation(),
    "health": q.localPlayer.getHealthRatio()
})

Code Style

Naming Conventions

Element Convention Example
Functions/Methods camelCase getItems(), isInventoryFull()
Classes PascalCase Inventory, BankInterface
Constants UPPER_CASE MAX_INVENTORY_SIZE
Private _camelCase _internalHelper()

Dependencies

# Use dependency injection with global fallback
class Inventory:
    def __init__(self, client=None):
        self.client = client or getClient()

Development

Running Tests

pytest                           # All tests
pytest --cov=escape           # With coverage
pytest tests/test_inventory.py   # Specific file
pytest -k "test_bank"            # Pattern match

Linting and Formatting

ruff check .           # Lint
ruff check --fix .     # Auto-fix
ruff format .          # Format
python check_naming.py # Verify naming conventions

Pre-commit Hooks

pre-commit install       # Install hooks
pre-commit run --all-files  # Run manually

Generated Files

On first import, Escape downloads and generates:

~/.cache/escape/
├── generated/           # Proxy classes, constants
│   ├── constants/       # ItemID, NpcID, ObjectID, etc.
│   └── proxies/         # API proxy classes
└── data/                # Game data
    ├── api_data.json    # API metadata
    ├── varps.json       # Varp definitions
    └── objects.json     # Object database

Contributing

  1. Fork the repository
  2. Create a feature branch (git checkout -b feat/new-feature)
  3. Follow naming conventions and code style
  4. Write tests for new functionality
  5. Commit with conventional commits (feat:, fix:, etc.)
  6. Open a Pull Request

See CONTRIBUTING.md for detailed guidelines.

License

MIT License - see LICENSE file for details.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

escape_sdk-1.0.3.tar.gz (179.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

escape_sdk-1.0.3-py3-none-any.whl (209.7 kB view details)

Uploaded Python 3

File details

Details for the file escape_sdk-1.0.3.tar.gz.

File metadata

  • Download URL: escape_sdk-1.0.3.tar.gz
  • Upload date:
  • Size: 179.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.12

File hashes

Hashes for escape_sdk-1.0.3.tar.gz
Algorithm Hash digest
SHA256 ae734fc9479c1855fdac475e001fd873c4e83f31dffadec1bcb166a6f1342ccd
MD5 1aee5818c4ec0464401058089ca37b30
BLAKE2b-256 086ffc7244504bde91ba25c806eae318a0d56dc69e58745b0e3bb8e4310bc519

See more details on using hashes here.

File details

Details for the file escape_sdk-1.0.3-py3-none-any.whl.

File metadata

  • Download URL: escape_sdk-1.0.3-py3-none-any.whl
  • Upload date:
  • Size: 209.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.12

File hashes

Hashes for escape_sdk-1.0.3-py3-none-any.whl
Algorithm Hash digest
SHA256 acce22c4cfc73b63cd2745fe9c6e97fbd9f3c3d5198ae6d12c094aa62392ca8f
MD5 98ba5ad6320090e3139dff92ca9fb607
BLAKE2b-256 1814e44e6dce9ac3c22604214c01e01f5e58513d993371035925a4a1796e1cef

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page