LLM Agent files
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
# Architecture
|
||||
|
||||
How the 2009scape game server is put together. See [`../AGENTS.md`](../AGENTS.md) for
|
||||
build/run instructions and the top-level rules, and [`CONTENT_GUIDE.md`](CONTENT_GUIDE.md)
|
||||
for adding gameplay.
|
||||
|
||||
## The two halves: engine vs. content
|
||||
|
||||
The codebase is deliberately split:
|
||||
|
||||
- **`Server/src/main/core/`** — the **engine**. Networking, the RS cache reader, the game
|
||||
loop/pulse scheduler, entities (players, NPCs), the world map, storage/auth, and the
|
||||
**content API** (`core.api`). Engine code must not depend on any specific piece of
|
||||
content.
|
||||
- **`Server/src/main/content/`** — the **content**. Quests, NPC behavior, minigames,
|
||||
skills, items, region-specific interactions. Content is built on top of `core.api` and
|
||||
is discovered reflectively at startup.
|
||||
|
||||
Keeping this boundary clean is the main architectural discipline of the project.
|
||||
|
||||
## Startup and plugin discovery
|
||||
|
||||
Entry point: `core.Server` (`core.Server.main`). During boot the server:
|
||||
|
||||
1. Loads configuration from `worldprops/default.conf` (TOML) into `GameWorld.settings`.
|
||||
2. Loads the RuneScape **cache** (map, item/npc/object definitions) via `core.cache`.
|
||||
3. Runs **`core.plugin.ClassScanner`**, which uses the **classgraph** library to scan the
|
||||
classpath and reflectively instantiate every class implementing a known
|
||||
`ContentInterface` sub-type (see the table in `AGENTS.md`). This is how content
|
||||
"registers" itself — there is no central manifest to edit.
|
||||
4. Starts the world / game loop and opens the network listener(s).
|
||||
|
||||
Because discovery is reflective, **adding a new content class is enough to activate it** —
|
||||
just implement the right interface and put the file under `content/`.
|
||||
|
||||
`ClassScanner` also builds the registry of player-runnable bot scripts (see
|
||||
[`BOT_SCRIPTING.md`](BOT_SCRIPTING.md)) by picking up classes annotated with
|
||||
`@PlayerCompatible`.
|
||||
|
||||
## The game loop / pulse system
|
||||
|
||||
The world advances in discrete **ticks** (RuneScape's canonical tick is 600 ms). Timed
|
||||
and repeating work is modeled as **`Pulse`** objects submitted to `GameWorld.Pulser`. A
|
||||
`Pulse` overrides `pulse(): Boolean` and returns `true` when it is finished. Movement,
|
||||
combat swings, skill actions, and bot scripts are all implemented as pulses.
|
||||
|
||||
`TickListener` content runs once per tick globally; `MovementPulse` handles walking a
|
||||
player/NPC to a destination.
|
||||
|
||||
Coroutines (`kotlinx-coroutines`) back parts of the worker/pulse infrastructure.
|
||||
|
||||
## Entities and nodes
|
||||
|
||||
Everything interactable in the world is a **`Node`** (`core.game.node`):
|
||||
|
||||
- `Entity` → `Player` and `NPC` (living things with combat, skills, movement).
|
||||
- `Player` also has an AI subclass, `AIPlayer`, used for bots.
|
||||
- `Item` / `GroundItem` — items in containers or on the floor.
|
||||
- `Scenery` — world objects (trees, doors, altars…).
|
||||
|
||||
Players carry a lot of linked state under `core.game.node.entity.player.link` (quests,
|
||||
diaries, prayers, emotes, teleport manager, hint icons, etc.) and use `Container`s
|
||||
(`inventory`, `equipment`, `bank`) for items.
|
||||
|
||||
## The content API (`core.api`)
|
||||
|
||||
`core.api.ContentAPI.kt` is a large file of **top-level helper functions** that form the
|
||||
intended, stable surface for content code — e.g. `sendMessage`, `animate`, `addItem`,
|
||||
`removeItem`, `getStatLevel`, `rewardXP`, `teleport`, `getVarp`/`setVarp`, `lock`,
|
||||
`sendDialogue`. Content should call these rather than reaching into engine internals.
|
||||
|
||||
Key interfaces in `core.api` (all extend the `ContentInterface` marker so `ClassScanner`
|
||||
finds them): `InteractionListener`, `InterfaceListener`, `Commands`, `LoginListener`,
|
||||
`LogoutListener`, `TickListener`, `StartupListener`, `ShutdownListener`, `PersistPlayer`,
|
||||
`PersistWorld`, `MapArea`.
|
||||
|
||||
## Networking
|
||||
|
||||
The server speaks the original RS2 build-530 binary protocol.
|
||||
|
||||
- **TCP** is the primary transport.
|
||||
- An optional **WebSocket** listener (`org.java-websocket`) carries the *same* raw binary
|
||||
protocol inside binary frames, enabling browser/PWA clients. It is off by default;
|
||||
enable via `websocket_enabled`/`websocket_port` in `default.conf` (port defaults to
|
||||
`53594 + world_id`). Plain `ws://` for local testing; `wss://` (with a PKCS12 keystore)
|
||||
for production/PWA. See the README for keystore setup.
|
||||
- Client and server must share `secret_key` or the connection is refused.
|
||||
|
||||
A separate **management** interface is defined via Protobuf (`Proto/Management.proto`,
|
||||
`src/main/proto/`).
|
||||
|
||||
## Data, config, and storage
|
||||
|
||||
- **Configuration:** `worldprops/default.conf` (TOML), parsed by
|
||||
`core.game.system.config.ServerConfigParser` into `GameWorld.settings`
|
||||
(`core.game.world.GameSettings`). Paths (cache, saves, data, logs, scripts) are held in
|
||||
`core.ServerConstants`.
|
||||
- **Static game data** (npc spawns, shops, drop tables, item configs) lives in JSON files
|
||||
loaded at runtime. Edit these with the **Thanos tool** (needs Java 11), *not* by hand.
|
||||
- **Item/NPC/object/animation IDs** come from the external **`ConstLib`** dependency
|
||||
(`org.rs09.consts.*`, bundled in `Server/libs/`) — reference them as `Items.FEATHER_314`,
|
||||
`NPCs.CHICKEN_...`, etc.
|
||||
- **Persistence:** player saves + account data via MySQL (`mysql-connector-java`) or
|
||||
SQLite (`sqlite-jdbc`). Controlled by `use_auth` / `persist_accounts` in config; when
|
||||
both are false, **no database is needed** (ideal for local content work). Both MUST be
|
||||
`true` in production.
|
||||
|
||||
## Testing & static analysis
|
||||
|
||||
- **JUnit 5** tests in `Server/src/test/kotlin/` (e.g. `APITests`, `ExchangeTests`,
|
||||
skill/region tests). Run with `./run -t` or `sh mvnw test`.
|
||||
- **detekt** (`Server/detekt.yml`) runs in the Maven `verify` phase and in CI
|
||||
(`.gitlab-ci.yml`); keep new Kotlin clean against it.
|
||||
|
||||
## Build specifics worth knowing
|
||||
|
||||
- Maven, JVM target **11** (JDK 11 required — not newer).
|
||||
- `mvn clean` installs the bundled `ConstLib` and `PrimitiveExtensions` jars from
|
||||
`Server/libs/` into the local Maven repo, so a fresh checkout won't compile until clean
|
||||
has run once.
|
||||
- The package build produces a fat jar (`…-with-dependencies.jar`) whose main class is
|
||||
`core.Server`; the `build` script renames it to `builddir/server.jar`.
|
||||
Reference in New Issue
Block a user