diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
new file mode 100644
index 0000000..246bf7c
--- /dev/null
+++ b/.github/workflows/docs.yml
@@ -0,0 +1,65 @@
+name: Documentation
+
+on:
+ release:
+ types: [published]
+ workflow_dispatch:
+ pull_request:
+ paths:
+ - 'docs/**/*.md'
+ - '.markdownlint.yml'
+
+# Required for GitHub Pages deployment via Actions
+permissions:
+ contents: read
+ pages: write
+ id-token: write
+
+# Allow only one concurrent deployment; skip in-progress runs
+concurrency:
+ group: "pages"
+ cancel-in-progress: false
+
+jobs:
+ lint:
+ name: Validate Markdown
+ runs-on: ubuntu-latest
+ if: github.event_name == 'pull_request'
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Lint documentation Markdown
+ uses: DavidAnson/markdownlint-cli2-action@v16
+ with:
+ globs: "docs/**/*.md"
+ config: ".markdownlint.yml"
+
+ build-and-deploy:
+ name: Build and Deploy
+ runs-on: ubuntu-latest
+ if: github.event_name == 'release' || github.event_name == 'workflow_dispatch'
+
+ environment:
+ name: github-pages
+ url: ${{ steps.deployment.outputs.page_url }}
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Configure GitHub Pages
+ uses: actions/configure-pages@v5
+
+ - name: Build Jekyll site
+ uses: actions/jekyll-build-pages@v1
+ with:
+ source: ./docs
+ destination: ./_site
+
+ - name: Upload Pages artifact
+ uses: actions/upload-pages-artifact@v3
+
+ - name: Deploy to GitHub Pages
+ id: deployment
+ uses: actions/deploy-pages@v4
diff --git a/.markdownlint.yml b/.markdownlint.yml
new file mode 100644
index 0000000..9abb020
--- /dev/null
+++ b/.markdownlint.yml
@@ -0,0 +1,34 @@
+# markdownlint configuration
+# https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md
+
+default: true
+
+# MD013 — Line length: documentation lines are allowed to exceed 80 chars
+MD013: false
+
+# MD022 — Blanks around headings: Kramdown attribute syntax ({: .no_toc }) must
+# immediately follow a heading with no blank line — disable this rule
+MD022: false
+
+# MD025 — Single H1: pages have both front-matter title: and an # H1 heading;
+# disable front-matter title detection to avoid false positives
+MD025:
+ front_matter_title: ""
+
+# MD033 — Inline HTML: needed for Jekyll/Liquid includes and badge images
+MD033: false
+
+# MD036 — Emphasis as heading: just-the-docs sub-sections inside tables use
+# bold labels that markdownlint mistakes for headings
+MD036: false
+
+# MD041 — First line must be H1: front-matter pages don't start with a heading
+MD041: false
+
+# MD024 — No duplicate headings: allow sibling duplicates across sections
+MD024:
+ siblings_only: true
+
+# MD007 — Unordered list indentation: use 2-space indent under list items
+MD007:
+ indent: 2
diff --git a/docs/Gemfile b/docs/Gemfile
new file mode 100644
index 0000000..7359a80
--- /dev/null
+++ b/docs/Gemfile
@@ -0,0 +1,4 @@
+source "https://rubygems.org"
+
+gem "github-pages", group: :jekyll_plugins
+gem "just-the-docs"
diff --git a/docs/_config.yml b/docs/_config.yml
new file mode 100644
index 0000000..a48cb55
--- /dev/null
+++ b/docs/_config.yml
@@ -0,0 +1,71 @@
+remote_theme: just-the-docs/just-the-docs
+
+title: EzRTP
+description: >-
+ Production-focused random teleport plugin for Minecraft servers — safety-first
+ teleportation, cross-platform compatibility, and configuration-driven control
+ for server owners.
+
+url: "https://ez-plugins.github.io"
+baseurl: "/EzRTP"
+
+# ── Appearance
+────────────────────────────────────────────────────────────────
+color_scheme: ezrtp
+heading_anchors: true
+
+# ── Header links
+──────────────────────────────────────────────────────────────
+aux_links:
+ "GitHub":
+ - "https://github.com/ez-plugins/EzRTP"
+ "Modrinth":
+ - "https://modrinth.com/plugin/ezplugins-ezrtp"
+aux_links_new_tab: true
+
+# ── Navigation
+────────────────────────────────────────────────────────────────
+nav_sort: case_insensitive
+nav_external_links:
+ - title: Changelog
+ url: "https://github.com/ez-plugins/EzRTP/releases"
+ hide_icon: false
+
+# ── Search
+────────────────────────────────────────────────────────────────────
+search_enabled: true
+search:
+ heading_level: 2
+ previews: 3
+ preview_words_before: 5
+ preview_words_after: 10
+ tokenizer_separator: /[\s/]+/
+
+# ── Footer
+────────────────────────────────────────────────────────────────────
+back_to_top: true
+back_to_top_text: "Back to top"
+
+footer_content: >-
+ Copyright © 2024–2026 Gyvex.
+ Distributed under the
+ MIT License.
+
+# ── Kramdown
+──────────────────────────────────────────────────────────────────
+kramdown:
+ syntax_highlighter_opts:
+ block:
+ line_numbers: false
+
+# ── Plugins
+───────────────────────────────────────────────────────────────────
+plugins:
+ - jekyll-remote-theme
+ - jekyll-seo-tag
+
+# ── Build exclusions
+──────────────────────────────────────────────────────────
+exclude:
+ - Gemfile
+ - Gemfile.lock
diff --git a/docs/_sass/color_schemes/ezrtp.scss b/docs/_sass/color_schemes/ezrtp.scss
new file mode 100644
index 0000000..2f2c868
--- /dev/null
+++ b/docs/_sass/color_schemes/ezrtp.scss
@@ -0,0 +1,50 @@
+// EzRTP — dark/blue/white color scheme for just-the-docs
+//
+// Palette:
+// Background #141414 (body)
+// Surface #1c1c1c (sidebar, cards)
+// Elevated #222222 (code blocks, search, table rows)
+// Border #2e2e2e
+// Text #d0d0d0 (body) / #ffffff (headings)
+// Accent #29b6f6 (blue — links, nav highlight, buttons)
+// Accent dim #0288d1 (hover state)
+
+$color-scheme: dark;
+
+// ── Surfaces
+─────────────────────────────────────────────────────────────────
+$body-background-color: #141414;
+$sidebar-color: #1c1c1c;
+$feedback-color: #181818;
+
+// ── Typography
+────────────────────────────────────────────────────────────────
+$body-text-color: #d0d0d0;
+$body-heading-color: #ffffff;
+
+// ── Links & accent
+───────────────────────────────────────────────────────────
+$link-color: #29b6f6;
+$btn-primary-color: #29b6f6;
+
+// ── Borders
+───────────────────────────────────────────────────────────────────
+$border-color: #2e2e2e;
+
+// ── Code
+──────────────────────────────────────────────────────────────────────
+$code-background-color: #222222;
+
+// ── Tables
+────────────────────────────────────────────────────────────────────
+$table-background-color: #1a1a1a;
+
+// ── Search
+────────────────────────────────────────────────────────────────────
+$search-background-color: #222222;
+$search-foreground-color: #c0c0c0;
+$search-border-color: #333333;
+
+// ── Buttons
+─────────────────────────────────────────────────────────────────
+$base-button-color: #252525;
diff --git a/docs/api/plugin-api.md b/docs/api/plugin-api.md
index 9f5720d..567b03e 100644
--- a/docs/api/plugin-api.md
+++ b/docs/api/plugin-api.md
@@ -1,58 +1,238 @@
-# EzRTP Plugin API
+---
+title: Developer API
+nav_order: 10
+---
-Purpose: Expose a small helper API for other plugins to trigger random teleports (RTP) programmatically.
+# Developer API
-Usage:
+EzRTP exposes a small public API so other plugins can trigger random teleports
+programmatically — including custom destinations, radius overrides, and
+success/failure callbacks.
-- Simple RTP: Calls the plugin's default RTP flow (uses configured settings, costs, cooldowns, etc.)
+---
-Example:
+## Adding the dependency
+
+The API is published as a separate lightweight artifact (`ezrtp-api`) that
+contains only the classes you need. You do **not** need to depend on the full
+plugin jar.
+
+### Maven
+
+```xml
+
+ com.skyblockexp
+ ezrtp-api
+ 3.0.1
+ provided
+
+```
+
+### Gradle
+
+```groovy
+compileOnly 'com.skyblockexp:ezrtp-api:3.0.1'
+```
+
+Declare EzRTP as a soft or hard dependency in your `plugin.yml` so Bukkit
+loads it before your plugin:
+
+```yml
+# hard dependency — your plugin will not load if EzRTP is absent
+depend: [EzRTP]
+
+# soft dependency — your plugin loads with or without EzRTP
+softdepend: [EzRTP]
+```
+
+---
+
+## Quick start
```java
import com.skyblockexp.ezrtp.api.EzRtpAPI;
-// RTP with default settings
+// Check that EzRTP is running before calling anything
+if (EzRtpAPI.isAvailable()) {
+ EzRtpAPI.rtpPlayer(player);
+}
+```
+
+That's it for the simplest case. The teleport uses all settings from the
+server's `rtp.yml`, charges any configured cost, and respects cooldowns.
+
+---
+
+## API reference
+
+All methods are static on `EzRtpAPI`.
+
+### `isAvailable()`
+
+```java
+boolean available = EzRtpAPI.isAvailable();
+```
+
+Returns `true` if EzRTP is loaded and its service is registered. Call this
+before any other method if EzRTP is a soft dependency.
+
+---
+
+### `rtpPlayer(Player player)`
+
+Teleport a player using the server's default RTP settings.
+
+```java
EzRtpAPI.rtpPlayer(player);
```
-- RTP with custom settings: Use an instance of `RandomTeleportSettings`.
+- Uses `TeleportReason.COMMAND` — cooldowns, costs, and limits apply normally.
+- Fire-and-forget. No way to know if it succeeded.
+
+---
-Example:
+### `rtpPlayer(Player player, Object settings)`
+
+Teleport with custom settings (e.g. a different world or radius).
```java
-import com.skyblockexp.ezrtp.api.EzRtpAPI;
import com.skyblockexp.ezrtp.config.RandomTeleportSettings;
+import org.bukkit.configuration.file.YamlConfiguration;
-RandomTeleportSettings settings = ...; // build or obtain from config
+YamlConfiguration cfg = new YamlConfiguration();
+cfg.set("world", "world_the_end");
+cfg.set("radius.min", 1000);
+cfg.set("radius.max", 5000);
+
+RandomTeleportSettings settings = RandomTeleportSettings.fromConfiguration(cfg, getLogger());
+EzRtpAPI.rtpPlayer(player, settings);
+```
+
+`RandomTeleportSettings.fromConfiguration()` reads a `ConfigurationSection`
+exactly like EzRTP reads `rtp.yml`. Any key you omit uses the built-in default.
+
+---
+
+### `rtpPlayer(Player player, Object settings, Consumer callback)`
+
+Teleport with custom settings **and** a callback that fires when the teleport
+completes or fails.
+
+```java
EzRtpAPI.rtpPlayer(player, settings, success -> {
if (success) {
- // do something on success
+ player.sendMessage("You were teleported!");
+ } else {
+ player.sendMessage("No safe location found.");
}
});
```
-Notes:
+The `Boolean` passed to the callback is:
-- If EzRTP is not present or not enabled, the API will log a warning and the call will be ignored.
-- The API helpers default to using `TeleportReason.COMMAND` for cost/cooldown resolution.
+- `true` — teleport completed successfully.
+- `false` — teleport failed (no valid location found, player moved during countdown, etc.).
-Where to find the code:
+The callback is invoked on the main server thread.
-- API helper: [ezrtp-common/src/main/java/com/skyblockexp/ezrtp/api/EzRtpAPI.java](ezrtp-common/src/main/java/com/skyblockexp/ezrtp/api/EzRtpAPI.java#L1-L200)
-- Plugin accessor: [ezrtp-common/src/main/java/com/skyblockexp/ezrtp/EzRtpPlugin.java](ezrtp-common/src/main/java/com/skyblockexp/ezrtp/EzRtpPlugin.java#L1-L200)
+---
-This repository includes a lightweight API helper designed for ease-of-use by other plugins. It does not introduce a formal service registry; instead it locates the running EzRTP plugin via the server plugin manager and delegates to the running `RandomTeleportService`.
+### `getTeleportService()`
-If you need a more advanced integration (for example, to run teleports using a specific teleport reason, or to integrate with economy providers in a custom way), obtain the `RandomTeleportService` directly:
+Returns the raw `TeleportService` instance for advanced use cases.
```java
-import com.skyblockexp.ezrtp.api.EzRtpAPI;
-import com.skyblockexp.ezrtp.teleport.RandomTeleportService;
+import com.skyblockexp.ezrtp.api.TeleportService;
+import com.skyblockexp.ezrtp.teleport.TeleportReason;
-RandomTeleportService service = EzRtpAPI.getTeleportService();
+TeleportService service = EzRtpAPI.getTeleportService();
if (service != null) {
- // call service.teleportPlayer(...) variants directly
+ service.teleportPlayer(player, TeleportReason.JOIN);
+}
+```
+
+Use this when you need to pass a specific `TeleportReason` or call a variant
+not exposed by the static helpers.
+
+---
+
+## `TeleportReason` enum
+
+`TeleportReason` tells EzRTP why the teleport is happening. This affects which
+cost and cooldown rules are applied.
+
+| Value | When to use |
+|:------|:------------|
+| `COMMAND` | Player triggered the teleport via a command or button. Cooldowns and costs apply. |
+| `JOIN` | Player joined the server and was auto-teleported. Uses the `on-join` cost/cooldown rules. |
+
+---
+
+## `TeleportService` interface
+
+The full interface for advanced callers:
+
+```java
+public interface TeleportService {
+ // Simple teleport, no callback
+ void teleportPlayer(Player player, TeleportReason reason);
+
+ // Custom settings, no callback
+ void teleportPlayer(Player player, Object settings, TeleportReason reason);
+
+ // Simple teleport with callback
+ void teleportPlayer(Player player, TeleportReason reason, Consumer callback);
+
+ // Custom settings with callback
+ void teleportPlayer(Player player, Object settings, TeleportReason reason, Consumer callback);
}
```
-The API is intentionally minimal to avoid tight coupling; it is safe to call from other plugins during runtime, but callers should handle the case where EzRTP is not present or not yet enabled.
+---
+
+## `RandomTeleportSettings` — full YAML reference
+
+`RandomTeleportSettings.fromConfiguration(ConfigurationSection, Logger)` reads
+any YAML section. Below are the keys you can set:
+
+| Key | Type | Example | Description |
+|:----|:-----|:--------|:------------|
+| `world` | String | `world_the_end` | World to teleport into. `auto` = player's current world. |
+| `center.x` / `center.z` | int | `0` | Search centre coordinates. |
+| `radius.min` | int | `500` | Minimum distance from centre. |
+| `radius.max` | int | `3000` | Maximum distance from centre. |
+| `radius.use-world-border` | bool | `true` | Use world border edge as the maximum radius. |
+| `min-y` / `max-y` | int | `54` / `320` | Vertical bounds for destinations. |
+| `max-attempts` | int | `20` | Candidate attempts before failing. |
+| `cost` | double | `5.0` | Economy cost per teleport (Vault). |
+| `countdown-seconds` | int | `3` | Countdown before teleport. `0` = instant. |
+| `search-pattern` | String | `circle` | Coordinate search shape. See [Search Patterns](../config/search-patterns). |
+| `biomes.include` | list | `[FOREST, PLAINS]` | Require one of these biomes. |
+| `biomes.exclude` | list | `[OCEAN]` | Reject these biomes. |
+| `protection.avoid-claims` | bool | `true` | Skip locations inside protected claims. |
+
+```java
+YamlConfiguration cfg = new YamlConfiguration();
+cfg.set("world", "world");
+cfg.set("radius.min", 2000);
+cfg.set("radius.max", 8000);
+cfg.set("search-pattern", "circle");
+cfg.set("biomes.include", List.of("FOREST", "BIRCH_FOREST"));
+cfg.set("cost", 10.0);
+
+RandomTeleportSettings settings = RandomTeleportSettings.fromConfiguration(cfg, getLogger());
+EzRtpAPI.rtpPlayer(player, settings, success -> {
+ if (!success) player.sendMessage("Could not find a forest location. Try again.");
+});
+```
+
+---
+
+## Null safety
+
+- If EzRTP is not installed or not yet enabled, `getTeleportService()` returns
+ `null` and all `rtpPlayer` helpers silently do nothing (or invoke the callback
+ with `false`).
+- Always guard with `EzRtpAPI.isAvailable()` when EzRTP is a soft dependency.
+- The API is safe to call from any thread; EzRTP handles its own async/sync
+ boundaries internally.
diff --git a/docs/commands-permissions.md b/docs/commands.md
similarity index 68%
rename from docs/commands-permissions.md
rename to docs/commands.md
index c9e4c08..d6ad059 100644
--- a/docs/commands-permissions.md
+++ b/docs/commands.md
@@ -1,8 +1,11 @@
-# EzRTP Commands & Permissions
+---
+title: Commands
+nav_order: 3
+---
-## Commands
+# Commands
-### Player command
+## Player command
- `/rtp`
- Opens GUI when GUI is enabled.
@@ -11,7 +14,7 @@
- Teleports using the named center configured under `centers.named` in `rtp.yml`.
- Named center is applied as a center override only; the world's normal RTP settings still apply.
-### RTP subcommands
+## RTP subcommands
- `/rtp reload`
- `/rtp stats [page]`
@@ -26,12 +29,12 @@
- `/rtp addcenter `
- `/rtp pregenerate [world] [radius]`
-### Force command
+## Force command
- `/forcertp [world]`
- If world is omitted, EzRTP uses `force-rtp.yml` `default-world`.
-### WorldGuard region mode (optional)
+## WorldGuard region mode (optional)
When enabled:
@@ -39,22 +42,6 @@ When enabled:
This centers RTP around the specified WorldGuard region and can apply per-region overrides.
-## Declared permissions
-
-- `ezrtp.use` (default `true`)
-- `ezrtp.reload` (default `op`)
-- `ezrtp.stats` (default `op`)
-- `ezrtp.heatmap` (default `op`)
-- `ezrtp.heatmap.fake` (default `op`)
-- `ezrtp.queue.bypass` (default `op`)
-- `ezrtp.forcertp` (default `op`)
-
-## Additional permission usage
-
-- `ezrtp.setcenter` is used by `/rtp setcenter` and `/rtp addcenter`.
-- `queue.yml` controls queue bypass permission path.
-- `gui.yml` and `network.yml` entries can require per-option permissions.
-
## Quick admin cheatsheet
- Reload config: `/rtp reload`
diff --git a/docs/config/config.md b/docs/config/config.md
index 70786c6..8add096 100644
--- a/docs/config/config.md
+++ b/docs/config/config.md
@@ -1,12 +1,45 @@
+---
+title: config.yml
+nav_order: 1
+parent: Config Reference
+---
+
# config.yml
-Global/core plugin settings:
+Global plugin settings — language, message prefix, and a few defaults used by
+utility commands. All teleport behaviour, safety rules, and cooldowns live in
+their own dedicated files.
+
+## Settings
+
+| Key | Default | Description |
+|:----|:--------|:------------|
+| `message-prefix` | `"&7[&bEzRTP&7] &r"` | Text prepended to every chat message the plugin sends. Supports `&` color codes and MiniMessage. |
+| `language` | `en` | Selects which file under `messages/` is used for player-facing text. Example: `en` loads `messages/en.yml`. |
+| `messages.force-legacy-colors` | `false` | Set to `true` on servers running very old clients that do not understand MiniMessage formatting. Converts all output to legacy `§` color codes. |
+| `worldguard.region-command.enabled` | `false` | Allows `/rtp ` to teleport players randomly **inside** a named WorldGuard region. Requires WorldGuard. |
+| `worldguard.region-command.autocomplete` | `false` | When enabled, tab-completing `/rtp` will suggest WorldGuard region names. |
+| `world` | `world` | Fallback world name used by utility commands (e.g. `/rtp pregenerate`). Does not affect normal RTP — that is set in `rtp.yml`. |
+| `radius.min` | `500` | Fallback minimum radius used by utility commands. Does not affect normal RTP. |
+| `enable-bstats` | `true` | Sends anonymous usage statistics to [bStats](https://bstats.org/plugin/bukkit/EzRTP/27735). No personal data is collected. Set to `false` to opt out. |
+
+## Example
+
+```yml
+message-prefix: "&7[&bEzRTP&7] &r"
+language: en
+
+messages:
+ force-legacy-colors: false
+
+worldguard:
+ region-command:
+ enabled: false
+ autocomplete: false
-- `message-prefix`: prefix injected into plugin chat output.
-- `language`: selects `messages/.yml`.
-- `messages.force-legacy-colors`: forces MiniMessage output into legacy colors.
-- `worldguard.region-command.*`: toggles `/rtp ` command behavior.
-- `world`, `radius.min`: defaults used by utility commands such as pregeneration helpers.
-- `enable-bstats`: enables anonymous bStats telemetry.
+enable-bstats: true
+```
-Most gameplay RTP settings moved to `rtp.yml` and `limits.yml`.
+{: .note }
+Most gameplay settings (destination bounds, safety, cooldowns, biomes) are in
+`rtp.yml` and `limits.yml`, **not** here.
diff --git a/docs/config/force-rtp.md b/docs/config/force-rtp.md
index 340687b..50a0253 100644
--- a/docs/config/force-rtp.md
+++ b/docs/config/force-rtp.md
@@ -1,5 +1,41 @@
+---
+title: force-rtp.yml
+nav_order: 7
+parent: Config Reference
+---
+
# force-rtp.yml
-Administrative `/forcertp` behavior.
+Controls the `/forcertp ` command that lets admins and moderators teleport
+another player to a random location. This file lets you set the default world and
+choose which normal restrictions are bypassed when the command is used.
+
+---
+
+## Settings
+
+| Key | Default | Description |
+|:----|:--------|:------------|
+| `default-world` | `world` | World used when the command is run without specifying a world. Set to `auto` to send the target player to a random location in their current world. |
+| `bypass.cooldown` | `true` | When `true`, `/forcertp` ignores the target player’s cooldown. Recommended — admins should never be blocked by a cooldown on a moderation tool. |
+| `bypass.permission` | `true` | When `true`, `/forcertp` ignores any per-destination permission checks on the target player. |
+| `bypass.safety` | `false` | When `true`, the destination does **not** have to pass safety checks — players can be placed on lava, inside water, etc. Leave this `false` unless you have a specific reason. |
+
+---
+
+## Example
+
+```yml
+default-world: world
+
+bypass:
+ cooldown: true # don't wait for the player's cooldown
+ permission: true # ignore per-destination permissions on the target
+ safety: false # still enforce safe landing
+```
+
+---
-Use this file to control command restrictions and defaults for forced teleports. Keep this file aligned with your moderation workflow and permission setup.
+{: .note }
+The `/forcertp` command itself still requires the `ezrtp.admin.forcertp` permission.
+The bypass settings only control what restrictions are waived for the **target** player.
diff --git a/docs/config/gui.md b/docs/config/gui.md
index deb3fe3..8f54819 100644
--- a/docs/config/gui.md
+++ b/docs/config/gui.md
@@ -1,10 +1,140 @@
+---
+title: gui.yml
+nav_order: 4
+parent: Config Reference
+---
+
# gui.yml
-Inventory GUI for selecting RTP destinations.
+Configures the optional inventory GUI that lets players pick an RTP destination
+by clicking an item. Each "world" entry becomes a clickable icon in the chest
+inventory.
+
+---
+
+## Top-level settings
+
+| Key | Default | Description |
+|:----|:--------|:------------|
+| `enabled` | `true` | Set to `false` to disable the GUI entirely. Players use `/rtp` only as a plain command. |
+| `title` | `Random Teleport` | Title shown at the top of the inventory. Supports MiniMessage formatting. |
+| `rows` | `5` | Number of rows in the chest GUI (1–6). Each row has 9 slots, so 5 rows = 45 slots total (0–44). |
+| `no-permission-message` | `You do not have permission...` | Message shown when a player clicks a destination they don’t have permission for. |
+| `no-destinations` | *(see default)* | Message shown when no destinations are available to the player. |
+| `filler.enabled` | `true` | Fill empty slots with a decorative item. |
+| `filler.material` | `GLASS_PANE` | Material used for filler items. |
+| `filler.name` | ` ` | Display name for filler items (a space makes it invisible in most themes). |
+
+---
+
+## Slot numbering
+
+Slots are numbered left-to-right, top-to-bottom, starting at `0`:
+
+```text
+Row 1: 0 1 2 3 4 5 6 7 8
+Row 2: 9 10 11 12 13 14 15 16 17
+Row 3: 18 19 20 21 22 23 24 25 26
+Row 4: 27 28 29 30 31 32 33 34 35
+Row 5: 36 37 38 39 40 41 42 43 44
+```
+
+For a 5-row GUI the centre slot of row 3 is **slot 22** (the default Overworld position).
+
+---
+
+## Adding a destination entry
+
+Each entry under `worlds` becomes one clickable icon. The key name (e.g.
+`overworld`) is just an internal identifier — it is never shown to players.
+
+```yml
+worlds:
+ overworld:
+ slot: 22 # which slot to put this icon in
+ permission: "" # leave blank = everyone can click it
+ icon:
+ material: GRASS_BLOCK
+ name: "Overworld"
+ lore:
+ - "Explore the surface world."
+ - "Click to teleport."
+ settings:
+ world: world # must match the world folder name
+```
+
+### Entry fields
+
+| Field | Description |
+|:------|:------------|
+| `slot` | Inventory slot (0–53). Omit to place entries in order. |
+| `permission` | Permission node required to click. Leave blank for no restriction. |
+| `icon.material` | Any valid Bukkit/Minecraft item ID (e.g. `GRASS_BLOCK`, `NETHERRACK`). |
+| `icon.name` | Display name. Supports MiniMessage. Supports PlaceholderAPI if installed. |
+| `icon.lore` | List of lore lines. Supports MiniMessage and PlaceholderAPI. |
+| `settings.world` | World to teleport into. Use `auto` to teleport within the player’s current world. |
+| `settings.*` | Any `rtp.yml` setting can be overridden here (radius, biomes, cost, etc.). |
+
+---
+
+## Biome-restricted entry example
+
+To add a button that only sends players to forest biomes:
+
+```yml
+worlds:
+ forest:
+ slot: 11
+ permission: "ezrtp.gui.forest"
+ icon:
+ material: OAK_SAPLING
+ name: "Forest"
+ lore:
+ - "Land in a forest biome."
+ settings:
+ world: world
+ biomes:
+ include:
+ - FOREST
+ - BIRCH_FOREST
+ - DARK_FOREST
+ pre-cache:
+ enabled: true
+ max-per-biome: 30
+```
+
+---
+
+## “Current world” entry
+
+A special entry that teleports the player within whichever world they are already
+in. Useful on multi-world servers.
+
+```yml
+worlds:
+ current-world:
+ slot: 13
+ permission: ""
+ icon:
+ material: COMPASS
+ name: "Current World"
+ lore:
+ - "Teleport within your current world."
+ settings:
+ world: auto
+```
+
+---
+
+## Cache filtering
+
+When biome pre-caching is enabled, EzRTP can hide destinations that have no
+pre-cached locations ready, so players are never shown an option that would make
+them wait.
-- `enabled`, `title`, `rows`: top-level GUI toggles/layout.
-- `no-permission-message`, `cache-filter-info`, `no-destinations`: GUI messages.
-- `disable-cache-filtering`, `admin-only-cache-info`: cache visibility behavior.
-- `rare_biomes.*`: cache requirements for rare biome destinations.
-- `filler.*`: decorative slot filler item.
-- `worlds..*`: clickable entries (slot, permission, icon, RTP settings overrides).
+| Key | Default | Description |
+|:----|:--------|:------------|
+| `disable-cache-filtering` | `false` | Set to `true` to always show all destinations regardless of cache state. |
+| `admin-only-cache-info` | `false` | When `true`, the “only showing cached options” notice is only shown to players with admin permissions. |
+| `rare_biomes.enabled` | `true` | Apply stricter cache requirements to rare biomes listed in `rtp.yml`. |
+| `rare_biomes.require-cache.minimum-cached` | `1` | Minimum pre-cached locations needed before a rare-biome destination is shown. |
diff --git a/docs/config/index.md b/docs/config/index.md
new file mode 100644
index 0000000..75d8eec
--- /dev/null
+++ b/docs/config/index.md
@@ -0,0 +1,10 @@
+---
+title: Config Reference
+nav_order: 7
+has_children: true
+---
+
+# Config Reference
+
+EzRTP uses a set of focused configuration files. Browse the pages below for
+full details on each file.
diff --git a/docs/config/limits.md b/docs/config/limits.md
index 6498cbd..49da7fe 100644
--- a/docs/config/limits.md
+++ b/docs/config/limits.md
@@ -1,10 +1,99 @@
+---
+title: limits.yml
+nav_order: 3
+parent: Config Reference
+---
+
# limits.yml
-Cooldown, usage limits, and storage backend settings.
+Controls how often players can use `/rtp`, how many times per day/week, and what
+it costs. You can set different rules per world and per permission group (e.g. VIP
+gets a shorter cooldown than regular players).
+
+## Default limits
+
+The `rtp-limits.default` block applies to all players and worlds unless a more
+specific override exists.
+
+| Key | Default | Description |
+|:----|:--------|:------------|
+| `cooldown-seconds` | `300` | Seconds a player must wait between teleports. `0` = no cooldown. |
+| `daily-limit` | `10` | Maximum number of RTP uses per calendar day. `-1` = unlimited. |
+| `weekly-limit` | `50` | Maximum number of RTP uses per calendar week. `-1` = unlimited. |
+| `cost` | `0.0` | Economy cost per teleport (requires Vault). `0.0` = free. |
+
+## Per-world and per-group overrides
+
+Under `rtp-limits.worlds` you can override any of the four keys above for a
+specific world, and optionally narrow it further to a permission group.
+
+Group keys start with `group.` followed by the group name as reported by your
+permissions plugin (e.g. `group.vip`, `group.staff`).
+
+```yml
+rtp-limits:
+ default:
+ cooldown-seconds: 300
+ daily-limit: 10
+ weekly-limit: 50
+ cost: 0.0
+ worlds:
+ world: # overworld rules
+ default: # applies to everyone
+ cooldown-seconds: 300
+ daily-limit: 10
+ weekly-limit: 50
+ cost: 0.0
+ group.vip: # VIP players get less cooldown and more uses
+ cooldown-seconds: 60
+ daily-limit: 50
+ weekly-limit: 200
+ cost: 0.0
+ world_nether: # stricter limits in the Nether
+ default:
+ cooldown-seconds: 600
+ daily-limit: 5
+ weekly-limit: 20
+ cost: 0.0
+ group.staff: # staff have no restrictions
+ cooldown-seconds: 0
+ daily-limit: -1
+ weekly-limit: -1
+ cost: 0.0
+```
+
+## Bypass permissions
+
+Players with any of the listed permissions skip cooldowns and usage limits
+entirely, regardless of world or group configuration.
+
+```yml
+rtp-limits:
+ bypass-permissions:
+ - ezrtp.bypass.cooldown
+ - ezrtp.bypass.limit
+```
+
+## Other settings
+
+| Key | Default | Description |
+|:----|:--------|:------------|
+| `allow-gui-during-cooldown` | `true` | When `true`, players on cooldown can still open the destination GUI — they just cannot confirm a teleport until the cooldown expires. Set to `false` to hide the GUI entirely. |
+
+## Storage backend
+
+Cooldown and usage data can be stored in flat files or MySQL.
+
+```yml
+rtp-limits:
+ storage: yaml # options: yaml | mysql
+ mysql:
+ host: localhost
+ port: 3306
+ database: ezrtp
+ username: ""
+ password: ""
+```
-- `rtp-limits.storage`: `yaml` or `mysql`.
-- `rtp-limits.mysql.*`: JDBC connection values for MySQL storage.
-- `rtp-limits.default.*`: baseline cooldown/limits/cost.
-- `rtp-limits.worlds...*`: per-world and permission-group overrides.
-- `rtp-limits.bypass-permissions`: permissions that bypass cooldown/limits.
-- `rtp-limits.allow-gui-during-cooldown`: whether GUI open is allowed while on cooldown.
+Use `mysql` on networks where multiple servers share player data (e.g. a survival
+server and a resource world that both enforce the same weekly limit).
diff --git a/docs/config/network.md b/docs/config/network.md
index 1450dc1..5fa303a 100644
--- a/docs/config/network.md
+++ b/docs/config/network.md
@@ -1,8 +1,75 @@
+---
+title: network.yml
+nav_order: 6
+parent: Config Reference
+---
+
# network.yml
-Proxy/network destination integration for GUI server transfer.
+Adds cross-server entries to the RTP GUI. When a player clicks a network entry
+they are transferred to another server on your BungeeCord or Velocity network
+and their RTP runs there.
+
+This file is meant to be placed on a **lobby server** that acts as the selector.
+Leave it disabled on game servers unless they also show the selection GUI.
+
+---
+
+## Top-level settings
+
+| Key | Default | Description |
+|:----|:--------|:------------|
+| `enabled` | `false` | Set to `true` to enable network entries in the GUI. |
+| `lobby` | `false` | Set to `true` on the server that hosts the GUI (typically the lobby). |
+| `ping-interval-ticks` | `200` | How often EzRTP pings each configured server to check if it’s online, in ticks (200 = 10 seconds). |
+| `ping-timeout-millis` | `1500` | How long to wait for a ping response before marking the server as offline (milliseconds). |
+
+---
+
+## Adding a server entry
+
+Each entry under `servers` becomes a clickable icon in the GUI — identical to a
+normal world entry but it transfers the player instead of teleporting them.
+
+```yml
+servers:
+ skyblock:
+ bungee-server: "skyblock" # must match the server name in your proxy config
+ host: "127.0.0.1" # used for pinging (can be 127.0.0.1 on the same machine)
+ port: 25566
+ slot: 4
+ permission: "" # leave blank = everyone can click
+ display-name: "Skyblock"
+ hide-when-offline: false # hide the icon when the server is down
+ allow-when-offline: false # prevent clicking when the server is down
+ connect-message: "Connecting you to ..."
+ offline-message: " is currently unavailable."
+ icon:
+ material: ENDER_PEARL
+ name: ""
+ lore:
+ - "Status: "
+ - "Ping: ms"
+```
+
+### Server entry fields
+
+| Field | Description |
+|:------|:------------|
+| `bungee-server` | Server name as defined in your proxy’s `config.yml` (`servers` section). |
+| `host` / `port` | Address used to ping the server and check if it’s online. |
+| `slot` | GUI slot (0–53). |
+| `permission` | Permission required to use this entry. Leave blank for all players. |
+| `hide-when-offline` | Hide the icon entirely when the server is unreachable. |
+| `allow-when-offline` | Allow clicking even when the server is unreachable (the transfer will still fail at the proxy level). |
+| `connect-message` | Chat message sent before the transfer. `` is replaced with `display-name`. |
+| `offline-message` | Message shown when the player tries to connect to an offline server. |
+| `icon.lore` placeholders | `` → Online/Offline, `` → latency in ms, `` → display name. |
+
+---
-- `enabled`: enables network integration.
-- `lobby`: marks this server as the lobby selector host.
-- `ping-interval-ticks`, `ping-timeout-millis`: polling behavior.
-- `servers..*`: proxy target settings, display metadata, permissions, icon, offline behavior.
+{: .note }
+Network transfers use the plugin messaging channel (`BungeeCord` / `Velocity`).
+Make sure your proxy is configured to allow transfers from game servers and that
+`bungeecord: true` (or Velocity forwarding) is set in the game server’s
+`spigot.yml` / `paper-global.yml`.
diff --git a/docs/config/queue.md b/docs/config/queue.md
index 5cdcaa8..7cd9dcb 100644
--- a/docs/config/queue.md
+++ b/docs/config/queue.md
@@ -1,9 +1,38 @@
+---
+title: queue.yml
+nav_order: 5
+parent: Config Reference
+---
+
# queue.yml
-Teleport request queue tuning.
+When many players use `/rtp` at the same time, the queue prevents a sudden spike
+of teleport requests from overloading the server. Teleports are processed one at
+a time, with a configurable pause between each one.
+
+The queue is **disabled by default**. Enable it only if you regularly see lag when
+multiple players teleport simultaneously.
+
+## Settings
+
+| Key | Default | Description |
+|:----|:--------|:------------|
+| `enabled` | `false` | Set to `true` to turn on the queue. |
+| `max-size` | `0` | Maximum number of players allowed to wait in line. `0` = no cap. When the queue is full, additional requests are rejected until a slot opens. |
+| `bypass-permission` | `ezrtp.queue.bypass` | Players with this permission skip the queue and teleport immediately. Useful for VIP or staff. |
+| `start-delay-ticks` | `20` | How long to wait before processing the first queued request after a quiet period, in server ticks (20 ticks = 1 second). |
+| `interval-ticks` | `40` | Pause between each queued teleport (20 ticks = 1 second). Setting this to `40` means at most one teleport every 2 seconds. |
+
+## Example — busy survival server
+
+```yml
+enabled: true
+max-size: 20 # up to 20 players can wait
+bypass-permission: "ezrtp.queue.bypass"
+start-delay-ticks: 20 # begin processing after 1 second
+interval-ticks: 40 # one teleport every 2 seconds
+```
-- `enabled`: enables queue mode.
-- `max-size`: queue size cap (`0` = unlimited).
-- `bypass-permission`: queue bypass permission.
-- `start-delay-ticks`: delay before first queued teleport.
-- `interval-ticks`: delay between queued teleports.
+{: .note }
+**Tick reference:** 20 ticks = 1 second. If you want a 3-second gap between
+teleports, set `interval-ticks: 60`.
diff --git a/docs/config/rtp.md b/docs/config/rtp.md
index 25092c8..a1654f5 100644
--- a/docs/config/rtp.md
+++ b/docs/config/rtp.md
@@ -1,37 +1,270 @@
+---
+title: rtp.yml
+nav_order: 2
+parent: Config Reference
+---
+
# rtp.yml
-Default random teleport behavior and safety controls.
+The main configuration file for random teleport behaviour — where players land,
+how far they can travel, what's considered safe, and optional features like
+biome filtering and countdown timers.
+
+---
+
+## Destination bounds
+
+These settings define where a teleport destination can be.
+
+| Key | Default | Description |
+|:----|:--------|:------------|
+| `world` | `world` | The world players are teleported into. Use the exact world folder name (e.g. `world_nether`). Set to `auto` to always teleport within the player's current world — handy on multi-world servers. |
+| `center.x` / `center.z` | `0` / `0` | The X/Z coordinate used as the centre of the search circle. Defaults to 0,0 (world spawn area). |
+| `radius.min` | `500` | Closest a destination can be from the centre, in blocks. |
+| `radius.max` | `2000` | Furthest a destination can be from the centre, in blocks. Leave this out to use the world border as the maximum. |
+| `min-y` | `54` | Lowest Y level a destination is considered valid at. |
+| `max-y` | `320` | Highest Y level a destination is considered valid at. |
+
+```yml
+world: world
+center:
+ x: 0
+ z: 0
+radius:
+ min: 500
+ max: 2000
+min-y: 54
+max-y: 320
+```
+
+---
+
+## Search settings
+
+| Key | Default | Description |
+|:----|:--------|:------------|
+| `search-pattern` | `random` | The shape used when picking candidate coordinates. See [Search Patterns](search-patterns) for all options. |
+| `max-attempts` | `16` | How many candidate locations EzRTP tries before giving up and sending a failure message. Higher values reduce failed teleports but cost slightly more CPU per attempt. |
+
+---
+
+## Cost
+
+```yml
+cost: 0.0
+```
+
+Economy cost charged per teleport (requires Vault). `0.0` means free. Per-world
+and per-group cost overrides go in `limits.yml`.
+
+---
+
+## Countdown
+
+A countdown timer that shows before the teleport happens. Players who move or
+take damage during the countdown can have their teleport cancelled (configure
+that in `limits.yml`).
+
+| Key | Default | Description |
+|:----|:--------|:------------|
+| `countdown-seconds` | `5` | How many seconds to count down. `0` disables the countdown entirely. |
+| `countdown.bossbar.enabled` | `true` | Shows a bossbar with the countdown. |
+| `countdown.bossbar.title` | *(see below)* | MiniMessage text shown in the bossbar. `` is replaced with the remaining time. |
+| `countdown.particles.enabled` | `true` | Plays a particle effect around the player during countdown. |
+| `countdown.chat-messages` | `false` | Also prints countdown seconds into chat. |
+
+```yml
+countdown-seconds: 5
+countdown:
+ bossbar:
+ enabled: true
+ title: "Teleporting in seconds..."
+ color: YELLOW
+ style: SOLID
+ particles:
+ enabled: true
+ type: ENCHANTMENT_TABLE
+ points: 12
+ radius: 1.2
+ chat-messages: false
+```
+
+---
+
+## Safety
+
+EzRTP will not place players inside water, lava, or other hazardous blocks.
+The `unsafe-blocks` list controls what counts as unsafe. You can add or remove
+any Bukkit material name.
+
+```yml
+unsafe-blocks:
+ - WATER
+ - LAVA
+ - MAGMA_BLOCK
+ - POWDER_SNOW
+ - FIRE
+ - CAMPFIRE
+ - SOUL_FIRE
+```
+
+### Surface recovery
+
+When the chosen location is mid-air or underground, EzRTP scans up or down to
+find solid footing before confirming the destination.
+
+| Key | Default | Description |
+|:----|:--------|:------------|
+| `safety.recovery.enabled` | `true` | Enables vertical scanning to find a safe Y level. |
+| `safety.recovery.max-vertical-adjust` | `6` | Maximum blocks to move up or down during surface recovery. |
+| `safety.recovery.max-surface-scan-depth` | `20` | How far below the candidate to scan for a footing block in normal worlds. Keep this small for performance. |
+| `safety.recovery.max-surface-scan-depth-nether` | `128` | Same, but in the Nether. Larger because candidates often start near the Nether roof. |
+
+### Water landing
+
+```yml
+safety:
+ water:
+ place-block-on-surface: true
+ material: ICE
+```
+
+When `place-block-on-surface: true`, players who land on water get a temporary
+block placed under them so they don't drown. `material` sets what block is placed
+(default `ICE`).
+
+---
+
+## Chunk loading
+
+Controls how EzRTP loads chunks while searching for destinations. On Paper 1.21+
+the async API is used automatically for the best performance.
+
+| Key | Default | Description |
+|:----|:--------|:------------|
+| `chunk-loading.use-paper-async-api` | `auto-detect` | `auto-detect` uses Paper's async chunk API when available. `always` forces it. `never` falls back to the legacy throttle on all platforms. |
+| `legacy-throttle.enabled` | `true` | Enables the tick-based throttle used on Spigot/Bukkit or older Paper. |
+| `legacy-throttle.interval-ticks` | `10` | Ticks between chunk load batches. |
+| `legacy-throttle.max-chunks-per-tick` | `1` | Maximum chunks loaded per batch. |
-Main groups:
+---
-- `debug-rejection-logging`: logs candidate rejection reasons.
-- `rtp.*`: global RTP flags.
-- `min-y`, `max-y`, `world`, `center`, `radius`: destination bounds.
-- `centers.named.*`: named RTP center presets for `/rtp `.
-- `search-pattern`, `max-attempts`: search strategy/performance.
-- `cost`: base teleport cost.
-- `countdown*`: pre-teleport delay, bossbar, and particles.
-- `unsafe-blocks`, `safety.*`: landing safety rules.
-- `chunk-loading.*`: chunk-load queue throttling.
-- `chunky-integration.*`: optional Chunky pregeneration integration.
-- `biomes.*`: include/exclude filters, pre-cache, rare biome optimization, search failover limits.
-- `protection.*`: claim/region avoidance providers.
-- `worldguard.region-command.*`: RTP-around-region defaults.
-- `particles.*`: teleport-success particle effect.
-- `on-join.*`: automatic join teleport settings.
+## Biomes
-## Safety recovery surface scan depth
+Biome filtering lets you restrict (or require) specific biomes for RTP destinations.
+The full feature set includes pre-caching and rare-biome optimisation.
-`safety.recovery` includes environment-aware vertical scan controls:
+| Key | Default | Description |
+|:----|:--------|:------------|
+| `biomes.enabled` | `true` | Master switch. Set to `false` to disable all biome features. |
+| `biomes.include` | `[]` | If non-empty, only destinations in one of these biomes are accepted. Use Bukkit biome names, e.g. `FOREST`, `PLAINS`. |
+| `biomes.exclude` | `[]` | Destinations in any listed biome are rejected. |
-- `max-surface-scan-depth` (default `20`): overwrite/default-world scan depth used for normal worlds.
-- `max-surface-scan-depth-nether` (default `128`): Nether scan depth used when candidate resolution starts near the Nether roof.
+### Pre-cache
-This keeps overworld scans conservative for performance while allowing deeper Nether recovery to find valid footing below roof-level terrain.
+Pre-caching runs searches in the background and stores valid locations so players
+get instant teleports instead of waiting for a search.
+
+```yml
+biomes:
+ pre-cache:
+ enabled: false
+ auto-enable-for-filters: true # automatically enable when include/exclude lists are set
+ max-per-biome: 16
+ warmup-size: 8
+ expiration-minutes: 15
+ refill-interval-minutes: 10
+ refill-batch-size: 3
+```
+
+### Rare biome optimisation
+
+For biomes that are hard to find (e.g. Mushroom Fields, Deep Dark), this tracks
+discovered hotspot coordinates to speed up future searches.
+
+```yml
+biomes:
+ rare-biome-optimization:
+ enabled: false
+ rare-biomes:
+ - MUSHROOM_FIELDS
+ - DEEP_DARK
+ use-weighted-search: true
+ enable-hotspot-tracking: true
+ max-hotspots-per-biome: 20
+```
+
+### Search budget (performance caps)
+
+These caps prevent a single biome search from blocking the server too long.
+
+| Key | Default | Description |
+|:----|:--------|:------------|
+| `biome-filtering.performance-budget.max-total-time-ms` | `0` | Maximum wall-clock search time for normal searches (ms). `0` = auto (100 ms when filters active). |
+| `max-total-time-ms-rare` | `8000` | Maximum search time for rare biomes. |
+| `max-biome-rejections` | `0` | Maximum number of biome mismatches before giving up. `0` = auto. |
+| `fallback-on-timeout` | `cached-location` | What to do when the budget is exhausted: `cached-location`, `generic`, or `abort`. |
+
+---
+
+## Protection (claim avoidance)
+
+Prevents EzRTP from landing players inside protected claims or regions.
+
+```yml
+protection:
+ avoid-claims: false
+ providers:
+ - worldguard
+ - griefprevention
+```
+
+Set `avoid-claims: true` to enable. Only providers that are installed will be used.
+
+---
+
+## Particles (arrival effect)
+
+An optional particle burst played at the destination when the player arrives.
+
+```yml
+particles:
+ enabled: false
+ type: PORTAL
+ count: 40
+ offset:
+ x: 0.5
+ y: 1.0
+ z: 0.5
+```
+
+---
+
+## On-join teleport
+
+Automatically teleport new (or returning) players when they join the server.
+
+| Key | Default | Description |
+|:----|:--------|:------------|
+| `on-join.enabled` | `false` | Enables automatic RTP on join. |
+| `on-join.only-first-join` | `false` | When `true`, only teleports players who have never joined before. |
+| `on-join.bypass-permission` | `""` | Players with this permission are **not** teleported on join (e.g. for staff). |
+| `on-join.delay-ticks` | `40` | Ticks to wait after joining before teleporting (40 = 2 seconds). |
+
+```yml
+on-join:
+ enabled: false
+ only-first-join: false
+ bypass-permission: ""
+ delay-ticks: 40
+```
+
+---
## Named centers
-`rtp.yml` supports reusable named centers under:
+Named centers let you create multiple RTP zones that players can access with
+`/rtp `. Each center can override the world, coordinates, and radius.
```yml
centers:
@@ -41,8 +274,25 @@ centers:
center:
x: 0
z: 0
+ north:
+ world: world
+ center:
+ x: 0
+ z: -5000
+```
+
+- Add or update a center in-game with `/rtp addcenter `.
+- Players use `/rtp ` to teleport using that center's settings.
+
+---
+
+## Debug logging
+
+```yml
+debug-rejection-logging: false
```
-- Use `/rtp addcenter ` in-game to write/update entries.
-- Use `/rtp ` to RTP with that center override.
-- If `worldguard.region-command.enabled: true`, `/rtp ` remains supported.
+Set to `true` to log every rejected candidate location with the reason why it was
+rejected (wrong biome, unsafe block, inside a claim, etc.). Useful when tuning
+biome filters or safety settings. Disable on production servers as it generates
+a lot of output.
diff --git a/docs/config/search-patterns.md b/docs/config/search-patterns.md
new file mode 100644
index 0000000..1e22db5
--- /dev/null
+++ b/docs/config/search-patterns.md
@@ -0,0 +1,128 @@
+---
+title: Search Patterns
+nav_order: 8
+parent: Config Reference
+---
+
+# Search Patterns
+
+The `search-pattern` key in `rtp.yml` controls how EzRTP generates candidate coordinates
+on each teleport attempt. Different patterns produce different spatial distributions,
+which affects both fairness and performance depending on your world shape and biome layout.
+
+```yml
+# rtp.yml
+search-pattern: random
+```
+
+Valid values: `random`, `circle`, `square`, `diamond`, `triangle`
+
+---
+
+## random (default)
+
+**Config key:** `random`
+
+Picks a random integer radius within `[min-radius, max-radius]` and a random angle, then
+places the candidate on the perimeter of that circle. Because the radius is sampled as a
+whole-block integer, candidates cluster slightly more at smaller radii but the distribution
+is otherwise uniform.
+
+**Best for:** general use; matches the classic RTP feel on flat or varied terrain.
+
+```yml
+search-pattern: random
+```
+
+---
+
+## circle
+
+**Config key:** `circle`
+
+Identical in shape to `random` but uses a **floating-point** radius sampled continuously
+from `[min-radius, max-radius]`. This eliminates the slight integer-radius clustering and
+produces a more even ring distribution.
+
+**Best for:** large radius ranges where you want an even spread across the full annulus.
+
+```yml
+search-pattern: circle
+```
+
+---
+
+## square
+
+**Config key:** `square`
+
+Candidates are placed uniformly on the **perimeter of an axis-aligned square** whose
+half-side equals the sampled radius. Each of the four edges has equal probability.
+
+The result is a square band of candidate points tilted to align with the world axes — the
+four cardinal directions (N/S/E/W) see no concentration bias.
+
+**Best for:** square or grid-shaped worlds, or servers with rectangular claimed areas to
+work around.
+
+```yml
+search-pattern: square
+```
+
+---
+
+## diamond
+
+**Config key:** `diamond`
+
+Places candidates on the perimeter of an **8-vertex chamfered diamond** (an octagonal
+lozenge) aligned so the four primary vertices point north, east, south, and west. The shape
+approximates the classic Minecraft "diamond" silhouette that appears on maps, with small
+beveled corners.
+
+Each edge of the octagon has equal probability, giving a uniform perimeter distribution
+with a 45-degree rotated footprint compared to `square`.
+
+**Best for:** when a rotated square feel is preferred, or when you want to bias exploration
+toward the intercardinal (NE/SE/SW/NW) diagonals.
+
+```yml
+search-pattern: diamond
+```
+
+---
+
+## triangle
+
+**Config key:** `triangle`
+
+Generates a candidate on the **perimeter of an equilateral triangle** centered on the RTP
+center. One vertex of the triangle is always placed due north; the other two are rotated
+120° and 240° from it. A random edge is selected, then a uniformly random interpolation
+along that edge determines the final point.
+
+The triangle pattern concentrates travel in three preferred directions and leaves the
+space between the vertices relatively undersampled — this can be useful to force players
+into distinct "wings" of the world.
+
+**Best for:** experimental use or unusual world shapes where three-directional bias is
+acceptable.
+
+```yml
+search-pattern: triangle
+```
+
+---
+
+## Comparison summary
+
+| Pattern | Shape | Coverage type | Radius sampling |
+|:--------|:------|:-------------|:----------------|
+| `random` | Circle perimeter | Perimeter only | Integer |
+| `circle` | Circle perimeter | Perimeter only | Floating-point |
+| `square` | Square perimeter | Perimeter only | Integer |
+| `diamond` | Octagonal lozenge perimeter | Perimeter only | Integer |
+| `triangle` | Equilateral triangle perimeter | Perimeter only | Integer |
+
+All patterns respect `min-radius` and `max-radius`. Candidates are generated fresh on each
+attempt up to `max-attempts` before the teleport fails.
diff --git a/docs/development-collaboration.md b/docs/development-collaboration.md
index 94092de..34c6efb 100644
--- a/docs/development-collaboration.md
+++ b/docs/development-collaboration.md
@@ -1,4 +1,9 @@
-# Development & Collaboration Guide
+---
+title: Developer Guide
+nav_order: 11
+---
+
+# Developer Guide
This guide is for contributors working on EzRTP source code, reviews, and releases.
diff --git a/docs/index.md b/docs/index.md
new file mode 100644
index 0000000..21f1e53
--- /dev/null
+++ b/docs/index.md
@@ -0,0 +1,72 @@
+---
+title: Home
+layout: home
+nav_order: 1
+---
+
+# EzRTP
+
+EzRTP is a production-focused random teleport plugin for Minecraft servers running
+Bukkit, Spigot, Paper, or Purpur. It provides safety-first teleportation,
+cross-platform compatibility, and configuration-driven control for server owners.
+
+## Highlights
+
+- **Safety-first teleportation** — configurable unsafe-block lists, surface recovery, and claim avoidance.
+- **Platform modules** — Bukkit base + optional Paper or Purpur adapters for native async APIs.
+- **Economy integration** — Vault-compatible cost system with per-world and per-group pricing.
+- **Queue throttling** — request queues prevent load spikes on busy servers.
+- **Biome filtering** — include/exclude filters, pre-cache, and rare-biome optimization.
+- **GUI destination selector** — inventory GUI with per-world icons, permissions, and network entries.
+- **Network/proxy support** — cross-server destinations in GUI for multi-server networks.
+
+---
+
+## For Server Owners & Admins
+
+| | |
+|:---|:---|
+| [Getting Started](overview-installation) | Install EzRTP, choose a platform module, verify your setup |
+| [Commands](commands) | Player and admin command reference |
+| [Permissions](permissions) | Permission nodes and recommended role assignments |
+| [Messages & Localization](messages-localization) | Customize messages, add languages |
+| [Operations & Troubleshooting](operations-troubleshooting) | Tuning profiles, common issues |
+
+## Configuration Reference
+
+| | |
+|:---|:---|
+| [config.yml](config/config) | Global plugin settings and language |
+| [rtp.yml](config/rtp) | RTP bounds, safety, biomes, chunky, and countdown |
+| [limits.yml](config/limits) | Cooldowns, usage limits, cost overrides, and storage backend |
+| [gui.yml](config/gui) | Inventory GUI layout and destination entries |
+| [queue.yml](config/queue) | Teleport queue throttling |
+| [network.yml](config/network) | Cross-server proxy destination settings |
+| [force-rtp.yml](config/force-rtp) | Admin `/forcertp` command defaults |
+| [Search Patterns](config/search-patterns) | All `search-pattern` values explained |
+
+## Platform Modules
+
+| | |
+|:---|:---|
+| [Bukkit / CraftBukkit](modules/installation-bukkit) | Base plugin only |
+| [Spigot](modules/installation-spigot) | Base plugin only |
+| [Paper](modules/installation-paper) | Base + `ezrtp-paper` module |
+| [Purpur](modules/installation-purpur) | Base + `ezrtp-purpur` module |
+
+## Integrations
+
+| | |
+|:---|:---|
+| [Vault / Economy](integrations/vault-economy) | Charge players for RTP |
+| [PlaceholderAPI](integrations/placeholderapi) | Dynamic placeholders in GUI |
+| [Chunky](integrations/chunky) | World pre-generation |
+| [WorldGuard & GriefPrevention](integrations/protection-worldguard-griefprevention) | Claim & region avoidance |
+| [Network & Proxy](integrations/network-proxy) | Cross-server GUI entries |
+
+## For Developers
+
+| | |
+|:---|:---|
+| [Developer API](api/plugin-api) | Trigger RTP programmatically from other plugins |
+| [Developer Guide](development-collaboration) | Architecture, build workflow, contributor conventions |
diff --git a/docs/integrations/chunky.md b/docs/integrations/chunky.md
index 53d518e..9db88a2 100644
--- a/docs/integrations/chunky.md
+++ b/docs/integrations/chunky.md
@@ -1,3 +1,9 @@
+---
+title: Chunky
+nav_order: 3
+parent: Integrations
+---
+
# Chunky Integration (World Pre-Generation)
Use this integration when you want faster/more reliable RTP in large unexplored worlds by pre-generating terrain.
diff --git a/docs/integrations/index.md b/docs/integrations/index.md
new file mode 100644
index 0000000..8482760
--- /dev/null
+++ b/docs/integrations/index.md
@@ -0,0 +1,10 @@
+---
+title: Integrations
+nav_order: 8
+has_children: true
+---
+
+# Integrations
+
+EzRTP integrates with several popular plugins. Browse the pages below to
+set up each one.
diff --git a/docs/integrations/network-proxy.md b/docs/integrations/network-proxy.md
index 85fa77f..e1787b9 100644
--- a/docs/integrations/network-proxy.md
+++ b/docs/integrations/network-proxy.md
@@ -1,3 +1,9 @@
+---
+title: Network & Proxy
+nav_order: 5
+parent: Integrations
+---
+
# Network / Proxy Integration (BungeeCord/Velocity-style server selector)
Use this integration when your `/rtp` GUI should also present cross-server destinations.
diff --git a/docs/integrations/placeholderapi.md b/docs/integrations/placeholderapi.md
index 546687f..ff65ff5 100644
--- a/docs/integrations/placeholderapi.md
+++ b/docs/integrations/placeholderapi.md
@@ -1,3 +1,9 @@
+---
+title: PlaceholderAPI
+nav_order: 2
+parent: Integrations
+---
+
# PlaceholderAPI Integration
Use this integration when you want dynamic values (player name, rank, stats, etc.) in GUI icon text.
@@ -7,6 +13,7 @@ Use this integration when you want dynamic values (player name, rank, stats, etc
EzRTP resolves PlaceholderAPI placeholders in GUI icon names and lore when PlaceholderAPI is installed.
Typical examples:
+
- `%player_name%`
- `%vault_eco_balance_formatted%`
- placeholders from your rank/stats plugins
diff --git a/docs/integrations/protection-worldguard-griefprevention.md b/docs/integrations/protection-worldguard-griefprevention.md
index 3e2da05..99037b0 100644
--- a/docs/integrations/protection-worldguard-griefprevention.md
+++ b/docs/integrations/protection-worldguard-griefprevention.md
@@ -1,3 +1,9 @@
+---
+title: WorldGuard & GriefPrevention
+nav_order: 4
+parent: Integrations
+---
+
# WorldGuard / GriefPrevention Protection Integration
Use this integration when you want EzRTP to avoid protected regions/claims.
@@ -7,6 +13,7 @@ Use this integration when you want EzRTP to avoid protected regions/claims.
When enabled, EzRTP validates candidate RTP locations against configured protection providers and rejects protected points.
Supported provider names in config:
+
- `worldguard`
- `griefprevention`
diff --git a/docs/integrations/vault-economy.md b/docs/integrations/vault-economy.md
index 16812fa..2d5f142 100644
--- a/docs/integrations/vault-economy.md
+++ b/docs/integrations/vault-economy.md
@@ -1,3 +1,9 @@
+---
+title: Vault / Economy
+nav_order: 1
+parent: Integrations
+---
+
# Vault / Economy Integration
Use this integration when you want RTP to cost money (for economy sinks, progression pacing, or VIP balancing).
diff --git a/docs/messages-localization.md b/docs/messages-localization.md
index c1ce35e..a1caefc 100644
--- a/docs/messages-localization.md
+++ b/docs/messages-localization.md
@@ -1,4 +1,9 @@
-# EzRTP Messages & Localization
+---
+title: Messages & Localization
+nav_order: 5
+---
+
+# Messages & Localization
EzRTP messages are controlled by `messages/.yml`.
diff --git a/docs/modules/index.md b/docs/modules/index.md
new file mode 100644
index 0000000..e53a332
--- /dev/null
+++ b/docs/modules/index.md
@@ -0,0 +1,17 @@
+---
+title: Platform Modules
+nav_order: 9
+has_children: true
+---
+
+# Platform Modules
+
+EzRTP ships a base plugin jar plus optional platform modules. Install the
+module that matches your server software.
+
+| Server | Required jars |
+|:-------|:--------------|
+| Bukkit / CraftBukkit | `EzRTP-.jar` |
+| Spigot | `EzRTP-.jar` |
+| Paper | `EzRTP-.jar` + `ezrtp-paper-.jar` |
+| Purpur | `EzRTP-.jar` + `ezrtp-purpur-.jar` |
diff --git a/docs/modules/installation-bukkit.md b/docs/modules/installation-bukkit.md
index 57df993..ad9461d 100644
--- a/docs/modules/installation-bukkit.md
+++ b/docs/modules/installation-bukkit.md
@@ -1,3 +1,9 @@
+---
+title: Bukkit
+nav_order: 1
+parent: Platform Modules
+---
+
# Bukkit / CraftBukkit Module Installation
## Required jar(s)
diff --git a/docs/modules/installation-paper.md b/docs/modules/installation-paper.md
index ba87fbe..27e41e8 100644
--- a/docs/modules/installation-paper.md
+++ b/docs/modules/installation-paper.md
@@ -1,3 +1,9 @@
+---
+title: Paper
+nav_order: 3
+parent: Platform Modules
+---
+
# Paper Module Installation
## Required jar(s)
diff --git a/docs/modules/installation-purpur.md b/docs/modules/installation-purpur.md
index 55999ef..7e2bf87 100644
--- a/docs/modules/installation-purpur.md
+++ b/docs/modules/installation-purpur.md
@@ -1,3 +1,9 @@
+---
+title: Purpur
+nav_order: 4
+parent: Platform Modules
+---
+
# Purpur Module Installation
## Required jar(s)
diff --git a/docs/modules/installation-spigot.md b/docs/modules/installation-spigot.md
index 7eb3139..2fe49ac 100644
--- a/docs/modules/installation-spigot.md
+++ b/docs/modules/installation-spigot.md
@@ -1,3 +1,9 @@
+---
+title: Spigot
+nav_order: 2
+parent: Platform Modules
+---
+
# Spigot Module Installation
## Required jar(s)
diff --git a/docs/operations-troubleshooting.md b/docs/operations-troubleshooting.md
index ea4e6ed..c1b5635 100644
--- a/docs/operations-troubleshooting.md
+++ b/docs/operations-troubleshooting.md
@@ -1,4 +1,9 @@
-# EzRTP Operations, Tuning, and Troubleshooting
+---
+title: Operations & Troubleshooting
+nav_order: 6
+---
+
+# Operations, Tuning, and Troubleshooting
## Runtime behavior notes
diff --git a/docs/overview-installation.md b/docs/overview-installation.md
index a0ab3f7..339f2a7 100644
--- a/docs/overview-installation.md
+++ b/docs/overview-installation.md
@@ -1,4 +1,9 @@
-# EzRTP Overview & Installation
+---
+title: Getting Started
+nav_order: 2
+---
+
+# Getting Started
## What EzRTP does
diff --git a/docs/permissions.md b/docs/permissions.md
new file mode 100644
index 0000000..b56adaf
--- /dev/null
+++ b/docs/permissions.md
@@ -0,0 +1,24 @@
+---
+title: Permissions
+nav_order: 4
+---
+
+# Permissions
+
+## Declared permissions
+
+| Permission | Default | Description |
+|:-----------|:--------|:------------|
+| `ezrtp.use` | `true` | Use `/rtp` |
+| `ezrtp.reload` | `op` | Reload configuration with `/rtp reload` |
+| `ezrtp.stats` | `op` | View RTP statistics with `/rtp stats` |
+| `ezrtp.heatmap` | `op` | View/save heatmap with `/rtp heatmap` |
+| `ezrtp.heatmap.fake` | `op` | Add fake heatmap points with `/rtp fake` |
+| `ezrtp.queue.bypass` | `op` | Skip the teleport queue |
+| `ezrtp.forcertp` | `op` | Force-teleport players with `/forcertp` |
+| `ezrtp.setcenter` | `op` | Set or save a named RTP center |
+
+## Additional permission usage
+
+- `queue.yml` controls the queue bypass permission path.
+- `gui.yml` and `network.yml` entries can require per-option permissions.
diff --git a/docs/topics/bbcode.txt b/topics/bbcode.txt
similarity index 100%
rename from docs/topics/bbcode.txt
rename to topics/bbcode.txt
diff --git a/docs/topics/markdown.md b/topics/markdown.md
similarity index 100%
rename from docs/topics/markdown.md
rename to topics/markdown.md