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
    ├── batch.py        # Batch query execution
    ├── 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-2.0.1.tar.gz (126.0 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-2.0.1-py3-none-any.whl (152.1 kB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for escape_sdk-2.0.1.tar.gz
Algorithm Hash digest
SHA256 cb1de3268171463f71aa5fe5f3cbd0ca8893ea08a4856c4543258419bf3831b1
MD5 6dfe358fee1b6e0bc9bd598ed2dd044b
BLAKE2b-256 b7577ff3be89f3d229656ad95acdcb66e6a3a2c82196931e2fa8f9be61dde46c

See more details on using hashes here.

File details

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

File metadata

  • Download URL: escape_sdk-2.0.1-py3-none-any.whl
  • Upload date:
  • Size: 152.1 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-2.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 7ecc55a00ec0c07b4ed4c46f499ba47abfac05066d4cf2a1b4b890b93cd061ac
MD5 4ad45c434d3027c74d4eea70002581f1
BLAKE2b-256 f08083d464edebefd4837fa2b703cc42913210400d41392077f9568957490fb9

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