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.