diff --git a/.gitignore b/.gitignore index 3c37caf..a61caf3 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ *.iws # IntelliJ +out.map out/ # mpeltonen/sbt-idea plugin .idea_modules/ diff --git a/build.gradle b/build.gradle index 4894144..15bb424 100644 --- a/build.gradle +++ b/build.gradle @@ -1,3 +1,20 @@ +import groovy.json.JsonOutput +import groovy.json.JsonSlurper +import proguard.gradle.ProGuardTask +import net.fabricmc.loom.task.RunClientTask +import net.fabricmc.loom.task.RunServerTask + +buildscript { + repositories { + mavenLocal() + google() + } + + dependencies { + classpath 'com.guardsquare:proguard-gradle:7.0.1' + } +} + plugins { id 'fabric-loom' version '0.7-SNAPSHOT' id 'maven-publish' @@ -12,6 +29,22 @@ group = project.maven_group repositories { maven { url = 'https://maven.terraformersmc.com/'; name = "ModMenu" } + maven { url "https://maven.shedaniel.me/" } +} + +sourceSets { + testmod { + compileClasspath += main.compileClasspath + main.output + runtimeClasspath += main.runtimeClasspath + main.output + } +} + +task runTestmodClient(type: RunClientTask) { + classpath sourceSets.testmod.runtimeClasspath +} + +task runTestmodServer(type: RunServerTask) { + classpath sourceSets.testmod.runtimeClasspath } dependencies { @@ -24,6 +57,7 @@ dependencies { // PSA: Some older mods, compiled on Loom 0.2.1, might have outdated Maven POMs. // You may need to force-disable transitiveness on them. modImplementation "com.terraformersmc:modmenu:1.16.9" + include modImplementation("me.shedaniel.cloth:cloth-config-lite-fabric:1.0.4") } processResources { @@ -37,6 +71,29 @@ processResources { from(sourceSets.main.resources.srcDirs) { exclude "fabric.mod.json" } + + doLast { + fileTree(dir: outputs.files.asPath, include: "**/*.json").each { + File file -> file.text = JsonOutput.toJson(new JsonSlurper().parse(file)) + } + } +} + +def proguardJarOut = file(jar.archiveFile.get().getAsFile().absolutePath.replace(".jar", "-min.jar")) + +task proguardJar(type: ProGuardTask, dependsOn: jar) { + configuration './proguard.conf' + verbose + injars jar.archiveFile.get().getAsFile() + outjars proguardJarOut + libraryjars files(configurations.compileClasspath) + printmapping 'out.map' +} + +tasks.remapJar { + input.set proguardJarOut + archiveClassifier.set null + dependsOn proguardJar } // ensure that the encoding is set to UTF-8, no matter what the system default is diff --git a/proguard.conf b/proguard.conf new file mode 100644 index 0000000..f634b59 --- /dev/null +++ b/proguard.conf @@ -0,0 +1,11 @@ +-libraryjars /jmods/java.base.jmod(!**.jar;!module-info.class) +-keep public class io.gitlab.jfronny.libjf.config.JfConfig { + public protected *; +} +-keep public class io.gitlab.jfronny.libjf.Libjf +-keep public class io.gitlab.jfronny.libjf.config.ModMenu +-repackageclasses 'jf.lib' +-allowaccessmodification +-overloadaggressively +-optimizationpasses 5 +-optimizations '*' \ No newline at end of file diff --git a/src/main/java/io/gitlab/jfronny/libjf/ExampleConfig.java b/src/main/java/io/gitlab/jfronny/libjf/ExampleConfig.java deleted file mode 100644 index a2c2249..0000000 --- a/src/main/java/io/gitlab/jfronny/libjf/ExampleConfig.java +++ /dev/null @@ -1,36 +0,0 @@ -package io.gitlab.jfronny.libjf; - -import io.gitlab.jfronny.libjf.config.Entry; -import io.gitlab.jfronny.libjf.config.EntryInfo; -import io.gitlab.jfronny.libjf.config.JfConfig; -import net.minecraft.text.LiteralText; -import net.minecraft.text.Text; - -import java.util.Collections; -import java.util.List; - -/*from https://github.com/Minenash/TinyConfig*/ -public class ExampleConfig implements JfConfig { - @Entry - public static boolean boolTest = false; - - @Entry(min = 5) - public static int intTest = 20; - - @Entry - public static double decimalTest = 20; - - @Entry(dynamicTooltip = "dieStrTooltip", max = 5) - public static String dieStr = "lolz"; - - @Entry - public static Test enumTest = Test.Test; - - private static List dieStrTooltip(List infos) { - return Collections.singletonList(new LiteralText("safbjkasf")); - } - - public enum Test { - Test, ER - } -} diff --git a/src/main/java/io/gitlab/jfronny/libjf/Libjf.java b/src/main/java/io/gitlab/jfronny/libjf/Libjf.java index e1d3573..3d2f3da 100644 --- a/src/main/java/io/gitlab/jfronny/libjf/Libjf.java +++ b/src/main/java/io/gitlab/jfronny/libjf/Libjf.java @@ -27,7 +27,6 @@ public class Libjf implements PreLaunchEntrypoint { @Override public void onPreLaunch() { - registerConfig(MOD_ID, ExampleConfig.class); for (EntrypointContainer entrypointContainer : FabricLoader.getInstance().getEntrypointContainers(MOD_ID + ":config", JfConfig.class)) { String id = entrypointContainer.getProvider().getMetadata().getId(); Libjf.registerConfig(id, entrypointContainer.getEntrypoint().getClass()); diff --git a/src/main/java/io/gitlab/jfronny/libjf/config/Config.java b/src/main/java/io/gitlab/jfronny/libjf/config/Config.java index a15e071..cc47629 100644 --- a/src/main/java/io/gitlab/jfronny/libjf/config/Config.java +++ b/src/main/java/io/gitlab/jfronny/libjf/config/Config.java @@ -2,30 +2,14 @@ package io.gitlab.jfronny.libjf.config; import io.gitlab.jfronny.libjf.Libjf; import net.fabricmc.loader.api.FabricLoader; -import net.minecraft.client.gui.widget.ButtonWidget; -import net.minecraft.client.gui.widget.TextFieldWidget; -import net.minecraft.text.*; -import net.minecraft.util.Formatting; import java.lang.reflect.Field; import java.nio.file.Files; import java.nio.file.Path; import java.util.*; -import java.util.function.BiFunction; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.regex.Pattern; -/* Based on https://github.com/TeamMidnightDust/MidnightLib -* see https://github.com/TeamMidnightDust/MidnightLib/blob/main/LICENSE -*/ - -@SuppressWarnings("rawtypes") public class Config { - private static final Pattern INTEGER_ONLY = Pattern.compile("(-?[0-9]*)"); - private static final Pattern DECIMAL_ONLY = Pattern.compile("-?([\\d]+\\.?[\\d]*|[\\d]*\\.?[\\d]+|\\.)"); - - public Class configClass; + public Class configClass; public String translationPrefix; public Path path; public final List entries = new ArrayList<>(); @@ -36,49 +20,13 @@ public class Config { configClass = config; for (Field field : config.getFields()) { - Class type = field.getType(); EntryInfo info = new EntryInfo(); - Entry e; - try { e = field.getAnnotation(Entry.class); } - catch (Exception ignored) { continue; } - - info.width = e.width(); info.field = field; - - if (type == int.class) textField(info, Integer::parseInt, INTEGER_ONLY, e.min(), e.max(), true); - else if (type == double.class) textField(info, Double::parseDouble, DECIMAL_ONLY, e.min(), e.max(),false); - else if (type == float.class) textField(info, Float::parseFloat, DECIMAL_ONLY, e.min(), e.max(),false); - else if (type == String.class) textField(info, String::length, null, Math.min(e.min(),0), Math.max(e.max(),1),true); - else if (type == boolean.class) { - Function func = value -> new LiteralText((Boolean) value ? "True" : "False").formatted((Boolean) value ? Formatting.GREEN : Formatting.RED); - info.widget = new AbstractMap.SimpleEntry>(button -> { - info.value = !(Boolean) info.value; - button.setMessage(func.apply(info.value)); - }, func); - } - else if (type.isEnum()) { - List values = Arrays.asList(field.getType().getEnumConstants()); - Function func = value -> new TranslatableText(translationPrefix + "enum." + type.getSimpleName() + "." + info.value.toString()); - info.widget = new AbstractMap.SimpleEntry>( button -> { - int index = values.indexOf(info.value) + 1; - info.value = values.get(index >= values.size()? 0 : index); - button.setMessage(func.apply(info.value)); - }, func); - } - else - continue; - entries.add(info); try { info.defaultValue = field.get(null); } catch (IllegalAccessException ignored) {} - - try { - info.dynamicTooltip = config.getMethod(e.dynamicTooltip()); - info.dynamicTooltip.setAccessible(true); - } catch (Exception ignored) {} - } try { Libjf.GSON.fromJson(Files.newBufferedReader(path), config); } @@ -87,48 +35,15 @@ public class Config { for (EntryInfo info : entries) { try { info.value = info.field.get(null); - info.tempValue = info.value.toString(); } catch (IllegalAccessException ignored) {} } } - private void textField(EntryInfo info, Function f, Pattern pattern, double min, double max, boolean cast) { - boolean isNumber = pattern != null; - info.widget = (BiFunction>) (t, b) -> s -> { - s = s.trim(); - if (!(s.isEmpty() || !isNumber || pattern.matcher(s).matches())) - return false; - - Number value = 0; - boolean inLimits = false; - System.out.println(((isNumber ^ s.isEmpty()))); - System.out.println(!s.equals("-") && !s.equals(".")); - info.error = null; - if (!(isNumber && s.isEmpty()) && !s.equals("-") && !s.equals(".")) { - value = f.apply(s); - inLimits = value.doubleValue() >= min && value.doubleValue() <= max; - info.error = inLimits? null : new AbstractMap.SimpleEntry<>(t, new LiteralText(value.doubleValue() < min ? - "§cMinimum " + (isNumber? "value" : "length") + (cast? " is " + (int)min : " is " + min) : - "§cMaximum " + (isNumber? "value" : "length") + (cast? " is " + (int)max : " is " + max))); - } - - info.tempValue = s; - t.setEditableColor(inLimits? 0xFFFFFFFF : 0xFFFF7777); - info.inLimits = inLimits; - b.active = entries.stream().allMatch(e -> e.inLimits); - - if (inLimits) - info.value = isNumber? value : s; - - return true; - }; - } - public void write() { try { if (!Files.exists(path)) Files.createFile(path); - Files.write(path, Libjf.GSON.toJson(configClass.newInstance()).getBytes()); + Files.write(path, Libjf.GSON.toJson(configClass.getDeclaredConstructor().newInstance()).getBytes()); } catch (Exception e) { e.printStackTrace(); } diff --git a/src/main/java/io/gitlab/jfronny/libjf/config/ConfigScreen.java b/src/main/java/io/gitlab/jfronny/libjf/config/ConfigScreen.java deleted file mode 100644 index 50066f7..0000000 --- a/src/main/java/io/gitlab/jfronny/libjf/config/ConfigScreen.java +++ /dev/null @@ -1,132 +0,0 @@ -package io.gitlab.jfronny.libjf.config; - -import io.gitlab.jfronny.libjf.Libjf; -import net.minecraft.client.gui.screen.Screen; -import net.minecraft.client.gui.screen.ScreenTexts; -import net.minecraft.client.gui.widget.ButtonWidget; -import net.minecraft.client.gui.widget.TextFieldWidget; -import net.minecraft.client.resource.language.I18n; -import net.minecraft.client.util.math.MatrixStack; -import net.minecraft.text.LiteralText; -import net.minecraft.text.Text; -import net.minecraft.text.TranslatableText; -import net.minecraft.util.Formatting; - -import java.nio.file.Files; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.function.BiFunction; -import java.util.function.Function; -import java.util.function.Predicate; - -public class ConfigScreen extends Screen { - public ConfigScreen(Screen parent, Config config) { - super(new TranslatableText(config.translationPrefix + "title")); - this.parent = parent; - this.config = config; - } - private final Screen parent; - private final Config config; - - // Real Time config update // - @Override - public void tick() { - for (EntryInfo info : config.entries) - try { info.field.set(null, info.value); } - catch (IllegalAccessException ignore) {} - } - - @Override - protected void init() { - super.init(); - this.addButton(new ButtonWidget(this.width / 2 - 154, this.height - 28, 150, 20, ScreenTexts.CANCEL, button -> { - try { Libjf.GSON.fromJson(Files.newBufferedReader(config.path), config.configClass); } - catch (Exception e) { config.write(); } - - for (EntryInfo info : config.entries) { - try { - info.value = info.field.get(null); - info.tempValue = info.value.toString(); - } - catch (IllegalAccessException ignored) {} - } - Objects.requireNonNull(client).openScreen(parent); - })); - - ButtonWidget done = this.addButton(new ButtonWidget(this.width / 2 + 4, this.height - 28, 150, 20, ScreenTexts.DONE, (button) -> { - for (EntryInfo info : config.entries) - try { info.field.set(null, info.value); } - catch (IllegalAccessException ignore) {} - config.write(); - Objects.requireNonNull(client).openScreen(parent); - })); - - int y = 45; - for (EntryInfo info : config.entries) { - addButton(new ButtonWidget(width - 155, y, 40,20, new LiteralText("Reset").formatted(Formatting.RED), (button -> { - info.value = info.defaultValue; - info.tempValue = info.value.toString(); - Objects.requireNonNull(client).openScreen(this); - }))); - - if (info.widget instanceof Map.Entry) { - Map.Entry> widget = (Map.Entry>) info.widget; - addButton(new ButtonWidget(width-110,y,info.width,20, widget.getValue().apply(info.value), widget.getKey())); - } - else { - TextFieldWidget widget = addButton(new TextFieldWidget(textRenderer, width-110, y, info.width, 20, null)); - widget.setText(info.tempValue); - - Predicate processor = ((BiFunction>) info.widget).apply(widget,done); - widget.setTextPredicate(processor); - - children.add(widget); - } - y += 25; - } - - } - int aniX = this.width / 2; - @Override - public void render(MatrixStack matrices, int mouseX, int mouseY, float delta) { - this.renderBackground(matrices); - - if (aniX < this.width / 2) { - aniX = aniX +40; - } - - int stringWidth = (int) (title.getString().length() * 2.75f); - this.fillGradient(matrices, this.width / 2 - stringWidth, 10, this.width /2 + stringWidth, 29, -1072689136, -804253680); - this.fillGradient(matrices, this.width / 2 - aniX, 35, width/2 + aniX, this.height - 40, -1072689136, -804253680); - - super.render(matrices, mouseX, mouseY, delta); - drawCenteredText(matrices, textRenderer, title, width/2, 15, 0xFFFFFF); - - int y = 40; - for (EntryInfo info : config.entries) { - drawTextWithShadow(matrices, textRenderer, new TranslatableText(config.translationPrefix + info.field.getName()), 12, y + 10, 0xFFFFFF); - - if (info.error != null && info.error.getKey().isMouseOver(mouseX,mouseY)) - renderTooltip(matrices, info.error.getValue(), mouseX, mouseY); - else if (mouseY >= y && mouseY < (y + 25)) { - if (info.dynamicTooltip != null) { - try { - renderTooltip(matrices, (List) info.dynamicTooltip.invoke(null, config.entries), mouseX, mouseY); - y += 25; - continue; - } catch (Exception e) { e.printStackTrace(); } - } - String key = config.translationPrefix + info.field.getName() + ".tooltip"; - if (I18n.hasTranslation(key)) { - List list = new ArrayList<>(); - for (String str : I18n.translate(key).split("\n")) - list.add(new LiteralText(str)); - renderTooltip(matrices, list, mouseX, mouseY); - } - } - y += 25; - } - } -} diff --git a/src/main/java/io/gitlab/jfronny/libjf/config/Entry.java b/src/main/java/io/gitlab/jfronny/libjf/config/Entry.java deleted file mode 100644 index 23ffe0f..0000000 --- a/src/main/java/io/gitlab/jfronny/libjf/config/Entry.java +++ /dev/null @@ -1,15 +0,0 @@ -package io.gitlab.jfronny.libjf.config; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.FIELD) -public @interface Entry { - String dynamicTooltip() default ""; - int width() default 100; - double min() default Double.MIN_NORMAL; - double max() default Double.MAX_VALUE; -} \ No newline at end of file diff --git a/src/main/java/io/gitlab/jfronny/libjf/config/EntryInfo.java b/src/main/java/io/gitlab/jfronny/libjf/config/EntryInfo.java index 512935e..ebc7def 100644 --- a/src/main/java/io/gitlab/jfronny/libjf/config/EntryInfo.java +++ b/src/main/java/io/gitlab/jfronny/libjf/config/EntryInfo.java @@ -1,20 +1,9 @@ package io.gitlab.jfronny.libjf.config; -import net.minecraft.client.gui.widget.TextFieldWidget; -import net.minecraft.text.Text; - import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.util.Map; public class EntryInfo { Field field; - Object widget; - int width; - Method dynamicTooltip; - Map.Entry error; Object defaultValue; Object value; - String tempValue; - boolean inLimits = true; } diff --git a/src/main/java/io/gitlab/jfronny/libjf/config/ModMenu.java b/src/main/java/io/gitlab/jfronny/libjf/config/ModMenu.java index ead1ff2..8470316 100644 --- a/src/main/java/io/gitlab/jfronny/libjf/config/ModMenu.java +++ b/src/main/java/io/gitlab/jfronny/libjf/config/ModMenu.java @@ -3,6 +3,8 @@ package io.gitlab.jfronny.libjf.config; import com.terraformersmc.modmenu.api.ConfigScreenFactory; import com.terraformersmc.modmenu.api.ModMenuApi; import io.gitlab.jfronny.libjf.Libjf; +import me.shedaniel.clothconfiglite.api.ConfigScreen; +import net.minecraft.text.TranslatableText; import java.util.HashMap; import java.util.Map; @@ -13,18 +15,22 @@ public class ModMenu implements ModMenuApi { Map> factories = new HashMap<>(); for (Map.Entry entry : Libjf.getConfigs().entrySet()) { if (!Libjf.MOD_ID.equals(entry.getKey())) - factories.put(entry.getKey(), s -> new ConfigScreen(s, entry.getValue())); + factories.put(entry.getKey(), buildFactory(entry.getValue())); } return factories; } - @Override - public ConfigScreenFactory getModConfigScreenFactory() { - for (Map.Entry entry : Libjf.getConfigs().entrySet()) { - if (Libjf.MOD_ID.equals(entry.getKey())) - return s -> new ConfigScreen(s, entry.getValue()); - } - new Exception("Could not find own config screen, this is bad").printStackTrace(); - return null; + private static ConfigScreenFactory buildFactory(Config config) { + return s -> { + ConfigScreen c = ConfigScreen.create(new TranslatableText(config.translationPrefix + "title"), s); + for (EntryInfo entry : config.entries) { + c.add(new TranslatableText(config.translationPrefix + entry.field.getName()), + entry.value, () -> entry.defaultValue, v -> { + entry.value = v; + config.write(); + }); + } + return c.get(); + }; } } diff --git a/src/main/resources/assets/libjf/lang/en_us.json b/src/main/resources/assets/libjf/lang/en_us.json deleted file mode 100644 index 5c48476..0000000 --- a/src/main/resources/assets/libjf/lang/en_us.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "libjf.jfconfig.title": "JfConfig example", - "libjf.jfconfig.boolTest": "Bool Test", - "libjf.jfconfig.intTest": "Int Test", - "libjf.jfconfig.decimalTest": "Decimal Test", - "libjf.jfconfig.dieStr": "String Test", - "libjf.jfconfig.enumTest": "Enum Test", - "libjf.jfconfig.enum.Test.Test": "Test", - "libjf.jfconfig.enum.Test.ER": "ER" -} \ No newline at end of file diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 4352455..a574bb5 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -20,9 +20,6 @@ ], "modmenu": [ "io.gitlab.jfronny.libjf.config.ModMenu" - ], - "libjf:config": [ - "io.gitlab.jfronny.libjf.ExampleConfig" ] }, "mixins": [ @@ -31,5 +28,10 @@ "depends": { "fabricloader": ">=0.11.3", "minecraft": "1.16.5" + }, + "custom": { + "modmenu": { + "badges": ["library"] + } } } diff --git a/src/testmod/java/io/gitlab/jfronny/libjf/test/TestMod.java b/src/testmod/java/io/gitlab/jfronny/libjf/test/TestMod.java new file mode 100644 index 0000000..0cff6ad --- /dev/null +++ b/src/testmod/java/io/gitlab/jfronny/libjf/test/TestMod.java @@ -0,0 +1,15 @@ +package io.gitlab.jfronny.libjf.test; + +import io.gitlab.jfronny.libjf.config.JfConfig; + +public class TestMod implements JfConfig { + public static boolean boolTest = false; + public static int intTest = 20; + public static double decimalTest = 20; + public static String dieStr = "lolz"; + public static Test enumTest = Test.Test; + + public enum Test { + Test, ER + } +} diff --git a/src/testmod/resources/assets/libjf-testmod/lang/en_us.json b/src/testmod/resources/assets/libjf-testmod/lang/en_us.json new file mode 100644 index 0000000..f1b46ae --- /dev/null +++ b/src/testmod/resources/assets/libjf-testmod/lang/en_us.json @@ -0,0 +1,8 @@ +{ + "libjf-testmod.jfconfig.title": "JfConfig example", + "libjf-testmod.jfconfig.boolTest": "Bool Test", + "libjf-testmod.jfconfig.intTest": "Int Test", + "libjf-testmod.jfconfig.decimalTest": "Decimal Test", + "libjf-testmod.jfconfig.dieStr": "String Test", + "libjf-testmod.jfconfig.enumTest": "Enum Test" +} \ No newline at end of file diff --git a/src/testmod/resources/fabric.mod.json b/src/testmod/resources/fabric.mod.json new file mode 100644 index 0000000..94f8039 --- /dev/null +++ b/src/testmod/resources/fabric.mod.json @@ -0,0 +1,11 @@ +{ + "schemaVersion": 1, + "id": "libjf-testmod", + "version": "${version}", + "environment": "*", + "entrypoints": { + "libjf:config": [ + "io.gitlab.jfronny.libjf.test.TestMod" + ] + } +} \ No newline at end of file