Complete plugin development guide
Build a KLite marketplace plugin.
Use the Copper & Tin Miner as a complete reference implementation for dependency injection, cooperative automation, Shortest Path walking, object interaction, banking, configuration, overlays, diagnostics, packaging, and marketplace publication.
Foundation
Project structure
Keep the plugin entrypoint, configuration, automation state machine, and overlay in separate classes. This makes shutdown behavior, testing, and marketplace review easier to reason about.
examples/klite-copper-tin-miner/
├── build.gradle.kts
└── src/main/java/net/runelite/client/plugins/klite/examples/miner/
├── CopperTinMinerPlugin.java
├── CopperTinMinerConfig.java
├── CopperTinMinerTask.java
└── CopperTinMinerOverlay.java
Lifecycle owner
Registers dependencies, the automation task, and the plugin-owned overlay.
State machine
Coordinates mining, walking, banking, cancellation, and bounded API waits.
User settings
Exposes supported options through RuneLite's normal configuration system.
Runtime visibility
Shows current mode, destination, route status, runtime, and failures.
Lifecycle
Plugin entrypoint and dependency
The entrypoint connects the external plugin to KLite Core and owns every resource that must be released during shutdown.
@PluginDependency(KLitePlugin.class)
@PluginDescriptor(
name = "Copper and Tin Miner",
description = "Mines copper and tin near Varrock and banks only when full",
version = "0.0.2",
enabledByDefault = false,
isExternal = true
)
public class CopperTinMinerPlugin extends Plugin
{
// Inject KLite services and own startup/shutdown here.
}
Startup sequence
- 1
Create the automation task.
- 2
Create and register the plugin overlay.
- 3
Enable the shared automation manager.
- 4
Start the task after checking for conflicts.
Shutdown sequence
- 1
Stop and cancel the automation task.
- 2
Clear the active web-walker route.
- 3
Remove the overlay from the manager.
- 4
Release references so the class loader can unload.
Settings
Configuration
Provide configuration through ConfigManager. The example exposes a Show overlay option enabled by default, while operational constants remain in the task implementation.
@Provides
CopperTinMinerConfig provideConfig(ConfigManager configManager)
{
return configManager.getConfig(CopperTinMinerConfig.class);
}
Execution model
Cooperative automation task
The task implements AutomationTask and advances one bounded unit of work per tick. RuneLite operations return CompletableFuture values; wait through AutomationContext.await with an explicit timeout.
| State | Responsibility | Exit condition |
|---|---|---|
MINING | Move to the mine, select the preferred ore, and track the active rock. | Inventory becomes full. |
BANKING | Move to the bank and deposit every non-pickaxe stack. | Only the pickaxe remains. |
WALKING | Delegate route planning and progress to the shared web walker. | Arrival radius is reached. |
STOPPING | Treat cancellation as normal shutdown rather than a task failure. | Resources are released. |
Interaction control
Mining interaction lifecycle
A successful Mine dispatch does not immediately permit another click. Track the rock ID, world location, dispatch time, and whether the mining animation has been observed.
Completion conditions
- The tracked rock disappears or changes object ID at the same tile.
- The mining animation was observed and then remains stopped for a confirmation interval.
- The animation never starts before the startup timeout, allowing one controlled retry.
activeRockId = selected.getObjectId();
activeRockLocation = selected.getLocation();
miningAnimationObserved = false;
// Every task tick:
// 1. Check whether the object ID at the tile changed.
// 2. Check whether the player is still animating.
// 3. Confirm that animation has stopped before selecting another rock.
Inventory control
Full-inventory banking
Begin banking only when all 28 inventory slots are occupied and at least one item is not a pickaxe. This prevents empty, partial, and pickaxe-only inventories from opening the bank.
boolean inventoryFull = inventory.size() >= 28;
Optional<InventoryItem> depositable = inventory.stream()
.filter(item -> !isPickaxe(item))
.findFirst();
if (!banking && inventoryFull && depositable.isPresent())
{
banking = true;
}
Navigation
Integrated web walking
The plugin owns destinations and arrival radiuses. KLite Core owns route planning, collision data, waypoint selection, scene and minimap rendering, progress tracking, and stall recovery.
WebWalkResult result = context.await(
webWalker.step(destination, arrivalDistance),
API_TIMEOUT
);
if (result.getState() == WebWalkState.NO_PATH)
{
throw new IllegalStateException(result.getMessage());
}
Observability
Plugin-owned overlay
Keep plugin-specific status separate from the global KLite status overlay. The user should be able to identify what the task is doing without opening logs.
Task status
Activity, current mode, runtime, and failure state.
Target state
Preferred ore, active rock, and mining destination.
Route state
Walker state, route length, next waypoint, and route message.
Transition reason
The explicit full-inventory trigger and deposit progress.
Troubleshooting
Diagnostics and logging
Use KLite Logs → Client / Plugin Debug for task and walker behavior. Use the Marketplace tab for artifact download, hash verification, class loading, enable/disable, and unload failures.
[CopperTinMiner] Tracking rock ... until mining stops or object changes.
[WebWalker] Integrated Shortest Path accepted route length=...
[WebWalker] Clicking waypoint ... surface=minimap
[CopperTinMiner] Deposit result: status=...
Distribution
Build and publish
./gradlew :copper-tin-miner:clean :copper-tin-miner:jar --no-daemon
- BuildCreate a reproducible JAR containing only the plugin implementation.
- CopyPlace the JAR in
marketplace-site/public/artifacts/. - VerifyCalculate the SHA-256 digest and exact file size.
- CatalogUpdate
plugins.jsonwith version, hash, size, and entrypoint. - DeployPublish the static artifact and catalog together.
- TestUnload the previous in-memory version before enabling the replacement.
Marketplace quality gate
Review checklist
- Declares
@PluginDependency(KLitePlugin.class)when using KLite services. - Does not bundle protected RuneLite or KLite classes.
- Stops automation and removes overlays during shutdown.
- Uses bounded asynchronous API calls.
- Does not retain passwords, secrets, or unbounded logs.
- Uses the shared
WebWalkerinstead of implementing arbitrary movement. - Logs state transitions and actionable failure details.
- Declares accurate version, minimum client version, authors, and entrypoints.
Next reference
Explore the callable API.
Use the API reference to inspect public KLite types and method signatures available to marketplace plugins.