[DEV] New Cloth Config GUIs, new nojang apis, and bug fixes
This commit is contained in:
@@ -3,7 +3,7 @@ def projectIcon = "https://cdn.modrinth.com/data/Nn8Wasaq/a172c634683a11a2e9ae59
|
|||||||
def JDK = "21";
|
def JDK = "21";
|
||||||
def majorMc = "1.21.2";
|
def majorMc = "1.21.2";
|
||||||
def modLoaders = "neoforge|fabric|quilt|paper";
|
def modLoaders = "neoforge|fabric|quilt|paper";
|
||||||
def supportedMc = "1.21.3";
|
def supportedMc = "1.21.3|1.21.4";
|
||||||
def reltype = "port";
|
def reltype = "port";
|
||||||
|
|
||||||
pipeline {
|
pipeline {
|
||||||
|
@@ -3,6 +3,8 @@ archivesBaseName = "${mod_name.replace(" ", "")}-Common-${minecraft_version}"
|
|||||||
dependencies {
|
dependencies {
|
||||||
stupidRemapArch("dev.ftb.mods:ftb-essentials:${ftb_essentials}")
|
stupidRemapArch("dev.ftb.mods:ftb-essentials:${ftb_essentials}")
|
||||||
stupidRemapArch("dev.ftb.mods:ftb-ranks:${ftb_ranks}")
|
stupidRemapArch("dev.ftb.mods:ftb-ranks:${ftb_ranks}")
|
||||||
|
|
||||||
|
stupidRemapArch("me.shedaniel.cloth:cloth-config:${cloth_config}")
|
||||||
}
|
}
|
||||||
|
|
||||||
shadowJar {
|
shadowJar {
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
package com.hypherionmc.craterlib.api.commands;
|
package com.hypherionmc.craterlib.api.commands;
|
||||||
|
|
||||||
|
import com.hypherionmc.craterlib.CraterConstants;
|
||||||
import com.hypherionmc.craterlib.compat.LuckPermsCompat;
|
import com.hypherionmc.craterlib.compat.LuckPermsCompat;
|
||||||
import com.hypherionmc.craterlib.core.platform.ModloaderEnvironment;
|
import com.hypherionmc.craterlib.core.platform.ModloaderEnvironment;
|
||||||
import com.hypherionmc.craterlib.nojang.authlib.BridgedGameProfile;
|
import com.hypherionmc.craterlib.nojang.authlib.BridgedGameProfile;
|
||||||
@@ -137,10 +138,15 @@ public class CraterCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean checkPermission(CommandSourceStack stack) {
|
private boolean checkPermission(CommandSourceStack stack) {
|
||||||
if (!ModloaderEnvironment.INSTANCE.isModLoaded("luckperms") || !stack.isPlayer() || luckPermNode.isEmpty())
|
try {
|
||||||
return stack.hasPermission(this.permLevel);
|
if (!ModloaderEnvironment.INSTANCE.isModLoaded("luckperms") || !stack.isPlayer() || luckPermNode.isEmpty())
|
||||||
|
return stack.hasPermission(this.permLevel);
|
||||||
|
|
||||||
return LuckPermsCompat.INSTANCE.hasPermission(stack.getPlayer(), this.luckPermNode) || stack.hasPermission(this.permLevel);
|
return LuckPermsCompat.INSTANCE.hasPermission(stack.getPlayer(), this.luckPermNode) || stack.hasPermission(this.permLevel);
|
||||||
|
} catch (Exception e) {
|
||||||
|
CraterConstants.LOG.error("Failed to check luckperms permissions", e);
|
||||||
|
return stack.hasPermission(this.permLevel);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
|
@@ -2,6 +2,7 @@ package com.hypherionmc.craterlib.api.events.compat;
|
|||||||
|
|
||||||
import com.hypherionmc.craterlib.core.event.CraterEvent;
|
import com.hypherionmc.craterlib.core.event.CraterEvent;
|
||||||
import com.hypherionmc.craterlib.nojang.authlib.BridgedGameProfile;
|
import com.hypherionmc.craterlib.nojang.authlib.BridgedGameProfile;
|
||||||
|
import lombok.Getter;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
@@ -9,6 +10,7 @@ import java.util.UUID;
|
|||||||
public class LuckPermsCompatEvents {
|
public class LuckPermsCompatEvents {
|
||||||
|
|
||||||
@RequiredArgsConstructor(staticName = "of")
|
@RequiredArgsConstructor(staticName = "of")
|
||||||
|
@Getter
|
||||||
public static class GroupAddedEvent extends CraterEvent {
|
public static class GroupAddedEvent extends CraterEvent {
|
||||||
private final String identifier;
|
private final String identifier;
|
||||||
private final UUID uuid;
|
private final UUID uuid;
|
||||||
@@ -20,6 +22,7 @@ public class LuckPermsCompatEvents {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@RequiredArgsConstructor(staticName = "of")
|
@RequiredArgsConstructor(staticName = "of")
|
||||||
|
@Getter
|
||||||
public static class GroupRemovedEvent extends CraterEvent {
|
public static class GroupRemovedEvent extends CraterEvent {
|
||||||
private final String identifier;
|
private final String identifier;
|
||||||
private final UUID uuid;
|
private final UUID uuid;
|
||||||
|
@@ -0,0 +1,409 @@
|
|||||||
|
package com.hypherionmc.craterlib.client.gui.config;
|
||||||
|
|
||||||
|
import com.hypherionmc.craterlib.CraterConstants;
|
||||||
|
import com.hypherionmc.craterlib.client.gui.config.widgets.ClothConfigButtonEntry;
|
||||||
|
import com.hypherionmc.craterlib.core.config.AbstractConfig;
|
||||||
|
import com.hypherionmc.craterlib.core.config.annotations.HideFromScreen;
|
||||||
|
import com.hypherionmc.craterlib.core.config.annotations.SubConfig;
|
||||||
|
import com.hypherionmc.craterlib.core.config.annotations.Tooltip;
|
||||||
|
import me.hypherionmc.moonconfig.core.conversion.SpecComment;
|
||||||
|
import me.shedaniel.clothconfig2.api.ConfigBuilder;
|
||||||
|
import me.shedaniel.clothconfig2.api.ConfigCategory;
|
||||||
|
import me.shedaniel.clothconfig2.api.ConfigEntryBuilder;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.gui.components.toasts.SystemToast;
|
||||||
|
import net.minecraft.client.gui.screens.Screen;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
|
import org.jetbrains.annotations.ApiStatus;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.lang.reflect.ParameterizedType;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.StandardCopyOption;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author HypherionSA
|
||||||
|
* A Helper Class to convert {@link AbstractConfig}s into Cloth Config Screens
|
||||||
|
*/
|
||||||
|
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||||
|
@ApiStatus.Internal
|
||||||
|
public class ClothConfigScreenBuilder {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a new Cloth Config screen, from an {@link AbstractConfig}
|
||||||
|
*
|
||||||
|
* @param config The {@link AbstractConfig} the config screen is for
|
||||||
|
* @param parent The parent {@link Screen} this screen will return to when closed
|
||||||
|
* @return A fully usable Cloth Config screen
|
||||||
|
*/
|
||||||
|
public static Screen buildConfigScreen(AbstractConfig config, @Nullable Screen parent) {
|
||||||
|
ConfigBuilder builder = ConfigBuilder.create()
|
||||||
|
.setParentScreen(parent)
|
||||||
|
.setTitle(getTranslationKey(config, config, null));
|
||||||
|
|
||||||
|
readConfigFields(config, config, builder);
|
||||||
|
|
||||||
|
builder.setSavingRunnable(() -> safeSaveConfig(config));
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a new sub-screen for config entries marked with {@link SubConfig}
|
||||||
|
*
|
||||||
|
* @param config The {@link AbstractConfig} the config screen is for
|
||||||
|
* @param clazz The object or class that the screen is being built for
|
||||||
|
* @param parent The parent {@link Screen} this screen will return to when closed
|
||||||
|
* @return A fully usable Cloth Config screen
|
||||||
|
*/
|
||||||
|
private static Screen buildSubScreen(AbstractConfig config, Object clazz, @Nullable Screen parent) {
|
||||||
|
ConfigBuilder builder = ConfigBuilder.create()
|
||||||
|
.setParentScreen(parent)
|
||||||
|
.setTitle(getTranslationKey(config, clazz, null));
|
||||||
|
|
||||||
|
readConfigFields(config, clazz, builder);
|
||||||
|
|
||||||
|
builder.setSavingRunnable(() -> safeSaveConfig(config));
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a new screen, that allows editing lists of complex objects, like a list of Classes
|
||||||
|
*
|
||||||
|
* @param config The {@link AbstractConfig} the config screen is for
|
||||||
|
* @param list The list of objects this screen will be responsible for
|
||||||
|
* @param field The field this list belongs to in the main config
|
||||||
|
* @param invoker The object used to invoke the field, when setting the new value
|
||||||
|
* @param parent The parent {@link Screen} this screen will return to when closed
|
||||||
|
* @param edited Was this list edited
|
||||||
|
* @return A fully usable Cloth Config screen
|
||||||
|
*/
|
||||||
|
private static Screen buildListScreen(AbstractConfig config, List list, Field field, Object invoker, @Nullable Screen parent, boolean edited) {
|
||||||
|
ConfigBuilder builder = ConfigBuilder.create()
|
||||||
|
.setParentScreen(parent)
|
||||||
|
.setTitle(getTranslationKey(config, invoker, field.getName()));
|
||||||
|
|
||||||
|
ConfigCategory category = builder.getOrCreateCategory(Component.literal("Entries"));
|
||||||
|
|
||||||
|
// Handle Existing items in the list
|
||||||
|
for (int i = 0; i < list.size(); i++) {
|
||||||
|
Object item = list.get(i);
|
||||||
|
|
||||||
|
int finalI = i;
|
||||||
|
// Add a button to open the edit screen, as well as a delete button
|
||||||
|
category.addEntry(
|
||||||
|
new ClothConfigButtonEntry(
|
||||||
|
Component.translatable("cl.buttons.entry", (i + 1)),
|
||||||
|
Component.translatable("cl.buttons.edit"),
|
||||||
|
button -> Minecraft.getInstance().setScreen(
|
||||||
|
buildSubScreen(config, item, builder.build())
|
||||||
|
),
|
||||||
|
button -> {
|
||||||
|
list.remove(finalI);
|
||||||
|
saveFieldValue(list, field, invoker);
|
||||||
|
Minecraft.getInstance().setScreen(buildListScreen(config, list, field, invoker, parent, true));
|
||||||
|
},
|
||||||
|
edited
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a button to add new list entries
|
||||||
|
Type listType = field.getGenericType();
|
||||||
|
if (listType instanceof ParameterizedType paramType) {
|
||||||
|
Class<?> elementType = (Class<?>) paramType.getActualTypeArguments()[0];
|
||||||
|
|
||||||
|
category.addEntry(
|
||||||
|
new ClothConfigButtonEntry(
|
||||||
|
Component.literal(""),
|
||||||
|
Component.translatable("cl.buttons.add_entry"),
|
||||||
|
button -> {
|
||||||
|
try {
|
||||||
|
Object newItem = elementType.getDeclaredConstructor().newInstance();
|
||||||
|
list.add(newItem);
|
||||||
|
saveFieldValue(list, field, invoker);
|
||||||
|
Minecraft.getInstance().setScreen(buildListScreen(config, list, field, invoker, parent, true));
|
||||||
|
} catch (Exception e) {
|
||||||
|
CraterConstants.LOG.error("Failed to create new list entry", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.setSavingRunnable(() -> safeSaveConfig(config));
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A helper method to convert an {@link AbstractConfig} into the corresponding cloth config gui fields
|
||||||
|
*
|
||||||
|
* @param baseConfig The {@link AbstractConfig} to convert
|
||||||
|
* @param config The base class that is being processed
|
||||||
|
* @param builder The {@link ClothConfigScreenBuilder} we are currently working with
|
||||||
|
*/
|
||||||
|
private static void readConfigFields(AbstractConfig baseConfig, Object config, ConfigBuilder builder) {
|
||||||
|
ConfigEntryBuilder entryBuilder = builder.entryBuilder();
|
||||||
|
ConfigCategory configCategory = builder.getOrCreateCategory(Component.literal("General"));
|
||||||
|
|
||||||
|
for (Field field : config.getClass().getDeclaredFields()) {
|
||||||
|
try {
|
||||||
|
field.setAccessible(true);
|
||||||
|
|
||||||
|
// We ignore static, transient fields or fields marked with @HideFromScreen
|
||||||
|
final int fieldModifiers = field.getModifiers();
|
||||||
|
if (Modifier.isStatic(fieldModifiers) || Modifier.isTransient(fieldModifiers) || field.isAnnotationPresent(HideFromScreen.class))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Object val = field.get(config);
|
||||||
|
|
||||||
|
// Field is marked as sub-config, so we add a button field for it
|
||||||
|
if (field.isAnnotationPresent(SubConfig.class)) {
|
||||||
|
if (val != null) {
|
||||||
|
configCategory.addEntry(
|
||||||
|
new ClothConfigButtonEntry(
|
||||||
|
Component.translatable("cl.config." + baseConfig.getClass().getSimpleName().toLowerCase() + "." + field.getName().toLowerCase()),
|
||||||
|
Component.translatable("cl.buttons.edit"),
|
||||||
|
button -> Minecraft.getInstance().setScreen(
|
||||||
|
buildSubScreen(baseConfig, val, builder.build())
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Boolean Values
|
||||||
|
if (val instanceof Boolean bool) {
|
||||||
|
configCategory.addEntry(entryBuilder.startBooleanToggle(getTranslationKey(baseConfig, config, field.getName()), bool)
|
||||||
|
.setTooltip(getToolTip(field))
|
||||||
|
.setSaveConsumer(newValue -> saveFieldValue(newValue, field, config))
|
||||||
|
.setDefaultValue(bool).build());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enum Values
|
||||||
|
if (val instanceof Enum<?> enumValue) {
|
||||||
|
Class<Enum> enumClass = (Class<Enum>)enumValue.getDeclaringClass();
|
||||||
|
configCategory.addEntry(entryBuilder.startEnumSelector(
|
||||||
|
getTranslationKey(baseConfig, config, field.getName()),
|
||||||
|
enumClass,
|
||||||
|
enumValue)
|
||||||
|
.setTooltip(getToolTip(field))
|
||||||
|
.setSaveConsumer(newValue -> saveFieldValue(newValue, field, config))
|
||||||
|
.setDefaultValue(enumValue)
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int Values
|
||||||
|
if (val instanceof Integer intt) {
|
||||||
|
configCategory.addEntry(entryBuilder.startIntField(getTranslationKey(baseConfig, config, field.getName()), intt)
|
||||||
|
.setTooltip(getToolTip(field))
|
||||||
|
.setSaveConsumer(newValue -> saveFieldValue(newValue, field, config))
|
||||||
|
.setDefaultValue(intt).build());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Long Values
|
||||||
|
if (val instanceof Long longt) {
|
||||||
|
configCategory.addEntry(entryBuilder.startLongField(getTranslationKey(baseConfig, config, field.getName()), longt)
|
||||||
|
.setTooltip(getToolTip(field))
|
||||||
|
.setSaveConsumer(newValue -> saveFieldValue(newValue, field, config))
|
||||||
|
.setDefaultValue(longt).build());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float Values
|
||||||
|
if (val instanceof Float floatt) {
|
||||||
|
configCategory.addEntry(entryBuilder.startFloatField(getTranslationKey(baseConfig, config, field.getName()), floatt)
|
||||||
|
.setTooltip(getToolTip(field))
|
||||||
|
.setSaveConsumer(newValue -> saveFieldValue(newValue, field, config))
|
||||||
|
.setDefaultValue(floatt).build());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Double Values
|
||||||
|
if (val instanceof Double doublet) {
|
||||||
|
configCategory.addEntry(entryBuilder.startDoubleField(getTranslationKey(baseConfig, config, field.getName()), doublet)
|
||||||
|
.setTooltip(getToolTip(field))
|
||||||
|
.setSaveConsumer(newValue -> saveFieldValue(newValue, field, config))
|
||||||
|
.setDefaultValue(doublet).build());
|
||||||
|
}
|
||||||
|
|
||||||
|
// String Values
|
||||||
|
if (val instanceof String string) {
|
||||||
|
configCategory.addEntry(entryBuilder.startStrField(getTranslationKey(baseConfig, config, field.getName()), string)
|
||||||
|
.setTooltip(getToolTip(field))
|
||||||
|
.setSaveConsumer(newValue -> saveFieldValue(newValue, field, config))
|
||||||
|
.setDefaultValue(string).build());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lists
|
||||||
|
if (val instanceof List<?> list) {
|
||||||
|
Type listType = field.getGenericType();
|
||||||
|
if (listType instanceof ParameterizedType paramType) {
|
||||||
|
Type elementType = paramType.getActualTypeArguments()[0];
|
||||||
|
|
||||||
|
// String List
|
||||||
|
if (elementType.equals(String.class)) {
|
||||||
|
configCategory.addEntry(entryBuilder.startStrList(getTranslationKey(baseConfig, config, field.getName()), (List<String>) list)
|
||||||
|
.setTooltip(getToolTip(field))
|
||||||
|
.setSaveConsumer(newValue -> saveFieldValue(new ArrayList<>(newValue), field, config))
|
||||||
|
.setDefaultValue((List<String>) list).build());
|
||||||
|
|
||||||
|
// Int List
|
||||||
|
} else if (elementType.equals(Integer.class)) {
|
||||||
|
configCategory.addEntry(entryBuilder.startIntList(getTranslationKey(baseConfig, config, field.getName()), (List<Integer>) list)
|
||||||
|
.setTooltip(getToolTip(field))
|
||||||
|
.setSaveConsumer(newValue -> saveFieldValue(new ArrayList<>(newValue), field, config))
|
||||||
|
.setDefaultValue((List<Integer>) list).build());
|
||||||
|
|
||||||
|
// Long List
|
||||||
|
} else if (elementType.equals(Long.class)) {
|
||||||
|
configCategory.addEntry(entryBuilder.startLongList(getTranslationKey(baseConfig, config, field.getName()), (List<Long>) list)
|
||||||
|
.setTooltip(getToolTip(field))
|
||||||
|
.setSaveConsumer(newValue -> saveFieldValue(new ArrayList<>(newValue), field, config))
|
||||||
|
.setDefaultValue((List<Long>) list).build());
|
||||||
|
|
||||||
|
// Float List
|
||||||
|
} else if (elementType.equals(Float.class)) {
|
||||||
|
configCategory.addEntry(entryBuilder.startFloatList(getTranslationKey(baseConfig, config, field.getName()), (List<Float>) list)
|
||||||
|
.setTooltip(getToolTip(field))
|
||||||
|
.setSaveConsumer(newValue -> saveFieldValue(new ArrayList<>(newValue), field, config))
|
||||||
|
.setDefaultValue((List<Float>) list).build());
|
||||||
|
|
||||||
|
// Double List
|
||||||
|
} else if (elementType.equals(Double.class)) {
|
||||||
|
configCategory.addEntry(entryBuilder.startDoubleList(getTranslationKey(baseConfig, config, field.getName()), (List<Double>) list)
|
||||||
|
.setTooltip(getToolTip(field))
|
||||||
|
.setSaveConsumer(newValue -> saveFieldValue(new ArrayList<>(newValue), field, config))
|
||||||
|
.setDefaultValue((List<Double>) list).build());
|
||||||
|
} else {
|
||||||
|
// List of complex objects
|
||||||
|
configCategory.addEntry(
|
||||||
|
new ClothConfigButtonEntry(
|
||||||
|
getTranslationKey(baseConfig, config, field.getName()),
|
||||||
|
Component.translatable("cl.buttons.edit"),
|
||||||
|
button -> Minecraft.getInstance().setScreen(
|
||||||
|
buildListScreen(baseConfig, list, field, config, builder.build(), false)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
CraterConstants.LOG.error("Failed to process config file {}", baseConfig.getConfigName(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper method to supply tooltips to config fields.
|
||||||
|
* If the field has an {@link SpecComment}, we use that, otherwise we use the {@link Tooltip} annotation
|
||||||
|
* or generate one from the field name
|
||||||
|
*
|
||||||
|
* @param field The field that is being processed
|
||||||
|
* @return A {@link Component} that can be used for the tooltip
|
||||||
|
*/
|
||||||
|
private static Component getToolTip(Field field) {
|
||||||
|
Component component = Component.empty();
|
||||||
|
|
||||||
|
if (field.isAnnotationPresent(SpecComment.class)) {
|
||||||
|
SpecComment comment = field.getAnnotation(SpecComment.class);
|
||||||
|
component = Component.literal(comment.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (field.isAnnotationPresent(Tooltip.class)) {
|
||||||
|
Tooltip tooltip = field.getAnnotation(Tooltip.class);
|
||||||
|
Component c = Component.empty();
|
||||||
|
|
||||||
|
for (String comment : tooltip.value()) {
|
||||||
|
c.getSiblings().add(Component.literal(comment));
|
||||||
|
}
|
||||||
|
|
||||||
|
component = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
return component;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A helper method to build translation keys for config screens, fields etc
|
||||||
|
*
|
||||||
|
* @param baseConfig The {@link AbstractConfig} being processed
|
||||||
|
* @param currentConfig The field being processed
|
||||||
|
* @param fieldName The raw name of the field
|
||||||
|
* @return A {@link Component} with the new translation key
|
||||||
|
*/
|
||||||
|
private static Component getTranslationKey(AbstractConfig baseConfig, Object currentConfig, String fieldName) {
|
||||||
|
String baseKey = "cl.config." + baseConfig.getClass().getSimpleName().toLowerCase();
|
||||||
|
|
||||||
|
if (currentConfig != baseConfig) {
|
||||||
|
baseKey += "." + currentConfig.getClass().getSimpleName().toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fieldName != null) {
|
||||||
|
baseKey += "." + fieldName.toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Component.translatable(baseKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper method to write changes values back to the config using reflection
|
||||||
|
*
|
||||||
|
* @param value The new value of the field
|
||||||
|
* @param field The field that needs to be updated
|
||||||
|
* @param config The object used to invoke the field for updating
|
||||||
|
*/
|
||||||
|
private static void saveFieldValue(Object value, Field field, Object config) {
|
||||||
|
try {
|
||||||
|
if (value instanceof List && !field.getType().equals(List.class)) {
|
||||||
|
List newList = (List)field.getType().getDeclaredConstructor().newInstance();
|
||||||
|
newList.addAll((List)value);
|
||||||
|
field.set(config, newList);
|
||||||
|
} else {
|
||||||
|
field.set(config, value);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
CraterConstants.LOG.error("Failed to write config field {}", field.getName(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Safety method to prevent config screens from corrupting configs. In some edge cases, the config gui
|
||||||
|
* can generate invalid values, that cause the config saving to fail, and then save an empty file.
|
||||||
|
* This method makes a backup of the config before writing to it, and restores the backup if it fails
|
||||||
|
*
|
||||||
|
* @param config The {@link AbstractConfig} being saved
|
||||||
|
*/
|
||||||
|
private static void safeSaveConfig(AbstractConfig config) {
|
||||||
|
File configPath = config.getConfigPath();
|
||||||
|
Path backupPath = configPath.toPath().resolveSibling(configPath.getName() + ".bak");
|
||||||
|
|
||||||
|
try {
|
||||||
|
Files.copy(configPath.toPath(), backupPath, StandardCopyOption.REPLACE_EXISTING);
|
||||||
|
config.saveConfig(config);
|
||||||
|
Files.deleteIfExists(backupPath);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Minecraft.getInstance().getToastManager().addToast(
|
||||||
|
new SystemToast(
|
||||||
|
SystemToast.SystemToastId.PACK_LOAD_FAILURE,
|
||||||
|
Component.literal("Failed To Save Config"),
|
||||||
|
Component.literal("Restoring Backup Copy. Check log for details"))
|
||||||
|
);
|
||||||
|
CraterConstants.LOG.error("Failed to save config, restoring backup", e);
|
||||||
|
try {
|
||||||
|
Files.copy(backupPath, configPath.toPath(), StandardCopyOption.REPLACE_EXISTING);
|
||||||
|
config.configReloaded();
|
||||||
|
} catch (Exception restoreError) {
|
||||||
|
CraterConstants.LOG.error("Failed to restore config backup", restoreError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -37,6 +37,7 @@ import java.util.function.Supplier;
|
|||||||
/**
|
/**
|
||||||
* @author HypherionSA
|
* @author HypherionSA
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(forRemoval = true, since = "2.1.3")
|
||||||
public class CraterConfigScreen extends Screen {
|
public class CraterConfigScreen extends Screen {
|
||||||
public static final float SCROLLBAR_BOTTOM_COLOR = .5f;
|
public static final float SCROLLBAR_BOTTOM_COLOR = .5f;
|
||||||
public static final float SCROLLBAR_TOP_COLOR = .67f;
|
public static final float SCROLLBAR_TOP_COLOR = .67f;
|
||||||
|
@@ -10,6 +10,7 @@ import net.minecraft.client.gui.components.EditBox;
|
|||||||
* Copied from Cloth Config Lite
|
* Copied from Cloth Config Lite
|
||||||
* <a href="https://github.com/shedaniel/cloth-config-lite/blob/1.17/src/main/java/me/shedaniel/clothconfiglite/impl/option/AbstractWidgetOption.java">...</a>
|
* <a href="https://github.com/shedaniel/cloth-config-lite/blob/1.17/src/main/java/me/shedaniel/clothconfiglite/impl/option/AbstractWidgetOption.java">...</a>
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(forRemoval = true, since = "2.1.3")
|
||||||
public class AbstractConfigWidget<T, W extends AbstractWidget> extends BaseWidget<T> {
|
public class AbstractConfigWidget<T, W extends AbstractWidget> extends BaseWidget<T> {
|
||||||
|
|
||||||
public static final int buttonWidth = 200;
|
public static final int buttonWidth = 200;
|
||||||
|
@@ -13,6 +13,7 @@ import net.minecraft.network.chat.TextColor;
|
|||||||
* Copied from Cloth Config Lite
|
* Copied from Cloth Config Lite
|
||||||
* <a href="https://github.com/shedaniel/cloth-config-lite/blob/1.17/src/main/java/me/shedaniel/clothconfiglite/impl/option/BaseOption.java">...</a>
|
* <a href="https://github.com/shedaniel/cloth-config-lite/blob/1.17/src/main/java/me/shedaniel/clothconfiglite/impl/option/BaseOption.java">...</a>
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(forRemoval = true, since = "2.1.3")
|
||||||
public class BaseWidget<T> extends Option<T> {
|
public class BaseWidget<T> extends Option<T> {
|
||||||
|
|
||||||
public static final int resetButtonOffset = 48;
|
public static final int resetButtonOffset = 48;
|
||||||
|
@@ -0,0 +1,128 @@
|
|||||||
|
package com.hypherionmc.craterlib.client.gui.config.widgets;
|
||||||
|
|
||||||
|
import com.mojang.blaze3d.platform.Window;
|
||||||
|
import me.shedaniel.clothconfig2.api.AbstractConfigListEntry;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.gui.GuiGraphics;
|
||||||
|
import net.minecraft.client.gui.components.Button;
|
||||||
|
import net.minecraft.client.gui.components.events.GuiEventListener;
|
||||||
|
import net.minecraft.client.gui.narration.NarratableEntry;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author HypherionSA
|
||||||
|
* A Custom Cloth Config GUI entry to allow buttons to be added to the GUI
|
||||||
|
*/
|
||||||
|
public class ClothConfigButtonEntry extends AbstractConfigListEntry<Void> {
|
||||||
|
|
||||||
|
private final Button button;
|
||||||
|
private final Button deleteButton;
|
||||||
|
private final Component displayName;
|
||||||
|
private final boolean hasDeleteButton;
|
||||||
|
private final boolean wasEdited;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new Cloth Button Entry, that will have no delete button
|
||||||
|
*
|
||||||
|
* @param displayName The Display Name that will be used for the field
|
||||||
|
* @param fieldName The Display Name that will be used on the button
|
||||||
|
* @param onPress The Action to perform when the button was pressed
|
||||||
|
*/
|
||||||
|
public ClothConfigButtonEntry(Component displayName, Component fieldName, @Nullable Button.OnPress onPress) {
|
||||||
|
this(displayName, fieldName, onPress, null, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Create a new Cloth Button Entry, with optional delete button
|
||||||
|
*
|
||||||
|
* @param displayName The Display Name that will be used for the field
|
||||||
|
* @param fieldName The Display Name that will be used on the button
|
||||||
|
* @param onPress The Action to perform when the button was pressed
|
||||||
|
* @param deletePress The Action to perform when the delete button is pressed. If this is null, the button is disabled
|
||||||
|
* @param wasEdited Was a change made to the field this button belongs to. This is to tell cloth to enable the save button
|
||||||
|
*/
|
||||||
|
public ClothConfigButtonEntry(Component displayName, Component fieldName, Button.OnPress onPress, @Nullable Button.OnPress deletePress, boolean wasEdited) {
|
||||||
|
super(fieldName, false);
|
||||||
|
this.hasDeleteButton = deletePress != null;
|
||||||
|
this.wasEdited = wasEdited;
|
||||||
|
|
||||||
|
int mainButtonWidth = hasDeleteButton ? 75 : 100;
|
||||||
|
this.button = Button.builder(fieldName, onPress).size(mainButtonWidth, 20).pos(0, 0).build();
|
||||||
|
this.deleteButton = deletePress != null ? Button.builder(Component.literal("X"), deletePress).size(20, 20).pos(0, 0).build() : null;
|
||||||
|
this.displayName = displayName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void render(GuiGraphics matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean isHovered, float delta) {
|
||||||
|
Window window = Minecraft.getInstance().getWindow();
|
||||||
|
Component displayedFieldName = displayName;
|
||||||
|
if (Minecraft.getInstance().font.isBidirectional()) {
|
||||||
|
matrices.drawString(Minecraft.getInstance().font, displayedFieldName.getVisualOrderText(), window.getGuiScaledWidth() - x - Minecraft.getInstance().font.width(displayedFieldName), y + 6, 16777215);
|
||||||
|
this.button.setX(x);
|
||||||
|
if (hasDeleteButton) {
|
||||||
|
this.deleteButton.setX(x + this.button.getWidth() + 4);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
matrices.drawString(Minecraft.getInstance().font, displayedFieldName.getVisualOrderText(), x, y + 6, this.getPreferredTextColor());
|
||||||
|
if (hasDeleteButton) {
|
||||||
|
this.button.setX(x + entryWidth - this.button.getWidth() - 24);
|
||||||
|
this.deleteButton.setX(x + entryWidth - 20);
|
||||||
|
} else {
|
||||||
|
this.button.setX(x + entryWidth - this.button.getWidth());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
button.setY(y + (entryHeight - 20) / 2);
|
||||||
|
button.render(matrices, mouseX, mouseY, delta);
|
||||||
|
|
||||||
|
if (hasDeleteButton) {
|
||||||
|
deleteButton.setY(y + (entryHeight - 20) / 2);
|
||||||
|
deleteButton.render(matrices, mouseX, mouseY, delta);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Void getValue() { return null; }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<Void> getDefaultValue() { return Optional.empty(); }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void save() {}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public List<? extends GuiEventListener> children() {
|
||||||
|
ArrayList<GuiEventListener> children = new ArrayList<>();
|
||||||
|
children.add(button);
|
||||||
|
|
||||||
|
if (hasDeleteButton) {
|
||||||
|
children.add(deleteButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
return children;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<? extends NarratableEntry> narratables() {
|
||||||
|
ArrayList<NarratableEntry> children = new ArrayList<>();
|
||||||
|
children.add(button);
|
||||||
|
|
||||||
|
if (hasDeleteButton) {
|
||||||
|
children.add(deleteButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
return children;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEdited() {
|
||||||
|
return wasEdited;
|
||||||
|
}
|
||||||
|
}
|
@@ -10,6 +10,7 @@ import net.minecraft.network.chat.Component;
|
|||||||
/**
|
/**
|
||||||
* @author HypherionSA
|
* @author HypherionSA
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(forRemoval = true, since = "2.1.3")
|
||||||
public class InternalConfigButton extends AbstractButton {
|
public class InternalConfigButton extends AbstractButton {
|
||||||
|
|
||||||
CraterConfigScreen screen;
|
CraterConfigScreen screen;
|
||||||
|
@@ -20,6 +20,7 @@ import java.util.function.Supplier;
|
|||||||
* Copied from Cloth Config Lite
|
* Copied from Cloth Config Lite
|
||||||
* <a href="https://github.com/shedaniel/cloth-config-lite/blob/1.17/src/main/java/me/shedaniel/clothconfiglite/impl/option/Option.java">...</a>
|
* <a href="https://github.com/shedaniel/cloth-config-lite/blob/1.17/src/main/java/me/shedaniel/clothconfiglite/impl/option/Option.java">...</a>
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(forRemoval = true, since = "2.1.3")
|
||||||
public abstract class Option<T> extends AbstractContainerEventHandler {
|
public abstract class Option<T> extends AbstractContainerEventHandler {
|
||||||
|
|
||||||
public Component text;
|
public Component text;
|
||||||
|
@@ -12,6 +12,7 @@ import net.minecraft.network.chat.Component;
|
|||||||
/**
|
/**
|
||||||
* @author HypherionSA
|
* @author HypherionSA
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(forRemoval = true, since = "2.1.3")
|
||||||
public class SubConfigWidget<T> extends AbstractConfigWidget<T, Button> {
|
public class SubConfigWidget<T> extends AbstractConfigWidget<T, Button> {
|
||||||
|
|
||||||
private final Object subConfig;
|
private final Object subConfig;
|
||||||
|
@@ -10,6 +10,7 @@ import java.util.function.Function;
|
|||||||
* Copied from Cloth Config Lite
|
* Copied from Cloth Config Lite
|
||||||
* <a href="https://github.com/shedaniel/cloth-config-lite/blob/1.17/src/main/java/me/shedaniel/clothconfiglite/impl/option/TextFieldOption.java">...</a>
|
* <a href="https://github.com/shedaniel/cloth-config-lite/blob/1.17/src/main/java/me/shedaniel/clothconfiglite/impl/option/TextFieldOption.java">...</a>
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(forRemoval = true, since = "2.1.3")
|
||||||
public class TextConfigOption<T> extends AbstractConfigWidget<T, WrappedEditBox> {
|
public class TextConfigOption<T> extends AbstractConfigWidget<T, WrappedEditBox> {
|
||||||
|
|
||||||
private final Function<T, String> toString;
|
private final Function<T, String> toString;
|
||||||
|
@@ -10,6 +10,7 @@ import java.util.function.Function;
|
|||||||
* Copied from Cloth Config Lite
|
* Copied from Cloth Config Lite
|
||||||
* <a href="https://github.com/shedaniel/cloth-config-lite/blob/1.17/src/main/java/me/shedaniel/clothconfiglite/impl/option/ToggleOption.java">...</a>
|
* <a href="https://github.com/shedaniel/cloth-config-lite/blob/1.17/src/main/java/me/shedaniel/clothconfiglite/impl/option/ToggleOption.java">...</a>
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(forRemoval = true, since = "2.1.3")
|
||||||
public class ToggleButton<T> extends AbstractConfigWidget<T, Button> {
|
public class ToggleButton<T> extends AbstractConfigWidget<T, Button> {
|
||||||
|
|
||||||
private final List<T> options;
|
private final List<T> options;
|
||||||
|
@@ -10,6 +10,7 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
/**
|
/**
|
||||||
* @author HypherionSA
|
* @author HypherionSA
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(forRemoval = true, since = "2.1.3")
|
||||||
public class WrappedEditBox extends EditBox {
|
public class WrappedEditBox extends EditBox {
|
||||||
|
|
||||||
public WrappedEditBox(Font font, int i, int j, int k, int l, @NotNull Component component) {
|
public WrappedEditBox(Font font, int i, int j, int k, int l, @NotNull Component component) {
|
||||||
|
@@ -0,0 +1,12 @@
|
|||||||
|
package com.hypherionmc.craterlib.core.config.annotations;
|
||||||
|
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author HypherionSA
|
||||||
|
* Allow mods to make use of the new Cloth Config based Config Screens
|
||||||
|
*/
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface ClothScreen {
|
||||||
|
}
|
@@ -1,10 +1,7 @@
|
|||||||
package com.hypherionmc.craterlib.nojang.client.gui;
|
package com.hypherionmc.craterlib.nojang.client.gui;
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import net.minecraft.client.gui.screens.LevelLoadingScreen;
|
import net.minecraft.client.gui.screens.*;
|
||||||
import net.minecraft.client.gui.screens.ReceivingLevelScreen;
|
|
||||||
import net.minecraft.client.gui.screens.Screen;
|
|
||||||
import net.minecraft.client.gui.screens.TitleScreen;
|
|
||||||
import net.minecraft.client.gui.screens.multiplayer.JoinMultiplayerScreen;
|
import net.minecraft.client.gui.screens.multiplayer.JoinMultiplayerScreen;
|
||||||
import net.minecraft.realms.RealmsScreen;
|
import net.minecraft.realms.RealmsScreen;
|
||||||
|
|
||||||
@@ -29,6 +26,14 @@ public class BridgedScreen {
|
|||||||
return internal instanceof LevelLoadingScreen || internal instanceof ReceivingLevelScreen;
|
return internal instanceof LevelLoadingScreen || internal instanceof ReceivingLevelScreen;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isPauseScreen() {
|
||||||
|
return internal instanceof PauseScreen;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDisconnetedScreen() {
|
||||||
|
return internal instanceof DisconnectedScreen;
|
||||||
|
}
|
||||||
|
|
||||||
public Screen toMojang() {
|
public Screen toMojang() {
|
||||||
return internal;
|
return internal;
|
||||||
}
|
}
|
||||||
|
@@ -7,6 +7,7 @@ import lombok.RequiredArgsConstructor;
|
|||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
import net.minecraft.server.level.ServerPlayer;
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
import net.minecraft.server.network.ServerGamePacketListenerImpl;
|
import net.minecraft.server.network.ServerGamePacketListenerImpl;
|
||||||
|
import net.minecraft.world.InteractionHand;
|
||||||
import net.minecraft.world.entity.player.Player;
|
import net.minecraft.world.entity.player.Player;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
@@ -49,6 +50,34 @@ public class BridgedPlayer {
|
|||||||
return BridgedBlockPos.of(internal.getOnPos());
|
return BridgedBlockPos.of(internal.getOnPos());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public float getHealth() {
|
||||||
|
return internal.getHealth();
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getMaxHealth() {
|
||||||
|
return internal.getMaxHealth();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getHeldItemMainHand() {
|
||||||
|
String value = "Nothing";
|
||||||
|
|
||||||
|
if (!internal.getItemInHand(InteractionHand.MAIN_HAND).isEmpty()) {
|
||||||
|
value = internal.getItemInHand(InteractionHand.MAIN_HAND).getDisplayName().getString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getHeldItemOffHand() {
|
||||||
|
String value = "Nothing";
|
||||||
|
|
||||||
|
if (!internal.getItemInHand(InteractionHand.OFF_HAND).isEmpty()) {
|
||||||
|
value = internal.getItemInHand(InteractionHand.OFF_HAND).getDisplayName().getString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public ServerGamePacketListenerImpl getConnection() {
|
public ServerGamePacketListenerImpl getConnection() {
|
||||||
if (isServerPlayer()) {
|
if (isServerPlayer()) {
|
||||||
|
@@ -4,5 +4,9 @@
|
|||||||
"t.clc.cancel_discard": "Discard",
|
"t.clc.cancel_discard": "Discard",
|
||||||
"t.clc.quit_config": "Unsaved Changes",
|
"t.clc.quit_config": "Unsaved Changes",
|
||||||
"t.clc.quit_config_sure": "You have unsaved config changes. Are you sure you want to discard them?",
|
"t.clc.quit_config_sure": "You have unsaved config changes. Are you sure you want to discard them?",
|
||||||
"t.clc.quit_discard": "Quit & Discard"
|
"t.clc.quit_discard": "Quit & Discard",
|
||||||
|
|
||||||
|
"cl.buttons.edit": "Edit",
|
||||||
|
"cl.buttons.add_entry": "+ Add Entry",
|
||||||
|
"cl.buttons.entry": "Entry %s"
|
||||||
}
|
}
|
||||||
|
@@ -12,6 +12,7 @@ dependencies {
|
|||||||
stupidRemapArch("dev.ftb.mods:ftb-essentials:${ftb_essentials}")
|
stupidRemapArch("dev.ftb.mods:ftb-essentials:${ftb_essentials}")
|
||||||
stupidRemapArch("dev.ftb.mods:ftb-ranks:${ftb_ranks}")
|
stupidRemapArch("dev.ftb.mods:ftb-ranks:${ftb_ranks}")
|
||||||
|
|
||||||
|
modImplementation("me.shedaniel.cloth:cloth-config-fabric:${cloth_config}")
|
||||||
modImplementation "maven.modrinth:fabrictailor:${fabrictailor}"
|
modImplementation "maven.modrinth:fabrictailor:${fabrictailor}"
|
||||||
modImplementation "maven.modrinth:vanish:${vanish}"
|
modImplementation "maven.modrinth:vanish:${vanish}"
|
||||||
|
|
||||||
@@ -116,8 +117,8 @@ publisher {
|
|||||||
setVersionType("release")
|
setVersionType("release")
|
||||||
setChangelog(rootProject.file("changelog.md"))
|
setChangelog(rootProject.file("changelog.md"))
|
||||||
setProjectVersion("${minecraft_version}-${project.version}")
|
setProjectVersion("${minecraft_version}-${project.version}")
|
||||||
setDisplayName("[FABRIC/QUILT 1.21.3] CraterLib - ${project.version}")
|
setDisplayName("[FABRIC/QUILT 1.21.3/4] CraterLib - ${project.version}")
|
||||||
setGameVersions("1.21.3")
|
setGameVersions("1.21.3", "1.21.4")
|
||||||
setLoaders("fabric", "quilt")
|
setLoaders("fabric", "quilt")
|
||||||
setArtifact(remapJar)
|
setArtifact(remapJar)
|
||||||
setCurseEnvironment("both")
|
setCurseEnvironment("both")
|
||||||
@@ -125,9 +126,11 @@ publisher {
|
|||||||
|
|
||||||
modrinthDepends {
|
modrinthDepends {
|
||||||
required("fabric-api")
|
required("fabric-api")
|
||||||
|
optional("cloth-config", "modmenu")
|
||||||
}
|
}
|
||||||
|
|
||||||
curseDepends {
|
curseDepends {
|
||||||
required("fabric-api")
|
required("fabric-api")
|
||||||
|
optional("cloth-config", "modmenu")
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,8 +1,12 @@
|
|||||||
package com.hypherionmc.craterlib;
|
package com.hypherionmc.craterlib;
|
||||||
|
|
||||||
|
import com.hypherionmc.craterlib.client.gui.config.ClothConfigScreenBuilder;
|
||||||
import com.hypherionmc.craterlib.client.gui.config.CraterConfigScreen;
|
import com.hypherionmc.craterlib.client.gui.config.CraterConfigScreen;
|
||||||
|
import com.hypherionmc.craterlib.core.config.AbstractConfig;
|
||||||
import com.hypherionmc.craterlib.core.config.ConfigController;
|
import com.hypherionmc.craterlib.core.config.ConfigController;
|
||||||
|
import com.hypherionmc.craterlib.core.config.annotations.ClothScreen;
|
||||||
import com.hypherionmc.craterlib.core.config.annotations.NoConfigScreen;
|
import com.hypherionmc.craterlib.core.config.annotations.NoConfigScreen;
|
||||||
|
import com.hypherionmc.craterlib.core.platform.ModloaderEnvironment;
|
||||||
import com.terraformersmc.modmenu.api.ConfigScreenFactory;
|
import com.terraformersmc.modmenu.api.ConfigScreenFactory;
|
||||||
import com.terraformersmc.modmenu.api.ModMenuApi;
|
import com.terraformersmc.modmenu.api.ModMenuApi;
|
||||||
|
|
||||||
@@ -19,8 +23,14 @@ public class CraterLibModMenuIntegration implements ModMenuApi {
|
|||||||
Map<String, ConfigScreenFactory<?>> configScreens = new HashMap<>();
|
Map<String, ConfigScreenFactory<?>> configScreens = new HashMap<>();
|
||||||
|
|
||||||
ConfigController.getWatchedConfigs().forEach((conf, watcher) -> {
|
ConfigController.getWatchedConfigs().forEach((conf, watcher) -> {
|
||||||
if (!conf.getClass().isAnnotationPresent(NoConfigScreen.class)) {
|
AbstractConfig config = watcher.getLeft();
|
||||||
configScreens.put(watcher.getLeft().getModId(), screen -> new CraterConfigScreen(watcher.getLeft(), screen));
|
if (config.getClass().isAnnotationPresent(NoConfigScreen.class))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (watcher.getLeft().getClass().isAnnotationPresent(ClothScreen.class) && (ModloaderEnvironment.INSTANCE.isModLoaded("cloth_config") || ModloaderEnvironment.INSTANCE.isModLoaded("cloth-config") || ModloaderEnvironment.INSTANCE.isModLoaded("clothconfig"))) {
|
||||||
|
configScreens.put(config.getModId(), screen -> ClothConfigScreenBuilder.buildConfigScreen(config, screen));
|
||||||
|
} else {
|
||||||
|
//configScreens.put(config.getModId(), screen -> new CraterConfigScreen(config, screen));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -5,6 +5,8 @@ dependencies {
|
|||||||
// Compat
|
// Compat
|
||||||
// NOT AVAILABLE ON FORGE modImplementation("maven.modrinth:vanishmod:${vanishmod}")
|
// NOT AVAILABLE ON FORGE modImplementation("maven.modrinth:vanishmod:${vanishmod}")
|
||||||
|
|
||||||
|
modImplementation("me.shedaniel.cloth:cloth-config-forge:${cloth_config}")
|
||||||
|
|
||||||
// Do not edit or remove
|
// Do not edit or remove
|
||||||
implementation project(":Common")
|
implementation project(":Common")
|
||||||
}
|
}
|
||||||
@@ -113,4 +115,12 @@ publisher {
|
|||||||
setArtifact(remapJar)
|
setArtifact(remapJar)
|
||||||
setCurseEnvironment("both")
|
setCurseEnvironment("both")
|
||||||
setIsManualRelease(true)
|
setIsManualRelease(true)
|
||||||
|
|
||||||
|
curseDepends {
|
||||||
|
optional("cloth-config")
|
||||||
|
}
|
||||||
|
|
||||||
|
modrinthDepends {
|
||||||
|
optional("cloth-config")
|
||||||
|
}
|
||||||
}
|
}
|
@@ -29,13 +29,14 @@ public class ConfigScreenHandlerMixin {
|
|||||||
@Inject(at = @At("RETURN"), method = "getScreenFactoryFor", cancellable = true, remap = false)
|
@Inject(at = @At("RETURN"), method = "getScreenFactoryFor", cancellable = true, remap = false)
|
||||||
private static void injectConfigScreen(IModInfo selectedMod, CallbackInfoReturnable<Optional<BiFunction<Minecraft, Screen, Screen>>> cir) {
|
private static void injectConfigScreen(IModInfo selectedMod, CallbackInfoReturnable<Optional<BiFunction<Minecraft, Screen, Screen>>> cir) {
|
||||||
ConfigController.getMonitoredConfigs().forEach((conf, watcher) -> {
|
ConfigController.getMonitoredConfigs().forEach((conf, watcher) -> {
|
||||||
if (!conf.getClass().isAnnotationPresent(NoConfigScreen.class)) {
|
AbstractConfig config = watcher.getLeft();
|
||||||
ModuleConfig config = (ModuleConfig) conf;
|
if (config.getClass().isAnnotationPresent(NoConfigScreen.class))
|
||||||
if (config.getModId().equals(selectedMod.getModId())) {
|
return;
|
||||||
cir.setReturnValue(
|
|
||||||
Optional.of((minecraft, screen) -> new CraterConfigScreen(config, screen))
|
if (watcher.getLeft().getClass().isAnnotationPresent(ClothScreen.class) && (ModloaderEnvironment.INSTANCE.isModLoaded("cloth_config") || ModloaderEnvironment.INSTANCE.isModLoaded("cloth-config") || ModloaderEnvironment.INSTANCE.isModLoaded("clothconfig"))) {
|
||||||
);
|
ModList.get().getModContainerById(config.getModId()).ifPresent(c -> c.registerExtensionPoint(IConfigScreenFactory.class, ((minecraft, screen) -> ClothConfigScreenBuilder.buildConfigScreen(config, screen))));
|
||||||
}
|
} else {
|
||||||
|
//ModList.get().getModContainerById(config.getModId()).ifPresent(c -> c.registerExtensionPoint(IConfigScreenFactory.class, ((minecraft, screen) -> new CraterConfigScreen(config, screen))));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@@ -7,6 +7,8 @@ dependencies {
|
|||||||
stupidRemapArch("dev.ftb.mods:ftb-essentials-neoforge:${ftb_essentials}")
|
stupidRemapArch("dev.ftb.mods:ftb-essentials-neoforge:${ftb_essentials}")
|
||||||
stupidRemapArch("dev.ftb.mods:ftb-ranks-neoforge:${ftb_ranks}")
|
stupidRemapArch("dev.ftb.mods:ftb-ranks-neoforge:${ftb_ranks}")
|
||||||
|
|
||||||
|
modImplementation("me.shedaniel.cloth:cloth-config-neoforge:${cloth_config}")
|
||||||
|
|
||||||
// Do not edit or remove
|
// Do not edit or remove
|
||||||
implementation project(":Common")
|
implementation project(":Common")
|
||||||
}
|
}
|
||||||
@@ -114,10 +116,18 @@ publisher {
|
|||||||
setVersionType("release")
|
setVersionType("release")
|
||||||
setChangelog(rootProject.file("changelog.md"))
|
setChangelog(rootProject.file("changelog.md"))
|
||||||
setProjectVersion("${minecraft_version}-${project.version}")
|
setProjectVersion("${minecraft_version}-${project.version}")
|
||||||
setDisplayName("[NeoForge 1.21.3] CraterLib - ${project.version}")
|
setDisplayName("[NeoForge 1.21.3/1.21.4] CraterLib - ${project.version}")
|
||||||
setGameVersions("1.21.3")
|
setGameVersions("1.21.3", "1.21.4")
|
||||||
setLoaders("neoforge")
|
setLoaders("neoforge")
|
||||||
setArtifact(remapJar)
|
setArtifact(remapJar)
|
||||||
setCurseEnvironment("both")
|
setCurseEnvironment("both")
|
||||||
setIsManualRelease(true)
|
setIsManualRelease(true)
|
||||||
|
|
||||||
|
curseDepends {
|
||||||
|
optional("cloth-config")
|
||||||
|
}
|
||||||
|
|
||||||
|
modrinthDepends {
|
||||||
|
optional("cloth-config")
|
||||||
|
}
|
||||||
}
|
}
|
@@ -1,12 +1,15 @@
|
|||||||
package com.hypherionmc.craterlib.client;
|
package com.hypherionmc.craterlib.client;
|
||||||
|
|
||||||
import com.hypherionmc.craterlib.api.events.client.LateInitEvent;
|
import com.hypherionmc.craterlib.api.events.client.LateInitEvent;
|
||||||
|
import com.hypherionmc.craterlib.client.gui.config.ClothConfigScreenBuilder;
|
||||||
import com.hypherionmc.craterlib.client.gui.config.CraterConfigScreen;
|
import com.hypherionmc.craterlib.client.gui.config.CraterConfigScreen;
|
||||||
import com.hypherionmc.craterlib.core.config.AbstractConfig;
|
import com.hypherionmc.craterlib.core.config.AbstractConfig;
|
||||||
import com.hypherionmc.craterlib.core.config.ConfigController;
|
import com.hypherionmc.craterlib.core.config.ConfigController;
|
||||||
|
import com.hypherionmc.craterlib.core.config.annotations.ClothScreen;
|
||||||
import com.hypherionmc.craterlib.core.config.annotations.NoConfigScreen;
|
import com.hypherionmc.craterlib.core.config.annotations.NoConfigScreen;
|
||||||
import com.hypherionmc.craterlib.core.event.CraterEventBus;
|
import com.hypherionmc.craterlib.core.event.CraterEventBus;
|
||||||
import com.hypherionmc.craterlib.core.platform.ClientPlatform;
|
import com.hypherionmc.craterlib.core.platform.ClientPlatform;
|
||||||
|
import com.hypherionmc.craterlib.core.platform.ModloaderEnvironment;
|
||||||
import com.hypherionmc.craterlib.nojang.client.BridgedMinecraft;
|
import com.hypherionmc.craterlib.nojang.client.BridgedMinecraft;
|
||||||
import com.hypherionmc.craterlib.nojang.client.BridgedOptions;
|
import com.hypherionmc.craterlib.nojang.client.BridgedOptions;
|
||||||
import com.hypherionmc.craterlib.nojang.client.multiplayer.BridgedClientLevel;
|
import com.hypherionmc.craterlib.nojang.client.multiplayer.BridgedClientLevel;
|
||||||
@@ -52,9 +55,14 @@ public class NeoForgeClientHelper implements ClientPlatform {
|
|||||||
CraterEventBus.INSTANCE.postEvent(event);
|
CraterEventBus.INSTANCE.postEvent(event);
|
||||||
|
|
||||||
ConfigController.getWatchedConfigs().forEach((conf, watcher) -> {
|
ConfigController.getWatchedConfigs().forEach((conf, watcher) -> {
|
||||||
if (!conf.getClass().isAnnotationPresent(NoConfigScreen.class)) {
|
AbstractConfig config = watcher.getLeft();
|
||||||
AbstractConfig config = watcher.getLeft();
|
if (config.getClass().isAnnotationPresent(NoConfigScreen.class))
|
||||||
ModList.get().getModContainerById(config.getModId()).ifPresent(c -> c.registerExtensionPoint(IConfigScreenFactory.class, ((minecraft, screen) -> new CraterConfigScreen(config, screen))));
|
return;
|
||||||
|
|
||||||
|
if (watcher.getLeft().getClass().isAnnotationPresent(ClothScreen.class) && (ModloaderEnvironment.INSTANCE.isModLoaded("cloth_config") || ModloaderEnvironment.INSTANCE.isModLoaded("cloth-config") || ModloaderEnvironment.INSTANCE.isModLoaded("clothconfig"))) {
|
||||||
|
ModList.get().getModContainerById(config.getModId()).ifPresent(c -> c.registerExtensionPoint(IConfigScreenFactory.class, ((minecraft, screen) -> ClothConfigScreenBuilder.buildConfigScreen(config, screen))));
|
||||||
|
} else {
|
||||||
|
//ModList.get().getModContainerById(config.getModId()).ifPresent(c -> c.registerExtensionPoint(IConfigScreenFactory.class, ((minecraft, screen) -> new CraterConfigScreen(config, screen))));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
13
changelog.md
13
changelog.md
@@ -1,12 +1,9 @@
|
|||||||
**New Features**:
|
|
||||||
|
|
||||||
- Paper Support. Currently only available on [NightBloom](https://nightbloom.cc/project/craterlib/files?loader=paper)
|
|
||||||
- Added APIs for working with FTB Ranks and LuckPerms groups
|
|
||||||
|
|
||||||
**Bug Fixes**:
|
**Bug Fixes**:
|
||||||
|
|
||||||
- Fixed Vanish compact API being swapped
|
- Added a workaround for LuckPerms turning players into ghost players
|
||||||
|
- Fixed Missing Getters on LuckPerms events
|
||||||
|
|
||||||
**Changes**:
|
**New Features**:
|
||||||
|
|
||||||
- Config library now logs which line of the config the error is on
|
- Swapped Built In Config screen for a Cloth Config System, so client side mods can have in-game configs
|
||||||
|
- Added new APIs for Simple RPC (V4 rewrite)
|
@@ -1,8 +1,8 @@
|
|||||||
#Project
|
#Project
|
||||||
version_major=2
|
version_major=2
|
||||||
version_minor=1
|
version_minor=1
|
||||||
version_patch=2
|
version_patch=3
|
||||||
version_build=1
|
version_build=0
|
||||||
|
|
||||||
#Mod
|
#Mod
|
||||||
mod_author=HypherionSA
|
mod_author=HypherionSA
|
||||||
@@ -29,6 +29,7 @@ lombok=1.18.32
|
|||||||
adventure=4.17.0
|
adventure=4.17.0
|
||||||
rpc_sdk=1.0
|
rpc_sdk=1.0
|
||||||
discord_formatter=2.0.0
|
discord_formatter=2.0.0
|
||||||
|
cloth_config=17.0.144
|
||||||
|
|
||||||
# Mod Dependencies
|
# Mod Dependencies
|
||||||
fabrictailor=2.3.1
|
fabrictailor=2.3.1
|
||||||
|
Reference in New Issue
Block a user