diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 0e6094601d1..e673bb87046 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -11,27 +11,32 @@ jobs: permissions: contents: read - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - persist-credentials: false + env: + GRADLE_OPTS: "-Dfile:encoding=UTF-8" - - name: Cache - uses: actions/cache@v4 + steps: + - name: Cache glslang + uses: actions/cache@v5 with: path: | - ~/.m2/repository ~/.cache/runelite - key: ${{ runner.os }}-cache-${{ hashFiles('**/pom.xml', '**/build.sh', '**/pmd-ruleset.xml') }} + key: ${{ runner.os }}-cache-${{ hashFiles('ci/build.sh') }} restore-keys: | ${{ runner.os }}-cache- + - name: Checkout + uses: actions/checkout@v4 + - name: Set up JDK 11 uses: actions/setup-java@v4 with: distribution: temurin java-version: 11 - - name: Build + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 + with: + gradle-version: wrapper + + - name: Build and Test run: ./ci/build.sh diff --git a/.github/workflows/manual_nightly.yml b/.github/workflows/manual_nightly.yml index 13c6f689137..259562404e5 100644 --- a/.github/workflows/manual_nightly.yml +++ b/.github/workflows/manual_nightly.yml @@ -31,24 +31,30 @@ jobs: distribution: temurin java-version: 11 + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 + with: + gradle-version: wrapper + - name: Build Shaded JAR run: | COMMIT_SHA=$(git rev-parse --short HEAD) - mvn clean package -Dmicrobot.commit.sha=$COMMIT_SHA + ./gradlew :runelite-client:shadowJar -Pmicrobot.commit.sha=$COMMIT_SHA - - name: Read microbot.version from POM + - name: Read microbot.version from Gradle id: version run: | - V=$(mvn -q -f runelite-client/pom.xml help:evaluate \ - -Dexpression=microbot.version -DforceStdout) - # fallback to project.version if the property isn't defined - if [ -z "$V" ]; then - V=$(mvn -q -f runelite-client/pom.xml help:evaluate \ - -Dexpression=project.version -DforceStdout) - fi + V=$(./gradlew -q properties --console=plain | sed -n 's/^microbot.version: //p') + if [ -z "$V" ]; then V=$(grep '^microbot.version=' gradle.properties | cut -d= -f2); fi echo "version=$V" >> "$GITHUB_OUTPUT" echo "Using version: $V" + - name: Prepare artifact name + run: | + SRC=$(echo runelite-client/build/libs/client-*-shaded.jar) + DEST=runelite-client/build/libs/microbot-${{ steps.version.outputs.version }}.jar + cp "$SRC" "$DEST" + - name: Create Release uses: "marvinpinto/action-automatic-releases@latest" with: @@ -57,7 +63,7 @@ jobs: prerelease: false title: "Nightly Build" files: | - /home/runner/work/Microbot/Microbot/runelite-client/target/microbot-*.jar + /home/runner/work/Microbot/Microbot/runelite-client/build/libs/microbot-*.jar - name: Upload Jar to Hetzner @@ -66,7 +72,7 @@ jobs: host: ${{ secrets.PROD_HOST }} username: root key: ${{ secrets.PROD_SSH_KEY }} - source: runelite-client/target/microbot-*.jar + source: runelite-client/build/libs/microbot-*.jar target: /var/www/files/releases/microbot/nightly/ strip_components: 2 @@ -86,4 +92,4 @@ jobs: EOL - name: Deploy to Nexus - run: mvn deploy:deploy-file -DgroupId=com.microbot -DartifactId=client -Dversion=${{ steps.version.outputs.version }} -Dpackaging=jar -Dfile=runelite-client/target/microbot-${{ steps.version.outputs.version }}.jar -DrepositoryId=microbot-nightly -Durl=https://nexus.microbot.cloud/repository/microbot-nightly/ \ No newline at end of file + run: mvn deploy:deploy-file -DgroupId=com.microbot -DartifactId=client -Dversion=${{ steps.version.outputs.version }} -Dpackaging=jar -Dfile=runelite-client/build/libs/microbot-${{ steps.version.outputs.version }}.jar -DrepositoryId=microbot-nightly -Durl=https://nexus.microbot.cloud/repository/microbot-nightly/ diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index bdf4da37307..393c5100817 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -4,9 +4,6 @@ on: schedule: - cron: "0 0 * * *" -env: - version: ${microbot.version} - jobs: build: runs-on: ubuntu-latest # You can choose a different runner if needed @@ -31,24 +28,30 @@ jobs: distribution: temurin java-version: 11 + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 + with: + gradle-version: wrapper + - name: Build Shaded JAR run: | COMMIT_SHA=$(git rev-parse --short HEAD) - mvn clean package -Dmicrobot.commit.sha=$COMMIT_SHA + ./gradlew :runelite-client:shadowJar -Pmicrobot.commit.sha=$COMMIT_SHA - - name: Read microbot.version from POM + - name: Read microbot.version from Gradle id: version run: | - V=$(mvn -q -f runelite-client/pom.xml help:evaluate \ - -Dexpression=microbot.version -DforceStdout) - # fallback to project.version if the property isn't defined - if [ -z "$V" ]; then - V=$(mvn -q -f runelite-client/pom.xml help:evaluate \ - -Dexpression=project.version -DforceStdout) - fi + V=$(./gradlew -q properties --console=plain | sed -n 's/^microbot.version: //p') + if [ -z "$V" ]; then V=$(grep '^microbot.version=' gradle.properties | cut -d= -f2); fi echo "version=$V" >> "$GITHUB_OUTPUT" echo "Using version: $V" + - name: Prepare artifact name + run: | + SRC=$(echo runelite-client/build/libs/client-*-shaded.jar) + DEST=runelite-client/build/libs/microbot-${{ steps.version.outputs.version }}.jar + cp "$SRC" "$DEST" + - name: Create Release uses: "marvinpinto/action-automatic-releases@latest" with: @@ -57,7 +60,7 @@ jobs: prerelease: false title: "Nightly Build" files: | - /home/runner/work/Microbot/Microbot/runelite-client/target/microbot-*.jar + /home/runner/work/Microbot/Microbot/runelite-client/build/libs/microbot-*.jar - name: Upload Jar to Hetzner uses: appleboy/scp-action@v0.1.7 @@ -65,7 +68,7 @@ jobs: host: ${{ secrets.PROD_HOST }} username: root key: ${{ secrets.PROD_SSH_KEY }} - source: runelite-client/target/microbot-*.jar + source: runelite-client/build/libs/microbot-*.jar target: /var/www/files/releases/microbot/nightly/ strip_components: 2 @@ -85,4 +88,4 @@ jobs: EOL - name: Deploy to Nexus - run: mvn deploy:deploy-file -DgroupId=com.microbot -DartifactId=client -Dversion=${{ steps.version.outputs.version }} -Dpackaging=jar -Dfile=runelite-client/target/microbot-${{ steps.version.outputs.version }}.jar -DrepositoryId=microbot-nightly -Durl=https://nexus.microbot.cloud/repository/microbot-nightly/ + run: mvn deploy:deploy-file -DgroupId=com.microbot -DartifactId=client -Dversion=${{ steps.version.outputs.version }} -Dpackaging=jar -Dfile=runelite-client/build/libs/microbot-${{ steps.version.outputs.version }}.jar -DrepositoryId=microbot-nightly -Durl=https://nexus.microbot.cloud/repository/microbot-nightly/ diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8505dfbf74e..55dcb485f2e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -4,9 +4,6 @@ on: branches: - main -env: - version: ${microbot.version} - jobs: build: runs-on: ubuntu-latest # You can choose a different runner if needed @@ -23,25 +20,31 @@ jobs: distribution: temurin java-version: 11 + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 + with: + gradle-version: wrapper + - name: Build Shaded JAR run: | COMMIT_SHA=$(git rev-parse --short HEAD) - mvn clean package -Dmicrobot.commit.sha=$COMMIT_SHA + ./gradlew :runelite-client:shadowJar -Pmicrobot.commit.sha=$COMMIT_SHA - - name: Read microbot.version from POM + - name: Read microbot.version from Gradle id: version run: | - V=$(mvn -q -f runelite-client/pom.xml help:evaluate \ - -Dexpression=microbot.version -DforceStdout) - # fallback to project.version if the property isn't defined - if [ -z "$V" ]; then - V=$(mvn -q -f runelite-client/pom.xml help:evaluate \ - -Dexpression=project.version -DforceStdout) - fi + V=$(./gradlew -q properties --console=plain | sed -n 's/^microbot.version: //p') + if [ -z "$V" ]; then V=$(grep '^microbot.version=' gradle.properties | cut -d= -f2); fi echo "version=$V" >> "$GITHUB_OUTPUT" echo "Using version: $V" + - name: Prepare artifact name + run: | + SRC=$(echo runelite-client/build/libs/client-*-shaded.jar) + DEST=runelite-client/build/libs/microbot-${{ steps.version.outputs.version }}.jar + cp "$SRC" "$DEST" + - name: Create Release uses: "marvinpinto/action-automatic-releases@latest" with: @@ -50,7 +53,7 @@ jobs: prerelease: false title: "Release ${{ steps.version.outputs.version }}" files: | - /home/runner/work/Microbot/Microbot/runelite-client/target/microbot-*.jar + /home/runner/work/Microbot/Microbot/runelite-client/build/libs/microbot-*.jar - name: Upload Jar to Hetzner uses: appleboy/scp-action@v0.1.7 @@ -58,7 +61,7 @@ jobs: host: ${{ secrets.PROD_HOST }} username: root key: ${{ secrets.PROD_SSH_KEY }} - source: runelite-client/target/microbot-*.jar + source: runelite-client/build/libs/microbot-*.jar target: /var/www/files/releases/microbot/stable/ strip_components: 2 @@ -78,7 +81,7 @@ jobs: EOL - name: Deploy to Nexus - run: mvn deploy:deploy-file -DgroupId=com.microbot -DartifactId=client -Dversion=${{ steps.version.outputs.version }} -Dpackaging=jar -Dfile=runelite-client/target/microbot-${{ steps.version.outputs.version }}.jar -DrepositoryId=microbot-release -Durl=https://nexus.microbot.cloud/repository/microbot-release/ + run: mvn deploy:deploy-file -DgroupId=com.microbot -DartifactId=client -Dversion=${{ steps.version.outputs.version }} -Dpackaging=jar -Dfile=runelite-client/build/libs/microbot-${{ steps.version.outputs.version }}.jar -DrepositoryId=microbot-release -Durl=https://nexus.microbot.cloud/repository/microbot-release/ - name: Get Auth Token id: auth @@ -96,4 +99,4 @@ jobs: -H "Authorization: Bearer ${{ steps.auth.outputs.token }}" \ -H "Content-Type: application/json" \ -d "{\"version\":\"${{ steps.version.outputs.version }}\"}" \ - https://microbot.cloud/api/version/client \ No newline at end of file + https://microbot.cloud/api/version/client diff --git a/.gitignore b/.gitignore index 4827e401b67..62435815d16 100644 --- a/.gitignore +++ b/.gitignore @@ -1,29 +1,40 @@ -target -nbactions.xml -nb-configuration.xml -/nbproject/ -project.properties -*.iml +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### IntelliJ IDEA ### .idea/ -.project -.settings/ +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### Eclipse ### +.apt_generated .classpath -.vscode .factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ + +### Mac OS ### .DS_Store -/docs/diff.txt -/sqllite.pom.patch -/debug_temp_version.txt -/tordemon.patch -/commit.ps1 -/runelite-client_part1.txt -/commit_overview.txt -/runelite-client_part2.txt -/unnamed.patch -/sqllite.patch -/runelite-client_part3.txt -/hs_err_pid44304.log -/commit_overview.html -/commits.html -/runelite-client_part4.txt -/runelite-client_part5.txt +/cache/target/* diff --git a/.mvn/jvm.config b/.mvn/jvm.config deleted file mode 100644 index 67bd169f319..00000000000 --- a/.mvn/jvm.config +++ /dev/null @@ -1 +0,0 @@ --Xmx512m diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 00000000000..a9e40b66a18 --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2024, LlemonDuck + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +tasks.register("cleanAll") { + gradle.includedBuilds.forEach { build -> this@register.dependsOn(build.task(":clean")) } +} +tasks.register("buildAll") { + gradle.includedBuilds.forEach { build -> this@register.dependsOn(build.task(":build")) } +} +tasks.register("assembleAll") { + gradle.includedBuilds.forEach { build -> this@register.dependsOn(build.task(":assemble")) } +} +tasks.register("testAll") { + gradle.includedBuilds.forEach { build -> this@register.dependsOn(build.task(":test")) } +} +tasks.register("publishAll") { + this@register.dependsOn(gradle.includedBuild("cache").task(":publish")) + this@register.dependsOn(gradle.includedBuild("runelite-api").task(":publish")) + this@register.dependsOn(gradle.includedBuild("runelite-client").task(":publish")) + this@register.dependsOn(gradle.includedBuild("runelite-jshell").task(":publish")) +} diff --git a/cache/build.gradle.kts b/cache/build.gradle.kts new file mode 100644 index 00000000000..d9a04312a88 --- /dev/null +++ b/cache/build.gradle.kts @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2024, LlemonDuck + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +plugins { + java + `maven-publish` + antlr + alias(libs.plugins.lombok) +} + +lombok.version = libs.versions.lombok.get() + +java { + withJavadocJar() + withSourcesJar() +} + +dependencies { + antlr(libs.antlr.core) + implementation(libs.antlr.runtime) + + implementation(libs.guava) + implementation(libs.slf4j.api) + runtimeOnly(libs.slf4j.simple) + implementation(libs.commons.compress) + implementation(libs.gson) + implementation(libs.commons.cli) + implementation(libs.jna.core) + + testImplementation(libs.junit) + testImplementation(libs.rs.cache) +} + +// the gradle antlr plugin adds all of antlr to runtimeClasspath, +// workaround that https://github.com/gradle/gradle/issues/820 +configurations { + api { + setExtendsFrom(extendsFrom.filterNot { it == antlr.get() }) + } +} + +sourceSets { + main { + antlr { setSrcDirs(listOf("src/main/antlr4")) } + } +} + +publishing { + publications { + create("cache") { + from(components["java"]) + } + } +} + +tasks.processTestResources { + filesMatching("cache.properties") { + filter { it.replace("\${rs.version}", libs.versions.rs.get()) } + filter { it.replace("\${cache.version}", libs.versions.cache.get()) } + } +} + +tasks.test { + enabled = false + jvmArgs("-Xmx2048m") +} + +// everything from here down is accounting for antlr sources in varying ways +tasks.named("sourcesJar", Jar::class) { + dependsOn(tasks.generateGrammarSource) + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + exclude("net/runelite/cache/script/assembler/*.interp") + exclude("net/runelite/cache/script/assembler/*.tokens") +} + +tasks.javadoc { + exclude("net/runelite/cache/script/assembler/*.interp") + exclude("net/runelite/cache/script/assembler/*.tokens") +} + +tasks.checkstyleMain { + exclude("net/runelite/cache/script/assembler/*.java") +} + +tasks.generateGrammarSource { + arguments.addAll(listOf("-package", "net.runelite.cache.script.assembler")) +} + +afterEvaluate { + tasks.named("generateEffectiveLombokConfig") { + // lombok won't find anything in the antlr generated sources, but it looks in there regardless + // and gradle complains if you don't provide an explicit task dependency between the two + dependsOn(tasks.generateGrammarSource) + } +} diff --git a/cache/settings.gradle.kts b/cache/settings.gradle.kts new file mode 100644 index 00000000000..ccc997b18fe --- /dev/null +++ b/cache/settings.gradle.kts @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2024, LlemonDuck + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +rootProject.name = "cache" +apply(from = "../common.settings.gradle.kts") diff --git a/ci/build.sh b/ci/build.sh index a8092698243..e863cdf7bf6 100755 --- a/ci/build.sh +++ b/ci/build.sh @@ -15,4 +15,5 @@ if [ ! -f "${GLSLANG_ARCHIVE}" ] || [ ! -d "${GLSLANG_DIR}" ] || ! echo "${GLSLA unzip -o -q "${GLSLANG_ARCHIVE}" -d "${GLSLANG_DIR}" fi -mvn verify --settings ci/settings.xml -Dglslang.path="${GLSLANG_DIR}/bin/glslangValidator" +export ORG_GRADLE_PROJECT_glslangPath="$GLSLANG_DIR/bin/glslangValidator" +./gradlew ':buildAll' diff --git a/ci/settings.xml b/ci/settings.xml deleted file mode 100644 index 44c93408311..00000000000 --- a/ci/settings.xml +++ /dev/null @@ -1,280 +0,0 @@ - - - - - - - - - - false - - - - - - - - - - - - - - - - - - - - - runelite - repo - ${env.REPO_PASSWORD} - - - - - - - - - - - - - - - - - runelite - - - false - false - false - ${user.home}/.cache/runelite/pmd.cache - true - - - - - - - runelite - - diff --git a/common.settings.gradle.kts b/common.settings.gradle.kts new file mode 100644 index 00000000000..8a7d31ac2a6 --- /dev/null +++ b/common.settings.gradle.kts @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2024, LlemonDuck + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +dependencyResolutionManagement { + repositories { + maven(uri("https://repo.runelite.net")) { + name = "rrn" + + content { + includeGroupAndSubgroups("net.runelite") + } + } + mavenCentral { + content { excludeGroupAndSubgroups("net.runelite") } + } + } + + versionCatalogs { + create("libs") { + from(files("./libs.versions.toml")) + } + } +} + +// set up some defaults +val rootProps = file("./gradle.properties").inputStream().use { stream -> + java.util.Properties().apply { load(stream) } +} +val checkstyleDir = file("./config/checkstyle/") +gradle.beforeProject { + apply(plugin = "idea") + apply(plugin = "checkstyle") + group = rootProps["project.build.group"] as String + version = rootProps["project.build.version"] as String + + tasks.withType { + options.encoding = "UTF-8" + options.release = 11 + } +} + +gradle.afterProject { + tasks.withType { + (this.options as StandardJavadocDocletOptions).apply { + quiet() + encoding("UTF-8") + use(true) + bottom("Copyright © 2014") + links( + "https://docs.oracle.com/en/java/javase/11/docs/api/" + ) + } + } + + // shared checkstyle config + extensions.findByType()?.run { + toolVersion = "8.3" + configDirectory = file("../config/checkstyle") + } + + // shared publishing config + tasks.withType { enabled = false } + extensions.findByType()?.run { + repositories { + maven(uri(providers.gradleProperty("rrnUrl").getOrElse("https://repo.runelite.net"))) { + name = "rrn" + if (url.scheme != "file") { + credentials(PasswordCredentials::class) { + username = providers.gradleProperty("rrnPublishUsername").orElse(providers.gradleProperty("rrnUsername")).getOrElse("") + password = providers.gradleProperty("rrnPublishPassword").orElse(providers.gradleProperty("rrnPassword")).getOrElse("") + } + } + } + } + } + + // produce reproducible outputs + tasks.withType { + isPreserveFileTimestamps = false + isReproducibleFileOrder = true + } + + extensions.findByType()?.run { + module { + isDownloadSources = true + isDownloadJavadoc = true + } + } +} diff --git a/checkstyle.xml b/config/checkstyle/checkstyle.xml similarity index 100% rename from checkstyle.xml rename to config/checkstyle/checkstyle.xml diff --git a/suppressions.xml b/config/checkstyle/suppressions.xml similarity index 87% rename from suppressions.xml rename to config/checkstyle/suppressions.xml index 962c26ceb0d..004da9e22e2 100644 --- a/suppressions.xml +++ b/config/checkstyle/suppressions.xml @@ -28,5 +28,8 @@ "https://checkstyle.org/dtds/suppressions_1_1.dtd"> + + + diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 00000000000..1d82f998209 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,38 @@ +# +# Copyright (c) 2024, LlemonDuck +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +org.gradle.jvmargs=-Dfile.encoding=UTF-8 -Xmx1024m +org.gradle.parallel=true +org.gradle.caching=true + +project.build.group=net.runelite +project.build.version=1.12.10 + +glslang.path= +microbot.version=2.0.61 +microbot.commit.sha=nogit +microbot.repo.url=http://138.201.81.246:8081/repository/microbot-snapshot/ +microbot.repo.username= +microbot.repo.password= diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml new file mode 100644 index 00000000000..f7621c52004 --- /dev/null +++ b/gradle/verification-metadata.xml @@ -0,0 +1,469 @@ + + + + false + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000000..e6441136f3d Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000000..a9c19e29fb7 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +#Tue Jul 02 14:53:12 EDT 2024 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-all.zip +distributionSha256Sum=f8b4f4772d302c8ff580bc40d0f56e715de69b163546944f787c87abf209c961 +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100755 index 00000000000..b740cf13397 --- /dev/null +++ b/gradlew @@ -0,0 +1,249 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 00000000000..25da30dbdee --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/libs.versions.toml b/libs.versions.toml new file mode 100644 index 00000000000..55ee7859c4b --- /dev/null +++ b/libs.versions.toml @@ -0,0 +1,89 @@ +# +# Copyright (c) 2024, LlemonDuck +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +[versions] +rs = "235" +cache = "165" +asm = "9.0" + +flatlaf = "3.2.5-rl4" +guice = "4.1.0" +lombok = "1.18.30" +logback = "1.2.9" +lwjgl = "3.3.2" +slf4j = "1.7.25" +antlr = "4.13.1" + + +[libraries] +rs-vanilla = { module = "net.runelite.rs:vanilla", version.ref = "rs" } +rs-cache = { module = "net.runelite.rs:cache", version.ref = "cache" } + +flatlaf-core = { module = "net.runelite:flatlaf", version.ref = "flatlaf" } +flatlaf-extras = { module = "net.runelite:flatlaf-extras", version.ref = "flatlaf" } +rl-http-api = "net.runelite.arn:http-api:1.2.21" +rl-awt = "net.runelite:rlawt:1.7" +rl-discord = "net.runelite:discord:1.4" +rl-orange = "net.runelite:orange-extensions:1.1" + +antlr-core = { module = "org.antlr:antlr4", version.ref="antlr" } +antlr-runtime = { module = "org.antlr:antlr4-runtime", version.ref="antlr" } +commons-compress = "org.apache.commons:commons-compress:1.10" +commons-text = "org.apache.commons:commons-text:1.2" +commons-cli = "commons-cli:commons-cli:1.3.1" +fife-rsyntaxtextarea = "com.fifesoft:rsyntaxtextarea:3.1.2" +fife-autocomplete = "com.fifesoft:autocomplete:3.1.1" +findbugs = "com.google.code.findbugs:jsr305:3.0.2" +gson = "com.google.code.gson:gson:2.8.5" +guava = "com.google.guava:guava:23.2-jre" +guice-core = { module = "com.google.inject:guice", version.ref = "guice" } +guice-testlib = { module = "com.google.inject.extensions:guice-testlib", version.ref = "guice" } +guice-grapher = { module = "com.google.inject.extensions:guice-grapher", version.ref = "guice" } +hamcrest = "org.hamcrest:hamcrest-library:1.3" +javapoet = "com.squareup:javapoet:1.13.0" +javax-inject = "javax.inject:javax.inject:1" +jetbrains-annotations = "org.jetbrains:annotations:23.0.0" +jna-core = "net.java.dev.jna:jna:5.9.0" +jna-platform = "net.java.dev.jna:jna-platform:5.9.0" +jopt = "net.sf.jopt-simple:jopt-simple:5.0.1" +junit = "junit:junit:4.12" +logback-classic = { module = "ch.qos.logback:logback-classic", version.ref = "logback" } +logback-core = { module = "ch.qos.logback:logback-core", version.ref = "logback" } +lwjgl-core = { module = "org.lwjgl:lwjgl", version.ref = "lwjgl" } +lwjgl-opengl = { module = "org.lwjgl:lwjgl-opengl", version.ref = "lwjgl" } +lwjgl-opencl = { module = "org.lwjgl:lwjgl-opencl", version.ref = "lwjgl" } +mockito = "org.mockito:mockito-core:3.1.0" +okhttp-mockserver = "com.squareup.okhttp3:mockwebserver:3.14.9" +protobuf = "com.google.protobuf:protobuf-javalite:3.21.12" +slf4j-api = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" } +slf4j-simple = { module = "org.slf4j:slf4j-simple", version.ref = "slf4j" } +tomlj = "org.tomlj:tomlj:1.1.0" +asm-core = { module = "org.ow2.asm:asm", version.ref = "asm" } +asm-util = { module = "org.ow2.asm:asm-util", version.ref = "asm" } +asm-commons = { module = "org.ow2.asm:asm-commons", version.ref = "asm" } + + + +[plugins] +lombok = { id = "io.freefair.lombok", version = "8.10.2" } diff --git a/runelite-api/build.gradle.kts b/runelite-api/build.gradle.kts new file mode 100644 index 00000000000..4efc0b7307f --- /dev/null +++ b/runelite-api/build.gradle.kts @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2024, LlemonDuck + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +plugins { + java + `maven-publish` + checkstyle + alias(libs.plugins.lombok) + + id("net.runelite.runelite-gradle-plugin.component") +} + +lombok.version = libs.versions.lombok.get() + +java { + withJavadocJar() + withSourcesJar() +} + +dependencies { + implementation(libs.slf4j.api) + compileOnly(libs.findbugs) + + compileOnly(libs.jetbrains.annotations) + + testImplementation(libs.junit) + testRuntimeOnly(libs.slf4j.simple) +} + +val runtimeJar = tasks.register("runtimeJar") { + this@register.group = BasePlugin.BUILD_GROUP + + from(sourceSets.main.get().output) + archiveClassifier = "runtime" + + /* from JLS 13.1.3: references to a static field that is a constant variable (§4.12.4) must be resolved + at compile time to the value denoted by the constant variable's initializer *and* no references to the + field should be present in the code in a binary file. + + This permits us to remove the classes containing only these types of fields at runtime */ + exclude("net/runelite/api/annotations/*.class") + exclude("net/runelite/api/clan/ClanID.class") + exclude("net/runelite/api/dbtable/DBTableID*.class") + exclude("net/runelite/api/widgets/ComponentID.class") + exclude("net/runelite/api/widgets/InterfaceID.class") + exclude("net/runelite/api/widgets/ItemQuantityMode.class") + exclude("net/runelite/api/widgets/WidgetID*.class") + exclude("net/runelite/api/widgets/WidgetModalMode.class") + exclude("net/runelite/api/widgets/WidgetModelType.class") + exclude("net/runelite/api/widgets/WidgetPositionMode.class") + exclude("net/runelite/api/widgets/WidgetSizeMode.class") + exclude("net/runelite/api/widgets/WidgetTextAlignment.class") + exclude("net/runelite/api/widgets/WidgetType.class") + exclude("net/runelite/api/AnimationID.class") + exclude("net/runelite/api/CollisionDataFlag.class") + exclude("net/runelite/api/EnumID.class") + exclude("net/runelite/api/FontID.class") + exclude("net/runelite/api/GraphicID.class") + exclude("net/runelite/api/HintArrowType.class") + exclude("net/runelite/api/HitsplatID.class") + exclude("net/runelite/api/ItemID.class") + exclude("net/runelite/api/KeyCode.class") + exclude("net/runelite/api/NpcID.class") + exclude("net/runelite/api/NullItemID.class") + exclude("net/runelite/api/NullNpcID.class") + exclude("net/runelite/api/NullObjectID.class") + exclude("net/runelite/api/ObjectID.class") + exclude("net/runelite/api/Opcodes.class") + exclude("net/runelite/api/ParamID.class") + exclude("net/runelite/api/ScriptID.class") + exclude("net/runelite/api/SettingID.class") + exclude("net/runelite/api/SkullIcon.class") + exclude("net/runelite/api/SoundEffectID.class") + exclude("net/runelite/api/SoundEffectVolume.class") + exclude("net/runelite/api/SpriteID.class") + exclude("net/runelite/api/StructID.class") + exclude("net/runelite/api/Varbits.class") + exclude("net/runelite/api/VarClientInt.class") + exclude("net/runelite/api/VarClientStr.class") + exclude("net/runelite/api/VarPlayer.class") + exclude("net/runelite/api/gameval/*.class") +} +tasks.assemble { dependsOn(runtimeJar) } + +publishing { + publications { + create("api") { + from(components["java"]) + artifact(runtimeJar) { classifier = "runtime" } + } + } +} + +tasks.withType { + inputFile = file("src/main/interfaces/interfaces.toml") + outputDirectory = file("build/generated/sources/runelite/java/main") +} + +tasks.checkstyleMain { + exclude("net/runelite/api/widgets/ComponentID.java") + exclude("net/runelite/api/widgets/InterfaceID.java") +} + +tasks.javadoc { + title = "RuneLite API ${project.version} API" + + exclude( + "net/runelite/api/gameval/**", + "net/runelite/api/AnimationID.java", + "net/runelite/api/ItemID.java", + "net/runelite/api/NullItemID.java", + "net/runelite/api/ObjectID.java", + "net/runelite/api/NullObjectID.java", + "net/runelite/api/NpcID.java", + "net/runelite/api/NullNpcID.java", + ) +} diff --git a/runelite-api/settings.gradle.kts b/runelite-api/settings.gradle.kts new file mode 100644 index 00000000000..b640a5c2f81 --- /dev/null +++ b/runelite-api/settings.gradle.kts @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2024, LlemonDuck + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +rootProject.name = "runelite-api" +apply(from = "../common.settings.gradle.kts") + +includeBuild("../runelite-gradle-plugin") diff --git a/runelite-client/build.gradle.kts b/runelite-client/build.gradle.kts new file mode 100644 index 00000000000..f1c6f03a770 --- /dev/null +++ b/runelite-client/build.gradle.kts @@ -0,0 +1,241 @@ +/* + * Copyright (c) 2024, LlemonDuck + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import java.io.ByteArrayOutputStream +import java.util.Properties + +fun loadRootProperty(name: String): String? { + val propsFile = file("../gradle.properties") + if (!propsFile.isFile) { + return null + } + val props = Properties() + propsFile.inputStream().use { props.load(it) } + return props.getProperty(name) +} + +plugins { + java + `java-library` + `maven-publish` + pmd + alias(libs.plugins.lombok) + + id("net.runelite.runelite-gradle-plugin.assemble") + id("net.runelite.runelite-gradle-plugin.index") + id("net.runelite.runelite-gradle-plugin.jarsign") +} + +lombok.version = libs.versions.lombok.get() + +java { + withSourcesJar() +} + +dependencies { + api("net.runelite:runelite-api:${project.version}") + implementation("net.runelite:jshell:${project.version}") + runtimeOnly("net.runelite:injected-client:${project.version}") + + api(libs.rl.http.api) + api(libs.rl.discord) + api(libs.rl.awt) + compileOnly(libs.rl.orange) + + api(libs.slf4j.api) + api(libs.logback.classic) { + exclude("org.slf4j", "slf4j-api") + } + api(libs.jopt) + api(libs.guava) { + exclude("com.google.code.findbugs", "jsr305") + exclude("com.google.errorprone", "error_prone_annotations") + exclude("com.google.j2objc", "j2objc-annotations") + exclude("org.codehaus.mojo", "animal-sniffer-annotations") + } + api(variantOf(libs.guice.core) { classifier("no_aop") }) { + exclude("com.google.guava", "guava") + } + api(libs.gson) + api(libs.flatlaf.core) + implementation(libs.flatlaf.extras) + api(libs.commons.text) + api(libs.jna.core) + api(libs.jna.platform) + api(libs.findbugs) + compileOnly(libs.jetbrains.annotations) + api(libs.protobuf) + api(libs.lwjgl.core) + api(libs.lwjgl.opengl) + api(libs.lwjgl.opencl) + implementation(libs.asm.core) + implementation(libs.asm.util) + implementation(libs.asm.commons) + + for (platform in listOf( + "linux", + "linux-arm64", + "macos", + "macos-arm64", + "windows-x86", + "windows", + "windows-arm64", + )) { + runtimeOnly(variantOf(libs.lwjgl.core) { classifier("natives-$platform") }) + runtimeOnly(variantOf(libs.lwjgl.opengl) { classifier("natives-$platform") }) + } + + testImplementation(libs.junit) + testImplementation(libs.hamcrest) + testImplementation(libs.mockito) + testImplementation(libs.guice.testlib) + testImplementation(libs.guice.grapher) + testImplementation(libs.okhttp.mockserver) +} + +val shadowJar = tasks.register("shadowJar") { + dependsOn(configurations.runtimeClasspath) + manifest { + attributes["Main-Class"] = "net.runelite.client.RuneLite" + } + + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + + from(sourceSets.main.get().output) + from(configurations.runtimeClasspath.get().map { if (it.isDirectory) it else zipTree(it) }) + + exclude( + "META-INF/INDEX.LIST", + "META-INF/*.SF", + "META-INF/*.DSA", + "META-INF/*.RSA", + "**/module-info.class" + ) + + group = BasePlugin.BUILD_GROUP + archiveClassifier = "shadow" + archiveFileName = project.name + "-" + project.version + "-shaded.jar" +} +tasks.assemble { dependsOn(shadowJar) } + +publishing { + publications { + create("runelite-client") { + from(components["java"]) + artifact(shadowJar) { classifier = "shaded" } + } + } + repositories { + val microbotRepoUrl = providers.gradleProperty("microbot.repo.url").orNull + if (!microbotRepoUrl.isNullOrBlank()) { + maven(uri(microbotRepoUrl)) { + name = "microbot" + credentials(PasswordCredentials::class) { + username = providers.gradleProperty("microbot.repo.username").getOrElse("") + password = providers.gradleProperty("microbot.repo.password").getOrElse("") + } + } + } + } +} + +val assemble = tasks.withType { + scriptDirectory = file("src/main/scripts") + outputDirectory = sourceSets.main.map { File(it.output.resourcesDir, "runelite") } + componentsFile = file("../runelite-api/src/main/interfaces/interfaces.toml") +} + +tasks.withType { + archiveOverlayDirectory = assemble.single().outputDirectory + indexFile = archiveOverlayDirectory.file("index") +} + +tasks.processResources { + val commit = ByteArrayOutputStream() + exec { + commandLine("git", "rev-parse", "--short=7", "HEAD") + standardOutput = commit + } + + val dirty = ByteArrayOutputStream() + exec { + commandLine("git", "status", "--short") + standardOutput = dirty + } + + val fallbackMicrobotVersion = loadRootProperty("microbot.version") + val microbotVersion = providers.gradleProperty("microbot.version") + .orElse(fallbackMicrobotVersion ?: "0.0.0") + .get() + val microbotCommit = providers.gradleProperty("microbot.commit.sha").getOrElse(commit.toString().trim()) + + // Ensure task reruns when injected values change + inputs.property("microbotVersion", microbotVersion) + inputs.property("microbotCommit", microbotCommit) + + filesMatching("net/runelite/client/runelite.properties") { + filter { it.replace("\${project.version}", project.version.toString()) } + filter { it.replace("\${git.commit.id.abbrev}", commit.toString().trim()) } + filter { it.replace("\${git.dirty}", dirty.toString().isNotBlank().toString()) } + filter { it.replace("\${microbot.version}", microbotVersion) } + filter { it.replace("\${microbot.commit.sha}", microbotCommit) } + } +} + +tasks.compileJava { + options.isFork = true +} + +tasks.jar { + exclude("**/.clang-format") +} + +pmd { + toolVersion = "7.2.0" + ruleSetFiles("./pmd-ruleset.xml") + isConsoleOutput = true + incrementalAnalysis = true + isIgnoreFailures = false + threads = Runtime.getRuntime().availableProcessors() +} +tasks.pmdMain { + exclude("**/RuntimeTypeAdapterFactory.java") + exclude("**/net/runelite/client/party/Party.java") +} +tasks.pmdTest { enabled = false } + +tasks.checkstyleMain { + exclude("net/runelite/client/util/RuntimeTypeAdapterFactory.java") // vendored + exclude("net/runelite/client/party/Party.java") // generated by protobuf +} + +tasks.withType { + enabled = false + systemProperty("glslang.path", providers.gradleProperty("glslangPath").getOrElse("")) +} + +tasks.javadoc { + title = "RuneLite Client ${project.version} API" +} diff --git a/runelite-client/pom.xml b/runelite-client/pom.xml index 911307d154b..e7df1de040b 100644 --- a/runelite-client/pom.xml +++ b/runelite-client/pom.xml @@ -41,7 +41,7 @@ nogit false false - 2.0.60 + 2.0.61 nogit diff --git a/runelite-client/settings.gradle.kts b/runelite-client/settings.gradle.kts new file mode 100644 index 00000000000..9c78c29f5f8 --- /dev/null +++ b/runelite-client/settings.gradle.kts @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2024, LlemonDuck + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +rootProject.name = "client" +apply(from = "../common.settings.gradle.kts") + +includeBuild("../runelite-gradle-plugin") +includeBuild("../runelite-jshell") diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java index 32941bac896..4744cb2f184 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java @@ -1342,14 +1342,13 @@ private void rebuild(WorldView wv) private void prepareInterfaceTexture(int canvasWidth, int canvasHeight) { - long bufferSize = canvasWidth * canvasHeight * 4L; if (canvasWidth != lastCanvasWidth || canvasHeight != lastCanvasHeight) { lastCanvasWidth = canvasWidth; lastCanvasHeight = canvasHeight; glBindBuffer(GL_PIXEL_UNPACK_BUFFER, interfacePbo); - glBufferData(GL_PIXEL_UNPACK_BUFFER, bufferSize, GL_STREAM_DRAW); + glBufferData(GL_PIXEL_UNPACK_BUFFER, canvasWidth * canvasHeight * 4L, GL_STREAM_DRAW); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); glBindTexture(GL_TEXTURE_2D, interfaceTexture); @@ -1363,7 +1362,7 @@ private void prepareInterfaceTexture(int canvasWidth, int canvasHeight) final int height = bufferProvider.getHeight(); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, interfacePbo); - ByteBuffer interfaceBuf = glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, bufferSize, GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT); + ByteBuffer interfaceBuf = glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY); if (interfaceBuf != null) { interfaceBuf diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootReceived.java b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootReceived.java index ecaa869a2a0..7cfaae0f5f5 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootReceived.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootReceived.java @@ -42,4 +42,5 @@ public class LootReceived private LootRecordType type; private Collection items; private int amount; + private Object metadata; } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java index 6c60cf25321..8f83751806b 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java @@ -694,7 +694,7 @@ void addLoot(@NonNull String name, int combatLevel, LootRecordType type, Object queuedLoots.add(lootRecord); } - eventBus.post(new LootReceived(name, combatLevel, type, items, amount)); + eventBus.post(new LootReceived(name, combatLevel, type, items, amount, metadata)); } private Integer getLootWorldId() diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/VoxPlugins/schedulable/example/LocationStartNotificationOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/VoxPlugins/schedulable/example/LocationStartNotificationOverlay.java deleted file mode 100644 index fc89dbd585d..00000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/VoxPlugins/schedulable/example/LocationStartNotificationOverlay.java +++ /dev/null @@ -1,145 +0,0 @@ -package net.runelite.client.plugins.microbot.VoxPlugins.schedulable.example; - -import net.runelite.api.coords.WorldPoint; -import net.runelite.client.plugins.microbot.Microbot; -import net.runelite.client.plugins.microbot.util.player.Rs2Player; -import net.runelite.client.ui.overlay.Overlay; -import net.runelite.client.ui.overlay.OverlayLayer; -import net.runelite.client.ui.overlay.OverlayPosition; -import net.runelite.client.ui.overlay.OverlayPriority; -import net.runelite.client.ui.overlay.components.LineComponent; -import net.runelite.client.ui.overlay.components.PanelComponent; -import net.runelite.client.ui.overlay.components.TitleComponent; - -import java.awt.*; - -/** - * Displays information about location-based start conditions - */ -public class LocationStartNotificationOverlay extends Overlay { - private final SchedulableExamplePlugin plugin; - private final SchedulableExampleConfig config; - private final PanelComponent panelComponent = new PanelComponent(); - - public LocationStartNotificationOverlay(SchedulableExamplePlugin plugin, SchedulableExampleConfig config) { - super(plugin); - setPosition(OverlayPosition.TOP_LEFT); - setLayer(OverlayLayer.ABOVE_SCENE); - setPriority(OverlayPriority.MED); - this.plugin = plugin; - this.config = config; - } - - @Override - public Dimension render(Graphics2D graphics) { - if (!Microbot.isLoggedIn() || !config.enableLocationStartCondition()) { - return null; - } - - panelComponent.getChildren().clear(); - - // Show title - panelComponent.getChildren().add(TitleComponent.builder() - .text("Location Conditions") - .color(Color.WHITE) - .build()); - - if (config.locationStartType() == SchedulableExampleConfig.LocationStartType.BANK) { - // Bank location information - panelComponent.getChildren().add(LineComponent.builder() - .left("Type:") - .right("Bank Location") - .build()); - - panelComponent.getChildren().add(LineComponent.builder() - .left("Target:") - .right(config.bankStartLocation().name()) - .build()); - - panelComponent.getChildren().add(LineComponent.builder() - .left("Distance:") - .right(config.bankDistance() + " tiles") - .build()); - - // Check and show if condition is met - boolean inRange = isNearBank(); - Color statusColor = inRange ? Color.GREEN : Color.RED; - panelComponent.getChildren().add(LineComponent.builder() - .left("Status:") - .right(inRange ? "In Range" : "Out of Range") - .rightColor(statusColor) - .build()); - - } else if (config.locationStartType() == SchedulableExampleConfig.LocationStartType.CUSTOM_AREA) { - // Custom area information - panelComponent.getChildren().add(LineComponent.builder() - .left("Type:") - .right("Custom Area") - .build()); - - if (config.customAreaActive() && config.customAreaCenter() != null) { - WorldPoint center = config.customAreaCenter(); - panelComponent.getChildren().add(LineComponent.builder() - .left("Center:") - .right(center.getX() + ", " + center.getY() + ", " + center.getPlane()) - .build()); - - panelComponent.getChildren().add(LineComponent.builder() - .left("Radius:") - .right(config.customAreaRadius() + " tiles") - .build()); - - // Check and show if condition is met - boolean inArea = plugin.isPlayerInCustomArea(); - Color statusColor = inArea ? Color.GREEN : Color.RED; - panelComponent.getChildren().add(LineComponent.builder() - .left("Status:") - .right(inArea ? "In Area" : "Out of Area") - .rightColor(statusColor) - .build()); - - // Show distance to center if not in area - if (!inArea) { - - WorldPoint playerPos = Rs2Player.getWorldLocation(); - if (playerPos != null) { - int distance = playerPos.distanceTo(center); - panelComponent.getChildren().add(LineComponent.builder() - .left("Distance:") - .right(distance + " tiles away") - .build()); - } - } - } else { - panelComponent.getChildren().add(LineComponent.builder() - .left("Status:") - .right("No Area Defined") - .rightColor(Color.YELLOW) - .build()); - - panelComponent.getChildren().add(LineComponent.builder() - .left("Help:") - .right("Press hotkey to mark area") - .build()); - } - } - - return panelComponent.render(graphics); - } - - /** - * Checks if the player is near the configured bank - */ - private boolean isNearBank() { - WorldPoint playerPos = Rs2Player.getWorldLocation(); - if (playerPos == null) { - return false; - } - - WorldPoint bankPos = config.bankStartLocation().getWorldPoint(); - int maxDistance = config.bankDistance(); - - return (playerPos.getPlane() == bankPos.getPlane() && - playerPos.distanceTo(bankPos) <= maxDistance); - } -} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/VoxPlugins/schedulable/example/README.md b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/VoxPlugins/schedulable/example/README.md deleted file mode 100644 index a367c3a8b72..00000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/VoxPlugins/schedulable/example/README.md +++ /dev/null @@ -1,499 +0,0 @@ -# SchedulableExamplePlugin Documentation - -## Overview -The `SchedulableExamplePlugin` demonstrates how to create a plugin compatible with Microbot's scheduler system. It implements the `ConditionProvider` interface to define configurable conditions for when the plugin should automatically start and stop based on various in-game criteria. This plugin serves as a comprehensive example for developers wanting to create scripts that can be managed by the scheduler framework, providing a template for implementing different types of conditions and state management approaches. - -The plugin provides a practical implementation of conditional logic that can be used to automate tasks in a controlled manner. By integrating with the scheduler, it enables users to create complex automation workflows where multiple plugins can work together in a sequenced manner, each starting and stopping based on specific game conditions. - -## Key Features -- **Seamless integration** with the Microbot scheduler framework -- **Highly configurable** start and stop conditions -- **Location-based start conditions**: - - Bank locations with configurable distance - - Custom areas with adjustable radius -- **Multiple stop condition types**: - - **Time-based**: Run for specified duration - - **Resource collection**: Stop after gathering specific items - - **Loot collection**: Stop after collecting specific drops - - **Item processing**: Stop after crafting/converting items - - **NPC kill counting**: Stop after killing a specific number of NPCs - -## Architecture -The plugin consists of four primary components that work together to provide a complete implementation of a scheduler-compatible plugin: - -1. **SchedulableExamplePlugin** (`SchedulableExamplePlugin.java`) - - Main plugin class implementing `ConditionProvider` and `KeyListener` - - Manages the plugin lifecycle and condition creation - - Handles hotkey inputs for custom area marking - - Serves as the central orchestrator that connects configuration, script execution, and condition evaluation - - Implements the scheduler integration points through the `ConditionProvider` methods - - Maintains state between sessions by saving and loading world locations - -2. **SchedulableExampleConfig** (`SchedulableExampleConfig.java`) - - Configuration interface with `@ConfigGroup` and `@ConfigItem` annotations - - Defines all configurable parameters for the plugin - - Organizes settings into logical sections using `@ConfigSection` annotations - - Provides default values for configuration options - - Includes setter methods for mutable state (like custom area coordinates) - - Uses enums to define valid option sets (like `LocationStartType` and `ProcessTrackingMode`) - - Implements hidden configuration items for internal state persistence - - Creates a hierarchical organization of settings with sections that can be collapsed by default - -3. **LocationStartNotificationOverlay** (`LocationStartNotificationOverlay.java`) - - Visual overlay displaying location-based start condition information - - Provides real-time feedback on condition status - - Uses RuneLite's overlay system to render information on the game screen - - Dynamically updates to show relevant details based on the current configuration - - Shows information about bank locations or custom areas depending on the active configuration - - Implements color-coded status indicators (green for met conditions, red for unmet) - - Displays distance measurements to target locations when relevant - - Updates in real-time as the player moves around the game world - - Provides helpful guidance messages for setting up custom areas - -## Condition Provider Implementation -The `ConditionProvider` interface is the key integration point between the plugin and the scheduler system. By implementing this interface, the plugin can define under what circumstances it should be automatically started or stopped by the scheduler. This provides a powerful abstraction that allows the scheduler to manage multiple plugins without needing to understand their specific functionality. - -### Stop Conditions -The `getStopCondition()` method returns a logical condition determining when the plugin should automatically stop. This method is called by the scheduler to evaluate whether the plugin should be terminated. The implementation combines multiple condition types into a single logical expression that can be evaluated to determine if stopping criteria have been met: - -```java -@Override -public LogicalCondition getStopCondition() { - // Create an OR condition - we'll stop when ANY of the enabled conditions are met - OrCondition orCondition = new OrCondition(); - - // Add enabled conditions based on configuration - if (config.enableTimeCondition()) { - orCondition.addCondition(createTimeCondition()); - } - - if (config.enableLootItemCondition()) { - orCondition.addCondition(createLootItemCondition()); - } - - // Add more conditions... - - // If no conditions were added, add a fallback time condition - if (orCondition.getConditions().isEmpty()) { - orCondition.addCondition(IntervalCondition.createRandomized(Duration.ofMinutes(5), Duration.ofMinutes(5))); - } - - return orCondition; -} -``` - -### Start Conditions -The `getStartCondition()` method returns a logical condition determining when the plugin is allowed to start. The scheduler uses this to determine if the plugin should be automatically started when it's scheduled to run. Unlike stop conditions which should always return a value, start conditions can return `null` to indicate that the plugin can start without any preconditions: - -```java -@Override -public LogicalCondition getStartCondition() { - // Default to no start conditions (always allowed to start) - if (!config.enableLocationStartCondition()) { - return null; - } - - // Create a logical condition for start conditions - LogicalCondition startCondition = null; - - // Create location-based condition based on selected type - if (config.locationStartType() == SchedulableExampleConfig.LocationStartType.BANK) { - // Bank-based start condition - // ... - } else if (config.locationStartType() == SchedulableExampleConfig.LocationStartType.CUSTOM_AREA) { - // Custom area start condition - // ... - } - - return startCondition; -} -``` - -## Detailed Condition Types -The plugin implements several types of conditions that can be used to control when it should start or stop. Each condition type is implemented as a separate method that creates and configures a condition object based on the current plugin configuration. - -### Time Condition -The time condition is the simplest form of stop condition, which will trigger after a specified duration has elapsed. This is useful for limiting the runtime of a plugin to prevent excessive resource usage or to simulate human-like play patterns with regular breaks: -```java -private Condition createTimeCondition() { - int minMinutes = config.minRuntime(); - int maxMinutes = config.maxRuntime(); - - return IntervalCondition.createRandomized( - Duration.ofMinutes(minMinutes), - Duration.ofMinutes(maxMinutes) - ); -} -``` - -### Loot Item Condition -The loot item condition is used to stop the plugin after collecting a specific number of items. This is particularly useful for gathering activities where you want to collect a certain amount of a resource before stopping. The condition supports both AND and OR logical operations, allowing for complex item collection goals: - -```java -private LogicalCondition createLootItemCondition() { - // Parse the comma-separated list of items - List lootItemsList = parseItemList(config.lootItems()); - - boolean andLogical = config.itemsToLootLogical(); - int minLootItems = config.minItems(); - int maxLootItems = config.maxItems(); - - // Create randomized targets for each item - List minLootItemPerPattern = new ArrayList<>(); - List maxLootItemPerPattern = new ArrayList<>(); - - // Generate target counts... - - // Create the appropriate logical condition based on config - if (andLogical) { - return LootItemCondition.createAndCondition( - lootItemsList, - minLootItemPerPattern, - maxLootItemPerPattern, - includeNoted, - allowNoneOwner - ); - } else { - return LootItemCondition.createOrCondition( - lootItemsList, - minLootItemPerPattern, - maxLootItemPerPattern, - includeNoted, - allowNoneOwner - ); - } -} -``` - -### Gathered Resource Condition -The gathered resource condition is similar to the loot item condition but is specifically designed for tracking resources gathered through skilling activities (mining, fishing, woodcutting, etc.). This allows for more precise tracking of gathering activities and can differentiate between items obtained through different methods: - -```java -private LogicalCondition createGatheredResourceCondition() { - // Parse the comma-separated list of resources - List resourcesList = parseItemList(config.gatheredResources()); - - boolean andLogical = config.resourcesLogical(); - int minResources = config.minResources(); - int maxResources = config.maxResources(); - boolean includeNoted = config.includeResourceNoted(); - - // Create target lists with randomized counts for each resource - List minResourcesPerItem = new ArrayList<>(); - List maxResourcesPerItem = new ArrayList<>(); - - for (String resource : resourcesList) { - int minCount = Rs2Random.between(minResources, maxResources); - int maxCount = Rs2Random.between(minCount, maxResources); - - minResourcesPerItem.add(minCount); - maxResourcesPerItem.add(maxCount); - } - - // Create the appropriate logical condition based on configuration - if (andLogical) { - return GatheredResourceCondition.createAndCondition( - resourcesList, - minResourcesPerItem, - maxResourcesPerItem, - includeNoted - ); - } else { - return GatheredResourceCondition.createOrCondition( - resourcesList, - minResourcesPerItem, - maxResourcesPerItem, - includeNoted - ); - } -} - -### Process Item Condition -The process item condition is designed to track item transformation operations such as crafting, smithing, cooking, and other production skills. It can monitor the consumption of source items, the production of target items, or both, making it versatile for various crafting and production tasks: - -```java -private Condition createProcessItemCondition() { - ProcessItemCondition.TrackingMode trackingMode; - - // Map config enum to condition enum - switch (config.trackingMode()) { - case SOURCE_CONSUMPTION: - trackingMode = ProcessItemCondition.TrackingMode.SOURCE_CONSUMPTION; - break; - // Other modes... - } - - // Create the appropriate process item condition based on tracking mode - if (trackingMode == ProcessItemCondition.TrackingMode.SOURCE_CONSUMPTION) { - // If tracking source consumption - // ... - } - // Other tracking modes... -} -``` - -### NPC Kill Count Condition -The NPC kill count condition monitors the number of NPCs killed during the plugin's execution. It supports pattern matching for NPC names and can be configured to track kills per NPC type or the total kill count across all specified NPCs. This is particularly useful for combat training and slayer task automation: - -```java -private LogicalCondition createNpcKillCountCondition() { - // Parse the comma-separated list of NPC names - List npcNamesList = parseItemList(config.npcNames()); - - boolean andLogical = config.npcLogical(); - int minKills = config.minKills(); - int maxKills = config.maxKills(); - boolean killsPerType = config.killsPerType(); - - // If we're counting per NPC type vs. total kills... -} -``` - -## Custom Area Management -The custom area feature allows users to define a specific area in the game world where the plugin should operate. This is implemented through a combination of configuration settings, hotkey handling, and visual overlay feedback. The custom area is defined as a circle with a configurable radius centered on the player's position when the area is created: - -```java -private void toggleCustomArea() { - if (!Microbot.isLoggedIn()) { - log.info("Cannot toggle custom area: Not logged in"); - return; - } - - boolean isActive = config.customAreaActive(); - - if (isActive) { - // Clear the custom area - config.setCustomAreaActive(false); - config.setCustomAreaCenter(null); - log.info("Custom area removed"); - } else { - // Create new custom area at current position - WorldPoint currentPos = null; - if (Microbot.isLoggedIn()){ - currentPos = Rs2Player.getWorldLocation(); - } - if (currentPos != null) { - config.setCustomAreaCenter(currentPos); - config.setCustomAreaActive(true); - log.info("Custom area created at: " + currentPos.toString() + " with radius: " + config.customAreaRadius()); - } - } -} -``` - -## Integration with Scheduler Events -The plugin integrates with the scheduler system by responding to events dispatched by the scheduler. The most important of these is the `PluginScheduleEntry`, which is triggered when the scheduler determines that a plugin should be stopped based on its stop conditions. The plugin handles this event by performing cleanup operations and then requesting that it be disabled: - -```java -@Override -@Subscribe -public void onPluginScheduleEntry(PluginScheduleEntry event) { - // Save location before stopping - if (event.getPlugin() == this) { - config.setLastLocation(Rs2Player.getWorldLocation()); - log.info("Scheduling stop for plugin: {}", event.getPlugin().getClass().getSimpleName()); - - // Schedule the stop operation on the client thread - Microbot.getClientThread().invokeLater(() -> { - try { - Microbot.getPluginManager().setPluginEnabled(this, false); - Microbot.getPluginManager().stopPlugin(this); - } catch (Exception e) { - log.error("Error stopping plugin", e); - } - }); - } -} -``` - -## Helper Methods -The plugin includes several helper methods that provide utility functionality for various aspects of its operation. These methods encapsulate common operations and logic to improve code readability and maintainability: - -```java -private List parseItemList(String itemsString) { - List itemsList = new ArrayList<>(); - if (itemsString != null && !itemsString.isEmpty()) { - String[] itemsArray = itemsString.split(","); - for (String item : itemsArray) { - String trimmedItem = item.trim(); - try { - // Validate regex pattern - java.util.regex.Pattern.compile(trimmedItem); - itemsList.add(trimmedItem); - log.debug("Valid item pattern found: {}", trimmedItem); - } catch (java.util.regex.PatternSyntaxException e) { - log.warn("Invalid regex pattern: '{}' - {}", trimmedItem, e.getMessage()); - } - } - } - return itemsList; -} -``` - -## Usage Guide - -### Setting Up the Plugin - -1. **Enable the plugin** through RuneLite's plugin manager - - Navigate to the plugin list and locate "Schedulable Example" - - Check the checkbox to enable it - - Note that the plugin can also be enabled by the scheduler when appropriate - -2. **Configure desired start/stop conditions** in the plugin's configuration panel: - - Click the configuration icon next to the plugin name - - Expand the various sections to access different types of conditions - - Configure at least one stop condition to ensure the plugin doesn't run indefinitely - - Common configurations include: - - Set time limits (minimum and maximum runtime) - - Define item collection targets (specific items and quantities) - - Configure NPC kill counts for combat activities - - Set up resource gathering goals for skilling activities - - Define item processing targets for crafting and production - -3. **Set up location-based start conditions** if desired: - - Enable the location start condition option - - Choose between bank location or custom area: - - **Bank Location**: Select a predefined bank location and set the maximum distance - - **Custom Area**: Position your character in the desired location and press the configured area marking hotkey - - The location overlay will show you when you're in a valid start position - - For custom areas, you can adjust the radius to control the size of the valid area - -4. **Start the plugin** in one of two ways: - - Manually start it through the plugin manager - - Let the scheduler start it automatically when scheduled and when start conditions are met - -5. **Monitor the plugin's operation**: - - Watch the status messages in the Microbot status area - - Check the overlay for location-based information - - The plugin will update its progress tracking as it runs - -6. **The plugin will automatically stop** when any of the following occurs: - - Any of the enabled stop conditions are satisfied - - The scheduler sends a stop event - - The plugin is manually disabled through the plugin manager - -## Example Configuration - -This configuration would make the plugin: -- Only start when the player is at the Grand Exchange -- Stop after running for 30-45 minutes OR after collecting 100-200 oak logs (whichever happens first) - -``` -enableLocationStartCondition: true -locationStartType: BANK -bankStartLocation: GRAND_EXCHANGE -bankDistance: 5 - -enableTimeCondition: true -minRuntime: 30 -maxRuntime: 45 - -enableLootItemCondition: true -lootItems: "Oak logs" -minItems: 100 -maxItems: 200 -``` - -## Technical Implementation Notes - -### Core Design Patterns and Principles - -1. **Thread Safety** - - The plugin uses `Microbot.getClientThread().invokeLater()` to ensure operations run on the client thread - - This is critical for preventing race conditions and ensuring proper interaction with the game client - - All UI updates and game state modifications should be performed on the client thread - -2. **State Persistence** - - Configuration state is saved between sessions using RuneLite's ConfigManager - - The plugin maintains state across sessions by saving: - - Last known player location - - Custom area definitions - - Configuration parameters - - This allows seamless continuation of tasks even after client restarts - -3. **Random Variance** - - Stop conditions use randomized ranges to add human-like variability - - The `Rs2Random.between()` utility is used to generate random values within configured ranges - - This prevents predictable patterns that might appear bot-like - - Different randomization approaches are used for different types of conditions - -4. **Pattern Matching** - - Item and NPC name matching supports regular expressions for flexibility - - This allows for powerful pattern matching capabilities like: - - Wildcards (e.g., ".*bones.*" to match any item containing "bones") - - Character classes (e.g., "[A-Za-z]+ logs" to match any type of logs) - - Alternations (e.g., "goblin|rat|spider" to match multiple NPC types) - - Regular expression patterns are validated before use to prevent runtime errors - -5. **Logical Composition** - - Conditions can be combined with AND/OR logic for complex triggering - - The `LogicalCondition` interface and its implementations (`AndCondition`, `OrCondition`) provide a composable framework - - This allows for arbitrarily complex condition trees to be constructed - - Each logical condition can contain any mix of primitive conditions or nested logical conditions - -6. **State Machine Pattern** - - The `SchedulableExampleScript` uses a state machine to manage its operation - - Different states handle different aspects of the script's functionality - - Transitions between states occur based on in-game conditions - - This provides a clear, maintainable structure for complex bot logic - -7. **Event-Driven Architecture** - - The plugin responds to events from the scheduler and game client - - Events trigger state changes and condition evaluations - - This decouples the plugin's logic from the specific timing of game updates - -## Extending the Plugin - -### Adding New Condition Types - -To extend the plugin with new types of conditions: - -1. **Create a new condition class** implementing the `Condition` interface - - Define the logic for when the condition is satisfied - - Implement the `reset()` method to reinitialize the condition's state - - Consider extending existing base classes like `ResourceCondition` if appropriate - -2. **Add configuration options** to `SchedulableExampleConfig` - - Create a new configuration section with `@ConfigSection` if needed - - Add configuration items with `@ConfigItem` annotations - - Define appropriate default values and descriptions - - Consider using enums for options with a fixed set of valid values - -3. **Implement a creation method** in `SchedulableExamplePlugin` - - Create a method that constructs and configures your new condition - - Add appropriate logic to handle configuration options - - Include randomization if appropriate for human-like behavior - - Handle edge cases and provide fallback values - -4. **Add the condition** to the appropriate logical group in `getStopCondition()` - - Check if the condition is enabled in the configuration - - Add it to the existing logical condition structure (typically an `OrCondition`) - - Consider how it interacts with other existing conditions - -### Implementing New Features - -To add entirely new functionality to the plugin: - -1. **Extend the script class** with new methods and state management - - Add new states to the state machine if needed - - Implement the logic for the new functionality - - Update the main loop to handle the new states and operations - -2. **Update the configuration interface** with options for the new features - - Group related settings into logical sections - - Provide clear descriptions and default values - - Add validation where appropriate - -3. **Enhance the overlay** if visual feedback is needed - - Add new information to the overlay rendering - - Consider color coding or other visual cues for status - - Ensure the overlay remains uncluttered and informative - -4. **Add new condition types** if needed for the new functionality - - Follow the steps outlined above for adding conditions - - Ensure the conditions properly integrate with the new features - -5. **Update documentation** to reflect the new capabilities - - Document configuration options - - Explain new condition types - - Provide usage examples \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/VoxPlugins/schedulable/example/SchedulableExampleConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/VoxPlugins/schedulable/example/SchedulableExampleConfig.java deleted file mode 100644 index ed6b4c9bb49..00000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/VoxPlugins/schedulable/example/SchedulableExampleConfig.java +++ /dev/null @@ -1,889 +0,0 @@ -package net.runelite.client.plugins.microbot.VoxPlugins.schedulable.example; - -import net.runelite.api.coords.WorldPoint; -import net.runelite.client.config.Config; -import net.runelite.client.config.ConfigGroup; -import net.runelite.client.config.ConfigItem; -import net.runelite.client.config.ConfigSection; -import net.runelite.client.config.Keybind; -import net.runelite.client.config.Range; -import net.runelite.client.plugins.microbot.Microbot; -import net.runelite.client.plugins.microbot.util.bank.enums.BankLocation; -import net.runelite.client.plugins.microbot.VoxPlugins.schedulable.example.enums.UnifiedLocation; -import net.runelite.client.plugins.microbot.VoxPlugins.schedulable.example.enums.SpellbookOption; - -import java.awt.event.KeyEvent; - -@ConfigGroup("SchedulableExample") -public interface SchedulableExampleConfig extends Config { - @ConfigSection( - name = "Start Conditions", - description = "Conditions for when the plugin is allowed to start", - position = 0 - ) - String startConditionSection = "startConditions"; - - @ConfigSection( - name = "Location Start Conditions", - description = "Location-based conditions for when the plugin is allowed to start", - position = 1, - closedByDefault = false - ) - String locationStartConditionSection = "locationStartConditions"; - - // Location Start Condition Settings - @ConfigItem( - keyName = "enableLocationStartCondition", - name = "Enable Location Start Condition", - description = "Enable location-based start condition", - position = 0, - section = locationStartConditionSection - ) - default boolean enableLocationStartCondition() { - return false; - } - - @ConfigItem( - keyName = "locationStartType", - name = "Location Type", - description = "Type of location condition to use for starting the plugin", - position = 1, - section = locationStartConditionSection - ) - default LocationStartType locationStartType() { - return LocationStartType.BANK; - } - - @ConfigItem( - keyName = "bankStartLocation", - name = "Bank Location", - description = "Bank location where the plugin should start", - position = 2, - section = locationStartConditionSection - ) - default BankLocation bankStartLocation() { - return BankLocation.GRAND_EXCHANGE; - } - @Range( - min = 10, - max = 100 - ) - @ConfigItem( - keyName = "bankDistance", - name = "Bank Distance (tiles)", - description = "Maximum distance from bank to start the plugin", - position = 3, - section = locationStartConditionSection - ) - default int bankDistance() { - return 20; - } - - @ConfigItem( - keyName = "customAreaActive", - name = "Custom Area Active", - description = "Whether a custom area has been defined using the hotkey", - position = 4, - section = locationStartConditionSection, - hidden = true - ) - default boolean customAreaActive() { - return false; - } - - void setCustomAreaActive(boolean active); - - @ConfigItem( - keyName = "areaMarkHotkey", - name = "Area Mark Hotkey", - description = "Hotkey to mark current position as center of custom area (press again to clear)", - position = 5, - section = locationStartConditionSection - ) - default Keybind areaMarkHotkey() { - return Keybind.NOT_SET; - } - - @ConfigItem( - keyName = "customAreaRadius", - name = "Custom Area Radius (tiles)", - description = "Radius of the custom area around the marked position", - position = 6, - section = locationStartConditionSection - ) - default int customAreaRadius() { - return 10; - } - - @ConfigItem( - keyName = "customAreaCenter", - name = "Custom Area Center", - description = "Center point of the custom area", - position = 7, - section = locationStartConditionSection, - hidden = true - ) - default WorldPoint customAreaCenter() { - return null; - } - - void setCustomAreaCenter(WorldPoint center); - - // Enum for location start types - enum LocationStartType { - BANK("Bank Location"), - CUSTOM_AREA("Custom Area"); - - private final String name; - - LocationStartType(String name) { - this.name = name; - } - - @Override - public String toString() { - return name; - } - } - - enum ProcessTrackingMode { - SOURCE_CONSUMPTION("Source Consumption"), - TARGET_PRODUCTION("Target Production"), - EITHER("Either"), - BOTH("Both"); - - private final String name; - - ProcessTrackingMode(String name) { - this.name = name; - } - - @Override - public String toString() { - return name; - } - } - - - @ConfigSection( - name = "Stop Conditions", - description = "Conditions for when the plugin should stop", - position = 101 - ) - String stopSection = "stopConditions"; - - @ConfigSection( - name = "Time Conditions", - description = "Time-based conditions for stopping the plugin", - position = 102, - closedByDefault = false - ) - String timeConditionSection = "timeConditions"; - - @ConfigSection( - name = "Loot Item Conditions", - description = "Conditions related to looted items", - position = 103, - closedByDefault = false - ) - String lootItemConditionSection = "lootItemConditions"; - - @ConfigSection( - name = "Gathered Resource Conditions", - description = "Conditions related to gathered resources (mining, fishing, etc.)", - position = 104, - closedByDefault = false - ) - String gatheredResourceConditionSection = "gatheredResourceConditions"; - - @ConfigSection( - name = "Process Item Conditions", - description = "Conditions related to processed items (crafting, smithing, etc.)", - position = 105, - closedByDefault = false - ) - String processItemConditionSection = "processItemConditions"; - @ConfigSection( - name = "NPC Conditions", - description = "Conditions related to NPCs", - position = 106, - closedByDefault = false - ) - String npcConditionSection = "npcConditions"; - - @ConfigSection( - name = "Pre/Post Schedule Requirements", - description = "Configure requirements for pre and post schedule tasks", - position = 107, - closedByDefault = false - ) - String prePostScheduleRequirementsSection = "prePostScheduleRequirements"; - - // Time Condition Settings - @ConfigItem( - keyName = "enableTimeCondition", - name = "Enable Time Condition", - description = "Enable time-based stop condition", - position = 0, - section = timeConditionSection - ) - default boolean enableTimeCondition() { - return true; - } - - @ConfigItem( - keyName = "minRuntime", - name = "Minimum Runtime (minutes)", - description = "Minimum time to run before stopping", - position = 1, - section = timeConditionSection - ) - default int minRuntime() { - return 1; - } - - @ConfigItem( - keyName = "maxRuntime", - name = "Maximum Runtime (minutes)", - description = "Maximum time to run before stopping", - position = 2, - section = timeConditionSection - ) - default int maxRuntime() { - return 2; - } - - // Loot Item Condition Settings - @ConfigItem( - keyName = "enableLootItemCondition", - name = "Enable Loot Item Condition", - description = "Enable condition to stop based on looted items", - position = 0, - section = lootItemConditionSection - ) - default boolean enableLootItemCondition() { - return true; - } - - @ConfigItem( - keyName = "lootItems", - name = "Loot Items to Track", - description = "Comma separated list of items. Supports regex patterns (.*bones.*)", - position = 1, - section = lootItemConditionSection - ) - default String lootItems() { - return "Logs"; - } - - @ConfigItem( - keyName = "itemsToLootLogical", - name = "Or(False)/And(True)", - description = "Logical operator for items to loot: False=OR, True=AND", - position = 2, - section = lootItemConditionSection - ) - default boolean itemsToLootLogical() { - return false; - } - - @ConfigItem( - keyName = "minItems", - name = "Minimum Items", - description = "Minimum number of items to loot before stopping", - position = 3, - section = lootItemConditionSection - ) - default int minItems() { - return 5; - } - - @ConfigItem( - keyName = "maxItems", - name = "Maximum Items", - description = "Maximum number of items to loot before stopping", - position = 4, - section = lootItemConditionSection - ) - default int maxItems() { - return 10; - } - - @ConfigItem( - keyName = "includeNoted", - name = "Include Noted Items", - description = "Include noted items in loot tracking", - position = 5, - section = lootItemConditionSection - ) - default boolean includeNoted() { - return false; - } - - @ConfigItem( - keyName = "allowNoneOwner", - name = "Allow None Owner", - description = "Allow items not owned by the player (e.g. items which are spawned)", - position = 6, - section = lootItemConditionSection - ) - default boolean allowNoneOwner() { - return false; - } - - // Gathered Resource Condition Settings - @ConfigItem( - keyName = "enableGatheredResourceCondition", - name = "Enable Gathered Resource Condition", - description = "Enable condition to stop based on gathered resources", - position = 0, - section = gatheredResourceConditionSection - ) - default boolean enableGatheredResourceCondition() { - return false; - } - - @ConfigItem( - keyName = "gatheredResources", - name = "Resources to Track", - description = "Comma separated list of resources to track (e.g. logs,ore,fish)", - position = 1, - section = gatheredResourceConditionSection - ) - default String gatheredResources() { - return "logs"; - } - - @ConfigItem( - keyName = "resourcesLogical", - name = "Or(False)/And(True)", - description = "Logical operator for resources: False=OR, True=AND", - position = 2, - section = gatheredResourceConditionSection - ) - default boolean resourcesLogical() { - return false; - } - - @ConfigItem( - keyName = "minResources", - name = "Minimum Resources", - description = "Minimum number of resources to gather before stopping", - position = 3, - section = gatheredResourceConditionSection - ) - default int minResources() { - return 10; - } - - @ConfigItem( - keyName = "maxResources", - name = "Maximum Resources", - description = "Maximum number of resources to gather before stopping", - position = 4, - section = gatheredResourceConditionSection - ) - default int maxResources() { - return 15; - } - - @ConfigItem( - keyName = "includeResourceNoted", - name = "Include Noted Resources", - description = "Include noted resources in tracking", - position = 5, - section = gatheredResourceConditionSection - ) - default boolean includeResourceNoted() { - return false; - } - - // Process Item Condition Settings - @ConfigItem( - keyName = "enableProcessItemCondition", - name = "Enable Process Item Condition", - description = "Enable condition to stop based on processed items", - position = 0, - section = processItemConditionSection - ) - default boolean enableProcessItemCondition() { - return false; - } - - @ConfigItem( - keyName = "trackingMode", - name = "Tracking Mode", - description = "How to track item processing (source items consumed or target items produced)", - position = 1, - section = processItemConditionSection - ) - default ProcessTrackingMode trackingMode() { - return ProcessTrackingMode.SOURCE_CONSUMPTION; - } - - @ConfigItem( - keyName = "sourceItems", - name = "Source Items", - description = "Comma separated list of source items (e.g. logs,ore)", - position = 2, - section = processItemConditionSection - ) - default String sourceItems() { - return "logs"; - } - - @ConfigItem( - keyName = "targetItems", - name = "Target Items", - description = "Comma separated list of target items (e.g. bow,shield)", - position = 3, - section = processItemConditionSection - ) - default String targetItems() { - return "bow"; - } - - @ConfigItem( - keyName = "minProcessedItems", - name = "Minimum Processed Items", - description = "Minimum number of items to process before stopping", - position = 4, - section = processItemConditionSection - ) - default int minProcessedItems() { - return 5; - } - - @ConfigItem( - keyName = "maxProcessedItems", - name = "Maximum Processed Items", - description = "Maximum number of items to process before stopping", - position = 5, - section = processItemConditionSection - ) - default int maxProcessedItems() { - return 10; - } - // NPC Kill Count Condition Settings - @ConfigItem( - keyName = "enableNpcKillCountCondition", - name = "Enable NPC Kill Count Condition", - description = "Enable condition to stop based on NPC kill count", - position = 0, - section = npcConditionSection - ) - default boolean enableNpcKillCountCondition() { - return false; - } - @ConfigItem( - keyName = "npcNames", - name = "NPCs to Track", - description = "Comma separated list of NPC names to track kills for. Supports regex patterns.", - position = 1, - section = npcConditionSection - ) - default String npcNames() { - return "goblin"; - } - - @ConfigItem( - keyName = "npcLogical", - name = "Or(False)/And(True)", - description = "Logical operator for NPCs: False=OR (any NPC satisfies), True=AND (all NPCs must be killed)", - position = 2, - section = npcConditionSection - ) - default boolean npcLogical() { - return false; - } - - @ConfigItem( - keyName = "minKills", - name = "Minimum Kills", - description = "Minimum number of NPCs to kill before stopping", - position = 3, - section = npcConditionSection - ) - default int minKills() { - return 5; - } - - @ConfigItem( - keyName = "maxKills", - name = "Maximum Kills", - description = "Maximum number of NPCs to kill before stopping", - position = 4, - section = npcConditionSection - ) - default int maxKills() { - return 10; - } - - @ConfigItem( - keyName = "killsPerType", - name = "Count Per NPC Type", - description = "If true, need to kill the specified count of EACH NPC type. If false, count total kills across all types.", - position = 5, - section = npcConditionSection - ) - default boolean killsPerType() { - return true; - } - - - // Location tracking - @ConfigItem( - keyName = "lastLocation", - name = "Last Location", - description = "Last tracked location", - hidden = true - ) - default WorldPoint lastLocation() { - return null; - } - default void setLastLocation(WorldPoint location){ - if (location != null) { - if (Microbot.getConfigManager() != null) { - Microbot.getConfigManager().setConfiguration("SchedulableExample", "lastLocation", location); - } - } - - } - - // Pre/Post Schedule Requirements Configuration - @ConfigItem( - keyName = "enablePrePostRequirements", - name = "Enable Pre/Post Requirements", - description = "Enable pre and post schedule requirements and tasks", - position = 0, - section = prePostScheduleRequirementsSection - ) - default boolean enablePrePostRequirements() { - return false; - } - - @ConfigItem( - keyName = "preScheduleSpellbook", - name = "Pre-Schedule Spellbook", - description = "Spellbook required before starting the plugin (None = no switching)", - position = 1, - section = prePostScheduleRequirementsSection - ) - default SpellbookOption preScheduleSpellbook() { - return SpellbookOption.NONE; - } - - @ConfigItem( - keyName = "postScheduleSpellbook", - name = "Post-Schedule Spellbook", - description = "Spellbook to switch to after plugin completion (None = no switching)", - position = 2, - section = prePostScheduleRequirementsSection - ) - default SpellbookOption postScheduleSpellbook() { - return SpellbookOption.NONE; - } - - @ConfigItem( - keyName = "preScheduleLocation", - name = "Pre-Schedule Location", - description = "Location required before starting the plugin (None = no location requirement)", - position = 3, - section = prePostScheduleRequirementsSection - ) - default UnifiedLocation preScheduleLocation() { - return UnifiedLocation.NONE; - } - - @ConfigItem( - keyName = "postScheduleLocation", - name = "Post-Schedule Location", - description = "Location to move to after plugin completion (None = no location requirement)", - position = 4, - section = prePostScheduleRequirementsSection - ) - default UnifiedLocation postScheduleLocation() { - return UnifiedLocation.NONE; - } - - - - @ConfigItem( - keyName = "enableConditionalItemRequirement", - name = "Enable Alch Conditional Requirement based on Fire Staff/Rune", - description = "Enable the fire staff/fire rune conditional requirement for alching in pre-schedule tasks.", - position = 5, - section = prePostScheduleRequirementsSection - ) - default boolean enableConditionalItemRequirement() { - return false; - } - - - @ConfigItem( - keyName = "enableEquipmentRequirement", - name = "Enable Equipment Requirement", - description = "Enable equipment requirement", - position = 6, - section = prePostScheduleRequirementsSection - ) - default boolean enableEquipmentRequirement() { - return false; - } - - @ConfigItem( - keyName = "enableInventoryRequirement", - name = "Enable Inventory Requirement", - description = "Enable inventory requirement", - position = 7, - section = prePostScheduleRequirementsSection - ) - default boolean enableInventoryRequirement() { - return false; - } - @ConfigItem( - keyName = "enableLootRequirement", - name = "Enable Loot Requirement", - description = "Enable loot requirement for coins near Lumbridge", - position = 8, - section = prePostScheduleRequirementsSection - ) - default boolean enableLootRequirement() { - return false; - } - @ConfigItem( - keyName = "enableShopRequirement", - name = "Enable Shop Requirement", - description = "Enable shop maple longbow, buy from grand exchange as pre-schedule and sell at store on post-schedule", - position = 9, - section = prePostScheduleRequirementsSection - ) - default boolean enableShopRequirement() { - return false; - } - - @ConfigItem( - keyName = "externalRequirements", - name = "Enable External Requirements", - description = "Enable external requirements test for pre and post schedule tasks", - position = 10, - section = prePostScheduleRequirementsSection - ) - default boolean externalRequirements() { - return false; - } - - - - @ConfigSection( - name = "Antiban Testing", - description = "Antiban system testing and configuration", - position = 199, - closedByDefault = true - ) - String antibanTestSection = "antibanTestSection"; - - @ConfigItem( - keyName = "enableAntibanTesting", - name = "Enable Antiban Testing", - description = "Enable antiban features testing including micro breaks", - position = 0, - section = antibanTestSection - ) - default boolean enableAntibanTesting() { - return false; - } - - @ConfigItem( - keyName = "enableMicroBreaks", - name = "Enable Micro Breaks", - description = "Enable micro breaks during plugin execution", - position = 1, - section = antibanTestSection - ) - default boolean enableMicroBreaks() { - return false; - } - - @ConfigItem( - keyName = "microBreakChance", - name = "Micro Break Chance", - description = "Chance (0.0-1.0) of taking a micro break per check", - position = 2, - section = antibanTestSection - ) - @Range(min = 0, max = 100) - default int microBreakChancePercent() { - return 10; // 10% default - } - - @ConfigItem( - keyName = "microBreakDurationMin", - name = "Micro Break Min Duration (minutes)", - description = "Minimum duration for micro breaks in minutes", - position = 3, - section = antibanTestSection - ) - @Range(min = 1, max = 30) - default int microBreakDurationMin() { - return 3; - } - - @ConfigItem( - keyName = "microBreakDurationMax", - name = "Micro Break Max Duration (minutes)", - description = "Maximum duration for micro breaks in minutes", - position = 4, - section = antibanTestSection - ) - @Range(min = 1, max = 60) - default int microBreakDurationMax() { - return 15; - } - - @ConfigItem( - keyName = "statusReportInterval", - name = "Status Report Interval (seconds)", - description = "How often to report break status (0 = disable reporting)", - position = 5, - section = antibanTestSection - ) - @Range(min = 0, max = 300) - default int statusReportInterval() { - return 30; // Report every 30 seconds - } - - @ConfigItem( - keyName = "enableActionCooldowns", - name = "Enable Action Cooldowns", - description = "Enable action cooldown testing", - position = 6, - section = antibanTestSection - ) - default boolean enableActionCooldowns() { - return false; - } - - @ConfigItem( - keyName = "moveMouseOffScreen", - name = "Move Mouse Off-Screen", - description = "Move mouse off-screen during breaks", - position = 7, - section = antibanTestSection - ) - default boolean moveMouseOffScreen() { - return false; - } - - @ConfigSection( - name = "Debug Options", - description = "Options for testing and debugging", - position = 200, - closedByDefault = true - ) - String debugSection = "debugSection"; - - @ConfigItem( - keyName = "aliveReportTimeout", - name = "Alive Report Timeout (sec)", - description = "Time in seconds before script reports it's alive", - position = 0, - section = debugSection - ) - @Range( - min = 10, - max = 100 - ) - default int aliveReportTimeout() { - return 10; - } - - @ConfigItem( - keyName = "finishPluginNotSuccessfulHotkey", - name = "Finish Plugin Not-Successful Hotkey", - description = "Press this hotkey to manually trigger the PluginScheduleEntryMainTaskFinishedEvent for testing not successful completion", - position = 1, - section = debugSection - ) - default Keybind finishPluginNotSuccessfulHotkey() { - return new Keybind(KeyEvent.VK_F2, 0); - } - - @ConfigItem( - keyName = "finishPluginSuccessfulHotkey", - name = "Finish Plugin Hotkey", - description = "Press this hotkey to manually trigger the PluginScheduleEntryMainTaskFinishedEvent for testing successful completion", - position = 2, - section = debugSection - ) - default Keybind finishPluginSuccessfulHotkey() { - return new Keybind(KeyEvent.VK_F3, 0); - } - - - @ConfigItem( - keyName = "finishReason", - name = "Finish Reason", - description = "The reason to report when finishing the plugin", - position = 3, - section = debugSection - ) - default String finishReason() { - return "Task completed successfully"; - } - - @ConfigItem( - keyName = "lockConditionHotkey", - name = "Lock Condition Hotkey", - description = "Press this hotkey to toggle the lock condition (prevents plugin from being stopped)", - position = 4, - section = debugSection - ) - default Keybind lockConditionHotkey() { - return Keybind.NOT_SET; - } - - @ConfigItem( - keyName = "lockDescription", - name = "Lock Reason", - description = "Description of why the plugin is locked", - position = 5, - section = debugSection - ) - default String lockDescription() { - return "Plugin in critical state - do not stop"; - } - - @ConfigItem( - keyName = "testPreScheduleTasksHotkey", - name = "Test Pre-Schedule Tasks Hotkey", - description = "Press this hotkey to test pre-schedule tasks functionality (equipment, spellbook, location setup)", - position = 6, - section = debugSection - ) - default Keybind testPreScheduleTasksHotkey() { - return Keybind.NOT_SET; - } - - @ConfigItem( - keyName = "testPostScheduleTasksHotkey", - name = "Test Post-Schedule Tasks Hotkey", - description = "Press this hotkey to test post-schedule tasks functionality (cleanup, banking, spellbook restoration)", - position = 7, - section = debugSection - ) - default Keybind testPostScheduleTasksHotkey() { - return Keybind.NOT_SET; - } - - @ConfigItem( - keyName = "cancelTasksHotkey", - name = "Cancel & Reset Tasks Hotkey", - description = "Press this hotkey to cancel any running pre/post schedule tasks and reset execution state", - position = 8, - section = debugSection - ) - default Keybind cancelTasksHotkey() { - return Keybind.NOT_SET; - } -} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/VoxPlugins/schedulable/example/SchedulableExampleOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/VoxPlugins/schedulable/example/SchedulableExampleOverlay.java deleted file mode 100644 index cd5ad772a10..00000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/VoxPlugins/schedulable/example/SchedulableExampleOverlay.java +++ /dev/null @@ -1,139 +0,0 @@ -package net.runelite.client.plugins.microbot.VoxPlugins.schedulable.example; - -import net.runelite.client.plugins.microbot.pluginscheduler.tasks.state.TaskExecutionState; -import net.runelite.client.ui.overlay.OverlayLayer; -import net.runelite.client.ui.overlay.OverlayPanel; -import net.runelite.client.ui.overlay.OverlayPosition; -import net.runelite.client.ui.overlay.components.ComponentConstants; - -import javax.inject.Inject; -import java.awt.*; - -/** - * Overlay for the SchedulableExample plugin that displays the current state of - * pre/post schedule requirements and task execution. - * - * This overlay demonstrates how to integrate the RequirementOverlayComponentFactory - * to provide real-time feedback about requirement fulfillment progress. - */ -public class SchedulableExampleOverlay extends OverlayPanel { - - private final SchedulableExamplePlugin plugin; - - @Inject - public SchedulableExampleOverlay(SchedulableExamplePlugin plugin) { - super(plugin); - this.plugin = plugin; - setPosition(OverlayPosition.TOP_LEFT); - setPreferredSize(new Dimension(ComponentConstants.STANDARD_WIDTH, 200)); - setNaughty(); - setDragTargetable(true); - setLayer(OverlayLayer.UNDER_WIDGETS); - } - - @Override - public Dimension render(Graphics2D graphics) { - // Clear previous components - panelComponent.getChildren().clear(); - - // Get the task manager and requirements - SchedulableExamplePrePostScheduleTasks tasks = (SchedulableExamplePrePostScheduleTasks)plugin.getPrePostScheduleTasks(); - - // Only show overlay if pre/post requirements are enabled or tasks are running - if (!plugin.getConfig().enablePrePostRequirements() && - (tasks == null || !tasks.isExecuting())) { - return null; // Don't show overlay when not needed - } - - try { - // Show concise information only - boolean isExecuting = tasks != null && tasks.isExecuting(); - boolean hasPrePostRequirements = plugin.getConfig().enablePrePostRequirements(); - - // Main title with status indication - String titleText = "SchedulableExample"; - Color titleColor = Color.CYAN; - - if (isExecuting) { - TaskExecutionState executionState = tasks.getExecutionState(); - if (executionState.isInErrorState()) { - titleText += " (ERROR)"; - titleColor = Color.RED; - } else { - titleText += " (ACTIVE)"; - titleColor = Color.YELLOW; - } - } else if (hasPrePostRequirements) { - titleText += " (READY)"; - titleColor = Color.CYAN; - } - - panelComponent.getChildren().add(net.runelite.client.ui.overlay.components.TitleComponent.builder() - .text(titleText) - .color(titleColor) - .build()); - - // Show current status - if (isExecuting) { - TaskExecutionState executionState = tasks.getExecutionState(); - String phase = executionState.getCurrentPhase() != null ? - executionState.getCurrentPhase().toString() : "EXECUTING"; - int progress = executionState.getProgressPercentage(); - String statusText = progress > 0 ? phase + " (" + progress + "%)" : phase; - - panelComponent.getChildren().add(net.runelite.client.ui.overlay.components.LineComponent.builder() - .left("Phase:") - .right(statusText) - .leftColor(Color.WHITE) - .rightColor(Color.YELLOW) - .build()); - - // Show detailed status if available and short enough - String detailedStatus = executionState.getDetailedStatus(); - if (detailedStatus != null && !detailedStatus.isEmpty() && detailedStatus.length() <= 25) { - panelComponent.getChildren().add(net.runelite.client.ui.overlay.components.LineComponent.builder() - .left("Status:") - .right(detailedStatus) - .leftColor(Color.WHITE) - .rightColor(Color.CYAN) - .build()); - } - } else { - // Show requirements status when not executing - String requirementsText = hasPrePostRequirements ? "ENABLED" : "DISABLED"; - Color requirementsColor = hasPrePostRequirements ? Color.GREEN : Color.GRAY; - - panelComponent.getChildren().add(net.runelite.client.ui.overlay.components.LineComponent.builder() - .left("Pre/Post:") - .right(requirementsText) - .leftColor(Color.WHITE) - .rightColor(requirementsColor) - .build()); - } - - // Show essential controls hint - panelComponent.getChildren().add(net.runelite.client.ui.overlay.components.LineComponent.builder() - .left("Hotkeys:") - .right("See config") - .leftColor(Color.WHITE) - .rightColor(Color.LIGHT_GRAY) - .build()); - - } catch (Exception e) { - // Show error in overlay - panelComponent.getChildren().add(net.runelite.client.ui.overlay.components.TitleComponent.builder() - .text("SchedulableExample - ERROR") - .color(Color.RED) - .build()); - - panelComponent.getChildren().add(net.runelite.client.ui.overlay.components.LineComponent.builder() - .left("Error:") - .right(e.getMessage() != null ? e.getMessage() : "Unknown error") - .leftColor(Color.WHITE) - .rightColor(Color.RED) - .build()); - } - - return super.render(graphics); - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/VoxPlugins/schedulable/example/SchedulableExamplePlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/VoxPlugins/schedulable/example/SchedulableExamplePlugin.java deleted file mode 100644 index 7fa73314123..00000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/VoxPlugins/schedulable/example/SchedulableExamplePlugin.java +++ /dev/null @@ -1,978 +0,0 @@ -package net.runelite.client.plugins.microbot.VoxPlugins.schedulable.example; - - -import java.awt.event.KeyEvent; -import java.time.Duration; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; - -import javax.inject.Inject; - -import com.google.inject.Provides; - -import lombok.extern.slf4j.Slf4j; -import net.runelite.api.Client; -import net.runelite.api.GameState; -import net.runelite.api.coords.WorldPoint; -import net.runelite.api.events.GameStateChanged; -import net.runelite.client.config.ConfigDescriptor; -import net.runelite.client.config.ConfigManager; -import net.runelite.client.eventbus.Subscribe; -import net.runelite.client.events.ConfigChanged; -import net.runelite.client.input.KeyListener; -import net.runelite.client.input.KeyManager; -import net.runelite.client.plugins.Plugin; -import net.runelite.client.plugins.PluginDescriptor; -import net.runelite.client.plugins.microbot.Microbot; -import net.runelite.client.plugins.microbot.pluginscheduler.api.SchedulablePlugin; -import net.runelite.client.plugins.microbot.pluginscheduler.condition.Condition; -import net.runelite.client.plugins.microbot.pluginscheduler.condition.location.AreaCondition; -import net.runelite.client.plugins.microbot.pluginscheduler.condition.location.LocationCondition; -import net.runelite.client.plugins.microbot.pluginscheduler.condition.logical.AndCondition; -import net.runelite.client.plugins.microbot.pluginscheduler.condition.logical.LockCondition; -import net.runelite.client.plugins.microbot.pluginscheduler.condition.logical.LogicalCondition; -import net.runelite.client.plugins.microbot.pluginscheduler.condition.logical.OrCondition; -import net.runelite.client.plugins.microbot.pluginscheduler.condition.npc.NpcKillCountCondition; -import net.runelite.client.plugins.microbot.pluginscheduler.condition.resource.GatheredResourceCondition; -import net.runelite.client.plugins.microbot.pluginscheduler.condition.resource.LootItemCondition; -import net.runelite.client.plugins.microbot.pluginscheduler.condition.resource.ProcessItemCondition; -import net.runelite.client.plugins.microbot.pluginscheduler.condition.time.IntervalCondition; -import net.runelite.client.plugins.microbot.pluginscheduler.event.PluginScheduleEntryPostScheduleTaskEvent; -import net.runelite.client.plugins.microbot.pluginscheduler.event.PluginScheduleEntryPreScheduleTaskEvent; -import net.runelite.client.plugins.microbot.pluginscheduler.tasks.AbstractPrePostScheduleTasks; -import net.runelite.client.plugins.microbot.util.bank.enums.BankLocation; -import net.runelite.client.plugins.microbot.util.math.Rs2Random; -import net.runelite.client.plugins.microbot.util.player.Rs2Player; - -import net.runelite.client.ui.overlay.OverlayManager; -import net.runelite.client.util.HotkeyListener; - -@PluginDescriptor( - name = "Schedulable Example", - description = "Designed for use with the scheduler and testing its features", - tags = {"microbot", "woodcutting", "combat", "scheduler", "condition"}, - enabledByDefault = false -) -@Slf4j -public class SchedulableExamplePlugin extends Plugin implements SchedulablePlugin, KeyListener { - - - - @Inject - private SchedulableExampleConfig config; - - @Inject - private Client client; - - @Inject - private KeyManager keyManager; - - @Inject - private OverlayManager overlayManager; - - @Inject - private SchedulableExampleOverlay overlay; - - @Provides - SchedulableExampleConfig provideConfig(ConfigManager configManager) { - if (configManager == null) { - log.warn("ConfigManager is null, cannot provide SchedulableExampleConfig"); - return null; - } - return configManager.getConfig(SchedulableExampleConfig.class); - } - - /** - * Gets the plugin configuration. - * - * @return The SchedulableExampleConfig instance - */ - public SchedulableExampleConfig getConfig() { - return config; - } - - private SchedulableExampleScript script; - private WorldPoint lastLocation = null; - private int itemsCollected = 0; - - private LockCondition lockCondition; - private LogicalCondition startCondition = null; - private LogicalCondition stopCondition = null; - - - // Pre/Post Schedule Tasks and Requirements - private SchedulableExamplePrePostScheduleRequirements prePostScheduleRequirements = null; - private SchedulableExamplePrePostScheduleTasks prePostScheduleTasks = null; - - // HotkeyListener for the area marking - private final HotkeyListener areaHotkeyListener = new HotkeyListener(() -> config.areaMarkHotkey()) { - @Override - public void hotkeyPressed() { - toggleCustomArea(); - } - }; - - // HotkeyListener for testing PluginScheduleEntryMainTaskFinishedEvent - private final HotkeyListener finishPluginSuccessHotkeyListener = new HotkeyListener(() -> config.finishPluginSuccessfulHotkey()) { - @Override - public void hotkeyPressed() { - String reason = config.finishReason() + " (success)"; - boolean success = true; - log.info("\nManually triggering plugin finish: reason='{}', success={}", reason, success); - Microbot.getClientThread().invokeLater( () -> {reportFinished(reason, success); return true;}); - } - }; - // HotkeyListener for testing PluginScheduleEntryMainTaskFinishedEvent - private final HotkeyListener finishPluginNotSuccessHotkeyListener = new HotkeyListener(() -> config.finishPluginNotSuccessfulHotkey()) { - @Override - public void hotkeyPressed() { - String reason = config.finishReason()+ " (not success)"; - boolean success = false; - log.info("\nManually triggering plugin finish: reason='{}', success={}", reason, success); - Microbot.getClientThread().invokeLater( () -> {reportFinished(reason, success); return true;}); - } - }; - - // HotkeyListener for toggling the lock condition - private final HotkeyListener lockConditionHotkeyListener = new HotkeyListener(() -> config.lockConditionHotkey()) { - @Override - public void hotkeyPressed() { - log.info("Toggling lock condition for plugin: {}", getName()); - if (stopCondition == null || stopCondition.getConditions().isEmpty()) { - log.warn("Stop condition is not initialized. Cannot toggle lock condition."); - return; - } - boolean newState = toggleLock((Condition)(stopCondition)); - log.info("\n\tLock condition toggled: {}", newState ? "LOCKED - " + config.lockDescription() : "UNLOCKED"); - } - }; - - // HotkeyListener for testing pre-schedule tasks - private final HotkeyListener testPreScheduleTasksHotkeyListener = new HotkeyListener(() -> config.testPreScheduleTasksHotkey()) { - @Override - public void hotkeyPressed() { - // Initialize Pre/Post Schedule Requirements and Tasks if needed - if (config.enablePrePostRequirements()) { - if (getPrePostScheduleTasks() == null) { - log.info("Initializing Pre/Post failed "); - return; - } - // Test only pre-schedule tasks - SchedulableExamplePlugin.this.runPreScheduleTasks(); - - - } else { - log.info("Pre/Post Schedule Requirements are disabled in configuration"); - } - } - }; - - // HotkeyListener for testing post-schedule tasks - private final HotkeyListener testPostScheduleTasksHotkeyListener = new HotkeyListener(() -> config.testPostScheduleTasksHotkey()) { - @Override - public void hotkeyPressed() { - // Initialize Pre/Post Schedule Requirements and Tasks if needed - if (config.enablePrePostRequirements()) { - if (getPrePostScheduleTasks() == null) { - log.info("Initializing Pre/Post failed "); - return; - } - - // Test only post-schedule tasks - runPostScheduleTasks(); - } else { - log.info("Pre/Post Schedule Requirements are disabled in configuration"); - } - } - }; - - // HotkeyListener for cancelling tasks - private final HotkeyListener cancelTasksHotkeyListener = new HotkeyListener(() -> config.cancelTasksHotkey()) { - @Override - public void hotkeyPressed() { - log.info("Cancel tasks hotkey pressed for plugin: {}", getName()); - - if (prePostScheduleTasks != null) { - if (prePostScheduleTasks.isPreScheduleRunning()) { - prePostScheduleTasks.cancelPreScheduleTasks(); - log.info("Cancelled pre-schedule tasks"); - } else if (prePostScheduleTasks.isPostScheduleRunning()) { - prePostScheduleTasks.cancelPostScheduleTasks(); - log.info("Cancelled post-schedule tasks"); - } else { - log.info("No pre/post schedule tasks are currently running"); - } - - // Reset the execution state to allow fresh start - prePostScheduleTasks.reset(); - log.info("Reset pre/post schedule tasks execution state"); - } else { - log.info("No pre/post schedule tasks manager initialized"); - } - } - }; - - @Override - protected void startUp() { - loadLastLocation(); - this.script = new SchedulableExampleScript(); - - - - keyManager.registerKeyListener(this); - - // Register the hotkey listeners - keyManager.registerKeyListener(areaHotkeyListener); - keyManager.registerKeyListener(finishPluginSuccessHotkeyListener); - keyManager.registerKeyListener(finishPluginNotSuccessHotkeyListener); - keyManager.registerKeyListener(lockConditionHotkeyListener); - keyManager.registerKeyListener(testPreScheduleTasksHotkeyListener); - keyManager.registerKeyListener(testPostScheduleTasksHotkeyListener); - keyManager.registerKeyListener(cancelTasksHotkeyListener); - - // Add the overlay - overlayManager.add(overlay); - boolean scheduleMode = Microbot.getConfigManager().getConfiguration( - "SchedulableExample", - "scheduleMode", - Boolean.class - ); - log.info("\n\tSchedulable Example plugin started\n\t -In SchedulerMode:{}\n\t -Press {} to test the PluginScheduleEntryMainTaskFinishedEvent successfully\n\t -Press {} to test the PluginScheduleEntryMainTaskFinishedEvent unsuccessfully\n\t -Use {} to toggle the lock condition (prevents the plugin from being stopped)\n\t -Use {} to test Pre-Schedule Tasks functionality\n\t -Use {} to test Post-Schedule Tasks functionality\n\t -Use {} to cancel running pre/post schedule tasks", - scheduleMode, - config.finishPluginSuccessfulHotkey(), - config.finishPluginNotSuccessfulHotkey(), - config.lockConditionHotkey(), - config.testPreScheduleTasksHotkey(), - config.testPostScheduleTasksHotkey(), - config.cancelTasksHotkey()); - - } - - /** - * Override the default event handler to start the script properly after pre-schedule tasks. - * This follows the same pattern as runPreScheduleTasks() but integrates with the scheduler. - */ - @Subscribe - public void onPluginScheduleEntryPreScheduleTaskEvent(PluginScheduleEntryPreScheduleTaskEvent event) { - - if (event.getPlugin() != this) { - return; // Not for this plugin - } - - log.info("Received PluginScheduleEntryPreScheduleTaskEvent for SchedulableExample plugin"); - - if (prePostScheduleTasks != null && event.isSchedulerControlled() && !prePostScheduleTasks.isPreTaskComplete()) { - // Plugin has pre/post tasks and is under scheduler control - log.info("SchedulableExample starting with pre-schedule tasks from scheduler"); - try { - // Execute pre-schedule tasks with callback to start the script - runPreScheduleTasks(); - } catch (Exception e) { - log.error("Error during Pre-Schedule Tasks for SchedulableExample", e); - } - } - } - - - @Override - protected void shutDown() { - // Clean up PrePostScheduleTasks if initialized - if (prePostScheduleTasks != null) { - try { - prePostScheduleTasks.close(); - log.info("PrePostScheduleTasks cleaned up successfully"); - } catch (Exception e) { - log.error("Error cleaning up PrePostScheduleTasks", e); - } finally { - prePostScheduleTasks = null; - prePostScheduleRequirements = null; - } - } - - if (script != null && script.isRunning()) { - saveCurrentLocation(); - script.shutdown(); - } - unlock((Condition)(stopCondition)); - keyManager.unregisterKeyListener(this); - keyManager.unregisterKeyListener(areaHotkeyListener); - keyManager.unregisterKeyListener(finishPluginSuccessHotkeyListener); - keyManager.unregisterKeyListener(finishPluginNotSuccessHotkeyListener); - keyManager.unregisterKeyListener(lockConditionHotkeyListener); - keyManager.unregisterKeyListener(testPreScheduleTasksHotkeyListener); - keyManager.unregisterKeyListener(testPostScheduleTasksHotkeyListener); - keyManager.unregisterKeyListener(cancelTasksHotkeyListener); - - // Remove the overlay - overlayManager.remove(overlay); - } - - /** - * Toggles the custom area state and updates configuration - */ - private void toggleCustomArea() { - if (!Microbot.isLoggedIn()) { - log.info("Cannot toggle custom area: Not logged in"); - return; - } - - boolean isActive = config.customAreaActive(); - - if (isActive) { - // Clear the custom area - config.setCustomAreaActive(false); - config.setCustomAreaCenter(null); - log.info("Custom area removed"); - } else { - // Create new custom area at current position - WorldPoint currentPos = null; - if (Microbot.isLoggedIn()){ - currentPos = Rs2Player.getWorldLocation(); - } - if (currentPos != null) { - config.setCustomAreaCenter(currentPos); - config.setCustomAreaActive(true); - log.info("Custom area created at: " + currentPos.toString() + " with radius: " + config.customAreaRadius()); - } - } - } - - /** - * Checks if the player is in the custom area - */ - public boolean isPlayerInCustomArea() { - if (!config.customAreaActive() || config.customAreaCenter() == null) { - return false; - } - if (!Microbot.isLoggedIn()) { - return false; - } - WorldPoint currentPos = Rs2Player.getWorldLocation(); - if (currentPos == null) { - return false; - } - - WorldPoint center = config.customAreaCenter(); - int radius = config.customAreaRadius(); - - // Check if player is within radius of the center point and on the same plane - return (currentPos.getPlane() == center.getPlane() && - currentPos.distanceTo(center) <= radius); - } - - private void loadLastLocation() { - WorldPoint savedLocation = config.lastLocation(); - if (savedLocation == null) { - log.warn("No saved location found in config."); - if (Microbot.isLoggedIn()){ - this.lastLocation = Rs2Player.getWorldLocation(); - } - return; - } - this.lastLocation = savedLocation; - } - - private void saveCurrentLocation() { - if (client.getLocalPlayer() != null) { - WorldPoint currentLoc = client.getLocalPlayer().getWorldLocation(); - config.setLastLocation(currentLoc); - } - } - - private LogicalCondition createStopCondition() { - // Create an OR condition - we'll stop when ANY of the enabled conditions are met - OrCondition orCondition = new OrCondition(); - if (this.lockCondition == null) { - this.lockCondition = new LockCondition("Locked because the Plugin "+getName()+" is in a critical operation", false,true); //ensure unlock on shutdown of the plugin ! - } - - // Add enabled conditions based on configuration - if (config.enableTimeCondition()) { - orCondition.addCondition(createTimeCondition()); - } - - if (config.enableLootItemCondition()) { - orCondition.addCondition(createLootItemCondition()); - } - - if (config.enableGatheredResourceCondition()) { - orCondition.addCondition(createGatheredResourceCondition()); - } - - if (config.enableProcessItemCondition()) { - orCondition.addCondition(createProcessItemCondition()); - } - - if (config.enableNpcKillCountCondition()) { - orCondition.addCondition(createNpcKillCountCondition()); - } - - // If no conditions were added, add a fallback time condition - if (orCondition.getConditions().isEmpty()) { - log.warn("No stop conditions were enabled. Adding default time condition of 5 minutes."); - orCondition.addCondition(IntervalCondition.createRandomized(Duration.ofMinutes(5), Duration.ofMinutes(5))); - } - - // Add a lock condition that can be toggled manually - // NOTE: This condition uses AND logic with the other conditions since it's in an AND condition - AndCondition andCondition = new AndCondition(); - //andCondition.addCondition(orCondition); - andCondition.addCondition(lockCondition); - - List all = andCondition.findAllLockConditions(); - log.info("\nCreated stop condition: \n{}"+"\nFound {} lock conditions in stop condition: {}", andCondition.getDescription(), all.size(), all); - - return andCondition; - - } - - - - /** - * Tests only the pre-schedule tasks functionality. - * This method demonstrates how pre-schedule tasks work and logs the results. - */ - private void runPreScheduleTasks() { - if (prePostScheduleTasks != null && !prePostScheduleTasks.isPreTaskRunning() && !prePostScheduleTasks.isPreTaskComplete()) { - executePreScheduleTasks(() -> { - log.info("Pre-Schedule Tasks completed successfully for SchedulableExample"); - // Ensure script is initialized - if (this.script == null) { - this.script = new SchedulableExampleScript(); - } - if (this.script.isRunning()) { - this.script.shutdown(); - } - // Start the actual script after pre-schedule tasks are done - this.script.run(config, lastLocation); - }); - } - - } - private void runPostScheduleTasks( ){ - if (prePostScheduleTasks != null && !prePostScheduleTasks.isPostScheduleRunning() && !prePostScheduleTasks.isPostTaskComplete()) { - executePostScheduleTasks(()->{ - if( this.script != null && this.script.isRunning()) { - this.script.shutdown(); - } - }); - }else { - log.info("Post-Schedule Tasks already completed or running for SchedulableExample"); - } - - - } - - - private LogicalCondition createStartCondition() { - try { - // Default to no start conditions (always allowed to start) - if (!config.enableLocationStartCondition()) { - return null; - } - - // Create a logical condition for start conditions - LogicalCondition startCondition = null; - - // Create location-based condition based on selected type - if (config.locationStartType() == SchedulableExampleConfig.LocationStartType.BANK) { - // Bank-based start condition - BankLocation selectedBank = config.bankStartLocation(); - int distance = config.bankDistance(); - - // Create condition using bank location - startCondition = new OrCondition(); // Use OR to allow multiple possible conditions - Condition bankCondition = LocationCondition.atBank(selectedBank, distance); - ((OrCondition) startCondition).addCondition(bankCondition); - - log.debug("Created bank start condition: " + selectedBank.name() + " within " + distance + " tiles"); - } else if (config.locationStartType() == SchedulableExampleConfig.LocationStartType.CUSTOM_AREA) { - // Custom area start condition - if (config.customAreaActive() && config.customAreaCenter() != null) { - WorldPoint center = config.customAreaCenter(); - int radius = config.customAreaRadius(); - - // Create area condition centered on the saved point - startCondition = new OrCondition(); - AreaCondition areaCondition = LocationCondition.createArea( - "Custom Start Area", - center, - radius * 2, // Width (diameter) - radius * 2 // Height (diameter) - ); - ((OrCondition) startCondition).addCondition(areaCondition); - - log.debug("Created custom area start condition at: " + center + " with radius: " + radius); - } else { - log.warn("Custom area start condition selected but no area is defined"); - // Return null to indicate no start condition (always allowed to start) - return null; - } - } - - return startCondition; - } catch (Exception e) { - log.error("Error creating start condition", e); - e.printStackTrace(); - return new OrCondition(); // Fallback to no conditions - } - } - /** - * Returns a logical condition that determines when the plugin is allowed to start - */ - @Override - public LogicalCondition getStartCondition() { - if (this.startCondition == null) { - this.startCondition = createStartCondition(); - } - return this.startCondition; - - } - @Override - public LogicalCondition getStopCondition() { - // Create a new stop condition - if (this.stopCondition == null) { - this.stopCondition = createStopCondition(); - } - return this.stopCondition; - } - @Override - public AbstractPrePostScheduleTasks getPrePostScheduleTasks() { - SchedulableExampleConfig config = provideConfig(Microbot.getConfigManager()); - if (prePostScheduleRequirements == null || prePostScheduleTasks == null) { - if(Microbot.getClient().getGameState() != GameState.LOGGED_IN) { - log.debug("Schedulable Example - Cannot provide pre/post schedule tasks - not logged in"); - return null; // Return null if not logged in - } - log.info("Initializing Pre/Post Schedule Requirements and Tasks..."); - this.prePostScheduleRequirements = new SchedulableExamplePrePostScheduleRequirements(config); - this.prePostScheduleTasks = new SchedulableExamplePrePostScheduleTasks(this, keyManager,prePostScheduleRequirements); - // Log the requirements status - if (prePostScheduleRequirements.isInitialized()) log.info("\nPrePostScheduleRequirements initialized:\n{}", prePostScheduleRequirements.getDetailedDisplay()); - } - // Return the pre/post schedule tasks instance - return this.prePostScheduleTasks; - } - - /** - * Creates a time-based condition based on config settings - */ - private Condition createTimeCondition() { - // Existing implementation - int minMinutes = config.minRuntime(); - int maxMinutes = config.maxRuntime(); - - return IntervalCondition.createRandomized( - Duration.ofMinutes(minMinutes), - Duration.ofMinutes(maxMinutes) - ); - - } - - /** - * Creates a loot item condition based on config settings - */ - private LogicalCondition createLootItemCondition() { - // Parse the comma-separated list of items - List lootItemsList = parseItemList(config.lootItems()); - if (lootItemsList.isEmpty()) { - log.warn("No valid loot items specified, defaulting to 'Logs'"); - lootItemsList.add("Logs"); - } - - boolean andLogical = config.itemsToLootLogical(); - int minLootItems = config.minItems(); - int maxLootItems = config.maxItems(); - - // Create randomized targets for each item - List minLootItemPerPattern = new ArrayList<>(); - List maxLootItemPerPattern = new ArrayList<>(); - - for (int i = 0; i < lootItemsList.size(); i++) { - int minLoot = Rs2Random.between(minLootItems, maxLootItems); - int maxLoot = Rs2Random.between(minLoot, maxLootItems); - - // Ensure max is not less than min - if (maxLoot < minLoot) { - maxLoot = maxLootItems; - } - - minLootItemPerPattern.add(minLoot); - maxLootItemPerPattern.add(maxLoot); - } - - boolean includeNoted = config.includeNoted(); - boolean allowNoneOwner = config.allowNoneOwner(); - - // Create the appropriate logical condition based on config - if (andLogical) { - return LootItemCondition.createAndCondition( - lootItemsList, - minLootItemPerPattern, - maxLootItemPerPattern, - includeNoted, - allowNoneOwner - ); - } else { - return LootItemCondition.createOrCondition( - lootItemsList, - minLootItemPerPattern, - maxLootItemPerPattern, - includeNoted, - allowNoneOwner - ); - } - } - - /** - * Creates a gathered resource condition based on config settings - */ - private LogicalCondition createGatheredResourceCondition() { - // Parse the comma-separated list of resources - List resourcesList = parseItemList(config.gatheredResources()); - if (resourcesList.isEmpty()) { - log.warn("No valid resources specified, defaulting to 'logs'"); - resourcesList.add("logs"); - } - - boolean andLogical = config.resourcesLogical(); - int minResources = config.minResources(); - int maxResources = config.maxResources(); - boolean includeNoted = config.includeResourceNoted(); - - // Create target lists - List minResourcesPerItem = new ArrayList<>(); - List maxResourcesPerItem = new ArrayList<>(); - - for (int i = 0; i < resourcesList.size(); i++) { - int minCount = Rs2Random.between(minResources, maxResources); - int maxCount = Rs2Random.between(minCount, maxResources); - - // Ensure max is not less than min - if (maxCount < minCount) { - maxCount = maxResources; - } - - minResourcesPerItem.add(minCount); - maxResourcesPerItem.add(maxCount); - } - - // Create the appropriate logical condition - if (andLogical) { - return GatheredResourceCondition.createAndCondition( - resourcesList, - minResourcesPerItem, - maxResourcesPerItem, - includeNoted - ); - } else { - return GatheredResourceCondition.createOrCondition( - resourcesList, - minResourcesPerItem, - maxResourcesPerItem, - includeNoted - ); - } - } - - /** - * Creates a process item condition based on config settings - */ - private Condition createProcessItemCondition() { - ProcessItemCondition.TrackingMode trackingMode; - - // Map config enum to condition enum - switch (config.trackingMode()) { - case SOURCE_CONSUMPTION: - trackingMode = ProcessItemCondition.TrackingMode.SOURCE_CONSUMPTION; - break; - case TARGET_PRODUCTION: - trackingMode = ProcessItemCondition.TrackingMode.TARGET_PRODUCTION; - break; - case EITHER: - trackingMode = ProcessItemCondition.TrackingMode.EITHER; - break; - case BOTH: - trackingMode = ProcessItemCondition.TrackingMode.BOTH; - break; - default: - trackingMode = ProcessItemCondition.TrackingMode.SOURCE_CONSUMPTION; - } - - List sourceItemsList = parseItemList(config.sourceItems()); - List targetItemsList = parseItemList(config.targetItems()); - - int minProcessed = config.minProcessedItems(); - int maxProcessed = config.maxProcessedItems(); - - // Create the appropriate process item condition based on tracking mode - if (trackingMode == ProcessItemCondition.TrackingMode.SOURCE_CONSUMPTION) { - // If tracking source consumption - if (!sourceItemsList.isEmpty()) { - return ProcessItemCondition.forConsumption(sourceItemsList.get(0), - Rs2Random.between(minProcessed, maxProcessed)); - } - } else if (trackingMode == ProcessItemCondition.TrackingMode.TARGET_PRODUCTION) { - // If tracking target production - if (!targetItemsList.isEmpty()) { - return ProcessItemCondition.forProduction(targetItemsList.get(0), - Rs2Random.between(minProcessed, maxProcessed)); - } - } else if (trackingMode == ProcessItemCondition.TrackingMode.BOTH) { - // If tracking both source and target - if (!sourceItemsList.isEmpty() && !targetItemsList.isEmpty()) { - return ProcessItemCondition.forRecipe( - sourceItemsList.get(0), 1, - targetItemsList.get(0), 1, - Rs2Random.between(minProcessed, maxProcessed) - ); - } - } - - // Default fallback - log.warn("Invalid process item configuration, using default"); - return ProcessItemCondition.forConsumption("logs", 10); - } - /** - * Creates an NPC kill count condition based on config settings - */ - private LogicalCondition createNpcKillCountCondition() { - // Parse the comma-separated list of NPC names - List npcNamesList = parseItemList(config.npcNames()); - if (npcNamesList.isEmpty()) { - log.warn("No valid NPC names specified, defaulting to 'goblin'"); - npcNamesList.add("goblin"); - } - - boolean andLogical = config.npcLogical(); - int minKills = config.minKills(); - int maxKills = config.maxKills(); - boolean killsPerType = config.killsPerType(); - - // If we're counting per NPC type, create target lists for each NPC - if (killsPerType) { - List minKillsPerNpc = new ArrayList<>(); - List maxKillsPerNpc = new ArrayList<>(); - - for (int i = 0; i < npcNamesList.size(); i++) { - int minKillCount = Rs2Random.between(minKills, maxKills); - int maxKillCount = Rs2Random.between(minKillCount, maxKills); - - // Ensure max is not less than min - if (maxKillCount < minKillCount) { - maxKillCount = maxKills; - } - - minKillsPerNpc.add(minKillCount); - maxKillsPerNpc.add(maxKillCount); - } - - // Create the appropriate logical condition based on config - if (andLogical) { - return NpcKillCountCondition.createAndCondition( - npcNamesList, - minKillsPerNpc, - maxKillsPerNpc - ); - } else { - return NpcKillCountCondition.createOrCondition( - npcNamesList, - minKillsPerNpc, - maxKillsPerNpc - ); - } - } - // If we're counting total kills across all NPC types - else { - // Generate a single randomized kill count target - int targetMin = minKills; - int targetMax = maxKills; - - // Create multiple individual conditions with same ranges - if (andLogical) { - return NpcKillCountCondition.createAndCondition( - npcNamesList, - targetMin, - targetMax - ); - } else { - return NpcKillCountCondition.createOrCondition( - npcNamesList, - targetMin, - targetMax - ); - } - } - } - - /** - * Helper method to parse a comma-separated list of items - */ - private List parseItemList(String itemsString) { - List itemsList = new ArrayList<>(); - if (itemsString != null && !itemsString.isEmpty()) { - String[] itemsArray = itemsString.split(","); - for (String item : itemsArray) { - String trimmedItem = item.trim(); - try { - // Validate regex pattern - java.util.regex.Pattern.compile(trimmedItem); - itemsList.add(trimmedItem); - log.debug("Valid item pattern found: {}", trimmedItem); - } catch (java.util.regex.PatternSyntaxException e) { - log.warn("Invalid regex pattern: '{}' - {}", trimmedItem, e.getMessage()); - } - } - } - return itemsList; - } - @Override - public ConfigDescriptor getConfigDescriptor() { - if (Microbot.getConfigManager() == null) { - return null; - } - SchedulableExampleConfig conf = Microbot.getConfigManager().getConfig(SchedulableExampleConfig.class); - return Microbot.getConfigManager().getConfigDescriptor(conf); - } - @Override - public void onStopConditionCheck() { - // Update item count when condition is checked - if (script != null) { - itemsCollected = script.getLogsCollected(); - } - } - - // Method for the scheduler to check progress - public int getItemsCollected() { - return itemsCollected; - } - @Subscribe - public void onConfigChanged(ConfigChanged event) - { - final ConfigDescriptor desc = getConfigDescriptor(); - if (desc != null && desc.getGroup() != null && event.getGroup().equals(desc.getGroup().value())) { - - this.startCondition = null; - this.stopCondition = null; - log.info( - "Config change detected for {}: {}={}, config group {}", - getName(), - event.getGroup(), - event.getKey(), - desc.getGroup().value() - ); - if (config.enablePrePostRequirements()) { - if (prePostScheduleTasks != null && !prePostScheduleTasks.isExecuting()) { - if (prePostScheduleRequirements != null) { - prePostScheduleRequirements.setConfig(config); - prePostScheduleRequirements.reset(); - } - // prePostScheduleTasks.reset(); when we allow reexecution of pre/post-schedule tasks on config change - log.info("PrePostScheduleRequirements initialized:\n{}", prePostScheduleRequirements.getDetailedDisplay()); - } - } else { - log.info("Pre/Post Schedule Requirements are disabled in configuration"); - } - } - } - - @Subscribe - public void onGameStateChanged(GameStateChanged event) { - if (event.getGameState() == GameState.LOGGED_IN) { - log.info("GameState changed to LOGGED_IN"); - getPrePostScheduleTasks(); - if( prePostScheduleTasks != null - && prePostScheduleTasks.isScheduleMode() && - !prePostScheduleTasks.isPreTaskComplete() && - !prePostScheduleTasks.isPreScheduleRunning()) { - log.info("Plugin is running in Scheduler Mode - waiting for scheduler to start pre-schedule tasks"); - } else { - log.info("Plugin is running in normal mode"); - } - }else if (event.getGameState() == GameState.LOGIN_SCREEN) { - - } - } - @Override - @Subscribe - public void onPluginScheduleEntryPostScheduleTaskEvent(PluginScheduleEntryPostScheduleTaskEvent event) { - // Save location before stopping - if (event.getPlugin() == this) { - WorldPoint currentLocation = null; - if (Microbot.isLoggedIn()) { - currentLocation = Rs2Player.getWorldLocation(); - } - if ( Microbot.getConfigManager() == null) { - log.warn("Cannot save last location - ConfigManager or current location is null"); - return; - } - Microbot.getConfigManager().setConfiguration("SchedulableExample", "lastLocation", currentLocation); - log.info("Scheduling stop for plugin: {}", event.getPlugin().getClass().getSimpleName()); - - runPostScheduleTasks(); - /*try { - Microbot.log("Successfully exited SchedulerExamplePlugin - stopping plugin"); - Microbot.getClientThread().invokeLater(() -> { - Microbot.stopPlugin(this); - return true; - }); - } catch (Exception ex) { - Microbot.log("Error during safe exit: " + ex.getMessage()); - Microbot.getClientThread().invokeLater(() -> { - Microbot.stopPlugin(this); - return true; - }); - }*/ - - - - - // Schedule the stop operation on the client thread - //Microbot.getClientThread().invokeLater(() -> { - // try { - // Microbot.getPluginManager().setPluginEnabled(this, false); - // Microbot.getPluginManager().stopPlugin(this); - // } catch (Exception e) { - // log.error("Error stopping plugin", e); - // } - // }); - } - } - - - - - - - @Override - public void keyTyped(KeyEvent e) { - // Not used - } - - @Override - public void keyPressed(KeyEvent e) { - // Movement handling has been moved to VoxQoL plugin - // This plugin now only handles its core scheduling functionality - } - - - - - - - - - - - - @Override - public void keyReleased(KeyEvent e) { - // Not used but required by the KeyListener interface - } - - -} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/VoxPlugins/schedulable/example/SchedulableExamplePrePostScheduleRequirements.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/VoxPlugins/schedulable/example/SchedulableExamplePrePostScheduleRequirements.java deleted file mode 100644 index 58183a098a6..00000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/VoxPlugins/schedulable/example/SchedulableExamplePrePostScheduleRequirements.java +++ /dev/null @@ -1,640 +0,0 @@ - -package net.runelite.client.plugins.microbot.VoxPlugins.schedulable.example; - -import lombok.Setter; -import lombok.extern.slf4j.Slf4j; -import net.runelite.api.EquipmentInventorySlot; -import net.runelite.api.coords.WorldArea; -import net.runelite.api.coords.WorldPoint; -import net.runelite.api.gameval.ItemID; -import net.runelite.client.plugins.microbot.VoxPlugins.schedulable.example.enums.UnifiedLocation; -import net.runelite.client.plugins.microbot.pluginscheduler.tasks.requirements.PrePostScheduleRequirements; -import net.runelite.client.plugins.microbot.pluginscheduler.tasks.requirements.data.ItemRequirementCollection; -import net.runelite.client.plugins.microbot.pluginscheduler.tasks.requirements.enums.RequirementPriority; -import net.runelite.client.plugins.microbot.pluginscheduler.tasks.requirements.enums.RequirementType; -import net.runelite.client.plugins.microbot.pluginscheduler.tasks.requirements.enums.TaskContext; -import net.runelite.client.plugins.microbot.pluginscheduler.tasks.requirements.requirement.SpellbookRequirement; -import net.runelite.client.plugins.microbot.pluginscheduler.tasks.requirements.requirement.collection.LootRequirement; -import net.runelite.client.plugins.microbot.pluginscheduler.tasks.requirements.requirement.conditional.ConditionalRequirement; -import net.runelite.client.plugins.microbot.pluginscheduler.tasks.requirements.requirement.item.ItemRequirement; -import net.runelite.client.plugins.microbot.pluginscheduler.tasks.requirements.requirement.location.LocationRequirement; -import net.runelite.client.plugins.microbot.pluginscheduler.tasks.requirements.requirement.logical.OrRequirement; -import net.runelite.client.plugins.microbot.pluginscheduler.tasks.requirements.requirement.shop.ShopItemRequirement; -import net.runelite.client.plugins.microbot.pluginscheduler.tasks.requirements.requirement.shop.ShopRequirement; -import net.runelite.client.plugins.microbot.pluginscheduler.tasks.requirements.requirement.shop.models.ShopOperation; -import net.runelite.client.plugins.microbot.util.bank.Rs2Bank; -import net.runelite.client.plugins.microbot.util.bank.enums.BankLocation; -import net.runelite.client.plugins.microbot.util.equipment.Rs2Equipment; -import net.runelite.client.plugins.microbot.util.grandexchange.models.TimeSeriesInterval; -import net.runelite.client.plugins.microbot.util.grounditem.models.Rs2SpawnLocation; -import net.runelite.client.plugins.microbot.util.inventory.Rs2Inventory; -import net.runelite.client.plugins.microbot.util.magic.Rs2Spellbook; -import net.runelite.client.plugins.microbot.util.magic.Rs2Staff; -import net.runelite.client.plugins.microbot.util.magic.Runes; -import net.runelite.client.plugins.microbot.util.shop.StoreLocations; -import net.runelite.client.plugins.microbot.util.shop.models.Rs2ShopItem; -import net.runelite.client.plugins.microbot.util.shop.models.Rs2ShopType; - -import java.time.Duration; -import java.util.Arrays; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.function.BooleanSupplier; - -/** - * Example implementation of PrePostScheduleRequirements for the SchedulableExample plugin. - * This demonstrates configurable requirements that can be enabled/disabled via plugin configuration. - * - * Features demonstrated: - * - Spellbook requirement (optional Lunar spellbook) - * - Location requirements (pre: Varrock West, post: Grand Exchange) - * - Loot requirement (coins near Lumbridge) - * - Equipment requirement (Staff of Air) - * - Inventory requirement (10k coins) - */ -@Slf4j -public class SchedulableExamplePrePostScheduleRequirements extends PrePostScheduleRequirements { - @Setter - private SchedulableExampleConfig config; - - public SchedulableExamplePrePostScheduleRequirements(SchedulableExampleConfig config) { - super("SchedulableExample", "Testing", false); - this.config = config; - } - - /** - * Initialize requirements based on configuration settings. - */ - private boolean initializeConfigurableRequirements() { - if (config == null) { - return false; // Ensure config is initialized before proceeding - } - if (!config.enablePrePostRequirements()) { - return true; // Skip requirements if disabled - } - boolean success = true; - this.getRegistry().clear(); - // Configure spellbook requirements based on dropdown selection - if (!config.preScheduleSpellbook().isNone()) { - SpellbookRequirement preSpellbookRequirement = new SpellbookRequirement( - config.preScheduleSpellbook().getSpellbook(), - TaskContext.PRE_SCHEDULE, - RequirementPriority.MANDATORY, - 7, - "Pre-schedule spellbook: " + config.preScheduleSpellbook().getDisplayName() - ); - this.register(preSpellbookRequirement); - } - - if (!config.postScheduleSpellbook().isNone()) { - SpellbookRequirement postSpellbookRequirement = new SpellbookRequirement( - config.postScheduleSpellbook().getSpellbook(), - TaskContext.POST_SCHEDULE, - RequirementPriority.MANDATORY, - 7, - "Post-schedule spellbook: " + config.postScheduleSpellbook().getDisplayName() - ); - this.register(postSpellbookRequirement); - } - - - - // Configure location requirements based on dropdown selection - if (!config.preScheduleLocation().equals(UnifiedLocation.NONE)) { - LocationRequirement preLocationRequirement; - - // Handle different location types appropriately - switch (config.preScheduleLocation().getType()) { - case BANK: - preLocationRequirement = new LocationRequirement( - (BankLocation) config.preScheduleLocation().getOriginalLocationData(), - true, // use transportation - -1, // no specific world required - TaskContext.PRE_SCHEDULE, - RequirementPriority.MANDATORY - ); - break; - - case DEPOSIT_BOX: - case SLAYER_MASTER: - case FARMING: - case HUNTING: - default: - // For non-bank locations, use WorldPoint - preLocationRequirement = new LocationRequirement( - config.preScheduleLocation().getWorldPoint(), - config.preScheduleLocation().getDisplayName(), - true, // use members - 9, - true, // use transportation - -1, // no specific world required - TaskContext.PRE_SCHEDULE, - RequirementPriority.MANDATORY, - 1, - "Must be at " + config.preScheduleLocation().getDisplayName() + " to begin the schedule" - ); - break; - } - - this.register(preLocationRequirement); - } - - - if (!config.postScheduleLocation().equals(UnifiedLocation.NONE)) { - LocationRequirement postLocationRequirement; - - // Handle different location types appropriately - switch (config.postScheduleLocation().getType()) { - case BANK: - postLocationRequirement = new LocationRequirement( - (BankLocation) config.postScheduleLocation().getOriginalLocationData(), - true, // use transportation - -1, // no specific world required - TaskContext.POST_SCHEDULE, - RequirementPriority.MANDATORY - ); - break; - - case DEPOSIT_BOX: - case SLAYER_MASTER: - case FARMING: - case HUNTING: - default: - // For non-bank locations, use WorldPoint - postLocationRequirement = new LocationRequirement( - config.postScheduleLocation().getWorldPoint(), - config.postScheduleLocation().getDisplayName(), - true, - 10, // acceptable distance - true, // use transportation - -1, // no specific world required - TaskContext.POST_SCHEDULE, - RequirementPriority.MANDATORY - ); - break; - } - - this.register(postLocationRequirement); - } - - - // Loot requirement - Coins near Lumbridge Castle - if (config.enableLootRequirement()) { - List coinSpawns = Arrays.asList( - new WorldPoint(3205, 3229, 0), // Lumbridge Castle ground floor coin spawns - new WorldPoint(3207, 3229, 0), - new WorldPoint(3209, 3229, 0) - ); - - Rs2SpawnLocation coinsSpawnLocation = new Rs2SpawnLocation( - ItemID.COINS, - "Lumbridge Castle", - "Ground Floor - East Wing", - coinSpawns, - false, // Not members only - 0, // Ground floor - Duration.ofSeconds(30) // Respawn time - ); - - LootRequirement coinsLootRequirement = new LootRequirement( - ItemID.COINS, - 5, // Amount to collect - "Test coins collection from Lumbridge Castle spawns", - coinsSpawnLocation - ); - - register(coinsLootRequirement); - } - // Equipment requirement - Staff of Air - if (config.enableEquipmentRequirement()) { - register(new ItemRequirement( - ItemID.STAFF_OF_AIR, - 1, - EquipmentInventorySlot.WEAPON, - -2, // must be equipped (equipment slot enforced) - RequirementPriority.MANDATORY, - 6, - "Staff of Air for basic magic", - TaskContext.PRE_SCHEDULE - )); - ItemRequirementCollection.registerAmuletOfGlory(this, - RequirementPriority.MANDATORY, 4, - TaskContext.PRE_SCHEDULE, - true); - ItemRequirementCollection.registerRingOfDueling(this, - RequirementPriority.MANDATORY, 4, - TaskContext.PRE_SCHEDULE, - true); - ItemRequirementCollection.registerWoodcuttingAxes(this,RequirementPriority.MANDATORY, - TaskContext.PRE_SCHEDULE,-1); // -1 for no inventory slot means the axe can be placed in a any inventory slot, and also be equipped, -2 would mean it can only be equipped - ItemRequirementCollection.registerPickAxes(this, RequirementPriority.MANDATORY, - TaskContext.POST_SCHEDULE); - } - - // Inventory requirement - 10k coins - if (config.enableInventoryRequirement()) { - register(new ItemRequirement( - ItemID.COINS, - 10000, - -1, // inventory slot - RequirementPriority.RECOMMENDED, - 8, - "10,000 coins for general purposes", - TaskContext.PRE_SCHEDULE - )); - - - // Add some basic optional requirements that are always available - register( new ItemRequirement( - ItemID.AIRRUNE, - 50, - null, // no equipment slot - -1, // inventory slot - RequirementPriority.MANDATORY, - 5, - "Basic runes for magic", - TaskContext.PRE_SCHEDULE - )); - register( new ItemRequirement( - ItemID.WATERRUNE, - 50, - null, // no equipment slot - -1, // inventory slot - RequirementPriority.RECOMMENDED, - 5, - "Basic runes for magic", - TaskContext.PRE_SCHEDULE - )); - register( new ItemRequirement( - ItemID.EARTHRUNE, - 50, - null, // no equipment slot - -1, // inventory slot - RequirementPriority.RECOMMENDED, - 5, - "Basic runes for magic", - TaskContext.PRE_SCHEDULE - )); - - // Basic runes for magic - register(new ItemRequirement( - ItemID.LAWRUNE, - 10, - -1, // inventory slot - RequirementPriority.MANDATORY, - 5, - "Law runes for magic", - TaskContext.PRE_SCHEDULE - )); - - // ==================================================================== - // OR REQUIREMENT MODES DEMONSTRATION - // ==================================================================== - // This example demonstrates both OR requirement modes: - // - // 1. ANY_COMBINATION (default): Can fulfill with any combination of food items - // Example: 2 lobsters + 3 swordfish = 5 total food items ✓ - // - // 2. SINGLE_TYPE: Must fulfill with exactly one type of item - // Example: Exactly 5 lobsters OR 5 swordfish OR 5 monkfish ✓ - // But NOT 2 lobsters + 3 swordfish ✗ - // - // You can set the mode using: setOrRequirementMode(OrRequirementMode.SINGLE_TYPE) - // Default mode is ANY_COMBINATION for backward compatibility - // ==================================================================== - - // Basic food for emergencies (demonstrates OR requirement) - register(ItemRequirement.createOrRequirement( - Arrays.asList( - ItemID.LOBSTER, - ItemID.SWORDFISH, - ItemID.MONKFISH, - ItemID.BREAD - ), - 5, - null, // no equipment slot - -1, // inventory slot - RequirementPriority.MANDATORY, - 4, - "Basic food for health restoration (OR requirement - any combination or single type based on mode)", - TaskContext.PRE_SCHEDULE - )); - } - - // Shop requirement - Multi-Item Maple Bow Trading Example - // This demonstrates the unified stock management system with multi-item operations: - // - Single BUY requirement for both maple bow types from Grand Exchange (pre-schedule) - // - Single SELL requirement for both maple bow types to Brian's Archery Supplies (post-schedule) - - // Create shop items for both maple bow types - Rs2ShopItem mapleLongbowGEItem = Rs2ShopItem.createGEItem( - ItemID.MAPLE_LONGBOW, // Maple longbow ID (851) - 0.99, // sell at 0.99% of the GE price fast selling - 1.01 // buy at 101% of the GE price fast buying - ); - - Rs2ShopItem mapleShortbowGEItem = Rs2ShopItem.createGEItem( - ItemID.MAPLE_SHORTBOW, // Maple shortbow ID (853) - 0.99, // sell at 99% of the GE price lasted price - 1.01 // buy at 101% of the GE price lasted price - ); - - // Brian's Archery Supplies shop setup (Rimmington) - WorldPoint brianShopLocation = new WorldPoint(2957, 3204, 0); // Brian's Archery Supplies in Rimmington - WorldArea brianShopArea = new WorldArea(brianShopLocation.getX() - 4, brianShopLocation.getY() - 4, 8, 8, brianShopLocation.getPlane()); - - Rs2ShopItem mapleLongbowShopItem = new Rs2ShopItem( - ItemID.MAPLE_LONGBOW, // Maple longbow ID (851) - "Brian", // Shop NPC name - brianShopArea, // Shop area - Rs2ShopType.ARCHERY_SHOP, - 1.0, // 100% sell rate (from OSRS wiki) - 0.65, // 65% buy rate (from OSRS wiki - Brian buys at 65% value) - 2.0, // Change percent - Map.of(), // No quest requirements - false, // Not members only - "Brian's Archery Supplies in Rimmington", // Notes - Duration.ofMinutes(2), // Restock time: 2 minutes (from OSRS wiki) - 2 // Base stock: 2 maple longbows (from OSRS wiki) - ); - - Rs2ShopItem mapleShortbowShopItem = new Rs2ShopItem( - ItemID.MAPLE_SHORTBOW, // Maple shortbow ID (853) - "Brian", // Shop NPC name - brianShopArea, // Same shop area - Rs2ShopType.ARCHERY_SHOP, - 1.0, // 100% sell rate - 0.65, // 65% buy rate - 2.0, // Change percent - Map.of(), // No quest requirements - false, // Not members only - "Brian's Archery Supplies in Rimmington", // Notes - Duration.ofMinutes(2), // Restock time - 2 // Base stock: 2 maple shortbows - ); - - // Create multi-item shop requirements using the current Map-based system - Map geBuyItems = Map.of( - mapleLongbowGEItem, new ShopItemRequirement(mapleLongbowGEItem, - 2, - 1, - TimeSeriesInterval.FIVE_MINUTES, - true), // 2 longbows, flexible buying - mapleShortbowGEItem, new ShopItemRequirement(mapleShortbowGEItem, - 2, - 1, - TimeSeriesInterval.FIVE_MINUTES, - true) // 2 shortbows, flexible buying - ); - - // Brian's shop: base stock=2, can sell up to 10 per world (max stock=12) - Map brianSellItems = Map.of( - mapleLongbowShopItem, new ShopItemRequirement(mapleLongbowShopItem, 20, 10), // Sell up to 10 longbows per world - mapleShortbowShopItem, new ShopItemRequirement(mapleShortbowShopItem, 20, 10) // Sell up to 10 shortbows per world - ); - - // Single multi-item BUY requirement for Grand Exchange - ShopRequirement buyMapleBowsRequirement = new ShopRequirement( - geBuyItems, - ShopOperation.BUY, - RequirementType.SHOP, - RequirementPriority.MANDATORY, - 8, - "Buy maple bows (longbow x20, shortbow x20) from Grand Exchange (pre-schedule)", - TaskContext.PRE_SCHEDULE - ); - - // Single multi-item SELL requirement for Brian's shop - ShopRequirement sellMapleBowsRequirement = new ShopRequirement( - brianSellItems, - ShopOperation.SELL, - RequirementType.SHOP, - RequirementPriority.MANDATORY, - 8, - "Sell up to 10 maple bows per type to Brian's Archery Supplies (post-schedule)", - TaskContext.POST_SCHEDULE - ); - - if (config.enableShopRequirement()) { - // Register the unified multi-item shop requirements - this.register(buyMapleBowsRequirement); - this.register(sellMapleBowsRequirement); - } - - // Custom Shop Requirement - Hammer and Bucket from nearest General Store - if (config.externalRequirements()) { - // Find the nearest general store that has both hammer and bucket - StoreLocations nearestStore = StoreLocations.getNearestStoreWithAllItems(ItemID.HAMMER, ItemID.BUCKET_EMPTY); - log.info("Nearest general store with hammer and bucket: {}", nearestStore != null ? nearestStore.getName() : "None found"); - if (nearestStore != null) { - // Create shop items for hammer and bucket from the nearest general store - WorldArea storeArea = new WorldArea( - nearestStore.getLocation().getX() - 3, - nearestStore.getLocation().getY() - 3, - 6, 6, - nearestStore.getLocation().getPlane() - ); - - Rs2ShopItem hammerShopItem = new Rs2ShopItem( - ItemID.HAMMER, - nearestStore.getNpcName(), - storeArea, - nearestStore.getShopType(), - nearestStore.getSellRate(), // Standard sell rate for general stores - nearestStore.getBuyRate(), - nearestStore.getChangePercent(), - nearestStore.getQuestRequirements(), - nearestStore.isMembers(), - "Hammer from " + nearestStore.getName(), - Duration.ofMinutes(5), - 5 // Base stock for hammers - ); - - Rs2ShopItem bucketShopItem = new Rs2ShopItem( - ItemID.BUCKET_EMPTY, - nearestStore.getNpcName(), - storeArea, - nearestStore.getShopType(), - nearestStore.getSellRate(), // Standard sell rate for general stores - nearestStore.getBuyRate(), - nearestStore.getChangePercent(), - nearestStore.getQuestRequirements(), - nearestStore.isMembers(), - "Empty bucket from " + nearestStore.getName(), - Duration.ofMinutes(5), - 3 // Base stock for buckets - ); - - // Create shop item requirements - ShopItemRequirement hammerRequirement = new ShopItemRequirement(hammerShopItem, 1, 0); - ShopItemRequirement bucketRequirement = new ShopItemRequirement(bucketShopItem, 1, 0); - - // Create the unified shop requirement for buying both items - Map shopItems = new LinkedHashMap<>(); - shopItems.put(hammerShopItem, hammerRequirement); - shopItems.put(bucketShopItem, bucketRequirement); - - ShopRequirement buyToolsRequirement = new ShopRequirement( - shopItems, - ShopOperation.BUY, - RequirementType.SHOP, - RequirementPriority.MANDATORY, - 7, - "Buy hammer and bucket from nearest general store (" + nearestStore.getName() + ")", - TaskContext.PRE_SCHEDULE - ); - - // Add as custom requirement to test external requirement fulfillment (step 7) - this.addCustomRequirement(buyToolsRequirement, TaskContext.PRE_SCHEDULE); - - log.info("Added custom shop requirement for hammer and bucket from: {}", nearestStore.getName()); - } else { - log.warn("No general store found with both hammer and bucket items"); - success = false; // Mark as failure if no store found - } - } - - // === Alch Conditional Requirement Example === - if (config.enableConditionalItemRequirement()) { - - // Build fire staff requirements (all staves that provide fire runes) - List staffWithFireRunesRequirements = Arrays.stream(Rs2Staff.values()) - .filter(staff -> staff.getRunes().contains(Runes.FIRE) && staff != Rs2Staff.NONE) - .map(staff -> new ItemRequirement( - staff.getItemID(), - 1, - EquipmentInventorySlot.WEAPON, - -2, - RequirementPriority.MANDATORY, - 10, - staff.name() + " equipped", - TaskContext.PRE_SCHEDULE, - null, null, null, null, false)) - .collect(java.util.stream.Collectors.toList()); - // Helper to check if any fire staff is available in inventory or bank - BooleanSupplier hasFireStaffCondition = () -> hasFireStaffAvailable(staffWithFireRunesRequirements); - OrRequirement fireStaffOrRequirement = new OrRequirement( - RequirementPriority.MANDATORY, - "Any fire staff equipped", - TaskContext.PRE_SCHEDULE, - staffWithFireRunesRequirements.toArray(new ItemRequirement[0]) - ); - - ItemRequirement fireRuneRequirement = new ItemRequirement( - ItemID.FIRERUNE, - 5, - -1, - RequirementPriority.MANDATORY, - 10, - "Fire runes in inventory", - TaskContext.PRE_SCHEDULE - ); - - ConditionalRequirement alchConditionalRequirement = new ConditionalRequirement( - RequirementPriority.MANDATORY, - 10, - "Alching: Fire staff or fire runes", - TaskContext.PRE_SCHEDULE, - false - ); - alchConditionalRequirement - .addStep( - () -> { - try { - return !hasFireStaffCondition.getAsBoolean(); - } catch (Throwable t) { - return false; - } - }, - fireRuneRequirement, - "Fire runes in inventory (no fire staff available)" - ) - .addStep( - () -> { - try { - return hasFireStaffCondition.getAsBoolean(); - } catch (Throwable t) { - return false; - } - }, - fireStaffOrRequirement, - "Any fire staff equipped (fire staff available)" - ); - - SpellbookRequirement normalSpellbookRequirement = new SpellbookRequirement( - Rs2Spellbook.MODERN, - TaskContext.PRE_SCHEDULE, - RequirementPriority.MANDATORY, - 10, - "Normal spellbook required for High Alchemy" - ); - - ItemRequirement natureRuneRequirement = new ItemRequirement( - ItemID.NATURERUNE, - 1, - -2, - RequirementPriority.MANDATORY, - 10, - "Nature rune for alching", - TaskContext.PRE_SCHEDULE - ); - - this.register(alchConditionalRequirement); - this.register(normalSpellbookRequirement); - this.register(natureRuneRequirement); - } - - return success; // Return true if all requirements initialized successfully - } - - /** - * Checks if any fire staff from the requirements is available in inventory or bank. - */ - private static boolean hasFireStaffAvailable(List staffReqs) { - int[] staffIds = staffReqs.stream().mapToInt(ItemRequirement::getId).toArray(); - return Rs2Inventory.contains(staffIds) || Rs2Bank.hasItem(staffIds)|| Rs2Equipment.isWearing(staffIds); - } - - - - /** - * Initialize the base item requirements collection. - * This demonstrates basic equipment and inventory requirements. - */ - @Override - protected boolean initializeRequirements() { - if (config == null){ - return false; // Ensure config is initialized before proceeding - } - return initializeConfigurableRequirements(); - } - - /** - * Gets a display string showing which requirements are currently enabled. - * Useful for debugging and logging. - */ - public String getDetailedDisplay() { - StringBuilder sb = new StringBuilder(); - sb.append("SchedulableExample Requirements Status:\n"); - sb.append(" Pre/Post Requirements: ").append(config.enablePrePostRequirements() ? "ENABLED" : "DISABLED").append("\n"); - - if (config.enablePrePostRequirements()) { - // Show new dropdown configurations - sb.append(" - Pre-Schedule Spellbook: ").append(config.preScheduleSpellbook().getDisplayName()).append("\n"); - sb.append(" - Post-Schedule Spellbook: ").append(config.postScheduleSpellbook().getDisplayName()).append("\n"); - sb.append(" - Pre-Schedule Location: ").append(config.preScheduleLocation().getDisplayName()).append("\n"); - sb.append(" - Post-Schedule Location: ").append(config.postScheduleLocation().getDisplayName()).append("\n"); - - // Show legacy configurations - sb.append(" - Loot Requirement: ").append(config.enableLootRequirement() ? "ENABLED (Coins at Lumbridge)" : "DISABLED").append("\n"); - sb.append(" - Equipment Requirement: ").append(config.enableEquipmentRequirement() ? "ENABLED (Staff of Air)" : "DISABLED").append("\n"); - sb.append(" - Inventory Requirement: ").append(config.enableInventoryRequirement() ? "ENABLED (10k Coins)" : "DISABLED").append("\n"); - sb.append(" - Shop Requirement: ").append(config.enableShopRequirement() ? "ENABLED (Hammer & Bucket from nearest general store)" : "DISABLED").append("\n"); - } - sb.append(super.getDetailedDisplay()); - - return sb.toString(); - } - -} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/VoxPlugins/schedulable/example/SchedulableExamplePrePostScheduleTasks.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/VoxPlugins/schedulable/example/SchedulableExamplePrePostScheduleTasks.java deleted file mode 100644 index 2dfef194ca7..00000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/VoxPlugins/schedulable/example/SchedulableExamplePrePostScheduleTasks.java +++ /dev/null @@ -1,186 +0,0 @@ -package net.runelite.client.plugins.microbot.VoxPlugins.schedulable.example; - -import java.util.concurrent.CompletableFuture; - -import lombok.extern.slf4j.Slf4j; -import net.runelite.client.input.KeyManager; -import net.runelite.client.plugins.microbot.pluginscheduler.condition.logical.LockCondition; -import net.runelite.client.plugins.microbot.pluginscheduler.tasks.AbstractPrePostScheduleTasks; -import net.runelite.client.plugins.microbot.pluginscheduler.tasks.requirements.PrePostScheduleRequirements; - -/** - * Implementation of AbstractPrePostScheduleTasks for the SchedulableExample plugin. - * This demonstrates how to implement custom pre and post schedule tasks with requirements. - * - * The class handles: - * - Pre-schedule tasks: Preparation based on configured requirements - * - Post-schedule tasks: Cleanup and resource management - * - Schedule mode detection for proper task execution - * - Requirement fulfillment through the associated PrePostScheduleRequirements - */ -@Slf4j -public class SchedulableExamplePrePostScheduleTasks extends AbstractPrePostScheduleTasks { - - private final SchedulableExamplePlugin examplePlugin; - private final SchedulableExamplePrePostScheduleRequirements requirements; - - /** - * Constructor for SchedulableExamplePrePostScheduleTasks. - * - * @param plugin The SchedulableExamplePlugin instance - * @param requirements The requirements collection for this plugin - */ - public SchedulableExamplePrePostScheduleTasks(SchedulableExamplePlugin plugin, KeyManager keyManager, SchedulableExamplePrePostScheduleRequirements requirements) { - super(plugin,keyManager); - this.examplePlugin = plugin; - this.requirements = requirements; - } - - /** - * Executes custom pre-schedule preparation tasks for the example plugin. - * This method is called AFTER standard requirement fulfillment (equipment, spellbook, location). - * The threading and safety infrastructure is handled by the parent class. - * - * @param lockCondition The lock condition to prevent interruption during critical operations - * @return true if custom preparation was successful, false otherwise - */ - @Override - protected boolean executeCustomPreScheduleTask(CompletableFuture preScheduledFuture, LockCondition lockCondition) { - StringBuilder logBuilder = new StringBuilder(); - logBuilder.append("SchedulableExample: Executing custom pre-schedule tasks...\n"); - - // Check if pre/post requirements are enabled - if (!examplePlugin.getConfig().enablePrePostRequirements()) { - logBuilder.append(" Pre/Post requirements are disabled - skipping custom pre-schedule tasks\n"); - log.info(logBuilder.toString()); - return true; - } - - // Get comprehensive validation summary from RequirementRegistry - logBuilder.append("\n=== PRE-SCHEDULE REQUIREMENTS VALIDATION ===\n"); - String validationSummary = requirements.getRegistry().getValidationSummary(net.runelite.client.plugins.microbot.pluginscheduler.tasks.requirements.enums.TaskContext.PRE_SCHEDULE); - logBuilder.append(validationSummary).append("\n"); - - // Get concise status for quick reference - String statusSummary = requirements.getRegistry().getValidationStatusSummary(net.runelite.client.plugins.microbot.pluginscheduler.tasks.requirements.enums.TaskContext.PRE_SCHEDULE); - logBuilder.append("Status Summary: ").append(statusSummary).append("\n\n"); - - // Validate critical mandatory requirements - boolean allMandatoryMet = requirements.getRegistry().getAllRequirements().stream() - .filter(req -> req.getPriority() == net.runelite.client.plugins.microbot.pluginscheduler.tasks.requirements.enums.RequirementPriority.MANDATORY) - .filter(req -> req.isPreSchedule()) - .allMatch(net.runelite.client.plugins.microbot.pluginscheduler.tasks.requirements.requirement.Requirement::isFulfilled); - - if (!allMandatoryMet) { - logBuilder.append("⚠️ WARNING: Some mandatory pre-schedule requirements are not fulfilled\n"); - logBuilder.append(" Continuing execution for testing purposes, but this may affect plugin performance\n"); - } else { - logBuilder.append("✓ All mandatory pre-schedule requirements are properly fulfilled\n"); - } - - // Note about standard requirements handling - logBuilder.append("\n--- Infrastructure Notes ---\n"); - logBuilder.append(" Standard requirements (equipment, spellbook, location) are fulfilled by parent class\n"); - logBuilder.append(" Custom plugin-specific preparation logic can be added here\n"); - logBuilder.append(" Validation summary shows overall requirement status for this context\n"); - - logBuilder.append("\nCustom pre-schedule tasks completed successfully"); - log.info(logBuilder.toString()); - - return true; - } - - /** - * Executes custom post-schedule cleanup tasks for the example plugin. - * This method is called BEFORE standard requirement fulfillment (location, spellbook restoration). - * The threading and safety infrastructure is handled by the parent class. - * - * @param lockCondition The lock condition to prevent interruption during critical operations - * @return true if custom cleanup was successful, false otherwise - */ - @Override - protected boolean executeCustomPostScheduleTask(CompletableFuture postScheduledFuture, LockCondition lockCondition) { - StringBuilder logBuilder = new StringBuilder(); - logBuilder.append("SchedulableExample: Executing custom post-schedule tasks...\n"); - - // Check if pre/post requirements are enabled - if (!examplePlugin.getConfig().enablePrePostRequirements()) { - logBuilder.append(" Pre/Post requirements are disabled - skipping custom post-schedule tasks\n"); - log.info(logBuilder.toString()); - return true; - } - - // Get comprehensive validation summary from RequirementRegistry for post-schedule context - logBuilder.append("\n=== POST-SCHEDULE REQUIREMENTS VALIDATION ===\n"); - String validationSummary = requirements.getRegistry().getValidationSummary(net.runelite.client.plugins.microbot.pluginscheduler.tasks.requirements.enums.TaskContext.POST_SCHEDULE); - logBuilder.append(validationSummary).append("\n"); - - // Get concise status for quick reference - String statusSummary = requirements.getRegistry().getValidationStatusSummary(net.runelite.client.plugins.microbot.pluginscheduler.tasks.requirements.enums.TaskContext.POST_SCHEDULE); - logBuilder.append("Status Summary: ").append(statusSummary).append("\n\n"); - - // Session completion summary - logBuilder.append("--- Session Completion Summary ---\n"); - - // Overall requirements processed during the session - int totalRequirements = requirements.getRegistry().getAllRequirements().size(); - int externalRequirements = requirements.getRegistry().getExternalRequirements(net.runelite.client.plugins.microbot.pluginscheduler.tasks.requirements.enums.TaskContext.BOTH).size(); - - logBuilder.append(" Total requirements processed: ").append(totalRequirements).append("\n"); - if (externalRequirements > 0) { - logBuilder.append(" External requirements: ").append(externalRequirements).append("\n"); - } - - // Validate post-schedule mandatory requirements - boolean allPostMandatoryMet = requirements.getRegistry().getAllRequirements().stream() - .filter(req -> req.getPriority() == net.runelite.client.plugins.microbot.pluginscheduler.tasks.requirements.enums.RequirementPriority.MANDATORY) - .filter(req -> req.isPostSchedule()) - .allMatch(net.runelite.client.plugins.microbot.pluginscheduler.tasks.requirements.requirement.Requirement::isFulfilled); - - if (!allPostMandatoryMet) { - logBuilder.append("⚠️ WARNING: Some mandatory post-schedule requirements are not fulfilled\n"); - } else if (requirements.getRegistry().getAllRequirements().stream().anyMatch(req -> req.getPriority() == net.runelite.client.plugins.microbot.pluginscheduler.tasks.requirements.enums.RequirementPriority.MANDATORY && req.isPostSchedule())) { - logBuilder.append("✓ All mandatory post-schedule requirements are properly fulfilled\n"); - } - - // Custom cleanup operations for the example plugin - logBuilder.append("\n--- Custom Plugin Cleanup ---\n"); - logBuilder.append(" ✓ Example-specific inventory cleanup completed\n"); - logBuilder.append(" ✓ Example-specific session data saved\n"); - logBuilder.append(" ✓ Plugin state reset to initial configuration\n"); - - // Note about standard requirements handling - logBuilder.append("\n--- Infrastructure Notes ---\n"); - logBuilder.append(" Standard requirements (location, spellbook restoration) will be fulfilled by parent class\n"); - logBuilder.append(" Custom plugin-specific cleanup logic has been executed\n"); - logBuilder.append(" Validation summary shows overall requirement status for post-schedule context\n"); - - logBuilder.append("\nCustom post-schedule tasks completed successfully"); - log.info(logBuilder.toString()); - - return true; - } - - - - /** - * Implementation of the abstract method from AbstractPrePostScheduleTasks. - * Returns the PrePostScheduleRequirements instance for this plugin. - * - * @return The requirements collection - */ - @Override - protected PrePostScheduleRequirements getPrePostScheduleRequirements() { - return requirements; - } - - /** - * Gets a reference to the plugin's configuration for convenience. - * - * @return The SchedulableExampleConfig instance - */ - public SchedulableExampleConfig getConfig() { - return examplePlugin.getConfig(); - } - -} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/VoxPlugins/schedulable/example/SchedulableExampleScript.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/VoxPlugins/schedulable/example/SchedulableExampleScript.java deleted file mode 100644 index d1dbe17cbb3..00000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/VoxPlugins/schedulable/example/SchedulableExampleScript.java +++ /dev/null @@ -1,495 +0,0 @@ -package net.runelite.client.plugins.microbot.VoxPlugins.schedulable.example; - -import net.runelite.api.Constants; -import net.runelite.api.coords.WorldPoint; -import net.runelite.api.gameval.ItemID; -import net.runelite.client.plugins.microbot.Microbot; -import net.runelite.client.plugins.microbot.Script; -import net.runelite.client.plugins.microbot.breakhandler.BreakHandlerScript; -import net.runelite.client.plugins.microbot.util.antiban.Rs2Antiban; -import net.runelite.client.plugins.microbot.util.antiban.Rs2AntibanSettings; -import net.runelite.client.plugins.microbot.util.antiban.enums.Activity; -import net.runelite.client.plugins.microbot.util.antiban.enums.ActivityIntensity; -import net.runelite.client.plugins.microbot.util.equipment.Rs2Equipment; -import net.runelite.client.plugins.microbot.util.events.PluginPauseEvent; -import net.runelite.client.plugins.microbot.util.inventory.Rs2Inventory; -import net.runelite.client.plugins.microbot.util.inventory.Rs2ItemModel; -import net.runelite.client.plugins.microbot.util.player.Rs2Player; -// import net.runelite.client.plugins.microbot.util.walker.Rs2Walker; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeUnit; -import java.util.regex.Pattern; -import lombok.extern.slf4j.Slf4j; -@Slf4j -public class SchedulableExampleScript extends Script { - private SchedulableExampleConfig config; - private WorldPoint returnPoint; - private int logsCollected = 0; - - // Antiban testing state - private boolean antibanOriginalTakeMicroBreaks = false; - private double antibanOriginalMicroBreakChance = 0.0; - private int antibanOriginalMicroBreakDurationLow = 3; - private int antibanOriginalMicroBreakDurationHigh = 15; - private boolean antibanOriginalActionCooldownActive = false; - private boolean antibanOriginalMoveMouseOffScreen = false; - private long lastStatusReport = 0; - private long lastBreakStatusCheck = 0; - private boolean wasOnBreak = false; - private long breakStartTime = 0; - private long totalBreakTime = 0; - private int microBreakCount = 0; - private boolean antibanInitialized = false; - private int aliveCounter = 0; // Counter to track when to report alive - - enum State { - IDELE, - RESETTING, - BREAK_PAUSED - } - - private State state = State.IDELE; - - - - public boolean main(SchedulableExampleConfig config) { - if (!Microbot.isLoggedIn()) return false; - if (!super.run()) return false; - - // Initialize antiban settings if enabled - if (config.enableAntibanTesting() && !antibanInitialized) { - setupAntibanTesting(); - } - - // Check break status and handle state changes - handleBreakStatusChecks(); - - // Set initial location if none was saved - if (initialPlayerLocation == null) { - initialPlayerLocation = Rs2Player.getWorldLocation(); - } - - if (this.returnPoint == null) { - this.returnPoint = initialPlayerLocation; - } - - // Check if we have an axe - if (!hasAxe()) { - // Microbot.status = "No axe found! Stopping..."; - // return false; - } - - // Handle break pause state - don't do anything while on break - if (state == State.BREAK_PAUSED) { - return true; - } - - // Skip if player is moving or animating, unless resetting - if (state != State.RESETTING && (Rs2Player.isMoving() || Rs2Player.isAnimating())) { - return true; - } - - // Trigger antiban behaviors if enabled - if (config.enableAntibanTesting()) { - handleAntibanBehaviors(); - } - - switch (state) { - case IDELE: - if (Rs2Inventory.isFull()) { - state = State.RESETTING; - return true; - } - break; - - case RESETTING: - resetInventory(); - return true; - - case BREAK_PAUSED: - // Already handled above - return true; - } - - return true; - } - - public boolean run(SchedulableExampleConfig config, WorldPoint savedLocation) { - this.returnPoint = savedLocation; - this.config = config; - this.aliveCounter = 0; - this.mainScheduledFuture = scheduledExecutorService.scheduleWithFixedDelay(() -> { - try { - if (!Microbot.isLoggedIn()) return; - if (!super.run()) return; - log.info("aliveCounter: {}", aliveCounter); - // Call the main method with antiban testing - main(config); - // Increment counter and check if we should report alive - aliveCounter++; - // Compute iterations from milliseconds, clamp to at least 1 - final long periodMs = Constants.GAME_TICK_LENGTH * 2L; - final long timeoutMs = Math.max(0L, (long) config.aliveReportTimeout()*1000L); - final int reportThreshold = (int) Math.max(1L, (long) Math.ceil(timeoutMs / (double) periodMs)); - if (aliveCounter >= reportThreshold) { - Rs2ItemModel oneDosePrayerRegeneration= Rs2ItemModel.createFromCache(ItemID._1DOSE1PRAYER_REGENERATION,1,1); - List equipmentActions =oneDosePrayerRegeneration.getEquipmentActions(); - boolean isTradeable = oneDosePrayerRegeneration.isTradeable(); - log.info("{}",oneDosePrayerRegeneration.toString() ); - Rs2ItemModel graceFullHelm= Rs2ItemModel.createFromCache(ItemID.GRACEFUL_HOOD,1,1); - List graceFullHelmActions = graceFullHelm.getEquipmentActions(); - boolean isGraceFullHelmTradeable = graceFullHelm.isTradeable(); - log.info("{}",graceFullHelm.toString() ); - log.info("SchedulableExampleScript is alive! \n- PauseEvent {} (valid),pauseAllScripts: {}, BreakHanlderLook: {}", PluginPauseEvent.isPaused(), Microbot.pauseAllScripts.get(), BreakHandlerScript.lockState.get()); - aliveCounter = 0; // Reset counter - } - - return; //manuel play testing the Scheduler plugin.. doing nothing for now - } catch (Exception ex) { - Microbot.log("SchedulableExampleScript error: " + ex.getMessage()); - } - }, 0, Constants.GAME_TICK_LENGTH*2, TimeUnit.MILLISECONDS); - - return true; - } - - private boolean hasAxe() { - return Rs2Inventory.hasItem("axe") || Rs2Equipment.isWearing("axe"); - } - - private void resetInventory() { - // Update count before moving to next state - updateItemCount(); - state = State.IDELE; - } - - /* - private void bankItems() { - Microbot.status = "Banking "; - - // Find and use nearest bank - if (!Rs2Bank.isOpen()) { - if (!Rs2Bank.useBank()) { - return; - } - } - - // Deposit logs but keep axe - Rs2Bank.depositAllExcept("axe"); - Rs2Bank.closeBank(); - - // Return to woodcutting spot - walkToReturnPoint(); - } - */ - private List getLootItemPatterns(){ - String lootItemsString = config.lootItems(); - List lootItemsList = new ArrayList<>(); - List lootItemsListPattern = new ArrayList<>(); - if (lootItemsString != null && !lootItemsString.isEmpty()) { - String[] lootItemsArray = lootItemsString.split(","); - for (String item : lootItemsArray) { - String trimmedItem = item.trim(); - try { - // Validate regex pattern - lootItemsListPattern.add(java.util.regex.Pattern.compile(trimmedItem)); - lootItemsList.add(trimmedItem); - } catch (java.util.regex.PatternSyntaxException e) { - //log.warn("Invalid regex pattern: '{}' - {}", trimmedItem, e.getMessage()); - } - } - } - return lootItemsListPattern; - } - /* - private void dropItems() { - Microbot.status = "Dropping Items"; - - // Drop all logs - List lootItemsListPattern = getLootItemPatterns(); - //List foundItems = - Rs2Inventory.all().forEach(item -> { - if (lootItemsListPattern.stream().anyMatch(pattern -> pattern.matcher(item.getName()).find())) { - - }else{ - // Drop all logs - Rs2Inventory.dropAll(item.getName()); - } - - }); - // Drop all logs - - } - - - - private void walkToReturnPoint() { - if (Rs2Player.getWorldLocation().distanceTo(returnPoint) > 3) { - Rs2Walker.walkTo(returnPoint); - } - } - */ - - public void updateItemCount() { - List lootItemsListPattern = getLootItemPatterns(); - int currentItems =Rs2Inventory.all().stream().filter(item -> lootItemsListPattern.stream().anyMatch(pattern -> pattern.matcher(item.getName()).find())).mapToInt(Rs2ItemModel::getQuantity).sum(); - - - if (currentItems > 0) { - logsCollected += currentItems; - Microbot.log("Total logs collected: " + logsCollected); - } - } - - public int getLogsCollected() { - return logsCollected; - } - - @Override - public void shutdown() { - // Teardown antiban testing if it was initialized - if (config != null && config.enableAntibanTesting()) { - teardownAntibanTesting(); - } - - Microbot.log("Shutting down SchedulableExampleScript"); - super.shutdown(); - returnPoint = null; - - // Log final antiban stats if testing was enabled - if (config != null && config.enableAntibanTesting()) { - Microbot.log("Final " + getAntibanStats()); - } - } - - /** - * Sets up antiban testing configuration based on plugin config - */ - private void setupAntibanTesting() { - if (antibanInitialized) { - return; - } - - Microbot.log("Setting up antiban testing..."); - - // Store original antiban settings - antibanOriginalTakeMicroBreaks = Rs2AntibanSettings.takeMicroBreaks; - antibanOriginalMicroBreakChance = Rs2AntibanSettings.microBreakChance; - antibanOriginalMicroBreakDurationLow = Rs2AntibanSettings.microBreakDurationLow; - antibanOriginalMicroBreakDurationHigh = Rs2AntibanSettings.microBreakDurationHigh; - antibanOriginalActionCooldownActive = Rs2AntibanSettings.actionCooldownActive; - antibanOriginalMoveMouseOffScreen = Rs2AntibanSettings.moveMouseOffScreen; - - // Apply test configuration - if (config.enableMicroBreaks()) { - Rs2AntibanSettings.takeMicroBreaks = true; - Rs2AntibanSettings.microBreakChance = config.microBreakChancePercent() / 100.0; - Rs2AntibanSettings.microBreakDurationLow = config.microBreakDurationMin(); - Rs2AntibanSettings.microBreakDurationHigh = config.microBreakDurationMax(); - - Microbot.log("Micro breaks enabled - Chance: " + (config.microBreakChancePercent()) + - "%, Duration: " + config.microBreakDurationMin() + "-" + config.microBreakDurationMax() + " minutes"); - } - - if (config.enableActionCooldowns()) { - Rs2AntibanSettings.actionCooldownActive = true; - Microbot.log("Action cooldowns enabled"); - } - - if (config.moveMouseOffScreen()) { - Rs2AntibanSettings.moveMouseOffScreen = true; - Microbot.log("Mouse off-screen movement enabled"); - } - - // Set antiban activity - Rs2Antiban.setActivity(Activity.GENERAL_WOODCUTTING); - Rs2Antiban.setActivityIntensity(ActivityIntensity.MODERATE); - - antibanInitialized = true; - Microbot.log("Antiban testing setup complete"); - } - - /** - * Restores original antiban settings - */ - private void teardownAntibanTesting() { - if (!antibanInitialized) { - return; - } - - Microbot.log("Restoring original antiban settings..."); - - // Restore original settings - Rs2AntibanSettings.takeMicroBreaks = antibanOriginalTakeMicroBreaks; - Rs2AntibanSettings.microBreakChance = antibanOriginalMicroBreakChance; - Rs2AntibanSettings.microBreakDurationLow = antibanOriginalMicroBreakDurationLow; - Rs2AntibanSettings.microBreakDurationHigh = antibanOriginalMicroBreakDurationHigh; - Rs2AntibanSettings.actionCooldownActive = antibanOriginalActionCooldownActive; - Rs2AntibanSettings.moveMouseOffScreen = antibanOriginalMoveMouseOffScreen; - - // Reset antiban activity - Rs2Antiban.resetAntibanSettings(); - - antibanInitialized = false; - Microbot.log("Antiban settings restored"); - } - - /** - * Handles break status monitoring and state transitions - */ - private void handleBreakStatusChecks() { - long currentTime = System.currentTimeMillis(); - - // Check break status every second - if (currentTime - lastBreakStatusCheck < 1000) { - return; - } - lastBreakStatusCheck = currentTime; - - boolean isCurrentlyOnBreak = BreakHandlerScript.isBreakActive() || - Rs2AntibanSettings.microBreakActive || - Rs2AntibanSettings.actionCooldownActive; - - // Detect break start - if (isCurrentlyOnBreak && !wasOnBreak) { - handleBreakStart(); - } - // Detect break end - else if (!isCurrentlyOnBreak && wasOnBreak) { - handleBreakEnd(); - } - - // Report status periodically if on break - if (isCurrentlyOnBreak && config.statusReportInterval() > 0) { - if (currentTime - lastStatusReport >= config.statusReportInterval() * 1000) { - reportBreakStatus(); - lastStatusReport = currentTime; - } - } - - wasOnBreak = isCurrentlyOnBreak; - } - - /** - * Handles the start of a break - */ - private void handleBreakStart() { - breakStartTime = System.currentTimeMillis(); - state = State.BREAK_PAUSED; - - String breakType = getBreakType(); - Microbot.log("Break started - Type: " + breakType + ", Script state: PAUSED"); - - if (Rs2AntibanSettings.microBreakActive) { - microBreakCount++; - } - } - - /** - * Handles the end of a break - */ - private void handleBreakEnd() { - if (breakStartTime > 0) { - long breakDuration = System.currentTimeMillis() - breakStartTime; - totalBreakTime += breakDuration; - - String breakType = getBreakType(); - Microbot.log("Break ended - Type: " + breakType + - ", Duration: " + formatDuration(breakDuration) + - ", Script state: RESUMED"); - - breakStartTime = 0; - } - - // Resume normal operation - if (state == State.BREAK_PAUSED) { - state = State.IDELE; - } - } - - /** - * Reports current break status - */ - private void reportBreakStatus() { - String breakType = getBreakType(); - long currentBreakDuration = breakStartTime > 0 ? - System.currentTimeMillis() - breakStartTime : 0; - - Microbot.log("Break Status - Type: " + breakType + - ", Current Duration: " + formatDuration(currentBreakDuration) + - ", Total Break Time: " + formatDuration(totalBreakTime) + - ", Micro Breaks: " + microBreakCount); - } - - /** - * Determines the current break type - */ - private String getBreakType() { - if (BreakHandlerScript.isBreakActive()) { - return "Regular Break"; - } else if (Rs2AntibanSettings.microBreakActive) { - return "Micro Break"; - } else if (Rs2AntibanSettings.actionCooldownActive) { - return "Action Cooldown"; - } else { - return "Unknown"; - } - } - - /** - * Handles antiban behaviors during normal operation - */ - private void handleAntibanBehaviors() { - // Trigger action cooldown occasionally - if (config.enableActionCooldowns() && Math.random() < 0.01) { // 1% chance per tick - Rs2Antiban.actionCooldown(); - } - - // Take micro breaks by chance - if (config.enableMicroBreaks() && Math.random() < (config.microBreakChancePercent() / 100.0 )) { - Rs2Antiban.takeMicroBreakByChance(); - if (Rs2AntibanSettings.microBreakActive) { - Microbot.log("Taking a new micro break - Count: " + microBreakCount); - } - } - - // Move mouse randomly - if (config.moveMouseOffScreen() && Math.random() < 0.005) { // 0.5% chance per tick - Rs2Antiban.moveMouseRandomly(); - } - } - - /** - * Formats a duration in milliseconds to a readable string - */ - private String formatDuration(long durationMillis) { - long seconds = durationMillis / 1000; - long minutes = seconds / 60; - long hours = minutes / 60; - - if (hours > 0) { - return String.format("%dh %dm %ds", hours, minutes % 60, seconds % 60); - } else if (minutes > 0) { - return String.format("%dm %ds", minutes, seconds % 60); - } else { - return String.format("%ds", seconds); - } - } - - /** - * Gets comprehensive antiban testing statistics - */ - public String getAntibanStats() { - if (!config.enableAntibanTesting()) { - return "Antiban testing disabled"; - } - - return String.format("Antiban Stats - Total Break Time: %s, Micro Breaks: %d, " + - "Current State: %s, Break Active: %s", - formatDuration(totalBreakTime), - microBreakCount, - state.name(), - wasOnBreak ? getBreakType() : "None"); - } -} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/VoxPlugins/schedulable/example/enums/SpellbookOption.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/VoxPlugins/schedulable/example/enums/SpellbookOption.java deleted file mode 100644 index c23bd3de976..00000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/VoxPlugins/schedulable/example/enums/SpellbookOption.java +++ /dev/null @@ -1,49 +0,0 @@ -package net.runelite.client.plugins.microbot.VoxPlugins.schedulable.example.enums; - -import net.runelite.client.plugins.microbot.util.magic.Rs2Spellbook; -import lombok.Getter; - -/** - * Unified spellbook enum that includes a "NONE" option for configurations - * where no spellbook switching is desired. - */ -@Getter -public enum SpellbookOption { - - NONE("None (No Switching)", null), - MODERN("Standard/Modern Spellbook", Rs2Spellbook.MODERN), - ANCIENT("Ancient Magicks", Rs2Spellbook.ANCIENT), - LUNAR("Lunar Spellbook", Rs2Spellbook.LUNAR), - ARCEUUS("Arceuus Spellbook", Rs2Spellbook.ARCEUUS); - - private final String displayName; - private final Rs2Spellbook spellbook; - - SpellbookOption(String displayName, Rs2Spellbook spellbook) { - this.displayName = displayName; - this.spellbook = spellbook; - } - - /** - * Gets the Rs2Spellbook enum value, or null if this is the NONE option - * - * @return Rs2Spellbook enum value or null - */ - public Rs2Spellbook getSpellbook() { - return spellbook; - } - - /** - * Checks if this option represents no spellbook switching - * - * @return true if this is the NONE option - */ - public boolean isNone() { - return this == NONE; - } - - @Override - public String toString() { - return displayName; - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/VoxPlugins/schedulable/example/enums/UnifiedLocation.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/VoxPlugins/schedulable/example/enums/UnifiedLocation.java deleted file mode 100644 index af6bfd6197d..00000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/VoxPlugins/schedulable/example/enums/UnifiedLocation.java +++ /dev/null @@ -1,190 +0,0 @@ -package net.runelite.client.plugins.microbot.VoxPlugins.schedulable.example.enums; - -import net.runelite.api.coords.WorldPoint; -import net.runelite.client.plugins.microbot.util.bank.enums.BankLocation; -import net.runelite.client.plugins.microbot.util.depositbox.DepositBoxLocation; -import net.runelite.client.plugins.microbot.util.walker.enums.*; -import lombok.Getter; - -/** - * Unified location enum that encompasses major location types used in the walker system. - * This provides a single interface for selecting locations from various categories - * including banks, deposit boxes, farming locations, slayer masters, and hunting areas. - * - * This is a simplified version containing only the most commonly used locations - * to avoid compatibility issues with varying enum implementations. - */ -@Getter -public enum UnifiedLocation { - - // None option - NONE("None", LocationType.NONE, null), - - // Major Bank Locations - BANK_GRAND_EXCHANGE("Grand Exchange Bank", LocationType.BANK, BankLocation.GRAND_EXCHANGE), - BANK_VARROCK_WEST("Varrock West Bank", LocationType.BANK, BankLocation.VARROCK_WEST), - BANK_VARROCK_EAST("Varrock East Bank", LocationType.BANK, BankLocation.VARROCK_EAST), - BANK_LUMBRIDGE_FRONT("Lumbridge Bank", LocationType.BANK, BankLocation.LUMBRIDGE_FRONT), - BANK_FALADOR_WEST("Falador West Bank", LocationType.BANK, BankLocation.FALADOR_WEST), - BANK_FALADOR_EAST("Falador East Bank", LocationType.BANK, BankLocation.FALADOR_EAST), - BANK_EDGEVILLE("Edgeville Bank", LocationType.BANK, BankLocation.EDGEVILLE), - BANK_DRAYNOR_VILLAGE("Draynor Village Bank", LocationType.BANK, BankLocation.DRAYNOR_VILLAGE), - BANK_AL_KHARID("Al Kharid Bank", LocationType.BANK, BankLocation.AL_KHARID), - BANK_CATHERBY("Catherby Bank", LocationType.BANK, BankLocation.CATHERBY), - BANK_CAMELOT("Camelot Bank", LocationType.BANK, BankLocation.CAMELOT), - BANK_ARDOUGNE_NORTH("Ardougne North Bank", LocationType.BANK, BankLocation.ARDOUGNE_NORTH), - BANK_ARDOUGNE_SOUTH("Ardougne South Bank", LocationType.BANK, BankLocation.ARDOUGNE_SOUTH), - BANK_CANIFIS("Canifis Bank", LocationType.BANK, BankLocation.CANIFIS), - BANK_FISHING_GUILD("Fishing Guild Bank", LocationType.BANK, BankLocation.FISHING_GUILD), - BANK_FOSSIL_ISLAND("Fossil Island Bank", LocationType.BANK, BankLocation.FOSSIL_ISLAND), - BANK_ARCEUUS("Arceuus Bank", LocationType.BANK, BankLocation.ARCEUUS), - BANK_HOSIDIUS("Hosidius Bank", LocationType.BANK, BankLocation.HOSIDIUS), - BANK_LOVAKENGJ("Lovakengj Bank", LocationType.BANK, BankLocation.LOVAKENGJ), - BANK_PISCARILIUS("Piscarilius Bank", LocationType.BANK, BankLocation.PISCARILIUS), - BANK_SHAYZIEN_BANK("Shayzien Bank", LocationType.BANK, BankLocation.SHAYZIEN_BANK), - BANK_FARMING_GUILD("Farming Guild Bank", LocationType.BANK, BankLocation.FARMING_GUILD), - - // Major Deposit Box Locations - DEPOSIT_BOX_GRAND_EXCHANGE("Grand Exchange Deposit Box", LocationType.DEPOSIT_BOX, DepositBoxLocation.GRAND_EXCHANGE), - DEPOSIT_BOX_EDGEVILLE("Edgeville Deposit Box", LocationType.DEPOSIT_BOX, DepositBoxLocation.EDGEVILLE), - DEPOSIT_BOX_BARBARIAN_ASSAULT("Barbarian Assault Deposit Box", LocationType.DEPOSIT_BOX, DepositBoxLocation.BARBARIAN_ASSAULT), - DEPOSIT_BOX_FALADOR("Falador Deposit Box", LocationType.DEPOSIT_BOX, DepositBoxLocation.FALADOR), - DEPOSIT_BOX_VARROCK("Varrock Deposit Box", LocationType.DEPOSIT_BOX, DepositBoxLocation.VARROCK), - DEPOSIT_BOX_LUMBRIDGE("Lumbridge Deposit Box", LocationType.DEPOSIT_BOX, DepositBoxLocation.LUMBRIDGE), - DEPOSIT_BOX_FARMING_GUILD("Farming Guild Deposit Box", LocationType.DEPOSIT_BOX, DepositBoxLocation.FARMING_GUILD), - - // Major Slayer Masters - SLAYER_MASTER_TURAEL("Turael (Burthorpe)", LocationType.SLAYER_MASTER, SlayerMasters.TURAEL), - SLAYER_MASTER_SPRIA("Spria (Draynor Village)", LocationType.SLAYER_MASTER, SlayerMasters.SPRIA), - SLAYER_MASTER_MAZCHNA("Mazchna (Canifis)", LocationType.SLAYER_MASTER, SlayerMasters.MAZCHNA), - SLAYER_MASTER_VANNAKA("Vannaka (Edgeville Dungeon)", LocationType.SLAYER_MASTER, SlayerMasters.VANNAKA), - SLAYER_MASTER_CHAELDAR("Chaeldar (Zanaris)", LocationType.SLAYER_MASTER, SlayerMasters.CHAELDAR), - SLAYER_MASTER_KONAR("Konar quo Maten (Mount Karuulm)", LocationType.SLAYER_MASTER, SlayerMasters.KONAR), - SLAYER_MASTER_NIEVE("Nieve (Gnome Stronghold)", LocationType.SLAYER_MASTER, SlayerMasters.NIEVE), - SLAYER_MASTER_STEVE("Steve (Gnome Stronghold)", LocationType.SLAYER_MASTER, SlayerMasters.STEVE), - SLAYER_MASTER_DURADEL("Duradel (Shilo Village)", LocationType.SLAYER_MASTER, SlayerMasters.DURADEL), - SLAYER_MASTER_KRYSTILIA("Krystilia (Edgeville)", LocationType.SLAYER_MASTER, SlayerMasters.KRYSTILIA), - - // Major Farming Locations - Allotments - FARMING_FALADOR_ALLOTMENT("Falador Allotment", LocationType.FARMING, Allotments.FALADOR), - FARMING_CATHERBY_ALLOTMENT("Catherby Allotment", LocationType.FARMING, Allotments.CATHERBY), - FARMING_ARDOUGNE_ALLOTMENT("Ardougne Allotment", LocationType.FARMING, Allotments.ARDOUGNE), - FARMING_MORYTANIA_ALLOTMENT("Morytania Allotment", LocationType.FARMING, Allotments.MORYTANIA), - FARMING_KOUREND_ALLOTMENT("Kourend Allotment", LocationType.FARMING, Allotments.KOUREND), - FARMING_GUILD_ALLOTMENT("Farming Guild Allotment", LocationType.FARMING, Allotments.FARMING_GUILD), - - // Major Farming Locations - Trees - FARMING_TREE_FALADOR("Falador Tree", LocationType.FARMING, Trees.FALADOR), - FARMING_TREE_FARMING_GUILD("Farming Guild Tree", LocationType.FARMING, Trees.FARMING_GUILD), - FARMING_TREE_GNOME_STRONGHOLD("Gnome Stronghold Tree", LocationType.FARMING, Trees.GNOME_STRONGHOLD), - FARMING_TREE_LUMBRIDGE("Lumbridge Tree", LocationType.FARMING, Trees.LUMBRIDGE), - FARMING_TREE_TAVERLEY("Taverley Tree", LocationType.FARMING, Trees.TAVERLEY), - FARMING_TREE_VARROCK("Varrock Tree", LocationType.FARMING, Trees.VARROCK), - - // Major Farming Locations - Fruit Trees - FARMING_FRUIT_TREE_BRIMHAVEN("Brimhaven Fruit Tree", LocationType.FARMING, FruitTrees.BRIMHAVEN), - FARMING_FRUIT_TREE_CATHERBY("Catherby Fruit Tree", LocationType.FARMING, FruitTrees.CATHERBY), - FARMING_FRUIT_TREE_FARMING_GUILD("Farming Guild Fruit Tree", LocationType.FARMING, FruitTrees.FARMING_GUILD), - FARMING_FRUIT_TREE_GNOME_STRONGHOLD("Gnome Stronghold Fruit Tree", LocationType.FARMING, FruitTrees.GNOME_STRONGHOLD), - FARMING_FRUIT_TREE_TREE_GNOME_VILLAGE("Tree Gnome Village Fruit Tree", LocationType.FARMING, FruitTrees.TREE_GNOME_VILLAGE), - FARMING_FRUIT_TREE_TAI_BWO_WANNAI("Tai Bwo Wannai Fruit Tree", LocationType.FARMING, FruitTrees.TAI_BWO_WANNAI), - FARMING_FRUIT_TREE_PRIFDDINAS("Prifddinas Fruit Tree", LocationType.FARMING, FruitTrees.PRIFDDINAS); - - private final String displayName; - private final LocationType type; - private final Object locationData; - - UnifiedLocation(String displayName, LocationType type, Object locationData) { - this.displayName = displayName; - this.type = type; - this.locationData = locationData; - } - - /** - * Gets the WorldPoint for this location. - * - * @return WorldPoint if available, null otherwise - */ - public WorldPoint getWorldPoint() { - if (locationData == null) { - return null; - } - - switch (type) { - case BANK: - return ((BankLocation) locationData).getWorldPoint(); - - case DEPOSIT_BOX: - return ((DepositBoxLocation) locationData).getWorldPoint(); - - case SLAYER_MASTER: - return ((SlayerMasters) locationData).getWorldPoint(); - - case FARMING: - if (locationData instanceof Allotments) { - return ((Allotments) locationData).getWorldPoint(); - } else if (locationData instanceof Herbs) { - return ((Herbs) locationData).getWorldPoint(); - } else if (locationData instanceof Trees) { - return ((Trees) locationData).getWorldPoint(); - } else if (locationData instanceof FruitTrees) { - return ((FruitTrees) locationData).getWorldPoint(); - } else if (locationData instanceof Bushes) { - return ((Bushes) locationData).getWorldPoint(); - } else if (locationData instanceof Hops) { - return ((Hops) locationData).getWorldPoint(); - } else if (locationData instanceof CompostBins) { - return ((CompostBins) locationData).getWorldPoint(); - } - break; - - case HUNTING: - if (locationData instanceof Birds) { - return ((Birds) locationData).getWorldPoint(); - } else if (locationData instanceof Chinchompas) { - return ((Chinchompas) locationData).getWorldPoint(); - } else if (locationData instanceof Insects) { - return ((Insects) locationData).getWorldPoint(); - } else if (locationData instanceof Kebbits) { - return ((Kebbits) locationData).getWorldPoint(); - } else if (locationData instanceof Salamanders) { - return ((Salamanders) locationData).getWorldPoint(); - } else if (locationData instanceof SpecialHuntingAreas) { - return ((SpecialHuntingAreas) locationData).getWorldPoint(); - } - break; - - case NONE: - default: - return null; - } - - return null; - } - - /** - * Gets the original location data object (BankLocation, DepositBoxLocation, etc.) - * - * @return The original location data object - */ - public Object getOriginalLocationData() { - return locationData; - } - - @Override - public String toString() { - return displayName; - } - - /** - * Enum representing the different types of locations - */ - public enum LocationType { - NONE, - BANK, - DEPOSIT_BOX, - SLAYER_MASTER, - FARMING, - HUNTING - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/example/ExampleScript.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/example/ExampleScript.java index 2de6c7670f8..8f3f573f190 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/example/ExampleScript.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/example/ExampleScript.java @@ -43,6 +43,8 @@ public boolean run() { try { if (!Microbot.isLoggedIn()) return; + System.out.println("hello world"); + WorldPoint worldPoint = WorldPoint.fromRegion(Microbot.getClient().getLocalPlayer().getWorldLocation().getRegionID(), 35, 34, diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/pluginscheduler/tasks/requirements/registry/RequirementRegistry.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/pluginscheduler/tasks/requirements/registry/RequirementRegistry.java index 03d0a5fa46a..d19c504f97e 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/pluginscheduler/tasks/requirements/registry/RequirementRegistry.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/pluginscheduler/tasks/requirements/registry/RequirementRegistry.java @@ -22,8 +22,6 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; -import org.benf.cfr.reader.util.output.BytecodeDumpConsumer.Item; - /** * Enhanced requirement registry that manages all types of requirements with automatic * uniqueness enforcement, consistency guarantees, and efficient lookup. diff --git a/runelite-gradle-plugin/build.gradle.kts b/runelite-gradle-plugin/build.gradle.kts new file mode 100644 index 00000000000..4f3e5384c2f --- /dev/null +++ b/runelite-gradle-plugin/build.gradle.kts @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2024, LlemonDuck + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +plugins { + `java-gradle-plugin` + pmd +} + +dependencies { + implementation("net.runelite:cache:${project.version}") + + implementation(libs.guava) + implementation(libs.tomlj) + implementation(libs.javapoet) +} + +gradlePlugin { + plugins { + create("rl-assemble") { + id = "net.runelite.runelite-gradle-plugin.assemble" + implementationClass = "net.runelite.gradle.assemble.AssemblePlugin" + } + create("rl-component") { + id = "net.runelite.runelite-gradle-plugin.component" + implementationClass = "net.runelite.gradle.component.ComponentPlugin" + } + create("rl-index") { + id = "net.runelite.runelite-gradle-plugin.index" + implementationClass = "net.runelite.gradle.index.IndexPlugin" + } + create("rl-jarsign") { + id = "net.runelite.runelite-gradle-plugin.jarsign" + implementationClass = "net.runelite.gradle.jarsign.JarsignPlugin" + } + } +} + +pmd { + toolVersion = "7.2.0" + ruleSetFiles("./pmd-ruleset.xml") + isConsoleOutput = true + incrementalAnalysis = true + isIgnoreFailures = false + threads = Runtime.getRuntime().availableProcessors() +} diff --git a/runelite-maven-plugin/pmd-ruleset.xml b/runelite-gradle-plugin/pmd-ruleset.xml similarity index 97% rename from runelite-maven-plugin/pmd-ruleset.xml rename to runelite-gradle-plugin/pmd-ruleset.xml index d269a39b8f4..d060dd1b2ac 100644 --- a/runelite-maven-plugin/pmd-ruleset.xml +++ b/runelite-gradle-plugin/pmd-ruleset.xml @@ -29,7 +29,7 @@ xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.sourceforge.io/ruleset_2_0_0.xsd"> - RuneLite Maven Plugin PMD ruleset + RuneLite PMD ruleset diff --git a/runelite-gradle-plugin/settings.gradle.kts b/runelite-gradle-plugin/settings.gradle.kts new file mode 100644 index 00000000000..54a1fadafbf --- /dev/null +++ b/runelite-gradle-plugin/settings.gradle.kts @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2024, LlemonDuck + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +rootProject.name = "runelite-gradle-plugin" +apply(from = "../common.settings.gradle.kts") + +includeBuild("../cache") diff --git a/runelite-gradle-plugin/src/main/java/net/runelite/gradle/assemble/AssemblePlugin.java b/runelite-gradle-plugin/src/main/java/net/runelite/gradle/assemble/AssemblePlugin.java new file mode 100644 index 00000000000..dfe30339389 --- /dev/null +++ b/runelite-gradle-plugin/src/main/java/net/runelite/gradle/assemble/AssemblePlugin.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2024, LlemonDuck + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package net.runelite.gradle.assemble; + +import org.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradle.api.tasks.TaskProvider; + +public abstract class AssemblePlugin implements Plugin +{ + + @Override + public void apply(Project project) + { + TaskProvider assembleRs2asm = project.getTasks() + .register("assembleRs2asm", AssembleTask.class, (task) -> task.setGroup("build")); + + project.getTasks() + .getByName("processResources") + .dependsOn(assembleRs2asm); + } + +} diff --git a/runelite-maven-plugin/src/main/java/net/runelite/mvn/AssembleMojo.java b/runelite-gradle-plugin/src/main/java/net/runelite/gradle/assemble/AssembleTask.java similarity index 68% rename from runelite-maven-plugin/src/main/java/net/runelite/mvn/AssembleMojo.java rename to runelite-gradle-plugin/src/main/java/net/runelite/gradle/assemble/AssembleTask.java index 22acaf4806b..9e35283fe95 100644 --- a/runelite-maven-plugin/src/main/java/net/runelite/mvn/AssembleMojo.java +++ b/runelite-gradle-plugin/src/main/java/net/runelite/gradle/assemble/AssembleTask.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Adam + * Copyright (c) 2024, LlemonDuck * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -22,11 +22,13 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package net.runelite.mvn; + +package net.runelite.gradle.assemble; import com.google.common.io.Files; import java.io.File; import java.io.FileInputStream; +import java.io.FileNotFoundException; import java.io.IOException; import java.util.HashMap; import java.util.Locale; @@ -36,38 +38,45 @@ import net.runelite.cache.definitions.savers.ScriptSaver; import net.runelite.cache.script.RuneLiteInstructions; import net.runelite.cache.script.assembler.Assembler; -import org.apache.maven.plugin.AbstractMojo; -import org.apache.maven.plugin.MojoExecutionException; -import org.apache.maven.plugin.MojoFailureException; -import org.apache.maven.plugin.logging.Log; -import org.apache.maven.plugins.annotations.LifecyclePhase; -import org.apache.maven.plugins.annotations.Mojo; -import org.apache.maven.plugins.annotations.Parameter; +import org.gradle.api.DefaultTask; +import org.gradle.api.file.DirectoryProperty; +import org.gradle.api.file.RegularFileProperty; +import org.gradle.api.logging.Logger; +import org.gradle.api.tasks.CacheableTask; +import org.gradle.api.tasks.InputDirectory; +import org.gradle.api.tasks.InputFile; +import org.gradle.api.tasks.OutputDirectory; +import org.gradle.api.tasks.PathSensitive; +import org.gradle.api.tasks.PathSensitivity; +import org.gradle.api.tasks.TaskAction; import org.tomlj.Toml; import org.tomlj.TomlParseError; import org.tomlj.TomlParseResult; import org.tomlj.TomlTable; -@Mojo( - name = "assemble", - defaultPhase = LifecyclePhase.GENERATE_RESOURCES -) -public class AssembleMojo extends AbstractMojo +@CacheableTask +public abstract class AssembleTask extends DefaultTask { - @Parameter(required = true) - private File scriptDirectory; + @InputDirectory + @PathSensitive(PathSensitivity.RELATIVE) + public abstract DirectoryProperty getScriptDirectory(); - @Parameter(required = true) - private File outputDirectory; + @OutputDirectory + public abstract DirectoryProperty getOutputDirectory(); - @Parameter(required = true) - private File componentsFile; + @InputFile + @PathSensitive(PathSensitivity.RELATIVE) + public abstract RegularFileProperty getComponentsFile(); - private final Log log = getLog(); + private final Logger log = getLogger(); - @Override - public void execute() throws MojoExecutionException, MojoFailureException + @TaskAction + public void assembleRs2Asm() throws IOException { + File scriptDirectory = getScriptDirectory().getAsFile().get(); + File outputDirectory = getOutputDirectory().getAsFile().get(); + File componentsFile = getComponentsFile().getAsFile().get(); + RuneLiteInstructions instructions = new RuneLiteInstructions(); instructions.init(); @@ -80,7 +89,7 @@ public void execute() throws MojoExecutionException, MojoFailureException for (File scriptFile : scriptDirectory.listFiles((dir, name) -> name.endsWith(".rs2asm"))) { - log.debug("Assembling " + scriptFile); + log.debug("Assembling {}", scriptFile); try (FileInputStream fin = new FileInputStream(scriptFile)) { @@ -99,21 +108,17 @@ public void execute() throws MojoExecutionException, MojoFailureException } else if (script.getId() < 10000) // Scripts >=10000 are RuneLite scripts, so they shouldn't have a .hash { - throw new MojoExecutionException("Unable to find hash file for " + scriptFile); + throw new FileNotFoundException("Unable to find hash file for " + scriptFile); } ++count; } - catch (IOException ex) - { - throw new MojoFailureException("unable to open file", ex); - } } - log.info("Assembled " + count + " scripts"); + log.lifecycle("Assembled {} scripts", count); } - private Map buildComponentSymbols(File file) throws MojoExecutionException + private Map buildComponentSymbols(File file) { TomlParseResult result; try @@ -122,7 +127,7 @@ private Map buildComponentSymbols(File file) throws MojoExecutio } catch (IOException e) { - throw new MojoExecutionException("unable to read component file " + file.getName(), e); + throw new RuntimeException("unable to read component file " + file.getName(), e); } if (result.hasErrors()) @@ -131,7 +136,7 @@ private Map buildComponentSymbols(File file) throws MojoExecutio { log.error(err.toString()); } - throw new MojoExecutionException("unable to parse component file " + file.getName()); + throw new RuntimeException("unable to parse component file " + file.getName()); } Map symbols = new HashMap<>(); @@ -142,13 +147,13 @@ private Map buildComponentSymbols(File file) throws MojoExecutio if (!tbl.contains("id")) { - throw new MojoExecutionException("interface " + interfaceName + " has no id"); + throw new RuntimeException("interface " + interfaceName + " has no id"); } int interfaceId = (int) (long) tbl.getLong("id"); if (interfaceId < 0 || interfaceId > 0xffff) { - throw new MojoExecutionException("interface id out of range for " + interfaceName); + throw new RuntimeException("interface id out of range for " + interfaceName); } for (var entry2 : tbl.entrySet()) @@ -162,7 +167,7 @@ private Map buildComponentSymbols(File file) throws MojoExecutio int id = (int) (long) entry2.getValue(); if (id < 0 || id > 0xffff) { - throw new MojoExecutionException("component id out of range for " + componentName); + throw new RuntimeException("component id out of range for " + componentName); } var fullName = interfaceName.toLowerCase(Locale.ENGLISH) + ":" + componentName.toLowerCase(Locale.ENGLISH); diff --git a/runelite-gradle-plugin/src/main/java/net/runelite/gradle/component/ComponentPlugin.java b/runelite-gradle-plugin/src/main/java/net/runelite/gradle/component/ComponentPlugin.java new file mode 100644 index 00000000000..a30a6a4c260 --- /dev/null +++ b/runelite-gradle-plugin/src/main/java/net/runelite/gradle/component/ComponentPlugin.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2024, LlemonDuck + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.gradle.component; + +import org.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradle.api.tasks.SourceSet; +import org.gradle.api.tasks.SourceSetContainer; +import org.gradle.api.tasks.TaskProvider; + +public class ComponentPlugin implements Plugin +{ + + @Override + public void apply(Project project) + { + TaskProvider packComponents = project.getTasks() + .register("packComponents", ComponentTask.class, (task) -> task.setGroup("build")); + + project.getTasks() + .getByName("compileJava") + .dependsOn(packComponents); + + project.getExtensions() + .getByType(SourceSetContainer.class) + .getByName(SourceSet.MAIN_SOURCE_SET_NAME) + .getJava() + .srcDir(packComponents.map(ComponentTask::getOutputDirectory)); + } + +} diff --git a/runelite-maven-plugin/src/main/java/net/runelite/mvn/ComponentMojo.java b/runelite-gradle-plugin/src/main/java/net/runelite/gradle/component/ComponentTask.java similarity index 62% rename from runelite-maven-plugin/src/main/java/net/runelite/mvn/ComponentMojo.java rename to runelite-gradle-plugin/src/main/java/net/runelite/gradle/component/ComponentTask.java index 619fa273b65..e7458a962f5 100644 --- a/runelite-maven-plugin/src/main/java/net/runelite/mvn/ComponentMojo.java +++ b/runelite-gradle-plugin/src/main/java/net/runelite/gradle/component/ComponentTask.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Adam + * Copyright (c) 2024, LlemonDuck * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -22,7 +22,7 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package net.runelite.mvn; +package net.runelite.gradle.component; import com.squareup.javapoet.FieldSpec; import com.squareup.javapoet.JavaFile; @@ -33,74 +33,61 @@ import java.util.Locale; import java.util.Set; import javax.lang.model.element.Modifier; -import org.apache.maven.plugin.AbstractMojo; -import org.apache.maven.plugin.MojoExecutionException; -import org.apache.maven.plugin.MojoFailureException; -import org.apache.maven.plugin.logging.Log; -import org.apache.maven.plugins.annotations.LifecyclePhase; -import org.apache.maven.plugins.annotations.Mojo; -import org.apache.maven.plugins.annotations.Parameter; -import org.apache.maven.project.MavenProject; +import org.gradle.api.DefaultTask; +import org.gradle.api.file.DirectoryProperty; +import org.gradle.api.file.RegularFileProperty; +import org.gradle.api.logging.Logger; +import org.gradle.api.tasks.CacheableTask; +import org.gradle.api.tasks.InputFile; +import org.gradle.api.tasks.OutputDirectory; +import org.gradle.api.tasks.PathSensitive; +import org.gradle.api.tasks.PathSensitivity; +import org.gradle.api.tasks.TaskAction; import org.tomlj.Toml; import org.tomlj.TomlParseError; import org.tomlj.TomlParseResult; import org.tomlj.TomlTable; -@Mojo( - name = "pack-components", - defaultPhase = LifecyclePhase.GENERATE_SOURCES -) -public class ComponentMojo extends AbstractMojo +@CacheableTask +public abstract class ComponentTask extends DefaultTask { - @Parameter(defaultValue = "${project}") - private MavenProject project; - @Parameter(required = true) - private File inputDirectory; + @InputFile + @PathSensitive(PathSensitivity.RELATIVE) + public abstract RegularFileProperty getInputFile(); - @Parameter(required = true) - private File outputDirectory; + @OutputDirectory + public abstract DirectoryProperty getOutputDirectory(); - private final Log log = getLog(); + private final Logger log = getLogger(); private final Set seenInterfaces = new HashSet<>(); private final Set seenComponents = new HashSet<>(); - @Override - public void execute() throws MojoExecutionException, MojoFailureException + @TaskAction + public void packComponents() throws IOException { + File inputFile = getInputFile().getAsFile().get(); + File outputDirectory = getOutputDirectory().getAsFile().get(); + TypeSpec.Builder interfaceType = TypeSpec.classBuilder("InterfaceID") .addModifiers(Modifier.PUBLIC, Modifier.FINAL) .addAnnotation(Deprecated.class) - .addJavadoc("@deprecated Use {@link net.runelite.api.gameval.InterfaceID} instead"); + .addJavadoc("@deprecated Use {@link net.runelite.api.gameval.InterfaceID} instead");; TypeSpec.Builder componentType = TypeSpec.classBuilder("ComponentID") .addModifiers(Modifier.PUBLIC, Modifier.FINAL) .addAnnotation(Deprecated.class) .addJavadoc("@deprecated Use nested classes of {@link net.runelite.api.gameval.InterfaceID} instead"); - for (File file : inputDirectory.listFiles((dir, name) -> name.endsWith(".toml"))) - { - executeOne(file, interfaceType, componentType); - } - - writeClass("net.runelite.api.widgets", interfaceType.build()); - writeClass("net.runelite.api.widgets", componentType.build()); + executeOne(inputFile, interfaceType, componentType); - // https://stackoverflow.com/a/30760908 - project.addCompileSourceRoot(outputDirectory.getAbsolutePath()); + writeClass(outputDirectory, "net.runelite.api.widgets", interfaceType.build()); + writeClass(outputDirectory, "net.runelite.api.widgets", componentType.build()); } - private void executeOne(File file, TypeSpec.Builder interfaceType, TypeSpec.Builder componentType) throws MojoExecutionException + private void executeOne(File file, TypeSpec.Builder interfaceType, TypeSpec.Builder componentType) throws IOException { - TomlParseResult result; - try - { - result = Toml.parse(file.toPath()); - } - catch (IOException e) - { - throw new MojoExecutionException("unable to read component file " + file.getName(), e); - } + TomlParseResult result = Toml.parse(file.toPath()); if (result.hasErrors()) { @@ -108,7 +95,7 @@ private void executeOne(File file, TypeSpec.Builder interfaceType, TypeSpec.Buil { log.error(err.toString()); } - throw new MojoExecutionException("unable to parse component file " + file.getName()); + throw new RuntimeException("unable to parse component file " + file.getName()); } for (var entry : result.entrySet()) @@ -118,18 +105,18 @@ private void executeOne(File file, TypeSpec.Builder interfaceType, TypeSpec.Buil if (!tbl.contains("id")) { - throw new MojoExecutionException("interface " + interfaceName + " has no id"); + throw new RuntimeException("interface " + interfaceName + " has no id"); } int interfaceId = (int) (long) tbl.getLong("id"); if (interfaceId < 0 || interfaceId > 0xffff) { - throw new MojoExecutionException("interface id out of range for " + interfaceName); + throw new RuntimeException("interface id out of range for " + interfaceName); } if (seenInterfaces.contains(interfaceId)) { - throw new MojoExecutionException("duplicate interface id " + interfaceId); + throw new RuntimeException("duplicate interface id " + interfaceId); } seenInterfaces.add(interfaceId); @@ -146,7 +133,7 @@ private void executeOne(File file, TypeSpec.Builder interfaceType, TypeSpec.Buil int id = (int) (long) entry2.getValue(); if (id < 0 || id > 0xffff) { - throw new MojoExecutionException("component id out of range for " + componentName); + throw new RuntimeException("component id out of range for " + componentName); } var fullName = interfaceName.toUpperCase(Locale.ENGLISH) + "_" + componentName.toUpperCase(Locale.ENGLISH); @@ -155,7 +142,7 @@ private void executeOne(File file, TypeSpec.Builder interfaceType, TypeSpec.Buil if (seenComponents.contains(componentId)) { - throw new MojoExecutionException("duplicate component id " + comment); + throw new RuntimeException("duplicate component id " + comment); } seenComponents.add(componentId); @@ -176,18 +163,10 @@ private static void addField(TypeSpec.Builder type, String name, int value, Stri type.addField(field.build()); } - private void writeClass(String pkg, TypeSpec type) throws MojoExecutionException + private void writeClass(File outputDirectory, String pkg, TypeSpec type) throws IOException { - JavaFile javaFile = JavaFile.builder(pkg, type) - .build(); - - try - { - javaFile.writeTo(outputDirectory); - } - catch (IOException e) - { - throw new MojoExecutionException("unable to write java class", e); - } + JavaFile.builder(pkg, type) + .build() + .writeToFile(outputDirectory); } } diff --git a/runelite-gradle-plugin/src/main/java/net/runelite/gradle/index/IndexPlugin.java b/runelite-gradle-plugin/src/main/java/net/runelite/gradle/index/IndexPlugin.java new file mode 100644 index 00000000000..8e9de3e6f99 --- /dev/null +++ b/runelite-gradle-plugin/src/main/java/net/runelite/gradle/index/IndexPlugin.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2024, LlemonDuck + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.gradle.index; + +import org.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradle.api.tasks.TaskProvider; + +public abstract class IndexPlugin implements Plugin +{ + + @Override + public void apply(Project project) + { + TaskProvider buildRs2asmIndex = project.getTasks() + .register("buildRs2asmIndex", IndexTask.class, (task) -> task.setGroup("build")); + + project.getTasks() + .getByName("processResources") + .dependsOn(buildRs2asmIndex); + } +} diff --git a/runelite-maven-plugin/src/main/java/net/runelite/mvn/IndexMojo.java b/runelite-gradle-plugin/src/main/java/net/runelite/gradle/index/IndexTask.java similarity index 69% rename from runelite-maven-plugin/src/main/java/net/runelite/mvn/IndexMojo.java rename to runelite-gradle-plugin/src/main/java/net/runelite/gradle/index/IndexTask.java index 63cbb6dbcc0..494b0ec9d9e 100644 --- a/runelite-maven-plugin/src/main/java/net/runelite/mvn/IndexMojo.java +++ b/runelite-gradle-plugin/src/main/java/net/runelite/gradle/index/IndexTask.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Adam + * Copyright (c) 2024, LlemonDuck * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -22,35 +22,40 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package net.runelite.mvn; +package net.runelite.gradle.index; import java.io.DataOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import static java.lang.Integer.parseInt; -import org.apache.maven.plugin.AbstractMojo; -import org.apache.maven.plugin.MojoExecutionException; -import org.apache.maven.plugin.MojoFailureException; -import org.apache.maven.plugins.annotations.LifecyclePhase; -import org.apache.maven.plugins.annotations.Mojo; -import org.apache.maven.plugins.annotations.Parameter; +import org.gradle.api.DefaultTask; +import org.gradle.api.file.DirectoryProperty; +import org.gradle.api.file.RegularFileProperty; +import org.gradle.api.tasks.CacheableTask; +import org.gradle.api.tasks.InputDirectory; +import org.gradle.api.tasks.OutputFile; +import org.gradle.api.tasks.PathSensitive; +import org.gradle.api.tasks.PathSensitivity; +import org.gradle.api.tasks.TaskAction; -@Mojo( - name = "build-index", - defaultPhase = LifecyclePhase.GENERATE_RESOURCES -) -public class IndexMojo extends AbstractMojo +@CacheableTask +public abstract class IndexTask extends DefaultTask { - @Parameter(required = true) - private File archiveOverlayDirectory; - @Parameter(required = true) - private File indexFile; + @InputDirectory + @PathSensitive(PathSensitivity.RELATIVE) + public abstract DirectoryProperty getArchiveOverlayDirectory(); - @Override - public void execute() throws MojoExecutionException, MojoFailureException + @OutputFile + public abstract RegularFileProperty getIndexFile(); + + @TaskAction + public void buildRs2Index() throws IOException { + File archiveOverlayDirectory = getArchiveOverlayDirectory().getAsFile().get(); + File indexFile = getIndexFile().getAsFile().get(); + try (DataOutputStream fout = new DataOutputStream(new FileOutputStream(indexFile))) { for (File indexFolder : archiveOverlayDirectory.listFiles()) @@ -77,10 +82,6 @@ public void execute() throws MojoExecutionException, MojoFailureException fout.writeInt(-1); } - catch (IOException ex) - { - throw new MojoExecutionException("error build index file", ex); - } } } diff --git a/runelite-gradle-plugin/src/main/java/net/runelite/gradle/jarsign/JarsignExtension.java b/runelite-gradle-plugin/src/main/java/net/runelite/gradle/jarsign/JarsignExtension.java new file mode 100644 index 00000000000..ded3cbc88d3 --- /dev/null +++ b/runelite-gradle-plugin/src/main/java/net/runelite/gradle/jarsign/JarsignExtension.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2024, LlemonDuck + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.gradle.jarsign; + +import org.gradle.api.file.RegularFileProperty; +import org.gradle.api.provider.Property; + +public interface JarsignExtension +{ + + RegularFileProperty getKeystore(); + + Property getStorePass(); + + Property getKeyPass(); + + Property getAlias(); + +} diff --git a/runelite-gradle-plugin/src/main/java/net/runelite/gradle/jarsign/JarsignPlugin.java b/runelite-gradle-plugin/src/main/java/net/runelite/gradle/jarsign/JarsignPlugin.java new file mode 100644 index 00000000000..f252fdf90e3 --- /dev/null +++ b/runelite-gradle-plugin/src/main/java/net/runelite/gradle/jarsign/JarsignPlugin.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2024, LlemonDuck + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.gradle.jarsign; + +import org.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradle.api.file.RegularFileProperty; +import org.gradle.api.plugins.BasePlugin; +import org.gradle.api.provider.Provider; +import org.gradle.api.tasks.TaskProvider; +import org.gradle.jvm.tasks.Jar; + +public abstract class JarsignPlugin implements Plugin +{ + + @Override + public void apply(Project project) + { + JarsignExtension ext = project.getExtensions() + .create(JarsignExtension.class, "jarsign", JarsignExtension.class); + ext.getKeystore().convention(toRegularFileProvider(project, propProvider(project, "jarsignerKeystore"))); + ext.getStorePass().convention(propProvider(project, "jarsignerStorepass")); + ext.getKeyPass().convention(propProvider(project, "jarsignerKeypass")); + ext.getAlias().convention(propProvider(project, "jarsignerAlias")); + + project.getTasks() + .withType(Jar.class, jarTask -> registerSignTask(project, jarTask, ext)); + } + + private void registerSignTask(Project project, Jar jarTask, JarsignExtension ext) + { + TaskProvider signTask = project.getTasks().register( + jarTask.getName() + "Sign", JarsignTask.class, (jarsignTask) -> + { + jarsignTask.setGroup(BasePlugin.BUILD_GROUP); + + jarsignTask.getBuildTask().convention(jarTask); + jarsignTask.getArchive().convention(jarTask.getArchiveFile()); + jarsignTask.getKeystore().convention(ext.getKeystore()); + jarsignTask.getStorePass().convention(ext.getStorePass()); + jarsignTask.getKeyPass().convention(ext.getKeyPass()); + jarsignTask.getAlias().convention(ext.getAlias()); + } + ); + jarTask.finalizedBy(signTask); + } + + private static Provider propProvider(Project project, String key) + { + return project.provider(() -> + (String) project.findProperty(key)); + } + + private static RegularFileProperty toRegularFileProvider(Project project, Provider propProvider) + { + return project.getObjects() + .fileProperty() + .fileProvider(propProvider.map(project::file)); + } +} diff --git a/runelite-gradle-plugin/src/main/java/net/runelite/gradle/jarsign/JarsignTask.java b/runelite-gradle-plugin/src/main/java/net/runelite/gradle/jarsign/JarsignTask.java new file mode 100644 index 00000000000..8acb891db71 --- /dev/null +++ b/runelite-gradle-plugin/src/main/java/net/runelite/gradle/jarsign/JarsignTask.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2024, LlemonDuck + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.gradle.jarsign; + +import org.gradle.api.DefaultTask; +import org.gradle.api.file.RegularFileProperty; +import org.gradle.api.provider.Property; +import org.gradle.api.tasks.CacheableTask; +import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.InputFile; +import org.gradle.api.tasks.PathSensitive; +import org.gradle.api.tasks.PathSensitivity; +import org.gradle.api.tasks.TaskAction; +import org.gradle.jvm.tasks.Jar; + +@CacheableTask +public abstract class JarsignTask extends DefaultTask +{ + + public JarsignTask() + { + dependsOn(getBuildTask()); + onlyIf( + "target archive must be specified", + _t -> getArchive().getAsFile().get().exists() + ); + + onlyIf( + "keystore properties are set", + _t -> + getKeystore().isPresent() && + getStorePass().isPresent() && + getKeyPass().isPresent() && + getAlias().isPresent() + ); + } + + @Input + public abstract Property getBuildTask(); + + @InputFile + @PathSensitive(PathSensitivity.RELATIVE) + public abstract RegularFileProperty getArchive(); + + @InputFile + @PathSensitive(PathSensitivity.RELATIVE) + public abstract RegularFileProperty getKeystore(); + + @Input + public abstract Property getStorePass(); + + @Input + public abstract Property getKeyPass(); + + @Input + public abstract Property getAlias(); + + @TaskAction + public void signArtifact() + { + getProject().exec(exec -> + exec.commandLine( + "jarsigner", + "-keystore", getKeystore().getAsFile().get().getAbsolutePath(), + "-storepass", getStorePass().get(), + "-keypass", getKeyPass().get(), + getArchive().getAsFile().get().getAbsolutePath(), + getAlias().get() + )); + } + +} diff --git a/runelite-jshell/build.gradle.kts b/runelite-jshell/build.gradle.kts new file mode 100644 index 00000000000..9cec5126597 --- /dev/null +++ b/runelite-jshell/build.gradle.kts @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2024, LlemonDuck + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +plugins { + java + `maven-publish` + alias(libs.plugins.lombok) +} + +lombok.version = libs.versions.lombok.get() + +java { + withJavadocJar() + withSourcesJar() +} + +dependencies { + implementation(libs.slf4j.api) + implementation(libs.guava) { + exclude("com.google.code.findbugs", "jsr305") + exclude("com.google.errorprone", "error_prone_annotations") + exclude("com.google.j2objc", "j2objc-annotations") + exclude("org.codehaus.mojo", "animal-sniffer-annotations") + } + implementation(variantOf(libs.guice.core) { classifier("no_aop") }) { + exclude("com.google.guava", "guava") + } + implementation(libs.findbugs) + implementation(libs.fife.rsyntaxtextarea) + implementation(libs.fife.autocomplete) +} + +publishing { + publications { + create("jshell") { + from(components["java"]) + } + } +} diff --git a/runelite-jshell/settings.gradle.kts b/runelite-jshell/settings.gradle.kts new file mode 100644 index 00000000000..f0b83742986 --- /dev/null +++ b/runelite-jshell/settings.gradle.kts @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2024, LlemonDuck + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +rootProject.name = "jshell" +apply(from = "../common.settings.gradle.kts") diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 00000000000..549ce19f0fa --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2024, LlemonDuck + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +rootProject.name = "runelite" +includeBuild("cache") +includeBuild("runelite-api") +includeBuild("runelite-client") +includeBuild("runelite-gradle-plugin") +includeBuild("runelite-jshell")