diff --git a/build.gradle b/build.gradle index 4f5e65f..5f6af22 100644 --- a/build.gradle +++ b/build.gradle @@ -16,13 +16,21 @@ dependencies { mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2" modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" - // Fabric API. This is technically optional, but you probably want it anyway. + // Fabric API modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" + // LuaJ + include(implementation("org.luaj:luaj-jse:3.0.1")) + + // Configurate + include(implementation("org.spongepowered:configurate-core:4.1.2")) + include(implementation("org.spongepowered:configurate-gson:4.1.2")) + include(implementation("io.leangen.geantyref:geantyref:1.3.16")) + + // Adventure include(modImplementation("net.kyori:adventure-platform-fabric:6.2.0")) include(implementation("net.kyori:adventure-text-serializer-gson:4.18.0")) include(implementation("net.kyori:adventure-text-serializer-legacy:4.18.0")) - include(implementation("org.luaj:luaj-jse:3.0.1")) } processResources { diff --git a/src/main/java/land/chipmunk/chipmunkmod/ChipmunkMod.java b/src/main/java/land/chipmunk/chipmunkmod/ChipmunkMod.java index 8f5ed3b..7fdd54c 100644 --- a/src/main/java/land/chipmunk/chipmunkmod/ChipmunkMod.java +++ b/src/main/java/land/chipmunk/chipmunkmod/ChipmunkMod.java @@ -1,85 +1,74 @@ package land.chipmunk.chipmunkmod; -import com.google.gson.GsonBuilder; import land.chipmunk.chipmunkmod.modules.KaboomCheck; import land.chipmunk.chipmunkmod.modules.SelfCare; -import land.chipmunk.chipmunkmod.util.gson.BlockBoxTypeAdapter; -import land.chipmunk.chipmunkmod.util.gson.BlockPosTypeAdapter; +import land.chipmunk.chipmunkmod.util.configurate.ConfigurateUtilities; import net.fabricmc.api.ModInitializer; -import java.io.InputStream; -import java.io.FileInputStream; -import java.io.InputStreamReader; -import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileWriter; +import net.fabricmc.loader.api.FabricLoader; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.spongepowered.configurate.BasicConfigurationNode; +import org.spongepowered.configurate.gson.GsonConfigurationLoader; +import org.spongepowered.configurate.objectmapping.ObjectMapper; +import org.spongepowered.configurate.util.NamingSchemes; + import java.io.IOException; +import java.nio.file.Path; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import net.minecraft.util.math.BlockBox; -import net.minecraft.util.math.BlockPos; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import com.google.gson.Gson; - public class ChipmunkMod implements ModInitializer { - // This logger is used to write text to the console and the log file. - // It is considered best practice to use your mod id as the logger's name. - // That way, it's clear which mod wrote info, warnings, and errors. - public static final Logger LOGGER = LoggerFactory.getLogger("chipmunkmod"); - public static Configuration CONFIG; - private static File CONFIG_DIR = new File("config"); - private static File CONFIG_FILE = new File(CONFIG_DIR, "chipmunkmod.json"); + private static final Path CONFIG_PATH = FabricLoader.getInstance() + .getConfigDir().resolve("chipmunkmod.json"); - public static ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); + public static final Logger LOGGER = LoggerFactory.getLogger("chipmunkmod"); + public static Configuration CONFIG; - @Override - public void onInitialize () { - // This code runs as soon as Minecraft is in a mod-load-ready state. - // However, some things (like resources) may still be uninitialized. - // Proceed with mild caution. + public static ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); - try { - CONFIG = loadConfig(); - } catch (IOException exception) { - throw new RuntimeException("Could not load the config", exception); + @Override + public void onInitialize() { + // This code runs as soon as Minecraft is in a mod-load-ready state. + // However, some things (like resources) may still be uninitialized. + // Proceed with mild caution. + + try { + CONFIG = loadConfig(); + } catch (IOException exception) { + throw new RuntimeException("Could not load the config", exception); + } + + KaboomCheck.INSTANCE.init(); + SelfCare.INSTANCE.init(); + + LOGGER.info("Loaded ChipmunkMod (chayapak's fork)"); } - KaboomCheck.INSTANCE.init(); - SelfCare.INSTANCE.init(); + public static Configuration loadConfig() throws IOException { + final ObjectMapper.Factory customFactory = ObjectMapper.factoryBuilder() + .defaultNamingScheme(NamingSchemes.CAMEL_CASE) + .build(); - LOGGER.info("Loaded ChipmunkMod (chayapak's fork)"); - } + final GsonConfigurationLoader loader = GsonConfigurationLoader.builder() + .defaultOptions(options -> options + .serializers(build -> build + .registerAnnotatedObjects(customFactory) + .registerAll(ConfigurateUtilities.customSerializers())) + .shouldCopyDefaults(true)) + .indent(4) + .path(CONFIG_PATH) + .build(); - public static Configuration loadConfig () throws IOException { - CONFIG_DIR.mkdirs(); + // Configurate will create parent directories for us, so we don't need to do it + final BasicConfigurationNode node = loader.load(); + final boolean isBlank = node.empty(); - final Gson gson = new GsonBuilder() - .registerTypeAdapter(BlockPos.class, new BlockPosTypeAdapter()) - .registerTypeAdapter(BlockBox.class, new BlockBoxTypeAdapter()) - .create(); - final File file = CONFIG_FILE; + final Configuration config = node.get(Configuration.class); + if (isBlank) { + node.set(Configuration.class, config); + loader.save(node); + } - if (!file.exists()) { - InputStream is = ChipmunkMod.class.getClassLoader().getResourceAsStream("default_config.json"); - BufferedReader reader = new BufferedReader(new InputStreamReader(is)); - - final StringBuilder sb = new StringBuilder(); - while (reader.ready()) sb.append((char) reader.read()); - final String defaultConfig = sb.toString(); - - // Write the default config - BufferedWriter configWriter = new BufferedWriter(new FileWriter(file)); - configWriter.write(defaultConfig); - configWriter.close(); - - return gson.fromJson(defaultConfig, Configuration.class); + return config; } - - InputStream is = new FileInputStream(file); - BufferedReader reader = new BufferedReader(new InputStreamReader(is)); - - return gson.fromJson(reader, Configuration.class); - } } diff --git a/src/main/java/land/chipmunk/chipmunkmod/Configuration.java b/src/main/java/land/chipmunk/chipmunkmod/Configuration.java index cfea049..a7a4ccf 100644 --- a/src/main/java/land/chipmunk/chipmunkmod/Configuration.java +++ b/src/main/java/land/chipmunk/chipmunkmod/Configuration.java @@ -1,26 +1,32 @@ package land.chipmunk.chipmunkmod; -import com.google.gson.JsonObject; +import net.kyori.adventure.text.Component; import net.minecraft.util.math.BlockBox; import net.minecraft.util.math.BlockPos; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.configurate.objectmapping.ConfigSerializable; - +@ConfigSerializable public class Configuration { public CommandManager commands = new CommandManager(); public CommandCore core = new CommandCore(); public Bots bots = new Bots(); public CustomChat customChat = new CustomChat(); - public boolean fullbright = true; // unused, but it is here for old configs + public boolean fullbright = false; // unused, but it is here for old configs public String autoSkinUsername = "off"; + @ConfigSerializable public static class CommandManager { public String prefix = "."; } + @ConfigSerializable public static class CommandCore { public BlockBox relativeArea = BlockBox.create(new BlockPos(0, 0, 0), new BlockPos(15, 0, 15)); } + @ConfigSerializable public static class Bots { public BotInfo hbot = new BotInfo("#", null); public BotInfo sbot = new BotInfo(":", null); @@ -32,13 +38,15 @@ public class Configuration { public TestBotInfo testbot = new TestBotInfo("-", null); } + @ConfigSerializable public static class ChomeNSBotInfo { public String prefix; - public String key; - public String authKey; - public String formatKey; + public @Nullable String key; + public @Nullable String authKey; + public @Nullable String formatKey; - public ChomeNSBotInfo (String prefix, String key, String authKey, String formatKey) { + public ChomeNSBotInfo() {} + public ChomeNSBotInfo (String prefix, @Nullable String key, @Nullable String authKey, @Nullable String formatKey) { this.prefix = prefix; this.key = key; this.authKey = authKey; @@ -46,27 +54,35 @@ public class Configuration { } } + @ConfigSerializable public static class TestBotInfo { public String prefix; - public String webhookUrl; + public @Nullable String webhookUrl; - public TestBotInfo (String prefix, String webhookUrl) { + public TestBotInfo() {} + public TestBotInfo (String prefix, @Nullable String webhookUrl) { this.prefix = prefix; this.webhookUrl = webhookUrl; } } + @ConfigSerializable public static class BotInfo { public String prefix; - public String key; + public @Nullable String key; - public BotInfo (String prefix, String key) { + public BotInfo() {} + public BotInfo (String prefix, @Nullable String key) { this.prefix = prefix; this.key = key; } } + @ConfigSerializable public static class CustomChat { - public JsonObject format; + public @NotNull Component format = + Component.translatable("chat.type.text", + Component.selector("UUID"), + Component.text("MESSAGE")); } } diff --git a/src/main/java/land/chipmunk/chipmunkmod/modules/CustomChat.java b/src/main/java/land/chipmunk/chipmunkmod/modules/CustomChat.java index 9033d72..8599206 100644 --- a/src/main/java/land/chipmunk/chipmunkmod/modules/CustomChat.java +++ b/src/main/java/land/chipmunk/chipmunkmod/modules/CustomChat.java @@ -1,7 +1,6 @@ package land.chipmunk.chipmunkmod.modules; import com.google.common.hash.Hashing; -import com.google.gson.JsonElement; import land.chipmunk.chipmunkmod.ChipmunkMod; import net.kyori.adventure.text.Component; @@ -17,6 +16,8 @@ import java.util.Timer; import java.util.TimerTask; public class CustomChat { + private static final GsonComponentSerializer GSON = GsonComponentSerializer.gson(); + private final MinecraftClient client; public static final CustomChat INSTANCE = new CustomChat(MinecraftClient.getInstance()); @@ -69,10 +70,7 @@ public class CustomChat { } public void reloadFormat () { - final JsonElement formatString = ChipmunkMod.CONFIG.customChat.format; - - if (formatString == null) format = "{\"translate\":\"chat.type.text\",\"with\":[\"USERNAME\",\"MESSAGE\"]}"; - else format = formatString.toString(); + this.format = GSON.serializeToTree(ChipmunkMod.CONFIG.customChat.format).toString(); } public void chat (String message) { diff --git a/src/main/java/land/chipmunk/chipmunkmod/util/configurate/BlockBoxTypeSerializer.java b/src/main/java/land/chipmunk/chipmunkmod/util/configurate/BlockBoxTypeSerializer.java new file mode 100644 index 0000000..c432272 --- /dev/null +++ b/src/main/java/land/chipmunk/chipmunkmod/util/configurate/BlockBoxTypeSerializer.java @@ -0,0 +1,40 @@ +package land.chipmunk.chipmunkmod.util.configurate; + +import net.minecraft.util.math.BlockBox; +import net.minecraft.util.math.BlockPos; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.configurate.ConfigurationNode; +import org.spongepowered.configurate.serialize.SerializationException; +import org.spongepowered.configurate.serialize.TypeSerializer; + +import java.lang.reflect.Type; + +public class BlockBoxTypeSerializer implements TypeSerializer<BlockBox> { + private static final String START = "start"; + private static final String END = "end"; + + public static final BlockBoxTypeSerializer INSTANCE = new BlockBoxTypeSerializer(); + + private BlockBoxTypeSerializer() { + } + + @Override + public BlockBox deserialize(final Type type, final ConfigurationNode source) throws SerializationException { + final BlockPos start = ConfigurateUtilities.getNodeOrThrow(source, START).require(BlockPos.class); + final BlockPos end = ConfigurateUtilities.getNodeOrThrow(source, END).require(BlockPos.class); + + return BlockBox.create(start, end); + } + + @Override + public void serialize(final Type type, final @Nullable BlockBox box, + final ConfigurationNode target) throws SerializationException { + if (box == null) { + target.raw(null); + return; + } + + target.node(START).set(BlockPos.class, new BlockPos(box.getMinX(), box.getMinY(), box.getMinZ())); + target.node(END).set(BlockPos.class, new BlockPos(box.getMaxX(), box.getMaxY(), box.getMaxZ())); + } +} diff --git a/src/main/java/land/chipmunk/chipmunkmod/util/configurate/BlockPosTypeSerializer.java b/src/main/java/land/chipmunk/chipmunkmod/util/configurate/BlockPosTypeSerializer.java new file mode 100644 index 0000000..1971373 --- /dev/null +++ b/src/main/java/land/chipmunk/chipmunkmod/util/configurate/BlockPosTypeSerializer.java @@ -0,0 +1,42 @@ +package land.chipmunk.chipmunkmod.util.configurate; + +import net.minecraft.util.math.BlockPos; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.configurate.ConfigurationNode; +import org.spongepowered.configurate.serialize.SerializationException; +import org.spongepowered.configurate.serialize.TypeSerializer; + +import java.lang.reflect.Type; + +public class BlockPosTypeSerializer implements TypeSerializer<BlockPos> { + private static final String X = "x"; + private static final String Y = "y"; + private static final String Z = "z"; + + public static final BlockPosTypeSerializer INSTANCE = new BlockPosTypeSerializer(); + + private BlockPosTypeSerializer() { + } + + @Override + public BlockPos deserialize(final Type type, final ConfigurationNode source) throws SerializationException { + final int x = ConfigurateUtilities.getNodeOrThrow(source, X).getInt(); + final int y = ConfigurateUtilities.getNodeOrThrow(source, Y).getInt(); + final int z = ConfigurateUtilities.getNodeOrThrow(source, Z).getInt(); + + return new BlockPos(x, y, z); + } + + @Override + public void serialize(final Type type, final @Nullable BlockPos pos, + final ConfigurationNode target) throws SerializationException { + if (pos == null) { + target.raw(null); + return; + } + + target.node(X).set(pos.getX()); + target.node(Y).set(pos.getY()); + target.node(Z).set(pos.getZ()); + } +} diff --git a/src/main/java/land/chipmunk/chipmunkmod/util/configurate/ComponentTypeSerializer.java b/src/main/java/land/chipmunk/chipmunkmod/util/configurate/ComponentTypeSerializer.java new file mode 100644 index 0000000..735dbd0 --- /dev/null +++ b/src/main/java/land/chipmunk/chipmunkmod/util/configurate/ComponentTypeSerializer.java @@ -0,0 +1,41 @@ +package land.chipmunk.chipmunkmod.util.configurate; + +import com.google.gson.JsonElement; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.configurate.ConfigurationNode; +import org.spongepowered.configurate.serialize.SerializationException; +import org.spongepowered.configurate.serialize.TypeSerializer; + +import java.lang.reflect.Type; + +public class ComponentTypeSerializer implements TypeSerializer<Component> { + private static final GsonComponentSerializer GSON = GsonComponentSerializer.gson(); + public static final ComponentTypeSerializer INSTANCE = new ComponentTypeSerializer(); + + private ComponentTypeSerializer() { + } + + @Override + public Component deserialize(final Type type, final ConfigurationNode source) throws SerializationException { + final JsonElement elem = source.get(JsonElement.class); + if (elem == null) { + return null; + } + + return GSON.deserializeFromTree(elem); + } + + @Override + public void serialize(final Type type, final @Nullable Component component, + final ConfigurationNode target) throws SerializationException { + if (component == null) { + target.raw(null); + return; + } + + final JsonElement elem = GSON.serializeToTree(component); + target.set(JsonElement.class, elem); + } +} diff --git a/src/main/java/land/chipmunk/chipmunkmod/util/configurate/ConfigurateUtilities.java b/src/main/java/land/chipmunk/chipmunkmod/util/configurate/ConfigurateUtilities.java new file mode 100644 index 0000000..793590d --- /dev/null +++ b/src/main/java/land/chipmunk/chipmunkmod/util/configurate/ConfigurateUtilities.java @@ -0,0 +1,35 @@ +package land.chipmunk.chipmunkmod.util.configurate; + +import net.kyori.adventure.text.Component; +import net.minecraft.util.math.BlockBox; +import net.minecraft.util.math.BlockPos; +import org.spongepowered.configurate.ConfigurationNode; +import org.spongepowered.configurate.gson.GsonConfigurationLoader; +import org.spongepowered.configurate.serialize.SerializationException; +import org.spongepowered.configurate.serialize.TypeSerializerCollection; + +import java.util.Arrays; + +public final class ConfigurateUtilities { + private static final TypeSerializerCollection CUSTOM_SERIALIZER_COLLECTION = TypeSerializerCollection.builder() + .registerAll(GsonConfigurationLoader.gsonSerializers()) + .register(BlockPos.class, BlockPosTypeSerializer.INSTANCE) + .register(BlockBox.class, BlockBoxTypeSerializer.INSTANCE) + .register(Component.class, ComponentTypeSerializer.INSTANCE) + .build(); + + private ConfigurateUtilities() { + } + + public static TypeSerializerCollection customSerializers() { + return CUSTOM_SERIALIZER_COLLECTION; + } + + public static ConfigurationNode getNodeOrThrow(final ConfigurationNode source, final Object... path) throws SerializationException { + if (!source.hasChild(path)) { + throw new SerializationException("Required field " + Arrays.toString(path) + " was not present in node"); + } + + return source.node(path); + } +} diff --git a/src/main/java/land/chipmunk/chipmunkmod/util/gson/BlockBoxTypeAdapter.java b/src/main/java/land/chipmunk/chipmunkmod/util/gson/BlockBoxTypeAdapter.java deleted file mode 100644 index 3f94f65..0000000 --- a/src/main/java/land/chipmunk/chipmunkmod/util/gson/BlockBoxTypeAdapter.java +++ /dev/null @@ -1,46 +0,0 @@ -package land.chipmunk.chipmunkmod.util.gson; - -import com.google.gson.TypeAdapter; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonToken; -import com.google.gson.stream.JsonWriter; -import net.minecraft.util.math.BlockBox; -import net.minecraft.util.math.BlockPos; - -import java.io.IOException; - -public class BlockBoxTypeAdapter extends TypeAdapter<BlockBox> { - private final BlockPosTypeAdapter blockPosAdapter = new BlockPosTypeAdapter(); - - @Override - public BlockBox read(JsonReader reader) throws IOException { - BlockPos start = null; - BlockPos end = null; - - reader.beginObject(); - - - while (!reader.peek().equals(JsonToken.END_OBJECT)) { - if (reader.peek().equals(JsonToken.NAME)) { - String name = reader.nextName(); - - switch (name) { - case "start" -> start = blockPosAdapter.read(reader); - case "end" -> end = blockPosAdapter.read(reader); - default -> reader.skipValue(); - } - } - } - - reader.endObject(); - - if (start == null || end == null) return null; - return BlockBox.create(start, end); - } - - @Override - public void write(JsonWriter writer, BlockBox box) { - // TODO - } - -} diff --git a/src/main/java/land/chipmunk/chipmunkmod/util/gson/BlockPosTypeAdapter.java b/src/main/java/land/chipmunk/chipmunkmod/util/gson/BlockPosTypeAdapter.java deleted file mode 100644 index 461c6af..0000000 --- a/src/main/java/land/chipmunk/chipmunkmod/util/gson/BlockPosTypeAdapter.java +++ /dev/null @@ -1,41 +0,0 @@ -package land.chipmunk.chipmunkmod.util.gson; - -import net.minecraft.util.math.BlockPos; -import com.google.gson.TypeAdapter; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonToken; -import com.google.gson.stream.JsonWriter; -import java.io.IOException; - -public class BlockPosTypeAdapter extends TypeAdapter<BlockPos> { - @Override - public BlockPos read (JsonReader reader) throws IOException { - int x = 0; - int y = 0; - int z = 0; - - reader.beginObject(); - - while (!reader.peek().equals(JsonToken.END_OBJECT)) { - if (reader.peek().equals(JsonToken.NAME)) { - String name = reader.nextName(); - - switch (name) { - case "x" -> x = reader.nextInt(); - case "y" -> y = reader.nextInt(); - case "z" -> z = reader.nextInt(); - default -> reader.skipValue(); - } - } - } - - reader.endObject(); - - return new BlockPos(x, y, z); - } - - @Override - public void write (JsonWriter out, BlockPos vec) throws IOException { - // TODO: make this do something lmfaooo - } -} diff --git a/src/main/resources/default_config.json b/src/main/resources/default_config.json deleted file mode 100644 index 2345328..0000000 --- a/src/main/resources/default_config.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "commands": { - "prefix": "." - }, - - "core": { - "relativeArea": { - "start": { "x": 0, "y": 0, "z": 0 }, - "end": { "x": 15, "y": 0, "z": 15 } - } - }, - - "bots": { - "hbot": { "prefix": "#", "key": null }, - "sbot": { "prefix": ":", "key": null }, - "chipmunk": { "prefix": "'", "key": null }, - "chomens": { "prefix": "*", "key": null, "authKey": null, "formatKey": null }, - "fnfboyfriend": { "prefix": "~", "key": null }, - "nbot": { "prefix": "?", "key": null }, - "kittycorp": { "prefix": "^", "key": null }, - "testbot": { "prefix": "-", "webhookUrl": null } - }, - - "customChat": { - "format": { - "translate": "chat.type.text", - "with": [ - { - "selector": "UUID" - }, - { - "text": "MESSAGE" - } - ] - } - }, - - "autoSkinUsername": "off" -}