[coat-bridge] Implement first version of Tweed Coat bridge
This commit is contained in:
@@ -4,5 +4,18 @@ plugins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
modImplementation(mcLibs.coat)
|
compileOnly("de.siphalor.tweed5:tweed5-core")
|
||||||
|
compileOnly("de.siphalor.tweed5:tweed5-attributes-extension")
|
||||||
|
compileOnly("de.siphalor.tweed5:tweed5-default-extensions")
|
||||||
|
compileOnly("de.siphalor.tweed5:tweed5-weaver-pojo")
|
||||||
|
modCompileOnly(mcLibs.coat)
|
||||||
|
|
||||||
|
listOf("fabric-key-binding-api-v1", "fabric-resource-loader-v0").forEach {
|
||||||
|
modTestmodImplementation(fabricApi.module(it, mcLibs.versions.fabric.api.get()))
|
||||||
|
}
|
||||||
|
testmodImplementation(project(":tweed5-bundle", configuration = "minecraftModElements"))
|
||||||
|
modTestmodImplementation(mcLibs.coat)
|
||||||
|
modTestmodImplementation(mcLibs.amecs.api)
|
||||||
|
testmodImplementation(project(":tweed5-fabric-helper"))
|
||||||
|
testmodImplementation("de.siphalor.tweed5:tweed5-serde-hjson")
|
||||||
}
|
}
|
||||||
|
|||||||
2
tweed5-minecraft/coat-bridge/gradle.properties
Normal file
2
tweed5-minecraft/coat-bridge/gradle.properties
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
module.name = Tweed 5 Coat Bridge
|
||||||
|
module.description = Provides a system that allows to generate a Coat config screen for a Tweed config.
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package de.siphalor.tweed5.coat.bridge.api;
|
||||||
|
|
||||||
|
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Getter;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
@Builder
|
||||||
|
@Getter
|
||||||
|
public class ConfigScreenCreateParams<T> {
|
||||||
|
private final ConfigEntry<T> rootEntry;
|
||||||
|
private final T currentValue;
|
||||||
|
private final T defaultValue;
|
||||||
|
private final Component title;
|
||||||
|
private final String translationKeyPrefix;
|
||||||
|
private final Consumer<T> saveHandler;
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package de.siphalor.tweed5.coat.bridge.api;
|
||||||
|
|
||||||
|
import lombok.AccessLevel;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||||
|
public class TweedCoatAttributes {
|
||||||
|
public static final String BACKGROUND_TEXTURE = "backgroundTexture";
|
||||||
|
public static final String TRANSLATION_KEY = "translationKey";
|
||||||
|
public static final String ENUM_TRANSLATION_KEY = "enumTranslationKey";
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package de.siphalor.tweed5.coat.bridge.api;
|
||||||
|
|
||||||
|
import de.siphalor.coat.screen.ConfigScreen;
|
||||||
|
import de.siphalor.tweed5.coat.bridge.api.mapping.TweedCoatMapper;
|
||||||
|
import de.siphalor.tweed5.coat.bridge.impl.TweedCoatBridgeExtensionImpl;
|
||||||
|
import de.siphalor.tweed5.core.api.extension.TweedExtension;
|
||||||
|
|
||||||
|
public interface TweedCoatBridgeExtension extends TweedExtension {
|
||||||
|
Class<? extends TweedCoatBridgeExtension> DEFAULT = TweedCoatBridgeExtensionImpl.class;
|
||||||
|
String EXTENSION_ID = "coat-bridge";
|
||||||
|
|
||||||
|
//static <T> Function<ConfigEntry<T>, ConfigScreen> createConfigScreen(T value) {
|
||||||
|
// return configEntry -> configEntry.container().extension(TweedCoatBridgeExtension.class)
|
||||||
|
// .map(extension -> extension.createConfigScreen(configEntry, value))
|
||||||
|
// .orElseThrow(() -> new IllegalStateException("No TweedCoatBridgeExtension present"));
|
||||||
|
//}
|
||||||
|
|
||||||
|
void addMapper(TweedCoatMapper<?> mapper);
|
||||||
|
|
||||||
|
<T> ConfigScreen createConfigScreen(ConfigScreenCreateParams<T> params);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default String getId() {
|
||||||
|
return EXTENSION_ID;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
package de.siphalor.tweed5.coat.bridge.api;
|
||||||
|
|
||||||
|
import de.siphalor.coat.util.EnumeratedMaterial;
|
||||||
|
import de.siphalor.tweed5.coat.bridge.api.mapping.TweedCoatMapper;
|
||||||
|
import de.siphalor.tweed5.coat.bridge.impl.TweedCoatMappersImpl;
|
||||||
|
import lombok.AccessLevel;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||||
|
public class TweedCoatMappers {
|
||||||
|
public static TweedCoatMapper<Byte> byteTextMapper() {
|
||||||
|
return TweedCoatMappersImpl.BYTE_TEXT_MAPPER;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TweedCoatMapper<Short> shortTextMapper() {
|
||||||
|
return TweedCoatMappersImpl.SHORT_TEXT_MAPPER;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TweedCoatMapper<Integer> integerTextMapper() {
|
||||||
|
return TweedCoatMappersImpl.INTEGER_TEXT_MAPPER;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TweedCoatMapper<Long> longTextMapper() {
|
||||||
|
return TweedCoatMappersImpl.LONG_TEXT_MAPPER;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TweedCoatMapper<Float> floatTextMapper() {
|
||||||
|
return TweedCoatMappersImpl.FLOAT_TEXT_MAPPER;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TweedCoatMapper<Double> doubleTextMapper() {
|
||||||
|
return TweedCoatMappersImpl.DOUBLE_TEXT_MAPPER;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TweedCoatMapper<Boolean> booleanCheckboxMapper() {
|
||||||
|
return TweedCoatMappersImpl.BOOLEAN_CHECKBOX_MAPPER;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TweedCoatMapper<String> stringTextMapper() {
|
||||||
|
return TweedCoatMappersImpl.STRING_TEXT_MAPPER;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T extends Enum<T>> TweedCoatMapper<T> enumCycleButtonMapper() {
|
||||||
|
//noinspection unchecked
|
||||||
|
return (TweedCoatMapper<T>) TweedCoatMappersImpl.ENUM_CYCLE_BUTTON_MAPPER;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> TweedCoatMapper<T> enumeratedMaterialCycleButtonMapper(
|
||||||
|
Class<T> valueClass,
|
||||||
|
EnumeratedMaterial<T> material
|
||||||
|
) {
|
||||||
|
return new TweedCoatMappersImpl.EnumeratedMaterialCycleButtonMapper<>(valueClass, material);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TweedCoatMapper<Object> compoundCategoryMapper() {
|
||||||
|
return TweedCoatMappersImpl.COMPOUND_CATEGORY_MAPPER;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> TweedCoatMapper<T> convertingTextMapper(
|
||||||
|
Class<T> valueClass,
|
||||||
|
Function<T, String> textMapper,
|
||||||
|
Function<String, T> textParser
|
||||||
|
) {
|
||||||
|
//noinspection unchecked
|
||||||
|
return TweedCoatMappersImpl.convertingTextMapper(new Class[]{valueClass}, textMapper, textParser);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package de.siphalor.tweed5.coat.bridge.api;
|
||||||
|
|
||||||
|
import lombok.AccessLevel;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
|
import net.minecraft.network.chat.MutableComponent;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
|
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||||
|
public class TweedCoatMappingUtils {
|
||||||
|
public static MutableComponent translatableComponentWithFallback(String translationKey, @Nullable String fallback) {
|
||||||
|
return Component.translatableWithFallback(translationKey, fallback == null ? "" : fallback);
|
||||||
|
// FIXME
|
||||||
|
//if (I18n.exists(translationKey)) {
|
||||||
|
// return Component.translatable(translationKey);
|
||||||
|
//} else if (fallback != null) {
|
||||||
|
// return Component.literal(fallback);
|
||||||
|
//} else {
|
||||||
|
// return Component.empty();
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package de.siphalor.tweed5.coat.bridge.api.mapping;
|
||||||
|
|
||||||
|
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||||
|
import lombok.AccessLevel;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
@Builder
|
||||||
|
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
|
||||||
|
@Getter
|
||||||
|
public class TweedCoatEntryCreationContext<T> {
|
||||||
|
private final ConfigEntry<T> entry;
|
||||||
|
private final T currentValue;
|
||||||
|
private final T defaultValue;
|
||||||
|
private final @Nullable Consumer<T> parentSaveHandler;
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package de.siphalor.tweed5.coat.bridge.api.mapping;
|
||||||
|
|
||||||
|
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||||
|
import lombok.AccessLevel;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.Setter;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Getter
|
||||||
|
public
|
||||||
|
class TweedCoatEntryMappingContext {
|
||||||
|
@Getter(AccessLevel.NONE)
|
||||||
|
private final TweedCoatMapper<?> mappingDelegate;
|
||||||
|
private final String entryName;
|
||||||
|
private final String translationKeyPrefix;
|
||||||
|
private final @Nullable Class<?> parentWidgetClass;
|
||||||
|
|
||||||
|
public static Builder rootBuilder(TweedCoatMapper<?> mappingDelegate, String translationKeyPrefix) {
|
||||||
|
return new Builder(mappingDelegate, "root", translationKeyPrefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> TweedCoatEntryMappingResult<T, ?> mapEntry(ConfigEntry<T> entry, TweedCoatEntryMappingContext context) {
|
||||||
|
//noinspection unchecked,rawtypes
|
||||||
|
return (TweedCoatEntryMappingResult<T, ?>) mappingDelegate.mapEntry((ConfigEntry) entry, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder subContextBuilder(String entryName) {
|
||||||
|
return new Builder(mappingDelegate, entryName, translationKeyPrefix + "." + entryName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
public static class Builder {
|
||||||
|
private final TweedCoatMapper<?> mappingDelegate;
|
||||||
|
private final String entryName;
|
||||||
|
private String translationKeyPrefix;
|
||||||
|
private @Nullable Class<?> parentWidgetClass;
|
||||||
|
|
||||||
|
private Builder(TweedCoatMapper<?> mappingDelegate, String entryName, String translationKeyPrefix) {
|
||||||
|
this.mappingDelegate = mappingDelegate;
|
||||||
|
this.entryName = entryName;
|
||||||
|
this.translationKeyPrefix = translationKeyPrefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TweedCoatEntryMappingContext build() {
|
||||||
|
return new TweedCoatEntryMappingContext(mappingDelegate, entryName, translationKeyPrefix, parentWidgetClass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
package de.siphalor.tweed5.coat.bridge.api.mapping;
|
||||||
|
|
||||||
|
import de.siphalor.coat.handler.ConfigEntryHandler;
|
||||||
|
import de.siphalor.coat.input.ConfigInput;
|
||||||
|
import de.siphalor.coat.screen.ConfigContentWidget;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The result of a {@link TweedCoatMapper}.
|
||||||
|
* <br />
|
||||||
|
* There are three types of results:
|
||||||
|
* <ol>
|
||||||
|
* <li>The mapper isn't applicable to the entry.</li>
|
||||||
|
* <li>The mapper is applicable and provides methods for creating
|
||||||
|
* {@link ConfigInput} and {@link ConfigEntryHandler} instances for the config entry.</li>
|
||||||
|
* FIXME
|
||||||
|
* </ol>
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param <T> the actual (Tweed) type of the entry
|
||||||
|
* @param <U> the UI (coat) type of the entry
|
||||||
|
*/
|
||||||
|
public interface TweedCoatEntryMappingResult<T, U> {
|
||||||
|
TweedCoatEntryMappingResult<?, ?> NOT_APPLICABLE = new TweedCoatEntryMappingResult<Object, Object>() {
|
||||||
|
@Override
|
||||||
|
public boolean isApplicable() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable ConfigInput<Object> createInput(TweedCoatEntryCreationContext<Object> context) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable ConfigEntryHandler<Object> createHandler(TweedCoatEntryCreationContext<Object> context) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable ConfigContentWidget createContentWidget(TweedCoatEntryCreationContext<Object> context) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
static <T, U> TweedCoatEntryMappingResult<T, U> notApplicable() {
|
||||||
|
//noinspection unchecked
|
||||||
|
return (TweedCoatEntryMappingResult<T, U>) NOT_APPLICABLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isApplicable();
|
||||||
|
|
||||||
|
@Nullable ConfigInput<U> createInput(TweedCoatEntryCreationContext<T> context);
|
||||||
|
|
||||||
|
@Nullable ConfigEntryHandler<U> createHandler(TweedCoatEntryCreationContext<T> context);
|
||||||
|
|
||||||
|
@Nullable ConfigContentWidget createContentWidget(TweedCoatEntryCreationContext<T> context);
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package de.siphalor.tweed5.coat.bridge.api.mapping;
|
||||||
|
|
||||||
|
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||||
|
|
||||||
|
public interface TweedCoatMapper<T> {
|
||||||
|
TweedCoatEntryMappingResult<T, ?> mapEntry(ConfigEntry<T> entry, TweedCoatEntryMappingContext context);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
package de.siphalor.tweed5.coat.bridge.api.mapping.handler;
|
||||||
|
|
||||||
|
import de.siphalor.coat.handler.ConfigEntryHandler;
|
||||||
|
import de.siphalor.coat.handler.Message;
|
||||||
|
import de.siphalor.tweed5.coat.bridge.impl.TweedCoatMappersImpl;
|
||||||
|
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||||
|
import de.siphalor.tweed5.defaultextensions.validation.api.ValidationExtension;
|
||||||
|
import de.siphalor.tweed5.defaultextensions.validation.api.result.ValidationIssueLevel;
|
||||||
|
import de.siphalor.tweed5.defaultextensions.validation.api.result.ValidationIssues;
|
||||||
|
import de.siphalor.tweed5.defaultextensions.validation.api.result.ValidationResult;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class BasicTweedCoatEntryHandler<T extends @Nullable Object> implements ConfigEntryHandler<T> {
|
||||||
|
protected final ConfigEntry<T> configEntry;
|
||||||
|
protected final T defaultValue;
|
||||||
|
protected final Consumer<T> parentSaveHandler;
|
||||||
|
protected final ValidationExtension validationExtension;
|
||||||
|
|
||||||
|
public BasicTweedCoatEntryHandler(ConfigEntry<T> configEntry, T defaultValue, Consumer<T> parentSaveHandler) {
|
||||||
|
this.configEntry = configEntry;
|
||||||
|
this.defaultValue = defaultValue;
|
||||||
|
this.parentSaveHandler = parentSaveHandler;
|
||||||
|
this.validationExtension = configEntry.container().extension(ValidationExtension.class)
|
||||||
|
.orElseThrow(() -> new IllegalStateException("No validation extension registered"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T getDefault() {
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<Message> getMessages(T value) {
|
||||||
|
ValidationIssues issues = validationExtension.validate(configEntry, value);
|
||||||
|
return issues.issuesByPath().values().stream()
|
||||||
|
.flatMap(entryIssues -> entryIssues.issues().stream())
|
||||||
|
.map(issue -> new Message(
|
||||||
|
mapLevel(issue.level()),
|
||||||
|
Component.literal(issue.message())
|
||||||
|
))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Message.Level mapLevel(ValidationIssueLevel level) {
|
||||||
|
switch (level) {
|
||||||
|
case INFO:
|
||||||
|
return Message.Level.INFO;
|
||||||
|
case WARN:
|
||||||
|
return Message.Level.WARNING;
|
||||||
|
case ERROR:
|
||||||
|
return Message.Level.ERROR;
|
||||||
|
default:
|
||||||
|
throw new IllegalStateException("Unknown validation issue level " + level);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void save(T value) {
|
||||||
|
parentSaveHandler.accept(processSaveValue(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Component asText(T value) {
|
||||||
|
return Component.literal(Objects.toString(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected T processSaveValue(T value) {
|
||||||
|
ValidationResult<T> validationResult = validationExtension.validateValueFlat(configEntry, value);
|
||||||
|
if (validationResult.hasError()) {
|
||||||
|
TweedCoatMappersImpl.log.warn(
|
||||||
|
"Failed to save value " + value + " because of issues: " + validationResult.issues()
|
||||||
|
+ "; using default: " + defaultValue + " instead"
|
||||||
|
);
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
return validationResult.value();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
package de.siphalor.tweed5.coat.bridge.api.mapping.handler;
|
||||||
|
|
||||||
|
import de.siphalor.coat.handler.ConfigEntryHandler;
|
||||||
|
import de.siphalor.coat.handler.Message;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.apachecommons.CommonsLog;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@CommonsLog
|
||||||
|
public class ConvertingTweedCoatEntryHandler<T extends @Nullable Object, C> implements ConfigEntryHandler<C> {
|
||||||
|
private static final String CONVERSION_EXCEPTION_TEXT_KEY = "tweed5_coat_bridge.handler.conversion.exception";
|
||||||
|
|
||||||
|
private final ConfigEntryHandler<T> inner;
|
||||||
|
private final Function<T, C> toCoatMapper;
|
||||||
|
private final Function<C, T> fromCoatMapper;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public C getDefault() {
|
||||||
|
return toCoatMapper.apply(inner.getDefault());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<Message> getMessages(C value) {
|
||||||
|
try {
|
||||||
|
T innerValue = fromCoatMapper.apply(value);
|
||||||
|
return inner.getMessages(innerValue);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return Collections.singletonList(new Message(
|
||||||
|
Message.Level.ERROR,
|
||||||
|
Component.translatable(CONVERSION_EXCEPTION_TEXT_KEY, e.getMessage())
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void save(C value) {
|
||||||
|
inner.save(convertSaveValue(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected T convertSaveValue(C value) {
|
||||||
|
try {
|
||||||
|
return fromCoatMapper.apply(value);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn(
|
||||||
|
"Failed to convert value "
|
||||||
|
+ value
|
||||||
|
+ " for saving, using default: "
|
||||||
|
+ inner.getDefault(), e
|
||||||
|
);
|
||||||
|
return inner.getDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Component asText(C value) {
|
||||||
|
return Component.literal(Objects.toString(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
@NullMarked
|
||||||
|
package de.siphalor.tweed5.coat.bridge.api.mapping.handler;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.NullMarked;
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
@NullMarked
|
||||||
|
package de.siphalor.tweed5.coat.bridge.api.mapping;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.NullMarked;
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
@NullMarked
|
||||||
|
package de.siphalor.tweed5.coat.bridge.api;
|
||||||
|
|
||||||
|
import org.jspecify.annotations.NullMarked;
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package de.siphalor.tweed5.coat.bridge.impl;
|
||||||
|
|
||||||
|
import de.siphalor.coat.util.EnumeratedMaterial;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import static de.siphalor.tweed5.coat.bridge.api.TweedCoatMappingUtils.translatableComponentWithFallback;
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class CoatEnumMaterial<T> implements EnumeratedMaterial<T> {
|
||||||
|
private final Class<T> enumClass;
|
||||||
|
private final String textTranslationKeyPrefix;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T[] values() {
|
||||||
|
return enumClass.getEnumConstants();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Component asText(T value) {
|
||||||
|
return translatableComponentWithFallback(
|
||||||
|
textTranslationKeyPrefix + "." + value.toString().toLowerCase(Locale.ROOT),
|
||||||
|
value.toString()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
package de.siphalor.tweed5.coat.bridge.impl;
|
||||||
|
|
||||||
|
import de.siphalor.coat.screen.ConfigContentWidget;
|
||||||
|
import de.siphalor.coat.screen.ConfigScreen;
|
||||||
|
import de.siphalor.tweed5.coat.bridge.api.ConfigScreenCreateParams;
|
||||||
|
import de.siphalor.tweed5.coat.bridge.api.TweedCoatBridgeExtension;
|
||||||
|
import de.siphalor.tweed5.coat.bridge.api.mapping.TweedCoatEntryCreationContext;
|
||||||
|
import de.siphalor.tweed5.coat.bridge.api.mapping.TweedCoatEntryMappingContext;
|
||||||
|
import de.siphalor.tweed5.coat.bridge.api.mapping.TweedCoatEntryMappingResult;
|
||||||
|
import de.siphalor.tweed5.coat.bridge.api.mapping.TweedCoatMapper;
|
||||||
|
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||||
|
import de.siphalor.tweed5.core.api.extension.TweedExtensionSetupContext;
|
||||||
|
import de.siphalor.tweed5.patchwork.api.PatchworkPartAccess;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class TweedCoatBridgeExtensionImpl implements TweedCoatBridgeExtension {
|
||||||
|
private final PatchworkPartAccess<CustomData> customDataAccess;
|
||||||
|
private final List<TweedCoatMapper<?>> mappers = new ArrayList<>();
|
||||||
|
|
||||||
|
public TweedCoatBridgeExtensionImpl(TweedExtensionSetupContext setupContext) {
|
||||||
|
customDataAccess = setupContext.registerEntryExtensionData(CustomData.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addMapper(TweedCoatMapper<?> mapper) {
|
||||||
|
mappers.add(mapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> ConfigScreen createConfigScreen(ConfigScreenCreateParams<T> params) {
|
||||||
|
Minecraft minecraft = Minecraft.getInstance();
|
||||||
|
|
||||||
|
TweedCoatEntryMappingContext mappingContext = TweedCoatEntryMappingContext.rootBuilder(
|
||||||
|
this::mapEntry,
|
||||||
|
params.translationKeyPrefix()
|
||||||
|
).parentWidgetClass(ConfigScreen.class).build();
|
||||||
|
|
||||||
|
TweedCoatEntryMappingResult<T, ?> rootResult = mapEntry(params.rootEntry(), mappingContext);
|
||||||
|
if (!rootResult.isApplicable()) {
|
||||||
|
throw new IllegalStateException("Failed to map root entry");
|
||||||
|
}
|
||||||
|
|
||||||
|
T value = params.rootEntry().deepCopy(params.currentValue());
|
||||||
|
TweedCoatEntryCreationContext<T> creationContext = TweedCoatEntryCreationContext.<T>builder()
|
||||||
|
.entry(params.rootEntry())
|
||||||
|
.currentValue(value)
|
||||||
|
.defaultValue(params.defaultValue())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
ConfigContentWidget contentWidget = rootResult.createContentWidget(creationContext);
|
||||||
|
if (contentWidget == null) {
|
||||||
|
throw new IllegalStateException("Failed to create root content widget");
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigScreen configScreen = new ConfigScreen(
|
||||||
|
minecraft.screen, params.title(), Collections.singletonList(contentWidget)
|
||||||
|
);
|
||||||
|
configScreen.setOnSave(() -> params.saveHandler().accept(value));
|
||||||
|
return configScreen;
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T> TweedCoatEntryMappingResult<T, ?> mapEntry(
|
||||||
|
ConfigEntry<T> entry,
|
||||||
|
TweedCoatEntryMappingContext context
|
||||||
|
) {
|
||||||
|
for (TweedCoatMapper<?> mapper : mappers) {
|
||||||
|
//noinspection rawtypes,unchecked
|
||||||
|
TweedCoatEntryMappingResult<T, ?> result = mapper.mapEntry((ConfigEntry) entry, context);
|
||||||
|
if (result.isApplicable()) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return TweedCoatEntryMappingResult.notApplicable();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class CustomData {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,370 @@
|
|||||||
|
package de.siphalor.tweed5.coat.bridge.impl;
|
||||||
|
|
||||||
|
import de.siphalor.coat.handler.ConfigEntryHandler;
|
||||||
|
import de.siphalor.coat.input.CheckBoxConfigInput;
|
||||||
|
import de.siphalor.coat.input.ConfigInput;
|
||||||
|
import de.siphalor.coat.input.CycleButtonConfigInput;
|
||||||
|
import de.siphalor.coat.input.TextConfigInput;
|
||||||
|
import de.siphalor.coat.list.complex.ConfigCategoryWidget;
|
||||||
|
import de.siphalor.coat.list.entry.ConfigCategoryConfigEntry;
|
||||||
|
import de.siphalor.coat.screen.ConfigContentWidget;
|
||||||
|
import de.siphalor.coat.util.EnumeratedMaterial;
|
||||||
|
import de.siphalor.tweed5.attributesextension.api.AttributesExtension;
|
||||||
|
import de.siphalor.tweed5.coat.bridge.api.TweedCoatAttributes;
|
||||||
|
import de.siphalor.tweed5.coat.bridge.api.mapping.TweedCoatEntryCreationContext;
|
||||||
|
import de.siphalor.tweed5.coat.bridge.api.mapping.TweedCoatEntryMappingContext;
|
||||||
|
import de.siphalor.tweed5.coat.bridge.api.mapping.TweedCoatEntryMappingResult;
|
||||||
|
import de.siphalor.tweed5.coat.bridge.api.mapping.TweedCoatMapper;
|
||||||
|
import de.siphalor.tweed5.coat.bridge.api.mapping.handler.BasicTweedCoatEntryHandler;
|
||||||
|
import de.siphalor.tweed5.coat.bridge.api.mapping.handler.ConvertingTweedCoatEntryHandler;
|
||||||
|
import de.siphalor.tweed5.core.api.entry.CompoundConfigEntry;
|
||||||
|
import de.siphalor.tweed5.core.api.entry.ConfigEntry;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.Value;
|
||||||
|
import lombok.extern.apachecommons.CommonsLog;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import org.jspecify.annotations.NonNull;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static de.siphalor.tweed5.coat.bridge.api.TweedCoatMappingUtils.translatableComponentWithFallback;
|
||||||
|
|
||||||
|
@CommonsLog
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public class TweedCoatMappersImpl {
|
||||||
|
public static TweedCoatMapper<Byte> BYTE_TEXT_MAPPER = convertingTextMapper(
|
||||||
|
new Class[]{Byte.class, byte.class},
|
||||||
|
value -> Byte.toString(value),
|
||||||
|
Byte::parseByte
|
||||||
|
);
|
||||||
|
public static TweedCoatMapper<Short> SHORT_TEXT_MAPPER = convertingTextMapper(
|
||||||
|
new Class[]{Short.class, short.class},
|
||||||
|
value -> Short.toString(value),
|
||||||
|
Short::parseShort
|
||||||
|
);
|
||||||
|
public static TweedCoatMapper<Integer> INTEGER_TEXT_MAPPER = convertingTextMapper(
|
||||||
|
new Class[]{Integer.class, int.class},
|
||||||
|
value -> Integer.toString(value),
|
||||||
|
Integer::parseInt
|
||||||
|
);
|
||||||
|
public static TweedCoatMapper<Long> LONG_TEXT_MAPPER = convertingTextMapper(
|
||||||
|
new Class[]{Long.class, long.class},
|
||||||
|
value -> Long.toString(value),
|
||||||
|
Long::parseLong
|
||||||
|
);
|
||||||
|
public static TweedCoatMapper<Float> FLOAT_TEXT_MAPPER = convertingTextMapper(
|
||||||
|
new Class[]{Float.class, float.class},
|
||||||
|
value -> Float.toString(value),
|
||||||
|
Float::parseFloat
|
||||||
|
);
|
||||||
|
public static TweedCoatMapper<Double> DOUBLE_TEXT_MAPPER = convertingTextMapper(
|
||||||
|
new Class[]{Double.class, double.class},
|
||||||
|
value -> Double.toString(value),
|
||||||
|
Double::parseDouble
|
||||||
|
);
|
||||||
|
public static TweedCoatMapper<Boolean> BOOLEAN_CHECKBOX_MAPPER
|
||||||
|
= new SimpleMapper<Boolean>(new Class[]{Boolean.class, boolean.class}, CheckBoxConfigInput::new);
|
||||||
|
|
||||||
|
public static TweedCoatMapper<String> STRING_TEXT_MAPPER = new SimpleMapper<String>(
|
||||||
|
new Class[]{String.class},
|
||||||
|
TextConfigInput::new
|
||||||
|
);
|
||||||
|
|
||||||
|
public static TweedCoatMapper<Enum<?>> ENUM_CYCLE_BUTTON_MAPPER = new EnumCycleButtonMapper<>();
|
||||||
|
|
||||||
|
public static TweedCoatMapper<Object> COMPOUND_CATEGORY_MAPPER = new CompoundCategoryMapper<>();
|
||||||
|
|
||||||
|
public static <T> TweedCoatMapper<T> convertingTextMapper(
|
||||||
|
Class<T>[] valueClasses,
|
||||||
|
Function<T, String> textMapper,
|
||||||
|
Function<String, T> textParser
|
||||||
|
) {
|
||||||
|
return new ConvertingTextMapper<>(valueClasses, textMapper, textParser);
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public static class ConvertingTextMapper<T> implements TweedCoatMapper<T> {
|
||||||
|
private final Class<T>[] valueClasses;
|
||||||
|
private final Function<T, String> textMapper;
|
||||||
|
private final Function<String, T> textParser;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TweedCoatEntryMappingResult<T, String> mapEntry(ConfigEntry<T> entry, TweedCoatEntryMappingContext context) {
|
||||||
|
boolean applicable = anyClassMatches(entry.valueClass(), valueClasses);
|
||||||
|
|
||||||
|
return new TweedCoatEntryMappingResult<T, String>() {
|
||||||
|
@Override
|
||||||
|
public boolean isApplicable() {
|
||||||
|
return applicable;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigInput<String> createInput(TweedCoatEntryCreationContext<T> context) {
|
||||||
|
return new TextConfigInput(textMapper.apply(context.currentValue()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigEntryHandler<String> createHandler(TweedCoatEntryCreationContext<T> context) {
|
||||||
|
if (context.parentSaveHandler() == null) {
|
||||||
|
throw new IllegalArgumentException("No parent save handler provided");
|
||||||
|
}
|
||||||
|
return new ConvertingTweedCoatEntryHandler<>(
|
||||||
|
new BasicTweedCoatEntryHandler<>(
|
||||||
|
context.entry(), context.defaultValue(), context.parentSaveHandler()
|
||||||
|
),
|
||||||
|
textMapper,
|
||||||
|
textParser
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable ConfigContentWidget createContentWidget(TweedCoatEntryCreationContext<T> context) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public static class EnumeratedMaterialCycleButtonMapper<T> implements TweedCoatMapper<T> {
|
||||||
|
private final Class<T> valueClass;
|
||||||
|
private final EnumeratedMaterial<T> material;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TweedCoatEntryMappingResult<T, ?> mapEntry(ConfigEntry<T> entry, TweedCoatEntryMappingContext context) {
|
||||||
|
if (!valueClass.isAssignableFrom(entry.valueClass())) {
|
||||||
|
return TweedCoatEntryMappingResult.notApplicable();
|
||||||
|
}
|
||||||
|
|
||||||
|
return new CycleButtonMappingResult<>(material);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class EnumCycleButtonMapper<T extends Enum<?>> implements TweedCoatMapper<T> {
|
||||||
|
@Override
|
||||||
|
public TweedCoatEntryMappingResult<T, ?> mapEntry(ConfigEntry<T> entry, TweedCoatEntryMappingContext context) {
|
||||||
|
if (!Enum.class.isAssignableFrom(entry.valueClass())) {
|
||||||
|
return TweedCoatEntryMappingResult.notApplicable();
|
||||||
|
}
|
||||||
|
Class<T> enumClass = entry.valueClass();
|
||||||
|
|
||||||
|
String translationKeyPrefix = entry.container().extension(AttributesExtension.class)
|
||||||
|
.map(extension -> extension.getAttributeValue(entry, TweedCoatAttributes.ENUM_TRANSLATION_KEY))
|
||||||
|
.orElse(enumClass.getPackage().getName());
|
||||||
|
|
||||||
|
CoatEnumMaterial<T> material = new CoatEnumMaterial<>(enumClass, translationKeyPrefix + ".");
|
||||||
|
return new CycleButtonMappingResult<>(material);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
private static class CycleButtonMappingResult<T> implements TweedCoatEntryMappingResult<T, T> {
|
||||||
|
private final EnumeratedMaterial<T> material;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isApplicable() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigInput<T> createInput(TweedCoatEntryCreationContext<T> context) {
|
||||||
|
return new CycleButtonConfigInput<>(material, false, context.currentValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigEntryHandler<T> createHandler(TweedCoatEntryCreationContext<T> context) {
|
||||||
|
if (context.parentSaveHandler() == null) {
|
||||||
|
throw new IllegalArgumentException("No parent save handler provided");
|
||||||
|
}
|
||||||
|
return new BasicTweedCoatEntryHandler<>(
|
||||||
|
context.entry(), context.defaultValue(), context.parentSaveHandler()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable ConfigContentWidget createContentWidget(TweedCoatEntryCreationContext<T> context) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
private static class SimpleMapper<T> implements TweedCoatMapper<T> {
|
||||||
|
private final Class<T>[] valueClasses;
|
||||||
|
private final Function<T, ConfigInput<T>> inputFactory;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TweedCoatEntryMappingResult<T, T> mapEntry(ConfigEntry<T> entry, TweedCoatEntryMappingContext context) {
|
||||||
|
matchingClass: {
|
||||||
|
for (Class<T> valueClass : valueClasses) {
|
||||||
|
if (entry.valueClass() == valueClass) {
|
||||||
|
break matchingClass;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return TweedCoatEntryMappingResult.notApplicable();
|
||||||
|
}
|
||||||
|
|
||||||
|
return new TweedCoatEntryMappingResult<T, T>() {
|
||||||
|
@Override
|
||||||
|
public boolean isApplicable() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigInput<T> createInput(TweedCoatEntryCreationContext<T> context) {
|
||||||
|
return inputFactory.apply(context.currentValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigEntryHandler<T> createHandler(TweedCoatEntryCreationContext<T> context) {
|
||||||
|
if (context.parentSaveHandler() == null) {
|
||||||
|
throw new IllegalArgumentException("No parent save handler provided");
|
||||||
|
}
|
||||||
|
return new BasicTweedCoatEntryHandler<>(
|
||||||
|
context.entry(), context.defaultValue(), context.parentSaveHandler()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable ConfigContentWidget createContentWidget(TweedCoatEntryCreationContext<T> context) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class CompoundCategoryMapper<T> implements TweedCoatMapper<T> {
|
||||||
|
@Override
|
||||||
|
public TweedCoatEntryMappingResult<T, ?> mapEntry(ConfigEntry<T> entry, TweedCoatEntryMappingContext mappingContext) {
|
||||||
|
@Value
|
||||||
|
class MappedEntry<U> {
|
||||||
|
String name;
|
||||||
|
String translationKeyPrefix;
|
||||||
|
ConfigEntry<U> entry;
|
||||||
|
TweedCoatEntryMappingContext mappingContext;
|
||||||
|
TweedCoatEntryMappingResult<U, ?> mappingResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(entry instanceof CompoundConfigEntry)) {
|
||||||
|
return TweedCoatEntryMappingResult.notApplicable();
|
||||||
|
}
|
||||||
|
CompoundConfigEntry<T> compoundEntry = (CompoundConfigEntry<T>) entry;
|
||||||
|
|
||||||
|
Optional<AttributesExtension> attributesExtension = entry.container().extension(AttributesExtension.class);
|
||||||
|
ResourceLocation backgroundTexture = attributesExtension
|
||||||
|
.map(extension -> extension.getAttributeValue(
|
||||||
|
entry,
|
||||||
|
TweedCoatAttributes.BACKGROUND_TEXTURE
|
||||||
|
))
|
||||||
|
.map(ResourceLocation::tryParse)
|
||||||
|
.orElse(null);
|
||||||
|
String translationKey = attributesExtension
|
||||||
|
.map(extension -> extension.getAttributeValue(
|
||||||
|
entry,
|
||||||
|
TweedCoatAttributes.TRANSLATION_KEY
|
||||||
|
))
|
||||||
|
.orElse(mappingContext.translationKeyPrefix());
|
||||||
|
|
||||||
|
List<MappedEntry<Object>> mappedEntries = compoundEntry.subEntries().entrySet().stream()
|
||||||
|
.map(mapEntry -> {
|
||||||
|
String subTranslationKeyPrefix = translationKey + "." + mapEntry.getKey();
|
||||||
|
TweedCoatEntryMappingContext subMappingContext = mappingContext.subContextBuilder(mapEntry.getKey())
|
||||||
|
.translationKeyPrefix(subTranslationKeyPrefix)
|
||||||
|
.parentWidgetClass(ConfigCategoryWidget.class)
|
||||||
|
.build();
|
||||||
|
return new MappedEntry<>(
|
||||||
|
mapEntry.getKey(),
|
||||||
|
subTranslationKeyPrefix,
|
||||||
|
(ConfigEntry<Object>) mapEntry.getValue(),
|
||||||
|
subMappingContext,
|
||||||
|
(TweedCoatEntryMappingResult<@NonNull Object, ?>) subMappingContext.mapEntry(
|
||||||
|
mapEntry.getValue(),
|
||||||
|
subMappingContext
|
||||||
|
)
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.filter(mappedEntry -> mappedEntry.mappingResult.isApplicable())
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
return new TweedCoatEntryMappingResult<T, T>() {
|
||||||
|
@Override
|
||||||
|
public boolean isApplicable() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable ConfigInput<T> createInput(TweedCoatEntryCreationContext<T> context) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable ConfigEntryHandler<T> createHandler(TweedCoatEntryCreationContext<T> context) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConfigContentWidget createContentWidget(TweedCoatEntryCreationContext<T> context) {
|
||||||
|
ConfigCategoryWidget categoryWidget = new ConfigCategoryWidget(
|
||||||
|
Minecraft.getInstance(),
|
||||||
|
translatableComponentWithFallback(
|
||||||
|
translationKey,
|
||||||
|
mappingContext.entryName()
|
||||||
|
),
|
||||||
|
Collections.emptyList(),
|
||||||
|
backgroundTexture
|
||||||
|
);
|
||||||
|
for (MappedEntry<Object> mappedEntry : mappedEntries) {
|
||||||
|
TweedCoatEntryMappingResult<Object, ?> mappingResult = mappedEntry.mappingResult();
|
||||||
|
if (!mappingResult.isApplicable()) {
|
||||||
|
log.warn(
|
||||||
|
"Failed to resolve mapping for entry \"" + mappedEntry.name() + "\" at \""
|
||||||
|
+ translationKey + "\". Entry will be ignored in UI."
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object subEntryValue = compoundEntry.get(context.currentValue(), mappedEntry.name());
|
||||||
|
Object subEntryDefaultValue = compoundEntry.get(context.defaultValue(), mappedEntry.name());
|
||||||
|
TweedCoatEntryCreationContext<Object> creationContext = TweedCoatEntryCreationContext.builder()
|
||||||
|
.entry(mappedEntry.entry())
|
||||||
|
.currentValue(subEntryValue)
|
||||||
|
.defaultValue(subEntryDefaultValue)
|
||||||
|
.parentSaveHandler(value -> compoundEntry.set(context.currentValue(), mappedEntry.name(), value))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
ConfigInput<?> input = mappingResult.createInput(creationContext);
|
||||||
|
if (input != null) {
|
||||||
|
ConfigCategoryConfigEntry<Object> entry = new ConfigCategoryConfigEntry<>(
|
||||||
|
translatableComponentWithFallback(mappedEntry.translationKeyPrefix(), mappedEntry.name()),
|
||||||
|
translatableComponentWithFallback(mappedEntry.translationKeyPrefix() + ".description", null),
|
||||||
|
(ConfigEntryHandler<Object>) mappingResult
|
||||||
|
.createHandler(creationContext),
|
||||||
|
(ConfigInput<Object>) input
|
||||||
|
);
|
||||||
|
categoryWidget.addEntry(entry);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigContentWidget contentWidget = mappingResult.createContentWidget(creationContext);
|
||||||
|
if (contentWidget != null) {
|
||||||
|
categoryWidget.addSubTree(contentWidget);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return categoryWidget;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean anyClassMatches(Object value, Class<?>... classes) {
|
||||||
|
for (Class<?> clazz : classes) {
|
||||||
|
if (clazz.isInstance(value)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
@NullMarked
|
||||||
|
@ApiStatus.Internal
|
||||||
|
package de.siphalor.tweed5.coat.bridge.impl;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.ApiStatus;
|
||||||
|
import org.jspecify.annotations.NullMarked;
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"tweed5_coat_bridge.handler.conversion.exception": "Failed to convert: %s"
|
||||||
|
}
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
package de.siphalor.tweed5.coat.bridge.testmod;
|
||||||
|
|
||||||
|
import de.siphalor.amecs.api.PriorityKeyBinding;
|
||||||
|
import de.siphalor.coat.screen.ConfigScreen;
|
||||||
|
import de.siphalor.tweed5.coat.bridge.api.ConfigScreenCreateParams;
|
||||||
|
import de.siphalor.tweed5.coat.bridge.api.TweedCoatBridgeExtension;
|
||||||
|
import de.siphalor.tweed5.coat.bridge.api.TweedCoatMappers;
|
||||||
|
import de.siphalor.tweed5.core.api.container.ConfigContainer;
|
||||||
|
import de.siphalor.tweed5.data.hjson.HjsonSerde;
|
||||||
|
import de.siphalor.tweed5.data.hjson.HjsonWriter;
|
||||||
|
import de.siphalor.tweed5.fabric.helper.api.FabricConfigContainerHelper;
|
||||||
|
import de.siphalor.tweed5.weaver.pojo.impl.weaving.TweedPojoWeaverBootstrapper;
|
||||||
|
import lombok.extern.apachecommons.CommonsLog;
|
||||||
|
import net.fabricmc.api.ClientModInitializer;
|
||||||
|
import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper;
|
||||||
|
import net.minecraft.client.KeyMapping;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.gui.screens.TitleScreen;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
@CommonsLog
|
||||||
|
public class TweedCoatBridgeTestMod implements ClientModInitializer {
|
||||||
|
public static final String MOD_ID = "tweed5_coat_bridge_testmod";
|
||||||
|
|
||||||
|
private static final TweedCoatBridgeTestModConfig DEFAULT_CONFIG_VALUE = new TweedCoatBridgeTestModConfig();
|
||||||
|
|
||||||
|
private ConfigContainer<TweedCoatBridgeTestModConfig> configContainer;
|
||||||
|
private TweedCoatBridgeExtension configCoatBridgeExtension;
|
||||||
|
private FabricConfigContainerHelper<TweedCoatBridgeTestModConfig> configContainerHelper;
|
||||||
|
private TweedCoatBridgeTestModConfig config;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onInitializeClient() {
|
||||||
|
configContainer = TweedPojoWeaverBootstrapper.create(TweedCoatBridgeTestModConfig.class).weave();
|
||||||
|
configCoatBridgeExtension = configContainer.extension(TweedCoatBridgeExtension.class)
|
||||||
|
.orElseThrow(() -> new IllegalStateException("TweedCoatBridgeExtension not found"));
|
||||||
|
Arrays.asList(
|
||||||
|
TweedCoatMappers.compoundCategoryMapper(),
|
||||||
|
TweedCoatMappers.stringTextMapper(),
|
||||||
|
TweedCoatMappers.integerTextMapper()
|
||||||
|
).forEach(configCoatBridgeExtension::addMapper);
|
||||||
|
|
||||||
|
configContainer.initialize();
|
||||||
|
|
||||||
|
configContainerHelper = FabricConfigContainerHelper.create(
|
||||||
|
configContainer,
|
||||||
|
new HjsonSerde(new HjsonWriter.Options()),
|
||||||
|
MOD_ID
|
||||||
|
);
|
||||||
|
|
||||||
|
config = configContainerHelper.loadAndUpdateInConfigDirectory(() -> DEFAULT_CONFIG_VALUE);
|
||||||
|
|
||||||
|
KeyBindingHelper.registerKeyBinding(new ScreenKeyBinding(MOD_ID + ".config", 84, KeyMapping.Category.MISC));
|
||||||
|
|
||||||
|
log.info("current config");
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ScreenKeyBinding extends KeyMapping implements PriorityKeyBinding {
|
||||||
|
public ScreenKeyBinding(String name, int key, Category category) {
|
||||||
|
super(name, key, category);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onPressedPriority() {
|
||||||
|
if (!(Minecraft.getInstance().screen instanceof TitleScreen)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigScreen configScreen = configCoatBridgeExtension.createConfigScreen(
|
||||||
|
ConfigScreenCreateParams.<TweedCoatBridgeTestModConfig>builder()
|
||||||
|
.translationKeyPrefix(MOD_ID + ".config")
|
||||||
|
.title(Component.translatable(MOD_ID + ".title"))
|
||||||
|
.rootEntry(configContainer.rootEntry())
|
||||||
|
.currentValue(config)
|
||||||
|
.defaultValue(DEFAULT_CONFIG_VALUE)
|
||||||
|
.saveHandler(value -> {
|
||||||
|
config = value;
|
||||||
|
log.info("Updated config: " + config);
|
||||||
|
configContainerHelper.writeConfigInConfigDirectory(config);
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
Minecraft.getInstance().setScreen(configScreen);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
package de.siphalor.tweed5.coat.bridge.testmod;
|
||||||
|
|
||||||
|
import de.siphalor.tweed5.attributesextension.api.AttributesExtension;
|
||||||
|
import de.siphalor.tweed5.coat.bridge.api.TweedCoatAttributes;
|
||||||
|
import de.siphalor.tweed5.coat.bridge.api.TweedCoatBridgeExtension;
|
||||||
|
import de.siphalor.tweed5.data.extension.api.ReadWriteExtension;
|
||||||
|
import de.siphalor.tweed5.defaultextensions.validation.api.ValidationExtension;
|
||||||
|
import de.siphalor.tweed5.weaver.pojo.api.annotation.CompoundWeaving;
|
||||||
|
import de.siphalor.tweed5.weaver.pojo.api.annotation.DefaultWeavingExtensions;
|
||||||
|
import de.siphalor.tweed5.weaver.pojo.api.annotation.PojoWeaving;
|
||||||
|
import de.siphalor.tweed5.weaver.pojo.api.annotation.PojoWeavingExtension;
|
||||||
|
import de.siphalor.tweed5.weaver.pojoext.attributes.api.Attribute;
|
||||||
|
import de.siphalor.tweed5.weaver.pojoext.attributes.api.AttributesPojoWeavingProcessor;
|
||||||
|
import de.siphalor.tweed5.weaver.pojoext.serde.api.auto.AutoReadWritePojoWeavingProcessor;
|
||||||
|
import de.siphalor.tweed5.weaver.pojoext.serde.api.auto.DefaultReadWriteMappings;
|
||||||
|
import de.siphalor.tweed5.weaver.pojoext.validation.api.Validator;
|
||||||
|
import de.siphalor.tweed5.weaver.pojoext.validation.api.ValidatorsPojoWeavingProcessor;
|
||||||
|
import de.siphalor.tweed5.weaver.pojoext.validation.api.validators.WeavableNumberRangeValidator;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@PojoWeaving(extensions = {
|
||||||
|
ReadWriteExtension.class,
|
||||||
|
TweedCoatBridgeExtension.class,
|
||||||
|
ValidationExtension.class,
|
||||||
|
AttributesExtension.class,
|
||||||
|
})
|
||||||
|
@PojoWeavingExtension(AutoReadWritePojoWeavingProcessor.class)
|
||||||
|
@PojoWeavingExtension(ValidatorsPojoWeavingProcessor.class)
|
||||||
|
@PojoWeavingExtension(AttributesPojoWeavingProcessor.class)
|
||||||
|
@DefaultWeavingExtensions
|
||||||
|
@DefaultReadWriteMappings
|
||||||
|
@CompoundWeaving(namingFormat = "kebab_case")
|
||||||
|
@Data
|
||||||
|
public class TweedCoatBridgeTestModConfig {
|
||||||
|
private String test = "hello world";
|
||||||
|
private int someInteger = 123;
|
||||||
|
@Validator(value = WeavableNumberRangeValidator.class, config = "-10=..=10")
|
||||||
|
private int integerInRange = -5;
|
||||||
|
|
||||||
|
@Attribute(key = TweedCoatAttributes.BACKGROUND_TEXTURE, values = "textures/block/green_terracotta.png")
|
||||||
|
private Greeting serverGreeting = new Greeting("Hello server!", "Server");
|
||||||
|
@Attribute(key = TweedCoatAttributes.BACKGROUND_TEXTURE, values = "textures/block/red_terracotta.png")
|
||||||
|
private Greeting clientGreeting = new Greeting("Hello client!", "Client");
|
||||||
|
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@CompoundWeaving
|
||||||
|
public static class Greeting {
|
||||||
|
public String greeting;
|
||||||
|
public String greeter;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"tweed5_coat_bridge_testmod.title": "Test Mod",
|
||||||
|
"tweed5_coat_bridge_testmod.config": "Test Mod config",
|
||||||
|
"tweed5_coat_bridge_testmod.config.test": "Test entry",
|
||||||
|
"tweed5_coat_bridge_testmod.config.test.description": "Just a simple string entry",
|
||||||
|
"tweed5_coat_bridge_testmod.config.some-integer": "Some integer",
|
||||||
|
"tweed5_coat_bridge_testmod.config.integer-in-range": "Integer in range"
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"schemaVersion": 1,
|
||||||
|
"id": "tweed5_coat_bridge_testmod",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"entrypoints": {
|
||||||
|
"client": [
|
||||||
|
"de.siphalor.tweed5.coat.bridge.testmod.TweedCoatBridgeTestMod"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +1,12 @@
|
|||||||
[versions]
|
[versions]
|
||||||
coat = "1.0.0-beta.23"
|
amecs-api = "1.6.2"
|
||||||
|
coat = "1.0.0-beta.24.local.3"
|
||||||
fabric-api = "0.136.0+1.21.10"
|
fabric-api = "0.136.0+1.21.10"
|
||||||
minecraft = "1.21.10"
|
minecraft = "1.21.10"
|
||||||
parchment = "2025.10.12"
|
parchment = "2025.10.12"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
|
amecs-api = { group = "de.siphalor.amecs-api", name = "amecs-api-mc1.21.9", version.ref = "amecs-api" }
|
||||||
coat = { group = "de.siphalor.coat", name = "coat-mc1.21.10", version.ref = "coat" }
|
coat = { group = "de.siphalor.coat", name = "coat-mc1.21.10", version.ref = "coat" }
|
||||||
fabric-api = { group = "net.fabricmc.fabric-api", name = "fabric-api", version.ref = "fabric-api" }
|
fabric-api = { group = "net.fabricmc.fabric-api", name = "fabric-api", version.ref = "fabric-api" }
|
||||||
minecraft = { group = "com.mojang", name = "minecraft", version.ref = "minecraft" }
|
minecraft = { group = "com.mojang", name = "minecraft", version.ref = "minecraft" }
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ dependencyResolutionManagement {
|
|||||||
includeBuild("../tweed5")
|
includeBuild("../tweed5")
|
||||||
|
|
||||||
includeNormalModule("bundle")
|
includeNormalModule("bundle")
|
||||||
|
includeNormalModule("coat-bridge")
|
||||||
includeNormalModule("fabric-helper")
|
includeNormalModule("fabric-helper")
|
||||||
|
|
||||||
fun includeNormalModule(name: String) {
|
fun includeNormalModule(name: String) {
|
||||||
|
|||||||
Reference in New Issue
Block a user