Vortrium Server Project
★ In Development ★
Last updated: 07 Sep 2026
Best viewed at 1024x768

▸ WELCOME

An authoritative game server with no inherited code.

L2V serves the unmodified retail client — protocol revisions 140 and 152, carrying High Five content. The protocol and the geodata are constraints we inherited; everything behind them is ours. The server is authoritative: the client sends intentions, never state.

No L2J, no forked core, no hand-written codec. Eighteen Gradle modules with module-info.java everywhere, so the compiler is the first thing that refuses an architecture violation.

Twenty inviolable rules hold the design in place, and half of them are refused by a machine rather than by review.

▸ WHAT RUNS TODAY

a real client signs in, enters the world and walks

Login and game are two ports of one process on day one. Everything below exists in the repository with tests over it; everything not below does not exist, and the roadmap says so plainly.

Login conversation

The inherited handshake in full: RSA credential block, Blowfish stream, scrambled modulus. Argon2id passwords, rate limiting per address and per account, backoff on failure.

World handoff

Server list, single-use session token, and a game port that expires the token the first time it accepts it. Accounts are created by command, never by the port.

Generated codec

Every packet, enum and buffer primitive is emitted from schema files. schema.lock fails the build on a removed, reordered or retyped field, or a renumbered opcode.

Entering and leaving

Character creation, selection and entry against Postgres, with templates and starting items from the datapack — and a way back out, to the character list or to the login screen.

Tick and zones

One platform thread per zone, one writer, two rendezvous per tick. A message posted in tick N is applied in tick N+1, always.

Movement on real ground

Movement resolved cell by cell against a converted collision grid, memory mapped off heap. A* over the same grid for routes.

Interest management

Appear, move and disappear go out as CharInfo, MoveToLocation and DeleteObject, decided by a cell grid and never by a sweep over the zone.

Chat and equipment

A line of chat and a change of gear both go out through the same three by three of cells. The audience is answered on the thread that owns it, not gathered at the edge.

Pickup and drop

The client names what it reaches for and where it believes it stands; the second is dropped. The zone measures the distance and decides which of two players got there first.

Inventory that survives a restart

What a character carries is written to Postgres and read back on entry. The counter that gives each item its identity survives a restart too, so the numbering continues instead of starting again.

Money as a balance

Currency is a balance rather than a row in a bag, so money on the ground no longer reads as money destroyed. Two packets can no longer change the same bag at the same moment either.

Ledger and transactions

Nothing can be added, removed or moved without saying where the record goes and why. Transactions apply together or undo backwards inside one tick.

Datapack catalogues

The skill catalogue and the class stat tables are converted from the client content and loaded at boot into immutable structures. Combat needs both before any of it can be written.

Targeting

A player can aim at something, and the server is what decides what was aimed at. Everything a target is for, damage and skills and death, is still ahead.

Determinism and replay

Five hundred ticks over sixty four entities, seeded, produce the same state hash every run; two zones trading traffic over four hundred ticks do too.

The probe

Instrumentation over every seam the simulation already exposes, so a silence stops looking like an answer. It decides nothing and is wired only at debug level.

▸ MEASURED, NOT CLAIMED

JMH with the allocation profiler, in the build
24 B
allocated per tick

Regardless of entity count, and zero bytes per entity per tick. A test asks the JVM and fails the build if it is not flat.

34 µs
to tick 1,000 walkers

Against a budget of 60,000 µs, movement resolved against a real collision grid.

100
entities per client, max

Nearest 24 updated every tick, the rest every fifth — whatever a siege does.

140 Mbit
for 3,000 players

Where an unmanaged broadcast costs 380. Interest management is the real bottleneck, not the tick.

▸ WHY NOT BUILD ON L2J

L2J made two decades of private servers possible. It is also the reason most of them cannot be reasoned about. Nothing here is a fork, a port or a cleanup of it.

01

Accreted, not designed

Twenty years of patches over an already reverse engineered core. Behaviour lives in special cases rather than in a model anyone can state.

02

A codec written by hand

Here the codec is generated from a versioned schema and a lock file, so a field that quietly moves is a failed build instead of a client rendering the wrong thing.

03

Locks and global state

Single writer per zone, no mutable static state, no locks in the simulation — all three refused by ArchUnit, not by convention.

04

Nothing to replay

Injected time and randomness make the simulation deterministic, so a concurrency bug can be reproduced instead of argued about.

▸ ARCHITECTURE

dependencies point one way only

The module system enforces the direction. The domain knows nothing about Netty, JDBC, Postgres or a packet; the game server is the only module that knows every other one.

                   domain  <----- (nothing beyond the JDK)
                      ^
  collision ----------|
  datapack -----------|
                      |
                    world
                      ^
  protocol ---> net --|---> journal
                      |
                persistence
                      ^
                game-server   (composition root)
EIGHTEEN MODULES
vortrium-bom
vortrium-protocol-schema
vortrium-protocol
vortrium-domain
vortrium-collision
vortrium-geodata
vortrium-content
vortrium-datapack
vortrium-world
vortrium-journal
vortrium-persistence
vortrium-net
vortrium-ops
vortrium-probe
vortrium-testkit
vortrium-conformance
vortrium-login-server
vortrium-game-server
edge working

Netty and framing

Handshake, cipher, framing and dispatch on the event loop, fixed at core count. Virtual threads only for edge work that blocks, never for game logic.

protocol working

Schema and codec

