Merge branch 'refs/heads/breaking'
This commit is contained in:
commit
bd30200197
@ -5,20 +5,11 @@ plugins {
|
||||
allprojects { group = "io.gitlab.jfronny.libjf" }
|
||||
subprojects { version = rootProject.version }
|
||||
|
||||
val fabricVersion by extra("0.91.1+1.20.4")
|
||||
val commonsVersion by extra("1.5-SNAPSHOT")
|
||||
val gsonCompileVersion by extra("1.4-SNAPSHOT")
|
||||
val modmenuVersion by extra("9.0.0-pre.1")
|
||||
|
||||
val annotationsVersion by extra("24.0.1")
|
||||
val javapoetVersion by extra("1.13.0")
|
||||
|
||||
val baseCommonsModules by extra(listOf("http-client", "io", "logging", "logging-slf4j", "serialize", "serialize-gson"))
|
||||
|
||||
jfMod {
|
||||
minecraftVersion = "1.20.4"
|
||||
yarn("build.1")
|
||||
loaderVersion = "0.15.0"
|
||||
minecraftVersion = libs.versions.minecraft
|
||||
yarn(libs.versions.yarn.get())
|
||||
loaderVersion = libs.versions.fabric.loader
|
||||
fabricApiVersion = libs.versions.fabric.api
|
||||
|
||||
modrinth {
|
||||
projectId = "libjf"
|
||||
@ -30,20 +21,21 @@ jfMod {
|
||||
}
|
||||
}
|
||||
|
||||
base {
|
||||
archivesName = "libjf"
|
||||
}
|
||||
|
||||
allprojects {
|
||||
if (!rootProject.jfMod.isMod(this)) return@allprojects
|
||||
|
||||
base {
|
||||
archivesName = "libjf"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
modLocalRuntime("com.terraformersmc:modmenu:$modmenuVersion") {
|
||||
exclude("net.fabricmc") // required to work around duplicate fabric loaders
|
||||
}
|
||||
modLocalRuntime(fabricApi.module("fabric-command-api-v2", fabricVersion))
|
||||
modLocalRuntime(fabricApi.module("fabric-networking-api-v1", fabricVersion))
|
||||
compileOnly("io.gitlab.jfronny:commons:$commonsVersion")
|
||||
baseCommonsModules.forEach { compileOnly("io.gitlab.jfronny:commons-$it:$commonsVersion") }
|
||||
// modLocalRuntime(libs.modmenu) {
|
||||
// exclude("net.fabricmc") // required to work around duplicate fabric loaders
|
||||
// }
|
||||
modLocalRuntime("net.fabricmc.fabric-api:fabric-command-api-v2")
|
||||
modLocalRuntime("net.fabricmc.fabric-api:fabric-networking-api-v1")
|
||||
modLocalRuntime("net.fabricmc.fabric-api:fabric-resource-loader-v0")
|
||||
modLocalRuntime("net.fabricmc.fabric-api:fabric-lifecycle-events-v1")
|
||||
compileOnly(libs.bundles.commons)
|
||||
}
|
||||
}
|
||||
|
27
gradle/libs.versions.toml
Normal file
27
gradle/libs.versions.toml
Normal file
@ -0,0 +1,27 @@
|
||||
# jf-scripts version is in settings.gradle.kts
|
||||
[versions]
|
||||
minecraft = "1.20.5"
|
||||
yarn = "build.1"
|
||||
fabric-loader = "0.15.10"
|
||||
fabric-api = "0.97.5+1.20.5"
|
||||
jf-commons = "1.7-SNAPSHOT"
|
||||
modmenu = "9.2.0-beta.2"
|
||||
annotations = "24.1.0"
|
||||
javapoet = "1.13.0"
|
||||
|
||||
[libraries]
|
||||
commons = { module = "io.gitlab.jfronny:commons", version.ref="jf-commons" }
|
||||
commons-http-client = { module = "io.gitlab.jfronny:commons-http-client", version.ref="jf-commons" }
|
||||
commons-io = { module = "io.gitlab.jfronny:commons-io", version.ref="jf-commons" }
|
||||
commons-logger = { module = "io.gitlab.jfronny:commons-logger", version.ref="jf-commons" }
|
||||
commons-serialize = { module = "io.gitlab.jfronny:commons-serialize", version.ref="jf-commons" }
|
||||
commons-serialize-databind = { module = "io.gitlab.jfronny:commons-serialize-databind", version.ref="jf-commons" }
|
||||
commons-serialize-json = { module = "io.gitlab.jfronny:commons-serialize-json", version.ref="jf-commons" }
|
||||
commons-serialize-generator-core = { module = "io.gitlab.jfronny:commons-serialize-generator-core", version.ref="jf-commons" }
|
||||
commons-unsafe = { module = "io.gitlab.jfronny:commons-unsafe", version.ref="jf-commons" }
|
||||
modmenu = { module = "com.terraformersmc:modmenu", version.ref="modmenu" }
|
||||
annotations = { module = "org.jetbrains:annotations", version.ref="annotations" }
|
||||
javapoet = { module = "com.squareup:javapoet", version.ref="javapoet" }
|
||||
|
||||
[bundles]
|
||||
commons = ["commons", "commons-http-client", "commons-logger", "commons-io", "commons-serialize", "commons-serialize-databind", "commons-serialize-json"]
|
@ -1,18 +1,35 @@
|
||||
import com.github.jengelman.gradle.plugins.shadow.transformers.ServiceFileTransformer
|
||||
import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext
|
||||
import org.codehaus.plexus.util.IOUtil
|
||||
import java.io.ByteArrayOutputStream
|
||||
|
||||
plugins {
|
||||
id("jfmod.module")
|
||||
}
|
||||
|
||||
base {
|
||||
archivesName.set("libjf-base")
|
||||
archivesName = "libjf-base"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
val fabricVersion: String by rootProject.extra
|
||||
val commonsVersion: String by rootProject.extra
|
||||
val baseCommonsModules: List<String> by rootProject.extra
|
||||
include(modImplementation(fabricApi.module("fabric-lifecycle-events-v1", fabricVersion))!!)
|
||||
shadow("io.gitlab.jfronny:commons:$commonsVersion")
|
||||
baseCommonsModules.forEach { shadow("io.gitlab.jfronny:commons-$it:$commonsVersion") {
|
||||
if (it == "logging-slf4j") isTransitive = false
|
||||
} }
|
||||
include(modImplementation("net.fabricmc.fabric-api:fabric-lifecycle-events-v1")!!)
|
||||
shadow(libs.bundles.commons)
|
||||
}
|
||||
|
||||
// workaround to ensure our custom System.LoggerFinder is initialized early
|
||||
// ideally, we would use the configuration API, but that seems to cause a class loading issue with fabric loader
|
||||
tasks.shadowJar {
|
||||
val path = "META-INF/services/java.lang.System\$LoggerFinder"
|
||||
val field = ServiceFileTransformer::class.java.getDeclaredField("serviceEntries").apply { isAccessible = true }
|
||||
exclude(path)
|
||||
transform(object: ServiceFileTransformer() {
|
||||
private val serviceEntries get() = field.get(this) as MutableMap<String, Any>
|
||||
override fun transform(context: TransformerContext?) {
|
||||
super.transform(context)
|
||||
(serviceEntries[path] as ByteArrayOutputStream).run {
|
||||
reset()
|
||||
file("src/main/resources/$path").inputStream().use { IOUtil.copy(it, this) }
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
@ -1,21 +1,25 @@
|
||||
package io.gitlab.jfronny.libjf;
|
||||
|
||||
import io.gitlab.jfronny.commons.logging.Level;
|
||||
import io.gitlab.jfronny.commons.logging.Logger;
|
||||
import io.gitlab.jfronny.commons.logging.slf4j.SLF4JLogger;
|
||||
import io.gitlab.jfronny.commons.serialize.gson.api.v2.GsonHolders;
|
||||
import io.gitlab.jfronny.libjf.gson.HiddenAnnotationExclusionStrategy;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import io.gitlab.jfronny.commons.Serializer;
|
||||
import io.gitlab.jfronny.commons.logger.DelegateLogger;
|
||||
import io.gitlab.jfronny.commons.logger.HotswapLoggerFinder;
|
||||
import io.gitlab.jfronny.commons.logger.SystemLoggerPlus;
|
||||
import io.gitlab.jfronny.commons.serialize.databind.DatabindSerializer;
|
||||
import io.gitlab.jfronny.commons.serialize.databind.ObjectMapper;
|
||||
import io.gitlab.jfronny.commons.serialize.json.JsonTransport;
|
||||
import io.gitlab.jfronny.libjf.log.JULBridge;
|
||||
import io.gitlab.jfronny.libjf.log.SLF4JPlatformLogger;
|
||||
import net.fabricmc.loader.api.entrypoint.PreLaunchEntrypoint;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class LibJf implements PreLaunchEntrypoint {
|
||||
public static final String MOD_ID = "libjf";
|
||||
public static final Logger LOGGER = Logger.forName(MOD_ID);
|
||||
public static final SystemLoggerPlus LOGGER = SystemLoggerPlus.forName(MOD_ID);
|
||||
public static final ObjectMapper MAPPER = new ObjectMapper();
|
||||
public static final JsonTransport JSON_TRANSPORT = new JsonTransport();
|
||||
|
||||
static {
|
||||
HiddenAnnotationExclusionStrategy.register();
|
||||
GsonHolders.registerSerializer();
|
||||
Logger.setMinimumLevel(FabricLoader.getInstance().isDevelopmentEnvironment() ? Level.TRACE : Level.INFO);
|
||||
Serializer.setInstance(new DatabindSerializer<>(JSON_TRANSPORT, MAPPER));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -24,10 +28,35 @@ public class LibJf implements PreLaunchEntrypoint {
|
||||
}
|
||||
|
||||
private static boolean setup = false;
|
||||
private static boolean bootstrapped = false;
|
||||
public static void setup() {
|
||||
bootstrap();
|
||||
if (!setup) {
|
||||
setup = true;
|
||||
Logger.registerFactory(SLF4JLogger::new);
|
||||
// Sometimes a mod using LibJF may want to log before game libraries are initialized.
|
||||
// For that case, an implementation of System.Logger that redirects to Fabric Loader's logger is used at startup.
|
||||
// After the game is initialized though, that becomes unnecessary, and we can switch to using SLF4J directly.
|
||||
// The setup method is called after the game is initialized, so this is the perfect place to do this.
|
||||
HotswapLoggerFinder.updateAllStrategies((name, module, level) -> new SLF4JPlatformLogger(LoggerFactory.getLogger(name)));
|
||||
}
|
||||
}
|
||||
|
||||
public static void bootstrap() {
|
||||
if (!bootstrapped) {
|
||||
bootstrapped = true;
|
||||
// I use System.Logger for logging.
|
||||
// LibJF ships a service loaded in System.LoggerFinder, which delegates to Fabric Loader and (later on) SLF4J (after setup).
|
||||
if (LOGGER instanceof DelegateLogger) {
|
||||
// If the ServiceLoader was able to find this service, any Logger generated will be a DelegateLogger since that class is used by the HotswapLoggerFinder.
|
||||
// This means that redirection to SLF4J is already set up and we don't need to do anything.
|
||||
LOGGER.debug("HotswapLoggerFinder was loaded successfully. No need to install JULBridge.");
|
||||
} else {
|
||||
// If Fabric Loader is run in production, the ServiceLoader may not pick up this implementation quickly enough.
|
||||
// In that case, the JUL backend is selected and this cannot be changed, so we need to redirect logs from JUL instead.
|
||||
// The JULBridge does exactly that by redirecting JUL logs to loggers created by its own instance of EarlyLoggerSetup.
|
||||
JULBridge.install();
|
||||
LOGGER.debug("JULBridge was installed. If available in your launcher, please enable the ServiceLoader fix for better performance.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +0,0 @@
|
||||
package io.gitlab.jfronny.libjf.gson;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface ClientOnly {
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
package io.gitlab.jfronny.libjf.gson;
|
||||
|
||||
import io.gitlab.jfronny.gson.*;
|
||||
import net.fabricmc.loader.api.metadata.CustomValue;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class FabricLoaderGsonGenerator {
|
||||
public static JsonElement toGson(CustomValue customValue) {
|
||||
if (customValue == null) return null;
|
||||
return switch (customValue.getType()) {
|
||||
case OBJECT -> {
|
||||
JsonObject jo = new JsonObject();
|
||||
for (Map.Entry<String, CustomValue> value : customValue.getAsObject())
|
||||
jo.add(value.getKey(), toGson(value.getValue()));
|
||||
yield jo;
|
||||
}
|
||||
case ARRAY -> {
|
||||
JsonArray jo = new JsonArray();
|
||||
for (CustomValue value : customValue.getAsArray())
|
||||
jo.add(toGson(value));
|
||||
yield jo;
|
||||
}
|
||||
case STRING -> new JsonPrimitive(customValue.getAsString());
|
||||
case NUMBER -> new JsonPrimitive(customValue.getAsNumber());
|
||||
case BOOLEAN -> new JsonPrimitive(customValue.getAsBoolean());
|
||||
case NULL -> JsonNull.INSTANCE;
|
||||
};
|
||||
}
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
package io.gitlab.jfronny.libjf.gson;
|
||||
|
||||
import io.gitlab.jfronny.gson.GsonBuilder;
|
||||
|
||||
public interface GsonAdapter {
|
||||
void apply(GsonBuilder builder);
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
package io.gitlab.jfronny.libjf.gson;
|
||||
|
||||
import io.gitlab.jfronny.commons.serialize.gson.api.v2.GsonHolders;
|
||||
import io.gitlab.jfronny.gson.ExclusionStrategy;
|
||||
import io.gitlab.jfronny.gson.FieldAttributes;
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
|
||||
public class HiddenAnnotationExclusionStrategy implements ExclusionStrategy {
|
||||
public boolean shouldSkipClass(Class<?> clazz) {
|
||||
return false;
|
||||
}
|
||||
public boolean shouldSkipField(FieldAttributes fieldAttributes) {
|
||||
return FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT
|
||||
? fieldAttributes.getAnnotation(ServerOnly.class) != null
|
||||
: fieldAttributes.getAnnotation(ClientOnly.class) != null;
|
||||
}
|
||||
|
||||
public static void register() {
|
||||
GsonHolders.applyTransform(builder -> builder.setExclusionStrategies(new HiddenAnnotationExclusionStrategy()));
|
||||
}
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
package io.gitlab.jfronny.libjf.gson;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface ServerOnly {
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package io.gitlab.jfronny.libjf.log;
|
||||
|
||||
import io.gitlab.jfronny.commons.logger.HotswapLoggerFinder;
|
||||
import io.gitlab.jfronny.commons.logger.LeveledLoggerFinder;
|
||||
import io.gitlab.jfronny.commons.logger.SystemLoggerPlus;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class EarlyLoggerSetup extends LeveledLoggerFinder {
|
||||
static {
|
||||
// When a logger is used before the game is initialized, SLF4J is not yet available.
|
||||
// To support that, this implementation which redirects to Fabric Loader's internal logging abstraction is used instead.
|
||||
// After the game is initialized, something calls LibJF.setup (usually preEntry), which replaces this factory with a SLF4J-based one.
|
||||
HotswapLoggerFinder.updateAllStrategies((name, module, level) -> new LoaderPlatformLogger(name));
|
||||
}
|
||||
|
||||
private final LeveledLoggerFinder delegate = new HotswapLoggerFinder();
|
||||
|
||||
@Override
|
||||
public SystemLoggerPlus getLogger(String name, Module module, @Nullable System.Logger.Level level) {
|
||||
return delegate.getLogger(name, module, level);
|
||||
}
|
||||
}
|
@ -0,0 +1,98 @@
|
||||
package io.gitlab.jfronny.libjf.log;
|
||||
|
||||
import io.gitlab.jfronny.commons.StringFormatter;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Objects;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.logging.Handler;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.LogManager;
|
||||
import java.util.logging.LogRecord;
|
||||
|
||||
/**
|
||||
* Based on SLF4JBridgeHandler, modified for LibJF.
|
||||
*/
|
||||
public class JULBridge extends Handler {
|
||||
public static void install() {
|
||||
var rootLogger = getRootLogger();
|
||||
rootLogger.addHandler(new JULBridge());
|
||||
boolean bridgeFound = false;
|
||||
for (Handler handler : rootLogger.getHandlers()) {
|
||||
if (handler instanceof JULBridge) {
|
||||
bridgeFound = true;
|
||||
continue;
|
||||
}
|
||||
rootLogger.removeHandler(handler);
|
||||
}
|
||||
if (!bridgeFound) {
|
||||
// Someone is wrapping our handlers. No matter, just add it again.
|
||||
rootLogger.addHandler(new JULBridge());
|
||||
}
|
||||
}
|
||||
|
||||
private static java.util.logging.Logger getRootLogger() {
|
||||
return LogManager.getLogManager().getLogger("");
|
||||
}
|
||||
|
||||
private JULBridge() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
private final Module module = JULBridge.class.getClassLoader().getUnnamedModule();
|
||||
private final EarlyLoggerSetup loggerFinder = new EarlyLoggerSetup();
|
||||
|
||||
private System.Logger getLoggerFor(LogRecord record) {
|
||||
return loggerFinder.getLogger(Objects.requireNonNullElse(record.getLoggerName(), ""), module);
|
||||
}
|
||||
|
||||
private static System.Logger.Level getLevel(LogRecord record) {
|
||||
System.Logger.Level level;
|
||||
if (record.getLevel().equals(Level.OFF)) level = System.Logger.Level.OFF;
|
||||
else if (record.getLevel().equals(Level.ALL)) level = System.Logger.Level.ALL;
|
||||
else if (record.getLevel().intValue() >= Level.SEVERE.intValue()) {
|
||||
level = System.Logger.Level.ERROR;
|
||||
} else if (record.getLevel().intValue() >= Level.WARNING.intValue()) {
|
||||
level = System.Logger.Level.WARNING;
|
||||
} else if (record.getLevel().intValue() >= Level.INFO.intValue()) {
|
||||
level = System.Logger.Level.INFO;
|
||||
} else {
|
||||
level = System.Logger.Level.DEBUG;
|
||||
}
|
||||
return level;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void publish(LogRecord record) {
|
||||
if (record == null) return;
|
||||
System.Logger logger = getLoggerFor(record);
|
||||
System.Logger.Level level = getLevel(record);
|
||||
String message = record.getMessage();
|
||||
ResourceBundle bundle = null;
|
||||
if (message == null) message = "";
|
||||
else bundle = record.getResourceBundle();
|
||||
Object[] params = record.getParameters();
|
||||
Throwable thrown = record.getThrown();
|
||||
if (thrown == null) {
|
||||
logger.log(level, bundle, message, params);
|
||||
} else {
|
||||
if (params != null && params.length > 0) {
|
||||
String[] strings = new String[params.length];
|
||||
for (int i = 0; i < params.length; i++) {
|
||||
strings[i] = StringFormatter.toString(params[i]);
|
||||
}
|
||||
message = new MessageFormat(message).format(strings);
|
||||
}
|
||||
logger.log(level, bundle, message, thrown);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
package io.gitlab.jfronny.libjf.log;
|
||||
|
||||
import io.gitlab.jfronny.commons.logger.CompactLogger;
|
||||
import net.fabricmc.loader.impl.util.log.Log;
|
||||
import net.fabricmc.loader.impl.util.log.LogCategory;
|
||||
import net.fabricmc.loader.impl.util.log.LogLevel;
|
||||
|
||||
public class LoaderPlatformLogger implements CompactLogger {
|
||||
private final LogCategory category;
|
||||
|
||||
public LoaderPlatformLogger(String context, String... names) {
|
||||
this.category = LogCategory.createCustom(context, names);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return category.name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Level getLevel() {
|
||||
for (Level value : Level.values()) {
|
||||
if (value == Level.ALL || value == Level.OFF) continue;
|
||||
if (isLoggable(value)) return value;
|
||||
}
|
||||
return Level.INFO; // should not happen, but if it does, INFO is a reasonable default
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(Level level, String message) {
|
||||
Log.log(jplLevelToFabricLevel(level), category, message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(Level level, String message, Throwable throwable) {
|
||||
Log.log(jplLevelToFabricLevel(level), category, message, throwable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLoggable(Level level) {
|
||||
if (level == Level.ALL) return true;
|
||||
if (level == Level.OFF) return false;
|
||||
return Log.shouldLog(jplLevelToFabricLevel(level), category);
|
||||
}
|
||||
|
||||
private LogLevel jplLevelToFabricLevel(Level jplLevel) {
|
||||
return switch (jplLevel) {
|
||||
case TRACE -> LogLevel.TRACE;
|
||||
case DEBUG -> LogLevel.DEBUG;
|
||||
case INFO -> LogLevel.INFO;
|
||||
case WARNING -> LogLevel.WARN;
|
||||
case ERROR -> LogLevel.ERROR;
|
||||
// should not happen, but if it does, INFO is a reasonable default
|
||||
default -> LogLevel.INFO;
|
||||
};
|
||||
}
|
||||
}
|
@ -0,0 +1,116 @@
|
||||
package io.gitlab.jfronny.libjf.log;
|
||||
|
||||
import io.gitlab.jfronny.commons.StringFormatter;
|
||||
import io.gitlab.jfronny.commons.logger.CompactLogger;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.spi.CallerBoundaryAware;
|
||||
import org.slf4j.spi.LoggingEventBuilder;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Objects;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
/**
|
||||
* A {@link System.Logger} implementation that delegates to SLF4J.
|
||||
* Adapted from slf4j-jdk-platform-logging for use in LibJF.
|
||||
*
|
||||
* @author Ceki Gülcü
|
||||
* @author JFronny
|
||||
*/
|
||||
public class SLF4JPlatformLogger implements CompactLogger {
|
||||
private static final String PRESUMED_CALLER_BOUNDARY = System.Logger.class.getName();
|
||||
private final Logger slf4jLogger;
|
||||
|
||||
public SLF4JPlatformLogger(Logger logger) {
|
||||
slf4jLogger = Objects.requireNonNull(logger);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return slf4jLogger.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Level getLevel() {
|
||||
if (slf4jLogger.isTraceEnabled()) return Level.TRACE;
|
||||
else if (slf4jLogger.isDebugEnabled()) return Level.DEBUG;
|
||||
else if (slf4jLogger.isInfoEnabled()) return Level.INFO;
|
||||
else if (slf4jLogger.isWarnEnabled()) return Level.WARNING;
|
||||
else if (slf4jLogger.isErrorEnabled()) return Level.ERROR;
|
||||
else return Level.INFO; // should not happen, but if it does, INFO is a reasonable default
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLoggable(Level level) {
|
||||
if (level == Level.ALL) return true;
|
||||
if (level == Level.OFF) return false;
|
||||
return slf4jLogger.isEnabledForLevel(jplLevelToSLF4JLevel(level));
|
||||
}
|
||||
|
||||
private org.slf4j.event.Level jplLevelToSLF4JLevel(Level jplLevel) {
|
||||
return switch (jplLevel) {
|
||||
case TRACE -> org.slf4j.event.Level.TRACE;
|
||||
case DEBUG -> org.slf4j.event.Level.DEBUG;
|
||||
case INFO -> org.slf4j.event.Level.INFO;
|
||||
case WARNING -> org.slf4j.event.Level.WARN;
|
||||
case ERROR -> org.slf4j.event.Level.ERROR;
|
||||
// should not happen, but if it does, INFO is a reasonable default
|
||||
default -> org.slf4j.event.Level.INFO;
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(Level level, String message) {
|
||||
log(level, null, message, null, (Object[]) null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(Level level, String message, Throwable throwable) {
|
||||
log(level, null, message, throwable, (Object[]) null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(Level level, ResourceBundle bundle, String msg, Throwable thrown) {
|
||||
log(level, bundle, msg, thrown, (Object[]) null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(Level level, ResourceBundle bundle, String format, Object... params) {
|
||||
log(level, bundle, format, null, params);
|
||||
}
|
||||
|
||||
private void log(Level jplLevel, ResourceBundle bundle, String msg, Throwable thrown, Object... params) {
|
||||
if (jplLevel == Level.OFF) return;
|
||||
if (jplLevel == Level.ALL) {
|
||||
performLog(org.slf4j.event.Level.TRACE, bundle, msg, thrown, params);
|
||||
return;
|
||||
}
|
||||
|
||||
org.slf4j.event.Level slf4jLevel = jplLevelToSLF4JLevel(jplLevel);
|
||||
if (slf4jLogger.isEnabledForLevel(slf4jLevel)) {
|
||||
performLog(slf4jLevel, bundle, msg, thrown, params);
|
||||
}
|
||||
}
|
||||
|
||||
private void performLog(org.slf4j.event.Level slf4jLevel, ResourceBundle bundle, String msg, Throwable thrown, Object... params) {
|
||||
String message = CompactLogger.getResourceStringOrMessage(bundle, msg);
|
||||
LoggingEventBuilder leb = slf4jLogger.makeLoggingEventBuilder(slf4jLevel);
|
||||
if (thrown != null) {
|
||||
leb = leb.setCause(thrown);
|
||||
}
|
||||
if (params != null && params.length > 0) {
|
||||
// add the arguments to the logging event for possible processing by the backend
|
||||
String[] strings = new String[params.length];
|
||||
for (int i = 0; i < params.length; i++) {
|
||||
leb = leb.addArgument(params[i]);
|
||||
strings[i] = StringFormatter.toString(params[i]);
|
||||
}
|
||||
// The JDK uses a different formatting convention. We must invoke it now.
|
||||
message = new MessageFormat(message).format(strings);
|
||||
}
|
||||
if (leb instanceof CallerBoundaryAware cba) {
|
||||
cba.setCallerBoundary(PRESUMED_CALLER_BOUNDARY);
|
||||
}
|
||||
leb.log(message);
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package io.gitlab.jfronny.libjf.serialize;
|
||||
|
||||
import io.gitlab.jfronny.commons.serialize.emulated.DataElement;
|
||||
import net.fabricmc.loader.api.metadata.CustomValue;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class FabricLoaderDataMapper {
|
||||
public static DataElement toGson(CustomValue customValue) {
|
||||
if (customValue == null) return null;
|
||||
return switch (customValue.getType()) {
|
||||
case OBJECT -> {
|
||||
DataElement.Object jo = new DataElement.Object();
|
||||
for (Map.Entry<String, CustomValue> value : customValue.getAsObject())
|
||||
jo.members().put(value.getKey(), toGson(value.getValue()));
|
||||
yield jo;
|
||||
}
|
||||
case ARRAY -> {
|
||||
DataElement.Array jo = new DataElement.Array();
|
||||
for (CustomValue value : customValue.getAsArray())
|
||||
jo.elements().add(toGson(value));
|
||||
yield jo;
|
||||
}
|
||||
case STRING -> new DataElement.Primitive.String(customValue.getAsString());
|
||||
case NUMBER -> new DataElement.Primitive.Number(customValue.getAsNumber());
|
||||
case BOOLEAN -> new DataElement.Primitive.Boolean(customValue.getAsBoolean());
|
||||
case NULL -> new DataElement.Null();
|
||||
};
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package io.gitlab.jfronny.libjf.serialize;
|
||||
|
||||
import io.gitlab.jfronny.commons.serialize.SerializeReader;
|
||||
import io.gitlab.jfronny.commons.serialize.SerializeWriter;
|
||||
import io.gitlab.jfronny.commons.serialize.json.JsonWriter;
|
||||
|
||||
public class SerializationMode {
|
||||
public static <TEx extends Exception, Reader extends SerializeReader<TEx, Reader>> Reader asConfig(Reader reader) {
|
||||
reader.setLenient(true)
|
||||
.setSerializeSpecialFloatingPointValues(true);
|
||||
return reader;
|
||||
}
|
||||
|
||||
public static <TEx extends Exception, Writer extends SerializeWriter<TEx, Writer>> Writer asConfig(Writer writer) {
|
||||
writer.setLenient(true)
|
||||
.setSerializeSpecialFloatingPointValues(true)
|
||||
.setSerializeNulls(true);
|
||||
if (writer instanceof JsonWriter jw) {
|
||||
jw.setIndent(" ")
|
||||
.setNewline("\n")
|
||||
.setOmitQuotes(true)
|
||||
.setCommentUnexpectedNames(true);
|
||||
}
|
||||
return writer;
|
||||
}
|
||||
}
|
@ -0,0 +1 @@
|
||||
io.gitlab.jfronny.libjf.log.EarlyLoggerSetup
|
@ -5,12 +5,11 @@ plugins {
|
||||
}
|
||||
|
||||
base {
|
||||
archivesName.set("libjf-config-commands")
|
||||
archivesName = "libjf-config-commands"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
val fabricVersion: String by rootProject.extra
|
||||
api(devProject(":libjf-base"))
|
||||
api(devProject(":libjf-config-core-v2"))
|
||||
include(modImplementation(fabricApi.module("fabric-command-api-v2", fabricVersion))!!)
|
||||
include(modImplementation("net.fabricmc.fabric-api:fabric-command-api-v2")!!)
|
||||
}
|
||||
|
@ -14,16 +14,13 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
val commonsVersion: String by rootProject.extra
|
||||
val gsonCompileVersion: String by rootProject.extra
|
||||
val annotationsVersion: String by rootProject.extra
|
||||
val javapoetVersion: String by rootProject.extra
|
||||
implementation("io.gitlab.jfronny.gson:gson-compile-processor-core:$gsonCompileVersion")
|
||||
implementation(libs.commons.serialize.generator.core)
|
||||
implementation(devProject(":libjf-config-core-v2"))
|
||||
implementation("org.jetbrains:annotations:$annotationsVersion")
|
||||
implementation("io.gitlab.jfronny:commons:$commonsVersion")
|
||||
implementation("io.gitlab.jfronny:commons-serialize-gson:$commonsVersion")
|
||||
implementation("com.squareup:javapoet:$javapoetVersion")
|
||||
implementation(libs.annotations)
|
||||
implementation(libs.commons)
|
||||
implementation(libs.commons.serialize.databind)
|
||||
implementation(libs.commons.serialize.json)
|
||||
implementation(libs.javapoet)
|
||||
testAnnotationProcessor(sourceSets.main.get().output)
|
||||
configurations.testAnnotationProcessor.get().extendsFrom(configurations.implementation.get())
|
||||
}
|
||||
|
@ -2,8 +2,8 @@ package io.gitlab.jfronny.libjf.config.plugin;
|
||||
|
||||
import com.squareup.javapoet.*;
|
||||
import io.gitlab.jfronny.commons.StringFormatter;
|
||||
import io.gitlab.jfronny.gson.compile.processor.core.*;
|
||||
import io.gitlab.jfronny.gson.reflect.TypeToken;
|
||||
import io.gitlab.jfronny.commons.serialize.databind.api.TypeToken;
|
||||
import io.gitlab.jfronny.commons.serialize.generator.core.*;
|
||||
import io.gitlab.jfronny.libjf.config.api.v2.*;
|
||||
import io.gitlab.jfronny.libjf.config.api.v2.dsl.DSL;
|
||||
import io.gitlab.jfronny.libjf.config.api.v2.type.Type;
|
||||
|
@ -5,11 +5,10 @@ plugins {
|
||||
}
|
||||
|
||||
base {
|
||||
archivesName.set("libjf-config-core-v2")
|
||||
archivesName = "libjf-config-core-v2"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
val modmenuVersion: String by rootProject.extra
|
||||
api(devProject(":libjf-base"))
|
||||
modCompileOnly("com.terraformersmc:modmenu:$modmenuVersion")
|
||||
modCompileOnly(libs.modmenu)
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
package io.gitlab.jfronny.libjf.config.api.v2;
|
||||
|
||||
import io.gitlab.jfronny.libjf.LibJf;
|
||||
import io.gitlab.jfronny.libjf.config.impl.ConfigHolderImpl;
|
||||
|
||||
import java.nio.file.Path;
|
||||
@ -15,7 +14,6 @@ public interface ConfigHolder {
|
||||
* @return The config holder
|
||||
*/
|
||||
static ConfigHolder getInstance() {
|
||||
LibJf.setup();
|
||||
return ConfigHolderImpl.INSTANCE;
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
package io.gitlab.jfronny.libjf.config.api.v2;
|
||||
|
||||
import io.gitlab.jfronny.gson.stream.*;
|
||||
import io.gitlab.jfronny.commons.serialize.SerializeReader;
|
||||
import io.gitlab.jfronny.commons.serialize.SerializeWriter;
|
||||
import io.gitlab.jfronny.libjf.config.api.v2.type.Type;
|
||||
import io.gitlab.jfronny.libjf.config.impl.dsl.DslEntryInfo;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
@ -68,13 +69,13 @@ public interface EntryInfo<T> {
|
||||
* Set this entry's value to that of the element
|
||||
* @param reader The reader to read from
|
||||
*/
|
||||
void loadFromJson(JsonReader reader) throws IOException, IllegalAccessException;
|
||||
<TEx extends Exception, Reader extends SerializeReader<TEx, Reader>> void loadFromJson(Reader reader) throws TEx, IllegalAccessException;
|
||||
|
||||
/**
|
||||
* Write the currently cached value to the writer
|
||||
* @param writer The writer to write to
|
||||
*/
|
||||
void writeTo(JsonWriter writer, String translationPrefix) throws IOException, IllegalAccessException;
|
||||
<TEx extends Exception, Writer extends SerializeWriter<TEx, Writer>> void writeTo(Writer writer, String translationPrefix) throws TEx, IllegalAccessException;
|
||||
|
||||
/**
|
||||
* @return Get the width for this entry
|
||||
|
@ -1,10 +1,10 @@
|
||||
package io.gitlab.jfronny.libjf.config.api.v2.dsl;
|
||||
|
||||
import io.gitlab.jfronny.gson.stream.JsonReader;
|
||||
import io.gitlab.jfronny.commons.serialize.SerializeReader;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface Migration {
|
||||
void apply(JsonReader reader) throws IOException;
|
||||
<TEx extends Exception, Reader extends SerializeReader<TEx, Reader>> void apply(Reader reader) throws IOException;
|
||||
}
|
||||
|
@ -1,16 +1,17 @@
|
||||
package io.gitlab.jfronny.libjf.config.impl;
|
||||
|
||||
import io.gitlab.jfronny.commons.serialize.gson.api.v2.GsonHolders;
|
||||
import io.gitlab.jfronny.commons.serialize.emulated.DataElement;
|
||||
import io.gitlab.jfronny.libjf.config.api.v2.Category;
|
||||
import io.gitlab.jfronny.libjf.config.api.v2.JfConfig;
|
||||
import io.gitlab.jfronny.libjf.config.api.v2.dsl.CategoryBuilder;
|
||||
import io.gitlab.jfronny.libjf.gson.FabricLoaderGsonGenerator;
|
||||
import io.gitlab.jfronny.libjf.serialize.FabricLoaderDataMapper;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import net.fabricmc.loader.api.metadata.CustomValue;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static io.gitlab.jfronny.libjf.config.impl.ConfigCore.MODULE_ID;
|
||||
|
||||
@ -43,11 +44,30 @@ public class AuxiliaryMetadata {
|
||||
cv = cv.getAsObject().get("config");
|
||||
}
|
||||
}
|
||||
if (cv != null) metaRef.meta = GsonHolders.API.getGson().fromJson(FabricLoaderGsonGenerator.toGson(cv), AuxiliaryMetadata.class);
|
||||
if (cv != null) metaRef.meta = read(FabricLoaderDataMapper.toGson(cv));
|
||||
});
|
||||
return metaRef.meta;
|
||||
}
|
||||
|
||||
public static AuxiliaryMetadata read(DataElement data) {
|
||||
if (!(data instanceof DataElement.Object obj)) throw new IllegalArgumentException("AuxiliaryMetadata must be an object");
|
||||
AuxiliaryMetadata meta = new AuxiliaryMetadata();
|
||||
for (Map.Entry<String, DataElement> entry : obj.members().entrySet()) {
|
||||
switch (entry.getKey()) {
|
||||
case "referencedConfigs" -> {
|
||||
if (!(entry.getValue() instanceof DataElement.Array arr)) throw new IllegalArgumentException("referencedConfigs must be an array");
|
||||
meta.referencedConfigs = new LinkedList<>();
|
||||
arr.elements().forEach(element -> {
|
||||
if (!(element instanceof DataElement.Primitive.String str)) throw new IllegalArgumentException("referencedConfigs must be an array of strings");
|
||||
meta.referencedConfigs.add(str.value());
|
||||
});
|
||||
}
|
||||
default -> throw new IllegalArgumentException("Unknown key in AuxiliaryMetadata: " + entry.getKey());
|
||||
}
|
||||
}
|
||||
return meta;
|
||||
}
|
||||
|
||||
public List<String> referencedConfigs;
|
||||
|
||||
public void applyTo(CategoryBuilder<?> builder) {
|
||||
|
@ -1,7 +1,7 @@
|
||||
package io.gitlab.jfronny.libjf.config.impl.dsl;
|
||||
|
||||
import io.gitlab.jfronny.commons.serialize.SerializeReader;
|
||||
import io.gitlab.jfronny.commons.throwable.Coerce;
|
||||
import io.gitlab.jfronny.gson.stream.JsonReader;
|
||||
import io.gitlab.jfronny.libjf.LibJf;
|
||||
import io.gitlab.jfronny.libjf.config.api.v2.*;
|
||||
import io.gitlab.jfronny.libjf.config.api.v2.dsl.CategoryBuilder;
|
||||
@ -21,7 +21,8 @@ public class CategoryBuilderImpl<Builder extends CategoryBuilderImpl<Builder>> i
|
||||
public final Map<String, Consumer<ConfigCategory>> presets = new LinkedHashMap<>();
|
||||
public final List<Supplier<List<ConfigInstance>>> referencedConfigs = new LinkedList<>();
|
||||
public final List<Consumer<ConfigCategory>> verifiers = new LinkedList<>();
|
||||
public final Map<String, Consumer<JsonReader>> migrations = new LinkedHashMap<>();
|
||||
@SuppressWarnings("rawtypes") // recursive types aren't really needed here
|
||||
public final Map<String, Consumer<SerializeReader>> migrations = new LinkedHashMap<>();
|
||||
private boolean built = false;
|
||||
|
||||
public CategoryBuilderImpl(String id, String categoryPath) {
|
||||
@ -174,7 +175,7 @@ public class CategoryBuilderImpl<Builder extends CategoryBuilderImpl<Builder>> i
|
||||
public Builder addMigration(String element, Migration migration) {
|
||||
checkBuilt();
|
||||
if (migrations.containsKey(element)) LibJf.LOGGER.warn("Duplicate migration registered for " + categoryPath + id + ": " + element + ", overriding");
|
||||
migrations.put(element, Coerce.consumer(migration::apply).addHandler(e -> LibJf.LOGGER.error("Could not apply migration for " + element, e)));
|
||||
migrations.put(element, Coerce.<SerializeReader, Exception>consumer(migration::apply).addHandler(e -> LibJf.LOGGER.error("Could not apply migration for " + element, e)));
|
||||
return asBuilder();
|
||||
}
|
||||
|
||||
|
@ -1,12 +1,14 @@
|
||||
package io.gitlab.jfronny.libjf.config.impl.dsl;
|
||||
|
||||
import io.gitlab.jfronny.commons.serialize.gson.api.v2.GsonHolders;
|
||||
import io.gitlab.jfronny.gson.*;
|
||||
import io.gitlab.jfronny.gson.stream.*;
|
||||
import io.gitlab.jfronny.commons.serialize.SerializeReader;
|
||||
import io.gitlab.jfronny.commons.serialize.Token;
|
||||
import io.gitlab.jfronny.commons.serialize.json.JsonReader;
|
||||
import io.gitlab.jfronny.commons.serialize.json.JsonWriter;
|
||||
import io.gitlab.jfronny.libjf.LibJf;
|
||||
import io.gitlab.jfronny.libjf.config.api.v2.*;
|
||||
import io.gitlab.jfronny.libjf.config.impl.entrypoint.JfConfigSafe;
|
||||
import io.gitlab.jfronny.libjf.config.impl.watch.JfConfigWatchService;
|
||||
import io.gitlab.jfronny.libjf.serialize.SerializationMode;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.file.Files;
|
||||
@ -19,7 +21,8 @@ public class DefaultConfigIO {
|
||||
// Actions cannot be cached since entries can change
|
||||
if (Files.exists(path)) {
|
||||
try (BufferedReader br = Files.newBufferedReader(path);
|
||||
JsonReader jr = GsonHolders.CONFIG.getGson().newJsonReader(br)) {
|
||||
JsonReader jr = new JsonReader(br)) {
|
||||
SerializationMode.asConfig(jr);
|
||||
runActions(id, createActions(c), jr);
|
||||
} catch (Exception e) {
|
||||
LibJf.LOGGER.error("Could not read config for " + id, e);
|
||||
@ -29,21 +32,21 @@ public class DefaultConfigIO {
|
||||
});
|
||||
}
|
||||
|
||||
record Action(Consumer<JsonReader> task, boolean required) {
|
||||
public Action(Consumer<JsonReader> task) {
|
||||
record Action(Consumer<SerializeReader> task, boolean required) {
|
||||
public Action(Consumer<SerializeReader> task) {
|
||||
this(task, true);
|
||||
}
|
||||
}
|
||||
|
||||
private static void runActions(String id, Map<String, Action> actions, JsonReader reader) {
|
||||
private static <TEx extends Exception, Reader extends SerializeReader<TEx, Reader>> void runActions(String id, Map<String, Action> actions, Reader reader) {
|
||||
try {
|
||||
if (reader.peek() != JsonToken.BEGIN_OBJECT) {
|
||||
if (reader.peek() != Token.BEGIN_OBJECT) {
|
||||
LibJf.LOGGER.error("Invalid config: Not a JSON object for " + id);
|
||||
return;
|
||||
}
|
||||
Set<String> appeared = new HashSet<>();
|
||||
reader.beginObject();
|
||||
while (reader.peek() != JsonToken.END_OBJECT) {
|
||||
while (reader.peek() != Token.END_OBJECT) {
|
||||
String name = reader.nextName();
|
||||
if (!actions.containsKey(name)) {
|
||||
LibJf.LOGGER.warn("Unrecognized key in config for " + id + ": " + name);
|
||||
@ -61,8 +64,8 @@ public class DefaultConfigIO {
|
||||
LibJf.LOGGER.error("Missing entry in config for " + id + ": " + name);
|
||||
}
|
||||
});
|
||||
} catch (IOException e) {
|
||||
throw new JsonParseException("Could not read config", e);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException("Could not read config", e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -71,7 +74,7 @@ public class DefaultConfigIO {
|
||||
category.getEntries().forEach(entry -> actions.putIfAbsent(entry.getName(), new Action(reader -> {
|
||||
try {
|
||||
entry.loadFromJson(reader);
|
||||
} catch (IllegalAccessException | IOException e) {
|
||||
} catch (Exception e) {
|
||||
LibJf.LOGGER.error("Could not set config entry value of " + entry.getName(), e);
|
||||
}
|
||||
})));
|
||||
@ -91,7 +94,8 @@ public class DefaultConfigIO {
|
||||
public static Consumer<ConfigInstance> writer(String id) {
|
||||
return c -> c.getFilePath().ifPresent(path -> JfConfigWatchService.lock(path, () -> {
|
||||
try (BufferedWriter bw = Files.newBufferedWriter(path);
|
||||
JsonWriter jw = GsonHolders.CONFIG.getGson().newJsonWriter(bw)) {
|
||||
JsonWriter jw = new JsonWriter(bw)) {
|
||||
SerializationMode.asConfig(jw);
|
||||
writeTo(jw, c);
|
||||
} catch (Exception e) {
|
||||
LibJf.LOGGER.error("Could not write config for " + id, e);
|
||||
|
@ -1,6 +1,6 @@
|
||||
package io.gitlab.jfronny.libjf.config.impl.dsl;
|
||||
|
||||
import io.gitlab.jfronny.gson.stream.JsonReader;
|
||||
import io.gitlab.jfronny.commons.serialize.SerializeReader;
|
||||
import io.gitlab.jfronny.libjf.config.api.v2.*;
|
||||
import io.gitlab.jfronny.libjf.config.api.v2.dsl.CategoryBuilder;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
@ -17,7 +17,7 @@ public class DslConfigCategory implements ConfigCategory {
|
||||
private final List<EntryInfo<?>> entries;
|
||||
private final Map<String, Runnable> presets;
|
||||
private final List<Supplier<List<ConfigInstance>>> referencedConfigs;
|
||||
@ApiStatus.Internal public final Map<String, Consumer<JsonReader>> migrations;
|
||||
@ApiStatus.Internal public final Map<String, Consumer<SerializeReader>> migrations;
|
||||
private final Map<String, ConfigCategory> categories;
|
||||
private final Supplier<ConfigInstance> root;
|
||||
private final List<Consumer<ConfigCategory>> verifiers;
|
||||
@ -31,7 +31,7 @@ public class DslConfigCategory implements ConfigCategory {
|
||||
List<CategoryBuilder<?>> categories,
|
||||
Supplier<ConfigInstance> root,
|
||||
List<Consumer<ConfigCategory>> verifiers,
|
||||
Map<String, Consumer<JsonReader>> migrations) {
|
||||
Map<String, Consumer<SerializeReader>> migrations) {
|
||||
this.id = id;
|
||||
this.categoryPath = categoryPath;
|
||||
this.translationPrefix = translationPrefix;
|
||||
|
@ -1,6 +1,6 @@
|
||||
package io.gitlab.jfronny.libjf.config.impl.dsl;
|
||||
|
||||
import io.gitlab.jfronny.gson.stream.JsonReader;
|
||||
import io.gitlab.jfronny.commons.serialize.SerializeReader;
|
||||
import io.gitlab.jfronny.libjf.config.api.v2.*;
|
||||
import io.gitlab.jfronny.libjf.config.api.v2.dsl.CategoryBuilder;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@ -23,7 +23,7 @@ public class DslConfigInstance extends DslConfigCategory implements ConfigInstan
|
||||
List<CategoryBuilder<?>> categories,
|
||||
Supplier<ConfigInstance> root,
|
||||
List<Consumer<ConfigCategory>> verifiers,
|
||||
Map<String, Consumer<JsonReader>> migrations,
|
||||
Map<String, Consumer<SerializeReader>> migrations,
|
||||
Consumer<ConfigInstance> load,
|
||||
Consumer<ConfigInstance> write,
|
||||
@Nullable Path path) {
|
||||
|
@ -1,16 +1,19 @@
|
||||
package io.gitlab.jfronny.libjf.config.impl.dsl;
|
||||
|
||||
import io.gitlab.jfronny.commons.serialize.gson.api.v2.GsonHolders;
|
||||
import io.gitlab.jfronny.commons.Serializer;
|
||||
import io.gitlab.jfronny.commons.serialize.MalformedDataException;
|
||||
import io.gitlab.jfronny.commons.serialize.SerializeReader;
|
||||
import io.gitlab.jfronny.commons.serialize.SerializeWriter;
|
||||
import io.gitlab.jfronny.commons.serialize.Token;
|
||||
import io.gitlab.jfronny.commons.serialize.databind.api.TypeToken;
|
||||
import io.gitlab.jfronny.commons.throwable.ThrowingConsumer;
|
||||
import io.gitlab.jfronny.commons.throwable.ThrowingSupplier;
|
||||
import io.gitlab.jfronny.gson.stream.*;
|
||||
import io.gitlab.jfronny.libjf.LibJf;
|
||||
import io.gitlab.jfronny.libjf.config.api.v2.Entry;
|
||||
import io.gitlab.jfronny.libjf.config.api.v2.EntryInfo;
|
||||
import io.gitlab.jfronny.libjf.config.api.v2.type.Type;
|
||||
import io.gitlab.jfronny.libjf.config.impl.entrypoint.JfConfigSafe;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
@ -127,38 +130,42 @@ public class DslEntryInfo<T> implements EntryInfo<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadFromJson(JsonReader reader) throws IOException, IllegalAccessException {
|
||||
public <TEx extends Exception, Reader extends SerializeReader<TEx, Reader>> void loadFromJson(Reader reader) throws TEx, IllegalAccessException {
|
||||
var next = reader.peek();
|
||||
if (type.isBool()) {
|
||||
if (next == JsonToken.BOOLEAN) setUnchecked(reader.nextBoolean());
|
||||
if (next == Token.BOOLEAN) setUnchecked(reader.nextBoolean());
|
||||
else LibJf.LOGGER.error("Unexpected value for " + name + ": expected boolean but got " + next);
|
||||
} else if (type.isString()) {
|
||||
if (next == JsonToken.STRING || next == JsonToken.NUMBER) setUnchecked(reader.nextString());
|
||||
else if (next == JsonToken.BOOLEAN) setUnchecked(Boolean.toString(reader.nextBoolean()));
|
||||
else if (next == JsonToken.NULL) {
|
||||
if (next == Token.STRING || next == Token.NUMBER) setUnchecked(reader.nextString());
|
||||
else if (next == Token.BOOLEAN) setUnchecked(Boolean.toString(reader.nextBoolean()));
|
||||
else if (next == Token.NULL) {
|
||||
reader.nextNull();
|
||||
setUnchecked(null);
|
||||
} else LibJf.LOGGER.error("Unexpected value for " + name + ": expected string but got " + next);
|
||||
} else if (type.isInt()) {
|
||||
if (next == JsonToken.NUMBER) setUnchecked(reader.nextInt());
|
||||
if (next == Token.NUMBER) setUnchecked(reader.nextInt());
|
||||
else LibJf.LOGGER.error("Unexpected value for " + name + ": expected number but got " + next);
|
||||
} else if (type.isLong()) {
|
||||
if (next == JsonToken.NUMBER) setUnchecked(reader.nextLong());
|
||||
if (next == Token.NUMBER) setUnchecked(reader.nextLong());
|
||||
else LibJf.LOGGER.error("Unexpected value for " + name + ": expected number but got " + next);
|
||||
} else if (type.isDouble()) {
|
||||
if (next == JsonToken.NUMBER) {
|
||||
if (next == Token.NUMBER) {
|
||||
setUnchecked(reader.nextDouble());
|
||||
}
|
||||
else LibJf.LOGGER.error("Unexpected value for " + name + ": expected number but got " + next);
|
||||
} else if (type.isFloat()) {
|
||||
if (next == JsonToken.NUMBER) setUnchecked((float) reader.nextDouble());
|
||||
if (next == Token.NUMBER) setUnchecked((float) reader.nextDouble());
|
||||
else LibJf.LOGGER.error("Unexpected value for " + name + ": expected number but got " + next);
|
||||
} else if (type.isEnum()) {
|
||||
Type.TEnum<T> e = (Type.TEnum<T>) type;
|
||||
if (next == JsonToken.STRING) setUnchecked(e.optionForString(reader.nextString()));
|
||||
if (next == Token.STRING) setUnchecked(e.optionForString(reader.nextString()));
|
||||
else LibJf.LOGGER.error("Unexpected value for " + name + ": expected string but got " + next);
|
||||
} else {
|
||||
setValue(GsonHolders.CONFIG.getGson().fromJson(reader, type.asClass()));
|
||||
try {
|
||||
setValue((T) LibJf.MAPPER.getAdapter(TypeToken.get(type.asClass())).deserialize(reader));
|
||||
} catch (MalformedDataException e) {
|
||||
LibJf.LOGGER.error("Could not read " + name, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -169,7 +176,7 @@ public class DslEntryInfo<T> implements EntryInfo<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(JsonWriter writer, String translationPrefix) throws IOException, IllegalAccessException {
|
||||
public <TEx extends Exception, Writer extends SerializeWriter<TEx, Writer>> void writeTo(Writer writer, String translationPrefix) throws TEx, IllegalAccessException {
|
||||
T value = getValue();
|
||||
String commentText;
|
||||
if ((commentText = JfConfigSafe.TRANSLATION_SUPPLIER.apply(translationPrefix + getName() + ".tooltip")) != null) {
|
||||
@ -179,7 +186,11 @@ public class DslEntryInfo<T> implements EntryInfo<T> {
|
||||
writer.comment("Valid: [" + Arrays.stream(((Type.TEnum<T>)type).options()).map(Objects::toString).collect(Collectors.joining(", ")) + "]");
|
||||
}
|
||||
writer.name(name);
|
||||
GsonHolders.CONFIG.getGson().toJson(value, Objects.requireNonNullElse(type.asClass(), String.class), writer);
|
||||
try {
|
||||
LibJf.MAPPER.getAdapter((TypeToken<T>) TypeToken.get(Objects.requireNonNullElse(type.asClass(), String.class))).serialize(value, writer);
|
||||
} catch (MalformedDataException e) {
|
||||
LibJf.LOGGER.error("Could not write " + name, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -26,7 +26,13 @@ public class JfConfigSafe implements PreLaunchEntrypoint {
|
||||
}
|
||||
}
|
||||
TRANSLATION_SUPPLIER = s -> {
|
||||
String translated = Language.getInstance().get(s);
|
||||
String translated;
|
||||
try {
|
||||
translated = Language.getInstance().get(s);
|
||||
} catch (Throwable t) {
|
||||
LibJf.LOGGER.debug("Failed to translate key " + s, t);
|
||||
return null;
|
||||
}
|
||||
return translated.equals(s) ? null : translated;
|
||||
};
|
||||
}
|
||||
|
@ -5,15 +5,13 @@ plugins {
|
||||
}
|
||||
|
||||
base {
|
||||
archivesName.set("libjf-config-network-v0")
|
||||
archivesName = "libjf-config-network-v0"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
val fabricVersion: String by rootProject.extra
|
||||
val modmenuVersion: String by rootProject.extra
|
||||
api(devProject(":libjf-base"))
|
||||
api(devProject(":libjf-config-core-v2"))
|
||||
modCompileOnly("com.terraformersmc:modmenu:$modmenuVersion")
|
||||
include(modImplementation(fabricApi.module("fabric-networking-api-v1", fabricVersion))!!)
|
||||
include(modImplementation(fabricApi.module("fabric-command-api-v2", fabricVersion))!!)
|
||||
modCompileOnly(libs.modmenu)
|
||||
include(modImplementation("net.fabricmc.fabric-api:fabric-networking-api-v1")!!)
|
||||
include(modImplementation("net.fabricmc.fabric-api:fabric-command-api-v2")!!)
|
||||
}
|
||||
|
@ -3,21 +3,24 @@ package io.gitlab.jfronny.libjf.config.impl.network.client;
|
||||
import io.gitlab.jfronny.libjf.config.impl.network.packet.ConfigurationCompletePacket;
|
||||
import io.gitlab.jfronny.libjf.config.impl.network.packet.ConfigurationPacket;
|
||||
import io.gitlab.jfronny.libjf.config.impl.network.RequestRouter;
|
||||
import io.gitlab.jfronny.libjf.config.impl.network.packet.RequestPacket;
|
||||
import io.gitlab.jfronny.libjf.config.impl.network.packet.ResponsePacket;
|
||||
import net.fabricmc.fabric.api.client.networking.v1.*;
|
||||
|
||||
public class JfConfigNetworkClient {
|
||||
public static boolean isAvailable = false;
|
||||
public static void initialize() {
|
||||
RequestRouter.initialize();
|
||||
JfConfigNetworkCommands.initialize();
|
||||
ClientPlayNetworking.registerGlobalReceiver(RequestRouter.RESPONSE_ID, (client, handler, buf, responseSender) -> {
|
||||
RequestRouter.acceptResponse(buf, responseSender);
|
||||
ClientPlayNetworking.registerGlobalReceiver(ResponsePacket.ID, (payload, context) -> {
|
||||
RequestRouter.acceptResponse(payload, context.responseSender());
|
||||
});
|
||||
ClientPlayNetworking.registerGlobalReceiver(RequestRouter.REQUEST_ID, (client, handler, buf, responseSender) -> {
|
||||
RequestRouter.acceptRequest(buf, responseSender);
|
||||
ClientPlayNetworking.registerGlobalReceiver(RequestPacket.ID, (payload, context) -> {
|
||||
RequestRouter.acceptRequest(payload, context.responseSender());
|
||||
});
|
||||
ClientConfigurationNetworking.registerGlobalReceiver(ConfigurationPacket.PACKET_TYPE, (packet, responseSender) -> {
|
||||
isAvailable = packet.version() == RequestRouter.PROTOCOL_VERSION; // Handshake possible?
|
||||
responseSender.sendPacket(new ConfigurationCompletePacket());
|
||||
ClientConfigurationNetworking.registerGlobalReceiver(ConfigurationPacket.ID, (payload, context) -> {
|
||||
isAvailable = payload.version() == RequestRouter.PROTOCOL_VERSION; // Handshake possible?
|
||||
context.responseSender().sendPacket(new ConfigurationCompletePacket());
|
||||
});
|
||||
ClientConfigurationConnectionEvents.INIT.register((handler, client) -> {
|
||||
isAvailable = false; // Reset for new server connection
|
||||
|
@ -1,84 +1,93 @@
|
||||
package io.gitlab.jfronny.libjf.config.impl.network;
|
||||
|
||||
import io.gitlab.jfronny.libjf.LibJf;
|
||||
import io.gitlab.jfronny.libjf.config.impl.network.packet.ConfigurationCompletePacket;
|
||||
import io.gitlab.jfronny.libjf.config.impl.network.packet.ConfigurationPacket;
|
||||
import io.gitlab.jfronny.libjf.config.impl.network.packet.RequestPacket;
|
||||
import io.gitlab.jfronny.libjf.config.impl.network.packet.ResponsePacket;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketSender;
|
||||
import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class RequestRouter {
|
||||
public static final String MOD_ID = "libjf-config-network-v0";
|
||||
public static final Identifier RESPONSE_ID = new Identifier(MOD_ID, "response");
|
||||
public static final Identifier REQUEST_ID = new Identifier(MOD_ID, "request");
|
||||
public static int PROTOCOL_VERSION = 1;
|
||||
|
||||
private static final Map<String, RequestHandler> persistendHandlers = new HashMap<>();
|
||||
private static final Map<Long, Request> currentRequests = new HashMap<>(); //TODO implement timeout and prune old requests
|
||||
private static final Random random = new Random();
|
||||
|
||||
public static void acceptResponse(PacketByteBuf buf, PacketSender responseSender) {
|
||||
Request request = currentRequests.remove(buf.readLong());
|
||||
public static void initialize() {
|
||||
PayloadTypeRegistry.playC2S().register(RequestPacket.ID, RequestPacket.CODEC);
|
||||
PayloadTypeRegistry.playS2C().register(RequestPacket.ID, RequestPacket.CODEC);
|
||||
PayloadTypeRegistry.playC2S().register(ResponsePacket.ID, ResponsePacket.CODEC);
|
||||
PayloadTypeRegistry.playS2C().register(ResponsePacket.ID, ResponsePacket.CODEC);
|
||||
PayloadTypeRegistry.configurationS2C().register(ConfigurationPacket.ID, ConfigurationPacket.CODEC);
|
||||
PayloadTypeRegistry.configurationC2S().register(ConfigurationCompletePacket.ID, ConfigurationCompletePacket.CODEC);
|
||||
}
|
||||
|
||||
public static void acceptResponse(ResponsePacket response, PacketSender responseSender) {
|
||||
Request request = currentRequests.remove(response.request());
|
||||
if (request != null) {
|
||||
switch (buf.readInt()) {
|
||||
case 0 -> request.responseHandler.onSuccess(buf);
|
||||
switch (response.status()) {
|
||||
case 0 -> request.responseHandler.onSuccess(response.aux());
|
||||
case 1 -> request.responseHandler.onNotFound();
|
||||
case 2 -> request.responseHandler.onDeny();
|
||||
case 3 -> request.responseHandler.onFailure(buf.readString());
|
||||
case 3 -> request.responseHandler.onFailure(response.aux().readString());
|
||||
default -> request.responseHandler.onFailure("Unrecognized error received");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void acceptRequest(PacketByteBuf buf, PacketSender responseSender) {
|
||||
PacketByteBuf resp = PacketByteBufs.create();
|
||||
long id = buf.readLong();
|
||||
resp.writeLong(id);
|
||||
public static void acceptRequest(RequestPacket request, PacketSender responseSender) {
|
||||
long id = request.request();
|
||||
int mode = 3;
|
||||
PacketByteBuf aux = PacketByteBufs.create();
|
||||
try {
|
||||
if (buf.readBoolean()) {
|
||||
if (request.parent() == null) {
|
||||
// persistent
|
||||
handleRequest(resp, id, buf, persistendHandlers.get(buf.readString()));
|
||||
mode = handleRequest(aux, id, request.aux(), persistendHandlers.get(request.name()));
|
||||
} else {
|
||||
// followup
|
||||
Request parent = currentRequests.get(buf.readLong());
|
||||
Request parent = currentRequests.get(request.parent());
|
||||
if (parent == null) {
|
||||
resp.writeInt(1);
|
||||
mode = 1;
|
||||
} else {
|
||||
String key = buf.readString();
|
||||
String key = request.name();
|
||||
RequestHandler handler = parent.temporaryHandlers.get(key);
|
||||
if (handler == null) handler = persistendHandlers.get(key);
|
||||
handleRequest(resp, id, buf, handler);
|
||||
mode = handleRequest(aux, id, request.aux(), handler);
|
||||
}
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
LibJf.LOGGER.error("Cannot complete request", t);
|
||||
resp.writeInt(3);
|
||||
resp.writeString(t.getMessage() == null ? "null" : t.getMessage());
|
||||
aux.clear();
|
||||
aux.writeString(t.getMessage() == null ? "null" : t.getMessage());
|
||||
} finally {
|
||||
responseSender.sendPacket(RESPONSE_ID, resp);
|
||||
responseSender.sendPacket(new ResponsePacket(id, mode, aux));
|
||||
}
|
||||
}
|
||||
|
||||
private static void handleRequest(PacketByteBuf resp, long id, PacketByteBuf buf, RequestHandler handler) throws Throwable {
|
||||
private static int handleRequest(ByteBuf aux, long id, PacketByteBuf buf, RequestHandler handler) throws Throwable {
|
||||
if (handler == null) {
|
||||
resp.writeInt(1);
|
||||
return 1;
|
||||
} else {
|
||||
PacketByteBuf response = handler.handle(
|
||||
buf,
|
||||
(responseSender, name, body, responseHandler, temporaryHandlers) ->
|
||||
RequestRouter.sendRequest(responseSender, id, name, body, responseHandler, temporaryHandlers)
|
||||
);
|
||||
resp.writeInt(0);
|
||||
if (response != null) resp.writeBytes(response.copy());
|
||||
if (response != null) aux.writeBytes(response.copy());
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static void deny(PacketByteBuf buf, PacketSender responseSender) {
|
||||
PacketByteBuf resp = PacketByteBufs.create();
|
||||
resp.writeLong(buf.readLong());
|
||||
resp.writeInt(2);
|
||||
responseSender.sendPacket(RESPONSE_ID, resp);
|
||||
public static void deny(long id, PacketSender responseSender) {
|
||||
responseSender.sendPacket(new ResponsePacket(id, 2, PacketByteBufs.empty()));
|
||||
}
|
||||
|
||||
public static void registerHandler(String name, RequestHandler handler) {
|
||||
@ -98,15 +107,7 @@ public class RequestRouter {
|
||||
} while (keys.contains(id));
|
||||
currentRequests.put(id, new Request(temporaryHandlers, responseHandler));
|
||||
}
|
||||
PacketByteBuf req = PacketByteBufs.create();
|
||||
req.writeLong(id);
|
||||
req.writeBoolean(parent == null);
|
||||
if (parent != null) req.writeLong(parent);
|
||||
req.writeString(name);
|
||||
if (body != null) {
|
||||
req.writeBytes(body.copy());
|
||||
}
|
||||
responseSender.sendPacket(REQUEST_ID, req);
|
||||
responseSender.sendPacket(new RequestPacket(id, parent, name, PacketByteBufs.copy(body)));
|
||||
}
|
||||
|
||||
private record Request(Map<String, RequestHandler> temporaryHandlers, ResponseHandler responseHandler) {}
|
||||
|
@ -1,25 +1,25 @@
|
||||
package io.gitlab.jfronny.libjf.config.impl.network.packet;
|
||||
|
||||
import io.gitlab.jfronny.libjf.config.impl.network.RequestRouter;
|
||||
import net.fabricmc.fabric.api.networking.v1.FabricPacket;
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketType;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.util.Identifier;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import net.minecraft.network.codec.PacketCodec;
|
||||
import net.minecraft.network.packet.CustomPayload;
|
||||
|
||||
public record ConfigurationCompletePacket() implements FabricPacket {
|
||||
public static final Identifier ID = new Identifier(RequestRouter.MOD_ID, "handshake_complete");
|
||||
public static final PacketType<ConfigurationCompletePacket> PACKET_TYPE = PacketType.create(ID, ConfigurationCompletePacket::new);
|
||||
|
||||
public ConfigurationCompletePacket(PacketByteBuf buf) {
|
||||
this();
|
||||
public record ConfigurationCompletePacket() implements CustomPayload {
|
||||
public static final CustomPayload.Id<ConfigurationCompletePacket> ID = CustomPayload.id(RequestRouter.MOD_ID + ":handshake_complete");
|
||||
public static final PacketCodec<ByteBuf, ConfigurationCompletePacket> CODEC = new PacketCodec<>() {
|
||||
@Override
|
||||
public ConfigurationCompletePacket decode(ByteBuf buf) {
|
||||
return new ConfigurationCompletePacket();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(PacketByteBuf buf) {
|
||||
public void encode(ByteBuf buf, ConfigurationCompletePacket value) {
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public PacketType<?> getType() {
|
||||
return PACKET_TYPE;
|
||||
public Id<? extends CustomPayload> getId() {
|
||||
return ID;
|
||||
}
|
||||
}
|
||||
|
@ -1,26 +1,17 @@
|
||||
package io.gitlab.jfronny.libjf.config.impl.network.packet;
|
||||
|
||||
import io.gitlab.jfronny.libjf.config.impl.network.RequestRouter;
|
||||
import net.fabricmc.fabric.api.networking.v1.FabricPacket;
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketType;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.util.Identifier;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import net.minecraft.network.codec.PacketCodec;
|
||||
import net.minecraft.network.codec.PacketCodecs;
|
||||
import net.minecraft.network.packet.CustomPayload;
|
||||
|
||||
public record ConfigurationPacket(int version) implements FabricPacket {
|
||||
public static final Identifier ID = new Identifier(RequestRouter.MOD_ID, "handshake");
|
||||
public static final PacketType<ConfigurationPacket> PACKET_TYPE = PacketType.create(ID, ConfigurationPacket::new);
|
||||
|
||||
public ConfigurationPacket(PacketByteBuf buf) {
|
||||
this(buf.readInt());
|
||||
}
|
||||
public record ConfigurationPacket(int version) implements CustomPayload {
|
||||
public static final CustomPayload.Id<ConfigurationPacket> ID = CustomPayload.id(RequestRouter.MOD_ID + ":handshake");
|
||||
public static final PacketCodec<ByteBuf, ConfigurationPacket> CODEC = PacketCodecs.INTEGER.xmap(ConfigurationPacket::new, ConfigurationPacket::version);
|
||||
|
||||
@Override
|
||||
public void write(PacketByteBuf buf) {
|
||||
buf.writeInt(version);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PacketType<?> getType() {
|
||||
return PACKET_TYPE;
|
||||
public Id<? extends CustomPayload> getId() {
|
||||
return ID;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,33 @@
|
||||
package io.gitlab.jfronny.libjf.config.impl.network.packet;
|
||||
|
||||
import io.gitlab.jfronny.libjf.config.impl.network.RequestRouter;
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.network.codec.PacketCodec;
|
||||
import net.minecraft.network.packet.CustomPayload;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public record RequestPacket(long request, @Nullable Long parent, String name, PacketByteBuf aux) implements CustomPayload {
|
||||
public static final Id<RequestPacket> ID = CustomPayload.id(RequestRouter.MOD_ID + ":request");
|
||||
public static final PacketCodec<PacketByteBuf, RequestPacket> CODEC = PacketCodec.of(
|
||||
(value, buf) -> {
|
||||
buf.writeLong(value.request);
|
||||
buf.writeBoolean(value.parent != null);
|
||||
if (value.parent != null) buf.writeLong(value.parent);
|
||||
buf.writeString(value.name);
|
||||
buf.writeBytes(value.aux);
|
||||
},
|
||||
buf -> {
|
||||
long request = buf.readLong();
|
||||
Long parent = buf.readBoolean() ? buf.readLong() : null;
|
||||
String name = buf.readString();
|
||||
PacketByteBuf aux = PacketByteBufs.copy(buf);
|
||||
return new RequestPacket(request, parent, name, aux);
|
||||
}
|
||||
);
|
||||
|
||||
@Override
|
||||
public Id<? extends CustomPayload> getId() {
|
||||
return ID;
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package io.gitlab.jfronny.libjf.config.impl.network.packet;
|
||||
|
||||
import io.gitlab.jfronny.libjf.config.impl.network.RequestRouter;
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
import net.minecraft.network.codec.PacketCodec;
|
||||
import net.minecraft.network.packet.CustomPayload;
|
||||
|
||||
public record ResponsePacket(long request, int status, PacketByteBuf aux) implements CustomPayload {
|
||||
public static final CustomPayload.Id<ResponsePacket> ID = CustomPayload.id(RequestRouter.MOD_ID + ":response");
|
||||
public static final PacketCodec<PacketByteBuf, ResponsePacket> CODEC = PacketCodec.of(
|
||||
(value, buf) -> {
|
||||
buf.writeLong(value.request());
|
||||
buf.writeInt(value.status());
|
||||
buf.writeBytes(value.aux());
|
||||
},
|
||||
buf -> {
|
||||
long request = buf.readLong();
|
||||
int status = buf.readInt();
|
||||
return new ResponsePacket(request, status, PacketByteBufs.copy(buf));
|
||||
}
|
||||
);
|
||||
|
||||
@Override
|
||||
public Id<? extends CustomPayload> getId() {
|
||||
return ID;
|
||||
}
|
||||
}
|
@ -1,18 +1,14 @@
|
||||
package io.gitlab.jfronny.libjf.config.impl.network.rci.entry;
|
||||
|
||||
import io.gitlab.jfronny.gson.stream.JsonReader;
|
||||
import io.gitlab.jfronny.gson.stream.JsonWriter;
|
||||
import io.gitlab.jfronny.commons.serialize.SerializeReader;
|
||||
import io.gitlab.jfronny.commons.serialize.SerializeWriter;
|
||||
import io.gitlab.jfronny.libjf.config.api.v2.EntryInfo;
|
||||
import io.gitlab.jfronny.libjf.config.api.v2.type.Type;
|
||||
import io.gitlab.jfronny.libjf.config.impl.network.rci.MirrorConfigCategory;
|
||||
import io.gitlab.jfronny.libjf.config.impl.network.rci.MirrorObject;
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
|
||||
import net.fabricmc.fabric.api.networking.v1.PacketSender;
|
||||
import net.minecraft.network.PacketByteBuf;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
public abstract class MirrorEntryInfoBase<T> extends MirrorObject implements EntryInfo<T> {
|
||||
protected final MirrorConfigCategory category;
|
||||
protected final String entryName;
|
||||
@ -34,13 +30,13 @@ public abstract class MirrorEntryInfoBase<T> extends MirrorObject implements Ent
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadFromJson(JsonReader reader) throws IOException, IllegalAccessException {
|
||||
throw new UnsupportedEncodingException();
|
||||
public <TEx extends Exception, Reader extends SerializeReader<TEx, Reader>> void loadFromJson(Reader reader) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(JsonWriter writer, String translationPrefix) throws IOException, IllegalAccessException {
|
||||
throw new UnsupportedEncodingException();
|
||||
public <TEx extends Exception, Writer extends SerializeWriter<TEx, Writer>> void writeTo(Writer writer, String translationPrefix) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -51,7 +47,7 @@ public abstract class MirrorEntryInfoBase<T> extends MirrorObject implements Ent
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() throws IllegalAccessException {
|
||||
public void reset() {
|
||||
PacketByteBuf buf = PacketByteBufs.create();
|
||||
writePath(buf);
|
||||
sendRequest("resetEntry", buf);
|
||||
|
@ -5,6 +5,8 @@ import io.gitlab.jfronny.libjf.config.api.v2.type.Type;
|
||||
import io.gitlab.jfronny.libjf.config.impl.network.packet.ConfigurationCompletePacket;
|
||||
import io.gitlab.jfronny.libjf.config.impl.network.packet.ConfigurationPacket;
|
||||
import io.gitlab.jfronny.libjf.config.impl.network.RequestRouter;
|
||||
import io.gitlab.jfronny.libjf.config.impl.network.packet.RequestPacket;
|
||||
import io.gitlab.jfronny.libjf.config.impl.network.packet.ResponsePacket;
|
||||
import io.gitlab.jfronny.libjf.config.impl.network.rci.entry.Datatype;
|
||||
import io.gitlab.jfronny.libjf.config.impl.network.rci.entry.MirrorEntryInfo;
|
||||
import net.fabricmc.fabric.api.networking.v1.*;
|
||||
@ -18,19 +20,20 @@ import java.util.function.Consumer;
|
||||
|
||||
public class JfConfigNetworkServer {
|
||||
public static void initialize() {
|
||||
ServerPlayNetworking.registerGlobalReceiver(RequestRouter.REQUEST_ID, (server, player, handler, buf, responseSender) -> {
|
||||
if (authenticate(player)) RequestRouter.acceptRequest(buf, responseSender);
|
||||
else RequestRouter.deny(buf, responseSender);
|
||||
RequestRouter.initialize();
|
||||
ServerPlayNetworking.registerGlobalReceiver(RequestPacket.ID, (payload, context) -> {
|
||||
if (authenticate(context.player())) RequestRouter.acceptRequest(payload, context.responseSender());
|
||||
else RequestRouter.deny(payload.request(), context.responseSender());
|
||||
});
|
||||
ServerPlayNetworking.registerGlobalReceiver(RequestRouter.RESPONSE_ID, (server, player, handler, buf, responseSender) -> {
|
||||
if (authenticate(player)) RequestRouter.acceptResponse(buf, responseSender);
|
||||
else RequestRouter.deny(buf, responseSender);
|
||||
ServerPlayNetworking.registerGlobalReceiver(ResponsePacket.ID, (payload, context) -> {
|
||||
if (authenticate(context.player())) RequestRouter.acceptResponse(payload, context.responseSender());
|
||||
else RequestRouter.deny(payload.request(), context.responseSender());
|
||||
});
|
||||
ServerConfigurationNetworking.registerGlobalReceiver(ConfigurationCompletePacket.PACKET_TYPE, (packet, networkHandler, responseSender) -> {
|
||||
networkHandler.completeTask(JfConfigNetworkConfigurationTask.KEY);
|
||||
ServerConfigurationNetworking.registerGlobalReceiver(ConfigurationCompletePacket.ID, (payload, context) -> {
|
||||
context.networkHandler().completeTask(JfConfigNetworkConfigurationTask.KEY);
|
||||
});
|
||||
ServerConfigurationConnectionEvents.CONFIGURE.register((handler, server) -> {
|
||||
if (ServerConfigurationNetworking.canSend(handler, ConfigurationPacket.PACKET_TYPE)) {
|
||||
if (ServerConfigurationNetworking.canSend(handler, ConfigurationPacket.ID)) {
|
||||
handler.addTask(new JfConfigNetworkConfigurationTask());
|
||||
}
|
||||
});
|
||||
|
@ -5,12 +5,11 @@ plugins {
|
||||
}
|
||||
|
||||
base {
|
||||
archivesName.set("libjf-config-ui-tiny")
|
||||
archivesName = "libjf-config-ui-tiny"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
val fabricVersion: String by rootProject.extra
|
||||
api(devProject(":libjf-base"))
|
||||
api(devProject(":libjf-config-core-v2"))
|
||||
include(fabricApi.module("fabric-resource-loader-v0", fabricVersion))
|
||||
include("net.fabricmc.fabric-api:fabric-resource-loader-v0")
|
||||
}
|
||||
|
@ -1,11 +1,8 @@
|
||||
package io.gitlab.jfronny.libjf.config.impl.ui.tiny;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.mojang.blaze3d.platform.GlStateManager;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import io.gitlab.jfronny.commons.ref.R;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import net.minecraft.SharedConstants;
|
||||
import net.minecraft.client.font.TextHandler;
|
||||
import net.minecraft.client.font.TextRenderer;
|
||||
import net.minecraft.client.gui.DrawContext;
|
||||
@ -18,6 +15,7 @@ import net.minecraft.client.util.math.Rect2i;
|
||||
import net.minecraft.screen.ScreenTexts;
|
||||
import net.minecraft.text.*;
|
||||
import net.minecraft.util.Formatting;
|
||||
import net.minecraft.util.StringHelper;
|
||||
import net.minecraft.util.Util;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
@ -161,7 +159,7 @@ public class EditorScreen extends ScreenWithSaveHook {
|
||||
if (super.charTyped(chr, modifiers)) {
|
||||
return true;
|
||||
}
|
||||
if (SharedConstants.isValidChar(chr)) {
|
||||
if (StringHelper.isValidChar(chr)) {
|
||||
this.currentPageSelectionManager.insert(Character.toString(chr));
|
||||
this.invalidatePageContent();
|
||||
return true;
|
||||
@ -296,12 +294,6 @@ public class EditorScreen extends ScreenWithSaveHook {
|
||||
final int maxScroll = this.getMaxScroll();
|
||||
final boolean showScrollbar = maxScroll > 0;
|
||||
|
||||
if (client.world == null) {
|
||||
RenderSystem.setShaderColor(0.125f, 0.125f, 0.125f, 1.0f);
|
||||
context.drawTexture(Screen.OPTIONS_BACKGROUND_TEXTURE, 0, HEADER_SIZE, showScrollbar ? width - SCROLLBAR_SIZE : width, height - FOOTER_SIZE + (int)scrollAmount, width - SCROLLBAR_SIZE, height - HEADER_SIZE - FOOTER_SIZE, 32, 32);
|
||||
RenderSystem.setShaderColor(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
}
|
||||
|
||||
context.enableScissor(0, HEADER_SIZE, width - SCROLLBAR_SIZE, height - FOOTER_SIZE);
|
||||
PageContent pageContent = this.getPageContent();
|
||||
for (Line line : pageContent.lines) {
|
||||
|
@ -1,18 +1,24 @@
|
||||
package io.gitlab.jfronny.libjf.config.impl.ui.tiny;
|
||||
|
||||
import io.gitlab.jfronny.commons.serialize.gson.api.v2.GsonHolders;
|
||||
import io.gitlab.jfronny.commons.Serializer;
|
||||
import io.gitlab.jfronny.commons.serialize.Transport;
|
||||
import io.gitlab.jfronny.commons.serialize.databind.api.TypeToken;
|
||||
import io.gitlab.jfronny.commons.serialize.json.JsonReader;
|
||||
import io.gitlab.jfronny.libjf.LibJf;
|
||||
import io.gitlab.jfronny.libjf.config.api.v2.ConfigInstance;
|
||||
import io.gitlab.jfronny.libjf.config.api.v2.EntryInfo;
|
||||
import io.gitlab.jfronny.libjf.config.api.v2.dsl.CategoryBuilder;
|
||||
import io.gitlab.jfronny.libjf.config.api.v2.type.Type;
|
||||
import io.gitlab.jfronny.libjf.config.api.v2.ui.ConfigScreenFactory;
|
||||
import io.gitlab.jfronny.libjf.serialize.SerializationMode;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.gui.screen.Screen;
|
||||
import net.minecraft.client.resource.language.I18n;
|
||||
import net.minecraft.client.toast.SystemToast;
|
||||
import net.minecraft.text.Text;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
// IDEA doesn't like this, but it does work in practice
|
||||
public class TinyConfigScreenFactory implements ConfigScreenFactory<Screen, TinyConfigScreenFactory.Built> {
|
||||
@Override
|
||||
@ -26,8 +32,9 @@ public class TinyConfigScreenFactory implements ConfigScreenFactory<Screen, Tiny
|
||||
if (type != null && !type.isInt() && !type.isLong() && !type.isFloat() && !type.isDouble() && !type.isString() && !type.isBool() && !type.isEnum()) {
|
||||
final String jsonified;
|
||||
try {
|
||||
jsonified = GsonHolders.CONFIG.getGson().toJson(entry.getValue());
|
||||
} catch (IllegalAccessException e) {
|
||||
var value = entry.getValue();
|
||||
jsonified = LibJf.JSON_TRANSPORT.write(writer -> LibJf.MAPPER.serialize(value, SerializationMode.asConfig(writer)));
|
||||
} catch (IllegalAccessException | IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
String key = config.getTranslationPrefix() + entry.getName();
|
||||
@ -38,7 +45,12 @@ public class TinyConfigScreenFactory implements ConfigScreenFactory<Screen, Tiny
|
||||
jsonified,
|
||||
json -> {
|
||||
try {
|
||||
entry.setValue(GsonHolders.CONFIG.getGson().fromJson(json, type.asClass()));
|
||||
entry.setValue(LibJf.JSON_TRANSPORT.read(
|
||||
json,
|
||||
(Transport.Returnable<JsonReader, ? extends Object, IOException>) reader -> LibJf.MAPPER
|
||||
.getAdapter(TypeToken.get(type.asClass()))
|
||||
.deserialize(SerializationMode.asConfig(reader))));
|
||||
entry.setValue(Serializer.getInstance().deserialize(json, type.asClass()));
|
||||
config.write();
|
||||
} catch (Throwable e) {
|
||||
LibJf.LOGGER.error("Could not write element", e);
|
||||
|
@ -1,14 +1,19 @@
|
||||
package io.gitlab.jfronny.libjf.config.impl.ui.tiny.entry;
|
||||
|
||||
import io.gitlab.jfronny.commons.Serializer;
|
||||
import io.gitlab.jfronny.commons.ref.R;
|
||||
import io.gitlab.jfronny.commons.serialize.gson.api.v2.GsonHolders;
|
||||
import io.gitlab.jfronny.commons.serialize.Transport;
|
||||
import io.gitlab.jfronny.commons.serialize.databind.api.TypeToken;
|
||||
import io.gitlab.jfronny.commons.serialize.json.JsonReader;
|
||||
import io.gitlab.jfronny.commons.throwable.Try;
|
||||
import io.gitlab.jfronny.libjf.LibJf;
|
||||
import io.gitlab.jfronny.libjf.config.api.v2.ConfigCategory;
|
||||
import io.gitlab.jfronny.libjf.config.api.v2.EntryInfo;
|
||||
import io.gitlab.jfronny.libjf.config.api.v2.JfConfig;
|
||||
import io.gitlab.jfronny.libjf.config.api.v2.type.Type;
|
||||
import io.gitlab.jfronny.libjf.config.impl.ConfigCore;
|
||||
import io.gitlab.jfronny.libjf.config.impl.ui.tiny.EditorScreen;
|
||||
import io.gitlab.jfronny.libjf.serialize.SerializationMode;
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
import net.minecraft.client.gui.widget.ButtonWidget;
|
||||
@ -19,6 +24,7 @@ import net.minecraft.text.Text;
|
||||
import net.minecraft.util.Formatting;
|
||||
import net.minecraft.util.Language;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
@ -171,7 +177,7 @@ public class EntryInfoWidgetBuilder {
|
||||
final String jsonified;
|
||||
if (state.tempValue == null) {
|
||||
try {
|
||||
jsonified = GsonHolders.CONFIG.getGson().toJson(state.cachedValue);
|
||||
jsonified = LibJf.JSON_TRANSPORT.write(writer -> LibJf.MAPPER.serialize(state.cachedValue, SerializationMode.asConfig(writer)));
|
||||
} catch (Throwable e) {
|
||||
LibJf.LOGGER.error("Could not stringify element", e);
|
||||
SystemToast.add(
|
||||
@ -193,7 +199,11 @@ public class EntryInfoWidgetBuilder {
|
||||
jsonified,
|
||||
json -> {
|
||||
try {
|
||||
state.updateCache(GsonHolders.CONFIG.getGson().fromJson(json, info.getValueType().asClass()));
|
||||
state.updateCache(LibJf.JSON_TRANSPORT.read(
|
||||
json,
|
||||
(Transport.Returnable<JsonReader, ? extends T, IOException>) reader -> LibJf.MAPPER
|
||||
.getAdapter((TypeToken<T>) TypeToken.get(info.getValueType().asClass()))
|
||||
.deserialize(SerializationMode.asConfig(reader))));
|
||||
state.tempValue = null;
|
||||
} catch (Throwable e) {
|
||||
LibJf.LOGGER.error("Could not write element", e);
|
||||
|
@ -19,18 +19,15 @@ import java.util.function.Supplier;
|
||||
@Environment(EnvType.CLIENT)
|
||||
public class EntryListWidget extends ElementListWidget<EntryListWidget.ConfigEntry> {
|
||||
private final TextRenderer textRenderer;
|
||||
private final boolean background;
|
||||
|
||||
public EntryListWidget(MinecraftClient client, TextRenderer tr, int width, int height, int top) {
|
||||
super(client, width, height, top, 25);
|
||||
this.centerListVertically = false;
|
||||
textRenderer = tr;
|
||||
background = client.world == null;
|
||||
setRenderBackground(background);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getScrollbarPositionX() {
|
||||
public int getScrollbarX() {
|
||||
return this.width -7;
|
||||
}
|
||||
|
||||
@ -82,12 +79,6 @@ public class EntryListWidget extends ElementListWidget<EntryListWidget.ConfigEnt
|
||||
@Override
|
||||
protected void renderDecorations(DrawContext context, int mouseX, int mouseY) {
|
||||
super.renderDecorations(context, mouseX, mouseY);
|
||||
if (background) return;
|
||||
|
||||
// Background texture at the top of the screen
|
||||
context.setShaderColor(0.25f, 0.25f, 0.25f, 1.0f);
|
||||
context.drawTexture(Screen.OPTIONS_BACKGROUND_TEXTURE, getX(), 0, 0.0f, 0.0f, width, getY(), 32, 32);
|
||||
context.setShaderColor(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
|
||||
// Gradient at the top of the screen
|
||||
context.fillGradient(RenderLayer.getGuiOverlay(), getX(), getY(), getRight(), getY() + 4, 0xff000000, 0x00000000, 0);
|
||||
|
@ -11,7 +11,6 @@ import java.util.List;
|
||||
public class PresetListWidget extends ElementListWidget<PresetListWidget.PresetEntry> {
|
||||
public PresetListWidget(MinecraftClient client, int width, int height, int top, int itemHeight) {
|
||||
super(client, width, height, top, itemHeight);
|
||||
setRenderBackground(client.world == null);
|
||||
}
|
||||
|
||||
public void addButton(ClickableWidget button) {
|
||||
@ -19,8 +18,8 @@ public class PresetListWidget extends ElementListWidget<PresetListWidget.PresetE
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getScrollbarPositionX() {
|
||||
return this.width -7;
|
||||
protected int getScrollbarX() {
|
||||
return this.width - 7;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -5,6 +5,7 @@ import io.gitlab.jfronny.libjf.config.api.v2.ui.ConfigScreenFactory;
|
||||
import net.minecraft.client.gui.screen.Screen;
|
||||
import net.minecraft.client.gui.screen.option.OptionsScreen;
|
||||
import net.minecraft.client.gui.widget.ButtonWidget;
|
||||
import net.minecraft.client.gui.widget.DirectionalLayoutWidget;
|
||||
import net.minecraft.client.gui.widget.GridWidget;
|
||||
import net.minecraft.text.Text;
|
||||
import org.spongepowered.asm.mixin.*;
|
||||
@ -25,8 +26,8 @@ public abstract class OptionsScreenMixin extends Screen {
|
||||
super(title);
|
||||
}
|
||||
|
||||
@Inject(method = "init()V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/widget/GridWidget;refreshPositions()V"), locals = LocalCapture.CAPTURE_FAILHARD)
|
||||
void injectButton(CallbackInfo ci, GridWidget gridWidget, GridWidget.Adder adder) {
|
||||
@Inject(method = "init()V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/widget/ThreePartsLayoutWidget;addBody(Lnet/minecraft/client/gui/widget/Widget;)Lnet/minecraft/client/gui/widget/Widget;"), locals = LocalCapture.CAPTURE_FAILHARD)
|
||||
void injectButton(CallbackInfo ci, DirectionalLayoutWidget directionalLayoutWidget, DirectionalLayoutWidget directionalLayoutWidget2, GridWidget gridWidget, GridWidget.Adder adder) {
|
||||
ConfigHolder.getInstance().getRegistered().forEach((key, config) -> {
|
||||
adder.add(
|
||||
createButton(Text.translatable(key + ".jfconfig.title"), () -> ConfigScreenFactory.getInstance()
|
||||
|
@ -5,12 +5,11 @@ plugins {
|
||||
}
|
||||
|
||||
base {
|
||||
archivesName.set("libjf-data-manipulation-v0")
|
||||
archivesName = "libjf-data-manipulation-v0"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
val fabricVersion: String by rootProject.extra
|
||||
api(devProject(":libjf-base"))
|
||||
api(devProject(":libjf-unsafe-v0"))
|
||||
modApi(fabricApi.module("fabric-api-base", fabricVersion))
|
||||
modApi("net.fabricmc.fabric-api:fabric-api-base")
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
package io.gitlab.jfronny.libjf.data.manipulation.api;
|
||||
|
||||
import io.gitlab.jfronny.commons.LazySupplier;
|
||||
import io.gitlab.jfronny.commons.concurrent.ScopedValue;
|
||||
import io.gitlab.jfronny.commons.throwable.ExceptionWrapper;
|
||||
import io.gitlab.jfronny.commons.throwable.ThrowingRunnable;
|
||||
import io.gitlab.jfronny.commons.throwable.ThrowingSupplier;
|
||||
import io.gitlab.jfronny.libjf.LibJf;
|
||||
@ -19,20 +21,17 @@ import java.util.function.Supplier;
|
||||
public class UserResourceEvents {
|
||||
public static <TVal, TEx extends Throwable> TVal disable(ThrowingSupplier<TVal, TEx> then) throws TEx {
|
||||
try {
|
||||
ResourcePackHook.setDisabled(true);
|
||||
return then.get();
|
||||
}
|
||||
finally {
|
||||
ResourcePackHook.setDisabled(false);
|
||||
return ScopedValue.getWhere(ResourcePackHook.DISABLED, true, then.orThrow());
|
||||
} catch (ExceptionWrapper ew) {
|
||||
throw (TEx) ExceptionWrapper.unwrap(ew);
|
||||
}
|
||||
}
|
||||
|
||||
public static <TEx extends Throwable> void disable(ThrowingRunnable<TEx> then) throws TEx {
|
||||
try {
|
||||
ResourcePackHook.setDisabled(true);
|
||||
then.run();
|
||||
} finally {
|
||||
ResourcePackHook.setDisabled(false);
|
||||
ScopedValue.runWhere(ResourcePackHook.DISABLED, true, then.orThrow());
|
||||
} catch (ExceptionWrapper ew) {
|
||||
throw (TEx) ExceptionWrapper.unwrap(ew);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
package io.gitlab.jfronny.libjf.data.manipulation.impl;
|
||||
|
||||
import io.gitlab.jfronny.commons.LazySupplier;
|
||||
import io.gitlab.jfronny.commons.concurrent.ScopedValue;
|
||||
import io.gitlab.jfronny.libjf.data.manipulation.api.UserResourceEvents;
|
||||
import net.minecraft.resource.*;
|
||||
import net.minecraft.resource.metadata.ResourceMetadataReader;
|
||||
@ -9,22 +10,15 @@ import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@ApiStatus.Internal
|
||||
public class ResourcePackHook {
|
||||
private static final Map<Long, Boolean> disabled = new HashMap<>();
|
||||
|
||||
@ApiStatus.Internal
|
||||
public static void setDisabled(boolean disabled) {
|
||||
ResourcePackHook.disabled.put(Thread.currentThread().getId(), disabled);
|
||||
}
|
||||
public static final ScopedValue<Boolean> DISABLED = new ScopedValue<>();
|
||||
|
||||
@ApiStatus.Internal
|
||||
public static boolean isDisabled() {
|
||||
return disabled.getOrDefault(Thread.currentThread().getId(), false);
|
||||
private static boolean isDisabled() {
|
||||
return DISABLED.orElse(false);
|
||||
}
|
||||
|
||||
public static InputSupplier<InputStream> hookOpenRoot(InputSupplier<InputStream> value, ResourcePack pack, String[] fileName) {
|
||||
|
@ -11,7 +11,7 @@ public class TestEntrypoint implements ModInitializer {
|
||||
// This should prevent resource packs from doing anything if my hooks are working and
|
||||
UserResourceEvents.OPEN.register((type, id, previous, pack) -> {
|
||||
if (pack instanceof DirectoryResourcePack) {
|
||||
LibJf.LOGGER.info(pack.getName() + " opened " + type.name() + "/" + id.toString());
|
||||
LibJf.LOGGER.info(pack.getInfo().title() + " opened " + type.name() + "/" + id.toString());
|
||||
return null;
|
||||
}
|
||||
return previous;
|
||||
|
@ -5,11 +5,10 @@ plugins {
|
||||
}
|
||||
|
||||
base {
|
||||
archivesName.set("libjf-data-v0")
|
||||
archivesName = "libjf-data-v0"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
val fabricVersion: String by rootProject.extra
|
||||
api(devProject(":libjf-base"))
|
||||
include(fabricApi.module("fabric-resource-loader-v0", fabricVersion))
|
||||
include("net.fabricmc.fabric-api:fabric-resource-loader-v0")
|
||||
}
|
||||
|
@ -2,7 +2,9 @@ package io.gitlab.jfronny.libjf.data.mixin;
|
||||
|
||||
import io.gitlab.jfronny.libjf.data.Tags;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.entity.damage.DamageSource;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
@ -37,8 +39,9 @@ public class EntityMixin {
|
||||
|
||||
private boolean libjf$opArmor() {
|
||||
Entity entity = (Entity) (Object) this;
|
||||
if (!(entity instanceof LivingEntity le)) return false;
|
||||
AtomicInteger armorCount = new AtomicInteger();
|
||||
for (ItemStack s : entity.getArmorItems()) {
|
||||
for (ItemStack s : le.getArmorItems()) {
|
||||
if (s.isIn(Tags.OVERPOWERED))
|
||||
armorCount.getAndIncrement();
|
||||
}
|
||||
|
@ -3,10 +3,9 @@ plugins {
|
||||
}
|
||||
|
||||
base {
|
||||
archivesName.set("libjf-mainhttp-v0")
|
||||
archivesName = "libjf-mainhttp-v0"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
val fabricVersion: String by rootProject.extra
|
||||
implementation(fabricApi.module("fabric-api-base", fabricVersion))
|
||||
modImplementation("net.fabricmc.fabric-api:fabric-api-base")
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ plugins {
|
||||
}
|
||||
|
||||
base {
|
||||
archivesName.set("libjf-translate-v1")
|
||||
archivesName = "libjf-translate-v1"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
@ -1,7 +1,7 @@
|
||||
package io.gitlab.jfronny.libjf.translate.impl.libretranslate;
|
||||
|
||||
import io.gitlab.jfronny.commons.http.client.HttpClient;
|
||||
import io.gitlab.jfronny.gson.reflect.TypeToken;
|
||||
import io.gitlab.jfronny.commons.serialize.databind.api.TypeToken;
|
||||
import io.gitlab.jfronny.libjf.translate.api.TranslateException;
|
||||
import io.gitlab.jfronny.libjf.translate.impl.AbstractTranslateService;
|
||||
import io.gitlab.jfronny.libjf.translate.impl.libretranslate.model.*;
|
||||
|
@ -5,12 +5,11 @@ plugins {
|
||||
}
|
||||
|
||||
base {
|
||||
archivesName.set("libjf-unsafe-v0")
|
||||
archivesName = "libjf-unsafe-v0"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
val commonsVersion: String by rootProject.extra
|
||||
api(devProject(":libjf-base"))
|
||||
compileOnly("io.gitlab.jfronny:commons-unsafe:$commonsVersion") { isTransitive = false }
|
||||
shadow("io.gitlab.jfronny:commons-unsafe:$commonsVersion") { isTransitive = false }
|
||||
compileOnly(libs.commons.unsafe) { isTransitive = false }
|
||||
shadow(libs.commons.unsafe) { isTransitive = false }
|
||||
}
|
||||
|
@ -1,118 +0,0 @@
|
||||
package io.gitlab.jfronny.libjf.unsafe;
|
||||
|
||||
import io.gitlab.jfronny.commons.logging.Logger;
|
||||
import net.fabricmc.loader.impl.util.log.Log;
|
||||
import net.fabricmc.loader.impl.util.log.LogCategory;
|
||||
|
||||
public class FLLogger implements Logger {
|
||||
private final LogCategory category;
|
||||
|
||||
public FLLogger(String context, String... names) {
|
||||
this.category = LogCategory.createCustom(context, names);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return category.name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void trace(String msg) {
|
||||
Log.trace(category, msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void trace(String format, Object arg) {
|
||||
Log.trace(category, format, arg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void trace(String format, Object... args) {
|
||||
Log.trace(category, format, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void trace(String msg, Throwable t) {
|
||||
Log.trace(category, msg, t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void debug(String msg) {
|
||||
Log.debug(category, msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void debug(String format, Object arg) {
|
||||
Log.debug(category, format, arg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void debug(String format, Object... args) {
|
||||
Log.debug(category, format, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void debug(String msg, Throwable t) {
|
||||
Log.debug(category, msg, t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void info(String msg) {
|
||||
Log.info(category, msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void info(String format, Object arg) {
|
||||
Log.info(category, format, arg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void info(String format, Object... args) {
|
||||
Log.info(category, format, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void info(String msg, Throwable t) {
|
||||
Log.info(category, msg, t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warn(String msg) {
|
||||
Log.warn(category, msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warn(String format, Object arg) {
|
||||
Log.warn(category, format, arg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warn(String format, Object... args) {
|
||||
Log.warn(category, format, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warn(String msg, Throwable t) {
|
||||
Log.warn(category, msg, t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(String msg) {
|
||||
Log.error(category, msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(String format, Object arg) {
|
||||
Log.error(category, format, arg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(String format, Object... args) {
|
||||
Log.error(category, format, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(String msg, Throwable t) {
|
||||
Log.error(category, msg, t);
|
||||
}
|
||||
}
|
@ -1,9 +1,6 @@
|
||||
package io.gitlab.jfronny.libjf.unsafe;
|
||||
|
||||
import io.gitlab.jfronny.commons.logging.Level;
|
||||
import io.gitlab.jfronny.commons.logging.Logger;
|
||||
import io.gitlab.jfronny.libjf.LibJf;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import net.fabricmc.loader.api.LanguageAdapter;
|
||||
|
||||
public class JfLanguageAdapter implements LanguageAdapter {
|
||||
@ -11,8 +8,6 @@ public class JfLanguageAdapter implements LanguageAdapter {
|
||||
public native <T> T create(net.fabricmc.loader.api.ModContainer mod, String value, Class<T> type);
|
||||
|
||||
static {
|
||||
Logger.registerFactory(FLLogger::new); // Reset in LibJf entrypoint
|
||||
Logger.setMinimumLevel(FabricLoader.getInstance().isDevelopmentEnvironment() ? Level.TRACE : Level.INFO);
|
||||
LibJf.LOGGER.info("Starting unsafe init"); // Also ensures LibJF.<clinit> is called and Gson is initialized
|
||||
DynamicEntry.execute("libjf:preEarly", UltraEarlyInit.class, s -> s.instance().init());
|
||||
DynamicEntry.execute("libjf:early", UltraEarlyInit.class, s -> s.instance().init());
|
||||
|
@ -5,15 +5,14 @@ plugins {
|
||||
}
|
||||
|
||||
base {
|
||||
archivesName.set("libjf-web-v1")
|
||||
archivesName = "libjf-web-v1"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
val fabricVersion: String by rootProject.extra
|
||||
api(devProject(":libjf-base"))
|
||||
api(devProject(":libjf-config-core-v2"))
|
||||
api(devProject(":libjf-mainhttp-v0"))
|
||||
include(modImplementation(fabricApi.module("fabric-command-api-v2", fabricVersion))!!)
|
||||
include(modImplementation("net.fabricmc.fabric-api:fabric-command-api-v2")!!)
|
||||
|
||||
annotationProcessor(project(":libjf-config-compiler-plugin-v2"))
|
||||
}
|
||||
|
@ -1,11 +1,13 @@
|
||||
pluginManagement {
|
||||
repositories {
|
||||
maven("https://maven.fabricmc.net/") // FabricMC
|
||||
maven("https://maven.architectury.dev/") // Architectury
|
||||
maven("https://files.minecraftforge.net/maven/") // Forge
|
||||
maven("https://maven.frohnmeyer-wds.de/artifacts") // scripts
|
||||
gradlePluginPortal()
|
||||
}
|
||||
plugins {
|
||||
id("jfmod") version("1.5-SNAPSHOT")
|
||||
id("jfmod") version("1.6-SNAPSHOT")
|
||||
id("jfmod.module")
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user