A complete developer toolkit for L2J Mobius C6 Interlude private servers. Connect headless bots, scan terrain at scale, edit geodata visually, and manage your server — all without a Windows game client.
A fully functional Lineage 2 game client written in Python. Connects directly to login + game servers, handles the full authentication chain, character creation, and in-game packet communication. No Windows. No game client install. Just Python.
- Full login protocol (BlowfishKey exchange, RSA auth, server selection)
- GameCrypt XOR cipher implementation (encrypt/decrypt game packets)
- Character creation from code (pick class, name, appearance)
- CreatureSay packet parsing for server-to-client communication
- Admin command execution (SendBypassBuildCmd)
Deploy up to 20+ headless agents simultaneously to scan the entire game world's terrain data. Each agent runs in its own thread with its own game connection, scanning regions in parallel via custom admin commands that query GeoEngine directly.
- Custom
AdminTerrainScanserver command — batch-queries 256 blocks per request via base64-encoded responses AdminGeoExport— full-fidelity geodata export using nativeABlock.saveBlock()(bit-perfect.l2dfiles)- SQLite persistence — resume interrupted scans
- 139 regions scanned in ~2 minutes with 20 agents
Flask-based web UI with Server-Sent Events for live progress tracking.
- World map grid — 11x16 region grid, color-coded: gray=pending, blue+pulse=scanning, green=complete, red=error
- Worker table — per-agent status, current region, cells/sec, error count
- Controls — start/stop, dynamic worker count, scan mode selection
- Event log — scrolling real-time log
http://localhost:5556— Terrain Scanner Dashboardhttp://localhost:5555— Geodata Editor
Visual geodata browser and editor. Renders heightmaps, NSWE walkability maps, block type overlays. Edit individual cells, unblock paths, export renders.
- L2D binary format parser (flat, complex, multilayer blocks)
- Multiple render modes: heightmap, NSWE flags, block types, combined, cell detail
- World coordinate ↔ geo coordinate conversion
- CLI tool for batch operations (
geodata_tool.py)
One script to rule them all. Start/stop servers, manage accounts, tweak rates, backup databases.
./l2j-manage.sh status # Check if servers are running
./l2j-manage.sh start # Start login + game server
./l2j-manage.sh set-rates 5 # Set all rates to 5x
./l2j-manage.sh gm admin # Promote account to GM
./l2j-manage.sh db-backup # Backup MariaDB
./l2j-manage.sh rebuild # Recompile Java + copy JARs
Create N accounts + characters + promote to GM in one command. Uses AutoCreateAccounts for account creation and MariaDB for GM promotion.
# Create 20 scanner accounts with characters, promote all to GM
./terrain-scanner.sh --bootstrap --num 20 --promotelineage2vzla/
├── java/ # L2J Mobius C6 server source (Java)
│ └── org/l2jmobius/
│ ├── gameserver/
│ │ ├── geoengine/ # GeoEngine + geodata block types
│ │ │ └── GeoEngine.java # + exportRegion() for full-fidelity export
│ │ └── handler/
│ │ └── admincommandhandlers/
│ │ ├── AdminTerrainScan.java # Batch geo queries via packets
│ │ └── AdminGeoExport.java # Server-side L2D export
│ └── tools/
│ └── geodataconverter/ # L2J/L2OFF → L2D converter
│
├── tools/
│ ├── headless-client/ # Python headless L2 client
│ │ ├── l2_client.py # Login + Game client protocol
│ │ ├── l2_crypto.py # BlowfishEngine + GameCrypt
│ │ ├── dashboard.py # Web dashboard (Flask + SSE)
│ │ ├── scan_worker.py # Single agent worker thread
│ │ ├── scan_manager.py # Multi-worker orchestrator
│ │ ├── scan_state.py # Thread-safe state + SQLite
│ │ ├── bootstrap.py # Account/character automation
│ │ └── terrain_scanner.py # Single-agent scanner
│ │
│ └── geodata/ # Geodata processing tools
│ ├── l2d_parser.py # L2D binary format parser
│ ├── renderer.py # Region rendering (PNG)
│ ├── geodata_tool.py # CLI geodata tool
│ └── app.py # Web editor (Flask)
│
├── dist/ # Server distribution
│ ├── game/ # Game server runtime
│ │ ├── data/geodata/ # L2D geodata files (139 regions)
│ │ └── config/ # Server configuration
│ └── login/ # Login server runtime
│
├── l2j-manage.sh # Server management CLI
├── terrain-scanner.sh # Terrain scanner launcher
├── geodata-editor.sh # Geodata editor launcher
├── rebuild.sh # Java recompile script
└── build.xml # Ant build configuration
- Java JDK 21 (
brew install openjdk@21on macOS) - MariaDB (
brew install mariadb) - Python 3.10+ with Flask (
pip install flask) - Apache Ant (
brew install ant)
brew services start mariadb
./l2j-manage.sh db-reset # Creates database + imports schemas./l2j-manage.sh start # Starts MariaDB → Login → Game
./l2j-manage.sh status # Verify everything is running./geodata-editor.sh # Opens http://localhost:5555# Bootstrap 20 scanner accounts
./terrain-scanner.sh --bootstrap --num 20 --promote
# Launch the dashboard
./terrain-scanner.sh --dashboard
# Open http://localhost:5556, click "Start Scan"The server ships with flat-only geodata (minimal terrain detail). For production-quality geodata with proper walls, cliffs, and multi-layer terrain, you need a community geodata pack.
- Download a proper geodata pack (L2J or L2OFF format) for Interlude
- Place files in
dist/game/data/geodata/ - Run the built-in converter:
cd dist/game && ./GeoDataConverter.sh # Select J (L2J) or O (L2OFF) format # Converts to L2D with diagonal movement flags
- Restart the server
Export loaded geodata at full fidelity from a running server:
// In-game as GM:
//geo_export 20 18 // Export one region
//geo_export_all // Export all 139 regions
//geo_export_all /tmp/geodata/ // Export to custom directory
| Command | Description |
|---|---|
//geo_export <rx> <ry> |
Export one geodata region to L2D file |
//geo_export_all |
Export all loaded regions |
//scan_geo <rx> <ry> <blockY> |
Query 256 blocks of geo data (used by scanner) |
//scan_geo_check <rx> <ry> |
Check if geodata is loaded for a region |
The traditional approach to geodata scanning (teleporting a character and reading Z coordinates) doesn't work — L2J's teleToLocation does z += 5 without consulting GeoEngine.
Our solution: custom admin commands that query GeoEngine directly.
Scanner Worker L2J Game Server
│ │
│ SendBypassBuildCmd │
│ "scan_geo 20 18 0" │
│ ──────────────────────────────► │
│ │ GeoEngine.getHeightNearest()
│ │ GeoEngine.getNsweNearest()
│ │ × 256 blocks
│ CreatureSay │
│ "GEODATA|20|18|0|<base64>" │
│ ◄────────────────────────────── │
│ │
│ (decode base64 → height + NSWE) │
│ (repeat for blockY 0..255) │
│ (write .l2d file) │
Each region = 256 commands. 20 agents = ~2 minutes for all 139 regions.
This project is open source and contributions are welcome. Some areas that could use help:
- Better geodata generation — movement-based probing to detect actual walkability
- Pathnode generation — create pathnode files from geodata for NPC pathfinding
- Client data extraction — tools to parse official L2 client files for terrain data
- Dashboard improvements — 3D terrain viewer, region comparison tools
- More admin commands — NPC spawn visualization, zone boundary rendering
- Protocol coverage — implement more packet types in the headless client
- Testing — automated tests for the L2 protocol implementation
- Fork the repository
- Create a feature branch (
git checkout -b feature/my-feature) - Commit your changes
- Push to your branch
- Open a Pull Request
| Component | Technology |
|---|---|
| Game Server | Java 21, L2J Mobius C6 Interlude |
| Headless Client | Python 3, raw sockets, Blowfish/RSA crypto |
| Web Dashboards | Flask, Server-Sent Events, vanilla JS |
| Geodata Rendering | NumPy, Pillow |
| Database | MariaDB |
| Build System | Apache Ant |
This project is based on L2J Mobius and is licensed under the GNU General Public License v3.0.
The custom tooling (headless client, scanner, geodata editor, management scripts) is also released under GPLv3.
Created by elbercasa
Built with the L2J Mobius C6 Interlude open-source server emulator.
Lineage 2 is a registered trademark of NCSoft Corporation. This project is not affiliated with or endorsed by NCSoft.