diff --git a/docs/Guides/ScriptSubmissionWorkflow.md b/docs/Guides/ScriptSubmissionWorkflow.md index 13a2b436..b18bf996 100644 --- a/docs/Guides/ScriptSubmissionWorkflow.md +++ b/docs/Guides/ScriptSubmissionWorkflow.md @@ -1,8 +1,18 @@ -# Script Submission Workflow +--- +title: Script Submission Workflow +description: How to submit changes to your scripts using feature branches and squashed commits — GitHub Desktop and Git CLI walkthroughs. +--- + +import React from 'react'; +import ContentBlock from '@site/src/components/ContentBlock'; + + This document outlines the process for submitting changes to your scripts using feature branches and squashing commits. This ensures that your script reviewers can review all changes in a single, consolidated commit, improving the review process. -## Step-by-Step Workflow using Github Desktop (Recommended) + + + ### Clone the Repository 1. Open **GitHub Desktop**. @@ -64,9 +74,9 @@ Following this workflow helps ensure that your reviewers can see all your change If you have any questions or run into any issues, please reach out! ---- + -## Step-by-Step Workflow using Git CLI + ### Clone the Repository If you haven't already cloned your repository, you can do so using the following command: @@ -153,9 +163,9 @@ git branch -d feature/your-feature-name git push origin --delete feature/your-feature-name ``` ---- + -## Summary of Commands + 1. Create and switch to a new branch: ```bash @@ -192,8 +202,12 @@ git push origin --delete feature/your-feature-name git push origin --delete feature/your-feature-name ``` ---- + + + Following this workflow helps ensure that your reviewers can see all your changes in a single commit, making the review process smoother and more efficient. If you have any questions or run into any issues, please reach out! + + diff --git a/docs/Guides/Tutorial/01-install.md b/docs/Guides/Tutorial/01-install.md new file mode 100644 index 00000000..e5aeb843 --- /dev/null +++ b/docs/Guides/Tutorial/01-install.md @@ -0,0 +1,73 @@ +--- +title: 1. Install & First Launch +description: Buy a subscription on the BotWithUs website, install the loader, and exclude it from antivirus. +sidebar_position: 1 +--- + +import ContentBlock from '@site/src/components/ContentBlock'; +import BrowserWindow from '@site/src/components/BrowserWindow'; + + + + +BotWithUs is paid software. Before anything else, you need an active subscription. + +1. Create an account on [botwithus.com](https://botwithus.com) and sign in. +2. Choose how you want to pay — wallet top-up or direct card checkout. + +**Option 1 — Top up your wallet, then subscribe** + +Useful if you want to pay with crypto, or keep a balance for renewals. + +- Open [**My Account**](https://botwithus.com/profile/) and under **Wallet Balance** click **Manage** (or go straight to your [**Wallet**](https://botwithus.com/wallet/)). +- Click **Add Funds** and top up with card or crypto. + +> ![Add funds to your wallet](../images/TOP_UP.png) + +- Head to [**Plans**](https://botwithus.com/plans/), pick your subscription, and pay with your wallet balance. + +> ![Choose a subscription plan](../images/CHOOSE_SUB_PLAN.png) + +**Option 2 — Pay directly with card** + +- Go straight to [**Plans**](https://botwithus.com/plans/) and pay with a card at checkout. + +Once your subscription is active you can review or change it any time from [**Manage Subscription**](https://botwithus.com/subscription/manage/). + +:::info Payment issues? +If a payment went through but your subscription isn't showing as active, ping a `@Dev` in the [BotWithUs Discord](https://discord.gg/botwithus) and they'll sort it out. +::: + + + + + + +1. Head to the [**Download**](https://botwithus.com/download) page and grab the loader. The quick guide on the same page tells you which build to pick. +2. Run the installer. Defaults are fine — it puts the loader in your `BotWithUs/` user folder. +3. The first launch creates `~/BotWithUs/scripts/local/` (where your own scripts go later) and `~/BotWithUs/scripts/sdn/` (where marketplace scripts get cached). + + + + + +The loader injects into the RuneScape client process, which is exactly the kind of behaviour antivirus tools like to flag. Most users hit at least one false positive. + +Add an exclusion for the BotWithUs install folder *before* you run the loader for the first time. Even Microsoft Defender will quietly quarantine the executable on some systems. + +- **Windows Defender:** Settings → Privacy & security → Windows Security → Virus & threat protection → Manage settings → Add or remove exclusions → Add an exclusion → Folder → pick your `BotWithUs` install folder. +- **Third-party AVs (Avast, Bitdefender, Kaspersky, Malwarebytes, etc.):** add the same folder to the AV's allow/exclusions list. The exact menu varies, but every AV has one. + +If the loader vanishes a few seconds after you launch it, or never produces a window at all, antivirus is the first thing to check. The loader is signed and clean — it's the injection that triggers heuristics. + + + + + +1. Start the loader. +2. Sign in with the same account you used on [botwithus.com](https://botwithus.com). The loader fetches your active subscription from the website automatically — there's no separate serial or activation key. +3. Once you're signed in, the toolbar lights up and you're ready to add a game account. + + + +**Next:** [launching accounts →](./02-launching-accounts.md) diff --git a/docs/Guides/Tutorial/02-launching-accounts.md b/docs/Guides/Tutorial/02-launching-accounts.md new file mode 100644 index 00000000..8ff50879 --- /dev/null +++ b/docs/Guides/Tutorial/02-launching-accounts.md @@ -0,0 +1,64 @@ +--- +title: 2. Launching Accounts +description: Launch classic and launcher accounts through the BotWithUs loader, plus session limits. +sidebar_position: 2 +--- + +import ContentBlock from '@site/src/components/ContentBlock'; + + + +RuneScape has two login systems. **Both run directly from the BotWithUs loader** — the only difference is how the loader gets hold of your accounts. + +| Account type | How you log in normally | What you'll do here | +|---|---|---| +| **Classic** | Username + password directly into the game | Save credentials in the **account manager**, then create a session | +| **Launcher** | The launcher → click an account tile | Have the **launcher open** so the loader can pull your accounts in, then create a session | + +If you're not sure which you have: if you log in through the **launcher**, it's a launcher account. + + + + + +This is the simpler path. + +1. In the loader, open the **Account Manager** (button on the toolbar). +2. Click **Add account** and enter your username and password. + - You can leave credentials blank, but **autologin won't work** if you do. + +> ![Adding an account in the loader](../images/ADD_ACCOUNT_LOADER.png) + +3. Save the entry. +4. Back on the main loader screen, click **Launch account** then select classic or launcher. +5. Choose your saved account from the list. +6. Click **Launch session**. + +A game client window opens. After about **5 seconds**, BotWithUs injects into it. You'll know injection succeeded when the **BotWithUs logo** appears in the corner of the client — this is a prerequisite for opening the bot menu in [chapter 3](./03-bot-menu.md). + +> ![Loader injecting into the client](../images/LOADER_INJECT.png) + + + + + +Launcher accounts can't store credentials inside the loader — the launcher handles that. The loader pulls your accounts straight from the launcher, so it just needs to be **open** in the background. + +1. Open the **launcher** and sign in. +2. Switch to the **BotWithUs loader**. +3. Click **Add account**, and select the account type. +4. Click **Clear and repopulate** — the loader will re-read your accounts from the Jagex Launcher. +5. Click **Launch session**. + +The loader starts the client and BWU injects. It may take up to 10 seconds for the loader overlay to show in-game. + + + + + +- Your basic subscription allows for 2 sessions, any additional session can be purchased on the website if you want to bot more accounts at the same time. +- Closing the client frees a slot. + + + +**Next:** [the bot menu →](./03-bot-menu.md) diff --git a/docs/Guides/Tutorial/03-bot-menu.md b/docs/Guides/Tutorial/03-bot-menu.md new file mode 100644 index 00000000..31fbcf42 --- /dev/null +++ b/docs/Guides/Tutorial/03-bot-menu.md @@ -0,0 +1,65 @@ +--- +title: 3. The Bot Menu +description: Open the BotWithUs in-game menu, learn the hotkeys, and tell Scripts apart from SDN Scripts. +sidebar_position: 3 +--- + +import ContentBlock from '@site/src/components/ContentBlock'; + + + +This chapter assumes you've already launched a client and BotWithUs has injected — see [chapter 2](./02-launching-accounts.md) if the BWU logo isn't showing in the corner yet. + +Open the **bot menu** in two ways: + +- Press **Insert** on your keyboard, **or** +- Click the **BotWithUs logo** in the corner of the client. + +The overlay opens on top of the game. From there, click **Scripts** to bring up the scripts window. + +> ![Open the overlay and click Scripts](../images/OPEN_OVERLAY_AND_SCRIPTS.png) + + + + + +Inside the scripts window you'll see two tabs. Switch to the **SDN** tab to see anything you've subscribed to from the marketplace. + +> ![Switch to the SDN tab in the scripts window](../images/SWITCH_TO_SDN_SCRIPTS_SCRIPTS_WINDOW.png) + +| Tab | Where the scripts come from | When you'd use it | +|---|---------------------------------------------------|---| +| **Scripts** | `.jar` files you put in your local scripts folder | Scripts you wrote, or custom ones a friend sent you | +| **SDN** | The BotWithUs marketplace, downloaded on request | Anything you've subscribed to on [botwithus.net/sdn](https://botwithus.net/sdn) | + +You'll meet "Scripts" again in [chapter 5](./05-first-script.md) when you build your own. + + + + + +Two things to check: + +1. **Did you actually subscribe on the website?** Browse [botwithus.net/sdn](https://botwithus.net/sdn), click a script, and confirm it. +2. **Hit Refresh in the SDN tab.** No need to restart the loader — just click **Refresh** in the SDN tab and your new subscriptions will load. + +After that, the script should appear under the **SDN** tab in the scripts window. Press the load button to start the download (usually instant or only a few seconds). + + + + + +Same flow for both tabs: + +1. Click the script's name in the list. +2. Its options window appears (every script has its own — read what it asks for). +3. Click **Play** to start the script. Some scripts have their own play button inside the script's configuration UI — use whichever the script provides. +4. Watch your character act on screen, and watch the script console for any issues. + +> ![Configuring a script's options](../images/SCRIPT_OPTIONS.png) + +To stop a script, click **Stop** in its window (it replaces the Play button). Closing the script's configuration window with the configuration icon does **not** stop the script — only **Stop** does. The main overlay can be hidden the same way: press **Insert** or click the BWU logo. + + + +**Next:** if you only want to *use* scripts, you're done. To start writing your own, head to [setting up your coding environment →](./04-ide-setup.md). diff --git a/docs/Guides/Tutorial/04-ide-setup.md b/docs/Guides/Tutorial/04-ide-setup.md new file mode 100644 index 00000000..75d3e5a2 --- /dev/null +++ b/docs/Guides/Tutorial/04-ide-setup.md @@ -0,0 +1,113 @@ +--- +title: 4. Setting Up Your Coding Environment +description: Install IntelliJ IDEA, get JDK 20, and clone the BotWithUs skeleton script. +sidebar_position: 4 +--- + +import ContentBlock from '@site/src/components/ContentBlock'; +import BrowserWindow from '@site/src/components/BrowserWindow'; + + + +Before you can write a script, you need three things on your machine: + +| Tool | What it is | Why you need it | +|---|---|------------------------------------------------------------------------------------------------------------| +| **IntelliJ IDEA** | The code editor (an "IDE") | This is where you type, run, and build your scripts. This guide focuses on IntelliJ but any IDE will work. | +| **JDK 20** | The Java compiler | Scripts are written in Java; the JDK turns your code into a `.jar` BotWithUs can load | +| **Skeleton repo** | A pre-made starter project | Has all the BotWithUs APIs wired up so you can hit "build" and get a working script on day one | + + + + + + +IntelliJ comes in two editions: **Community** (free) and **Ultimate** (paid). Community is just fine for everything we'll do. + +1. Go to [jetbrains.com/idea/download](https://www.jetbrains.com/idea/download/). +2. Scroll down to the **Community Edition** section. Don't grab Ultimate by mistake — it's at the top of the page. +3. Click **Download**, run the installer. +4. Accept the defaults. The only useful checkbox is **Add "Open Folder as Project"** (it puts a right-click shortcut in Windows Explorer). +5. Launch IntelliJ when it finishes. + +On first launch IntelliJ asks about themes and plugins — pick whatever you like, defaults are safe. + + + + + + + +The **skeleton** is an empty BotWithUs script with the build setup already done. Cloning it means you don't have to set up Gradle, Maven, dependencies, or output paths by hand. + +You can clone using either Git (if you know it) or directly through IntelliJ. The IntelliJ way is easier. + +#### Cloning through IntelliJ + +1. From the IntelliJ welcome screen, click **Clone Repository** (or **File → New → Project from Version Control** if a project is already open). +2. In the **URL** field, paste: + ``` + https://github.com/BotWithUs/BwuScriptJavaSkeleton + ``` +3. Pick a folder for it (default is fine). +4. Click **Clone**. + +IntelliJ will download the project and ask if you want to **trust** it — say **Trust Project**. It'll then start indexing files (bottom status bar). Wait for indexing to finish before doing anything else. + +#### If IntelliJ asks about Git + +If IntelliJ says *"Git is not installed"* and you don't already have it: install it from [git-scm.com](https://git-scm.com/), accept all defaults, and try the clone again. Git is also useful later if you want to track your changes, and it's a must-have for publishing scripts to the marketplace. + + + + + + +You don't need to fetch a JDK from the web — IntelliJ has a built-in dialog. With the skeleton project open: + +1. Go to **File → Project Structure** (or `Ctrl+Alt+Shift+S`). +2. On the left, click **Project**. +3. Find the **SDK** dropdown. If it's blank or shows the wrong version, click it → **Download JDK…**. +4. In the dialog: + - **Version:** 20 + - **Vendor:** any works, but **Eclipse Temurin** or **Amazon Corretto** are common safe choices. + - **Location:** leave the default. +5. Click **Download**. IntelliJ pulls the JDK and selects it for the project. +6. Set **Language level** to **SDK Default**. +7. Click **OK**. + + + + + +The skeleton uses **Gradle** (Kotlin DSL — `build.gradle.kts`) to manage its dependencies. IntelliJ should detect this automatically and start a Gradle sync as soon as the project opens. + +- Look for a **Gradle** tool window on the right edge of IntelliJ. Click it. +- You should see the project with sections like **Tasks** (with `build`, `assemble`, etc.) and **Dependencies**. +- If a yellow banner at the top of the editor says *"Gradle project needs to be imported"* or *"Load Gradle Changes"* — click it (or press the refresh/sync icon in the Gradle tool window). + +When indexing and the Gradle sync finish, the bottom status bar goes quiet. You're ready. + + + + + +In the **Project** tool window (left edge): + +1. Expand `src` → `main` → `java` → `net.botwithus...` (the package path varies by skeleton version). +2. Find the `.java` file — it'll be the one with the same name as the skeleton's main class. +3. Double-click to open. + +:::info This is just a folder structure +What you see in the **Project** tool window is the same folder layout that exists on disk. IntelliJ collapses Java packages into a single dotted entry (e.g. `net.botwithus.example`) for readability, but if you open the project folder in Windows Explorer you'll find real, nested folders: `src\main\java\net\botwithus\example\`. Each `.` in the package name is a folder boundary on disk. +::: + +If the editor shows code with **no red squiggles**, the IDE, JDK, and dependencies are all happy. You're ready to build it into a real script. + +:::warning If there are red squiggles everywhere +That usually means Gradle hasn't finished syncing or the JDK isn't set. Re-check Step 3 and Step 4. Still stuck? Post a screenshot in the [public scripters channel](https://discord.com/channels/973830420858810378/1257291849475817565) on Discord. +::: + + + +**Next:** [build the skeleton into your first running script →](./05-first-script.md) diff --git a/docs/Guides/Tutorial/05-first-script.md b/docs/Guides/Tutorial/05-first-script.md new file mode 100644 index 00000000..0523089e --- /dev/null +++ b/docs/Guides/Tutorial/05-first-script.md @@ -0,0 +1,255 @@ +--- +title: 5. Your First Local Script +description: Build the BotWithUs skeleton into a .jar, let Gradle drop it in the right folder, and watch it run inside the bot menu. +sidebar_position: 5 +--- + +import ContentBlock from '@site/src/components/ContentBlock'; + + + +Before we open the skeleton, the absolute minimum Java you'll see in every script: **variables**. A variable is a named slot that holds one value of a specific type. Java is **statically typed** — you declare the type up front, and you can't put a value of the wrong type into it. + +Declaration is always `Type name = value;`: + +```java +int coins = 1500; // whole number (32-bit) +long xp = 2_500_000_000L;// whole number, bigger range — note the 'L' suffix +double accuracy = 0.875; // decimal number +boolean isFull = true; // true / false only +String loot = "Dragon bones";// text — note the capital S, String is a class + +final int MAX_TRIES = 3; // 'final' = "this never changes" (a constant) + +var nearestNpc = NpcQuery.newQuery().name("Banker").results().nearest(); // 'var' = "compiler, infer the type for me" +``` + +A few things to know: + +- **Naming**: `lowerCamelCase` for variables/fields (`botState`, `loopDelay`), `UPPER_SNAKE_CASE` for `final` constants (`MAX_TRIES`). +- **`int` vs `long`**: use `int` by default; reach for `long` when the number could exceed about 2 billion (timestamps, big XP totals, BotWithUs delays). +- **`String` is *not* a primitive** — it's a class. That's why it's capitalized, and why you compare two strings with `.equals(...)`, never `==`. +- **`var`** lets the compiler figure out the type from the right-hand side. Useful when the type is long-winded; the type is still fixed, you just don't have to type it out. + +You'll see all of these used in the skeleton in the next section. + + + + + +You already have the skeleton open in IntelliJ. In this chapter you'll: + +1. Look at the two files that make up the skeleton script. +2. Add a single line that prints to the console (so we can prove the script is alive). +3. Build it into a `.jar` with Gradle. +4. Confirm the `.jar` landed in the right folder — the skeleton's build script handles this for you. +5. Open BotWithUs and run it. + +That's the entire end-to-end flow. Everything else you'll learn later is variations on these five steps. + + + + + +Inside the skeleton you'll find two files under `src/main/java/net/botwithus/`: + +| File | Job | +|---|-----------------------------------------------------------------------------------------| +| `SkeletonScript.java` | The script itself — what runs every loop. | +| `SkeletonScriptGraphicsContext.java` | The ImGui settings panel users see when they click script configuration. | + +#### `SkeletonScript.java` (trimmed) + +Before the class itself, real Java files start with a **package declaration** and a list of **imports** — every class name you use (`LoopingScript`, `Client`, `LocalPlayer`, `Execution`, `Backpack`, etc.) has to be imported from its package, otherwise the compiler doesn't know where to find it. The skeleton's actual top of file looks like this: + +```java +package net.botwithus; + +import net.botwithus.api.game.hud.inventories.Backpack; +import net.botwithus.internal.scripts.ScriptDefinition; +import net.botwithus.rs3.game.Client; +import net.botwithus.rs3.game.hud.interfaces.Interfaces; +import net.botwithus.rs3.game.queries.builders.objects.SceneObjectQuery; +import net.botwithus.rs3.game.scene.entities.characters.player.LocalPlayer; +import net.botwithus.rs3.game.scene.entities.object.SceneObject; +import net.botwithus.rs3.script.Execution; +import net.botwithus.rs3.script.LoopingScript; +import net.botwithus.rs3.script.config.ScriptConfig; + +import java.util.Random; +``` + +You almost never type these by hand — when you reference a new class, IntelliJ pops up an *Unresolved reference* error and you press **Alt+Enter** to auto-import it. If you ever copy a snippet from this guide and the class name has a red squiggle, that's all it means: import is missing. Alt+Enter fixes it. + +The snippets below leave imports out for readability, but they exist in the real file. + +```java +public class SkeletonScript extends LoopingScript { + + private final Random random = new Random(); + private BotState botState = BotState.IDLE; + + enum BotState { IDLE, SKILLING, BANKING } + + public SkeletonScript(String s, ScriptConfig scriptConfig, ScriptDefinition scriptDefinition) { + super(s, scriptConfig, scriptDefinition); + this.sgc = new SkeletonScriptGraphicsContext(getConsole(), this); + } + + @Override + public void onLoop() { + LocalPlayer player = Client.getLocalPlayer(); + if (player == null + || Client.getGameState() != Client.GameState.LOGGED_IN + || botState == BotState.IDLE) { + Execution.delay(random.nextLong(3000, 7000)); + return; + } + switch (botState) { + case SKILLING -> Execution.delay(handleSkilling(player)); + case BANKING -> { /* your banking logic */ } + } + } +} +``` + +A few things worth knowing: + +| Piece | What it means | +|---|---| +| `extends LoopingScript` | "This class is a BotWithUs script." `LoopingScript` is the simplest base class — it just calls `onLoop()` repeatedly (~every 100 ms by default; change with `this.loopDelay = 500`). | +| `this.sgc = new SkeletonScriptGraphicsContext(...)` | Wires up the ImGui settings panel. The `sgc` field is what BWU draws when a user opens your script. | +| `BotState` enum + `botState` field | A simple state machine. The skeleton ships with `IDLE`, `SKILLING`, `BANKING` — replace these with whatever your script needs. | +| `onLoop()` early-return | If we're not logged in **or** the bot is idle, just delay and bail. The state machine only runs when the player is in-game and the bot is doing something. | + +`Execution.delay(...)` blocks the loop for the given number of milliseconds — use it to pace actions/queries so you don't spam the game. + +#### `SkeletonScriptGraphicsContext.java` + +The graphics context is where you describe your settings UI with ImGui. The skeleton's version draws a window with two tabs: + +```java +@Override +public void drawSettings() { + if (ImGui.Begin("My script", ImGuiWindowFlag.None.getValue())) { + if (ImGui.BeginTabBar("My bar", ImGuiWindowFlag.None.getValue())) { + if (ImGui.BeginTabItem("Settings", ImGuiWindowFlag.None.getValue())) { + ImGui.Text("Welcome to my script!"); + ImGui.Text("My scripts state is: " + script.getBotState()); + ImGui.EndTabItem(); + } + if (ImGui.BeginTabItem("Other", ImGuiWindowFlag.None.getValue())) { + script.setSomeBool(ImGui.Checkbox("Are you cool?", script.isSomeBool())); + ImGui.EndTabItem(); + } + ImGui.EndTabBar(); + } + ImGui.End(); + } +} +``` + +You don't need to touch this for now — just know it's where your script's settings window lives. + + + + + +Open `SkeletonScript.java` and add **one line** at the very top of `onLoop()`, before the early-return check: + +```java +@Override +public void onLoop() { + println("Hello from SkeletonScript!"); + + LocalPlayer player = Client.getLocalPlayer(); + if (player == null + || Client.getGameState() != Client.GameState.LOGGED_IN + || botState == BotState.IDLE) { + Execution.delay(random.nextLong(3000, 7000)); + return; + } + // ...rest unchanged +} +``` + +`println(...)` is a helper provided by `LoopingScript` — anything you pass to it shows up in the BotWithUs console window when the script is running. Putting it **above** the early-return guarantees it fires every loop, even while the bot is `IDLE`. We'll use that console heavily for debugging in later chapters. + +Save the file (`Ctrl+S`). + +:::tip Why not put real game logic yet? +Because we want to confirm the build → load → run pipeline works end-to-end **before** layering anything on top. If something's wrong, a one-line script makes the problem much easier to spot. Until you get comfortable it's best to test every new feature you add before adding more. +::: + + + + + +1. Open the **Gradle** tool window on IntelliJ's right edge. +2. Expand the project → **Tasks → build**. +3. Double-click **build**. + +IntelliJ runs Gradle, compiles your code, and produces a `.jar`. The bottom panel shows progress; you want to see **BUILD SUCCESSFUL** at the end. + +If you get **BUILD FAILED**, scroll up in the output. The first red line usually points at the file and line number with the problem. + +:::info You can also use the wrapper from the terminal +If you prefer the terminal: open IntelliJ's terminal (`Alt+F12`) and run `gradlew build`. Same result. +::: + + + + + +The skeleton's `build.gradle.kts` defines a custom `copyJar` task that runs **automatically after every successful build** and drops the finished `.jar` into your local scripts folder. + +A `.jar` is just a Java archive — very similar to a `.zip` or `.rar` — holding your compiled classes, resources like images, and metadata. + +``` +%USERPROFILE%\BotWithUs\scripts\local\ +``` + +(That's `C:\Users\\BotWithUs\scripts\local\` — paste the `%USERPROFILE%` form into Explorer's address bar and Windows will resolve it.) + +After **BUILD SUCCESSFUL**, open that folder. You should see the skeleton's jar (e.g. `BwuScriptJavaSkeleton-1.0-SNAPSHOT.jar`) sitting in there. + +:::info Don't see the .jar? +The `copyJar` task in `build.gradle.kts` controls the destination. Open it and look for the section that references `${user.home}\BotWithUs\scripts\local\` — that's where it lands. If you've changed it, either change it back or copy the `.jar` to the local folder by hand. +::: + + + + + +1. Launch a game client through the loader ([chapter 2](./02-launching-accounts.md)). +2. Wait for injection (BWU logo appears). +3. Press **Insert** to open the bot menu and click **Scripts**. +4. On the **Scripts** tab (the local one — *not* SDN), find **SkeletonScript** in the list. +5. Click it, then click **Play**. + +Open the BotWithUs **console**. Every loop, you should see: + +``` +Hello from SkeletonScript! +``` + +**That's a working script.** + +Open the script's settings window and you'll see the two ImGui tabs ("Settings" and "Other") rendered by `SkeletonScriptGraphicsContext` — proof that the graphics context is wired up too. + + + + + +| Symptom | Likely cause | Fix | +|---|---|---| +| Script not in the **Scripts** list | `.jar` isn't in `BotWithUs\scripts\local\` | Confirm `copyJar` ran in the build output; copy the `.jar` over manually if needed | +| Script appears, but errors when started | Compile target wrong / missing dependency | Re-check JDK 20 in [chapter 4](./04-ide-setup.md), then rebuild | +| Console shows nothing | Console window is closed or filtered | Open the console from the loader; check there's no filter set | +| BUILD FAILED in IntelliJ | Code error or wrong JDK | Read the first red line in the Gradle output — it tells you what and where | + + + +You've now built and run your first script. From here, every new feature is just *more code inside `onLoop()`* (or new tabs in the graphics context). The next chapter teaches you what game data you can actually look at from in there. + +**Next:** [reading the game →](./06-reading-the-game.md) diff --git a/docs/Guides/Tutorial/06-reading-the-game.md b/docs/Guides/Tutorial/06-reading-the-game.md new file mode 100644 index 00000000..aa676e27 --- /dev/null +++ b/docs/Guides/Tutorial/06-reading-the-game.md @@ -0,0 +1,383 @@ +--- +title: 6. Reading the Game +description: Plain-English tour of the data your script can read — varbits, scene objects, NPCs, inventory, skills, interfaces, position, events, and more. +sidebar_position: 6 +--- + +import ContentBlock from '@site/src/components/ContentBlock'; + + + +Your script is a brain that watches the game and decides what to do. Everything it watches falls into a handful of categories. Once you know what's in each category and *why it exists*, you stop guessing and start reading the game like the engine sees it. + +| Category | Answers the question | Examples | +|---|---|-------------------------------------------------------| +| **Varbits** | "Is this small flag on or off? What stage of the quest am I at?" | Is this prayer active? Is this potion buff active? | +| **Varps** | Same idea as varbits, just bigger numbers | Quest progress, packed settings, per-player counters | +| **Varcs** | Per-client values (UI state, options) | Whether the chat box is hidden, music volume | +| **Scene objects** | "What buildings, doors, trees, ores, banks are around me?" | Bank chest, magic tree, mining rock, ladder | +| **NPCs** | "What characters are around me — friendly or hostile?" | Boss, monster, banker, shopkeeper | +| **Ground items** | "What loot is on the floor near me?" | Bones, rare drops, dropped supplies | +| **Projectiles** | "Is something flying through the air right now?" | Boss special attack, player's spell, dragonfire | +| **Animations / spot animations** | "What is something *doing* right now?" | Player swinging axe, NPC casting, ground graphic effect | +| **Inventory, bank & equipment** | "What items am I carrying, banking, or wearing?" | Backpack stacks, equipped weapon, bank counts | +| **Skills & XP** | "What level am I? How much XP did I just gain?" | Mining level, XP gained this session, level-up | +| **Interfaces & components** | "What menu/dialog is open and what does it say?" | Bank window, quest dialog, options menu | +| **Player & position** | "Where am I, am I in my target area, what's the engine seeing me do?" | Coordinate, current Area, my animation/hitsplats | +| **Events** | "Tell me when X happens — I don't want to keep checking." | Chat message arrived, XP gained, inventory changed | + +The rest of this chapter is one section per category, in plain English, with concrete examples from real BotWithUs scripts. + + + + + +Think of a varbit as a labeled slot the game stores a small number in. Each varbit has an **ID** (a number) and a **value**. The value is *usually* 0 or 1 (a flag), but plenty of varbits are real small integers — counters, timers, tiered settings. + +In real WithUs scripts you'll find varbits doing all of these: + +- **Yes/no flags** — "Is this prayer turned on?" → `VarManager.getVarbitValue(prayer.getVarbitId()) > 0` +- **Counters** — "How many flowers are in the basket right now?" → `VarManager.getVarbitValue(basket.getFlowerCountVarbit())` +- **Buff/familiar timers** — "How much time is left on my BoB familiar?" → `VarManager.getVarbitValue(BOB_REMAINING_TIME_VARBIT)` +- **Mode toggles** — "Is area-loot enabled?" → `VarManager.getVarbitValue(27943) == 1` +- **Boss/quest stage** — "Which phase is the boss in?" → `varbit == 3` + +Reading one looks like: + +```java +int phase = VarManager.getVarbitValue(SOME_VARBIT_ID); +if (phase == 3) { + // boss is in phase 3, do phase-3 things +} +``` + +You don't memorize varbit IDs — you **find** them. Varbits behave differently from varps in the debug overlay, so finding them has its own workflow — see [chapter 7](./07-debug-overlays.md) for the details. + +In a finished script the IDs usually end up as named constants (`PERFECT_WOODCUTTING_JUJU_VARBIT = 26029`) so the call site reads naturally. + + + + + +A varp ("var player") is a varbit's bigger cousin — a full 32-bit integer the game tracks per player. Under the hood, varbits are *slices* of varps (each varp is split into bit-fields, and a varbit is one of those fields). For your purposes: + +- Use a **varbit** when the in-game state is small (a flag, a stage, a small counter) — most of the time. +- Reach for a **varp** when the value is too big to be a varbit (large counters, packed quest progress, certain settings) or when the debug overlay shows the change in the varp panel rather than the varbit panel. + +Reading is the same shape, different method: + +```java +int value = VarManager.getVarpValue(SOME_VARP_ID); +``` + +You'll mostly meet varps in quest tracking and certain global settings. + + + + + +A varc ("var client") lives only in **your** game client. They store UI state and per-client preferences: which chat tab is active, whether music is muted, how a sub-panel was last sorted. + +You'll use varcs less often than varbits/varps, but they're the right tool when your script depends on a **UI** state the server doesn't know about — for example, "is this side panel currently expanded or collapsed?" + +There's also `getVarcStringValue(...)` for varcs that hold strings rather than numbers (e.g. user-entered text or a selected option label). + + + + + +A **scene object** is anything in the world that *isn't a person*: trees, banks, ladders, mining rocks, fishing spots, doors, anvils, prayer altars, fires, ranges. Each has a position, a name, and one or more right-click options ("Chop down", "Mine", "Climb-up", "Add logs to"). + +The query is a chain of filters: `name(...)`, `option(...)`, `id(...)`, `animation(...)`, etc. — combine whichever you need. Real patterns from our scripts: + +```java +// Filter by name only: +SceneObject tree = SceneObjectQuery.newQuery() + .name("Magic tree") + .results() + .nearest(); + +// Filter by name AND right-click option (many "Fire" objects +// exist in the world; we only want a cookable one): +SceneObject fire = SceneObjectQuery.newQuery() + .name("Fire") + .option("Cook-at") + .results() + .nearest(); + +// Filter by option only — "give me whatever is nearby that lets +// me 'Load Last Preset from'", regardless of name: +SceneObject preset = SceneObjectQuery.newQuery() + .option("Load Last Preset from") + .results() + .nearest(); + +// Filter out scene objects that exist in the scene data but aren't +// currently rendered/interactable. Very common in encounters where +// the same name can refer to an active or inactive object (open vs +// closed door, powered vs unpowered anchor, used vs fresh trapdoor) +// you will use hidden flag almost everywhere: +SceneObject tree = SceneObjectQuery.newQuery() + .name("Tree") + .hidden(false) + .results() + .nearest(); + +if (tree != null) { + tree.interact("Chop down"); +} +``` + +What `SceneObjectQuery` actually does: "give me every scene object in the loaded scene matching these filters" — then you call `.nearest()` / `.first()` / iterate. If nothing matches, you get `null` back, which is why every interaction is preceded by a null check. + +:::tip Why filter by option, not just name? +Many objects share names (every "Fire" is named "Fire", but only some are cookable). Filtering by the right-click option you actually want skips decorative or wrong-state copies. This is *the* most common mistake in early scripts. +::: + + + + + + +NPCs are the moving people and monsters. Same query shape as scene objects: + +```java +Npc boss = NpcQuery.newQuery() + .name("Araxxor") + .results() + .nearest(); +``` + +What you read off an NPC: + +- **Position** — where they are (`getCoordinate()`). +- **Animation ID** — what they're currently doing (`getAnimationId()`; see below). +- **Health** — how alive they are (boss kill detection). +- **Overhead text** — what they just said. +- **Target** — who/what they're attacking — useful to detect when the boss switches to or away from you. + +NPCs are how your script "sees" enemies, vendors, summoning familiars, slayer task targets, and any creature in the world. Boss scripts in particular spend most of their time reading the boss NPC's animation and target. + + + + + +A **ground item** is anything sitting on a tile that you can pick up: bones a monster dropped, rares from a kill, supplies someone tossed. Same query pattern as scene objects and NPCs: + +```java +GroundItem loot = GroundItemQuery.newQuery() + .name("Dragon bones") + .results() + .nearest(); + +if (loot != null) { + loot.interact("Take"); +} +``` + +You'll mostly use this in money-making and combat scripts — anything that loots after a kill. + + + + + +A **projectile** is something the game has launched but not yet landed: a fireball, a thrown spear, a dragon's breath, a boss's homing attack. Each has an **ID**, a **source** (who fired it), a **target**, a **position**, and a **landing cycle** — the engine tick when it'll hit. + +Projectiles are critical for **boss scripts** — they're how you know an attack is *coming* before the damage actually hits. By the time the damage lands, it's too late to switch prayers or step out. + +The real pattern from our prayer-flicker code is: + +```java +EntityResultSet magicProjectiles = + ProjectileQuery.newQuery().ids(1234).results(); // some boss's magic attack ID + +for (Projectile p : magicProjectiles) { + long landingInMs = (Client.getClientCycle() - p.getEndCycle()) * 20L; + // landingInMs is negative while the projectile is still mid-flight, + // 0 when it lands, positive once it's already landed. + if (landingInMs > -300 && landingInMs < 0) { + // ~300 ms before impact — flick to the matching prayer + } +} +``` + +Two things to notice: + +- We query **by ID** (`.ids(1234)`) — each boss attack has its own projectile ID. You learn them with the debug overlay. +- `(Client.getClientCycle() - p.getEndCycle()) * 20L` gives you a number relative to landing. **Negative** = "lands in N ms" (still in flight), **positive** = "landed N ms ago." Boss flickers in our codebase (`ZukWithUsNM`, `AraxxorWithUs`) trigger when `landingInMs` is in a small negative window like `-300 < landingInMs < 0` — that's the lead-time you need to swap prayers before the hit registers. + + + + + +Every action in the game has an **animation ID**. Player swinging an axe = one ID. Player drinking a potion = another. Boss winding up its special = another. + +Two flavors: + +- **Animation** — attached to a character (player, NPC, monster). Read the character's `getAnimationId()`. +- **Spot animation** — a graphic that plays at a *location* without a character (e.g., a magic rune appearing on the ground, an explosion mark). + +The single most-used idiom is **`getAnimationId() == -1`**, which means *the character isn't doing anything right now*. You'll see it in two shapes: + +```java +// Wait until I'm idle (e.g. finished chopping): +Execution.delayUntil(12000, () -> Client.getLocalPlayer().getAnimationId() == -1); + +// "Am I currently doing anything?" — for skill/combat detection: +boolean busy = player.isMoving() || player.getAnimationId() != -1; +``` + +For specific actions, compare against known IDs (most of the time, the simple `!= -1` check above is enough): + +```java +LocalPlayer player = Client.getLocalPlayer(); +boolean isHarvesting = + player.getAnimationId() == 21228 + || player.getAnimationId() == 36249 + || player.getAnimationId() == 36250 + || player.getAnimationId() == 31055; +``` + +Animation IDs change between bosses, skills, and outfits — you'll be looking them up constantly. + + + + + +Your **inventory** (the backpack), the **bank**, and your **equipped gear** are all readable. The most-used helpers in our scripts: + +```java +if (Backpack.isFull()) { + // walk to bank +} + +int axes = Backpack.getCount("Dragon hatchet"); + +Item weapon = Equipment.getItemAt(EquipmentSlot.WEAPON); +``` + +Common questions you'll answer with these: + +- **Backpack** — "Is my backpack full? Do I still have logs to bank? Do I have a tinderbox?" +- **Bank** — "Do I have stamina potions in the bank to withdraw? How many planks left?" +- **Equipment** — "Am I wearing the right weapon for this monster?" + +For specific item *queries* by ID/predicate, use `InventoryItemQuery` — same shape as `SceneObjectQuery` but for items. + + + + + +The `Skills` API tells you levels and XP: + +```java +int miningLevel = Skills.MINING.getLevel(); +int miningXp = Skills.MINING.getExperience(); +``` + +Use this when you need to: + +- Gate behavior on level ("Don't try to mine runite until level 85"). +- Track session XP for an in-script counter. +- Detect when training is "done" (target level reached). + +For a *push-based* version — "tell me whenever XP changes" — see Events below. + + + + + +Every UI panel in the game (bank window, dialog box, quest journal, settings) is an **interface** made up of **components**. Each component has a position, text, and sometimes buttons or input fields. + +```java +if (Interfaces.isOpen(517)) { + // confirm option +} + +Component continueBtn = ComponentQuery.newQuery(517) + .text("Click here to continue") + .results() + .first(); +``` + +You'll reach for interfaces when your script depends on the **UI** being in a specific state — most commonly: + +- The **bank** is open before depositing/withdrawing. +- A **dialog** is on screen so you can advance it. +- The **deposit/withdraw mode** of a panel is the one you want. + +Interface IDs (like `517` above) are also discoverable through the debug overlays in the next chapter. + + + + + +Your own character is a `LocalPlayer` — same shape as any `Player`/`Npc`, so you read animation, position, target, and hitsplats off it the same way: + +```java +LocalPlayer me = Client.getLocalPlayer(); +Coordinate pos = me.getCoordinate(); +int anim = me.getAnimationId(); // -1 means idle +``` + +For "am I in the right zone?" use an `Area`: + +```java +Area minePit = new Area.Rectangular( + new Coordinate(3290, 3370, 0), + new Coordinate(3300, 3380, 0)); + +if (!minePit.contains(me)) { + // walk back to the mine +} +``` + +`Movement` and `NavPath` (the action side) build on top of these reads — your script asks "where am I?" with `Coordinate`, then asks `Movement` to walk somewhere new. + + + + + +Everything above is **pull**: your `onLoop()` runs and asks the game "what's the state right now?" Sometimes you'd rather the game *tell you* the moment something happens — that's an **event**. + +```java +EventBus.subscribe(ChatMessageEvent.class, e -> { + if (e.getMessage().contains("You receive a Clue scroll")) { + println("Clue dropped — switch state to bank."); + } +}); +``` + +The events you'll hit most often: + +- **`ChatMessageEvent`** — anything in the game chat. +- **`SkillUpdateEvent`** — XP gained / level up. +- **`InventoryUpdateEvent`** — items added or removed from the backpack. + +Two rules of thumb: + +- **Subscribe in your constructor or `initialize()`**, not inside `onLoop()` (you'd subscribe over and over). +- **Don't run game queries directly in an event handler** — handlers fire on a different thread. Use them to set a flag your `onLoop()` reads next tick. +- **Don't run execution delays inside the events**. + + + + + +Most scripts boil down to a tiny decision loop using these primitives: + +> *Look at varbits / inventory / NPC animations / projectiles to figure out what's happening, then interact with a scene object, NPC, or interface to do something about it.* + +A toy example for a woodcutter: + +1. **Look** — query for the nearest tree. Read my own animation ID. +2. **Decide** — if I'm idle (`getAnimationId() == -1`) and there's a tree, click it. If `Backpack.isFull()`, switch to banking instead. +3. **Repeat** — `onLoop()` fires again ~100 ms later. + +That's it. Boss scripts add more checks (projectiles, varbits, target switches) but the shape is identical. + + + +You now know **what** the game exposes. The next chapter shows you the on-screen tools BotWithUs gives you for **finding** the specific IDs and values your script needs. After that, we'll look at the cache dump, which contains almost everything the game has to offer. + +**Next:** [debug overlays →](./07-debug-overlays.md) diff --git a/docs/Guides/Tutorial/07-debug-overlays.md b/docs/Guides/Tutorial/07-debug-overlays.md new file mode 100644 index 00000000..224dd889 --- /dev/null +++ b/docs/Guides/Tutorial/07-debug-overlays.md @@ -0,0 +1,136 @@ +--- +title: 7. Debug Overlays +description: The in-game debug windows you use to find IDs, watch values change, and inspect the scene. +sidebar_position: 7 +--- + +import ContentBlock from '@site/src/components/ContentBlock'; + + + +Chapter 6 told you *what* the game exposes (varbits, NPCs, animations, etc.). This chapter shows you the **debug overlays** — small windows BotWithUs draws on top of the game that let you watch all of that live. + +Without overlays, finding "the varbit that flips when I pick up an item" means digging through the cache dump or printing values to the script console at runtime. The overlays cut that loop down to seconds. + +> ![All debug overlays open at once on the game client](../images/ALL_DEBUG.png) + + + + + +1. Inject into a client and open the **bot menu** (Insert). +2. Look for a **Debug** tab in the menu. +3. Toggle each overlay you want. They appear as draggable windows on top of the game. + +You can have many open at once. Drag them by their title bar; resize from the bottom-right corner. + + + + + +These are the workhorses. Pick the one that matches what you're trying to find. + +#### Player + +Shows your own character's current state: position, animation ID, world tile etc... + +**Use it when:** you want to know if your player is currently doing something. Watch `Animation ID` flip from `-1` (idle) to a specific number when you click a tree, attack a monster, drink a potion. That number is your "player is busy" check. + +#### NPC + +Lists every NPC currently in the scene with their name, ID, animation ID, and position. Click one to focus its details. + +**Use it when:** +- You need an NPC's **ID** to query for it cleanly. +- You're hunting the **animation ID** of a boss's special attack — open this, watch the boss, note which animation number flashes during the wind-up. + +#### Scene Object + +Same idea for objects in the world. Lists trees, doors, banks, etc. with their names, IDs, and right-click options. + +**Use it when:** +- You don't know what name BotWithUs uses for an object (the in-game tooltip and the API name don't always match). +- You're checking that an object's **interact options** are what you expect ("Mine", "Quick-mine", etc.). + +#### Varbit / Varp Watcher + +Lists varbits and varps with their current values. **Big asymmetry:** varps populate the watcher automatically — when one changes, you see it. Varbits **do not** auto-populate; the overlay can only show varbit IDs you've explicitly told it to watch (you cannot scan them live). + +**Use the watcher for varps when:** you need an unknown ID for a quest stage, a setting toggle, a phase counter. Procedure: filter to "changed recently" → do the in-game action → glance at which varp value flipped → that's your varp. + +**For varbits, the watcher is not the find tool.** You'll usually: + +- Look the varbit up in the **cache dump** (`latest_varbits.json`, chapter 8) by name/context, or +- Grab it from the BotWithUs **Discord data channels**, which keep curated lists for common things like potion timers and prayer flags, or +- If you've already found the *varp* with the watcher, drill down to which bit-range inside that varp moved — that's your varbit. + +#### Projectile Viewer + +Live list of projectiles flying through the scene with their source, target, and ID. + +**Use it when:** writing a boss script. You watch the boss attack and read the projectile ID off this overlay so your script can react to it next time. + +#### Component / Interface + +Lists every UI element ("interface component") on screen with its IDs and current text. + +**Use it when:** you need to click a specific button on an interface, or read text from the chat box, the bank window, a dialog. + +#### Spot Animation Viewer + +Lists active spot animations (those graphics-only effects that aren't tied to a character). + +**Use it when:** the cue you need is an effect on the ground (an AOE marker, an explosion, a teleport sparkle) rather than a character animation. + + + + + +This is the single most useful technique in the whole tutorial — for everything *except* varbits. You'll use it constantly. + +To find any unknown game value (varps, NPC fields, projectile IDs, animation IDs, scene-object state): + +1. Open the relevant overlay (varp watcher, NPC, projectile viewer — whichever matches). +2. Trigger the in-game event you care about — drink the potion, attack the boss, click the door. +3. Note which row(s) and what value they changed *to*. +4. That's your ID and target value. + +Example — *"What's the varp that holds my Overload buff state?"* + +1. Open the varp watcher, clear it. +2. Drink an Overload. +3. Multiple rows update, but since we're looking for a buff timer (a clear before/after) we can usually deduce which one matters. That row's ID is your "overload active" varp. +4. If you actually need the **varbit** inside that varp (because the engine packs several flags into one varp), open the cache dump's `latest_varbits.json` and find the varbits whose `varpId` matches — then pick the one whose bit-range covers the bits that flipped. + +:::warning Varbits don't show up here automatically +The find-by-changing trick **does not work for varbits** — the watcher can't scan them live, it only displays IDs you explicitly add. To find a varbit ID, use the **cache dump** (chapter 8), the BotWithUs **Discord data channels** (curated lists for prayers, potions, common buffs), or the "find the varp first, then drill down" approach above. +::: + + + + + +You'll find a lot of IDs while working on a script. Keep a plain `.txt` file open next to IntelliJ and dump them as you go: + +``` +Magic tree object_id: 38755 +Chop animation anim_id: 24937 +Boss special wind anim_id: 12345 +``` + +Future-you will thank present-you. A boss script can easily collect 30+ IDs. + + + + + +A few common gotchas: + +- **Multiple matching results.** "Magic tree" might appear twice (the visible tree, plus a stump waiting to respawn). The inspector shows them as separate rows — make sure your script's filter (`.name(...)`, `.option(...)`, `.hidden(false)`) picks the right one. +- **Varbit IDs differ between RuneScape versions.** When game updates ship (chapter 2), some IDs can also shift. If a working script suddenly doesn't, run the find-by-changing trick again to confirm the IDs you stored are still right. + + + +You can now (a) read the game and (b) find the specific IDs you need. The next chapter shows you how to skip the "look it up live" step entirely for static data — pulling it straight from the cache. + +**Next:** [cache data gathering →](./08-cache-data.md) diff --git a/docs/Guides/Tutorial/08-cache-data.md b/docs/Guides/Tutorial/08-cache-data.md new file mode 100644 index 00000000..9f5fef23 --- /dev/null +++ b/docs/Guides/Tutorial/08-cache-data.md @@ -0,0 +1,224 @@ +--- +title: 8. Cache Data Gathering +description: Pull item, NPC, and object info straight from the game cache so your script doesn't need hard-coded numbers. +sidebar_position: 8 +--- + +import ContentBlock from '@site/src/components/ContentBlock'; + + + +When the game client launches, it downloads (or reads from disk) a giant package of static data: every item's name, every NPC's stats, every object's right-click options, every interface layout. That package is **the cache**. + +The cache is *the* source of truth for things like: + +- "What's the official in-game name for item ID 12345?" +- "What right-click options does this object have?" +- "What stats does this NPC have?" +- "What's this varbit's underlying varp and bit range?" + +You can read all of this from inside your script without making the player walk over and look at it. + + + + + +Most beginner scripts get written with hard-coded numbers — and worse, hard-coded *lists*: + +```java +// Hardcoded raw fish IDs — fragile and incomplete. +// Every time Jagex adds a new fish, you have to find this list and update it. +int[] RAW_FISH = { 317, 321, 327, 331, 335, 341, 353, 359, 363, 371, 377, 383, 389, 395, 401, /* ...keep going forever... */ }; +``` + +That works until Jagex adds a new fish, renames an item, or you copy your script to a fresh setup and realize you missed twelve IDs. **Cache lookups** let you discover the answer at runtime instead — the cache is the single source of truth and you ask it directly. + +The most useful pattern is **iterating the cache to find every item matching a rule**. This loop is lifted straight from `CookWithUs.loadRawFishItems()`: + +```java +// Build the list of raw fish at script start by walking the cache. +List rawFishItems = new ArrayList<>(); +HashSet seenNames = new HashSet<>(); + +for (int i = 0; i < 60000; i++) { // walk every known item id + ItemType item = ConfigManager.getItemType(i); + if (item != null + && item.getCategory() == 57 // 57 = "Cooking" category + && item.getName().startsWith("Raw ")) { + if (seenNames.add(item.getName())) { // dedupe by name + rawFishItems.add(item); + } + } +} +println("Loaded " + rawFishItems.size() + " unique raw fish items from cache"); +``` + +When Jagex ships a new fish next month, your script picks it up automatically — no code change. You also don't have to *know* the IDs in advance; you describe what you want ("a Cooking-category item whose name starts with 'Raw '") and the cache hands you the matching set. Same trick works for "every overload variant", "every herb", "every uncharged piece of augmented gear" — anything where the in-game grouping is reflected in `getCategory()` or the item's name. + +Use simple by-name resolution when you only need one specific thing: + +```java +ItemType potion = ConfigManager.getItemType("Super restore (4)"); +int id = potion.getId(); +``` + +This is highly optional for one-off IDs but **strongly recommended** the moment your script needs a *list* of related items. + + + + + +BotWithUs exposes the cache through `ConfigManager` (or equivalent helpers — exact names vary by API version, but the *concepts* are stable). + +| Helper | Returns info about | Common use | +|---|---|---| +| **ItemType** | An item (name, examine text, value, stackable, etc.) | "Get the ID of the item called 'Magic logs'" | +| **NpcType** | An NPC's static info | "What name does NPC 1234 have?" | +| **ObjectType** | A scene object's static info | "What right-click options does this object define?" | +| **VarbitType** | A varbit's underlying structure | Maps a varbit to the varp it lives in and the bits it occupies | + +You'll usually use them as a *one-shot lookup* in `initialize()` (so you do the work once) and store the result in a variable but can also be used to check if specific item is for example notable on runtime. + + + + + +Walk the cache once in `initialize()` and store the result: + +```java +private List rawFishIds = new ArrayList<>(); +private int superRestore4Id; + +@Override +public boolean initialize() { + this.loopDelay = 600; + + // One-shot lookup for a specific item. + superRestore4Id = ConfigManager.getItemType("Super restore (4)").getId(); + + // Bulk discovery — same loop as above, this time storing IDs into a field. + rawFishIds = findItemIds(57, "Raw "); + + return super.initialize(); +} +``` + +Then in `onLoop()` you use the variables, never the literal numbers: + +```java +// Withdraw any raw fish we know about — no hardcoded list. +for (int id : rawFishIds) { + if (Bank.getCount(id) > 0) { + Bank.withdraw(id, 28); + break; + } +} + +if (Backpack.getCount(superRestore4Id) > 0) { + // we still have a 4-dose super restore in the bag +} +``` + +Your script now survives Jagex adding new fish, renaming an item, or shifting IDs in patches — as long as the **rule** stays the same ("Cooking category, name starts with 'Raw '"), you're fine. + +:::tip Names aren't unique +Some names appear on multiple things (e.g., several different "Bank chest" objects in different cities). When that happens, lookup-by-name returns *one* of them, which may not be the one you want. Add a second filter (location, animation, additional name keyword) or fall back to the ID printed by the debug overlay. +::: + + + + + +`ConfigManager` lets you read cache data **at runtime** from inside a script. There's a complementary tool: an **offline dump of the entire cache as JSON** that gets posted to the BotWithUs Discord and updated whenever a new RuneScape patch ships. You grep it from your editor instead of running the game and watching overlays. + +#### What's in the dump + +| File | What it holds | +|---|---| +| `items.json` | Every item: id, name, examine, stack flags, ground/widget actions | +| `npcs.json` | Every NPC: id, name, combat stats, animation IDs | +| `locations.json` | Every scene object ("location"): id, name, right-click options | +| `structs.json` | Game **structs** — small typed records the engine uses for everything from buff bar entries to potion timers (see below) | +| `enums.json` | Enum tables (e.g. lookup tables that map item id → noted item id) | +| `dbrows.json` | Database rows (modern Jagex content like Vault rooms ships through these) | +| `latest_varbits.json` / `latest_varps.json` | Every varbit/varp the cache currently knows about | +| `quests.json`, `achievements.json` | Quest and achievement metadata | +| `cs2/` | ~20k+ decompiled **CS2 client scripts** (TypeScript-like) — what the game itself runs when you click a button | +| `cs2_diff.json` | Diff vs the previous dump — perfect after game updates to see exactly what shifted | + +#### Where to get it + +Grab the latest dump from the BotWithUs Discord. The repo is auto-updated, so cloning/pulling always gives you the current cache without having to run an extractor yourself. + +#### When to reach for it instead of `ConfigManager` + +- **You don't have the game running** and just want to find an ID — grep `items.json` for the name and read the `id` field. +- **You want to see what *all* the entries look like**, not just one — e.g., listing every item whose name contains "Potion". +- **You're investigating game mechanics** — the cs2 scripts show you exactly what the game does when an interface button is clicked, which is often faster than trial-and-error in the live game. +- **A varbit ID changed after a patch** — `cs2_diff.json` and the dated `zips/` archives let you compare yesterday's cache to today's. +- **You need a struct's field layout** — buff bar entries, action bar slots, and many quest-stage records are structs; the dump shows every key that struct uses. + +#### Practical examples + +The dump is just text files, so any search tool works. The IntelliJ **Find in Files** dialog (`Ctrl+Shift+F`) over the dump folder is the easiest cross-platform option. + +```bash +# Bash / Git Bash / WSL — "What's the item ID for Magic logs?" +grep -B1 -A2 '"name": "Magic logs"' items.json + +# "Which NPCs have 'Araxxor' in their name?" +grep '"name": "[^"]*Araxxor' npcs.json +``` + +```powershell +# PowerShell equivalents — "What's the item ID for Magic logs?" +Select-String -Path items.json -Pattern '"name": "Magic logs"' -Context 1,2 + +# "Which CS2 scripts touch the bank?" +Select-String -Path cs2\*.ts -Pattern 'bank' -List +``` + +In a hard-stuck debugging session, this is often faster than starting the client and chasing values through the debug overlays — especially for static info like item names, struct fields, or "did this varbit ID change in the last patch?" + + + + + +Cache data is for **static** info — facts that don't change while the game is running. Don't reach for the cache when you need to know: + +- The **current animation** of a specific NPC (use the NPC's live state). +- The **current value** of a varbit (use `VarManager`). +- Whether the boss is *right now* in phase 3 (use a varbit or NPC animation). + +Rule of thumb: if the answer can change *during a session*, it's not in the cache. + + + + + +Two things to start using `ConfigManager` for immediately: + +1. **Replace any item ID literal in your code** with a `getItemType("name").getId()` lookup. Cleaner code, fewer bugs. +2. **Build a small "constants" class** that holds every cache-resolved ID for your script: + +```java +public class Cfg { + public static int SUPER_RESTORE_4_ID; + public static int OVERLOAD_6_ID; + public static List RAW_FISH_IDS = new ArrayList<>(); + + public static void load() { + SUPER_RESTORE_4_ID = ConfigManager.getItemType("Super restore (4)").getId(); + OVERLOAD_6_ID = ConfigManager.getItemType("Overload (6)").getId(); + RAW_FISH_IDS = findItemIds(57, "Raw "); // same iteration helper as above + } +} +``` + +Call `Cfg.load()` once in `initialize()`. Now your whole script reads `Cfg.SUPER_RESTORE_4_ID` everywhere — readable and centralised. + + + +You can now (a) write a script, (b) read live game state, (c) find unknown IDs, and (d) resolve static data cleanly. The last chapter takes your finished script and puts it on the marketplace. + +**Next:** [publishing to SDN →](./09-publishing-to-sdn.md) diff --git a/docs/Guides/Tutorial/09-publishing-to-sdn.md b/docs/Guides/Tutorial/09-publishing-to-sdn.md new file mode 100644 index 00000000..d46551f5 --- /dev/null +++ b/docs/Guides/Tutorial/09-publishing-to-sdn.md @@ -0,0 +1,213 @@ +--- +title: 9. Publishing to SDN +description: Take your finished local script and put it on the BotWithUs marketplace so others can use it. +sidebar_position: 9 +--- + +import ContentBlock from '@site/src/components/ContentBlock'; + + + +So far your script has lived in `BotWithUs\scripts\local\`. That folder is **personal** — only you and people you hand the `.jar` to can run it. + +The **SDN** (Script Distribution Network) is the central marketplace at [botwithus.net/sdn](https://botwithus.net/sdn). Once a script is on the SDN it shows up under **SDN Scripts** in everyone's bot menu when they subscribe to it (free or paid). + +Publishing is the last step — it's also a *gated* step, because every SDN script gets reviewed before it ships. + + + + + +Going through this list yourself before sending your script for review will speed up your release and avoid the most common rejection reasons. + +#### 1. Build system — Gradle or Maven + +Your script must build through a real, reproducible build system — either **Gradle** or **Maven** is fine, including the Maven multi-module workspace from [chapter 10](./10-multi-module-projects.md). + +- For Gradle: `build.gradle` or `build.gradle.kts` at the root, and `./gradlew build` (or `gradle build`) must produce a jar cleanly. +- For Maven: `pom.xml` at the root (or a parent POM that includes the script as a module), and `mvn package` must produce the final jar. +- **No pre-built jars.** Submissions without a working build setup are auto-rejected — reviewers must be able to compile from source. + +#### 2. Java version — must target Java 20 + +BotWithUs runs on **Java 20**. Scripts compiled against Java 21 or newer will crash the loader. + +Gradle: + +```groovy +java { + sourceCompatibility = JavaVersion.VERSION_20 + targetCompatibility = JavaVersion.VERSION_20 +} +``` + +Maven: + +```xml + + 20 + 20 + +``` + +To verify what your jar was actually compiled with: + +```bash +# Bash / Git Bash / WSL +javap -verbose -classpath build/libs/YourScript.jar YourMainClass | grep "major version" +``` + +```powershell +# PowerShell equivalent +javap -verbose -classpath build\libs\YourScript.jar YourMainClass | Select-String "major version" +``` + +The output must say **major version 64** (Java 20). Version 65 = Java 21 = rejection. + +#### 3. `script.ini` — required fields + +The file must exist at one of these paths: + +- `src/main/resources/script.ini`, or +- `script.ini` at the repo root. + +It must contain at least: + +```ini +version=1 +apiVersion=2 +``` + +Missing or malformed `script.ini` is an automatic block. + +#### 4. Jar size — under 15 MB + +If your compiled jar exceeds **15 MB**, contact a moderator before submitting. Don't submit anyway. + +#### 5. No sensitive data in the repo + +Don't commit API keys, tokens, passwords, or credentials. If your script needs config, load it from a local file that's listed in `.gitignore`. + +#### 6. Behavioral basics + +These aren't auto-rejected by tooling, but reviewers bounce on them constantly: + +- It works end-to-end for the activity it advertises — no "will TODO this later." +- It handles the user clicking Stop mid-action without crashing. +- It pauses or logs out gracefully on long disconnects — not spam-clicking a logged-out client. +- It doesn't print confidential info to the console (player names, paths, debug dumps). +- You've tested it on at least one fresh account — your dev account often has setup that hides bugs. +- You've read the [BotWithUs Community Rules](../../Rules/Community%20Rules.md) so your script doesn't violate any (e.g., no real-world trading flows). + + + + + +Before reviewers build your script, an automated security scan runs over the source. Hits on the patterns below either block submission outright or require explicit staff approval. + +#### Always blocked — no exceptions + +These are detected by Jagex's anti-cheat and cause player bans, so they never ship in an SDN script. + +| Pattern | Why | +|---|---| +| `java.awt.event.KeyEvent` | Anti-cheat detection → bans | +| `java.awt.event.MouseEvent` | Same | +| `java.awt.event.InputEvent` | Same | + +#### Blocked unless you get explicit staff approval + +These have legitimate uses (a local log file, a curated GE flow), but each one is reviewed case-by-case. **Disclose them in your submission message.** Trying to obfuscate or hide them is an automatic permanent ban. + +| Pattern | Category | +|---|---| +| `java.net.` / `HttpURLConnection` / `HttpClient` / `Socket` / `URLConnection` | Network access | +| `OkHttp` / `okhttp` / `retrofit` | Network libraries | +| `FileOutputStream` / `FileWriter` / `DataOutput` / `BufferedWriter` | File writing | +| `discord.com` / `webhook` / `telegram` / `pastebin` | External services | +| `trade` / `Trading` / `GrandExchange` | Economy manipulation | +| `Class.forName` / `getMethod` / `invoke` | Reflection | +| `Base64.decode` / `DatatypeConverter` | Encoded payloads | +| `Runtime.getRuntime` / `ProcessBuilder` / `exec(` | Process execution | +| `java.awt.Robot` / `java.awt.Toolkit` / `GraphicsEnvironment` / `MouseInfo` / `PointerInfo` | System input/screen | +| `ClassLoader` / `URLClassLoader` / `defineClass` / `Unsafe` | Dynamic class loading | +| `System.loadLibrary` / `System.load` | Native libraries | +| `native` method | Native code | + + + + + +Confirm all of the following before you send the link: + +- [ ] Gradle or Maven build system present, and `./gradlew build` / `mvn package` succeeds cleanly +- [ ] Jar compiles to Java 20 (major version 64) +- [ ] `script.ini` present with `version=1` and `apiVersion=2` +- [ ] Jar is under 15 MB +- [ ] No `java.awt.event.KeyEvent/MouseEvent/InputEvent` anywhere in source +- [ ] No network, file I/O, or reflection code — *or* you've disclosed it and have staff approval +- [ ] No credentials or tokens in the repo +- [ ] Script works correctly in your local BWU client +- [ ] You've read the BotWithUs rules and your script doesn't violate them + +If anything is "no," fix it first. Ask in the community Discord if you're unsure — much faster than going through a rejection cycle. + + + + + +The full GitHub-based submission workflow has its own page. Open it in a new tab and follow it end-to-end: + +> **→ [Script Submission Workflow](../ScriptSubmissionWorkflow.md)** + +That page covers: + +- Cloning the SDN scripts repo with **GitHub Desktop** (no command line required). +- Creating a **feature branch** for your script. +- Committing your code, syncing with `main`, **squashing**, and pushing. +- Opening the **pull request** for review. + +Come back here once you've read it. + + + + + +A `@Dev` or script reviewer will check: + +- **Does it build?** Pull the branch, run `./gradlew build` or `mvn package` (whichever your repo uses), expect success. +- **Does it match the description?** A "woodcutter" that secretly does combat fails review. +- **Is it safe?** No outbound network calls to unknown URLs, no reading random files, no obvious anti-detection trickery. +- **Is it a duplicate?** If five other scripts already do exactly the same thing, you may be asked to differentiate or contribute to the existing one. +- **Is the UI sane?** ImGui windows that crash on edge inputs or display garbage will get flagged. + +Reviews can take from a few hours to a few days depending on volume. Be patient and respond to feedback in the PR thread. + + + + + +Once your script is merged and built by SDN: + +1. It appears at [botwithus.net/sdn](https://botwithus.net/sdn) in the appropriate category. +2. Users **subscribe** to it on the website. +3. The script appears in their bot menu under **SDN Scripts** after a refresh. + +Updates work the same way: another feature branch, another PR, another (faster) review. + + + + + +You started with no software and no code. You now have: + +- A working BotWithUs install. +- A built skeleton script you can extend. +- A mental model for everything you can read off the running game. +- The debug tools to find any unknown ID. +- A clean way to resolve static data from the cache. +- A path to ship your work to other users. + +If you got value out of this tutorial, drop a thanks in [Discord](https://discord.gg/botwithus) — and consider passing it along to someone earlier in the journey. The community runs on it. + + diff --git a/docs/Guides/Tutorial/10-multi-module-projects.md b/docs/Guides/Tutorial/10-multi-module-projects.md new file mode 100644 index 00000000..5748b991 --- /dev/null +++ b/docs/Guides/Tutorial/10-multi-module-projects.md @@ -0,0 +1,418 @@ +--- +title: 10. Multi-Module Projects & Sharing Code (Advanced) +description: Scale beyond one-script-per-repo — a Maven multi-module workspace, your own reusable API, and many scripts in a single project. +sidebar_position: 10 +--- + +import ContentBlock from '@site/src/components/ContentBlock'; + + + +By chapter 9 you can ship a single script. This chapter is for when **one repo per script stops working** for you: + +- You're maintaining 10+ scripts and find yourself copy-pasting helpers (banking, prayer flicking, area logic) between them. +- You want a **shared API** — your own utility classes — that every script you write pulls in by name. +- You're tired of opening a different IDE window per script. + +The pattern below is the same one BotWithUs uses internally for the production `BwUMavenScripts` collection: **a single Maven workspace with one parent POM, a few category aggregators, individual script modules, and one or more shared API modules**. `mvn install` from the root builds *everything*, drops every jar into your scripts folder, and updates shared APIs across all scripts atomically. + + + + + +Chapter 5 used Gradle because that's what the official skeleton ships with. Multi-module is possible in either tool, but **Maven's multi-module story is dramatically more mature**, and the established BotWithUs production workspaces are all Maven. This chapter is Maven. + +Both Gradle and Maven are accepted for SDN submissions ([chapter 9](./09-publishing-to-sdn.md)), so the workspace you build here can ship straight to the SDN — no need to convert anything. + +The mental model is the same in both: + +- A **parent project** declares shared versions and plugins. +- **Module projects** inherit from the parent and either *aggregate* (point at sub-modules) or *produce* a jar. +- Modules can **depend on each other** by Maven coordinates — your script module pulls in your API module as if it were any third-party library. + + + + + +``` +MyScripts/ ← root (parent POM) +├── pom.xml ← parent: deps, plugins, repos +├── APIs/ +│ ├── pom.xml ← aggregator +│ └── MyApi/ +│ ├── pom.xml ← leaf: produces MyApi.jar +│ └── src/main/java/.../MyApi.java +├── Bosses/ +│ ├── pom.xml ← aggregator +│ ├── AraxxorScript/ +│ │ ├── pom.xml ← leaf: depends on MyApi +│ │ └── src/main/java/.../AraxxorScript.java +│ └── ZukScript/ +│ ├── pom.xml +│ └── src/main/java/... +└── Skilling/ + ├── pom.xml + ├── ChopScript/ + └── FishScript/ +``` + +Three layers: + +1. **Root** — declares Maven coordinates, the BotWithUs Nexus repo, shared dependency versions, and global plugins. `packaging=pom`. +2. **Category aggregators** (`Bosses`, `Skilling`, `APIs`) — group related modules. Also `packaging=pom`. Their only job is to list the leaf modules they contain. +3. **Leaf modules** — produce an actual jar. These are your scripts and your API libraries. + + + + + +The root `pom.xml`. Every leaf inherits from this. + +```xml + + + 4.0.0 + + com.example + MyScripts + 1.0-SNAPSHOT + pom + + + 20 + 20 + UTF-8 + + + + APIs + Bosses + Skilling + + + + + + + net.botwithus.rs3 + botwithus-api + [1.0.5,2.0.0) + + + net.botwithus.xapi.public + api + [1.2.6,2.0.0) + + + com.google.code.gson + gson + 2.10.1 + + + org.projectlombok + lombok + 1.18.28 + provided + + + + + + + botwithus + https://nexus.botwithus.net/repository/maven-releases/ + + + +``` + +Three things that earn their keep here: + +- **`packaging=pom`** says "this project doesn't produce a jar; it exists to be inherited from." +- **`dependencyManagement`** centralises versions. Child modules write `...net.botwithus.rs3...` *without* a ``; Maven fills it in from here. Bumping the BotWithUs API version is then a one-line change instead of edits across 40 modules. +- **``** points at the BotWithUs Nexus so `botwithus-api` and `xapi.public:api` resolve. You only need this once, in the parent. + +:::tip Version ranges +`[1.0.5,2.0.0)` means "any 1.x version ≥ 1.0.5". The BotWithUs API ships frequent compatible 1.x updates, so a range gives you new features automatically while shutting the door on a hypothetical breaking 2.0. +::: + + + + + +`Bosses/pom.xml` (and the equivalent for `Skilling`, `APIs`). These are pure routing — they tell Maven where the actual modules live. + +```xml + + 4.0.0 + + + com.example + MyScripts + 1.0-SNAPSHOT + ../pom.xml + + + Bosses + pom + + + AraxxorScript + ZukScript + + +``` + +That's it. The aggregator inherits from the parent and lists its children. Building `Bosses` builds every boss script underneath. + +You don't *need* this layer — the parent could list every script directly — but once you have 30+ scripts, grouping by category keeps things readable and lets you build "just the bosses" with `mvn -pl Bosses install`. + + + + + +This is where the real value of the multi-module setup lives. Build a module that holds your reusable code — banking helpers, area definitions, prayer flicker, whatever you'd otherwise paste into every script. + +`APIs/MyApi/pom.xml`: + +```xml + + 4.0.0 + + + com.example + MyScripts + 1.0-SNAPSHOT + ../../pom.xml + + + MyApi + + + + + net.botwithus.rs3 + botwithus-api + + + net.botwithus.xapi.public + api + + + org.projectlombok + lombok + provided + + + +``` + +`src/main/java/com/example/api/Banker.java`: + +```java +package com.example.api; + +import net.botwithus.api.game.hud.inventories.Backpack; +import net.botwithus.rs3.game.queries.builders.objects.SceneObjectQuery; +import net.botwithus.rs3.game.scene.entities.object.SceneObject; + +public final class Banker { + public static boolean depositAll() { + if (Backpack.isEmpty()) return true; + SceneObject chest = SceneObjectQuery.newQuery() + .name("Bank chest") + .option("Use") + .hidden(false) + .results() + .nearest(); + return chest != null && chest.interact("Use"); + } +} +``` + +Now any script in your workspace can call `Banker.depositAll()` after declaring `MyApi` as a dependency. + + + + + +`Bosses/AraxxorScript/pom.xml`: + +```xml + + 4.0.0 + + + com.example + MyScripts + 1.0-SNAPSHOT + ../../pom.xml + + + AraxxorScript + + + + net.botwithus.rs3 + botwithus-api + + + net.botwithus.xapi.public + api + + + + + com.example + MyApi + 1.0-SNAPSHOT + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.11.0 + + 20 + 20 + + + --enable-preview + + + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.4.1 + + + package + shade + + ${user.home}\BotWithUs\scripts\local\AraxxorScript.jar + + + + com.example + + net.botwithus.xapi.public + + + + + + + + + +``` + +The script source then uses your API straight up: + +```java +import com.example.api.Banker; +// ... +if (Backpack.isFull()) { + Banker.depositAll(); +} +``` + + + + + +The BotWithUs loader scans `~/BotWithUs/scripts/local/` for jars and loads each one independently. It does **not** recursively resolve Maven dependencies at runtime — if your script's jar references a class that isn't inside the jar, you'll get `NoClassDefFoundError` at startup. + +`maven-shade-plugin` solves this by **fattening** your final jar: it unpacks every dependency listed in `` and packs the classes back into a single jar. So the `AraxxorScript.jar` that lands in your local folder physically contains both your script code *and* `MyApi` classes. + +The rules are simpler than they look: + +- **Always include your own API modules.** They're not on the loader's classpath otherwise. Use a group-level include (`com.example`) so a single line covers all of them. +- **Always include `xapi.public:api`** (`net.botwithus.xapi.public`). It's a public extension API, not part of the loader, so your script needs its classes inside the jar. +- **Don't include `botwithus-api`** (`net.botwithus.rs3:botwithus-api`). That one *is* the loader's own API, supplied at runtime — bundling it creates duplicate-class conflicts. + +For comparison, the production `AraxxorWithUs/pom.xml` uses three group-includes — its own group, a third-party API its scripts share (`net.akisame.api`), and the public extension API: + +```xml + + + net.botwithus.api + net.akisame.api + net.botwithus.xapi.public + + +``` + +Same pattern, different group names. + +You can also put a default shade config in the parent POM and let leaves only override the `outputFile`. + + + + + +Once the structure is in place, your loop is: + +``` +# Build everything from the root — every script jar lands in ~/BotWithUs/scripts/local/ +mvn -f MyScripts/pom.xml install + +# Or build just one module + everything it depends on +mvn -pl Bosses/AraxxorScript -am install + +# Or one category +mvn -pl Bosses install +``` + +`-am` ("also make") is the magic flag for fast iteration: changing a function in `MyApi`, then running `mvn -pl Bosses/AraxxorScript -am install`, rebuilds `MyApi` *and* `AraxxorScript` and nothing else. Seconds, not a full workspace rebuild. + +In IntelliJ, the same thing is available as a **Maven tool window** — expand each module, double-click `package`, your script lands in the local folder. Keep the loader running; just hit **Refresh** in the SDN tab afterwards (chapter 3) and the new jar is picked up without restarting. + + + + + +Don't pre-emptively create `MyApi`. The right time is when you find yourself copying the same helper into a second script. Then: + +1. Create the API module (parent POM, basic dependencies — no shade plugin, since the API is consumed by other scripts, not loaded directly). +2. Move the helper there. +3. Add the API as a dependency in both consuming scripts. +4. From now on, fixes propagate to both with a single `mvn install`. + +Common things that earn their place in a shared API: + +- **Banking flows** — every skilling/combat script banks; the click sequence is identical. +- **Area definitions** — coordinates and `Area.Rectangular`s for common locations (Wars Retreat, GE, lobbies). +- **Prayer flicker** — the projectile-driven prayer switching from chapter 6, parameterised by attack IDs. +- **Action-bar helpers** — abstraction over `ActionBar` calls that retry, wait for the GCD, etc. +- **State machines / tree-script base classes** — if your scripts share a structure (e.g. all use a `LeafTask` tree), the framework belongs in the API. + +Keep things specific to a single script *out* of the shared API. Cross-cutting helpers belong there; boss-specific phase logic doesn't. + + + + + +- **`relativePath` wrong** — every leaf POM's `` must point at the actual parent POM. Off by one `..` and Maven warns "non-resolvable parent POM" during build. +- **Forgetting to bundle your API** — script jar builds, drops in folder, loads, and explodes the moment it calls `Banker.depositAll()` because shade didn't include `com.example:MyApi`. Add the include. +- **Bundling the BotWithUs loader API** — shading `net.botwithus.rs3:botwithus-api` into your jar gives you "duplicate class" errors at runtime when the loader's copy collides with yours. Bundle your own modules and `net.botwithus.xapi.public`, but never `botwithus-api` itself. +- **Using `LATEST` for your *own* inter-module deps** — for the BotWithUs Nexus artifacts (`botwithus-api`, `xapi.public:api`) the production scripts use `LATEST` and that's fine; the Nexus only ever serves a sane current version. But for *workspace-internal* deps (your `MyApi` module from a sibling script), pin the explicit version (`1.0-SNAPSHOT`). `LATEST` against a local module resolves unpredictably across reactor builds. +- **Editing `dependency-reduced-pom.xml`** — that file is auto-generated by shade. Edit `pom.xml` only. + + + +You now know how to scale a single tutorial script into a workspace of dozens — sharing code through your own API module, building everything with one command, and keeping versions consistent through a parent POM. That's the structure every serious BotWithUs author ends up with. + +Congrats — you've completed the zero-to-script tutorial. diff --git a/docs/Guides/Tutorial/_category.json b/docs/Guides/Tutorial/_category.json new file mode 100644 index 00000000..8e55d5bd --- /dev/null +++ b/docs/Guides/Tutorial/_category.json @@ -0,0 +1,8 @@ +{ + "label": "Zero-to-Script Tutorial", + "position": 1, + "link": { + "type": "generated-index", + "description": "A start-from-nothing walkthrough: install BotWithUs, launch your first session, write your first script, and read game data. No coding experience required." + } +} diff --git a/docs/Guides/Tutorial/index.md b/docs/Guides/Tutorial/index.md new file mode 100644 index 00000000..2fe25d25 --- /dev/null +++ b/docs/Guides/Tutorial/index.md @@ -0,0 +1,44 @@ +--- +title: Zero-to-Script Tutorial +description: A start-from-nothing walkthrough — from buying a sub to writing your first script. No coding experience required. +slug: /tutorial +sidebar_position: 0 +--- + +import React from 'react'; +import ContentBlock from '@site/src/components/ContentBlock'; + + + +This tutorial walks you all the way from **zero** to **your first working script**. It assumes you have **never written code before**. + +You don't have to read it in one sitting. Each chapter is short, focused on one thing, and links to the next. If you get stuck, ask in the [Discord](https://discord.gg/botwithus) — a screenshot of where you are usually unblocks you in seconds. + +### What you'll do + +1. **[Install & first launch](./01-install.md)** — buy a sub, install the loader, exclude it from antivirus, and run it for the first time. +2. **[Launching accounts](./02-launching-accounts.md)** — classic accounts via the account manager, launcher (Jagex) accounts, session limits. +3. **[The bot menu](./03-bot-menu.md)** — the Insert hotkey, Scripts vs SDN Scripts, subscribing to marketplace scripts. +4. **[Setting up your coding environment](./04-ide-setup.md)** — install IntelliJ, get JDK 20, clone the skeleton script. *Skip this if you only want to use marketplace scripts.* +5. **[Your first local script](./05-first-script.md)** — build the skeleton into a `.jar`, drop it in the right folder, and watch it run in the bot menu. +6. **[Reading the game](./06-reading-the-game.md)** — varbits, varps, varcs, scene objects, NPCs, projectiles, animations. What each one is *for*, in plain English. +7. **[Debug overlays](./07-debug-overlays.md)** — the in-game debug windows you use to find IDs and watch values change. +8. **[Cache data gathering](./08-cache-data.md)** — pulling item, NPC and object info from the game cache so your script doesn't need hard-coded numbers. +9. **[Publishing to SDN](./09-publishing-to-sdn.md)** — uploading your finished script so others can use it. +10. **[Multi-module projects](./10-multi-module-projects.md)** *(advanced)* — once you're maintaining several scripts, scale up to a Maven workspace with a shared API. + +### What you won't need + +- Prior coding experience (we'll explain Java basics as we hit them). +- Reverse-engineering or low-level know-how — BotWithUs gives you clean APIs so you can focus on **what** your script does, not **how** the game internally works. +- A fancy PC. Anything that runs RuneScape 3 will run a bot too. + +:::tip If you only want to *use* scripts +You can stop after **chapter 3**. Marketplace scripts cover most activities. Read chapter 4 onwards only if you want to write or modify your own. +::: + +:::info Keep the API reference open while you write +The full BotWithUs Java API is documented at **[static.botwithus.com/static/javadoc/net.botwithus.rs3/module-summary.html](https://static.botwithus.com/static/javadoc/net.botwithus.rs3/module-summary.html)**. This tutorial covers the *concepts* and the most common patterns, but every class you meet (`SceneObjectQuery`, `VarManager`, `Backpack`, `Execution`, etc.) has many more methods than we touch on. When you want to know "what else can I ask this object?" or "what overloads does this method have?", open the javadoc — it's the source of truth, generated straight from the API. +::: + + diff --git a/docs/Guides/images/ADD_ACCOUNT_LOADER.png b/docs/Guides/images/ADD_ACCOUNT_LOADER.png new file mode 100644 index 00000000..aff6a5fc Binary files /dev/null and b/docs/Guides/images/ADD_ACCOUNT_LOADER.png differ diff --git a/docs/Guides/images/ALL_DEBUG.png b/docs/Guides/images/ALL_DEBUG.png new file mode 100644 index 00000000..06fe1fbd Binary files /dev/null and b/docs/Guides/images/ALL_DEBUG.png differ diff --git a/docs/Guides/images/CHOOSE_SUB_PLAN.png b/docs/Guides/images/CHOOSE_SUB_PLAN.png new file mode 100644 index 00000000..40ebf2e5 Binary files /dev/null and b/docs/Guides/images/CHOOSE_SUB_PLAN.png differ diff --git a/docs/Guides/images/LOADER_INJECT.png b/docs/Guides/images/LOADER_INJECT.png new file mode 100644 index 00000000..eeea351a Binary files /dev/null and b/docs/Guides/images/LOADER_INJECT.png differ diff --git a/docs/Guides/images/OPEN_OVERLAY_AND_SCRIPTS.png b/docs/Guides/images/OPEN_OVERLAY_AND_SCRIPTS.png new file mode 100644 index 00000000..ac085a86 Binary files /dev/null and b/docs/Guides/images/OPEN_OVERLAY_AND_SCRIPTS.png differ diff --git a/docs/Guides/images/SCRIPT_OPTIONS.png b/docs/Guides/images/SCRIPT_OPTIONS.png new file mode 100644 index 00000000..5a5a2689 Binary files /dev/null and b/docs/Guides/images/SCRIPT_OPTIONS.png differ diff --git a/docs/Guides/images/SWITCH_TO_SDN_SCRIPTS_SCRIPTS_WINDOW.png b/docs/Guides/images/SWITCH_TO_SDN_SCRIPTS_SCRIPTS_WINDOW.png new file mode 100644 index 00000000..a800a22b Binary files /dev/null and b/docs/Guides/images/SWITCH_TO_SDN_SCRIPTS_SCRIPTS_WINDOW.png differ diff --git a/docs/Guides/images/TOP_UP.png b/docs/Guides/images/TOP_UP.png new file mode 100644 index 00000000..5580d64a Binary files /dev/null and b/docs/Guides/images/TOP_UP.png differ