move to version catalog, implement mod metadata format
This commit is contained in:
parent
808d4b0e1b
commit
35cfb2a7c1
|
@ -1,5 +1,6 @@
|
|||
plugins {
|
||||
java
|
||||
`java-library`
|
||||
id("io.freefair.lombok").version("8.+")
|
||||
`maven-publish`
|
||||
}
|
||||
|
@ -22,11 +23,14 @@ repositories {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
implementation("org.ecorous.esnesnon:nonsense-remapper:1.0.0-SNAPSHOT")
|
||||
implementation("net.fabricmc:sponge-mixin:0.13.4+mixin.0.8.5")
|
||||
implementation(libs.remapper)
|
||||
implementation("org.apache.logging.log4j:log4j-slf4j2-impl:3.0.0-beta2")
|
||||
implementation("org.apache.logging.log4j:log4j-api:3.0.0-beta2")
|
||||
implementation("org.apache.logging.log4j:log4j-core:3.0.0-beta2")
|
||||
|
||||
api(libs.mixin)
|
||||
api(libs.nightconfig)
|
||||
api(libs.annotations)
|
||||
}
|
||||
|
||||
java {
|
||||
|
|
17
gradle/libs.versions.toml
Normal file
17
gradle/libs.versions.toml
Normal file
|
@ -0,0 +1,17 @@
|
|||
[versions]
|
||||
|
||||
remapper = "1.0.0-SNAPSHOT"
|
||||
nightconfig = "3.7.1"
|
||||
mixin = "0.13.4+mixin.0.8.5"
|
||||
annotations = "24.1.0"
|
||||
|
||||
[libraries]
|
||||
|
||||
remapper = { module = "org.ecorous.esnesnon:nonsense-remapper", version.ref = "remapper" }
|
||||
nightconfig = { module = "com.electronwill.night-config:toml", version.ref = "nightconfig" }
|
||||
mixin = { module = "net.fabricmc:sponge-mixin", version.ref = "mixin" }
|
||||
annotations = { module = "org.jetbrains:annotations", version.ref = "annotations" }
|
||||
|
||||
[bundles]
|
||||
|
||||
[plugins]
|
|
@ -21,5 +21,5 @@ minecraft("1.20.6")
|
|||
|
||||
dependencies {
|
||||
implementation(project(":"))
|
||||
annotationProcessor("net.fabricmc:sponge-mixin:0.13.4+mixin.0.8.5")
|
||||
annotationProcessor(libs.mixin)
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
package org.ecorous.esnesnon.nonsense.loader.api.mod;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import org.ecorous.esnesnon.nonsense.loader.impl.LoaderImpl;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public final class License {
|
||||
|
||||
private static final Map<String, String> idToName = new HashMap<>();
|
||||
private static final Map<String, License> idToLicense = new HashMap<>();
|
||||
|
||||
static {
|
||||
loadList();
|
||||
}
|
||||
|
||||
private static void loadList(){
|
||||
try (InputStream in = License.class.getResourceAsStream("/assets/nonsense-loader/licenses.json")){
|
||||
if (in == null){
|
||||
throw new IllegalStateException("in == null");
|
||||
}
|
||||
JsonObject object = LoaderImpl.getInstance().getGson().fromJson(new InputStreamReader(in), JsonObject.class);
|
||||
object.getAsJsonArray("licenses").forEach(element -> {
|
||||
JsonObject entry = element.getAsJsonObject();
|
||||
idToName.put(entry.get("licenseId").getAsString(), entry.get("name").getAsString());
|
||||
});
|
||||
} catch (Exception e){
|
||||
LoggerFactory.getLogger(License.class).warn("Failed to load license list!", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static License fromId(String id){
|
||||
return idToLicense.computeIfAbsent(id, ignored -> new License(idToName.getOrDefault(id, id), id));
|
||||
}
|
||||
|
||||
private final String name, id;
|
||||
private License(String name, String id) {
|
||||
|
||||
this.name = name;
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String name() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String id() {
|
||||
return id;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
package org.ecorous.esnesnon.nonsense.loader.api.mod;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public final class ModCredits implements Map<String, Collection<String>> {
|
||||
|
||||
public static ModCredits of(Map<String, Collection<String>> credits){
|
||||
return new ModCredits(credits);
|
||||
}
|
||||
|
||||
private final Map<String, Collection<String>> credits;
|
||||
private ModCredits(Map<String, Collection<String>> credits){
|
||||
this.credits = credits;
|
||||
}
|
||||
|
||||
public Collection<String> getEntries(){
|
||||
return credits.keySet();
|
||||
}
|
||||
|
||||
public Collection<String> getRoles(String name){
|
||||
return credits.getOrDefault(name, Collections.emptySet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return credits.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return credits.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(Object key) {
|
||||
return credits.containsKey(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsValue(Object value) {
|
||||
return credits.containsValue(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> get(Object key) {
|
||||
return credits.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> put(String key, Collection<String> value) {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> remove(Object key) {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(@NotNull Map<? extends String, ? extends Collection<String>> m) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Set<String> keySet() {
|
||||
return credits.keySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Collection<Collection<String>> values() {
|
||||
return credits.values();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Set<Entry<String, Collection<String>>> entrySet() {
|
||||
return credits.entrySet();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
package org.ecorous.esnesnon.nonsense.loader.api.mod;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public final class ModDependencies {
|
||||
|
||||
private final Map<Type, Collection<Entry>> entries = new HashMap<>();
|
||||
|
||||
public ModDependencies(Collection<Entry> depends, Collection<Entry> breaks, Collection<Entry> suggests) {
|
||||
entries.put(Type.DEPEND, depends);
|
||||
entries.put(Type.BREAK, breaks);
|
||||
entries.put(Type.SUGGEST, suggests);
|
||||
}
|
||||
|
||||
public Collection<Entry> getForType(Type type) {
|
||||
return entries.get(type);
|
||||
}
|
||||
|
||||
public List<ModEntry> getForModId(String id) {
|
||||
List<ModEntry> entries = new ArrayList<>();
|
||||
for (Type type : Type.values()) {
|
||||
for (Entry entry : getForType(type)) {
|
||||
if (entry.id.equals(id)) {
|
||||
entries.add(new ModEntry(type, entry.range));
|
||||
}
|
||||
}
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
|
||||
public static class ModEntry {
|
||||
private final Type type;
|
||||
private final String range;
|
||||
|
||||
private ModEntry(Type type, String range) {
|
||||
this.type = type;
|
||||
this.range = range;
|
||||
}
|
||||
|
||||
public Type type() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public String range() {
|
||||
return range;
|
||||
}
|
||||
}
|
||||
|
||||
public record Entry(String id, String range) {
|
||||
|
||||
}
|
||||
|
||||
public enum Type {
|
||||
DEPEND, BREAK, SUGGEST
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package org.ecorous.esnesnon.nonsense.loader.api.mod;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public final class ModExtensions {
|
||||
|
||||
public static ModExtensions of(Map<String, Object> entries){
|
||||
return new ModExtensions(entries);
|
||||
}
|
||||
|
||||
private final Map<String, Object> extensions;
|
||||
|
||||
private ModExtensions(Map<String, Object> entries){
|
||||
extensions = entries;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T get(String key){
|
||||
return (T) extensions.get(key);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -6,5 +6,15 @@ public interface ModProperties {
|
|||
|
||||
String name();
|
||||
|
||||
SemVer version();
|
||||
|
||||
License license();
|
||||
|
||||
ModCredits credits();
|
||||
|
||||
ModDependencies dependencies();
|
||||
|
||||
ModExtensions extensions();
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
package org.ecorous.esnesnon.nonsense.loader.api.mod;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.function.IntSupplier;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import lombok.NonNull;
|
||||
import org.ecorous.esnesnon.nonsense.loader.impl.SemVerParseException;
|
||||
|
||||
public record SemVer(int major, int minor, int patch, String prerelease, String build) implements Comparable<SemVer> {
|
||||
// Adapted from https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string
|
||||
private static final Pattern SEMVER_PATTERN = Pattern.compile("^(?<major>0|[1-9]\\d*)\\." +
|
||||
"(?<minor>0|[1-9]\\d*)\\." +
|
||||
"(?<patch>0|[1-9]\\d*)" +
|
||||
"(?:-(?<prerelease>(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)" +
|
||||
"(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?" +
|
||||
"(?:\\+(?<buildmetadata>[0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$");
|
||||
|
||||
public static SemVer parse(String version) throws SemVerParseException {
|
||||
Matcher matcher = SEMVER_PATTERN.matcher(version);
|
||||
if (!matcher.find()) {
|
||||
throw new SemVerParseException(version);
|
||||
}
|
||||
|
||||
int major = Integer.parseInt(matcher.group("<major>"));
|
||||
int minor = Integer.parseInt(matcher.group("<minor>"));
|
||||
int patch = Integer.parseInt(matcher.group("<patch>"));
|
||||
String prerelease = matcher.group("<prerelease>");
|
||||
String buildmetadata = matcher.group("<buildmetadata>");
|
||||
return new SemVer(major, minor, patch, prerelease, buildmetadata);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder b = new StringBuilder();
|
||||
b.append(major).append(minor).append(patch);
|
||||
if (prerelease != null){
|
||||
b.append("-").append(prerelease);
|
||||
}
|
||||
if (build != null){
|
||||
b.append("+").append(build);
|
||||
}
|
||||
return b.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj instanceof SemVer s) {
|
||||
return compareTo(s) == 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(major, minor, patch, prerelease);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(@NonNull SemVer o) {
|
||||
int i;
|
||||
List<IntSupplier> suppliers = List.of(
|
||||
() -> Integer.compare(major, o.major),
|
||||
() -> Integer.compare(minor, o.minor),
|
||||
() -> Integer.compare(patch, o.patch),
|
||||
() -> prerelease != null ? o.prerelease != null ? 0 : -1 : o.prerelease != null ? 1 : 0
|
||||
);
|
||||
for (IntSupplier comparison : suppliers) {
|
||||
if ((i = comparison.getAsInt()) != 0) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
String[] self = prerelease.split("\\.");
|
||||
String[] other = o.prerelease.split("\\.");
|
||||
|
||||
for (int index = 0;index<Math.min(self.length, other.length);index++){
|
||||
boolean selfNumeric = self[index].matches("\\d+");
|
||||
boolean otherNumeric = other[index].matches("\\d+");
|
||||
if (selfNumeric != otherNumeric){
|
||||
return selfNumeric ? -1 : 1;
|
||||
} else if (!selfNumeric){
|
||||
if ((i = self[index].compareTo(other[index])) != 0){
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return Integer.compare(self.length, other.length);
|
||||
}
|
||||
}
|
|
@ -9,6 +9,7 @@ import java.nio.file.Path;
|
|||
import java.nio.file.Paths;
|
||||
import java.util.*;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import org.ecorous.esnesnon.nonsense.loader.api.Loader;
|
||||
import org.ecorous.esnesnon.nonsense.loader.api.env.Env;
|
||||
import org.ecorous.esnesnon.nonsense.loader.impl.launch.MixinClassloader;
|
||||
|
@ -41,6 +42,9 @@ public class LoaderImpl implements Loader {
|
|||
@Getter
|
||||
private final MixinClassloader classloader;
|
||||
|
||||
@Getter
|
||||
private final Gson gson = new Gson();
|
||||
|
||||
private LoaderImpl(String[] args, Env env) {
|
||||
instance = this;
|
||||
this.classloader = (MixinClassloader) this.getClass().getClassLoader();
|
||||
|
@ -65,6 +69,7 @@ public class LoaderImpl implements Loader {
|
|||
plugins.forEach(NonsensePlugin::run);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static void run(String[] args, Env env) {
|
||||
if (instance != null) {
|
||||
throw new IllegalStateException("Loader was started multiple times!");
|
||||
|
@ -79,9 +84,7 @@ public class LoaderImpl implements Loader {
|
|||
try (InputStream inputStream = url.openStream()) {
|
||||
new BufferedReader(new InputStreamReader(inputStream)).lines().forEach(classes::add);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
// TODO error handling
|
||||
throw new UncheckedIOException(e);
|
||||
LOGGER.error("Failed to load plugin: ", e);
|
||||
}
|
||||
});
|
||||
LOGGER.info("Found plugins: \n{}", String.join("\t\n", classes));
|
||||
|
@ -90,10 +93,10 @@ public class LoaderImpl implements Loader {
|
|||
try {
|
||||
return classloader.findClass(className);
|
||||
} catch (ClassNotFoundException e) {
|
||||
// TODO error handling
|
||||
throw new RuntimeException(e);
|
||||
LOGGER.error("Failed to load plugin: ", e);
|
||||
return null;
|
||||
}
|
||||
}).filter(NonsensePlugin.class::isAssignableFrom).toList()) {
|
||||
}).filter(Objects::nonNull).filter(NonsensePlugin.class::isAssignableFrom).toList()) {
|
||||
try {
|
||||
MethodHandle ctor = MethodHandles.publicLookup().findConstructor(c, MethodType.methodType(void.class));
|
||||
NonsensePlugin plugin = (NonsensePlugin) ctor.invoke();
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
package org.ecorous.esnesnon.nonsense.loader.impl;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class SemVerParseException extends IOException {
|
||||
public SemVerParseException(String message) {
|
||||
super("Failed to parse SemVer: "+message);
|
||||
}
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
package org.ecorous.esnesnon.nonsense.loader.impl.mod;
|
||||
|
||||
import org.ecorous.esnesnon.nonsense.loader.api.mod.ModProperties;
|
||||
|
||||
public record BuiltinModProperties(String id, String name) implements ModProperties {
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package org.ecorous.esnesnon.nonsense.loader.impl.mod;
|
||||
|
||||
import org.ecorous.esnesnon.nonsense.loader.api.mod.*;
|
||||
|
||||
public record ModPropertiesImpl(String id, String name, SemVer version, License license,
|
||||
ModCredits credits, ModDependencies dependencies,
|
||||
ModExtensions extensions) implements ModProperties {
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
package org.ecorous.esnesnon.nonsense.loader.impl.plugin.game;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonObject;
|
||||
import org.ecorous.esnesnon.nonsense.loader.impl.Discovery;
|
||||
import org.ecorous.esnesnon.nonsense.loader.impl.LoaderImpl;
|
||||
|
@ -110,7 +109,7 @@ public class Minecraft implements NonsensePlugin {
|
|||
if (Files.exists(fs.getPath(n))){
|
||||
LOGGER.info("Found game: {}", jar);
|
||||
foundMainClass = n.substring(0, n.length()-6).replace("/", ".");
|
||||
version = new Gson().fromJson(Files.readString(fs.getPath("version.json")), JsonObject.class).get("id").getAsString();
|
||||
version = LoaderImpl.getInstance().getGson().fromJson(Files.readString(fs.getPath("version.json")), JsonObject.class).get("id").getAsString();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
7997
src/main/resources/assets/nonsense-loader/licenses.json
Normal file
7997
src/main/resources/assets/nonsense-loader/licenses.json
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue