Architecture

The Cabinet of Chronicles

Each folder under the workspace is a complete, self-contained server distribution. Copy-and-diverge per era, not a single multi-module monorepo.

1. The repo is a cabinet of chronicles

Each folder under the workspace is one complete server distribution. There is no shared runtime jar across chronicles. The trade-off is intentional: each game patch reshuffles classes, skills, packets and stats so deeply that forking per chronicle is cheaper than maintaining N feature flags.

workspace/
│
├── L2J_Mobius_C1_HarbingersOfWar       ◄── one folder = one chronicle
├── L2J_Mobius_C4_ScionsOfDestiny           = one self-contained server
├── L2J_Mobius_CT_0_Interlude               (own java/, own dist/, own DB)
├── L2J_Mobius_CT_2.6_HighFive
├── L2J_Mobius_01.0_Ertheia             ◄── first "Main" branch
│
├── L2J_Mobius_Classic_*                ◄── "Classic" rule-set timeline
├── L2J_Mobius_Essence_*                ◄── "Essence" rule-set timeline
└── L2J_Mobius_GD_*                     ◄── "Goddess of Destruction"

2. Inside ONE chronicle folder

java/ is what you compile. dist/ is what you run. The Ant build produces a jar that gets copied into dist/libs/, and you launch the server from dist/login/ and dist/game/.

L2J_Mobius_GD_1.0_Awakening/
│
├── build.xml          ─ Ant build, produces game/login jars
├── readme.txt         ─ chronicle's patch notes
│
├── java/              ─ *source of truth*  (compiled into jars)
│   └── org/l2jmobius/
│        ├── commons/      shared infra (db pool, threads, crypt, net)
│        ├── loginserver/  the authentication daemon
│        ├── gameserver/   the world simulation daemon
│        ├── tools/        offline utilities
│        └── log/          logging glue
│
├── dist/              ─ *runtime layout* (what you ship/run)
│   ├── login/             login daemon's working dir + configs
│   ├── game/              game daemon's working dir + configs + content
│   ├── db_installer/      .sql schema + seed data
│   ├── libs/              third-party jars + built artifact
│   ├── backup/            ops scripts
│   └── images/            launcher art
│
├── launcher/          ─ Swing GUI to start/stop the daemons
└── bin/               ─ Eclipse output (gitignored in practice)

Why so many folders?

L2's release cadence is unusual. Each retail patch can change:

  • The opcode table - same logical packet may move opcodes between chronicles.
  • Item, skill and NPC IDs - same item may be ID 1234 in one era and 5678 in the next.
  • The class system - entirely new classes, skills, awakening paths, masteries.
  • The protocol version - encryption keys and packet layout differ.

Maintaining one codebase that runs all of these via flags would mean every file is full of if (chronicle >= X). Forking per chronicle keeps each codebase focused on the rule-set it actually emulates.