Records under a sealed interface, dispatch exhaustive by construction. Nine of the ten shapes an inherited wire format needs — including counts the wire never carries and spans the codec measures itself.

simulation in progress

Tick, zones, interest

Columnar entity storage rather than an array of objects, primitive maps, a free list of slots. No allocation per entity per tick.

domain early

Pure rules

Damage, skills, inventory validation, experience curves. No wall clock, no global random, no input or output — and no dependency beyond the JDK.

collision in progress

Converted ground

The retail High Five client ships no geodata at all, so the grid is converted offline from a geodata set of the same world. The server maps ground and never invents it.

durability designed

Journal and outbox

Write behind snapshots for what may be lost; an fsynced append only ledger for what may not. Confirmation of an irreversible event waits on the journal.

observability working

Probe

Instrumentation that wraps the seams the simulation already exposes, logs a line and delegates. Wired only at debug level, decides nothing, and depended on by the composition root alone.

▸ TWENTY INVIOLABLE RULES

A rule without enforcement is a suggestion, and a suggestion does not survive six months of hurry. Each rule names the mechanism that rejects a violation, and says honestly whether it exists yet.

10
automated
A machine rejects the violation before the commit completes.
8
partial
Half automated; the notes name the half that is not.
2
pending
Held by review until the code each one guards exists.
01 No input or output on the tick thread partial
02 Single writer per zone auto
03 The domain depends on nothing but the JDK auto
04 The server is authoritative; every packet is an intention pending
05 Zero allocation on the hot path in steady state auto
06 Determinism: time and randomness are injected auto
07 No mutable static state auto
08 The codec is generated, never hand written auto
09 No database reads in game runtime partial
10 Cross zone traffic is messages only auto
11 The datapack is immutable at runtime auto
12 An invalid packet drops the connection auto
13 No optimisation without measurement partial
14 Preview features stay out of production modules auto
15 Every item mutation emits a ledger event partial
16 An item transaction is atomic within one tick partial
17 Collision is converted from one source, never authored partial
18 No broadcast without interest management partial
19 No unbounded per connection buffer partial
20 An irreversible event is fsynced before it is confirmed pending

▸ ROADMAP

no dates promised

The figure above is the roadmap weighted by phase, not a guess about the game. Combat, skills, non player characters and quests do not exist yet: most of what is finished is the machine that has to carry them.

LEGEND
done
in progress
not started
01

Foundation and conformance

Module layout, module descriptors, quality gate, the rules as tests.

done
02

Protocol and codec generator

Schema language, generated codec, schema.lock, fuzzing at fifty thousand frames per direction.

done
03

Login server

Handshake, Argon2id, rate limiting, world directory, session handoff, account command.

done
04

World runtime

Tick loop, zones, barriers, columnar storage, deterministic replay.

done
05

Entering and walking

Character lifecycle, collision walk, pathfinding, interest managed sight, chat and equipment.

done
06

Items and durability

Inventory, currency and persistence are in; the journal with fsync behind them is not.

in progress
07

The whole world

A streaming converter for three thousand regions instead of the tiles a run can hold.

next
08

Combat and skills

The skill catalogue and the class stat tables load at boot and a player can take aim. High Five formulas, damage, death and non player character intelligence are still ahead.

early
09

Systems and endgame

Clans, sieges, olympiad, instances, quests.

not started
10

Load and hardening

Bot swarm against the cost premise, exploit review, operator tooling.

in progress

▸ LICENCES

open to contribute, closed to exploit

L2V is not open source and will not become it. The source is private, and a licence is nominal, is not transferable, and can be withdrawn. What happened to L2J was not that it was free; it was that nobody could say no. Here somebody can.

WHAT ENDS A LICENCE
  1. Redistributing the source or a binary, to anyone, at any price.
  2. Running a fork. A change goes upstream, or stays private and ends with the licence.
  3. Selling anything that changes combat balance.
  4. Claiming the verified badge while running a modified core.
  5. Leaving a known exploit unpatched after the fix has shipped.
Contributor
read the source, and change it
US$ 9 a month
or US$ 90 a year
  • The private repository: read it, and open a pull request against it.
  • Review from the people who wrote the rules your request has to pass.
  • The design discussions, and a vote on every RFC.
  • The converted datapack and the internal builds, so you can run what you changed.
Founder
earned, not sold
earned
the first twenty merged patches
  • Everything a contributor has, for good, with the fee waived.
  • Given for a merged patch of substance. There is no way to buy it.
  • A non-commercial operator licence, included.
  • Your name stays in the credits whatever happens to the project later.
Operator
run a server, sell nothing
US$ 180 a year
one server, no monetisation of any kind
  • Run one public server. No donations, no shop, no sale of anything.
  • The verified build badge, and a listing in the server directory.
  • Security patches the day the contributors get them, not later.
  • The conversion tooling for the world your server runs on.
Commercial
run a server and take money for it
US$ 150 a month
to 300 players; US$ 400 to 1,000; negotiated above
  • Everything above, plus the right to take donations and to sell.
  • Priced by peak concurrent players, which your own server reports.
  • An answer within one business day, and releases before they are public.
  • Nothing you sell may change combat balance. That line is in the licence.

Prices are the list rate. Brazil, Latin America and the CIS pay a regional rate; ask for it. Licences are issued by hand, one conversation at a time.

Where the design is written down

The architecture reference is the source of truth, not a summary of the code — when the two disagree, one of them is a bug and the pull request says which. vortrium-conformance is the executable half of the rules. The repository is private while the protocol work settles.