L2J Interlude Toolkit
Headless Python Client ท Multi-Agent Geodata Scanner ท Web Dashboard ท Server Management
No Windows. No Game Client. Just Python + Your L2J Server.
Hey everyone,
I've been deep in the L2J rabbit hole for a while now, building tools that I always wished existed. Today I'm releasing everything as open source. This is a
complete developer toolkit for
L2J Mobius C6 Interlude and the centerpiece is something I haven't seen anyone do before:
A fully functional headless Lineage 2 client written in pure Python.
No Wine. No VM. No Windows client install. Runs on macOS, Linux, anything with Python 3. Connects to your login server, authenticates, enters the game world, creates characters, executes admin commands all from the terminal.
━━━ What's Inside ━━━
① Headless L2 Client (Python)
The real deal not a simple socket wrapper. Full protocol implementation:
Complete login flow: BlowfishKey init → RSA authentication → server list → PlayOk
GameCrypt XOR cipher (encrypt + decrypt game traffic)
Character creation from code pick class, name, appearance
CreatureSay packet parsing (server → client text messages)
Admin command execution via SendBypassBuildCmd (opcode 0x5b)
Auto-reconnect with error handling
If you've ever wanted to automate anything on L2J without touching the game client, this is your starting point.
② Multi-Agent Terrain Scanner
Deploy
20+ headless bots simultaneously, each in its own thread with its own game connection. They scan the entire world's geodata in parallel.
Custom
AdminTerrainScan server command queries GeoEngine directly, returns base64-encoded height + NSWE data in batches of 256 blocks per packet
Custom
AdminGeoExport full-fidelity .l2d export using native ABlock.saveBlock() (bit-perfect output)
All 139 regions scanned in ~2 minutes with 20 agents
SQLite persistence resume interrupted scans
9.1 million cells scanned per run
The traditional approach (teleport + read Z) doesn't work because L2J's teleToLocation does
z += 5 without consulting GeoEngine. Our approach queries GeoEngine directly via custom admin commands
actual ground truth data.
③ Real-Time Web Dashboard
Flask-based web UI with Server-Sent Events for live progress:
World map grid 11ื16 region grid, color-coded: gray=pending, blue+pulse=scanning, green=done, red=error
Worker table per-agent status, current region, cells/sec, errors
Live controls start/stop, dynamic worker count, scan mode
Event log scrolling real-time log with timestamps
Code:
http://localhost:5556 → Terrain Scanner Dashboard
http://localhost:5555 → Geodata Editor
④ Geodata Editor (Web UI)
Visual geodata browser and editor right in your browser:
L2D binary format parser (flat, complex, multilayer blocks)
Render modes: heightmap, NSWE flags, block types, combined, cell detail
Edit individual cells, unblock paths, export renders as PNG
World coord ↔ geo coord conversion
CLI tool for batch operations
⑤ Server Management CLI
One script to manage everything. No more hunting through config files:
Code:
./l2j-manage.sh start # Start MariaDB → Login → Game
./l2j-manage.sh set-rates 5 # All rates to 5x
./l2j-manage.sh gm admin # Promote to GM
./l2j-manage.sh db-backup # Backup database
./l2j-manage.sh rebuild # Recompile Java + deploy JARs
./l2j-manage.sh info # Show full server config summary
⑥ Bootstrap Automation
Mass-create accounts, characters, and promote to GM in one command:
Code:
./terrain-scanner.sh --bootstrap --num 20 --promote
Creates 20 accounts, 20 characters (Human Fighter), promotes all to GM via MariaDB. Uses AutoCreateAccounts no manual registration needed.
━━━ How It Works ━━━
Code:
Scanner Worker L2J Game Server
| |
| SendBypassBuildCmd |
| "scan_geo 20 18 0" |
| ----------------------------> |
| | GeoEngine.getHeightNearest()
| | GeoEngine.getNsweNearest()
| | x 256 blocks
| CreatureSay |
| "GEODATA|20|18|0|<base64>" |
| <---------------------------- |
| |
| (decode -> height + NSWE) |
| (repeat for blockY 0..255) |
| (write .l2d file) |
Each region = 256 commands. 20 agents running in parallel = entire world scanned in ~2 minutes.
━━━ Tech Stack ━━━
Code:
Game Server -> Java 21, L2J Mobius C6 Interlude
Headless Client -> Python 3, raw sockets, Blowfish/RSA
Web Dashboards -> Flask, Server-Sent Events, vanilla JS
Geodata Render -> NumPy, Pillow
Database -> MariaDB
Build System -> Apache Ant
━━━ Looking for Contributors ━━━
This is just the beginning. Some areas where the community can help:
Movement-based geodata probing walk characters in all directions to detect actual walls/cliffs
Pathnode generation generate pathnode files from geodata for NPC pathfinding
Client data extraction parse official L2 client files for terrain data
3D terrain viewer WebGL-based viewer for the dashboard
More packet types expand the headless client protocol coverage
Other chronicles port the headless client to High Five, Classic, etc.
Whether you're a Java dev, Python dev, or just into L2J tinkering there's something to work on.
━━━ Quick Start ━━━
Prerequisites: Java 21, MariaDB, Python 3, Flask, Apache Ant
Code:
pip install flask numpy pillow
Step 1 Clone and setup database
Code:
git clone https://github.com/elberacasa/l2j-interlude-toolkit.git
cd l2j-interlude-toolkit
brew services start mariadb # or: systemctl start mariadb
./l2j-manage.sh db-reset # creates DB + imports all schemas
Step 2 Start your server
Code:
./l2j-manage.sh start # starts MariaDB -> Login -> Game
./l2j-manage.sh status # verify everything is running
Step 3 Launch the Geodata Editor
Browse and edit geodata visually heightmaps, NSWE walkability maps, block types. All in your browser.
Code:
./geodata-editor.sh # opens http://localhost:5555
Select any of the 139 regions from the dropdown
Switch render modes: heightmap (grayscale), NSWE (color-coded directions), block types, combined
Click cells to edit height and NSWE flags
Export renders as PNG for documentation or debugging
Step 4 Run the Terrain Scanner Dashboard
Real-time web dashboard that shows 20+ headless bots scanning the world in parallel with live progress.
Code:
# First time: create 20 bot accounts + characters + promote to GM
./terrain-scanner.sh --bootstrap --num 20 --promote
# Launch the dashboard
./terrain-scanner.sh --dashboard # opens http://localhost:5556
Open

