move to version catalog, implement mod metadata format

This commit is contained in:
moehreag 2024-05-20 00:24:40 +02:00
parent 808d4b0e1b
commit 35cfb2a7c1
15 changed files with 8368 additions and 17 deletions

View file

@ -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
View 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]

View file

@ -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)
}

View file

@ -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;
}
}

View file

@ -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();
}
}

View file

@ -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
}
}

View file

@ -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);
}
}

View file

@ -6,5 +6,15 @@ public interface ModProperties {
String name();
SemVer version();
License license();
ModCredits credits();
ModDependencies dependencies();
ModExtensions extensions();
}

View file

@ -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);
}
}

View file

@ -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();

View file

@ -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);
}
}

View file

@ -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 {
}

View file

@ -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 {
}

View file

@ -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;
}
}

File diff suppressed because it is too large Load diff