尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證Start here to write a script for the Project X client. xclient.dev
翻譯準備中
取自儲存庫 README
正在載入完整 README。
檢視完整 README ↗Scripts are RuneLite plugins compiled against the Project X client.
You write them with one import, net.runelite.client.plugins.projectx.bot.*, which gives short names
like Inventory, Bank, Walker, Objects and Npcs:
var rock = Objects.query().nameEquals("Copper rocks").nearestReachable();
if (rock != null) rock.interact("Mine");
Npcs.query("Cow").nearest().interact("Attack");
The full guide is docs/SCRIPTING.md
in the client repo. This repository is a complete, small script you can rename and build on. It works on an object
(a tree, a rock) until the inventory is full, banks at the nearest bank, walks back and carries
on, and it shows the parts every store script should have:
reliability(), so it stops itself
cleanly instead of clicking forever when something goes wrong;ScriptPanel, SkillTracker) every Project X script uses;Loadout.Your source stays yours. The store needs your source to review a build, but only the jar is ever given to players.
Full documentation, including an API cookbook and the common traps, is at xclient.dev/osrs/developer/docs.
You need a JDK 11 installed (for example Temurin 11). Gradle uses it for the build whatever your default Java is, so it is fine to have newer ones too.
git clone https://github.com/iEasyScript/xclient-script-template.git
cd xclient-script-template
./gradlew build
That produces two files:
| File | What it is |
|---|---|
build/libs/TemplatePlugin.jar | The script. This is what you upload |
build/distributions/TemplatePlugin-source.zip | Its source, which the store asks for alongside the jar |
src/main/java/net/runelite/client/plugins/projectx/templateplugin/
TemplatePlugin.java lifecycle and wiring; the @PluginDescriptor is your script's identity
TemplateScript.java the loop, on a background thread: states, guarded steps, recovery
TemplateConfig.java the settings the player can change, including the inventory setup
TemplateOverlay.java the stats panel
build.gradle finds the client jar, builds the jar and the source zip
gradle.properties pluginName and the client version to compile against
To make it yours:
MyCoolPlugin.pluginName in gradle.properties to the new plugin class name. The jar is named after
it, and the client finds your script by it.TemplateConfig.GROUP to something unique to your script.The build compiles against the Project X client jar. It finds one in this order:
-PprojectxClientPath=/path/to/projectx-1.0.79.jar, if you pass it;~/.projectx/projectx-<version>.jar
(%USERPROFILE%\.projectx\ on Windows);projectx-<version>.jar from the
client's GitHub release.<version> is projectxClientVersion in gradle.properties. The first line of the build says
which jar it used.
Always build against the client players are running. Item, object and widget ids are Java
constants, and the compiler copies their values into your classes. A jar built against an old
client keeps the old values even when it runs on a new one, which is how a script ends up
clicking the wrong widget after a game update. When a new client is released, bump
projectxClientVersion, rebuild, and upload a new version.
You do not have to publish to try your script. Copy the jar into
%USERPROFILE%\.runelite\projectx-plugins\dev\
(~/.runelite/projectx-plugins/dev/ elsewhere) and restart the client. It loads from there and
stays loaded. Needs client 1.0.77 or later. Since 1.0.78 the My Scripts window has a Store tab
and a Local tab, and your dev-folder scripts show up under Local.
projectx-plugins/: the client keeps only scripts the
store knows about there, and switches the rest off shortly after start-up.Also worth doing:
decide() in
the template), keep each state's method short, and log state changes. The session log is in
~/.runelite/logs/projectx/../gradlew build.build/libs/<pluginName>.jar and
build/distributions/<pluginName>-source.zip.version in your @PluginDescriptor. Each version can be uploaded
once, so bump it for every build.A reviewer reads the source, then approves or rejects with a note. For later versions, use Upload new version on your script's page. The portal explains pricing and payouts in full.
They are easy to mix up, and mixing them up stops your script loading.
| What it is | Now | Where it goes | |
|---|---|---|---|
| Client release | What the client is called | 1.0.79 | projectxClientVersion in gradle.properties |
| Plugin API | What the client offers scripts | 2.7.0 | minClientVersion in your @PluginDescriptor |
minClientVersion is the oldest plugin API your script needs. A client that offers less will not
load the script (the log says Plugin X needs plugin API 2.7.0 or higher), and the plugin hub
will not install it there. Set it to the API level that added the newest thing you use:
| Plugin API | Client | Added |
|---|---|---|
2.6.25 | 1.0.31 | ScriptPanel, ScriptPanelOverlay, SkillTracker |
2.6.29 | 1.0.47 | ProjectX.withDirectInput, Prayers.flick / flickQuickPrayer |
2.6.30 | 1.0.49 | BoatNavigator; Walker.walkTo sails to targets at sea |
2.6.31 | 1.0.56 | Script.reliability() and the util.reliability package |
2.7.0 | 1.0.79 | The simple API: the bot package (Inventory, Objects.query(), Loadout, ...) |
The template uses the simple API, so it declares 2.7.0. Putting a client release number such as
1.0.79 there is wrong: as an API level it means "anything", so nothing is checked. isExternal = true
must be set for the check to apply at all.
Threads. Three threads matter, and each has one job.
| Thread | Runs | Rule |
|---|---|---|
| Your script's executor | the loop in run() | The only place to click, walk or wait |
| Client thread | overlays, @Subscribe event handlers, clientThread.invoke(...) | Read and return. Never click, sleep or wait |
| Swing UI thread | startUp(), shutDown(), the config panel | Start or stop the loop and return |
sleepUntil after it is measuring the wrong moment and the action looks like
it failed. Search and click from the script thread, as the loop does:
var tree = Objects.query("Tree").nearest(); // the search hops threads for you
if (tree != null) tree.interact("Chop down"); // click from the script thread
ProjectX.getClientThread().runOnClientThreadOptional(() -> ...). The helpers (Inventory, Player,
Bank and the rest) already do this for you. Avoid clientThread.invoke(() -> someBoolean()): a lambda that returns a
boolean picks the overload that re-runs it every tick until it returns true, and returns
nothing.sleepUntil(() -> condition, timeoutMs).shutdown(). Call super.shutdown(), then clear your own state, so the next
start is clean.build() and render() cheap. They run every frame. No waiting, no clicking, no
network calls (such as GrandExchange.getPrice).Bank.withdrawX(name, amount) only matches a bank stack holding at least amount. Ask
for 28 when the bank has 20 and nothing happens. Use Math.min(amount, Bank.count(name, true)).Inventory.hasItem("axe") matches a pickaxe
(partial), while Inventory.contains("axe") does not (exact). Most name methods have an
overload with a boolean exact parameter: pass true unless you mean "contains".Inventory.count(name) counts slots, not a stack. Use itemQuantity(name) for coins,
runes and other stackables.SkillTracker resets whenever the start time you pass changes. Store
System.currentTimeMillis() once when the script starts and pass that; never now - runtime.ProjectX.status themselves (opening the bank, clicking an object). Keep your
own status field for the stats panel, as the template does.Rs2* names still work but are deprecated. Use the short names: Objects.query(),
Npcs.query(), Inventory, Bank. The full old-to-new table is in the client repo's
docs/api-migration.tsv.net.runelite.api.gameval.InterfaceID and ItemID, never raw
numbers. Game updates move widgets; the named ids move with them when you rebuild.BSD 2-Clause, like the client. Do what you like with this example, including keeping your own script closed source.