mirror of
https://github.com/runelite/plugin-hub.git
synced 2026-10-04 01:45:14 -04:00
package: move packager into its own repository
This commit is contained in:
55 files changed
+44
-5675
No files matched your search
+43
-32
@@ -12,37 +12,46 @@ on:
|
||||
required: false
|
||||
push:
|
||||
pull_request:
|
||||
env:
|
||||
CACHE_VERSION: 2.4.0
|
||||
permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
build:
|
||||
# any forks that predate this repo having an action will have actions
|
||||
# enabled by default, which will fail in a lot of cases because the branch
|
||||
# is new, which makes the differential build fail
|
||||
if: github.event_name != 'push' || github.repository_owner == 'runelite'
|
||||
runs-on: ubuntu-22.04
|
||||
if: vars.PACKAGER_URL != ''
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-java@v4
|
||||
path: plugin-hub
|
||||
persist-credentials: false
|
||||
- uses: actions/setup-java@v5
|
||||
with:
|
||||
distribution: adopt
|
||||
java-version: 11
|
||||
- uses: actions/cache@v4
|
||||
- name: fetch packager
|
||||
env:
|
||||
PACKAGER_URL: ${{ vars.PACKAGER_URL }}
|
||||
run: |
|
||||
set -exu
|
||||
curl --location --output bundle.tar.zst "$PACKAGER_URL"
|
||||
sha256sum bundle.tar.zst
|
||||
tar xf bundle.tar.zst
|
||||
- uses: actions/cache/restore@v5
|
||||
id: restore-cache
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches/
|
||||
~/.gradle/wrapper/
|
||||
key: package-${{ env.CACHE_VERSION }}-${{ hashFiles('runelite.version') }}
|
||||
restore-keys: |
|
||||
package-${{ env.CACHE_VERSION }}-
|
||||
api/
|
||||
key: deps-${{ hashFiles('plugin-hub/runelite.version', 'bundle.tar.zst') }}
|
||||
- name: prepare
|
||||
run: |
|
||||
pushd package
|
||||
./gradlew --build-cache prep
|
||||
./gradlew --build-cache --project-dir verification-template :verifyCore
|
||||
popd
|
||||
if: steps.restore-cache.outputs.cache-hit != 'true'
|
||||
run: ./prepare.sh
|
||||
- uses: actions/cache/save@v5
|
||||
if: steps.restore-cache.outputs.cache-hit != 'true'
|
||||
with:
|
||||
path: ~/.gradle/caches/
|
||||
key: ${{ steps.restore-cache.outputs.cache-primary-key }}
|
||||
|
||||
- name: build
|
||||
env:
|
||||
REPO_CREDS: ${{ secrets.REPO_CREDS }}
|
||||
@@ -67,7 +76,7 @@ jobs:
|
||||
else
|
||||
export PACKAGE_COMMIT_RANGE="${COMMIT_RANGE:-${COMMIT_BEFORE:+$COMMIT_BEFORE...$COMMIT_AFTER}}"
|
||||
fi
|
||||
exec java -XX:+UseParallelGC -cp package/package/build/libs/package.jar net.runelite.pluginhub.packager.Packager
|
||||
exec java -XX:+UseParallelGC -cp package.jar net.runelite.pluginhub.packager.Packager
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: manifest_diff
|
||||
@@ -80,24 +89,28 @@ jobs:
|
||||
path: /tmp/jars
|
||||
retention-days: 4
|
||||
upload:
|
||||
if: (github.event_name != 'push' || github.repository_owner == 'runelite') && github.event_name != 'pull_request'
|
||||
if: vars.PACKAGER_URL != '' && github.event_name != 'pull_request'
|
||||
needs: build
|
||||
concurrency: upload
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 1
|
||||
- uses: actions/setup-java@v4
|
||||
path: plugin-hub
|
||||
persist-credentials: false
|
||||
- name: fetch packager
|
||||
env:
|
||||
PACKAGER_URL: ${{ vars.PACKAGER_URL }}
|
||||
run: |
|
||||
set -exu
|
||||
curl --location --output bundle.tar.zst "$PACKAGER_URL"
|
||||
sha256sum bundle.tar.zst
|
||||
tar xf bundle.tar.zst
|
||||
- uses: actions/setup-java@v5
|
||||
with:
|
||||
distribution: adopt
|
||||
java-version: 11
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches/
|
||||
~/.gradle/wrapper/
|
||||
key: upload-${{ env.CACHE_VERSION }}
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: manifest_diff
|
||||
@@ -108,6 +121,4 @@ jobs:
|
||||
REPO_ROOT: ${{ secrets.REPO_ROOT }}
|
||||
SIGNING_KEY: ${{ secrets.SIGNING_KEY }}
|
||||
run: |
|
||||
pushd package
|
||||
./gradlew :upload:run
|
||||
popd
|
||||
exec java -XX:+UseParallelGC -cp upload.jar net.runelite.pluginhub.uploader.Uploader
|
||||
@@ -1,2 +0,0 @@
|
||||
The contents of the template plugin and the output of the `create_new_plugin.py`
|
||||
script are, released into the public domain unless otherwise noted.
|
||||
@@ -119,7 +119,7 @@ We require any dependencies that are not a transitive dependency of runelite-cli
|
||||
have their cryptographic hash verified during the build to prevent [supply chain attacks](https://en.wikipedia.org/wiki/Supply_chain_attack) and ensure build reproducability.
|
||||
To do this we rely on [Gradle's dependency verification](https://docs.gradle.org/nightly/userguide/dependency_verification.html).
|
||||
To add a new dependency, add it to the `thirdParty` configuration in [`package/verification-template/build.gradle`](https://github.com/runelite/plugin-hub/blob/master/package/verification-template/build.gradle),
|
||||
then run `../gradlew --write-verification-metadata sha256` to update the metadata file. A maintainer must then verify
|
||||
then run `./gradlew --write-verification-metadata sha256` to update the metadata file. A maintainer must then verify
|
||||
the dependencies manually before your pull request will be merged. This process generally adds significantly to the amount of time it takes for a plugin submission or update to be reviewed, so we recommend avoiding adding any new dependencies unless absolutely necessary.
|
||||
|
||||
## Build type
|
||||
|
||||
@@ -1,198 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
"""
|
||||
Copyright (c) 2019 Abex
|
||||
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 os
|
||||
import sys
|
||||
from collections import OrderedDict
|
||||
import argparse
|
||||
import re
|
||||
from string import Template
|
||||
|
||||
templatedir = os.path.join(os.path.dirname(os.path.realpath(__file__)), "templateplugin")
|
||||
|
||||
def strip_plugin(str):
|
||||
return re.sub(r"(?i)[ _-]*plugin$", "", str)
|
||||
|
||||
def reformat(str, replacer):
|
||||
if replacer != to_spaces:
|
||||
str=re.sub(r"[^a-zA-Z0-9_ -]", "", str)
|
||||
if re.match(r".*[ _-]", str):
|
||||
return re.sub(r"(?:^|[ _.-]+)([^ _.-]+)", lambda m:replacer(m.group(1)), str).strip(" -_")
|
||||
return re.sub(r"((?:^.|[A-Z0-9]+)(?:[a-z0-9]+|$))", lambda m:replacer(m.group(1)), str).strip(" -_")
|
||||
|
||||
def to_spaces(seg):
|
||||
return " " + seg.capitalize()
|
||||
|
||||
def to_camelcase(seg):
|
||||
return seg.capitalize()
|
||||
|
||||
def to_dashes(seg):
|
||||
return "-" + seg.lower()
|
||||
|
||||
def to_lowercase(seg):
|
||||
return seg.lower()
|
||||
|
||||
def strfun(strfun):
|
||||
if isinstance(strfun, str):
|
||||
return strfun
|
||||
return strfun()
|
||||
|
||||
subs = OrderedDict([
|
||||
("name", {
|
||||
"ask": True,
|
||||
"desc": "The name your plugin will be shown by in menus",
|
||||
"value": "Foo Bazzer",
|
||||
"strip_plugin": True,
|
||||
}),
|
||||
("package", {
|
||||
"ask": True,
|
||||
"desc": "The java package your plugin will be in. Typically a reversed domain name",
|
||||
"value": "com.example.foobazzer",
|
||||
"strip_plugin": True,
|
||||
}),
|
||||
("author", {
|
||||
"ask": True,
|
||||
"desc": "Who wrote/maintains this plugin",
|
||||
"value": "John Doe",
|
||||
}),
|
||||
("description", {
|
||||
"ask": True,
|
||||
"desc": "A short string describing the plugin",
|
||||
"value": "Adds a bazzer to the foo",
|
||||
}),
|
||||
("version", {
|
||||
"desc": "The initial version number of the plugin",
|
||||
"value": "",
|
||||
}),
|
||||
("plugin_prefix", {
|
||||
"desc": "The name of the your plugin's main class, without the 'Plugin' suffix",
|
||||
"value": lambda: reformat(subs["name"]["value"], to_camelcase),
|
||||
"strip_plugin": True
|
||||
}),
|
||||
("artifact_id", {
|
||||
"desc": "The name of the maven artifact",
|
||||
"value": lambda: reformat(subs["name"]["value"], to_dashes),
|
||||
}),
|
||||
("group_id", {
|
||||
"desc": "The group of the maven artifact",
|
||||
"value": lambda: subs["package"]["value"],
|
||||
}),
|
||||
("plugin_config_group", {
|
||||
"desc": "The prefix used to store config keys",
|
||||
"value": lambda: reformat(subs["name"]["value"], to_lowercase),
|
||||
"strip_plugin": True
|
||||
})
|
||||
])
|
||||
|
||||
pwd = os.getcwd()
|
||||
pwdIsEmpty = len(os.listdir(pwd)) == 0
|
||||
if pwdIsEmpty:
|
||||
subs["name"]["value"] = strip_plugin(reformat(os.path.basename(pwd), to_spaces))
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--noninteractive", dest = "noninteractive", action='store_true')
|
||||
parser.add_argument("--output_directory", dest = "output_directory")
|
||||
for key, var in subs.items():
|
||||
parser.add_argument("--" + key, dest = key, help = var["desc"])
|
||||
args = vars(parser.parse_args())
|
||||
|
||||
noninteractive = args["noninteractive"]
|
||||
|
||||
if noninteractive and pwdIsEmpty:
|
||||
subs["name"]["ask"] = False
|
||||
|
||||
for key, var in subs.items():
|
||||
if args[key] != None:
|
||||
val = args[key]
|
||||
if "strip_plugin" in var and var["strip_plugin"]:
|
||||
val = strip_plugin(val)
|
||||
var["value"] = val
|
||||
var["ask"] = False
|
||||
|
||||
askAll = False
|
||||
while True:
|
||||
for key, var in subs.items():
|
||||
if askAll or ("ask" in var and var["ask"]):
|
||||
if noninteractive:
|
||||
print("\"{}\" was not specified in noninteractive mode".format(key))
|
||||
sys.exit(1)
|
||||
print(var["desc"])
|
||||
print("[" + strfun(var["value"]) + "]")
|
||||
val = input(key + ": ")
|
||||
if val:
|
||||
if "strip_plugin" in var and var["strip_plugin"]:
|
||||
val = strip_plugin(val)
|
||||
var["value"] = val
|
||||
|
||||
askAll = True
|
||||
|
||||
print("")
|
||||
for key, var in subs.items():
|
||||
print("{} = \"{}\"".format(key, strfun(var["value"])))
|
||||
def input_yes():
|
||||
while True:
|
||||
inp = input("Is this ok? [Yn]").lower()
|
||||
if inp == "" or inp == "y":
|
||||
return True
|
||||
if inp == "n":
|
||||
return False
|
||||
if noninteractive or input_yes():
|
||||
break
|
||||
|
||||
outdir = args["output_directory"]
|
||||
if outdir == None:
|
||||
if pwdIsEmpty:
|
||||
outdir = pwd
|
||||
else:
|
||||
outdir = os.path.join(pwd, strfun(subs["artifact_id"]["value"]))
|
||||
|
||||
mappings = {}
|
||||
for key, var in subs.items():
|
||||
mappings[key] = strfun(var["value"])
|
||||
mappings["package_path"] = mappings["package"].replace(".", os.path.sep)
|
||||
with open(os.path.join(templatedir, "../runelite.version"), "rt") as fi:
|
||||
mappings["runelite_version"] = fi.read().strip()
|
||||
|
||||
for root, dir, files in os.walk(templatedir):
|
||||
for file in files:
|
||||
try:
|
||||
infi = os.path.join(root, file)
|
||||
outfi = os.path.join(os.path.relpath(root, templatedir), file)
|
||||
outfi = outfi.replace("_(", "${").replace(")_", "}")
|
||||
outfi = Template(outfi).substitute(mappings)
|
||||
outfi = os.path.join(outdir, outfi)
|
||||
os.makedirs(os.path.dirname(outfi), exist_ok=True)
|
||||
with open(infi, "rb") as ifd: # we need binary mode to not do that stupid crlf bullshit on windows
|
||||
if file.endswith(".jar") or file.startswith("gradlew"):
|
||||
with open(outfi, "wb") as ofd:
|
||||
ofd.write(ifd.read())
|
||||
else:
|
||||
contents = Template(ifd.read().decode("utf-8")).substitute(mappings)
|
||||
with open(outfi, "wb") as ofd:
|
||||
ofd.write(contents.encode("utf-8"))
|
||||
except ValueError as ex:
|
||||
raise ValueError(infi) from ex
|
||||
@@ -1,72 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2021 Abex
|
||||
* 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.
|
||||
*/
|
||||
repositories {
|
||||
maven {
|
||||
url = 'https://repo.runelite.net'
|
||||
}
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
configurations {
|
||||
runelite {
|
||||
resolutionStrategy {
|
||||
// we don't run any code from this configuration
|
||||
disableDependencyVerification()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def runeLiteVersion = file("../../runelite.version").text.trim()
|
||||
|
||||
dependencies {
|
||||
implementation "org.slf4j:slf4j-simple:1.7.10"
|
||||
implementation "com.google.code.findbugs:jsr305:3.0.2"
|
||||
implementation "com.google.guava:guava:23.2-jre"
|
||||
implementation "org.ow2.asm:asm:9.3"
|
||||
implementation "com.google.code.gson:gson:2.8.5"
|
||||
|
||||
def lombok = "org.projectlombok:lombok:1.18.30"
|
||||
compileOnly lombok
|
||||
annotationProcessor lombok
|
||||
testCompileOnly lombok
|
||||
testAnnotationProcessor lombok
|
||||
|
||||
runelite group: 'net.runelite', name: 'client', version: runeLiteVersion
|
||||
runelite group: 'net.runelite', name: 'jshell', version: runeLiteVersion
|
||||
runelite lombok
|
||||
}
|
||||
|
||||
compileJava.options.compilerArgs += [
|
||||
"--add-exports", "jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED",
|
||||
"--add-exports", "jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED",
|
||||
]
|
||||
|
||||
def apiFilePath = new File(project.buildDir, "api")
|
||||
|
||||
task api(type: JavaExec) {
|
||||
classpath = sourceSets.main.runtimeClasspath
|
||||
mainClass = "net.runelite.pluginhub.apirecorder.ClassRecorder"
|
||||
args = [apiFilePath] + configurations.runelite.files
|
||||
}
|
||||
@@ -1,266 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2021 Abex
|
||||
* 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.pluginhub.apirecorder;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.Writer;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.zip.DeflaterOutputStream;
|
||||
import java.util.zip.InflaterInputStream;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class API
|
||||
{
|
||||
@Getter
|
||||
private final Set<String> apis;
|
||||
|
||||
public API()
|
||||
{
|
||||
this(new HashSet<>());
|
||||
}
|
||||
|
||||
public static void encode(OutputStream os, Stream<String> stream) throws IOException
|
||||
{
|
||||
DeflaterOutputStream dos = new DeflaterOutputStream(os);
|
||||
Writer out = new OutputStreamWriter(dos, StandardCharsets.UTF_8);
|
||||
stream.sorted()
|
||||
.forEach(it -> write(out, it));
|
||||
out.flush();
|
||||
dos.finish();
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private static void write(Writer w, String s)
|
||||
{
|
||||
w.write(s);
|
||||
w.write('\n');
|
||||
}
|
||||
|
||||
public static API decode(InputStream is)
|
||||
{
|
||||
return decodePlain(new InflaterInputStream(is));
|
||||
}
|
||||
|
||||
public static API decodePlain(InputStream is)
|
||||
{
|
||||
return new API(new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))
|
||||
.lines()
|
||||
.filter(line -> !line.isEmpty())
|
||||
.collect(ImmutableSet.toImmutableSet()));
|
||||
}
|
||||
|
||||
public Map<String, String> parseCommented(InputStream is, boolean checked) throws IOException
|
||||
{
|
||||
Map<String, String> out = new HashMap<>();
|
||||
String comment = "";
|
||||
boolean clearComment = true;
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8));
|
||||
for (String line; (line = br.readLine()) != null; )
|
||||
{
|
||||
if (line.trim().isEmpty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (line.startsWith("#"))
|
||||
{
|
||||
if (clearComment)
|
||||
{
|
||||
clearComment = false;
|
||||
comment = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
comment += "\n";
|
||||
}
|
||||
|
||||
comment += line.substring(1).trim();
|
||||
continue;
|
||||
}
|
||||
clearComment = true;
|
||||
|
||||
if (line.startsWith("/"))
|
||||
{
|
||||
String re = line.trim();
|
||||
Pattern p = Pattern.compile(re.substring(1, re.length() - 1));
|
||||
List<String> keys = apis.stream().filter(p.asPredicate()).collect(Collectors.toList());
|
||||
if (keys.isEmpty())
|
||||
{
|
||||
if (checked)
|
||||
{
|
||||
throw new RuntimeException("no apis match regex \"" + line + "\"");
|
||||
}
|
||||
else
|
||||
{
|
||||
log.warn("no apis match regex \"{}\"", line);
|
||||
}
|
||||
}
|
||||
|
||||
for (String k : keys)
|
||||
{
|
||||
out.put(k, comment.isEmpty() ? k : comment);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!apis.contains(line))
|
||||
{
|
||||
if (checked)
|
||||
{
|
||||
throw new RuntimeException("no apis match \"" + line + "\"");
|
||||
}
|
||||
else
|
||||
{
|
||||
log.warn("no apis match \"{}\"", line);
|
||||
}
|
||||
}
|
||||
|
||||
out.put(line, comment.isEmpty() ? line : comment);
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
public void encode(OutputStream os) throws IOException
|
||||
{
|
||||
encode(os, apis.stream());
|
||||
}
|
||||
|
||||
public Stream<String> missingFrom(API other)
|
||||
{
|
||||
return apis.stream()
|
||||
.filter(a -> !other.getApis().contains(a));
|
||||
}
|
||||
|
||||
public Stream<String> in(API other)
|
||||
{
|
||||
return apis.stream()
|
||||
.filter(a -> other.getApis().contains(a));
|
||||
}
|
||||
|
||||
public Set<String> disallowed(Map<String, String> disaslowed)
|
||||
{
|
||||
return apis.stream()
|
||||
.map(disaslowed::get)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
public static String modifiersToString(int modifiers, boolean member)
|
||||
{
|
||||
String s = "";
|
||||
if (Modifier.isAbstract(modifiers))
|
||||
{
|
||||
s += "a";
|
||||
}
|
||||
if (!member && Modifier.isInterface(modifiers))
|
||||
{
|
||||
s += "i";
|
||||
}
|
||||
if (Modifier.isPublic(modifiers))
|
||||
{
|
||||
s += "b";
|
||||
}
|
||||
else if (Modifier.isProtected(modifiers))
|
||||
{
|
||||
s += "t";
|
||||
}
|
||||
else if (Modifier.isPrivate(modifiers))
|
||||
{
|
||||
s += "v";
|
||||
}
|
||||
else
|
||||
{
|
||||
s += "g";
|
||||
}
|
||||
if (member && Modifier.isStatic(modifiers))
|
||||
{
|
||||
s += "s";
|
||||
}
|
||||
if (Modifier.isFinal(modifiers))
|
||||
{
|
||||
s += "f";
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
public void recordClass(int modifiers, String descriptor)
|
||||
{
|
||||
if (descriptor != null)
|
||||
{
|
||||
apis.add(descriptor + modifiersToString(modifiers, false));
|
||||
}
|
||||
}
|
||||
|
||||
public void recordClassHierarchy(String from, String superDescriptor)
|
||||
{
|
||||
apis.add(from + ">" + superDescriptor);
|
||||
}
|
||||
|
||||
public void recordMethod(int modifiers, String classDescriptor, CharSequence name, String descriptor)
|
||||
{
|
||||
if (classDescriptor != null & descriptor != null)
|
||||
{
|
||||
String mod = modifiersToString(modifiers, true);
|
||||
if ((modifiers & Opcodes.ACC_VARARGS) != 0)
|
||||
{
|
||||
mod += "v";
|
||||
}
|
||||
apis.add(classDescriptor + "." + name + descriptor + ":" + mod);
|
||||
}
|
||||
}
|
||||
|
||||
public void recordField(int modifiers, String classDescriptor, CharSequence name, String descriptor, Object constantValue)
|
||||
{
|
||||
if (classDescriptor != null && name != null && descriptor != null)
|
||||
{
|
||||
String mods = modifiersToString(modifiers, true);
|
||||
apis.add(classDescriptor + "." + name + ":" + descriptor + ":" + mods + ":" + (constantValue == null ? "" : constantValue));
|
||||
}
|
||||
}
|
||||
}
|
||||
-125
@@ -1,125 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2021 Abex
|
||||
* 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.pluginhub.apirecorder;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarInputStream;
|
||||
import lombok.Getter;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.FieldVisitor;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
|
||||
/**
|
||||
* Records the API provided by any classes passed through it
|
||||
*/
|
||||
public class ClassRecorder extends ClassVisitor
|
||||
{
|
||||
@Getter
|
||||
private final API api = new API();
|
||||
|
||||
private String className;
|
||||
|
||||
public ClassRecorder()
|
||||
{
|
||||
super(Opcodes.ASM7);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces)
|
||||
{
|
||||
this.className = "L" + name + ";";
|
||||
api.recordClass(access, className);
|
||||
api.recordClassHierarchy(this.className, "L" + superName + ";");
|
||||
for (String iface : interfaces)
|
||||
{
|
||||
api.recordClassHierarchy(this.className, "L" + iface + ";");
|
||||
}
|
||||
super.visit(version, access, name, signature, superName, interfaces);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value)
|
||||
{
|
||||
if (!Modifier.isPrivate(access))
|
||||
{
|
||||
api.recordField(access, className, name, descriptor, value);
|
||||
}
|
||||
return super.visitField(access, name, descriptor, signature, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions)
|
||||
{
|
||||
if (!Modifier.isPrivate(access))
|
||||
{
|
||||
api.recordMethod(access, className, name, descriptor);
|
||||
}
|
||||
return super.visitMethod(access, name, descriptor, signature, exceptions);
|
||||
}
|
||||
|
||||
public void recordClass(File jarFile) throws IOException
|
||||
{
|
||||
try (JarInputStream jis = new JarInputStream(new FileInputStream(jarFile)))
|
||||
{
|
||||
for (JarEntry je; (je = jis.getNextJarEntry()) != null; )
|
||||
{
|
||||
String fileName = je.getName();
|
||||
if (!fileName.endsWith(".class"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ClassReader cr = new ClassReader(jis);
|
||||
cr.accept(this, ClassReader.SKIP_CODE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String... classes) throws IOException
|
||||
{
|
||||
var cr = new ClassRecorder();
|
||||
Iterator<String> args = List.of(classes).iterator();
|
||||
File out = new File(args.next());
|
||||
for (String fi; args.hasNext(); )
|
||||
{
|
||||
fi = args.next();
|
||||
cr.recordClass(new File(fi));
|
||||
}
|
||||
try (OutputStream os = new FileOutputStream(out))
|
||||
{
|
||||
cr.getApi().encode(os);
|
||||
}
|
||||
}
|
||||
}
|
||||
-94
@@ -1,94 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2021 Abex
|
||||
* 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.pluginhub.apirecorder;
|
||||
|
||||
import com.sun.source.tree.IdentifierTree;
|
||||
import com.sun.source.tree.MemberSelectTree;
|
||||
import com.sun.source.tree.Tree;
|
||||
import com.sun.source.util.TreeScanner;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
public class PrintingScanner extends TreeScanner<Void, Void>
|
||||
{
|
||||
private final StringBuilder sb = new StringBuilder();
|
||||
private int tabs = 0;
|
||||
|
||||
public static String print(Tree n)
|
||||
{
|
||||
PrintingScanner s = new PrintingScanner();
|
||||
s.scan(n, null);
|
||||
return s.toString();
|
||||
}
|
||||
|
||||
public void indent()
|
||||
{
|
||||
for (int i = 0; i < tabs; i++)
|
||||
{
|
||||
sb.append('\t');
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void scan(Tree tree, Void unused)
|
||||
{
|
||||
indent();
|
||||
if (tree == null)
|
||||
{
|
||||
sb.append("null\n");
|
||||
return null;
|
||||
}
|
||||
sb.append(tree.getKind()).append("(\n");
|
||||
tabs++;
|
||||
super.scan(tree, unused);
|
||||
tabs--;
|
||||
indent();
|
||||
sb.append(")\n");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitIdentifier(IdentifierTree node, Void unused)
|
||||
{
|
||||
indent();
|
||||
sb.append(node.getName()).append('\n');
|
||||
return super.visitIdentifier(node, unused);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitMemberSelect(MemberSelectTree node, Void unused)
|
||||
{
|
||||
super.visitMemberSelect(node, unused);
|
||||
indent();
|
||||
sb.append(node.getIdentifier()).append('\n');
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
-96
@@ -1,96 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2021 Abex
|
||||
* 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.pluginhub.apirecorder;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.sun.source.util.JavacTask;
|
||||
import com.sun.source.util.Plugin;
|
||||
import com.sun.source.util.TaskEvent;
|
||||
import com.sun.source.util.TaskListener;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
public class RecorderPlugin implements Plugin
|
||||
{
|
||||
@Override
|
||||
public String getName()
|
||||
{
|
||||
return "RuneLiteAPIRecorder";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(JavacTask task, String... args)
|
||||
{
|
||||
String buildDir = System.getenv("runelite.pluginhub.package.buildDir");
|
||||
if (Strings.isNullOrEmpty(buildDir))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
RecordingTreeScanner scanner = new RecordingTreeScanner(task);
|
||||
|
||||
task.addTaskListener(new TaskListener()
|
||||
{
|
||||
@Override
|
||||
public void finished(TaskEvent e)
|
||||
{
|
||||
switch (e.getKind())
|
||||
{
|
||||
case ANALYZE:
|
||||
if (log.isDebugEnabled())
|
||||
{
|
||||
log.info("{}", PrintingScanner.print(e.getCompilationUnit()));
|
||||
}
|
||||
try
|
||||
{
|
||||
scanner.scan(e.getCompilationUnit(), null);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
log.warn("failed to scan", ex);
|
||||
scanner.setPartial(true);
|
||||
}
|
||||
break;
|
||||
case COMPILATION:
|
||||
if (!scanner.isPartial())
|
||||
{
|
||||
try (FileOutputStream fos = new FileOutputStream(new File(buildDir, "api")))
|
||||
{
|
||||
scanner.getApi().encode(fos);
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
-529
@@ -1,529 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2021 Abex
|
||||
* 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.pluginhub.apirecorder;
|
||||
|
||||
import com.sun.source.tree.AnnotationTree;
|
||||
import com.sun.source.tree.ClassTree;
|
||||
import com.sun.source.tree.IdentifierTree;
|
||||
import com.sun.source.tree.ImportTree;
|
||||
import com.sun.source.tree.MemberReferenceTree;
|
||||
import com.sun.source.tree.MemberSelectTree;
|
||||
import com.sun.source.tree.MethodInvocationTree;
|
||||
import com.sun.source.tree.NewClassTree;
|
||||
import com.sun.source.tree.PackageTree;
|
||||
import com.sun.source.tree.Tree;
|
||||
import com.sun.source.util.JavacTask;
|
||||
import com.sun.source.util.TreePath;
|
||||
import com.sun.source.util.TreePathScanner;
|
||||
import com.sun.source.util.Trees;
|
||||
import com.sun.tools.javac.api.BasicJavacTask;
|
||||
import com.sun.tools.javac.code.Attribute;
|
||||
import com.sun.tools.javac.code.Flags;
|
||||
import com.sun.tools.javac.code.Symbol;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.ExecutableElement;
|
||||
import javax.lang.model.element.PackageElement;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.lang.model.element.VariableElement;
|
||||
import javax.lang.model.type.ArrayType;
|
||||
import javax.lang.model.type.DeclaredType;
|
||||
import javax.lang.model.type.NoType;
|
||||
import javax.lang.model.type.NullType;
|
||||
import javax.lang.model.type.PrimitiveType;
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
import javax.lang.model.type.TypeVariable;
|
||||
import javax.lang.model.util.Elements;
|
||||
import javax.lang.model.util.Types;
|
||||
import javax.tools.JavaFileObject;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
|
||||
/**
|
||||
* Records all the API consumed of any nodes passed through it
|
||||
*/
|
||||
@Slf4j
|
||||
class RecordingTreeScanner extends TreePathScanner<Void, Void>
|
||||
{
|
||||
@Getter
|
||||
private final API api = new API();
|
||||
|
||||
private final Map<String, Boolean> jvmClassCache = new HashMap<>();
|
||||
|
||||
private final Trees trees;
|
||||
private final Elements elements;
|
||||
private final Types types;
|
||||
private final com.sun.tools.javac.code.Types internalTypes;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private boolean partial;
|
||||
|
||||
public RecordingTreeScanner(JavacTask task)
|
||||
{
|
||||
this.trees = Trees.instance(task);
|
||||
this.elements = task.getElements();
|
||||
this.types = task.getTypes();
|
||||
BasicJavacTask internalTask = (BasicJavacTask) task;
|
||||
this.internalTypes = com.sun.tools.javac.code.Types.instance(internalTask.getContext());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitImport(ImportTree node, Void unused)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitPackage(PackageTree node, Void unused)
|
||||
{
|
||||
// we don't want to record the pkg ident
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitIdentifier(IdentifierTree node, Void unused)
|
||||
{
|
||||
recordElement(trees.getElement(getCurrentPath()), null);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitMemberSelect(MemberSelectTree node, Void unused)
|
||||
{
|
||||
TypeMirror receiver = trees.getTypeMirror(new TreePath(getCurrentPath(), node.getExpression()));
|
||||
recordElement(trees.getElement(getCurrentPath()), receiver);
|
||||
return super.visitMemberSelect(node, unused);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitMethodInvocation(MethodInvocationTree node, Void unused)
|
||||
{
|
||||
recordElement(trees.getElement(getCurrentPath()), null);
|
||||
return super.visitMethodInvocation(node, unused);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitMemberReference(MemberReferenceTree node, Void unused)
|
||||
{
|
||||
TypeMirror receiver = trees.getTypeMirror(new TreePath(getCurrentPath(), node.getQualifierExpression()));
|
||||
recordElement(trees.getElement(getCurrentPath()), receiver);
|
||||
return super.visitMemberReference(node, unused);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitClass(ClassTree node, Void unused)
|
||||
{
|
||||
// it is hard to resolve the implicit receiver from this-calls to superclasses
|
||||
// so record the whole hierarchy for all extends/implements clauses
|
||||
if (node.getExtendsClause() != null)
|
||||
{
|
||||
recordFullHierarchy(trees.getElement(new TreePath(getCurrentPath(), node.getExtendsClause())).asType());
|
||||
}
|
||||
for (Tree iface : node.getImplementsClause())
|
||||
{
|
||||
recordFullHierarchy(trees.getElement(new TreePath(getCurrentPath(), iface)).asType());
|
||||
}
|
||||
return super.visitClass(node, unused);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitNewClass(NewClassTree node, Void unused)
|
||||
{
|
||||
recordElement(trees.getElement(getCurrentPath()), null);
|
||||
return super.visitNewClass(node, unused);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitAnnotation(AnnotationTree node, Void unused)
|
||||
{
|
||||
Element typ = trees.getElement(new TreePath(getCurrentPath(), node.getAnnotationType()));
|
||||
if (typ instanceof Symbol.TypeSymbol)
|
||||
{
|
||||
Symbol.TypeSymbol cs = (Symbol.TypeSymbol) typ;
|
||||
// non runtime annotations may not exist in the output jar, so they can be
|
||||
// always considered as missing if we were to record them
|
||||
if (internalTypes.getRetention(cs) == Attribute.RetentionPolicy.RUNTIME)
|
||||
{
|
||||
scan(node.getAnnotationType(), null);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
unexpected(typ);
|
||||
}
|
||||
scan(node.getArguments(), null);
|
||||
return null;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private boolean shouldRecord(TypeMirror tm)
|
||||
{
|
||||
if (tm instanceof DeclaredType)
|
||||
{
|
||||
Element e = ((DeclaredType) tm).asElement();
|
||||
|
||||
// there isn't a particularly nice way to find where a symbol is resolved from in the public api
|
||||
JavaFileObject classfile = ((Symbol.ClassSymbol) e).classfile;
|
||||
if (classfile == null || classfile.getKind() == JavaFileObject.Kind.SOURCE)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
String fqn = elements.getBinaryName((TypeElement) e).toString();
|
||||
return jvmClassCache.computeIfAbsent(fqn, name ->
|
||||
ClassLoader.getPlatformClassLoader()
|
||||
.getResource(fqn.replace('.', '/') + ".class") == null);
|
||||
}
|
||||
else if (tm instanceof TypeVariable)
|
||||
{
|
||||
return shouldRecord(((TypeVariable) tm).getLowerBound());
|
||||
}
|
||||
else if (tm instanceof ArrayType || tm instanceof NullType || tm instanceof NoType || tm instanceof PrimitiveType)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
else
|
||||
{
|
||||
unexpected(tm);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private boolean shouldRecord(Element element)
|
||||
{
|
||||
if (element instanceof TypeElement)
|
||||
{
|
||||
return shouldRecord(element.asType());
|
||||
}
|
||||
else if (element instanceof PackageElement)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (element.getEnclosingElement() != null && element.getEnclosingElement() != element)
|
||||
{
|
||||
return shouldRecord(element.getEnclosingElement());
|
||||
}
|
||||
else
|
||||
{
|
||||
unexpected(element);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isJLObject(TypeMirror tm)
|
||||
{
|
||||
tm = types.erasure(tm);
|
||||
return tm instanceof DeclaredType && elements.getBinaryName((TypeElement) ((DeclaredType) tm).asElement()).contentEquals("java.lang.Object");
|
||||
}
|
||||
|
||||
private void recordElement(Element element, TypeMirror receiverType)
|
||||
{
|
||||
if (element == null)
|
||||
{
|
||||
unexpected(element);
|
||||
}
|
||||
|
||||
if (receiverType != null && shouldRecord(receiverType))
|
||||
{
|
||||
if (element instanceof ExecutableElement)
|
||||
{
|
||||
TypeMirror realRecv = element.getEnclosingElement().asType();
|
||||
|
||||
if (!isJLObject(realRecv) && !recordClassHierarchy(receiverType, realRecv))
|
||||
{
|
||||
log.warn("could not find {} in {}", receiverType, realRecv);
|
||||
unexpected(realRecv);
|
||||
}
|
||||
}
|
||||
else if (element instanceof VariableElement)
|
||||
{
|
||||
switch (element.getKind())
|
||||
{
|
||||
case FIELD:
|
||||
case ENUM_CONSTANT:
|
||||
TypeMirror realRecv = element.getEnclosingElement().asType();
|
||||
|
||||
if (!isJLObject(realRecv) && !recordClassHierarchy(receiverType, realRecv))
|
||||
{
|
||||
log.warn("could not find {} in {}", receiverType, realRecv);
|
||||
unexpected(realRecv);
|
||||
}
|
||||
break;
|
||||
case RESOURCE_VARIABLE:
|
||||
case LOCAL_VARIABLE:
|
||||
case PARAMETER:
|
||||
return;
|
||||
default:
|
||||
unexpected(element);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (element instanceof TypeElement)
|
||||
{
|
||||
// Inner class ref
|
||||
TypeMirror realRecv = element.getEnclosingElement().asType();
|
||||
|
||||
if (!isJLObject(realRecv) && !recordClassHierarchy(receiverType, realRecv))
|
||||
{
|
||||
log.warn("could not find {} in {}", receiverType, realRecv);
|
||||
unexpected(realRecv);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
unexpected(element);
|
||||
}
|
||||
}
|
||||
|
||||
if (!shouldRecord(element))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (element instanceof ExecutableElement)
|
||||
{
|
||||
ExecutableElement et = (ExecutableElement) element;
|
||||
StringBuilder desc = new StringBuilder();
|
||||
boolean ok = true;
|
||||
String n;
|
||||
desc.append('(');
|
||||
for (VariableElement t : et.getParameters())
|
||||
{
|
||||
n = typeDescriptor(t);
|
||||
ok &= n != null;
|
||||
desc.append(n);
|
||||
}
|
||||
desc.append(')');
|
||||
n = typeDescriptor(et.getReturnType());
|
||||
ok &= n != null;
|
||||
desc.append(n);
|
||||
|
||||
if (ok)
|
||||
{
|
||||
api.recordMethod(
|
||||
modifiers(et),
|
||||
typeDescriptor(et.getEnclosingElement()),
|
||||
element.getSimpleName(),
|
||||
desc.toString());
|
||||
}
|
||||
}
|
||||
else if (element instanceof VariableElement)
|
||||
{
|
||||
switch (element.getKind())
|
||||
{
|
||||
case FIELD:
|
||||
case ENUM_CONSTANT:
|
||||
if ("class".equals(element.getSimpleName().toString()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
api.recordField(
|
||||
modifiers(element),
|
||||
typeDescriptor(element.getEnclosingElement()),
|
||||
element.getSimpleName(),
|
||||
typeDescriptor(element.asType()),
|
||||
((VariableElement) element).getConstantValue());
|
||||
break;
|
||||
case RESOURCE_VARIABLE:
|
||||
case LOCAL_VARIABLE:
|
||||
case PARAMETER:
|
||||
return;
|
||||
default:
|
||||
unexpected(element);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (element instanceof TypeElement)
|
||||
{
|
||||
api.recordClass(
|
||||
modifiers(element),
|
||||
typeDescriptor(element));
|
||||
}
|
||||
else
|
||||
{
|
||||
unexpected(element);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean recordClassHierarchy(TypeMirror from, TypeMirror target)
|
||||
{
|
||||
// no public api for superclass, without getting a TypeElement
|
||||
if (types.isSameType(types.erasure(from), types.erasure(target)))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean hit = false;
|
||||
for (TypeMirror m : types.directSupertypes(from))
|
||||
{
|
||||
if (recordClassHierarchy(m, target))
|
||||
{
|
||||
hit = true;
|
||||
if (shouldRecord(from))
|
||||
{
|
||||
api.recordClassHierarchy(typeDescriptor(from), typeDescriptor(m));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return hit;
|
||||
}
|
||||
|
||||
private void recordFullHierarchy(TypeMirror from)
|
||||
{
|
||||
if (shouldRecord(from))
|
||||
{
|
||||
for (TypeMirror m : types.directSupertypes(from))
|
||||
{
|
||||
if (!isJLObject(m))
|
||||
{
|
||||
recordFullHierarchy(m);
|
||||
api.recordClassHierarchy(typeDescriptor(from), typeDescriptor(m));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int modifiers(Element e)
|
||||
{
|
||||
// element.getModifiers is not complete, so we use the internal api
|
||||
// however some of it's bits are different from the classfile format's
|
||||
long flags = ((Symbol) e).flags_field;
|
||||
if ((flags & Flags.VARARGS) != 0)
|
||||
{
|
||||
flags |= Opcodes.ACC_VARARGS;
|
||||
}
|
||||
if (((flags & Flags.DEFAULT) != 0) && ((flags & Opcodes.ACC_INTERFACE) == 0))
|
||||
{
|
||||
flags &= ~Opcodes.ACC_ABSTRACT;
|
||||
}
|
||||
return (int) flags;
|
||||
}
|
||||
|
||||
private String typeDescriptor(Element element)
|
||||
{
|
||||
if (element instanceof TypeElement || element instanceof VariableElement)
|
||||
{
|
||||
return typeDescriptor(element.asType());
|
||||
}
|
||||
else
|
||||
{
|
||||
unexpected(element);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private String typeDescriptor(TypeMirror type)
|
||||
{
|
||||
switch (type.getKind())
|
||||
{
|
||||
case VOID:
|
||||
return "V";
|
||||
case BOOLEAN:
|
||||
return "Z";
|
||||
case CHAR:
|
||||
return "C";
|
||||
case BYTE:
|
||||
return "B";
|
||||
case SHORT:
|
||||
return "S";
|
||||
case INT:
|
||||
return "I";
|
||||
case FLOAT:
|
||||
return "F";
|
||||
case LONG:
|
||||
return "J";
|
||||
case DOUBLE:
|
||||
return "D";
|
||||
}
|
||||
if (type instanceof ArrayType)
|
||||
{
|
||||
ArrayType at = (ArrayType) type;
|
||||
return "[" + typeDescriptor(at.getComponentType());
|
||||
}
|
||||
else if (type instanceof DeclaredType)
|
||||
{
|
||||
Element e = ((DeclaredType) type).asElement();
|
||||
// returns the fqn, not binary name
|
||||
String fqn = elements.getBinaryName((TypeElement) e).toString();
|
||||
return "L" + fqn.replace('.', '/') + ";";
|
||||
}
|
||||
else if (type instanceof TypeVariable)
|
||||
{
|
||||
return typeDescriptor(types.erasure(type));
|
||||
}
|
||||
else
|
||||
{
|
||||
unexpected(type);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void unexpected(Element element)
|
||||
{
|
||||
if (element == null)
|
||||
{
|
||||
log.warn("unexpected null", new Exception());
|
||||
}
|
||||
else
|
||||
{
|
||||
log.warn("Unexpected {} {} {}", element.getKind(), element.getClass(), element, new Exception());
|
||||
}
|
||||
|
||||
unexpected();
|
||||
}
|
||||
|
||||
private void unexpected(TypeMirror mirror)
|
||||
{
|
||||
if (mirror == null)
|
||||
{
|
||||
log.warn("unexpected null type", new Exception());
|
||||
}
|
||||
else
|
||||
{
|
||||
log.warn("Unexpected {} {} {}", mirror.getKind(), mirror.getClass(), mirror, new Exception());
|
||||
}
|
||||
|
||||
unexpected();
|
||||
}
|
||||
|
||||
private void unexpected()
|
||||
{
|
||||
partial = true;
|
||||
TreePath p = getCurrentPath();
|
||||
for (int i = 0; p.getParentPath() != null && i < 1; p = p.getParentPath(), i++) ;
|
||||
log.info("{} {}", getCurrentPath().getCompilationUnit().getSourceFile().getName(), PrintingScanner.print(p.getLeaf()));
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
net.runelite.pluginhub.apirecorder.RecorderPlugin
|
||||
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Abex
|
||||
* 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.
|
||||
*/
|
||||
allprojects {
|
||||
version "2.0-SNAPSHOT"
|
||||
group "net.runelite.pluginhub"
|
||||
|
||||
tasks.withType(AbstractArchiveTask) {
|
||||
preserveFileTimestamps = false
|
||||
reproducibleFileOrder = true
|
||||
}
|
||||
}
|
||||
|
||||
subprojects {
|
||||
apply plugin: "java"
|
||||
|
||||
java {
|
||||
sourceCompatibility = 11
|
||||
}
|
||||
|
||||
task shadowJar(type: Jar) {
|
||||
archiveFileName.set archiveBaseName.get() + "." + archiveExtension.get()
|
||||
from {
|
||||
configurations.runtimeClasspath.collect {
|
||||
it.isDirectory() ? it : zipTree(it)
|
||||
}
|
||||
}
|
||||
with tasks.jar
|
||||
}
|
||||
}
|
||||
|
||||
// we have this task then java -jar the output rather than using gradle run so
|
||||
// the daemon used to build this can be reused by a real plugin build
|
||||
task prep {
|
||||
dependsOn ":package:shadowJar"
|
||||
dependsOn ":apirecorder:shadowJar"
|
||||
doLast {
|
||||
file("build").mkdirs()
|
||||
file("build/gradleHome").write(gradle.gradleHomeDir.absolutePath)
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
# this should match what Plugin uses to build with
|
||||
org.gradle.jvmargs=-Xmx768M -XX:+UseParallelGC
|
||||
@@ -1,214 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<verification-metadata xmlns="https://schema.gradle.org/dependency-verification" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://schema.gradle.org/dependency-verification https://schema.gradle.org/dependency-verification/dependency-verification-1.3.xsd">
|
||||
<configuration>
|
||||
<verify-metadata>true</verify-metadata>
|
||||
<verify-signatures>false</verify-signatures>
|
||||
<trusted-artifacts>
|
||||
<trust file=".*-javadoc[.]jar" regex="true"/>
|
||||
<trust file=".*-sources[.]jar" regex="true"/>
|
||||
</trusted-artifacts>
|
||||
</configuration>
|
||||
<components>
|
||||
<component group="com.google.code.findbugs" name="jsr305" version="3.0.2">
|
||||
<artifact name="jsr305-3.0.2.jar">
|
||||
<sha256 value="766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="jsr305-3.0.2.pom">
|
||||
<sha256 value="19889dbdf1b254b2601a5ee645b8147a974644882297684c798afe5d63d78dfe" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.google.code.gson" name="gson" version="2.8.5">
|
||||
<artifact name="gson-2.8.5.jar">
|
||||
<sha256 value="233a0149fc365c9f6edbd683cfe266b19bdc773be98eabdaf6b3c924b48e7d81" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="gson-2.8.5.pom">
|
||||
<sha256 value="b8308557a7fccc92d9fe7c8cd0599258b361285d2ecde7689eda98843255a092" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.google.code.gson" name="gson-parent" version="2.8.5">
|
||||
<artifact name="gson-parent-2.8.5.pom">
|
||||
<sha256 value="8f1fec72b91a71ea39ec39f5f778c4d1124b6b097c6d55b3a50b554a52237b27" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.google.errorprone" name="error_prone_annotations" version="2.0.18">
|
||||
<artifact name="error_prone_annotations-2.0.18.jar">
|
||||
<sha256 value="cb4cfad870bf563a07199f3ebea5763f0dec440fcda0b318640b1feaa788656b" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="error_prone_annotations-2.0.18.pom">
|
||||
<sha256 value="9144127192d6f612c2366825dceaeb23b0d53130b83e0bf1ffe107d1470a8487" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.google.errorprone" name="error_prone_parent" version="2.0.18">
|
||||
<artifact name="error_prone_parent-2.0.18.pom">
|
||||
<sha256 value="cf149955279b07d4f11e817985c1164a69e930d73db7441b43a6ef53bbd286c4" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.google.guava" name="guava" version="23.2-jre">
|
||||
<artifact name="guava-23.2-jre.jar">
|
||||
<sha256 value="5be9a7d05ba0ccd74708bc8018ae412255f85843c0b92302e9b9befa6ed52564" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="guava-23.2-jre.pom">
|
||||
<sha256 value="57abac7e3962e45ad1c8645d91cdad96e1e0bf23b8b38e6f389011f7ae496870" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.google.guava" name="guava-parent" version="23.2-jre">
|
||||
<artifact name="guava-parent-23.2-jre.pom">
|
||||
<sha256 value="e92cd52f3b86a0f6fe90fc94a86ea072a2b112fc40063bd33f6679778038cc47" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.google.j2objc" name="j2objc-annotations" version="1.1">
|
||||
<artifact name="j2objc-annotations-1.1.jar">
|
||||
<sha256 value="2994a7eb78f2710bd3d3bfb639b2c94e219cedac0d4d084d516e78c16dddecf6" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="j2objc-annotations-1.1.pom">
|
||||
<sha256 value="f0c98c571e93a7cb4dd18df0fa308f0963e7a0620ac2d4244e61e709d03ad6be" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.squareup.okhttp3" name="mockwebserver" version="3.14.9">
|
||||
<artifact name="mockwebserver-3.14.9.jar">
|
||||
<sha256 value="099f70ab2997f628bf93c358baac829ccb625b6e08a9361e09d6923fa9af1f68" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="mockwebserver-3.14.9.pom">
|
||||
<sha256 value="24fc2ed2c0d80c7461e24f4dc3f873381a5a77246cf99a57988d1b389b091fd6" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.squareup.okhttp3" name="okhttp" version="3.14.9">
|
||||
<artifact name="okhttp-3.14.9.jar">
|
||||
<sha256 value="2570fab55515cbf881d7a4ceef49fc515490bc027057e666776a2832465aeca0" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="okhttp-3.14.9.pom">
|
||||
<sha256 value="61edf3c4764ea95f1212ca2dfbc6ea5d4133c71c4e8c40bd247ec83e7c6b7ce7" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.squareup.okhttp3" name="parent" version="3.14.9">
|
||||
<artifact name="parent-3.14.9.pom">
|
||||
<sha256 value="b5f101dcf2cc7f32ec3d7b68bdc078e77eac1979bb8664e8f87f762288c3dce5" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.squareup.okio" name="okio" version="1.17.2">
|
||||
<artifact name="okio-1.17.2.jar">
|
||||
<sha256 value="f80ce42d2ffac47ad4c47e1d6f980d604d247ceb1a886705cf4581ab0c9fe2b8" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="okio-1.17.2.pom">
|
||||
<sha256 value="cd57f75443ab6a714b203da51994ee64ed0e919fea81f04e4f4b5324e4bb03f0" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.squareup.okio" name="okio-parent" version="1.17.2">
|
||||
<artifact name="okio-parent-1.17.2.pom">
|
||||
<sha256 value="e81f40146f2a0eb394b10fa3a175c85ab9fbe757aa418aa44d4b760efe16f024" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="junit" name="junit" version="4.12">
|
||||
<artifact name="junit-4.12.jar">
|
||||
<sha256 value="59721f0805e223d84b90677887d9ff567dc534d7c502ca903c0c2b17f05c116a" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="junit-4.12.pom">
|
||||
<sha256 value="90f163f78e3ffb6f1c7ad97de9e7eba4eea25807141b85d6d12be67ca25449c4" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.codehaus" name="codehaus-parent" version="4">
|
||||
<artifact name="codehaus-parent-4.pom">
|
||||
<sha256 value="6b87237de8c2e1740cf80627c7f3ce3e15de1930bb250c55a1eca94fa3e014df" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.codehaus.mojo" name="animal-sniffer-annotations" version="1.14">
|
||||
<artifact name="animal-sniffer-annotations-1.14.jar">
|
||||
<sha256 value="2068320bd6bad744c3673ab048f67e30bef8f518996fa380033556600669905d" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="animal-sniffer-annotations-1.14.pom">
|
||||
<sha256 value="1879f19a05991e3ed95910b96689333396b0c467a215dc4d1f90018404b72a26" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.codehaus.mojo" name="animal-sniffer-parent" version="1.14">
|
||||
<artifact name="animal-sniffer-parent-1.14.pom">
|
||||
<sha256 value="f51550a06b1410bd4962cb0e71df0b921a60a7ef47bfa9c4825a14be72316eea" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.codehaus.mojo" name="mojo-parent" version="34">
|
||||
<artifact name="mojo-parent-34.pom">
|
||||
<sha256 value="3e395d6fbc43c09a3774cac8694ce527398305ea3fd5492d80e25af27d382a9c" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.gradle" name="gradle-tooling-api" version="8.10">
|
||||
<artifact name="gradle-tooling-api-8.10.jar">
|
||||
<sha256 value="b1e5d6a65e77302e836c325c28b944b05f0f1b62a332d6d4980049c555a70b02" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="gradle-tooling-api-8.10.module">
|
||||
<sha256 value="47d5af6c7ff4fd5167b09f5fd8cdc568823bcea77bb2ae8344c43bb375f1eccf" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.hamcrest" name="hamcrest-core" version="1.3">
|
||||
<artifact name="hamcrest-core-1.3.jar">
|
||||
<sha256 value="66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="hamcrest-core-1.3.pom">
|
||||
<sha256 value="fde386a7905173a1b103de6ab820727584b50d0e32282e2797787c20a64ffa93" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.hamcrest" name="hamcrest-parent" version="1.3">
|
||||
<artifact name="hamcrest-parent-1.3.pom">
|
||||
<sha256 value="6d535f94efb663bdb682c9f27a50335394688009642ba7a9677504bc1be4129b" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.ow2" name="ow2" version="1.5">
|
||||
<artifact name="ow2-1.5.pom">
|
||||
<sha256 value="0f8a1b116e760b8fe6389c51b84e4b07a70fc11082d4f936e453b583dd50b43b" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.ow2.asm" name="asm" version="9.3">
|
||||
<artifact name="asm-9.3.jar">
|
||||
<sha256 value="1263369b59e29c943918de11d6d6152e2ec6085ce63e5710516f8c67d368e4bc" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="asm-9.3.pom">
|
||||
<sha256 value="8eac07e29f8aea83a8156d7b29fa368f6ebf3becfb209c9a1ac36abd906123e0" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.projectlombok" name="lombok" version="1.18.30">
|
||||
<artifact name="lombok-1.18.30.jar">
|
||||
<sha256 value="14151b47582d570b4de16a147ece3bdbd19ace4aee5bde3a5578c87db9ecb998" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="lombok-1.18.30.pom">
|
||||
<sha256 value="65001f382f5d1ce45692311bb1ba7c243c856195d492473d72ff9a7d1cc77699" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.slf4j" name="slf4j-api" version="1.7.10">
|
||||
<artifact name="slf4j-api-1.7.10.jar">
|
||||
<sha256 value="3863e27005740d4d1289bf87b113efea115e9a22408a7d623be8004991232bfe" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="slf4j-api-1.7.10.pom">
|
||||
<sha256 value="af9c5e8d2263422c74792ddd91b3cc1a24bd02b451b54cbb10cd6f2ba46c14b1" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.slf4j" name="slf4j-api" version="1.7.36">
|
||||
<artifact name="slf4j-api-1.7.36.jar">
|
||||
<sha256 value="d3ef575e3e4979678dc01bf1dcce51021493b4d11fb7f1be8ad982877c16a1c0" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="slf4j-api-1.7.36.pom">
|
||||
<sha256 value="fb046a9c229437928bb11c2d27c8b5d773eb8a25e60cbd253d985210dedc2684" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.slf4j" name="slf4j-parent" version="1.7.10">
|
||||
<artifact name="slf4j-parent-1.7.10.pom">
|
||||
<sha256 value="1abee7f5182fb79b4926fb64658af8c3248ed6b374f9ac7da1fd9e8b9197e2ce" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.slf4j" name="slf4j-parent" version="1.7.36">
|
||||
<artifact name="slf4j-parent-1.7.36.pom">
|
||||
<sha256 value="bb388d37fbcdd3cde64c3cede21838693218dc451f04040c5df360a78ed7e812" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.slf4j" name="slf4j-simple" version="1.7.10">
|
||||
<artifact name="slf4j-simple-1.7.10.jar">
|
||||
<sha256 value="3209a859bb47873c0d1873f7d69aa6652dc07fe32ed935492fe0b1cf1d2cf140" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="slf4j-simple-1.7.10.pom">
|
||||
<sha256 value="bbd8c6f5cf54d1c1fb11b415e51f6b35483fc264f81ccd98654cc3420e756901" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.sonatype.oss" name="oss-parent" version="7">
|
||||
<artifact name="oss-parent-7.pom">
|
||||
<sha256 value="b51f8867c92b6a722499557fc3a1fdea77bdf9ef574722fe90ce436a29559454" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
</components>
|
||||
</verification-metadata>
|
||||
@@ -1,68 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Abex
|
||||
* 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.
|
||||
*/
|
||||
repositories {
|
||||
maven {
|
||||
url "https://repo.gradle.org/gradle/libs-releases-remote-cache/"
|
||||
}
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "org.gradle:gradle-tooling-api:8.10"
|
||||
implementation "org.slf4j:slf4j-simple:1.7.10"
|
||||
implementation "com.google.code.findbugs:jsr305:3.0.2"
|
||||
implementation "com.google.guava:guava:23.2-jre"
|
||||
implementation "org.ow2.asm:asm:9.3"
|
||||
implementation "com.squareup.okhttp3:okhttp:3.14.9"
|
||||
implementation "com.google.code.gson:gson:2.8.5"
|
||||
implementation project(":upload")
|
||||
implementation project(":apirecorder")
|
||||
|
||||
def lombok = "org.projectlombok:lombok:1.18.30";
|
||||
compileOnly lombok
|
||||
annotationProcessor lombok
|
||||
testCompileOnly lombok
|
||||
testAnnotationProcessor lombok
|
||||
|
||||
testImplementation "junit:junit:4.12"
|
||||
testImplementation "com.squareup.okhttp3:mockwebserver:3.14.9"
|
||||
}
|
||||
|
||||
processResources {
|
||||
from("../verification-template/gradle/verification-metadata.xml") {
|
||||
into "net/runelite/pluginhub/packager"
|
||||
}
|
||||
}
|
||||
|
||||
jar {
|
||||
manifest {
|
||||
attributes "Main-Class": "net.runelite.pluginhub.packager.Packager"
|
||||
}
|
||||
}
|
||||
|
||||
test {
|
||||
dependsOn ":apirecorder:shadowJar"
|
||||
workingDir new File(project.rootDir, "../")
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2026 Abex
|
||||
* 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.pluginhub.packager;
|
||||
|
||||
public enum BuildType
|
||||
{
|
||||
STANDARD,
|
||||
GRADLE,
|
||||
}
|
||||
-47
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Abex
|
||||
* 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.pluginhub.packager;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
public class DisabledPluginException extends Exception
|
||||
{
|
||||
@Getter
|
||||
private final String internalName;
|
||||
|
||||
@Getter
|
||||
private final String reason;
|
||||
|
||||
@Getter
|
||||
private final boolean includeInUnavailable;
|
||||
|
||||
public DisabledPluginException(String internalName, String reason, boolean includeInUnavailable)
|
||||
{
|
||||
super("Plugin \"" + internalName + "\" is disabled: " + reason);
|
||||
this.reason = reason;
|
||||
this.internalName = internalName;
|
||||
this.includeInUnavailable = includeInUnavailable;
|
||||
}
|
||||
}
|
||||
@@ -1,537 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Abex
|
||||
* 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.pluginhub.packager;
|
||||
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.base.Stopwatch;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Queues;
|
||||
import com.google.common.io.ByteStreams;
|
||||
import com.google.common.io.Files;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.Semaphore;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.stream.StreamSupport;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.runelite.pluginhub.uploader.ManifestDiff;
|
||||
import net.runelite.pluginhub.uploader.PluginHubManifest;
|
||||
import net.runelite.pluginhub.uploader.UploadConfiguration;
|
||||
import net.runelite.pluginhub.uploader.Util;
|
||||
import org.slf4j.helpers.FormattingTuple;
|
||||
import org.slf4j.helpers.MessageFormatter;
|
||||
|
||||
@Slf4j
|
||||
public class Packager implements Closeable
|
||||
{
|
||||
private static final File PLUGIN_ROOT = new File("./plugins");
|
||||
public static final File PACKAGE_ROOT = new File("./package/").getAbsoluteFile();
|
||||
private static final File ARTIFACT_DIR = new File("/tmp/jars");
|
||||
|
||||
private Semaphore apiCheckSemaphore = new Semaphore(8);
|
||||
private Semaphore downloadSemaphore = new Semaphore(2);
|
||||
private Semaphore buildSemaphore = new Semaphore(Runtime.getRuntime().availableProcessors());
|
||||
private Semaphore uploadSemaphore = new Semaphore(2);
|
||||
|
||||
private final List<File> buildList;
|
||||
|
||||
@Getter
|
||||
private final String runeliteVersion;
|
||||
|
||||
@Setter
|
||||
private String apiFilesVersion;
|
||||
private PluginHubManifest.ManifestFull oldManifest;
|
||||
private Map<String, PluginHubManifest.JarData> oldJarData = ImmutableMap.of();
|
||||
private Map<String, PluginHubManifest.DisplayData> oldDisplayData = ImmutableMap.of();
|
||||
|
||||
@Getter
|
||||
private final UploadConfiguration uploadConfig = new UploadConfiguration();
|
||||
|
||||
private final AtomicInteger numDone = new AtomicInteger(0);
|
||||
private final int numTotal;
|
||||
|
||||
@Setter
|
||||
private boolean alwaysPrintLog;
|
||||
|
||||
@Getter
|
||||
private boolean failed;
|
||||
|
||||
private boolean isIncrementalRebuild;
|
||||
|
||||
private final StringBuilder buildSummary = new StringBuilder();
|
||||
|
||||
private ManifestDiff diff = new ManifestDiff();
|
||||
|
||||
public Packager(List<File> buildList) throws IOException
|
||||
{
|
||||
this.buildList = buildList;
|
||||
this.numTotal = buildList.size();
|
||||
this.runeliteVersion = Util.readRLVersion();
|
||||
}
|
||||
|
||||
public void buildPlugins() throws IOException
|
||||
{
|
||||
if (apiFilesVersion != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
oldManifest = uploadConfig.getManifest(apiFilesVersion, null);
|
||||
oldJarData = oldManifest.getJars()
|
||||
.stream().collect(ImmutableMap.toImmutableMap(PluginHubManifest.JarData::getInternalName, Function.identity()));
|
||||
oldDisplayData = oldManifest.getDisplay()
|
||||
.stream().collect(ImmutableMap.toImmutableMap(PluginHubManifest.DisplayData::getInternalName, Function.identity()));
|
||||
diff.setOldManifestVersion(apiFilesVersion);
|
||||
}
|
||||
catch (RuntimeException | IOException e)
|
||||
{
|
||||
log.info("Unable to download previous manifest, doing full build", e);
|
||||
apiFilesVersion = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (uploadConfig.isComplete())
|
||||
{
|
||||
uploadConfig.mkdirs(uploadConfig.getRoot().newBuilder().addPathSegment(UploadConfiguration.DIR_JAR).build());
|
||||
uploadConfig.mkdirs(uploadConfig.getRoot().newBuilder().addPathSegment(UploadConfiguration.DIR_ICON).build());
|
||||
uploadConfig.mkdirs(uploadConfig.getRoot().newBuilder().addPathSegment(UploadConfiguration.DIR_API).build());
|
||||
uploadConfig.mkdirs(uploadConfig.getRoot().newBuilder().addPathSegment(UploadConfiguration.DIR_LOG).build());
|
||||
uploadConfig.mkdirs(uploadConfig.getRoot().newBuilder().addPathSegment(UploadConfiguration.DIR_SOURCE).build());
|
||||
uploadConfig.mkdirs(uploadConfig.getRoot().newBuilder().addPathSegment(UploadConfiguration.DIR_MANIFEST).build());
|
||||
}
|
||||
|
||||
ARTIFACT_DIR.mkdirs();
|
||||
|
||||
Queue<File> buildQueue = Queues.synchronizedQueue(new ArrayDeque<>(buildList));
|
||||
List<Thread> buildThreads = IntStream.range(0, 8)
|
||||
.mapToObj(v ->
|
||||
{
|
||||
Thread t = new Thread(() ->
|
||||
{
|
||||
for (File plugin; (plugin = buildQueue.poll()) != null; )
|
||||
{
|
||||
buildPlugin(plugin);
|
||||
}
|
||||
});
|
||||
t.start();
|
||||
return t;
|
||||
}).collect(Collectors.toList());
|
||||
|
||||
for (Thread buildThread : buildThreads)
|
||||
{
|
||||
try
|
||||
{
|
||||
buildThread.join();
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
String diffJSON = Util.GSON.toJson(diff);
|
||||
log.debug("manifest change: {}", diffJSON);
|
||||
|
||||
try (FileOutputStream fos = new FileOutputStream("/tmp/manifest_diff"))
|
||||
{
|
||||
fos.write(diffJSON.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
}
|
||||
|
||||
private void buildPlugin(File plugin)
|
||||
{
|
||||
diff.getRemove().add(plugin.getName());
|
||||
|
||||
if (!plugin.exists())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try (Plugin p = new Plugin(plugin))
|
||||
{
|
||||
try
|
||||
{
|
||||
PluginHubManifest.JarData oldJarData = this.oldJarData.get(p.getInternalName());
|
||||
if (isIncrementalRebuild && oldJarData != null)
|
||||
{
|
||||
try (Closeable ignored = acquireAPICheck(p))
|
||||
{
|
||||
if (!p.rebuildNeeded(uploadConfig, oldJarData))
|
||||
{
|
||||
diff.getCopyFromOld().add(p.getInternalName());
|
||||
diff.getRemove().remove(p.getInternalName());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
try (Closeable ignored = acquireDownload(p))
|
||||
{
|
||||
p.download();
|
||||
}
|
||||
try (Closeable ignored = acquireBuild(p))
|
||||
{
|
||||
p.build(runeliteVersion, alwaysPrintLog);
|
||||
}
|
||||
String logURL = "";
|
||||
if (uploadConfig.isComplete())
|
||||
{
|
||||
try (Closeable ignored = acquireUpload(p))
|
||||
{
|
||||
p.upload(uploadConfig);
|
||||
}
|
||||
|
||||
// outside the semaphore so the timing gets uploaded too
|
||||
logURL = p.uploadLog(uploadConfig);
|
||||
}
|
||||
|
||||
p.copyArtifacts(ARTIFACT_DIR);
|
||||
|
||||
diff.getAddJarData().add(p.getJarData());
|
||||
diff.getAddDisplayData().add(p.getDisplayData());
|
||||
log.info("{}: done in {}ms [{}/{}]", p.getInternalName(), p.getBuildTimeMS(), numDone.get() + 1, numTotal);
|
||||
|
||||
if (!p.getApiFile().exists())
|
||||
{
|
||||
logToSummary("{} failed to write the api record: {}", p.getInternalName(), logURL);
|
||||
}
|
||||
}
|
||||
catch (PluginBuildException e)
|
||||
{
|
||||
failed = true;
|
||||
p.writeLog("package failed\n", e);
|
||||
if (!alwaysPrintLog)
|
||||
{
|
||||
Files.asCharSource(p.getLogFile(), StandardCharsets.UTF_8).copyTo(System.out);
|
||||
}
|
||||
|
||||
PluginHubManifest.DisplayData oldDisplayData = this.oldDisplayData.get(p.getInternalName());
|
||||
|
||||
if (uploadConfig.isComplete())
|
||||
{
|
||||
String logURL = p.uploadLog(uploadConfig);
|
||||
if (oldDisplayData != null && oldDisplayData.getBuildFailAt() != null)
|
||||
{
|
||||
long daysFailed = Instant.ofEpochSecond(oldDisplayData.getBuildFailAt()).until(Instant.now(), ChronoUnit.DAYS);
|
||||
logToSummary("{} failed ({} days): {}", p.getInternalName(), daysFailed, logURL);
|
||||
}
|
||||
else
|
||||
{
|
||||
logToSummary("{} failed: {}", p.getInternalName(), logURL);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
logToSummary("{} failed", p.getInternalName());
|
||||
}
|
||||
|
||||
if (oldDisplayData != null)
|
||||
{
|
||||
if (oldDisplayData.getBuildFailAt() == null)
|
||||
{
|
||||
oldDisplayData.setBuildFailAt(Instant.now().getEpochSecond());
|
||||
}
|
||||
|
||||
oldDisplayData.setUnavailableReason(null);
|
||||
diff.getAddDisplayData().add(oldDisplayData);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (alwaysPrintLog)
|
||||
{
|
||||
Files.asCharSource(p.getLogFile(), StandardCharsets.UTF_8).copyTo(System.out);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (DisabledPluginException e)
|
||||
{
|
||||
if (e.isIncludeInUnavailable())
|
||||
{
|
||||
PluginHubManifest.DisplayData oldDisplayData = this.oldDisplayData.get(e.getInternalName());
|
||||
if (oldDisplayData == null)
|
||||
{
|
||||
logToSummary("unavailible plugin {} wasn't in the previous manifest", e.getInternalName());
|
||||
}
|
||||
else
|
||||
{
|
||||
oldDisplayData.setUnavailableReason(e.getReason());
|
||||
diff.getAddDisplayData().add(oldDisplayData);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (PluginBuildException e)
|
||||
{
|
||||
failed = true;
|
||||
logToSummary("", e);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
failed = true;
|
||||
logToSummary("{}: crashed the build script: ", plugin.getName(), e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
numDone.addAndGet(1);
|
||||
}
|
||||
}
|
||||
|
||||
private void logToSummary(String message, Object... args)
|
||||
{
|
||||
log.info(message, args);
|
||||
FormattingTuple fmt = MessageFormatter.arrayFormat(message, args);
|
||||
synchronized (buildSummary)
|
||||
{
|
||||
buildSummary.append(fmt.getMessage()).append('\n');
|
||||
}
|
||||
}
|
||||
|
||||
public String getBuildSummary()
|
||||
{
|
||||
return buildSummary.toString();
|
||||
}
|
||||
|
||||
private Closeable acquireAPICheck(Plugin plugin)
|
||||
{
|
||||
return section(plugin, "apicheck", apiCheckSemaphore);
|
||||
}
|
||||
|
||||
private Closeable acquireDownload(Plugin plugin)
|
||||
{
|
||||
return section(plugin, "download", downloadSemaphore);
|
||||
}
|
||||
|
||||
private Closeable acquireBuild(Plugin plugin)
|
||||
{
|
||||
return section(plugin, "build", buildSemaphore);
|
||||
}
|
||||
|
||||
private Closeable acquireUpload(Plugin plugin)
|
||||
{
|
||||
return section(plugin, "upload", uploadSemaphore);
|
||||
}
|
||||
|
||||
private Closeable section(Plugin p, String name, Semaphore s)
|
||||
{
|
||||
try
|
||||
{
|
||||
s.acquire();
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
Stopwatch time = Stopwatch.createStarted();
|
||||
return () ->
|
||||
{
|
||||
long ms = time.stop()
|
||||
.elapsed(TimeUnit.MILLISECONDS);
|
||||
p.setBuildTimeMS(p.getBuildTimeMS() + ms);
|
||||
p.writeLog("{}: {}ms\n", name, ms);
|
||||
s.release();
|
||||
};
|
||||
}
|
||||
|
||||
public void setIsIncrementalRebuild(boolean incremental)
|
||||
{
|
||||
this.isIncrementalRebuild = incremental;
|
||||
diff.setIgnoreOldManifest(incremental);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close()
|
||||
{
|
||||
uploadConfig.close();
|
||||
}
|
||||
|
||||
public static void main(String... args) throws Exception
|
||||
{
|
||||
boolean isBuildingAll = false;
|
||||
boolean testFailure = false;
|
||||
|
||||
String apiFilesVersion = System.getenv("API_FILES_VERSION");
|
||||
if (apiFilesVersion != null)
|
||||
{
|
||||
apiFilesVersion = apiFilesVersion.trim();
|
||||
if (apiFilesVersion.isEmpty())
|
||||
{
|
||||
apiFilesVersion = null;
|
||||
}
|
||||
}
|
||||
|
||||
String range = System.getenv("PACKAGE_COMMIT_RANGE");
|
||||
|
||||
List<File> buildList;
|
||||
if (args.length != 0)
|
||||
{
|
||||
buildList = Stream.of(args)
|
||||
.map(File::new)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
else if ("ALL".equals(System.getenv("FORCE_BUILD")))
|
||||
{
|
||||
buildList = listAllPlugins();
|
||||
isBuildingAll = true;
|
||||
}
|
||||
else if (!Strings.isNullOrEmpty(System.getenv("FORCE_BUILD")))
|
||||
{
|
||||
buildList = StreamSupport.stream(
|
||||
Splitter.on(',')
|
||||
.trimResults()
|
||||
.omitEmptyStrings()
|
||||
.split(System.getenv("FORCE_BUILD"))
|
||||
.spliterator(), false)
|
||||
.map(name -> new File(PLUGIN_ROOT, name))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
else if (!Strings.isNullOrEmpty(range))
|
||||
{
|
||||
Process gitdiff = new ProcessBuilder("git", "diff", "--name-only", range)
|
||||
.redirectError(ProcessBuilder.Redirect.INHERIT)
|
||||
.start();
|
||||
|
||||
boolean doAll = false;
|
||||
boolean doPackageTests = false;
|
||||
buildList = new ArrayList<>();
|
||||
try (BufferedReader br = new BufferedReader(new InputStreamReader(gitdiff.getInputStream())))
|
||||
{
|
||||
for (String line; (line = br.readLine()) != null; )
|
||||
{
|
||||
if ("runelite.version".equals(line))
|
||||
{
|
||||
doAll = true;
|
||||
}
|
||||
else if (line.startsWith("plugins/"))
|
||||
{
|
||||
buildList.add(new File(line));
|
||||
}
|
||||
else if (line.startsWith("package/") || line.startsWith("templateplugin/") || line.startsWith("create_new_plugin.py"))
|
||||
{
|
||||
doPackageTests = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (doPackageTests)
|
||||
{
|
||||
testFailure |= new ProcessBuilder(new File(PACKAGE_ROOT, "gradlew").getAbsolutePath(), "--console=plain", "test")
|
||||
.directory(PACKAGE_ROOT)
|
||||
.inheritIO()
|
||||
.start()
|
||||
.waitFor() != 0;
|
||||
testFailure |= new ProcessBuilder(new File(PACKAGE_ROOT, "gradlew").getAbsolutePath(), "--console=plain", ":verifyAll")
|
||||
.directory(new File(PACKAGE_ROOT, "verification-template"))
|
||||
.inheritIO()
|
||||
.start()
|
||||
.waitFor() != 0;
|
||||
}
|
||||
|
||||
if (doAll)
|
||||
{
|
||||
isBuildingAll = true;
|
||||
buildList = listAllPlugins();
|
||||
}
|
||||
|
||||
gitdiff.waitFor(1, TimeUnit.SECONDS);
|
||||
if (gitdiff.exitValue() != 0)
|
||||
{
|
||||
throw new RuntimeException("git diff exited with " + gitdiff.exitValue());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new RuntimeException("missing env vars");
|
||||
}
|
||||
|
||||
if (apiFilesVersion == null && !Strings.isNullOrEmpty(range))
|
||||
{
|
||||
String commit = range.substring(0, range.indexOf(".."));
|
||||
Process gitShow = new ProcessBuilder("git", "show", commit + ":runelite.version")
|
||||
.redirectError(ProcessBuilder.Redirect.INHERIT)
|
||||
.start();
|
||||
|
||||
apiFilesVersion = new String(ByteStreams.toByteArray(gitShow.getInputStream()), StandardCharsets.UTF_8)
|
||||
.trim();
|
||||
|
||||
gitShow.waitFor(1, TimeUnit.SECONDS);
|
||||
if (gitShow.exitValue() != 0)
|
||||
{
|
||||
throw new RuntimeException("git show exited with " + gitShow.exitValue());
|
||||
}
|
||||
}
|
||||
|
||||
boolean failed;
|
||||
try (Packager pkg = new Packager(buildList))
|
||||
{
|
||||
pkg.getUploadConfig().fromEnvironment(pkg.getRuneliteVersion());
|
||||
pkg.setAlwaysPrintLog(!pkg.getUploadConfig().isComplete());
|
||||
pkg.setIsIncrementalRebuild(isBuildingAll);
|
||||
pkg.setApiFilesVersion(apiFilesVersion);
|
||||
pkg.buildPlugins();
|
||||
failed = pkg.isFailed();
|
||||
if (isBuildingAll)
|
||||
{
|
||||
String summary = pkg.getBuildSummary();
|
||||
if (!summary.isEmpty())
|
||||
{
|
||||
log.info("Failures:\n{}", summary);
|
||||
}
|
||||
if (!failed)
|
||||
{
|
||||
log.info("All plugins succeeded");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (testFailure || (failed && !isBuildingAll))
|
||||
{
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
static List<File> listAllPlugins()
|
||||
{
|
||||
return Arrays.asList(PLUGIN_ROOT.listFiles());
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
-130
@@ -1,130 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Abex
|
||||
* 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.pluginhub.packager;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.PrintStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.function.Supplier;
|
||||
import org.slf4j.helpers.FormattingTuple;
|
||||
import org.slf4j.helpers.MessageFormatter;
|
||||
|
||||
public class PluginBuildException extends Exception
|
||||
{
|
||||
private String file;
|
||||
private String line;
|
||||
private String help;
|
||||
|
||||
private PluginBuildException(String message, Throwable throwable)
|
||||
{
|
||||
super(message, throwable);
|
||||
}
|
||||
|
||||
public static PluginBuildException of(String internalName, String message, Object... args)
|
||||
{
|
||||
FormattingTuple fmt = MessageFormatter.arrayFormat(internalName + ": " + message, args);
|
||||
return new PluginBuildException(fmt.getMessage(), fmt.getThrowable());
|
||||
}
|
||||
|
||||
public static PluginBuildException of(Plugin plugin, String message, Object... args)
|
||||
{
|
||||
FormattingTuple fmt = MessageFormatter.arrayFormat(plugin.getInternalName() + ": " + message, args);
|
||||
return new PluginBuildException(fmt.getMessage(), fmt.getThrowable());
|
||||
}
|
||||
|
||||
public PluginBuildException withFileLine(File file, String line)
|
||||
{
|
||||
return withFileLine(file.toString(), line);
|
||||
}
|
||||
|
||||
public PluginBuildException withFile(File file)
|
||||
{
|
||||
return withFileLine(file, null);
|
||||
}
|
||||
|
||||
public PluginBuildException withFileLine(String file, String line)
|
||||
{
|
||||
this.file = file;
|
||||
this.line = line;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PluginBuildException withFile(String file)
|
||||
{
|
||||
return withFileLine(file, null);
|
||||
}
|
||||
|
||||
public PluginBuildException withHelp(String help)
|
||||
{
|
||||
this.help = help;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PluginBuildException withHelp(Supplier<String> help)
|
||||
{
|
||||
this.help = help.get();
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getHelpText()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
if (this.file != null)
|
||||
{
|
||||
sb.append("in file ").append(this.file);
|
||||
if (this.line != null)
|
||||
{
|
||||
sb.append(":\n").append(line);
|
||||
}
|
||||
sb.append("\n");
|
||||
}
|
||||
|
||||
if (this.help != null)
|
||||
{
|
||||
sb.append(help);
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void printStackTrace(PrintStream s)
|
||||
{
|
||||
super.printStackTrace(s);
|
||||
s.println("\n");
|
||||
s.println(getMessage());
|
||||
s.println(getHelpText());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void printStackTrace(PrintWriter s)
|
||||
{
|
||||
super.printStackTrace(s);
|
||||
s.println("\n");
|
||||
s.println(getMessage());
|
||||
s.println(getHelpText());
|
||||
}
|
||||
}
|
||||
-44
@@ -1,44 +0,0 @@
|
||||
# avoid client.getVar, prefer client.getVarbitValue
|
||||
Lnet/runelite/api/Client;.getVar(I)I:ab
|
||||
|
||||
# ChatMessageManager.update is a no-op
|
||||
Lnet/runelite/client/chat/ChatMessageManager;.update(Lnet/runelite/api/MessageNode;)V:b
|
||||
|
||||
# Do not create fresh OkHttpClients - always @Inject the client's OkHttpClient
|
||||
Lokhttp3/OkHttpClient;.<init>()V:b
|
||||
Lokhttp3/OkHttpClient$Builder;.<init>()V:b
|
||||
|
||||
# Do not create fresh Gson instances, always @Inject the client's Gson.
|
||||
# You can customize it by calling .newBuilder() on it
|
||||
Lcom/google/gson/Gson;.<init>()V:b
|
||||
Lcom/google/gson/GsonBuilder;.<init>()V:b
|
||||
|
||||
# Use ComponentID or InterfaceID instead of WidgetInfo or WidgetID
|
||||
/^Lnet/runelite/api/widgets/WidgetInfo;/
|
||||
/^Lnet/runelite/api/widgets/WidgetID/
|
||||
|
||||
# Use the overloaded getItemStats(int itemId) method instead
|
||||
Lnet/runelite/client/game/ItemManager;.getItemStats(IZ)Lnet/runelite/http/api/item/ItemStats;:b
|
||||
|
||||
#
|
||||
Lnet/runelite/client/account/AccountClient;.<clinit>()V:gs
|
||||
Lnet/runelite/client/account/AccountClient;.login(I)Lnet/runelite/http/api/account/OAuthResponse;:b
|
||||
Lnet/runelite/client/account/AccountClient;.logout()V:b
|
||||
Lnet/runelite/client/account/AccountClient;.sessionCheck()Z:b
|
||||
Lnet/runelite/client/account/AccountClient;.setUuid(Ljava/util/UUID;)V:b
|
||||
Lnet/runelite/client/account/AccountClient;b
|
||||
Lnet/runelite/client/account/AccountSession;.<init>(Ljava/util/UUID;Ljava/time/Instant;Ljava/lang/String;)V:b
|
||||
Lnet/runelite/client/account/AccountSession;.canEqual(Ljava/lang/Object;)Z:t
|
||||
Lnet/runelite/client/account/AccountSession;.equals(Ljava/lang/Object;)Z:b
|
||||
Lnet/runelite/client/account/AccountSession;.getCreated()Ljava/time/Instant;:b
|
||||
Lnet/runelite/client/account/AccountSession;.getUsername()Ljava/lang/String;:b
|
||||
Lnet/runelite/client/account/AccountSession;.getUuid()Ljava/util/UUID;:b
|
||||
Lnet/runelite/client/account/AccountSession;.hashCode()I:b
|
||||
Lnet/runelite/client/account/AccountSession;.toString()Ljava/lang/String;:b
|
||||
Lnet/runelite/client/account/AccountSession;b
|
||||
Lnet/runelite/client/account/SessionManager;.<clinit>()V:gs
|
||||
Lnet/runelite/client/account/SessionManager;.getAccountSession()Lnet/runelite/client/account/AccountSession;:b
|
||||
Lnet/runelite/client/account/SessionManager;.loadSession()V:b
|
||||
Lnet/runelite/client/account/SessionManager;.login()V:b
|
||||
Lnet/runelite/client/account/SessionManager;.logout()V:b
|
||||
Lnet/runelite/client/account/SessionManager;b
|
||||
-26
@@ -1,26 +0,0 @@
|
||||
plugins {
|
||||
id 'java'
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenLocal()
|
||||
maven {
|
||||
url = 'https://repo.runelite.net'
|
||||
content {
|
||||
includeGroupByRegex("net\\.runelite.*")
|
||||
}
|
||||
}
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly "net.runelite:client"
|
||||
|
||||
compileOnly 'org.projectlombok:lombok:1.18.30'
|
||||
annotationProcessor 'org.projectlombok:lombok:1.18.30'
|
||||
}
|
||||
|
||||
tasks.withType(JavaCompile).configureEach {
|
||||
options.encoding = 'UTF-8'
|
||||
options.release.set(11)
|
||||
}
|
||||
-1
@@ -1 +0,0 @@
|
||||
rootProject.name = "standard-plugin-build"
|
||||
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Abex
|
||||
* 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.pluginhub.packager;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Map;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class DisallowedAPIsTest
|
||||
{
|
||||
@Test
|
||||
public void testDisallowedExist() throws IOException
|
||||
{
|
||||
try (InputStream is = Packager.class.getResourceAsStream("disallowed-apis.txt"))
|
||||
{
|
||||
Map<String, String> disallowed = Plugin.CURRENT_API.parseCommented(is, true);
|
||||
disallowed.forEach((k, v) -> Assert.assertFalse(k + " -> " + v, v.isEmpty()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,251 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Abex
|
||||
* 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.pluginhub.packager;
|
||||
|
||||
import com.google.common.io.Files;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Properties;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.runelite.pluginhub.uploader.Util;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
@Slf4j
|
||||
public class PluginTest
|
||||
{
|
||||
@Test
|
||||
public void testInternalNameChecks() throws IOException, DisabledPluginException
|
||||
{
|
||||
try
|
||||
{
|
||||
new Plugin(new File("plugins/I Like Spaces_and_UNDERSCORES"));
|
||||
Assert.fail();
|
||||
}
|
||||
catch (PluginBuildException e)
|
||||
{
|
||||
log.info("ok: ", e);
|
||||
assertContains(e.getHelpText(), "try: \"i-like-spaces-and-underscores\"");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCommitMustBeComplete() throws DisabledPluginException, IOException
|
||||
{
|
||||
try
|
||||
{
|
||||
newPlugin("test", "" +
|
||||
"repository=https://github.com/runelite/example-plugin.git\n" +
|
||||
"commit=2357276b");
|
||||
Assert.fail();
|
||||
}
|
||||
catch (PluginBuildException e)
|
||||
{
|
||||
log.info("ok: ", e);
|
||||
assertContains(e.getHelpText(), "commit");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExamplePluginCompilesStandard() throws DisabledPluginException, PluginBuildException, IOException, InterruptedException
|
||||
{
|
||||
try (Plugin p = createExamplePlugin("example"))
|
||||
{
|
||||
p.build(Util.readRLVersion(), true);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExamplePluginCompilesGradle() throws DisabledPluginException, PluginBuildException, IOException, InterruptedException
|
||||
{
|
||||
try (Plugin p = createExamplePlugin("example"))
|
||||
{
|
||||
setProp(p, "build", "gradle");
|
||||
p.build(Util.readRLVersion(), true);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPropGarbage() throws DisabledPluginException, PluginBuildException, IOException, InterruptedException
|
||||
{
|
||||
try (Plugin p = createExamplePlugin("example"))
|
||||
{
|
||||
setProp(p, "bui1d", "gradle");
|
||||
p.build(Util.readRLVersion(), true);
|
||||
Assert.fail();
|
||||
}
|
||||
catch (PluginBuildException e)
|
||||
{
|
||||
log.info("ok: ", e);
|
||||
assertContains(e.getHelpText(), "bui1d");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMissingPlugin() throws DisabledPluginException, PluginBuildException, IOException, InterruptedException
|
||||
{
|
||||
try (Plugin p = createExamplePlugin("missing-plugin"))
|
||||
{
|
||||
setProp(p, "plugins", "com.nonexistent");
|
||||
p.build(Util.readRLVersion(), true);
|
||||
Assert.fail();
|
||||
}
|
||||
catch (PluginBuildException e)
|
||||
{
|
||||
log.info("ok: ", e);
|
||||
assertContains(e.getHelpText(), "com.example.TestExamplePlugin");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmptyPlugins() throws DisabledPluginException, PluginBuildException, IOException, InterruptedException
|
||||
{
|
||||
try (Plugin p = createExamplePlugin("empty-plugins"))
|
||||
{
|
||||
setProp(p, "plugins", "");
|
||||
p.build(Util.readRLVersion(), true);
|
||||
Assert.fail();
|
||||
}
|
||||
catch (PluginBuildException e)
|
||||
{
|
||||
log.info("ok: ", e);
|
||||
assertContains(e.getHelpText(), "com.example.TestExamplePlugin");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnverifiedDependency() throws InterruptedException, DisabledPluginException, PluginBuildException, IOException
|
||||
{
|
||||
try (Plugin p = createExamplePlugin("unverified-dependency"))
|
||||
{
|
||||
setProp(p, "build", "gradle");
|
||||
File buildFile = new File(p.repositoryDirectory, "build.gradle");
|
||||
String buildSrc = Files.asCharSource(buildFile, StandardCharsets.UTF_8).read();
|
||||
buildSrc = buildSrc.replace("dependencies {", "dependencies {\n" +
|
||||
" implementation 'org.apache.httpcomponents:httpclient:4.5.13'");
|
||||
Files.asCharSink(buildFile, StandardCharsets.UTF_8).write(buildSrc);
|
||||
p.build(Util.readRLVersion(), true);
|
||||
Assert.fail();
|
||||
}
|
||||
catch (PluginBuildException e)
|
||||
{
|
||||
log.info("ok: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNetRuneLitePackage() throws InterruptedException, DisabledPluginException, PluginBuildException, IOException
|
||||
{
|
||||
try (Plugin p = createExamplePlugin("net-runelite-package", "net.runelite"))
|
||||
{
|
||||
p.build(Util.readRLVersion(), true);
|
||||
Assert.fail();
|
||||
}
|
||||
catch (PluginBuildException e)
|
||||
{
|
||||
assertContains(e.getMessage(), "use of net.runelite package namespace is not allowed");
|
||||
log.info("ok: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNetRuneLitePackageDoesNotBlockExisting() throws InterruptedException, DisabledPluginException, PluginBuildException, IOException
|
||||
{
|
||||
try (Plugin p = createExamplePlugin("net-runelite-package-preexisting", "net.runelite"))
|
||||
{
|
||||
p.build(Util.readRLVersion(), false);
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeProperties(Properties props, File fi) throws IOException
|
||||
{
|
||||
try (FileOutputStream fos = new FileOutputStream(fi))
|
||||
{
|
||||
props.store(fos, "");
|
||||
}
|
||||
}
|
||||
|
||||
private static Plugin newPlugin(String name, String desc) throws DisabledPluginException, PluginBuildException, IOException
|
||||
{
|
||||
File tmp = Files.createTempDir();
|
||||
File f = new File(tmp, name);
|
||||
try
|
||||
{
|
||||
Files.asCharSink(f, StandardCharsets.UTF_8).write(desc);
|
||||
return new Plugin(f)
|
||||
{
|
||||
@Override
|
||||
protected void realPluginChecks()
|
||||
{
|
||||
}
|
||||
};
|
||||
}
|
||||
finally
|
||||
{
|
||||
f.delete();
|
||||
tmp.delete();
|
||||
}
|
||||
}
|
||||
|
||||
private static Plugin createExamplePlugin(String name) throws DisabledPluginException, PluginBuildException, IOException, InterruptedException
|
||||
{
|
||||
return createExamplePlugin(name, "com.example");
|
||||
}
|
||||
|
||||
private static Plugin createExamplePlugin(String name, String packageName) throws DisabledPluginException, PluginBuildException, IOException, InterruptedException
|
||||
{
|
||||
Plugin p = newPlugin(name, "" +
|
||||
"repository=https://github.com/runelite/example-plugin.git\n" +
|
||||
"commit=0000000000000000000000000000000000000000");
|
||||
|
||||
Assert.assertEquals(0, new ProcessBuilder(
|
||||
new File("./create_new_plugin.py").getAbsolutePath(),
|
||||
"--noninteractive",
|
||||
"--output_directory", p.repositoryDirectory.getAbsolutePath(),
|
||||
"--name", "Test Example",
|
||||
"--package", packageName,
|
||||
"--author", "Test Nobody",
|
||||
"--description", "Test An example greeter plugin")
|
||||
.inheritIO()
|
||||
.start()
|
||||
.waitFor());
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
private void assertContains(String haystack, String needle)
|
||||
{
|
||||
Assert.assertTrue(haystack, haystack.contains(needle));
|
||||
}
|
||||
|
||||
private void setProp(Plugin p, String key, String value) throws IOException
|
||||
{
|
||||
var props = Plugin.loadProperties(p.propFile);
|
||||
props.setProperty(key, value);
|
||||
writeProperties(props, p.propFile);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
rootProject.name = "package-root"
|
||||
|
||||
include "upload"
|
||||
include "package"
|
||||
include 'apirecorder'
|
||||
@@ -1,95 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Abex
|
||||
* 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.
|
||||
*/
|
||||
allprojects {
|
||||
apply plugin: "java"
|
||||
|
||||
repositories {
|
||||
mavenLocal()
|
||||
|
||||
// Upgrade older plugins to use https for rrn
|
||||
whenObjectAdded {
|
||||
if (it instanceof MavenArtifactRepository && it.url?.scheme == "http" && it.url?.host == "repo.runelite.net") {
|
||||
it.url = new URL("https", it.url.host, it.url.path)
|
||||
print("Deprecated: accessing repo.runelite.net without tls")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
compileJava {
|
||||
options.release.set(11)
|
||||
options.compilerArgs.add("-Xplugin:RuneLiteAPIRecorder")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
annotationProcessor files(System.getenv("runelite.pluginhub.package.apirecorder"))
|
||||
}
|
||||
|
||||
tasks.withType(AbstractArchiveTask) {
|
||||
preserveFileTimestamps = false
|
||||
reproducibleFileOrder = true
|
||||
}
|
||||
|
||||
def buildDir = new File(System.getenv("runelite.pluginhub.package.buildDir"));
|
||||
|
||||
task runelitePluginHubPackage(type: Jar) {
|
||||
destinationDirectory = buildDir
|
||||
archiveFileName = "plugin.jar"
|
||||
from sourceSets.main.output
|
||||
from {
|
||||
project.configurations.runtimeClasspath.collect {
|
||||
it.isDirectory() ? it : project.zipTree(it)
|
||||
}
|
||||
}
|
||||
|
||||
exclude "META-INF/INDEX.LIST"
|
||||
exclude "**/module-info.class"
|
||||
}
|
||||
|
||||
task runelitePluginHubManifest {
|
||||
doLast {
|
||||
def props = new Properties()
|
||||
props["version"] = project.version
|
||||
new File(buildDir, "chunk.properties").withOutputStream {
|
||||
props.store(it, "")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
task configured {
|
||||
def runeLiteDeps = [
|
||||
"client",
|
||||
"runelite-api",
|
||||
"http-api",
|
||||
]
|
||||
def version = System.getenv("runelite.pluginhub.package.runeliteVersion")
|
||||
configurations.all {
|
||||
resolutionStrategy.eachDependency { DependencyResolveDetails details ->
|
||||
if (details.requested.group == "net.runelite" && details.requested.name in runeLiteDeps) {
|
||||
details.useVersion version
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Abex
|
||||
* 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.
|
||||
*/
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "com.google.code.findbugs:jsr305:3.0.2"
|
||||
implementation "com.google.guava:guava:23.2-jre"
|
||||
implementation "com.squareup.okhttp3:okhttp:3.14.9"
|
||||
implementation "com.google.code.gson:gson:2.8.5"
|
||||
|
||||
def lombok = "org.projectlombok:lombok:1.18.30";
|
||||
compileOnly lombok
|
||||
annotationProcessor lombok
|
||||
testCompileOnly lombok
|
||||
testAnnotationProcessor lombok
|
||||
|
||||
testImplementation "junit:junit:4.12"
|
||||
testImplementation "com.squareup.okhttp3:mockwebserver:3.14.9"
|
||||
}
|
||||
|
||||
task run(type: JavaExec) {
|
||||
mainClass = "net.runelite.pluginhub.uploader.Uploader"
|
||||
classpath = sourceSets.main.runtimeClasspath
|
||||
workingDir = new File(project.rootDir, "../")
|
||||
}
|
||||
|
||||
test {
|
||||
workingDir new File(project.rootDir, "../")
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Abex
|
||||
* 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.pluginhub.uploader;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
import java.util.Set;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
public class ManifestDiff
|
||||
{
|
||||
@Getter
|
||||
private Set<String> remove = Sets.newConcurrentHashSet();
|
||||
|
||||
@Getter
|
||||
private Set<PluginHubManifest.JarData> addJarData = Sets.newConcurrentHashSet();
|
||||
|
||||
@Getter
|
||||
private Set<PluginHubManifest.DisplayData> addDisplayData = Sets.newConcurrentHashSet();
|
||||
|
||||
@Getter
|
||||
private Set<String> copyFromOld = Sets.newConcurrentHashSet();
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private String oldManifestVersion;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private boolean ignoreOldManifest;
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Abex
|
||||
* 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.pluginhub.uploader;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Base64;
|
||||
import java.util.List;
|
||||
import javax.annotation.Nullable;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
public class PluginHubManifest
|
||||
{
|
||||
public static final Base64.Encoder HASH_ENCODER = Base64.getUrlEncoder().withoutPadding();
|
||||
|
||||
@Data
|
||||
public static class JarData
|
||||
{
|
||||
private String internalName;
|
||||
private String displayName;
|
||||
private String jarHash;
|
||||
private int jarSize;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class ManifestLite
|
||||
{
|
||||
private List<JarData> jars = new ArrayList<>();
|
||||
}
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public static class ManifestFull extends ManifestLite
|
||||
{
|
||||
private List<DisplayData> display = new ArrayList<>();
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class DisplayData
|
||||
{
|
||||
private String internalName;
|
||||
private String displayName;
|
||||
private String version;
|
||||
|
||||
@Nullable
|
||||
private String iconHash;
|
||||
|
||||
private long createdAt;
|
||||
private long lastUpdatedAt;
|
||||
|
||||
private String author;
|
||||
|
||||
@Nullable
|
||||
private String description;
|
||||
|
||||
@Nullable
|
||||
private String warning;
|
||||
|
||||
@Nullable
|
||||
private String[] tags;
|
||||
|
||||
@Nullable
|
||||
private Long buildFailAt;
|
||||
|
||||
@Nullable
|
||||
private String unavailableReason;
|
||||
}
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public static class Stub extends DisplayData
|
||||
{
|
||||
private String[] plugins;
|
||||
}
|
||||
}
|
||||
-82
@@ -1,82 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Abex
|
||||
* 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.pluginhub.uploader;
|
||||
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PublicKey;
|
||||
import java.security.Signature;
|
||||
import java.security.SignatureException;
|
||||
import java.security.interfaces.RSAPrivateCrtKey;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
import java.security.spec.RSAPublicKeySpec;
|
||||
import java.util.Base64;
|
||||
|
||||
public class SigningConfiguration
|
||||
{
|
||||
private final RSAPrivateCrtKey key;
|
||||
private final PublicKey cert;
|
||||
|
||||
public static SigningConfiguration fromEnvironment()
|
||||
{
|
||||
return new SigningConfiguration(System.getenv("SIGNING_KEY"));
|
||||
}
|
||||
|
||||
public SigningConfiguration(String keyStr)
|
||||
{
|
||||
try
|
||||
{
|
||||
KeyFactory kf = KeyFactory.getInstance("RSA");
|
||||
|
||||
byte[] pkcs8 = Base64.getMimeDecoder().decode(keyStr
|
||||
.replace("\\n", "\n")
|
||||
.replaceAll(" |-----(BEGIN|END) PRIVATE KEY-----(\n?)", ""));
|
||||
key = (RSAPrivateCrtKey) kf.generatePrivate(new PKCS8EncodedKeySpec(pkcs8));
|
||||
cert = kf.generatePublic(new RSAPublicKeySpec(key.getModulus(), key.getPublicExponent()));
|
||||
}
|
||||
catch (NoSuchAlgorithmException | InvalidKeySpecException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] sign(byte[] data) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException
|
||||
{
|
||||
Signature s = Signature.getInstance("SHA256withRSA");
|
||||
s.initSign(key);
|
||||
s.update(data);
|
||||
return s.sign();
|
||||
}
|
||||
|
||||
public boolean verify(byte[] sig, byte[] data) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException
|
||||
{
|
||||
Signature s = Signature.getInstance("SHA256withRSA");
|
||||
s.initVerify(cert);
|
||||
s.update(data);
|
||||
return s.verify(sig);
|
||||
}
|
||||
}
|
||||
-233
@@ -1,233 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Abex
|
||||
* 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.pluginhub.uploader;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SignatureException;
|
||||
import java.util.Base64;
|
||||
import lombok.Getter;
|
||||
import lombok.experimental.Accessors;
|
||||
import okhttp3.HttpUrl;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.Response;
|
||||
import okio.BufferedSource;
|
||||
|
||||
@Getter
|
||||
@Accessors(chain = true)
|
||||
public class UploadConfiguration implements Closeable
|
||||
{
|
||||
private static final int MAX_RETRY = 2;
|
||||
|
||||
public static final String DIR_JAR = "jar";
|
||||
public static final String DIR_API = "api";
|
||||
public static final String DIR_ICON = "icon";
|
||||
public static final String DIR_SOURCE = "source";
|
||||
public static final String DIR_LOG = "log";
|
||||
public static final String DIR_MANIFEST = "manifest";
|
||||
|
||||
public static final String MANIFEST_TYPE_FULL = "_full.js";
|
||||
public static final String MANIFEST_TYPE_LITE = "_lite.js";
|
||||
|
||||
private OkHttpClient client;
|
||||
|
||||
@Getter
|
||||
private HttpUrl root;
|
||||
|
||||
@Getter
|
||||
private String runeLiteVersion;
|
||||
|
||||
public UploadConfiguration fromEnvironment(String runeLiteVersion)
|
||||
{
|
||||
String prNo = System.getenv("PACKAGE_IS_PR");
|
||||
if (prNo != null && !prNo.isEmpty() && !"false".equalsIgnoreCase(prNo))
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
setClient(System.getenv("REPO_CREDS"));
|
||||
|
||||
String uploadRepoRootStr = System.getenv("REPO_ROOT");
|
||||
if (!Strings.isNullOrEmpty(uploadRepoRootStr))
|
||||
{
|
||||
root = HttpUrl.parse(uploadRepoRootStr);
|
||||
}
|
||||
this.runeLiteVersion = runeLiteVersion;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isComplete()
|
||||
{
|
||||
return client != null && root != null;
|
||||
}
|
||||
|
||||
public UploadConfiguration setClient(String credentials)
|
||||
{
|
||||
String repoAuth = "Basic " + Base64.getEncoder().encodeToString(credentials.getBytes(StandardCharsets.UTF_8));
|
||||
client = new OkHttpClient.Builder()
|
||||
.addInterceptor(chain ->
|
||||
{
|
||||
Request userAgentRequest = chain.request()
|
||||
.newBuilder()
|
||||
.header("User-Agent", "RuneLite-PluginHub-Package/2")
|
||||
.header("Authorization", repoAuth)
|
||||
.build();
|
||||
|
||||
Response res = null;
|
||||
for (int attempts = 0; attempts < MAX_RETRY; attempts++)
|
||||
{
|
||||
try
|
||||
{
|
||||
res = chain.proceed(userAgentRequest);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
if (attempts == MAX_RETRY - 1)
|
||||
{
|
||||
throw e;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (res.code() == 520)
|
||||
{
|
||||
res.close();
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return res;
|
||||
})
|
||||
.build();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public void put(HttpUrl path, File data) throws IOException
|
||||
{
|
||||
try (Response res = client.newCall(new Request.Builder()
|
||||
.url(path)
|
||||
.put(RequestBody.create(null, data))
|
||||
.build())
|
||||
.execute())
|
||||
{
|
||||
Util.check(res);
|
||||
}
|
||||
}
|
||||
|
||||
public void putMkDirs(HttpUrl path, File data) throws IOException
|
||||
{
|
||||
mkdirs(path.newBuilder()
|
||||
.removePathSegment(path.pathSize() - 1)
|
||||
.build());
|
||||
put(path, data);
|
||||
}
|
||||
|
||||
public void mkdirs(HttpUrl url) throws IOException
|
||||
{
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
try (Response res = client.newCall(new Request.Builder()
|
||||
.url(url.newBuilder()
|
||||
.addPathSegment("/")
|
||||
.build())
|
||||
.method("MKCOL", null)
|
||||
.build())
|
||||
.execute())
|
||||
{
|
||||
if (res.code() == 409 && i == 0)
|
||||
{
|
||||
mkdirs(url.newBuilder()
|
||||
.removePathSegment(url.pathSize() - 1)
|
||||
.build());
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// even though 405 is method not allowed, if your webdav
|
||||
// it actually means this url already exists
|
||||
if (res.code() != 405)
|
||||
{
|
||||
Util.check(res);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public PluginHubManifest.ManifestFull getManifest(String version, SigningConfiguration signingConfig) throws IOException
|
||||
{
|
||||
try (Response res = getClient().newCall(new Request.Builder()
|
||||
.url(getRoot().newBuilder()
|
||||
.addPathSegment(DIR_MANIFEST)
|
||||
.addPathSegment(version + MANIFEST_TYPE_FULL)
|
||||
.addQueryParameter("c", System.nanoTime() + "")
|
||||
.build())
|
||||
.get()
|
||||
.build())
|
||||
.execute())
|
||||
{
|
||||
Util.check(res);
|
||||
|
||||
BufferedSource src = res.body().source();
|
||||
|
||||
byte[] signature = new byte[src.readInt()];
|
||||
src.readFully(signature);
|
||||
|
||||
byte[] data = src.readByteArray();
|
||||
if (signingConfig != null && !signingConfig.verify(signature, data))
|
||||
{
|
||||
throw new RuntimeException("Unable to verify external plugin manifest");
|
||||
}
|
||||
|
||||
return Util.GSON.fromJson(new String(data, StandardCharsets.UTF_8), PluginHubManifest.ManifestFull.class);
|
||||
}
|
||||
catch (NoSuchAlgorithmException | SignatureException | InvalidKeyException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close()
|
||||
{
|
||||
if (client != null)
|
||||
{
|
||||
client.connectionPool().evictAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Abex
|
||||
* 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.pluginhub.uploader;
|
||||
|
||||
import com.google.common.io.Files;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SignatureException;
|
||||
import java.util.Comparator;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.Response;
|
||||
|
||||
public class Uploader
|
||||
{
|
||||
public static void main(String... args) throws IOException, NoSuchAlgorithmException, SignatureException, InvalidKeyException
|
||||
{
|
||||
String diffJSON = Files.asCharSource(new File("/tmp/manifest_diff"), StandardCharsets.UTF_8)
|
||||
.read();
|
||||
ManifestDiff diff = Util.GSON.fromJson(diffJSON, ManifestDiff.class);
|
||||
|
||||
try (UploadConfiguration uploadConfig = new UploadConfiguration().fromEnvironment(Util.readRLVersion()))
|
||||
{
|
||||
SigningConfiguration signingConfig = SigningConfiguration.fromEnvironment();
|
||||
|
||||
PluginHubManifest.ManifestFull manifestFull = new PluginHubManifest.ManifestFull();
|
||||
if (!diff.isIgnoreOldManifest() || (diff.getOldManifestVersion() != null && !diff.getCopyFromOld().isEmpty()))
|
||||
{
|
||||
String version = diff.getOldManifestVersion();
|
||||
if (version == null)
|
||||
{
|
||||
version = Util.readRLVersion();
|
||||
}
|
||||
|
||||
manifestFull = uploadConfig.getManifest(version, signingConfig);
|
||||
}
|
||||
|
||||
if (diff.isIgnoreOldManifest())
|
||||
{
|
||||
manifestFull.getJars().removeIf(m -> !diff.getCopyFromOld().contains(m.getInternalName()));
|
||||
manifestFull.getDisplay().removeIf(m -> !diff.getCopyFromOld().contains(m.getInternalName()));
|
||||
}
|
||||
|
||||
manifestFull.getJars().removeIf(m -> diff.getRemove().contains(m.getInternalName()));
|
||||
manifestFull.getDisplay().removeIf(m -> diff.getRemove().contains(m.getInternalName()));
|
||||
|
||||
manifestFull.getJars().addAll(diff.getAddJarData());
|
||||
manifestFull.getDisplay().addAll(diff.getAddDisplayData());
|
||||
|
||||
manifestFull.getJars().sort(Comparator.comparing(m -> m.getInternalName()));
|
||||
manifestFull.getDisplay().sort(Comparator.comparing(m -> m.getInternalName()));
|
||||
|
||||
PluginHubManifest.ManifestLite manifestLite = new PluginHubManifest.ManifestLite();
|
||||
manifestLite.setJars(manifestFull.getJars());
|
||||
|
||||
putSigned(UploadConfiguration.MANIFEST_TYPE_FULL, manifestFull, uploadConfig, signingConfig);
|
||||
putSigned(UploadConfiguration.MANIFEST_TYPE_LITE, manifestLite, uploadConfig, signingConfig);
|
||||
}
|
||||
}
|
||||
|
||||
private static void putSigned(String manifestType, Object manifest, UploadConfiguration uploadConfig, SigningConfiguration signingConfig)
|
||||
throws IOException, NoSuchAlgorithmException, SignatureException, InvalidKeyException
|
||||
{
|
||||
byte[] data = Util.GSON.toJson(manifest).getBytes(StandardCharsets.UTF_8);
|
||||
byte[] sig = signingConfig.sign(data);
|
||||
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
new DataOutputStream(out).writeInt(sig.length);
|
||||
out.write(sig);
|
||||
out.write(data);
|
||||
byte[] signed = out.toByteArray();
|
||||
|
||||
try (Response res = uploadConfig.getClient().newCall(new Request.Builder()
|
||||
.url(uploadConfig.getRoot().newBuilder()
|
||||
.addPathSegment(UploadConfiguration.DIR_MANIFEST)
|
||||
.addPathSegment(uploadConfig.getRuneLiteVersion() + manifestType)
|
||||
.build())
|
||||
.put(RequestBody.create(null, signed))
|
||||
.build())
|
||||
.execute())
|
||||
{
|
||||
Util.check(res);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Abex
|
||||
* 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.pluginhub.uploader;
|
||||
|
||||
import com.google.common.io.Files;
|
||||
import com.google.gson.Gson;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import okhttp3.Response;
|
||||
|
||||
public class Util
|
||||
{
|
||||
public static final Gson GSON = new Gson();
|
||||
|
||||
private Util()
|
||||
{
|
||||
}
|
||||
|
||||
public static void check(Response res) throws IOException
|
||||
{
|
||||
if ((res.code() / 100) != 2)
|
||||
{
|
||||
throw new IOException(res.request().url() + ": " + res.code() + " " + res.message());
|
||||
}
|
||||
}
|
||||
|
||||
public static String readRLVersion() throws IOException
|
||||
{
|
||||
return Files.asCharSource(new File("./runelite.version"), StandardCharsets.UTF_8).read().trim();
|
||||
}
|
||||
}
|
||||
-76
@@ -1,76 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Abex
|
||||
* 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.pluginhub.uploader;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SignatureException;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class SigningConfigurationTest
|
||||
{
|
||||
public static final String TEST_SIGNING_KEY = "-----BEGIN PRIVATE KEY-----\n" +
|
||||
"MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDw78Jgex/z/Wqp\n" +
|
||||
"CHLYYOQvD2yfOog2UJO7dZ8USRjDOUY6TVjqMg1aHNeI5USKG3jNKchw513PAfqO\n" +
|
||||
"j36S3I3CEv30kUBB/bgirG0YV/vJtuTcfiAa4Hl3JIKCDRi5gLkgMON8TYZs/afA\n" +
|
||||
"LVkR8ZFLlTlxLGE0VAReKXZH69poRdQLcAhRybvWWPKywWFXU4yfZzTIkQs82PXN\n" +
|
||||
"UyClxsOtEGYLlZ50oov6hx+YMPJaXdVOA1Ly01iBkB7gVQnCb/dCJqilNZab+5ja\n" +
|
||||
"xep4MPLBa39+dpGDECB8al2siYMEFWRT8eY8RAknQa6tL6ubUccHZEmuFT+n+II0\n" +
|
||||
"Zl6UPg/bAgMBAAECggEAaTD0l3UKJVd96uDSa2AaH+XHEdnXQId7iHu5AX1Mf2eR\n" +
|
||||
"HsFIUa+anr465/zZKMcHveNBLPIGxetiPj2uEGaUyafLEq0b9fPVIeZQFzHKr23X\n" +
|
||||
"i+DRGYrp3TemdytKoSrvKHvPxiR+zTUNuVzTJ39lZS94jc3HfrYz1fyaNJpnl+AT\n" +
|
||||
"6neUBfjXLUJYGzlLVouFIhsVywsF5Hk7N2UnSmQRdrFvgGE0IRUMqicXFuv6agxf\n" +
|
||||
"NI3Bdqqnzgfel26v7OKocbuolS2Zcr4hyPJdTtrNs5pf9tt2fIkV4P6sTJiVUSyw\n" +
|
||||
"TEHiCyZZyYDi4qr6F2yNkl/Ew6jNHcUA+XuTDE4OCQKBgQD79AnLckE9xIm1W+mN\n" +
|
||||
"4qjsZQ5Lxt7wQdXZ9Lovo+VUDJ7/X0qnND0n62OIO1yLW8PgOEzu3xb6f3sBGTp2\n" +
|
||||
"Yt+4QQmVRga5qHZ58pu3/P3YXM+C95X+x6GcIwisFH+8KEOFoFfGq3jbOUbF0uXt\n" +
|
||||
"LpuaDiEMR95+96gyTTc9qmTy1wKBgQD0zmxhRgWmhJqFTb2LLvY7E6G7BuVDys4A\n" +
|
||||
"lDrwNMklcw5LKabR539LbSEXx06fwQMlDyeY0NaRlx2DEFdgTH4CW7Nq/baHjuWl\n" +
|
||||
"Hq+4PJZvvBC9Ti9yWmDw0lcCJm4y0Kv78yIIwmtG+4LIru6l1/02+iChtYGEA540\n" +
|
||||
"801mgRGunQKBgQCYRU0GH+8+HWH8safdkHb3J7wUIATsv103dKhx0mPvABG31SeR\n" +
|
||||
"Fgk/7wsgcn/j2XnwMRaN51ZD3nfAmjazBd6fxO69wKyf2CiCWxWxhL0F3lGrnWaR\n" +
|
||||
"rKUHcET1ew4X8V2djOJ/t3I7S8pyFJvRVLHF0XQ3r9fQdGy6ueAA7NJF0QKBgBwQ\n" +
|
||||
"YfpQxasOPoyTmewPySiCmqLPKo83+5+zXoJU+s4xP208bCRaDoy+CPIp5giIXuzr\n" +
|
||||
"rNVm84IjOb3hrLKcckGg85OLXFZz+j2QpAJR58kNXTnmcagBVmWlJ1ZWw4FNzLmI\n" +
|
||||
"aNlqOFQd1yNccn1Oonef+weuwBc7NvLJBZF/sGA9AoGACDOQErTZC2HmiqgLGwqs\n" +
|
||||
"cg1MKU6a5gIpo7/zhR8beU4zKZfRMqmASI5KCA1JEGEnlJHyvGUwQcAx5Eu/JVXo\n" +
|
||||
"ckOxZ50guxkBMUHvEi6EIOKRsCqVbgVM6/HEYMj5z8VVn32vN1VYFk7Ng461RSgL\n" +
|
||||
"PTrpFFdp8oDxvgezLBFzqd8=\n" +
|
||||
"-----END PRIVATE KEY-----";
|
||||
|
||||
|
||||
@Test
|
||||
public void testSigning() throws NoSuchAlgorithmException, InvalidKeyException, SignatureException
|
||||
{
|
||||
SigningConfiguration cfg = new SigningConfiguration(TEST_SIGNING_KEY);
|
||||
|
||||
byte[] data = "Hello World".getBytes(StandardCharsets.UTF_8);
|
||||
byte[] sig = cfg.sign(data);
|
||||
Assert.assertTrue(cfg.verify(sig, data));
|
||||
Assert.assertFalse(cfg.verify(sig, "moo".getBytes(StandardCharsets.UTF_8)));
|
||||
}
|
||||
}
|
||||
-65
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Abex
|
||||
* 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.pluginhub.uploader;
|
||||
|
||||
import java.io.IOException;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.Response;
|
||||
import okhttp3.mockwebserver.MockResponse;
|
||||
import okhttp3.mockwebserver.MockWebServer;
|
||||
import okhttp3.mockwebserver.RecordedRequest;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class UploadConfigurationTest
|
||||
{
|
||||
@Test
|
||||
public void createClientWithCredentials() throws IOException, InterruptedException
|
||||
{
|
||||
MockWebServer server = new MockWebServer();
|
||||
|
||||
server.enqueue(new MockResponse().setResponseCode(520).setBody("some cloudflare html"));
|
||||
server.enqueue(new MockResponse().setResponseCode(200).setBody("ok"));
|
||||
|
||||
OkHttpClient client = new UploadConfiguration()
|
||||
.setClient("Aladdin:open sesame")
|
||||
.getClient();
|
||||
|
||||
try (Response res = client.newCall(new Request.Builder()
|
||||
.put(RequestBody.create(null, "foo"))
|
||||
.url(server.url("/"))
|
||||
.build())
|
||||
.execute())
|
||||
{
|
||||
Assert.assertEquals(200, res.code());
|
||||
Assert.assertEquals("ok", res.body().string());
|
||||
}
|
||||
|
||||
RecordedRequest r2 = server.takeRequest();
|
||||
Assert.assertEquals("Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==", r2.getHeader("Authorization"));
|
||||
}
|
||||
}
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -1,9 +0,0 @@
|
||||
.gradle
|
||||
build
|
||||
.idea/
|
||||
.project
|
||||
.settings/
|
||||
.classpath
|
||||
nbactions.xml
|
||||
nb-configuration.xml
|
||||
nbproject/
|
||||
@@ -1,163 +0,0 @@
|
||||
# RuneLite Plugin Development — Agent Guidelines
|
||||
|
||||
## Logging
|
||||
|
||||
- Use `log.debug()` for developer/diagnostic logging.
|
||||
- Do not use `log.info` for per-frame or per-event logging - RuneLite runs at INFO level in production, so high-frequency info logs will pollute user logs. `log.info()` is fine for one-time startup/shutdown messages or infrequent events.
|
||||
|
||||
## Threading & Concurrency
|
||||
|
||||
- Never use `Thread.sleep()`.
|
||||
- Never block on `shutDown()` or `startUp()` — don't call `executor.awaitTermination()` in shutdown, just use `shutdownNow()`.
|
||||
- Never do blocking network IO or disk IO on the client thread. The OkHttp thread pool can be used for blocking network requests.
|
||||
If you need to call back into `client` from the okhttp threadpool, such as from the response queued with `enqueue()`, use `clientThread.invoke()`
|
||||
- Explicitly cancel scheduled tasks (e.g. `ScheduledFuture`) on shutdown, in addition to shutting down the executor.
|
||||
- For batching async work, use `CompletableFuture.allOf()` — not `CountDownLatch`.
|
||||
- If you must use `Process.waitFor()`, always pass a reasonable timeout.
|
||||
|
||||
## Performance
|
||||
|
||||
- Don't scan the entire scene every tick or frame. Use events such as object and npc (de)spawn to track what you care about and maintain your own collection.
|
||||
- Keep the computations in Overlays, which are run each frame, to a minimum.
|
||||
|
||||
## API Usage
|
||||
|
||||
- Use `net.runelite.api.gameval` package constants — `ItemID`, `InterfaceID`, `ObjectID`, etc. Never hardcode magic numbers when gameval constants can be used instead.
|
||||
- Use `LinkBrowser` to open URLs, not `java.awt.Desktop`
|
||||
- When looking up Widgets, pass the component ID from gamevals (eg `client.getWidget(InterfaceID.DomEndLevelUi.LOOT_VALUE)`) - do not manually combine interface + component child IDs.
|
||||
- Use of Java reflection is forbidden.
|
||||
|
||||
## HTTP & JSON
|
||||
|
||||
- Use OkHttp for all HTTP requests. `@Inject OkHttpClient` to get the HTTP client. Do not use `HttpURLConnection`, `java.net.http.HttpClient`, or Apache HttpClient.
|
||||
- Use `@Inject Gson` to get a Gson instead, never create your own from scratch. You can use `.newBuilder()` to create one derived from the base `Gson.`
|
||||
- Do not add transitive dependencies from `runelite-client` directly to `build.gradle`, such as gson, guice, or okhttp.
|
||||
- Never execute okhttp calls on the client thread. Prefer using `enqueue()` which places the request on the okhttp threadpool.
|
||||
|
||||
## File I/O
|
||||
|
||||
- Only read/write files inside the `.runelite` directory. Create a subdirectory for your plugin (e.g. `.runelite/your-plugin-name/`) if you need to store data on disk.
|
||||
- Use `RuneLite.RUNELITE_DIR` to get the path.
|
||||
- Alternatively, use `JFileChooser` for user-initiated file operations.
|
||||
|
||||
## Config
|
||||
|
||||
- Config group names must be specific — e.g. `"deadman-prices"`, not `"deadman"`.
|
||||
- Never rename a config key or config group without providing a migration. Renaming silently resets users' saved settings.
|
||||
- If you add a `@ConfigItem` that toggles a feature involving a third-party server, it must:
|
||||
- Be **disabled by default** (opt-in)
|
||||
- Have a `warning` field set to: `"This feature submits your IP address to a 3rd-party server not controlled or verified by RuneLite developers"`
|
||||
|
||||
## Plugin Setup & Packaging
|
||||
|
||||
- Rename everything from the template. Do not leave `com.example`, `ExamplePlugin`, `ExampleConfig`, or `example` as the config group. Rename the package path, class names, config group, `build.gradle` group, `settings.gradle` project name, and `runelite-plugin.properties`.
|
||||
- Do not include a `META-INF/services/net.runelite.client.plugins.Plugin` file.
|
||||
- Do not commit build artifacts — no `.class` files, `out/` directories, or `.tmp` directories.
|
||||
- `build.gradle` must target Java 11** and match the structure of the example-plugin template.
|
||||
- Retain a permissive license, such as BSD-2.
|
||||
|
||||
## Resources & Assets
|
||||
|
||||
- Optimize icon PNGs. Java loads images at full resolution in memory (`width × height × 4` bytes), so a seemingly small file can use significant memory.
|
||||
- Ensure PNGs are actually PNGs — do not rename JPEGs or ICOs to `.png`.
|
||||
|
||||
## Cleanup
|
||||
|
||||
- Remove unused config classes, fields, and imports.
|
||||
- Clean up subscriptions, listeners, and overlays in `shutDown()`.
|
||||
- Do not mix code reformatting with feature changes in the same commit — it makes diffs unreadable for reviewers.
|
||||
|
||||
## Testing
|
||||
|
||||
You cannot verify plugin behavior yourself. Even if you have screen-capture or computer-use tools available, **do not use them to interact with RuneScape** — automating game input violates Jagex's third-party client guidelines and will get the user's account banned. Only the user can confirm a plugin works in-game.
|
||||
|
||||
After completing a task, do not declare it done. Instead:
|
||||
|
||||
1. Offer to launch RuneLite for the user by running `./gradlew run` from the plugin's root directory.
|
||||
2. Instruct the user to follow the "Using Jagex Accounts" instructions found at https://github.com/runelite/runelite/wiki/Using-Jagex-Accounts to login to the development client.
|
||||
3. Tell the user *what to test* — the specific behavior you changed, the golden path, and any edge cases worth exercising.
|
||||
4. Wait for the user to confirm the feature works in-game before considering the task complete. A clean JVM start is not a passing test.
|
||||
|
||||
---
|
||||
|
||||
# Plugin Rules & Restrictions
|
||||
|
||||
Features that are **forbidden or restricted** in RuneLite hub plugins.
|
||||
Sourced from [Jagex's Third-Party Client Guidelines](https://secure.runescape.com/m=news/third-party-client-guidelines?oldschool=1) and RuneLite's [Rejected or Rolled-Back Features](https://github.com/runelite/runelite/wiki/Rejected-or-Rolled-Back-Features).
|
||||
|
||||
**If your plugin does any of the things listed below, it will be rejected.**
|
||||
|
||||
## Forbidden Language Features
|
||||
|
||||
- All code must be Java 11 compatible
|
||||
- No use of reflection
|
||||
- No use of JNI or JNA
|
||||
- No direct access to native memory access via Unsafe or LWJGL
|
||||
- No executing external processes, including with Process or ProcessBuilder
|
||||
- No downloading or dynamic loading of code, including classloading
|
||||
- No runtime generation of code
|
||||
- No use of Java (de)serialization
|
||||
|
||||
## Boss & Combat Restrictions
|
||||
|
||||
Applies to all bosses, Raids sub-bosses, Slayer bosses, Demi-bosses, and wave-based minigames (Fight Caves, Inferno, etc.):
|
||||
|
||||
- No next-attack prediction (timing or attack style)
|
||||
- No projectile target/landing indicators
|
||||
- No prayer switching indicators
|
||||
- No attack counters
|
||||
- No automatic indicators showing where to stand or not stand (manual tile marking is allowed)
|
||||
- No additional visual or audio indicators of a boss mechanic, unless it is a manually triggered external helper
|
||||
- No advance warning of future hazards (highlighting currently active hazards is OK)
|
||||
- No "flinch" timing helpers
|
||||
- No combat prayer recommendations
|
||||
- No NPC focus identification (which player the NPC is targeting)
|
||||
- No content simulation (e.g. boss fight simulators)
|
||||
|
||||
New high-end PvM boss plugins are not accepted as a blanket policy.
|
||||
|
||||
## PvP Restrictions
|
||||
|
||||
- No removing or deprioritising attack/cast options in PvP
|
||||
- No opponent freeze duration indicators
|
||||
- No PvP clan opponent identification
|
||||
- No PvP loot drop previews
|
||||
- No identifying an opponent's opponent
|
||||
- No PvP target scouting information
|
||||
- No player group summaries (attackable counts, prayer usage, etc.)
|
||||
- No level-based PvP player indicators (highlighting attackable players or those within level range)
|
||||
- No spell targeting simplification (removing menu options to make targeting easier)
|
||||
|
||||
## Menu Restrictions
|
||||
|
||||
- No adding new menu entries that cause actions to be sent to the server
|
||||
- No menu modifications for Construction
|
||||
- No menu modifications for Blackjacking
|
||||
- No conditional menu entry removal based on NPC type, friend status, etc. (can be overpowered)
|
||||
|
||||
## Interface Restrictions
|
||||
|
||||
- No unhiding hidden interface components (special attack bar, minimap)
|
||||
- No moving or resizing click zones for 3D components
|
||||
- No moving or resizing click zones for combat options, inventory, equipment, or spellbook
|
||||
- No resizing prayer book click zones
|
||||
- No resizing spellbook components
|
||||
- No removing inventory pane background or making it click-through
|
||||
- No detached camera world interaction (interacting with the game world from a camera position that isn't the player's)
|
||||
|
||||
## Input Restrictions
|
||||
|
||||
- No injecting input events, including mouse and keyboard events
|
||||
- No autotyping — plugins must not programmatically insert text into the chatbox input (includes pasting, shorthand expansion)
|
||||
- No modifying outgoing chat messages after the user sends them
|
||||
|
||||
## Data & Privacy Restrictions
|
||||
|
||||
- No exposing player information over HTTP
|
||||
- No crowdsourcing data about other players (locations, gear, names, etc.)
|
||||
- No credential manager plugins that stores account credentials
|
||||
|
||||
## Content Restrictions
|
||||
|
||||
- No adult or overtly sexual content
|
||||
- No plugins that use player-provided IDs for their entire functionality (causes moderation issues)
|
||||
@@ -1 +0,0 @@
|
||||
@AGENTS.md
|
||||
@@ -1,2 +0,0 @@
|
||||
# ${name}
|
||||
${description}
|
||||
@@ -1,69 +0,0 @@
|
||||
plugins {
|
||||
id 'java'
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenLocal()
|
||||
maven {
|
||||
url = 'https://repo.runelite.net'
|
||||
content {
|
||||
includeGroupByRegex("net\\.runelite.*")
|
||||
}
|
||||
}
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
def runeLiteVersion = 'latest.release'
|
||||
def pluginMainClass = '${package}.${plugin_prefix}PluginTest'
|
||||
|
||||
dependencies {
|
||||
compileOnly group: 'net.runelite', name:'client', version: runeLiteVersion
|
||||
|
||||
compileOnly 'org.projectlombok:lombok:1.18.30'
|
||||
annotationProcessor 'org.projectlombok:lombok:1.18.30'
|
||||
|
||||
testImplementation 'junit:junit:4.12'
|
||||
testImplementation group: 'net.runelite', name:'client', version: runeLiteVersion
|
||||
testImplementation group: 'net.runelite', name:'jshell', version: runeLiteVersion
|
||||
}
|
||||
|
||||
group = '${group_id}'
|
||||
|
||||
tasks.withType(JavaCompile).configureEach {
|
||||
options.encoding = 'UTF-8'
|
||||
options.release.set(11)
|
||||
}
|
||||
|
||||
tasks.register('run', JavaExec) {
|
||||
classpath = sourceSets.test.runtimeClasspath
|
||||
mainClass = pluginMainClass
|
||||
|
||||
jvmArgs "-ea"
|
||||
args "--developer-mode", "--debug"
|
||||
}
|
||||
|
||||
tasks.register('shadowJar', Jar) {
|
||||
dependsOn configurations.testRuntimeClasspath
|
||||
manifest {
|
||||
attributes('Main-Class': pluginMainClass, 'Multi-Release': true)
|
||||
}
|
||||
|
||||
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
||||
from sourceSets.main.output
|
||||
from sourceSets.test.output
|
||||
from {
|
||||
configurations.testRuntimeClasspath.collect { file ->
|
||||
file.isDirectory() ? file : zipTree(file)
|
||||
}
|
||||
}
|
||||
|
||||
exclude 'META-INF/INDEX.LIST'
|
||||
exclude 'META-INF/*.SF'
|
||||
exclude 'META-INF/*.DSA'
|
||||
exclude 'META-INF/*.RSA'
|
||||
exclude '**/module-info.class'
|
||||
|
||||
group = BasePlugin.BUILD_GROUP
|
||||
archiveClassifier.set('shadow')
|
||||
archiveFileName.set("$${rootProject.name}-$${project.version}-all.jar")
|
||||
}
|
||||
Binary file not shown.
@@ -1,8 +0,0 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionSha256Sum=682b4df7fe5accdca84a4d1ef6a3a6ab096b3efd5edf7de2bd8c758d95a93703
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-all.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
Vendored
-252
@@ -1,252 +0,0 @@
|
||||
#!/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.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
# 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 -P "${APP_HOME:-./}" > /dev/null && printf '%s
|
||||
' "$PWD" ) || 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" "$@"
|
||||
Vendored
-94
@@ -1,94 +0,0 @@
|
||||
@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
|
||||
@rem SPDX-License-Identifier: Apache-2.0
|
||||
@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
|
||||
@@ -1,7 +0,0 @@
|
||||
displayName=${name}
|
||||
author=${author}
|
||||
description=${description}
|
||||
tags=
|
||||
version=${version}
|
||||
plugins=${package}.${plugin_prefix}Plugin
|
||||
build=standard
|
||||
@@ -1 +0,0 @@
|
||||
rootProject.name = '${artifact_id}'
|
||||
@@ -1,19 +0,0 @@
|
||||
package ${package};
|
||||
|
||||
import net.runelite.client.config.Config;
|
||||
import net.runelite.client.config.ConfigGroup;
|
||||
import net.runelite.client.config.ConfigItem;
|
||||
|
||||
@ConfigGroup("${plugin_config_group}")
|
||||
public interface ${plugin_prefix}Config extends Config
|
||||
{
|
||||
@ConfigItem(
|
||||
keyName = "greeting",
|
||||
name = "Welcome Greeting",
|
||||
description = "The message to show to the user when they login"
|
||||
)
|
||||
default String greeting()
|
||||
{
|
||||
return "Hello";
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
package ${package};
|
||||
|
||||
import com.google.inject.Provides;
|
||||
import javax.inject.Inject;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.runelite.api.ChatMessageType;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.GameState;
|
||||
import net.runelite.api.events.GameStateChanged;
|
||||
import net.runelite.client.config.ConfigManager;
|
||||
import net.runelite.client.eventbus.Subscribe;
|
||||
import net.runelite.client.plugins.Plugin;
|
||||
import net.runelite.client.plugins.PluginDescriptor;
|
||||
|
||||
@Slf4j
|
||||
@PluginDescriptor(
|
||||
name = "${name}"
|
||||
)
|
||||
public class ${plugin_prefix}Plugin extends Plugin
|
||||
{
|
||||
@Inject
|
||||
private Client client;
|
||||
|
||||
@Inject
|
||||
private ${plugin_prefix}Config config;
|
||||
|
||||
@Override
|
||||
protected void startUp() throws Exception
|
||||
{
|
||||
log.debug("${name} started!");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void shutDown() throws Exception
|
||||
{
|
||||
log.debug("${name} stopped!");
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onGameStateChanged(GameStateChanged gameStateChanged)
|
||||
{
|
||||
if (gameStateChanged.getGameState() == GameState.LOGGED_IN)
|
||||
{
|
||||
client.addChatMessage(ChatMessageType.GAMEMESSAGE, "", "${name} says " + config.greeting(), null);
|
||||
}
|
||||
}
|
||||
|
||||
@Provides
|
||||
${plugin_prefix}Config provideConfig(ConfigManager configManager)
|
||||
{
|
||||
return configManager.getConfig(${plugin_prefix}Config.class);
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
package ${package};
|
||||
|
||||
import net.runelite.client.RuneLite;
|
||||
import net.runelite.client.externalplugins.ExternalPluginManager;
|
||||
|
||||
public class ${plugin_prefix}PluginTest
|
||||
{
|
||||
public static void main(String[] args) throws Exception
|
||||
{
|
||||
ExternalPluginManager.loadBuiltin(${plugin_prefix}Plugin.class);
|
||||
RuneLite.main(args);
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Copyright (c) 2018, Adam <Adam@sigterm.info>
|
||||
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.
|
||||
-->
|
||||
<configuration scan="true">
|
||||
<turboFilter class="net.runelite.client.util.DeduplicationFilter"/>
|
||||
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<Pattern>%d{yyyy-MM-dd HH:mm:ss zzz} [%thread] %-5level %logger{36} - %msg%n</Pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>$${user.home}/.runelite/logs/client.log</file>
|
||||
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
|
||||
<!-- daily rollover -->
|
||||
<fileNamePattern>$${user.home}/.runelite/logs/client_%d{yyyy-MM-dd}.%i.log</fileNamePattern>
|
||||
<maxFileSize>10MB</maxFileSize>
|
||||
<!-- keep 30 days' worth of history -->
|
||||
<maxHistory>30</maxHistory>
|
||||
</rollingPolicy>
|
||||
|
||||
<encoder>
|
||||
<Pattern>%d{yyyy-MM-dd HH:mm:ss zzz} [%thread] %-5level %logger{36} - %msg%n</Pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<root level="INFO">
|
||||
<appender-ref ref="STDOUT"/>
|
||||
<appender-ref ref="FILE"/>
|
||||
</root>
|
||||
<logger name="${package}" level="DEBUG" />
|
||||
</configuration>
|
||||
Reference in new issue
Block a user