# 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//` — 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//` — self-contained minigames (Barrows, Castle Wars, Pest Control, MTA, etc.). - `content/global/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