diff --git a/common/src/main/java/com/teamresourceful/resourcefulconfig/api/annotations/ConfigEntry.java b/common/src/main/java/com/teamresourceful/resourcefulconfig/api/annotations/ConfigEntry.java index 92e45f7..03b6ef6 100644 --- a/common/src/main/java/com/teamresourceful/resourcefulconfig/api/annotations/ConfigEntry.java +++ b/common/src/main/java/com/teamresourceful/resourcefulconfig/api/annotations/ConfigEntry.java @@ -1,6 +1,7 @@ package com.teamresourceful.resourcefulconfig.api.annotations; import com.teamresourceful.resourcefulconfig.api.types.options.EntryType; +import org.jetbrains.annotations.ApiStatus; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; @@ -13,7 +14,12 @@ String id(); - EntryType type(); + /** + * @deprecated Type is no longer required and it will be inferred instead of parsing. + */ + @Deprecated + @ApiStatus.ScheduledForRemoval(inVersion = "1.21.2") + EntryType type() default EntryType.CANNOT_BE_PARSED; String translation() default ""; } diff --git a/common/src/main/java/com/teamresourceful/resourcefulconfig/api/loader/Configurator.java b/common/src/main/java/com/teamresourceful/resourcefulconfig/api/loader/Configurator.java index dc6569b..1cc539f 100644 --- a/common/src/main/java/com/teamresourceful/resourcefulconfig/api/loader/Configurator.java +++ b/common/src/main/java/com/teamresourceful/resourcefulconfig/api/loader/Configurator.java @@ -58,8 +58,12 @@ private ResourcefulConfig loadConfigClass(Class clazz, Consumer entries(); + default Object instance() { + return null; + } + } diff --git a/common/src/main/java/com/teamresourceful/resourcefulconfig/api/types/info/Translatable.java b/common/src/main/java/com/teamresourceful/resourcefulconfig/api/types/info/Translatable.java index f2c2252..2a8423b 100644 --- a/common/src/main/java/com/teamresourceful/resourcefulconfig/api/types/info/Translatable.java +++ b/common/src/main/java/com/teamresourceful/resourcefulconfig/api/types/info/Translatable.java @@ -25,4 +25,14 @@ static Component toComponent(Object value, Component defaultValue) { } return Component.literal(Objects.toString(value)); } + + static Component toSpeifiedComponent(Object value, Component specified) { + if (value instanceof Translatable translatable) { + return Component.translatable(translatable.getTranslationKey()); + } + if (value instanceof StringRepresentable string) { + return Component.literal(string.getSerializedName()); + } + return specified; + } } diff --git a/common/src/main/java/com/teamresourceful/resourcefulconfig/api/types/options/EntryType.java b/common/src/main/java/com/teamresourceful/resourcefulconfig/api/types/options/EntryType.java index f6d1532..af2a2e8 100644 --- a/common/src/main/java/com/teamresourceful/resourcefulconfig/api/types/options/EntryType.java +++ b/common/src/main/java/com/teamresourceful/resourcefulconfig/api/types/options/EntryType.java @@ -14,7 +14,9 @@ public enum EntryType { BOOLEAN(type -> type == boolean.class || type == Boolean.class), STRING(type -> type == String.class), ENUM(Class::isEnum), - OBJECT(type -> type.isAnnotationPresent(ConfigObject.class)) + OBJECT(type -> type.isAnnotationPresent(ConfigObject.class)), + + CANNOT_BE_PARSED(type -> false) ; private final Predicate> predicate; diff --git a/common/src/main/java/com/teamresourceful/resourcefulconfig/client/components/options/types/ObjectOptionWidget.java b/common/src/main/java/com/teamresourceful/resourcefulconfig/client/components/options/types/ObjectOptionWidget.java index e5923d1..f27de40 100644 --- a/common/src/main/java/com/teamresourceful/resourcefulconfig/client/components/options/types/ObjectOptionWidget.java +++ b/common/src/main/java/com/teamresourceful/resourcefulconfig/client/components/options/types/ObjectOptionWidget.java @@ -1,6 +1,7 @@ package com.teamresourceful.resourcefulconfig.client.components.options.types; import com.teamresourceful.resourcefulconfig.api.types.entries.ResourcefulConfigObjectEntry; +import com.teamresourceful.resourcefulconfig.api.types.info.Translatable; import com.teamresourceful.resourcefulconfig.client.UIConstants; import com.teamresourceful.resourcefulconfig.client.components.ModSprites; import com.teamresourceful.resourcefulconfig.client.components.base.BaseWidget; @@ -30,7 +31,7 @@ public ObjectOptionWidget(ResourcefulConfigObjectEntry entry) { protected void renderWidget(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) { graphics.blitSprite(ModSprites.ofButton(this.isHovered()), getX(), getY(), getWidth(), getHeight()); - int contentWidth = font.width(UIConstants.EDIT_OBJECT) + SPACING + SIZE; + int contentWidth = font.width(UIConstants.EDIT) + SPACING + SIZE; graphics.blitSprite( ModSprites.EDIT, @@ -38,7 +39,7 @@ protected void renderWidget(GuiGraphics graphics, int mouseX, int mouseY, float SIZE, SIZE ); graphics.drawString( - font, UIConstants.EDIT_OBJECT, + font, UIConstants.EDIT, getX() + (getWidth() - contentWidth) / 2 + SIZE + SPACING, getY() + (getHeight() - font.lineHeight) / 2 + 1, UIConstants.TEXT_TITLE @@ -59,7 +60,7 @@ protected ObjectEditOverlay(ResourcefulConfigObjectEntry entry) { super(); this.entry = entry; - this.title = UIConstants.EDIT_OBJECT; + this.title = Translatable.toSpeifiedComponent(entry.instance(), UIConstants.EDIT_OBJECT); } @Override diff --git a/common/src/main/java/com/teamresourceful/resourcefulconfig/common/loader/JavaConfigParser.java b/common/src/main/java/com/teamresourceful/resourcefulconfig/common/loader/JavaConfigParser.java index 15c827e..81088dd 100644 --- a/common/src/main/java/com/teamresourceful/resourcefulconfig/common/loader/JavaConfigParser.java +++ b/common/src/main/java/com/teamresourceful/resourcefulconfig/common/loader/JavaConfigParser.java @@ -1,9 +1,6 @@ package com.teamresourceful.resourcefulconfig.common.loader; -import com.teamresourceful.resourcefulconfig.api.annotations.Category; -import com.teamresourceful.resourcefulconfig.api.annotations.Config; -import com.teamresourceful.resourcefulconfig.api.annotations.ConfigButton; -import com.teamresourceful.resourcefulconfig.api.annotations.ConfigEntry; +import com.teamresourceful.resourcefulconfig.api.annotations.*; import com.teamresourceful.resourcefulconfig.api.loader.ConfigParser; import com.teamresourceful.resourcefulconfig.api.types.ResourcefulConfig; import com.teamresourceful.resourcefulconfig.api.types.entries.Observable; @@ -20,6 +17,8 @@ import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Field; import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.List; public class JavaConfigParser implements ConfigParser { @@ -40,19 +39,21 @@ private static T populateEntries(Class clazz, T for (Field field : clazz.getDeclaredFields()) { ConfigEntry data = assertEntry(field); if (data != null) { - if (data.type() == EntryType.OBJECT) { + var type = getEntryType(field, data.type()); + + if (type == EntryType.OBJECT) { Object instance = ParsingUtils.getField(field, null); - ParsedObjectEntry objectEntry = new ParsedObjectEntry(data.type(), field); + ParsedObjectEntry objectEntry = new ParsedObjectEntry(field); populateEntries(instance, objectEntry); config.entries().put(data.id(), objectEntry); } else if (field.getType() == Observable.class) { - ParsedObservableEntry observableEntry = ParsedObservableEntry.of(data.type(), field, null); + ParsedObservableEntry observableEntry = ParsedObservableEntry.of(type, field, null); config.entries().put(data.id(), observableEntry); if (observableEntry.defaultValue() == null) { throw new IllegalArgumentException("Entry " + field.getName() + " must not have a null default value!"); } } else { - ParsedInstanceEntry instanceEntry = new ParsedInstanceEntry(data.type(), field, null); + ParsedInstanceEntry instanceEntry = new ParsedInstanceEntry(type, field, null); config.entries().put(data.id(), instanceEntry); if (instanceEntry.defaultValue() == null) { throw new IllegalArgumentException("Entry " + field.getName() + " must not have a null default value!"); @@ -84,16 +85,17 @@ private static T populateEntries(Class clazz, T private static void populateEntries(Object instance, ParsedObjectEntry entry) { assertValidClass(instance.getClass()); for (Field field : instance.getClass().getDeclaredFields()) { - ConfigEntry data = assertAcciessbleEntry(instance, field); + ConfigEntry data = assertAccessibleEntry(instance, field); if (data == null) continue; - if (data.type() == EntryType.OBJECT) { + EntryType type = getEntryType(field, data.type()); + if (type == EntryType.OBJECT) { throw new IllegalArgumentException("Entry " + field.getName() + " cannot be an object!"); } ResourcefulConfigValueEntry valueEntry; if (field.getType() == Observable.class) { - valueEntry = ParsedObservableEntry.of(data.type(), field, null); + valueEntry = ParsedObservableEntry.of(type, field, null); } else { - valueEntry = new ParsedInstanceEntry(data.type(), field, instance); + valueEntry = new ParsedInstanceEntry(type, field, instance); } if (valueEntry.defaultValue() == null) { @@ -114,7 +116,7 @@ private static ConfigButton assertButton(Field field) { } private static ConfigEntry assertEntry(Field field) { - ConfigEntry data = assertAcciessbleEntry(null, field); + ConfigEntry data = assertAccessibleEntry(null, field); String name = field.getName(); if (data == null) return null; if (!Modifier.isStatic(field.getModifiers())) @@ -139,21 +141,22 @@ private static Class getFieldType(Object instance, Field field, EntryType ent return type; } - private static ConfigEntry assertAcciessbleEntry(Object instance, Field field) { + private static ConfigEntry assertAccessibleEntry(Object instance, Field field) { ConfigEntry data = field.getAnnotation(ConfigEntry.class); String name = field.getName(); if (data == null) return null; - if (!Modifier.isPublic(field.getModifiers())) - throw new IllegalArgumentException("Entry " + name + " is not public!"); - if (data.type().mustBeFinal() && !Modifier.isFinal(field.getModifiers())) - throw new IllegalArgumentException("Entry " + name + " must be final!"); - if (!data.type().mustBeFinal() && Modifier.isFinal(field.getModifiers())) - throw new IllegalArgumentException("Entry " + name + " must not be final!"); - Class type = getFieldType(instance, field, data.type()); - if (!data.type().test(type)) - throw new IllegalArgumentException("Entry " + field.getName() + " is not of type " + data.type().name() + "!"); - if (data.id().contains(".")) - throw new IllegalArgumentException("Entry " + field.getName() + " has an invalid id! Ids must not contain '.'"); + EntryType type = getEntryType(field, data.type()); + List errors = new ArrayList<>(); + if (!Modifier.isPublic(field.getModifiers())) errors.add("Entry is not public!"); + if (type.mustBeFinal() && !Modifier.isFinal(field.getModifiers())) errors.add("Entry must be final!"); + if (!type.mustBeFinal() && Modifier.isFinal(field.getModifiers())) errors.add("Entry must not be final!"); + if (!type.test(getFieldType(instance, field, type))) errors.add("Entry has an invalid type for " + type + "!"); + if (data.id().contains(".")) errors.add(data.id() + " is an invalid id!"); + if (!errors.isEmpty()) { + throw new IllegalArgumentException( + "Entry " + name + " is invalid!\n\t" + String.join("\n\t", errors) + "\n" + ); + } return data; } @@ -166,13 +169,39 @@ private static T assertAnnotation(AnnotatedElement elemen } private static void assertValidClass(Class config) { - if (!Modifier.isPublic(config.getModifiers())) - throw new IllegalArgumentException("Config class must be public!"); - if (config.getEnclosingClass() != null && !Modifier.isStatic(config.getModifiers())) - throw new IllegalArgumentException("Config class must be static!"); - if (config.isEnum()) throw new IllegalArgumentException("Config class cannot be an enum!"); - if (config.isInterface()) throw new IllegalArgumentException("Config class cannot be an interface!"); - if (config.isAnnotation()) throw new IllegalArgumentException("Config class cannot be an annotation!"); - if (config.isRecord()) throw new IllegalArgumentException("Config class cannot be a record!"); + List errors = new ArrayList<>(); + if (!Modifier.isPublic(config.getModifiers())) errors.add("Config class must be public!"); + if (config.getEnclosingClass() != null && !Modifier.isStatic(config.getModifiers())) errors.add("Inner config class must be static!"); + if (config.isEnum()) errors.add("Config class cannot be an enum!"); + if (config.isInterface()) errors.add("Config class cannot be an interface!"); + if (config.isAnnotation()) errors.add("Config class cannot be an annotation!"); + if (config.isRecord()) errors.add("Config class cannot be a record!"); + + if (!errors.isEmpty()) { + throw new IllegalArgumentException( + "Config class " + config.getName() + " is invalid!\n\t" + String.join("\n\t", errors) + "\n" + ); + } + } + + private static EntryType getEntryType(Field field, EntryType defaultValue) { + Class fieldType = field.getType(); + if (fieldType == Observable.class) fieldType = ((Observable) ParsingUtils.getField(field, null)).type(); + if (fieldType.isArray()) fieldType = fieldType.getComponentType(); + return getEntryType(fieldType, defaultValue); + } + + private static EntryType getEntryType(Class type, EntryType defaultValue) { + if (type.getAnnotation(ConfigObject.class) != null) return EntryType.OBJECT; + if (type == Long.TYPE || type == Long.class) return EntryType.INTEGER; + if (type == Integer.TYPE || type == Integer.class) return EntryType.INTEGER; + if (type == Short.TYPE || type == Short.class) return EntryType.INTEGER; + if (type == Byte.TYPE || type == Byte.class) return EntryType.INTEGER; + if (type == Double.TYPE || type == Double.class) return EntryType.DOUBLE; + if (type == Float.TYPE || type == Float.class) return EntryType.DOUBLE; + if (type == Boolean.TYPE || type == Boolean.class) return EntryType.BOOLEAN; + if (type == String.class) return EntryType.STRING; + if (type.isEnum()) return EntryType.ENUM; + return defaultValue; } } diff --git a/common/src/main/java/com/teamresourceful/resourcefulconfig/common/loader/entries/ParsedObjectEntry.java b/common/src/main/java/com/teamresourceful/resourcefulconfig/common/loader/entries/ParsedObjectEntry.java index b42496b..880a817 100644 --- a/common/src/main/java/com/teamresourceful/resourcefulconfig/common/loader/entries/ParsedObjectEntry.java +++ b/common/src/main/java/com/teamresourceful/resourcefulconfig/common/loader/entries/ParsedObjectEntry.java @@ -15,12 +15,21 @@ public record ParsedObjectEntry( LinkedHashMap entries ) implements ResourcefulConfigObjectEntry { - public ParsedObjectEntry(EntryType type, Field field) { - this(type, field, EntryData.of(field, field.getType()), new LinkedHashMap<>()); + public ParsedObjectEntry(Field field) { + this(EntryType.OBJECT, field, EntryData.of(field, field.getType()), new LinkedHashMap<>()); } @Override public void reset() { entries.values().forEach(ResourcefulConfigEntry::reset); } + + @Override + public Object instance() { + try { + return field.get(null); + } catch (Exception e) { + return null; + } + } } diff --git a/common/src/main/java/com/teamresourceful/resourcefulconfig/common/utils/ModUtils.java b/common/src/main/java/com/teamresourceful/resourcefulconfig/common/utils/ModUtils.java index c4420c9..3e5a45c 100644 --- a/common/src/main/java/com/teamresourceful/resourcefulconfig/common/utils/ModUtils.java +++ b/common/src/main/java/com/teamresourceful/resourcefulconfig/common/utils/ModUtils.java @@ -19,6 +19,12 @@ public static Path getConfigPath() { throw new NotImplementedException("Not implemented yet"); } + @Contract(pure = true) + @ExpectPlatform + public static boolean isDev() { + throw new NotImplementedException("Not implemented yet"); + } + public static void log(String message) { LOGGER.info("[ResourcefulConfig] {}", message); } diff --git a/fabric/src/main/java/com/teamresourceful/resourcefulconfig/common/utils/fabric/ModUtilsImpl.java b/fabric/src/main/java/com/teamresourceful/resourcefulconfig/common/utils/fabric/ModUtilsImpl.java index 33e32d9..b8aabfc 100644 --- a/fabric/src/main/java/com/teamresourceful/resourcefulconfig/common/utils/fabric/ModUtilsImpl.java +++ b/fabric/src/main/java/com/teamresourceful/resourcefulconfig/common/utils/fabric/ModUtilsImpl.java @@ -8,4 +8,9 @@ public class ModUtilsImpl { public static Path getConfigPath() { return FabricLoader.getInstance().getConfigDir(); } + + @org.jetbrains.annotations.Contract(pure = true) + public static boolean isDev() { + return FabricLoader.getInstance().isDevelopmentEnvironment(); + } } diff --git a/fabric/src/main/java/com/teamresourceful/resourcefulconfig/demo/DemoConfig.java b/fabric/src/main/java/com/teamresourceful/resourcefulconfig/demo/DemoConfig.java index c75d536..10b70fc 100644 --- a/fabric/src/main/java/com/teamresourceful/resourcefulconfig/demo/DemoConfig.java +++ b/fabric/src/main/java/com/teamresourceful/resourcefulconfig/demo/DemoConfig.java @@ -2,7 +2,6 @@ import com.teamresourceful.resourcefulconfig.api.annotations.*; import com.teamresourceful.resourcefulconfig.api.types.entries.Observable; -import com.teamresourceful.resourcefulconfig.api.types.options.EntryType; import net.minecraft.ChatFormatting; @ConfigInfo.Provider(DemoInfoProvider.class) @@ -11,7 +10,6 @@ public final class DemoConfig { @ConfigEntry( - type = EntryType.BOOLEAN, id = "demoBoolean", translation = "true" ) @@ -24,21 +22,18 @@ public final class DemoConfig { } @ConfigEntry( - type = EntryType.INTEGER, id = "demoInteger", translation = "1" ) public static int demoInteger = 1; @ConfigEntry( - type = EntryType.DOUBLE, id = "demoDouble", translation = "1.0" ) public static double demoDouble = 1.0; @ConfigEntry( - type = EntryType.INTEGER, id = "demoSlider", translation = "1" ) @@ -48,14 +43,12 @@ public final class DemoConfig { @ConfigEntry( - type = EntryType.STRING, id = "demoString", translation = "Hello World!" ) public static String demoString = "Hello World!"; @ConfigEntry( - type = EntryType.STRING, id = "multiline", translation = "multiline" ) @@ -67,7 +60,6 @@ public final class DemoConfig { """; @ConfigEntry( - type = EntryType.INTEGER, id = "demoRange", translation = "range" ) @@ -75,7 +67,6 @@ public final class DemoConfig { public static int demoIntegerRange = 5; @ConfigEntry( - type = EntryType.ENUM, id = "demoEnum", translation = "enum" ) @@ -86,7 +77,6 @@ public final class DemoConfig { description = "This is a separator." ) @ConfigEntry( - type = EntryType.STRING, id = "demoRegex", translation = "regex" ) @@ -94,7 +84,6 @@ public final class DemoConfig { public static String demoRegex = "#ff0000"; @ConfigEntry( - type = EntryType.INTEGER, id = "demoColor", translation = "demo color" ) @@ -106,7 +95,6 @@ public final class DemoConfig { public static int demoColor = 0xff0000; @ConfigEntry( - type = EntryType.INTEGER, id = "demoAlphaColor", translation = "demo alpha color" ) @@ -114,7 +102,6 @@ public final class DemoConfig { public static int demoAlphaColor = 0xffff0000; @ConfigEntry( - type = EntryType.ENUM, id = "demoSelect", translation = "select" ) @@ -129,7 +116,6 @@ public final class DemoConfig { public static final Runnable test = () -> System.out.println("Clicked!"); @ConfigEntry( - type = EntryType.ENUM, id = "demoDraggable", translation = "draggable" ) @@ -143,10 +129,12 @@ public final class DemoConfig { }; @ConfigEntry( - type = EntryType.INTEGER, id = "demoKeybind", translation = "keybind" ) @ConfigOption.Keybind public static int demoKeybind = 48; + + @ConfigEntry(id = "object") + public static final DemoObject object = new DemoObject(); } diff --git a/fabric/src/main/java/com/teamresourceful/resourcefulconfig/demo/DemoObject.java b/fabric/src/main/java/com/teamresourceful/resourcefulconfig/demo/DemoObject.java index fed7080..8936b4e 100644 --- a/fabric/src/main/java/com/teamresourceful/resourcefulconfig/demo/DemoObject.java +++ b/fabric/src/main/java/com/teamresourceful/resourcefulconfig/demo/DemoObject.java @@ -2,22 +2,19 @@ import com.teamresourceful.resourcefulconfig.api.annotations.ConfigEntry; import com.teamresourceful.resourcefulconfig.api.annotations.ConfigObject; -import com.teamresourceful.resourcefulconfig.api.types.options.EntryType; @ConfigObject public class DemoObject { @ConfigEntry( id = "string", - translation = "Hello World!", - type = EntryType.STRING + translation = "Hello World!" ) public String oldString = "Hello World!"; @ConfigEntry( id = "integer", - translation = "1", - type = EntryType.INTEGER + translation = "1" ) public int oldInteger = 1; } diff --git a/neoforge/src/main/java/com/teamresourceful/resourcefulconfig/common/utils/neoforge/ModUtilsImpl.java b/neoforge/src/main/java/com/teamresourceful/resourcefulconfig/common/utils/neoforge/ModUtilsImpl.java index 3de5e08..99a8d40 100644 --- a/neoforge/src/main/java/com/teamresourceful/resourcefulconfig/common/utils/neoforge/ModUtilsImpl.java +++ b/neoforge/src/main/java/com/teamresourceful/resourcefulconfig/common/utils/neoforge/ModUtilsImpl.java @@ -1,5 +1,6 @@ package com.teamresourceful.resourcefulconfig.common.utils.neoforge; +import net.neoforged.fml.loading.FMLLoader; import net.neoforged.fml.loading.FMLPaths; import java.nio.file.Path; @@ -8,4 +9,8 @@ public class ModUtilsImpl { public static Path getConfigPath() { return FMLPaths.CONFIGDIR.get(); } + + public static boolean isDev() { + return !FMLLoader.isProduction(); + } }