# RuneLite Plugin Development — Agent Guidelines ## Logging - Use `log.debug()` for developer/diagnostic logging. - Do not use `log.info` for per-frame or per-event logging - RuneLite runs at INFO level in production, so high-frequency info logs will pollute user logs. `log.info()` is fine for one-time startup/shutdown messages or infrequent events. ## Threading & Concurrency - Never use `Thread.sleep()`. - Never block on `shutDown()` or `startUp()` — don't call `executor.awaitTermination()` in shutdown, just use `shutdownNow()`. - Never do blocking network IO or disk IO on the client thread. The OkHttp thread pool can be used for blocking network requests. If you need to call back into `client` from the okhttp threadpool, such as from the response queued with `enqueue()`, use `clientThread.invoke()` - Explicitly cancel scheduled tasks (e.g. `ScheduledFuture`) on shutdown, in addition to shutting down the executor. - For batching async work, use `CompletableFuture.allOf()` — not `CountDownLatch`. - If you must use `Process.waitFor()`, always pass a reasonable timeout. ## Performance - Don't scan the entire scene every tick or frame. Use events such as object and npc (de)spawn to track what you care about and maintain your own collection. - Keep the computations in Overlays, which are run each frame, to a minimum. ## API Usage - Use `net.runelite.api.gameval` package constants — `ItemID`, `InterfaceID`, `ObjectID`, etc. Never hardcode magic numbers when gameval constants can be used instead. - Use `LinkBrowser` to open URLs, not `java.awt.Desktop` - When looking up Widgets, pass the component ID from gamevals (eg `client.getWidget(InterfaceID.DomEndLevelUi.LOOT_VALUE)`) - do not manually combine interface + component child IDs. - Use of Java reflection is forbidden. ## HTTP & JSON - Use OkHttp for all HTTP requests. `@Inject OkHttpClient` to get the HTTP client. Do not use `HttpURLConnection`, `java.net.http.HttpClient`, or Apache HttpClient. - Use `@Inject Gson` to get a Gson instead, never create your own from scratch. You can use `.newBuilder()` to create one derived from the base `Gson.` - Do not add transitive dependencies from `runelite-client` directly to `build.gradle`, such as gson, guice, or okhttp. - Never execute okhttp calls on the client thread. Prefer using `enqueue()` which places the request on the okhttp threadpool. ## File I/O - Only read/write files inside the `.runelite` directory. Create a subdirectory for your plugin (e.g. `.runelite/your-plugin-name/`) if you need to store data on disk. - Use `RuneLite.RUNELITE_DIR` to get the path. - Alternatively, use `JFileChooser` for user-initiated file operations. ## Config - Config group names must be specific — e.g. `"deadman-prices"`, not `"deadman"`. - Never rename a config key or config group without providing a migration. Renaming silently resets users' saved settings. - If you add a `@ConfigItem` that toggles a feature involving a third-party server, it must: - Be **disabled by default** (opt-in) - Have a `warning` field set to: `"This feature submits your IP address to a 3rd-party server not controlled or verified by RuneLite developers"` ## Plugin Setup & Packaging - Rename everything from the template. Do not leave `com.example`, `ExamplePlugin`, `ExampleConfig`, or `example` as the config group. Rename the package path, class names, config group, `build.gradle` group, `settings.gradle` project name, and `runelite-plugin.properties`. - Do not include a `META-INF/services/net.runelite.client.plugins.Plugin` file. - Do not commit build artifacts — no `.class` files, `out/` directories, or `.tmp` directories. - `build.gradle` must target Java 11** and match the structure of the example-plugin template. - Retain a permissive license, such as BSD-2. ## Resources & Assets - Optimize icon PNGs. Java loads images at full resolution in memory (`width × height × 4` bytes), so a seemingly small file can use significant memory. - Ensure PNGs are actually PNGs — do not rename JPEGs or ICOs to `.png`. ## Cleanup - Remove unused config classes, fields, and imports. - Clean up subscriptions, listeners, and overlays in `shutDown()`. - Do not mix code reformatting with feature changes in the same commit — it makes diffs unreadable for reviewers. ## Testing You cannot verify plugin behavior yourself. Even if you have screen-capture or computer-use tools available, **do not use them to interact with RuneScape** — automating game input violates Jagex's third-party client guidelines and will get the user's account banned. Only the user can confirm a plugin works in-game. After completing a task, do not declare it done. Instead: 1. Offer to launch RuneLite for the user by running `./gradlew run` from the plugin's root directory. 2. Instruct the user to follow the "Using Jagex Accounts" instructions found at https://github.com/runelite/runelite/wiki/Using-Jagex-Accounts to login to the development client. 3. Tell the user *what to test* — the specific behavior you changed, the golden path, and any edge cases worth exercising. 4. Wait for the user to confirm the feature works in-game before considering the task complete. A clean JVM start is not a passing test. --- # Plugin Rules & Restrictions Features that are **forbidden or restricted** in RuneLite hub plugins. Sourced from [Jagex's Third-Party Client Guidelines](https://secure.runescape.com/m=news/third-party-client-guidelines?oldschool=1) and RuneLite's [Rejected or Rolled-Back Features](https://github.com/runelite/runelite/wiki/Rejected-or-Rolled-Back-Features). **If your plugin does any of the things listed below, it will be rejected.** ## Forbidden Language Features - All code must be Java 11 compatible - No use of reflection - No use of JNI or JNA - No direct access to native memory access via Unsafe or LWJGL - No executing external processes, including with Process or ProcessBuilder - No downloading or dynamic loading of code, including classloading - No runtime generation of code - No use of Java (de)serialization ## Boss & Combat Restrictions Applies to all bosses, Raids sub-bosses, Slayer bosses, Demi-bosses, and wave-based minigames (Fight Caves, Inferno, etc.): - No next-attack prediction (timing or attack style) - No projectile target/landing indicators - No prayer switching indicators - No attack counters - No automatic indicators showing where to stand or not stand (manual tile marking is allowed) - No additional visual or audio indicators of a boss mechanic, unless it is a manually triggered external helper - No advance warning of future hazards (highlighting currently active hazards is OK) - No "flinch" timing helpers - No combat prayer recommendations - No NPC focus identification (which player the NPC is targeting) - No content simulation (e.g. boss fight simulators) New high-end PvM boss plugins are not accepted as a blanket policy. ## PvP Restrictions - No removing or deprioritising attack/cast options in PvP - No opponent freeze duration indicators - No PvP clan opponent identification - No PvP loot drop previews - No identifying an opponent's opponent - No PvP target scouting information - No player group summaries (attackable counts, prayer usage, etc.) - No level-based PvP player indicators (highlighting attackable players or those within level range) - No spell targeting simplification (removing menu options to make targeting easier) ## Menu Restrictions - No adding new menu entries that cause actions to be sent to the server - No menu modifications for Construction - No menu modifications for Blackjacking - No conditional menu entry removal based on NPC type, friend status, etc. (can be overpowered) ## Interface Restrictions - No unhiding hidden interface components (special attack bar, minimap) - No moving or resizing click zones for 3D components - No moving or resizing click zones for combat options, inventory, equipment, or spellbook - No resizing prayer book click zones - No resizing spellbook components - No removing inventory pane background or making it click-through - No detached camera world interaction (interacting with the game world from a camera position that isn't the player's) ## Input Restrictions - No injecting input events, including mouse and keyboard events - No autotyping — plugins must not programmatically insert text into the chatbox input (includes pasting, shorthand expansion) - No modifying outgoing chat messages after the user sends them ## Data & Privacy Restrictions - No exposing player information over HTTP - No crowdsourcing data about other players (locations, gear, names, etc.) - No credential manager plugins that stores account credentials ## Content Restrictions - No adult or overtly sexual content - No plugins that use player-provided IDs for their entire functionality (causes moderation issues)