L2J Interlude Toolkit
Headless Python Client ท Multi-Agent Geodata Scanner ท Web Dashboard ท Server Management

No Windows. No Game Client. Just Python + Your L2J Server.
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
━━━ 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) |
━━━ 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
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
Code:
./l2j-manage.sh start # starts MariaDB -> Login -> Game ./l2j-manage.sh status # verify everything is running
Browse and edit geodata visually heightmaps, NSWE walkability maps, block types. All in your browser.
Code:
./geodata-editor.sh # opens http://localhost:5555
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
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
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.







