OhMyGame
OhMyGame은 AI로 게임을 만들 수 있는 오픈소스 스튜디오로, 디자인·구축·플레이테스트·출판을 하나의 앱에서 모델 키와 함께 수행합니다
OSRS와 RS3용 봇·스크립트를 각각 전용 클라이언트에서 제공, 일주·이주·월 단위 구매 가능, 자동갱신 없고 카드 정보 저장 안 함
Bots and scripts for Old School RuneScape and RuneScape 3, each in its own client. Buy access for a week, two weeks or a month -- nothing auto-renews and no card is kept on file.
게임JavaGitHub @iEasyScript온라인운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기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.