format
This commit is contained in:
parent
9e61b40f81
commit
ac09413c61
|
@ -12,7 +12,7 @@ import java.util.List;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
public class Discovery {
|
public class Discovery {
|
||||||
public static Collection<Path> find(Path start, Predicate<Path> directoryFilter, Predicate<Path> fileFilter){
|
public static Collection<Path> find(Path start, Predicate<Path> directoryFilter, Predicate<Path> fileFilter) {
|
||||||
List<Path> paths = new ArrayList<>();
|
List<Path> paths = new ArrayList<>();
|
||||||
try {
|
try {
|
||||||
Files.walkFileTree(start, new SimpleFileVisitor<>() {
|
Files.walkFileTree(start, new SimpleFileVisitor<>() {
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
package org.ecorous.esnesnon.nonsense.loader.impl;
|
package org.ecorous.esnesnon.nonsense.loader.impl;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.BufferedReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
import java.lang.invoke.MethodHandle;
|
import java.lang.invoke.MethodHandle;
|
||||||
import java.lang.invoke.MethodHandles;
|
import java.lang.invoke.MethodHandles;
|
||||||
import java.lang.invoke.MethodType;
|
import java.lang.invoke.MethodType;
|
||||||
|
@ -11,29 +14,26 @@ import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
|
import lombok.Getter;
|
||||||
import org.ecorous.esnesnon.nonsense.loader.api.Loader;
|
import org.ecorous.esnesnon.nonsense.loader.api.Loader;
|
||||||
import org.ecorous.esnesnon.nonsense.loader.api.env.Env;
|
import org.ecorous.esnesnon.nonsense.loader.api.env.Env;
|
||||||
import org.ecorous.esnesnon.nonsense.loader.api.mod.ModProperties;
|
import org.ecorous.esnesnon.nonsense.loader.api.mod.ModProperties;
|
||||||
import org.ecorous.esnesnon.nonsense.loader.impl.launch.MixinClassLoader;
|
import org.ecorous.esnesnon.nonsense.loader.impl.launch.MixinClassLoader;
|
||||||
import org.ecorous.esnesnon.nonsense.loader.impl.mod.ModUtil;
|
import org.ecorous.esnesnon.nonsense.loader.impl.mod.ModUtil;
|
||||||
import org.ecorous.esnesnon.nonsense.loader.impl.plugin.NonsensePlugin;
|
import org.ecorous.esnesnon.nonsense.loader.impl.plugin.NonsensePlugin;
|
||||||
import lombok.Getter;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.spongepowered.asm.mixin.MixinEnvironment;
|
import org.spongepowered.asm.mixin.MixinEnvironment;
|
||||||
|
|
||||||
public class LoaderImpl implements Loader {
|
public class LoaderImpl implements Loader {
|
||||||
public static final String MOD_FILE_EXTENSION = ".frogmod";
|
public static final String MOD_FILE_EXTENSION = ".frogmod";
|
||||||
|
@Getter
|
||||||
|
private static LoaderImpl instance;
|
||||||
private final boolean DEV_ENV = Boolean.getBoolean("nonsense.development");
|
private final boolean DEV_ENV = Boolean.getBoolean("nonsense.development");
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
private final String[] args;
|
private final String[] args;
|
||||||
@Getter
|
@Getter
|
||||||
private final Env env;
|
private final Env env;
|
||||||
|
|
||||||
@Getter
|
|
||||||
private static LoaderImpl instance;
|
|
||||||
|
|
||||||
private final Logger LOGGER = LoggerFactory.getLogger("Frogloader");
|
private final Logger LOGGER = LoggerFactory.getLogger("Frogloader");
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
|
@ -79,16 +79,6 @@ public class LoaderImpl implements Loader {
|
||||||
plugins.forEach(NonsensePlugin::run);
|
plugins.forEach(NonsensePlugin::run);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void advanceMixinState(){
|
|
||||||
try {
|
|
||||||
MethodHandle m = MethodHandles.privateLookupIn(MixinEnvironment.class, MethodHandles.lookup()).findStatic(MixinEnvironment.class, "gotoPhase", MethodType.methodType(void.class, MixinEnvironment.Phase.class));
|
|
||||||
m.invoke(MixinEnvironment.Phase.INIT);
|
|
||||||
m.invoke(MixinEnvironment.Phase.DEFAULT);
|
|
||||||
} catch (Throwable e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public static void run(String[] args, Env env) {
|
public static void run(String[] args, Env env) {
|
||||||
if (instance != null) {
|
if (instance != null) {
|
||||||
|
@ -98,9 +88,19 @@ public class LoaderImpl implements Loader {
|
||||||
new LoaderImpl(args, env);
|
new LoaderImpl(args, env);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void advanceMixinState() {
|
||||||
|
try {
|
||||||
|
MethodHandle m = MethodHandles.privateLookupIn(MixinEnvironment.class, MethodHandles.lookup()).findStatic(MixinEnvironment.class, "gotoPhase", MethodType.methodType(void.class, MixinEnvironment.Phase.class));
|
||||||
|
m.invoke(MixinEnvironment.Phase.INIT);
|
||||||
|
m.invoke(MixinEnvironment.Phase.DEFAULT);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void discoverPlugins() {
|
private void discoverPlugins() {
|
||||||
List<String> classes = new ArrayList<>();
|
List<String> classes = new ArrayList<>();
|
||||||
this.getClass().getClassLoader().resources("META-INF/services/"+NonsensePlugin.class.getName()).distinct().forEach(url -> {
|
this.getClass().getClassLoader().resources("META-INF/services/" + NonsensePlugin.class.getName()).distinct().forEach(url -> {
|
||||||
try (InputStream inputStream = url.openStream()) {
|
try (InputStream inputStream = url.openStream()) {
|
||||||
new BufferedReader(new InputStreamReader(inputStream)).lines().forEach(classes::add);
|
new BufferedReader(new InputStreamReader(inputStream)).lines().forEach(classes::add);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
@ -130,24 +130,24 @@ public class LoaderImpl implements Loader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (plugins.isEmpty()){
|
if (plugins.isEmpty()) {
|
||||||
// TODO display error
|
// TODO display error
|
||||||
throw new IllegalStateException("No plugin applicable to the current state was found!");
|
throw new IllegalStateException("No plugin applicable to the current state was found!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getArgument(String name){
|
public String getArgument(String name) {
|
||||||
for (int i=0;i<args.length-1;i+=2){
|
for (int i = 0; i < args.length - 1; i += 2) {
|
||||||
if (args[i].equals("--"+name)){
|
if (args[i].equals("--" + name)) {
|
||||||
return args[i+1];
|
return args[i + 1];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getArgumentOrElse(String name, String other){
|
public String getArgumentOrElse(String name, String other) {
|
||||||
String res = getArgument(name);
|
String res = getArgument(name);
|
||||||
if (res.isEmpty()){
|
if (res.isEmpty()) {
|
||||||
return other;
|
return other;
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
|
@ -168,7 +168,7 @@ public class LoaderImpl implements Loader {
|
||||||
return Optional.ofNullable(mods.get(id));
|
return Optional.ofNullable(mods.get(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<String, ModProperties> collectMods(){
|
private Map<String, ModProperties> collectMods() {
|
||||||
Collection<ModProperties> properties = plugins.stream().map(NonsensePlugin::getMods).reduce(new HashSet<>(), (s1, s2) -> {
|
Collection<ModProperties> properties = plugins.stream().map(NonsensePlugin::getMods).reduce(new HashSet<>(), (s1, s2) -> {
|
||||||
s1.addAll(s2);
|
s1.addAll(s2);
|
||||||
return s1;
|
return s1;
|
||||||
|
@ -176,7 +176,7 @@ public class LoaderImpl implements Loader {
|
||||||
return properties.stream().collect(Collectors.toMap(ModProperties::id, m -> m));
|
return properties.stream().collect(Collectors.toMap(ModProperties::id, m -> m));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Collection<String> collectModIds(){
|
private Collection<String> collectModIds() {
|
||||||
return mods.keySet();
|
return mods.keySet();
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -4,6 +4,6 @@ import java.io.IOException;
|
||||||
|
|
||||||
public class SemVerParseException extends IOException {
|
public class SemVerParseException extends IOException {
|
||||||
public SemVerParseException(String message) {
|
public SemVerParseException(String message) {
|
||||||
super("Failed to parse SemVer: "+message);
|
super("Failed to parse SemVer: " + message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,32 +6,26 @@ import java.lang.invoke.MethodType;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
import org.ecorous.esnesnon.nonsense.loader.api.env.Env;
|
import org.ecorous.esnesnon.nonsense.loader.api.env.Env;
|
||||||
import org.ecorous.esnesnon.nonsense.loader.impl.mixin.NonsenseMixinService;
|
import org.ecorous.esnesnon.nonsense.loader.impl.mixin.NonsenseMixinService;
|
||||||
import lombok.Getter;
|
|
||||||
import org.spongepowered.asm.launch.MixinBootstrap;
|
import org.spongepowered.asm.launch.MixinBootstrap;
|
||||||
import org.spongepowered.asm.service.IPropertyKey;
|
import org.spongepowered.asm.service.IPropertyKey;
|
||||||
|
|
||||||
public class Launcher {
|
public class Launcher {
|
||||||
|
|
||||||
@Getter
|
|
||||||
private final MixinClassLoader targetClassLoader;
|
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
private static Launcher instance;
|
private static Launcher instance;
|
||||||
|
@Getter
|
||||||
|
private final MixinClassLoader targetClassLoader;
|
||||||
@Getter
|
@Getter
|
||||||
private final Map<IPropertyKey, Object> globalProperties = new HashMap<>();
|
private final Map<IPropertyKey, Object> globalProperties = new HashMap<>();
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
private final Env env;
|
private final Env env;
|
||||||
|
|
||||||
public static void run(String[] args, Env env) {
|
public Launcher(String[] args, Env env) {
|
||||||
new Launcher(args, env);
|
if (instance != null) {
|
||||||
}
|
|
||||||
|
|
||||||
public Launcher(String[] args, Env env){
|
|
||||||
if (instance != null){
|
|
||||||
throw new IllegalStateException();
|
throw new IllegalStateException();
|
||||||
}
|
}
|
||||||
instance = this;
|
instance = this;
|
||||||
|
@ -57,4 +51,8 @@ public class Launcher {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void run(String[] args, Env env) {
|
||||||
|
new Launcher(args, env);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,5 @@
|
||||||
package org.ecorous.esnesnon.nonsense.loader.impl.launch;
|
package org.ecorous.esnesnon.nonsense.loader.impl.launch;
|
||||||
|
|
||||||
import org.ecorous.esnesnon.nonsense.loader.impl.mixin.NonsenseMixinService;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
import org.spongepowered.asm.mixin.MixinEnvironment;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
@ -13,117 +9,122 @@ import java.util.Collections;
|
||||||
import java.util.Enumeration;
|
import java.util.Enumeration;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.ecorous.esnesnon.nonsense.loader.impl.mixin.NonsenseMixinService;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.spongepowered.asm.mixin.MixinEnvironment;
|
||||||
|
|
||||||
public class MixinClassLoader extends URLClassLoader {
|
public class MixinClassLoader extends URLClassLoader {
|
||||||
|
|
||||||
private static final ClassLoader SYSTEM = ClassLoader.getSystemClassLoader();
|
private static final ClassLoader SYSTEM = ClassLoader.getSystemClassLoader();
|
||||||
private final List<String> exclusions = new ArrayList<>();
|
|
||||||
|
|
||||||
static {
|
static {
|
||||||
registerAsParallelCapable();
|
registerAsParallelCapable();
|
||||||
}
|
}
|
||||||
|
|
||||||
public MixinClassLoader() {
|
private final List<String> exclusions = new ArrayList<>();
|
||||||
super(new URL[0], null);
|
|
||||||
excludePackage("java");
|
|
||||||
excludePackage("com.sun");
|
|
||||||
excludePackage("sun");
|
|
||||||
excludePackage("jdk");
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isClassLoaded(String name) {
|
public MixinClassLoader() {
|
||||||
return findLoadedClass(name) != null;
|
super(new URL[0], null);
|
||||||
}
|
excludePackage("java");
|
||||||
|
excludePackage("com.sun");
|
||||||
|
excludePackage("sun");
|
||||||
|
excludePackage("jdk");
|
||||||
|
}
|
||||||
|
|
||||||
public byte[] getClassBytes(String name) throws IOException {
|
public boolean isClassLoaded(String name) {
|
||||||
String binName = name.replace('.', '/');
|
return findLoadedClass(name) != null;
|
||||||
String path = binName.concat(".class");
|
}
|
||||||
|
|
||||||
try (InputStream in = getResourceAsStream(path)) {
|
public byte[] getClassBytes(String name) throws IOException {
|
||||||
if (in == null)
|
String binName = name.replace('.', '/');
|
||||||
return null;
|
String path = binName.concat(".class");
|
||||||
|
|
||||||
return NonsenseMixinService.getTransformer().transformClass(MixinEnvironment.getCurrentEnvironment(), name, AccessWidener.processClass(in.readAllBytes(), binName));
|
try (InputStream in = getResourceAsStream(path)) {
|
||||||
}
|
if (in == null)
|
||||||
}
|
return null;
|
||||||
|
|
||||||
public void excludePackage(String name) {
|
return NonsenseMixinService.getTransformer().transformClass(MixinEnvironment.getCurrentEnvironment(), name, AccessWidener.processClass(in.readAllBytes(), binName));
|
||||||
exclusions.add(name + '.');
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public void excludePackage(String name) {
|
||||||
public void addURL(URL url) {
|
exclusions.add(name + '.');
|
||||||
super.addURL(url);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
|
public void addURL(URL url) {
|
||||||
synchronized (getClassLoadingLock(name)) {
|
super.addURL(url);
|
||||||
for (String prefix : exclusions) {
|
}
|
||||||
if (name.startsWith(prefix)) {
|
|
||||||
return SYSTEM.loadClass(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Class<?> loaded = findLoadedClass(name);
|
@Override
|
||||||
if (loaded != null)
|
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
|
||||||
return loaded;
|
synchronized (getClassLoadingLock(name)) {
|
||||||
|
for (String prefix : exclusions) {
|
||||||
|
if (name.startsWith(prefix)) {
|
||||||
|
return SYSTEM.loadClass(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Class<?> result = findClass(name);
|
Class<?> loaded = findLoadedClass(name);
|
||||||
|
if (loaded != null)
|
||||||
|
return loaded;
|
||||||
|
|
||||||
if (resolve)
|
Class<?> result = findClass(name);
|
||||||
resolveClass(result);
|
|
||||||
|
|
||||||
return result;
|
if (resolve)
|
||||||
}
|
resolveClass(result);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
return result;
|
||||||
public synchronized Class<?> findClass(String name) throws ClassNotFoundException {
|
}
|
||||||
try {
|
}
|
||||||
byte[] bytes = getClassBytes(name);
|
|
||||||
if (bytes == null)
|
|
||||||
throw new ClassNotFoundException(name);
|
|
||||||
|
|
||||||
return defineClass(name, bytes, 0, bytes.length);
|
@Override
|
||||||
} catch (IOException e) {
|
public synchronized Class<?> findClass(String name) throws ClassNotFoundException {
|
||||||
throw new ClassNotFoundException(name, e);
|
try {
|
||||||
}
|
byte[] bytes = getClassBytes(name);
|
||||||
}
|
if (bytes == null)
|
||||||
|
throw new ClassNotFoundException(name);
|
||||||
|
|
||||||
@Nullable
|
return defineClass(name, bytes, 0, bytes.length);
|
||||||
@Override
|
} catch (IOException e) {
|
||||||
public URL getResource(String name) {
|
throw new ClassNotFoundException(name, e);
|
||||||
URL parentUrl = super.getResource(name);
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (parentUrl != null)
|
@Nullable
|
||||||
return parentUrl;
|
@Override
|
||||||
|
public URL getResource(String name) {
|
||||||
|
URL parentUrl = super.getResource(name);
|
||||||
|
|
||||||
return SYSTEM.getResource(name);
|
if (parentUrl != null)
|
||||||
}
|
return parentUrl;
|
||||||
|
|
||||||
@Override
|
return SYSTEM.getResource(name);
|
||||||
public Enumeration<URL> getResources(String name) throws IOException {
|
}
|
||||||
Enumeration<URL> parentResources = super.getResources(name);
|
|
||||||
Enumeration<URL> systemResources = SYSTEM.getResources(name);
|
|
||||||
|
|
||||||
if (parentResources.hasMoreElements() && systemResources.hasMoreElements()) {
|
@Override
|
||||||
List<URL> list = new ArrayList<>();
|
public Enumeration<URL> getResources(String name) throws IOException {
|
||||||
|
Enumeration<URL> parentResources = super.getResources(name);
|
||||||
|
Enumeration<URL> systemResources = SYSTEM.getResources(name);
|
||||||
|
|
||||||
while (parentResources.hasMoreElements())
|
if (parentResources.hasMoreElements() && systemResources.hasMoreElements()) {
|
||||||
list.add(parentResources.nextElement());
|
List<URL> list = new ArrayList<>();
|
||||||
|
|
||||||
while (systemResources.hasMoreElements())
|
while (parentResources.hasMoreElements())
|
||||||
list.add(systemResources.nextElement());
|
list.add(parentResources.nextElement());
|
||||||
|
|
||||||
return Collections.enumeration(list);
|
while (systemResources.hasMoreElements())
|
||||||
}
|
list.add(systemResources.nextElement());
|
||||||
|
|
||||||
if (parentResources.hasMoreElements())
|
return Collections.enumeration(list);
|
||||||
return parentResources;
|
}
|
||||||
|
|
||||||
if (systemResources.hasMoreElements())
|
if (parentResources.hasMoreElements())
|
||||||
return systemResources;
|
return parentResources;
|
||||||
|
|
||||||
return Collections.enumeration(Collections.emptyList());
|
if (systemResources.hasMoreElements())
|
||||||
}
|
return systemResources;
|
||||||
|
|
||||||
|
return Collections.enumeration(Collections.emptyList());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
package org.ecorous.esnesnon.nonsense.loader.impl.launch.client;
|
package org.ecorous.esnesnon.nonsense.loader.impl.launch.client;
|
||||||
|
|
||||||
import org.ecorous.esnesnon.nonsense.loader.impl.launch.Launcher;
|
|
||||||
import org.ecorous.esnesnon.nonsense.loader.api.env.Env;
|
import org.ecorous.esnesnon.nonsense.loader.api.env.Env;
|
||||||
|
import org.ecorous.esnesnon.nonsense.loader.impl.launch.Launcher;
|
||||||
|
|
||||||
public class NonsenseClient {
|
public class NonsenseClient {
|
||||||
|
|
||||||
public static void main(String[] args){
|
public static void main(String[] args) {
|
||||||
Launcher.run(args, Env.CLIENT);
|
Launcher.run(args, Env.CLIENT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
package org.ecorous.esnesnon.nonsense.loader.impl.launch.server;
|
package org.ecorous.esnesnon.nonsense.loader.impl.launch.server;
|
||||||
|
|
||||||
import org.ecorous.esnesnon.nonsense.loader.impl.launch.Launcher;
|
|
||||||
import org.ecorous.esnesnon.nonsense.loader.api.env.Env;
|
import org.ecorous.esnesnon.nonsense.loader.api.env.Env;
|
||||||
|
import org.ecorous.esnesnon.nonsense.loader.impl.launch.Launcher;
|
||||||
|
|
||||||
public class NonsenseServer {
|
public class NonsenseServer {
|
||||||
|
|
||||||
public static void main(String[] args){
|
public static void main(String[] args) {
|
||||||
Launcher.run(args, Env.SERVER);
|
Launcher.run(args, Env.SERVER);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,16 +12,15 @@ import org.spongepowered.asm.logging.LoggerAdapterAbstract;
|
||||||
|
|
||||||
public class NonsenseMixinLogger extends LoggerAdapterAbstract {
|
public class NonsenseMixinLogger extends LoggerAdapterAbstract {
|
||||||
private static final Map<String, NonsenseMixinLogger> LOGGERS = new ConcurrentHashMap<>();
|
private static final Map<String, NonsenseMixinLogger> LOGGERS = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
public static ILogger get(String name){
|
|
||||||
return LOGGERS.computeIfAbsent(name, NonsenseMixinLogger::new);
|
|
||||||
}
|
|
||||||
|
|
||||||
private final Logger log;
|
private final Logger log;
|
||||||
|
|
||||||
public NonsenseMixinLogger(String name){
|
public NonsenseMixinLogger(String name) {
|
||||||
super(name);
|
super(name);
|
||||||
log = LoggerFactory.getLogger("Nonsense Loader/"+name.substring(0, 1).toUpperCase(Locale.ROOT)+name.substring(1));
|
log = LoggerFactory.getLogger("Nonsense Loader/" + name.substring(0, 1).toUpperCase(Locale.ROOT) + name.substring(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ILogger get(String name) {
|
||||||
|
return LOGGERS.computeIfAbsent(name, NonsenseMixinLogger::new);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -33,6 +32,7 @@ public class NonsenseMixinLogger extends LoggerAdapterAbstract {
|
||||||
public void catching(Level level, Throwable t) {
|
public void catching(Level level, Throwable t) {
|
||||||
log.atLevel(org.slf4j.event.Level.valueOf(level.name())).setCause(t).log();
|
log.atLevel(org.slf4j.event.Level.valueOf(level.name())).setCause(t).log();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void log(Level level, String message, Object... params) {
|
public void log(Level level, String message, Object... params) {
|
||||||
log.atLevel(org.slf4j.event.Level.valueOf(level.name())).log(message, params);
|
log.atLevel(org.slf4j.event.Level.valueOf(level.name())).log(message, params);
|
||||||
|
|
|
@ -17,7 +17,7 @@ public class JavaModProperties {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ModProperties get() throws SemVerParseException {
|
public static ModProperties get() throws SemVerParseException {
|
||||||
if (INSTANCE == null){
|
if (INSTANCE == null) {
|
||||||
INSTANCE = new ModPropertiesImpl("java",
|
INSTANCE = new ModPropertiesImpl("java",
|
||||||
System.getProperty("java.vm.name"),
|
System.getProperty("java.vm.name"),
|
||||||
SemVerImpl.parse(System.getProperty("java.runtime.version")),
|
SemVerImpl.parse(System.getProperty("java.runtime.version")),
|
||||||
|
|
|
@ -19,16 +19,14 @@ import org.slf4j.LoggerFactory;
|
||||||
public class ModDependencyResolver {
|
public class ModDependencyResolver {
|
||||||
|
|
||||||
private final static Logger LOGGER = LoggerFactory.getLogger(ModDependencyResolver.class);
|
private final static Logger LOGGER = LoggerFactory.getLogger(ModDependencyResolver.class);
|
||||||
|
private static final Pattern COMPARATOR = Pattern.compile("(=|>=|<=|>|<)?(\\d.*)");
|
||||||
private final Collection<ModProperties> original;
|
private final Collection<ModProperties> original;
|
||||||
|
|
||||||
private final Map<String, DependencyEntry> dependencies = new HashMap<>();
|
private final Map<String, DependencyEntry> dependencies = new HashMap<>();
|
||||||
private final Map<String, DependencyEntry> breakings = new HashMap<>();
|
private final Map<String, DependencyEntry> breakings = new HashMap<>();
|
||||||
private final Map<String, DependencyEntry> suggests = new HashMap<>();
|
private final Map<String, DependencyEntry> suggests = new HashMap<>();
|
||||||
private final Map<String, SemVer> provides = new HashMap<>();
|
private final Map<String, SemVer> provides = new HashMap<>();
|
||||||
private final Map<String, ModProperties> presentMods = new HashMap<>();
|
private final Map<String, ModProperties> presentMods = new HashMap<>();
|
||||||
|
|
||||||
|
|
||||||
public ModDependencyResolver(Collection<ModProperties> mods) throws ResolverException {
|
public ModDependencyResolver(Collection<ModProperties> mods) throws ResolverException {
|
||||||
this.original = Collections.unmodifiableCollection(mods);
|
this.original = Collections.unmodifiableCollection(mods);
|
||||||
load();
|
load();
|
||||||
|
@ -73,7 +71,7 @@ public class ModDependencyResolver {
|
||||||
presentMods.forEach((s, modProperties) -> presentOrProvided.put(s, modProperties.version()));
|
presentMods.forEach((s, modProperties) -> presentOrProvided.put(s, modProperties.version()));
|
||||||
provides.forEach((s, ver) -> {
|
provides.forEach((s, ver) -> {
|
||||||
if (presentMods.containsKey(s)) {
|
if (presentMods.containsKey(s)) {
|
||||||
if (presentMods.get(s).version().compareTo(ver) < 0){
|
if (presentMods.get(s).version().compareTo(ver) < 0) {
|
||||||
presentOrProvided.replace(s, ver);
|
presentOrProvided.replace(s, ver);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -93,7 +91,7 @@ public class ModDependencyResolver {
|
||||||
|
|
||||||
// Step 4.1: Add all mods to the result that do not depend on any other mods
|
// Step 4.1: Add all mods to the result that do not depend on any other mods
|
||||||
presentMods.forEach((s, modProperties) -> {
|
presentMods.forEach((s, modProperties) -> {
|
||||||
if (modProperties.dependencies().getForType(ModDependencies.Type.DEPEND).isEmpty()){
|
if (modProperties.dependencies().getForType(ModDependencies.Type.DEPEND).isEmpty()) {
|
||||||
result.add(modProperties);
|
result.add(modProperties);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -116,35 +114,6 @@ public class ModDependencyResolver {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@AllArgsConstructor
|
|
||||||
@Getter
|
|
||||||
public static class BreakingModException extends Exception {
|
|
||||||
private final ModProperties source, broken;
|
|
||||||
private final VersionRange range;
|
|
||||||
}
|
|
||||||
|
|
||||||
@AllArgsConstructor
|
|
||||||
@Getter
|
|
||||||
public static class UnfulfilledDependencyException extends Exception {
|
|
||||||
private final ModProperties source;
|
|
||||||
private final String dependency;
|
|
||||||
private final VersionRange range;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class ResolverException extends Exception {
|
|
||||||
public ResolverException(String message) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private record DependencyEntry(VersionRange range, ModProperties origin) {
|
|
||||||
|
|
||||||
public DependencyEntry(String range, ModProperties origin) throws ResolverException {
|
|
||||||
this(VersionRange.parse(range), origin);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
private enum DependencyType {
|
private enum DependencyType {
|
||||||
EQ("=", Object::equals),
|
EQ("=", Object::equals),
|
||||||
|
@ -166,7 +135,33 @@ public class ModDependencyResolver {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final Pattern COMPARATOR = Pattern.compile("(=|>=|<=|>|<)?(\\d.*)");
|
@AllArgsConstructor
|
||||||
|
@Getter
|
||||||
|
public static class BreakingModException extends Exception {
|
||||||
|
private final ModProperties source, broken;
|
||||||
|
private final VersionRange range;
|
||||||
|
}
|
||||||
|
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Getter
|
||||||
|
public static class UnfulfilledDependencyException extends Exception {
|
||||||
|
private final ModProperties source;
|
||||||
|
private final String dependency;
|
||||||
|
private final VersionRange range;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ResolverException extends Exception {
|
||||||
|
public ResolverException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private record DependencyEntry(VersionRange range, ModProperties origin) {
|
||||||
|
|
||||||
|
public DependencyEntry(String range, ModProperties origin) throws ResolverException {
|
||||||
|
this(VersionRange.parse(range), origin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private record Comparator(DependencyType type, SemVer version) {
|
private record Comparator(DependencyType type, SemVer version) {
|
||||||
|
|
||||||
|
@ -222,6 +217,8 @@ public class ModDependencyResolver {
|
||||||
|
|
||||||
private record VersionRange(Collection<ComparatorSet> sets) {
|
private record VersionRange(Collection<ComparatorSet> sets) {
|
||||||
|
|
||||||
|
private static final Pattern NUMBER_EXTRACTOR = Pattern.compile("[^~]?(\\d+)(?:\\.(?:([\\dxX*]+)(?:\\.([\\dxX*]+)?)?)?)?(.*)");
|
||||||
|
|
||||||
public static VersionRange parse(String range) throws ResolverException {
|
public static VersionRange parse(String range) throws ResolverException {
|
||||||
|
|
||||||
String[] sets = resolveAdvanced(range).split("\\|\\|");
|
String[] sets = resolveAdvanced(range).split("\\|\\|");
|
||||||
|
@ -229,8 +226,6 @@ public class ModDependencyResolver {
|
||||||
return new VersionRange(Arrays.stream(sets).map(ComparatorSet::parse).toList());
|
return new VersionRange(Arrays.stream(sets).map(ComparatorSet::parse).toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final Pattern NUMBER_EXTRACTOR = Pattern.compile("[^~]?(\\d+)(?:\\.(?:([\\dxX*]+)(?:\\.([\\dxX*]+)?)?)?)?(.*)");
|
|
||||||
|
|
||||||
private static String resolveAdvanced(String range) throws ResolverException {
|
private static String resolveAdvanced(String range) throws ResolverException {
|
||||||
if (range.isEmpty() || range.equals("*")) {
|
if (range.isEmpty() || range.equals("*")) {
|
||||||
return ">=0.0.0";
|
return ">=0.0.0";
|
||||||
|
@ -242,7 +237,7 @@ public class ModDependencyResolver {
|
||||||
for (int i = 0, listSize = list.size(); i < listSize; i++) {
|
for (int i = 0, listSize = list.size(); i < listSize; i++) {
|
||||||
String s = list.get(i);
|
String s = list.get(i);
|
||||||
|
|
||||||
if (i < listSize-1 && list.get(i + 1).equals("-")) {
|
if (i < listSize - 1 && list.get(i + 1).equals("-")) {
|
||||||
handleHyphenRange(s, ranges, list.get(i + 2));
|
handleHyphenRange(s, ranges, list.get(i + 2));
|
||||||
i += 2;
|
i += 2;
|
||||||
} else if (s.startsWith("~")) {
|
} else if (s.startsWith("~")) {
|
||||||
|
@ -275,8 +270,8 @@ public class ModDependencyResolver {
|
||||||
ranges.add(" ");
|
ranges.add(" ");
|
||||||
|
|
||||||
Matcher matcher = NUMBER_EXTRACTOR.matcher(s);
|
Matcher matcher = NUMBER_EXTRACTOR.matcher(s);
|
||||||
if (!matcher.find()){
|
if (!matcher.find()) {
|
||||||
throw new ResolverException("Version "+s+" did not match the required pattern to find the numbers within!");
|
throw new ResolverException("Version " + s + " did not match the required pattern to find the numbers within!");
|
||||||
}
|
}
|
||||||
int major = Integer.parseInt(matcher.group(1));
|
int major = Integer.parseInt(matcher.group(1));
|
||||||
int minor = Optional.ofNullable(matcher.group(2)).map(Integer::parseInt).orElse(0);
|
int minor = Optional.ofNullable(matcher.group(2)).map(Integer::parseInt).orElse(0);
|
||||||
|
@ -305,12 +300,12 @@ public class ModDependencyResolver {
|
||||||
|
|
||||||
if (s.length() < 5) {
|
if (s.length() < 5) {
|
||||||
Matcher matcher = NUMBER_EXTRACTOR.matcher(s);
|
Matcher matcher = NUMBER_EXTRACTOR.matcher(s);
|
||||||
if (!matcher.find()){
|
if (!matcher.find()) {
|
||||||
throw new ResolverException("Version "+s+" did not match the required pattern to find the numbers within!");
|
throw new ResolverException("Version " + s + " did not match the required pattern to find the numbers within!");
|
||||||
}
|
}
|
||||||
int major = Integer.parseInt(matcher.group(1));
|
int major = Integer.parseInt(matcher.group(1));
|
||||||
int minor = Optional.ofNullable(matcher.group(2)).map(n -> n.equalsIgnoreCase("x") || n.equals("*") ? null : n).map(Integer::parseInt).orElse(0);
|
int minor = Optional.ofNullable(matcher.group(2)).map(n -> n.equalsIgnoreCase("x") || n.equals("*") ? null : n).map(Integer::parseInt).orElse(0);
|
||||||
int patch = Optional.ofNullable(matcher.group(3)).map(n -> n.equalsIgnoreCase("x") || n.equals("*")? null : n).map(Integer::parseInt).orElse(0);
|
int patch = Optional.ofNullable(matcher.group(3)).map(n -> n.equalsIgnoreCase("x") || n.equals("*") ? null : n).map(Integer::parseInt).orElse(0);
|
||||||
StringBuilder builder = new StringBuilder("<");
|
StringBuilder builder = new StringBuilder("<");
|
||||||
int[] ints = new int[]{major, minor, patch};
|
int[] ints = new int[]{major, minor, patch};
|
||||||
for (int j = 0, intsLength = ints.length; j < intsLength; j++) {
|
for (int j = 0, intsLength = ints.length; j < intsLength; j++) {
|
||||||
|
@ -335,8 +330,8 @@ public class ModDependencyResolver {
|
||||||
|
|
||||||
private static void handleCaret(String s, List<String> ranges) throws ResolverException {
|
private static void handleCaret(String s, List<String> ranges) throws ResolverException {
|
||||||
Matcher matcher = NUMBER_EXTRACTOR.matcher(s);
|
Matcher matcher = NUMBER_EXTRACTOR.matcher(s);
|
||||||
if (!matcher.find()){
|
if (!matcher.find()) {
|
||||||
throw new ResolverException("Version "+s+" did not match the required pattern to find the numbers within!");
|
throw new ResolverException("Version " + s + " did not match the required pattern to find the numbers within!");
|
||||||
}
|
}
|
||||||
int major = Integer.parseInt(matcher.group(1));
|
int major = Integer.parseInt(matcher.group(1));
|
||||||
int minor = Optional.ofNullable(matcher.group(2)).map(n -> n.equalsIgnoreCase("x") || n.equals("*") ? null : n).map(Integer::parseInt).orElse(0);
|
int minor = Optional.ofNullable(matcher.group(2)).map(n -> n.equalsIgnoreCase("x") || n.equals("*") ? null : n).map(Integer::parseInt).orElse(0);
|
||||||
|
@ -380,8 +375,8 @@ public class ModDependencyResolver {
|
||||||
|
|
||||||
if (end.length() < 5) {
|
if (end.length() < 5) {
|
||||||
Matcher matcher = NUMBER_EXTRACTOR.matcher(end);
|
Matcher matcher = NUMBER_EXTRACTOR.matcher(end);
|
||||||
if (!matcher.find()){
|
if (!matcher.find()) {
|
||||||
throw new ResolverException("Version "+s+" did not match the required pattern to find the numbers within!");
|
throw new ResolverException("Version " + s + " did not match the required pattern to find the numbers within!");
|
||||||
}
|
}
|
||||||
int major = Integer.parseInt(matcher.group(1));
|
int major = Integer.parseInt(matcher.group(1));
|
||||||
int minor = Optional.ofNullable(matcher.group(2)).map(Integer::parseInt).orElse(0);
|
int minor = Optional.ofNullable(matcher.group(2)).map(Integer::parseInt).orElse(0);
|
||||||
|
@ -411,7 +406,7 @@ public class ModDependencyResolver {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static @NotNull List<String> extractRanges(String range) {
|
private static @NotNull List<String> extractRanges(String range) {
|
||||||
if (!range.contains(" ") && !range.contains(" - ") && !range.contains("||")){
|
if (!range.contains(" ") && !range.contains(" - ") && !range.contains("||")) {
|
||||||
return List.of(range);
|
return List.of(range);
|
||||||
}
|
}
|
||||||
List<String> parts = new ArrayList<>();
|
List<String> parts = new ArrayList<>();
|
||||||
|
@ -423,7 +418,7 @@ public class ModDependencyResolver {
|
||||||
}
|
}
|
||||||
List<String> moreParts = new ArrayList<>();
|
List<String> moreParts = new ArrayList<>();
|
||||||
parts.forEach(s -> {
|
parts.forEach(s -> {
|
||||||
if (!s.contains("||")){
|
if (!s.contains("||")) {
|
||||||
moreParts.add(s);
|
moreParts.add(s);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -436,7 +431,7 @@ public class ModDependencyResolver {
|
||||||
});
|
});
|
||||||
List<String> list = new ArrayList<>();
|
List<String> list = new ArrayList<>();
|
||||||
moreParts.forEach(s -> {
|
moreParts.forEach(s -> {
|
||||||
if (!s.contains(" ")){
|
if (!s.contains(" ")) {
|
||||||
list.add(s);
|
list.add(s);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,14 +14,15 @@ import com.electronwill.nightconfig.core.UnmodifiableConfig;
|
||||||
import com.electronwill.nightconfig.core.file.FileNotFoundAction;
|
import com.electronwill.nightconfig.core.file.FileNotFoundAction;
|
||||||
import com.electronwill.nightconfig.toml.TomlParser;
|
import com.electronwill.nightconfig.toml.TomlParser;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import org.ecorous.esnesnon.nonsense.loader.api.mod.*;
|
import org.ecorous.esnesnon.nonsense.loader.api.mod.ModDependencies;
|
||||||
|
import org.ecorous.esnesnon.nonsense.loader.api.mod.ModExtensions;
|
||||||
|
import org.ecorous.esnesnon.nonsense.loader.api.mod.ModProperties;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
public class ModPropertiesReader {
|
public class ModPropertiesReader {
|
||||||
private static final Logger LOGGER = LoggerFactory.getLogger(ModPropertiesReader.class);
|
|
||||||
public static final String PROPERTIES_FILE_NAME = "frog.mod.toml";
|
public static final String PROPERTIES_FILE_NAME = "frog.mod.toml";
|
||||||
|
private static final Logger LOGGER = LoggerFactory.getLogger(ModPropertiesReader.class);
|
||||||
private static final TomlParser PARSER = new TomlParser();
|
private static final TomlParser PARSER = new TomlParser();
|
||||||
|
|
||||||
public static Optional<ModProperties> read(Path mod) {
|
public static Optional<ModProperties> read(Path mod) {
|
||||||
|
@ -61,7 +62,7 @@ public class ModPropertiesReader {
|
||||||
String version = config.get("frog.mod.version");
|
String version = config.get("frog.mod.version");
|
||||||
String license = config.get("frog.mod.license");
|
String license = config.get("frog.mod.license");
|
||||||
|
|
||||||
if (license == null){
|
if (license == null) {
|
||||||
license = "";
|
license = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,12 +105,11 @@ public class ModPropertiesReader {
|
||||||
return new ModPropertiesImpl(id, name, SemVerImpl.parse(version), license, Collections.unmodifiableMap(credits), new ModDependencies(depends, breaks, suggests, provides), ModExtensions.of(extensions));
|
return new ModPropertiesImpl(id, name, SemVerImpl.parse(version), license, Collections.unmodifiableMap(credits), new ModDependencies(depends, breaks, suggests, provides), ModExtensions.of(extensions));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
private static final Map<String, Parser> versions = Arrays.stream(values()).collect(Collectors.toMap(v -> v.version, v -> v.parser));
|
||||||
private final String version;
|
private final String version;
|
||||||
private final Parser parser;
|
private final Parser parser;
|
||||||
|
|
||||||
private static final Map<String, Parser> versions = Arrays.stream(values()).collect(Collectors.toMap(v -> v.version, v -> v.parser));
|
public static Parser get(String version) {
|
||||||
|
|
||||||
public static Parser get(String version){
|
|
||||||
return versions.get(version);
|
return versions.get(version);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,18 +6,18 @@ import org.ecorous.esnesnon.nonsense.loader.api.mod.ModProperties;
|
||||||
|
|
||||||
public class ModUtil {
|
public class ModUtil {
|
||||||
|
|
||||||
public static String getModList(Collection<ModProperties> mods){
|
public static String getModList(Collection<ModProperties> mods) {
|
||||||
StringBuilder builder = new StringBuilder();
|
StringBuilder builder = new StringBuilder();
|
||||||
int size = mods.size();
|
int size = mods.size();
|
||||||
builder.append("Loaded ").append(size).append(" mod");
|
builder.append("Loaded ").append(size).append(" mod");
|
||||||
if (size > 1){
|
if (size > 1) {
|
||||||
builder.append("s");
|
builder.append("s");
|
||||||
}
|
}
|
||||||
builder.append(":");
|
builder.append(":");
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (ModProperties p : mods) {
|
for (ModProperties p : mods) {
|
||||||
builder.append("\n\t");
|
builder.append("\n\t");
|
||||||
if (i < size-1) {
|
if (i < size - 1) {
|
||||||
builder.append("|- ");
|
builder.append("|- ");
|
||||||
} else {
|
} else {
|
||||||
builder.append("\\- ");
|
builder.append("\\- ");
|
||||||
|
|
|
@ -37,10 +37,10 @@ public record SemVerImpl(int major, int minor, int patch, String prerelease, Str
|
||||||
public String toString() {
|
public String toString() {
|
||||||
StringBuilder b = new StringBuilder();
|
StringBuilder b = new StringBuilder();
|
||||||
b.append(major).append(".").append(minor).append(".").append(patch);
|
b.append(major).append(".").append(minor).append(".").append(patch);
|
||||||
if (prerelease != null){
|
if (prerelease != null) {
|
||||||
b.append("-").append(prerelease);
|
b.append("-").append(prerelease);
|
||||||
}
|
}
|
||||||
if (build != null){
|
if (build != null) {
|
||||||
b.append("+").append(build);
|
b.append("+").append(build);
|
||||||
}
|
}
|
||||||
return b.toString();
|
return b.toString();
|
||||||
|
@ -77,13 +77,13 @@ public record SemVerImpl(int major, int minor, int patch, String prerelease, Str
|
||||||
String[] self = prerelease.split("\\.");
|
String[] self = prerelease.split("\\.");
|
||||||
String[] other = o.prerelease().split("\\.");
|
String[] other = o.prerelease().split("\\.");
|
||||||
|
|
||||||
for (int index = 0;index<Math.min(self.length, other.length);index++){
|
for (int index = 0; index < Math.min(self.length, other.length); index++) {
|
||||||
boolean selfNumeric = self[index].matches("\\d+");
|
boolean selfNumeric = self[index].matches("\\d+");
|
||||||
boolean otherNumeric = other[index].matches("\\d+");
|
boolean otherNumeric = other[index].matches("\\d+");
|
||||||
if (selfNumeric != otherNumeric){
|
if (selfNumeric != otherNumeric) {
|
||||||
return selfNumeric ? -1 : 1;
|
return selfNumeric ? -1 : 1;
|
||||||
} else if (!selfNumeric){
|
} else if (!selfNumeric) {
|
||||||
if ((i = self[index].compareTo(other[index])) != 0){
|
if ((i = self[index].compareTo(other[index])) != 0) {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ public interface NonsensePlugin extends Runnable {
|
||||||
/**
|
/**
|
||||||
* @return Whether this plugin is applicable to be loaded in the current environment
|
* @return Whether this plugin is applicable to be loaded in the current environment
|
||||||
*/
|
*/
|
||||||
default boolean isApplicable(){
|
default boolean isApplicable() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,210 @@
|
||||||
|
package org.ecorous.esnesnon.nonsense.loader.impl.plugin.game.minecraft;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.nio.file.FileSystem;
|
||||||
|
import java.nio.file.FileSystems;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ConcurrentSkipListSet;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import org.ecorous.esnesnon.nonsense.loader.api.mod.ModProperties;
|
||||||
|
import org.ecorous.esnesnon.nonsense.loader.impl.mod.BuiltinExtensions;
|
||||||
|
import org.objectweb.asm.*;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Minimal implementation of accesswideners.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class AWProcessor {
|
||||||
|
|
||||||
|
private static final String AW_EXTENSION_NAME = BuiltinExtensions.ACCESSWIDENER;
|
||||||
|
private static final Predicate<String> HEADER = Pattern.compile("accessWidener\\s+v[12]\\s+.*").asMatchPredicate();
|
||||||
|
private static final String SEPARATOR = "[\\t ]+";
|
||||||
|
private static final Logger LOGGER = LoggerFactory.getLogger("AccessWidener");
|
||||||
|
|
||||||
|
static void apply(Collection<ModProperties> mods, Path input, Path output) throws IOException {
|
||||||
|
long startTime = System.currentTimeMillis();
|
||||||
|
Map<String, Entry> classMap = new ConcurrentHashMap<>();
|
||||||
|
Map<String, Map<String, Entry>> methods = new ConcurrentHashMap<>();
|
||||||
|
Map<String, Map<String, Entry>> fields = new ConcurrentHashMap<>();
|
||||||
|
Map<String, Map<String, Entry>> mutations = new ConcurrentHashMap<>();
|
||||||
|
Set<String> classNames = new ConcurrentSkipListSet<>();
|
||||||
|
|
||||||
|
mods.stream().map(ModProperties::extensions).map(e -> (String) e.get(AW_EXTENSION_NAME))
|
||||||
|
.filter(Objects::nonNull).map(s -> "/" + s).map(AWProcessor.class::getResourceAsStream).filter(Objects::nonNull)
|
||||||
|
.map(InputStreamReader::new).map(BufferedReader::new).flatMap(BufferedReader::lines)
|
||||||
|
.map(l -> l.contains("#") ? l.split("#")[0] : l).filter(l -> !l.isBlank())
|
||||||
|
.filter(l -> !HEADER.test(l)).distinct()
|
||||||
|
.map(l -> l.replace("transitive-", "")) // ignore all transitive declarations (just make them normal) as they're only relevant for dev envs
|
||||||
|
.map(l -> l.split(SEPARATOR)).filter(l -> l.length > 0).map(Entry::new).forEach(e -> {
|
||||||
|
classNames.add(e.className);
|
||||||
|
if ("class".equals(e.targetType)) {
|
||||||
|
if (e.type == AccessType.MUTABLE) {
|
||||||
|
throw new IllegalArgumentException("aw format error: classes can not have a 'mutable' modifier (at: " + e + ")");
|
||||||
|
}
|
||||||
|
if (!classMap.containsKey(e.className)) {
|
||||||
|
classMap.put(e.className, e);
|
||||||
|
} else {
|
||||||
|
var other = classMap.get(e.className);
|
||||||
|
if (e.isAccessGreaterThan(other)) {
|
||||||
|
classMap.put(e.className, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if ("method".equals(e.targetType)) {
|
||||||
|
if (e.type == AccessType.MUTABLE) {
|
||||||
|
throw new IllegalArgumentException("aw format error: methods can not have a 'mutable' modifier (at: " + e + ")");
|
||||||
|
}
|
||||||
|
var map = methods.computeIfAbsent(e.className, s -> new ConcurrentHashMap<>());
|
||||||
|
var id = e.name + e.descriptor;
|
||||||
|
if (!map.containsKey(id)) {
|
||||||
|
map.put(id, e);
|
||||||
|
} else {
|
||||||
|
var other = map.get(id);
|
||||||
|
if (e.isAccessGreaterThan(other)) {
|
||||||
|
classMap.put(id, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if ("field".equals(e.targetType)) {
|
||||||
|
if (e.type == AccessType.EXTENDABLE) {
|
||||||
|
throw new IllegalArgumentException("aw format error: fields can not have a 'extendable' modifier (at: " + e + ")");
|
||||||
|
}
|
||||||
|
var map = fields.computeIfAbsent(e.className, s -> new ConcurrentHashMap<>());
|
||||||
|
var id = e.name + e.descriptor;
|
||||||
|
if (e.type == AccessType.MUTABLE) {
|
||||||
|
mutations.computeIfAbsent(e.className, s -> new ConcurrentHashMap<>()).putIfAbsent(id, e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!map.containsKey(id)) {
|
||||||
|
map.put(id, e);
|
||||||
|
} else {
|
||||||
|
var other = map.get(id);
|
||||||
|
if (e.isAccessGreaterThan(other)) {
|
||||||
|
classMap.put(id, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Files.deleteIfExists(output);
|
||||||
|
Files.copy(input, output);
|
||||||
|
|
||||||
|
if (!classNames.isEmpty()) {
|
||||||
|
try (FileSystem out = FileSystems.newFileSystem(output)) {
|
||||||
|
|
||||||
|
for (String name : classNames) {
|
||||||
|
processFile(name, out.getPath("/" + name + ".class"), classMap, fields, methods, mutations);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LOGGER.info(String.format("Applied AccessWideners in %.2fs", (System.currentTimeMillis() - startTime) / 1000f));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void processFile(String className, Path file, Map<String, Entry> classMap, Map<String, Map<String, Entry>> fields, Map<String, Map<String, Entry>> methods, Map<String, Map<String, Entry>> mutations) throws IOException {
|
||||||
|
ClassReader reader = new ClassReader(Files.newInputStream(file));
|
||||||
|
ClassWriter writer = new ClassWriter(0);
|
||||||
|
ClassVisitor mapper = new ClassVisitor(Opcodes.ASM9, writer) {
|
||||||
|
@Override
|
||||||
|
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
|
||||||
|
Entry e = classMap.get(className);
|
||||||
|
if (e != null) {
|
||||||
|
access &= ~(Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED | Opcodes.ACC_PUBLIC);
|
||||||
|
access |= e.type.access;
|
||||||
|
}
|
||||||
|
if (fields.containsKey(className) || methods.containsKey(className) || mutations.containsKey(className)) { // make all classes with modifications public as well
|
||||||
|
access &= ~(Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED | Opcodes.ACC_PUBLIC);
|
||||||
|
access |= Opcodes.ACC_PUBLIC;
|
||||||
|
}
|
||||||
|
super.visit(version, access, name, signature, superName, interfaces);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {
|
||||||
|
Map<String, Entry> map = fields.get(className);
|
||||||
|
if (map != null) {
|
||||||
|
Entry e = map.get(name + descriptor);
|
||||||
|
if (e != null) {
|
||||||
|
access &= ~(Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED | Opcodes.ACC_PUBLIC); // remove all access modifiers
|
||||||
|
access |= e.type.access; // re-add the new one
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((map = mutations.get(className)) != null) {
|
||||||
|
var e = map.get(name + descriptor);
|
||||||
|
if (e != null) {
|
||||||
|
access &= ~Opcodes.ACC_FINAL; // always AccessType.MUTABLE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return super.visitField(access, name, descriptor, signature, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
|
||||||
|
Map<String, Entry> map = methods.get(className);
|
||||||
|
if (map != null) {
|
||||||
|
Entry e = map.get(name + descriptor);
|
||||||
|
if (e != null) {
|
||||||
|
access &= ~(Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED | Opcodes.ACC_PUBLIC);
|
||||||
|
access |= e.type.access;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return super.visitMethod(access, name, descriptor, signature, exceptions);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
reader.accept(mapper, 0);
|
||||||
|
|
||||||
|
Files.write(file, writer.toByteArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
private record Entry(AccessType type, String targetType, String className, String name, String descriptor) {
|
||||||
|
|
||||||
|
public Entry(String[] line) {
|
||||||
|
this(AccessType.of(line[0]), line[1], line[2], line[3], line[4]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isAccessGreaterThan(Entry other) {
|
||||||
|
return Access.of(type().access).index < Access.of(other.type.access).index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@AllArgsConstructor
|
||||||
|
private enum AccessType {
|
||||||
|
ACCESSIBLE("accessible", Opcodes.ACC_PUBLIC),
|
||||||
|
EXTENDABLE("extendable", Opcodes.ACC_PROTECTED),
|
||||||
|
MUTABLE("mutable", ~Opcodes.ACC_FINAL);
|
||||||
|
private final String id;
|
||||||
|
private final int access;
|
||||||
|
|
||||||
|
public static AccessType of(String name) {
|
||||||
|
return Arrays.stream(values()).filter(a -> a.id.equals(name)).findFirst().orElseThrow(() -> new IllegalStateException("Unknown access type: " + name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@AllArgsConstructor
|
||||||
|
private enum Access {
|
||||||
|
PUBLIC(1), PROTECTED(2), PACKAGE_PRIVATE(3), PRIVATE(4);
|
||||||
|
private final int index;
|
||||||
|
|
||||||
|
public static Access of(int access) {
|
||||||
|
if ((access & Opcodes.ACC_PUBLIC) != 0) {
|
||||||
|
return PUBLIC;
|
||||||
|
}
|
||||||
|
if ((access & Opcodes.ACC_PROTECTED) != 0) {
|
||||||
|
return PROTECTED;
|
||||||
|
}
|
||||||
|
if ((access & Opcodes.ACC_PRIVATE) != 0) {
|
||||||
|
return PRIVATE;
|
||||||
|
}
|
||||||
|
return PACKAGE_PRIVATE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,7 +12,9 @@ import java.util.*;
|
||||||
|
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import org.ecorous.esnesnon.nonsense.loader.api.extensions.PreLaunchExtension;
|
import org.ecorous.esnesnon.nonsense.loader.api.extensions.PreLaunchExtension;
|
||||||
import org.ecorous.esnesnon.nonsense.loader.api.mod.*;
|
import org.ecorous.esnesnon.nonsense.loader.api.mod.ModDependencies;
|
||||||
|
import org.ecorous.esnesnon.nonsense.loader.api.mod.ModExtensions;
|
||||||
|
import org.ecorous.esnesnon.nonsense.loader.api.mod.ModProperties;
|
||||||
import org.ecorous.esnesnon.nonsense.loader.impl.Discovery;
|
import org.ecorous.esnesnon.nonsense.loader.impl.Discovery;
|
||||||
import org.ecorous.esnesnon.nonsense.loader.impl.LoaderImpl;
|
import org.ecorous.esnesnon.nonsense.loader.impl.LoaderImpl;
|
||||||
import org.ecorous.esnesnon.nonsense.loader.impl.mod.BuiltinExtensions;
|
import org.ecorous.esnesnon.nonsense.loader.impl.mod.BuiltinExtensions;
|
||||||
|
@ -27,17 +29,16 @@ import org.spongepowered.asm.mixin.Mixins;
|
||||||
|
|
||||||
public class Minecraft implements NonsensePlugin {
|
public class Minecraft implements NonsensePlugin {
|
||||||
|
|
||||||
private static final Logger LOGGER = LoggerFactory.getLogger("Plugin/Minecraft");
|
|
||||||
protected static final String[] MINECRAFT_CLASSES = new String[]{
|
protected static final String[] MINECRAFT_CLASSES = new String[]{
|
||||||
"net/minecraft/client/main/Main.class",
|
"net/minecraft/client/main/Main.class",
|
||||||
"net/minecraft/client/MinecraftApplet.class",
|
"net/minecraft/client/MinecraftApplet.class",
|
||||||
"net/minecraft/server/Main.class"
|
"net/minecraft/server/Main.class"
|
||||||
};
|
};
|
||||||
|
private static final Logger LOGGER = LoggerFactory.getLogger("Plugin/Minecraft");
|
||||||
protected final Collection<ModProperties> modProperties = new ArrayList<>();
|
protected final Collection<ModProperties> modProperties = new ArrayList<>();
|
||||||
private String version;
|
|
||||||
protected Path gamePath;
|
protected Path gamePath;
|
||||||
protected String foundMainClass;
|
protected String foundMainClass;
|
||||||
|
private String version;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isApplicable() {
|
public boolean isApplicable() {
|
||||||
|
@ -47,7 +48,7 @@ public class Minecraft implements NonsensePlugin {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init(LoaderImpl loader) throws Exception {
|
public void init(LoaderImpl loader) throws Exception {
|
||||||
if (gamePath == null){
|
if (gamePath == null) {
|
||||||
throw new IllegalStateException("Game not found yet!");
|
throw new IllegalStateException("Game not found yet!");
|
||||||
}
|
}
|
||||||
Path remappedGamePath = loader.getGameDir().resolve(".nonsense/remappedJars").resolve(version).resolve("game-" + version + "-remapped.jar");
|
Path remappedGamePath = loader.getGameDir().resolve(".nonsense/remappedJars").resolve(version).resolve("game-" + version + "-remapped.jar");
|
||||||
|
@ -80,7 +81,7 @@ public class Minecraft implements NonsensePlugin {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
modProperties.retainAll(new ModDependencyResolver(modProperties).solve());
|
modProperties.retainAll(new ModDependencyResolver(modProperties).solve());
|
||||||
} catch (ModDependencyResolver.BreakingModException e){
|
} catch (ModDependencyResolver.BreakingModException e) {
|
||||||
// TODO handle
|
// TODO handle
|
||||||
} catch (ModDependencyResolver.UnfulfilledDependencyException e) {
|
} catch (ModDependencyResolver.UnfulfilledDependencyException e) {
|
||||||
// TODO handle (and display)
|
// TODO handle (and display)
|
||||||
|
@ -120,10 +121,10 @@ public class Minecraft implements NonsensePlugin {
|
||||||
modProperties.add(p);
|
modProperties.add(p);
|
||||||
modPaths.put(mod, p);
|
modPaths.put(mod, p);
|
||||||
List<List<Map<String, String>>> entries = p.extensions().getOrDefault(BuiltinExtensions.INCLUDED_JARS, Collections.emptyList());
|
List<List<Map<String, String>>> entries = p.extensions().getOrDefault(BuiltinExtensions.INCLUDED_JARS, Collections.emptyList());
|
||||||
if (entries.isEmpty()){
|
if (entries.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try (FileSystem fs = FileSystems.newFileSystem(mod)){
|
try (FileSystem fs = FileSystems.newFileSystem(mod)) {
|
||||||
for (var jars : entries) {
|
for (var jars : entries) {
|
||||||
for (Map<String, String> jar : jars) {
|
for (Map<String, String> jar : jars) {
|
||||||
Path path = fs.getPath(jar.get("path")).toAbsolutePath();
|
Path path = fs.getPath(jar.get("path")).toAbsolutePath();
|
||||||
|
|
|
@ -8,11 +8,12 @@ import org.jetbrains.annotations.NotNull;
|
||||||
public class MinecraftSemVerImpl implements SemVer {
|
public class MinecraftSemVerImpl implements SemVer {
|
||||||
|
|
||||||
private final String version;
|
private final String version;
|
||||||
private MinecraftSemVerImpl(String version){
|
|
||||||
|
private MinecraftSemVerImpl(String version) {
|
||||||
this.version = version;
|
this.version = version;
|
||||||
}
|
}
|
||||||
|
|
||||||
static SemVer get(String version){
|
static SemVer get(String version) {
|
||||||
try {
|
try {
|
||||||
return SemVerImpl.parse(version);
|
return SemVerImpl.parse(version);
|
||||||
} catch (SemVerParseException e) {
|
} catch (SemVerParseException e) {
|
||||||
|
@ -22,17 +23,17 @@ public class MinecraftSemVerImpl implements SemVer {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int major() {
|
public int major() {
|
||||||
throw new UnsupportedOperationException("Minecraft version "+version+" does not represent a semver-compatible version");
|
throw new UnsupportedOperationException("Minecraft version " + version + " does not represent a semver-compatible version");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int minor() {
|
public int minor() {
|
||||||
throw new UnsupportedOperationException("Minecraft version "+version+" does not represent a semver-compatible version");
|
throw new UnsupportedOperationException("Minecraft version " + version + " does not represent a semver-compatible version");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int patch() {
|
public int patch() {
|
||||||
throw new UnsupportedOperationException("Minecraft version "+version+" does not represent a semver-compatible version");
|
throw new UnsupportedOperationException("Minecraft version " + version + " does not represent a semver-compatible version");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
Loading…
Reference in a new issue