in your browser
You'll see the
world map grid (11x16 regions), worker table, and event log
Set worker count (default 20), pick scan mode, click
"Start Scan"
Watch regions go from gray (pending) -> blue with pulse (scanning) -> green (complete)
Each worker shows: current region, cells scanned, cells/sec, errors
All 139 regions complete in ~2 minutes 9.1 million cells
Step 5 Export geodata
Once scanned, export bit-perfect .l2d files you can drop into any fresh L2J server:
Code:
# In-game as GM:
//geo_export_all # exports all 139 regions
//geo_export_all /path/to/output/ # custom output directory
//geo_export 20 18 # export a single region
Step 6 Manage your server
Code:
./l2j-manage.sh set-rates 5 # XP/SP/Drop/Adena all at 5x
./l2j-manage.sh gm admin # promote account to GM
./l2j-manage.sh accounts # list all accounts
./l2j-manage.sh players # show online characters
./l2j-manage.sh db-backup # backup MariaDB
./l2j-manage.sh rebuild # recompile Java + deploy JARs
./l2j-manage.sh info # full config summary
./l2j-manage.sh stop # stop everything
━━━ What You Get ━━━
Code:
TWO WEB DASHBOARDS:
http://localhost:5555 -> Geodata Editor (browse, render, edit .l2d files)
http://localhost:5556 -> Terrain Scanner (live world map, 20+ agents, real-time progress)
SERVER CLI:
./l2j-manage.sh -> start/stop, rates, GM, accounts, DB, rebuild
SCANNER CLI:
./terrain-scanner.sh -> bootstrap accounts, launch dashboard, single-agent scan
GEODATA CLI:
./geodata-editor.sh -> launch visual editor
python3 tools/geodata/geodata_tool.py info 20_18.l2d -> region info
python3 tools/geodata/geodata_tool.py render 20_18.l2d -> render to PNG
python3 tools/geodata/geodata_tool.py dump 20_18.l2d -> dump cell data
CUSTOM ADMIN COMMANDS (in-game as GM):
//scan_geo 20 18 0 -> query 256 blocks of geodata
//scan_geo_check 20 18 -> check if geodata is loaded
//geo_export 20 18 -> export one region to .l2d
//geo_export_all -> export all loaded regions
License: GPLv3 | Built on L2J Mobius C6 Interlude
by
elbercasa (elitepvpers:
mibanux)
If you find this useful, drop a star on GitHub and share it around.
PRs welcome let's build something the L2J community has never had before.