Files
2026-07-05 18:14:54 -04:00

189 lines
9.5 KiB
Markdown

# AGENTS.md — 2009scape
Guidance for LLM agents (and humans) working in this repository. Read this first,
then consult the deeper docs in [`docs/`](docs/) for architecture and content patterns.
## What this project is
2009scape is an **open-source MMORPG emulation server** — a from-scratch remake of
RuneScape 2 as it existed around **build 530 / January 2009**.
The server speaks the original RS2 binary protocol to a matching game client. Players
connect over TCP or (optionally) WebSocket. This repo is the **game server only** — the
client and cache tooling live in separate projects.
## The single most important rule
> **All new contributions MUST be written in Kotlin.** Only touch the legacy Java when
> fixing or updating existing Java code. Do not write new features in Java.
Everything else in this file elaborates on how to do that well.
## Repository layout (top level)
| Path | What it is |
|------|-----------|
| `Server/` | The game server Maven project — **almost all work happens here** |
| `Server/src/main/core/` | Engine: networking, cache, game loop, entities, the content **API** |
| `Server/src/main/content/` | Actual game content: quests, NPCs, minigames, skills, regions |
| `Server/src/test/kotlin/` | JUnit 5 tests |
| `Server/worldprops/default.conf` | TOML server configuration (log level, DB, websocket, etc.) |
| `Server/pom.xml` | Maven build definition |
| `Server/detekt.yml` | Kotlin static-analysis (detekt) ruleset — runs in the `verify` phase |
| `build`, `run` | Bash helper scripts (build / build+run the server) |
| `run-server.bat` | Windows run script |
| `Dockerfile`, `docker-compose.yml` | Containerized server + MySQL |
| `Proto/`, `Server/src/main/proto/` | Protobuf for the management interface |
| `Tools/` | Cache/data editing tools (RSDataSuite, drop-table tool, JSON diff) |
| `docs/` | Deeper docs generated for agents (see below) |
## Build, run, test
**Prerequisites:** JDK 11 (exactly — newer JDKs are not supported by this codebase),
and for JSON data editing, the Thanos tool which needs Java 11.
The Maven wrapper (`Server/mvnw`) is the source of truth; the root `build`/`run` scripts
wrap it. From the repo root:
```bash
./build -g # build the server jar into builddir/server.jar
./build -qgc # clean + build, skipping tests (-q)
./run # incremental build, then run the server
./run -r # force a clean rebuild, then run
./run -t # run tests only, don't start the server
./run -h # full option list
```
Directly with Maven (equivalent, from `Server/`):
```bash
sh mvnw clean # clean (also installs bundled libs from Server/libs/)
sh mvnw package # build the fat jar (…-with-dependencies.jar)
sh mvnw test # run the JUnit test suite
sh mvnw verify # build + run detekt static analysis
```
> If you build manually (not via the provided scripts/IntelliJ), you **must** run
> `mvn clean` at least once first, because the clean phase installs the bundled
> `ConstLib` and `PrimitiveExtensions` jars from `Server/libs/` into the local repo.
The server main class is `core.Server`. It runs headless and listens for client
connections. By default (`use_auth = false`, `persist_accounts = false` in
`default.conf`) **no database is required** — great for local content testing.
**Docker:** copy `mysql.env.example``mysql.env`, create `config/default.conf` from
`Server/worldprops/default.conf`, then `docker compose up --build`.
## Tech stack
- **Kotlin 1.8.20** (primary) + legacy **Java 11** (~roughly half the files, being phased out)
- **Maven** build; JVM target 11
- **kotlinx-coroutines** for the pulse/worker system
- **classgraph** for reflective plugin discovery (see below)
- **JSON-simple** + **toml4j** for config/data, **MySQL connector** + **SQLite** for storage
- **Java-WebSocket** for the optional browser transport
- **JUnit 5** for tests, **detekt** for static analysis
## How content is wired in — the plugin/listener model
This is the concept an agent most needs to understand. **There is no central registry
you edit to add content.** Instead, at startup `core.plugin.ClassScanner` reflectively
scans the classpath (via classgraph) and instantiates every class implementing certain
marker interfaces. You add content by creating a new class implementing the right
interface anywhere under `src/main/content/`.
The core marker interface is `core.api.ContentInterface`. The important sub-interfaces:
| Interface | Override | Purpose |
|-----------|----------|---------|
| `InteractionListener` | `defineListeners()` | React to player↔NPC/object/item/ground interactions (`on(...)`, `onUseWith(...)`, `onEquip(...)`) |
| `InterfaceListener` | `defineInterfaceListeners()` | Handle interface/widget button clicks |
| `Commands` | `defineCommands()` | Register `::command` chat commands, gated by `Privilege` |
| `LoginListener` / `LogoutListener` | | Run logic on player login/logout |
| `TickListener` | | Run logic every game tick |
| `StartupListener` / `ShutdownListener` | | Server lifecycle hooks |
| `PersistPlayer` / `PersistWorld` | | Serialize custom player/world data |
| `NPCBehavior` | | Per-NPC-id AI / combat behavior |
| `MapArea` / `ZoneBuilder` | | Define map zones and their rules |
| `WorldEvent` | | Global scheduled/world events |
| `ActivityPlugin` | | Minigames / activities |
Minimal example (`Server/src/main/content/global/skill/AttackListener.kt`):
```kotlin
class AttackListener : InteractionListener {
override fun defineListeners() {
on(IntType.NPC, "attack") { player, npc ->
player.attack(npc)
return@on true // true = interaction handled
}
}
}
```
Almost everything a content author needs is exposed as top-level helper functions in
`core.api.ContentAPI.kt` (e.g. `sendMessage`, `animate`, `addItem`, `getStatLevel`,
`teleport`, `setVarp`). **Prefer these API functions over reaching directly into engine
internals** — they are the stable, intended surface for content.
## Directory conventions for content
Put new content where similar content already lives:
- `content/region/<kingdom>/` — content tied to a geographic area (Misthalin, Asgarnia,
Kandarin, Karamja, Fremennik, Morytania, Desert, Tirannwn, Wilderness, misc). This is
by far the largest area (~850 files). Quest logic and area NPCs/dialogue go here.
- `content/minigame/<name>/` — self-contained minigames (Barrows, Castle Wars, Pest
Control, MTA, etc.).
- `content/global/skill/<skill>/` — skill training logic shared across the world.
- `content/global/handlers/` — cross-cutting item/interface/scenery handlers.
- `content/data/` — shared data tables and enums (consumables, jewellery, quests list).
Item/NPC/object/animation IDs come from the `ConstLib` dependency (`org.rs09.consts`,
bundled in `Server/libs/`). Static game data (spawns, shops, drop tables) lives in JSON
data files loaded at runtime — edit those with the **Thanos tool** (Java 11), not by hand.
## Coding standards & conventions
- **Kotlin for anything new.** Match the style of the surrounding file.
- Keep engine (`core/`) and content (`content/`) separate. Content depends on the API in
`core.api`; the engine must not depend on specific content.
- Use `ContentAPI` helpers and `colorize(...)` for player-facing messages.
- Author-tag classes with a KDoc `@author` where the surrounding code does.
- **detekt** runs in `mvn verify` and CI — keep new code clean against `Server/detekt.yml`.
- Write a JUnit test under `src/test/kotlin/` when adding non-trivial logic; the suite
has good precedent for API, exchange, and skill tests.
- **Authenticity matters.** This is a remake targeting the real Jan-2009 game. When
implementing behavior, match authentic RuneScape behavior of that era rather than
inventing new mechanics.
## Contributing / merge requests
- Upstream is **GitLab**, not GitHub. Development discussion is on Discord.
- All merge requests **must** use the default MR template
(`.gitlab/issue_templates/Default.md`) or they will be rejected.
- CI is defined in `.gitlab-ci.yml`.
- License is **AGPL-3.0** and will not be changed — keep all contributions compatible.
## Things to be careful about (agent gotchas)
- **Don't switch Java versions.** JDK 11 specifically. Higher versions break the build.
- **Don't write new Java.** New content must be Kotlin.
- **Don't hand-edit binary/JSON game data** — use the provided tools.
- **`mvn clean` installs bundled libs** — a fresh checkout won't compile until it's run.
- **No DB needed for local testing** — leave `use_auth`/`persist_accounts` false locally,
but note the config comments: both MUST be `true` in production.
- The `build`/`run` scripts are Bash (Linux/OSX); Windows uses `run-server.bat`.
- This repo has **no pre-existing CLAUDE.md/AGENTS.md**; the `docs/` folder here was
authored to help agents and is not upstream canon — treat the code and README as
ground truth if they ever disagree.
## Further reading
- [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) — engine internals and the game loop
- [`docs/CONTENT_GUIDE.md`](docs/CONTENT_GUIDE.md) — how to add quests, NPCs, items, commands
- [`docs/BOT_SCRIPTING.md`](docs/BOT_SCRIPTING.md) — the player self-botting system (`::script`) and how to add new scripts
- [`docs/SERVER_SETUP_ARCH.md`](docs/SERVER_SETUP_ARCH.md) — compile & run the server on Arch Linux and connect from a LAN PC
- [`docs/GLOSSARY.md`](docs/GLOSSARY.md) — RuneScape/RSPS terms used throughout the code
- `README.md` — project history, values, and full setup instructions