diff --git a/build.gradle.kts b/build.gradle.kts index 0111ee3..4810104 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -66,6 +66,8 @@ tasks.named("test") { } loom { + accessWidenerPath = file("../../src/main/resources/${mod.id}.accesswidener") + splitEnvironmentSourceSets() mods { diff --git a/gradle.properties b/gradle.properties index c04e84e..de5f264 100644 --- a/gradle.properties +++ b/gradle.properties @@ -7,7 +7,7 @@ mod.id=oneforall mod.author=Koralix Studios mod.group=com.koralix.oneforall mod.description=One mod to replace them all -mod.version=5.8.2 +mod.version=0.1.0 fabric.loader=0.15.11 fabric.versions=1.20.1 diff --git a/src/client/java/com/koralix/oneforall/ClientOneForAll.java b/src/client/java/com/koralix/oneforall/ClientOneForAll.java index b0cd784..f3f2516 100644 --- a/src/client/java/com/koralix/oneforall/ClientOneForAll.java +++ b/src/client/java/com/koralix/oneforall/ClientOneForAll.java @@ -1,9 +1,14 @@ package com.koralix.oneforall; +import com.koralix.oneforall.commands.ClientCommands; import com.koralix.oneforall.input.ButtonEvent; -import com.koralix.oneforall.input.hotkey.HotKey; import com.koralix.oneforall.input.InputManager; +import com.koralix.oneforall.input.hotkey.HotKey; +import com.koralix.oneforall.network.ClientLoginManager; import com.koralix.oneforall.platform.Platform; +import com.koralix.oneforall.settings.ClientSettings; +import com.koralix.oneforall.settings.SettingsManager; +import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; import org.lwjgl.glfw.GLFW; public class ClientOneForAll extends OneForAll { @@ -20,6 +25,10 @@ public static ClientOneForAll getInstance() { @Override public void onInitialize() { getLogger().info("Initializing OneForAll..."); + + SettingsManager.register(ClientSettings.class); + + ClientCommandRegistrationCallback.EVENT.register(ClientCommands::register); } public void onInitializeClient() { @@ -37,6 +46,8 @@ public void onInitializeClient() { .add(new ButtonEvent(GLFW.GLFW_KEY_A, GLFW.GLFW_PRESS, ButtonEvent.ButtonType.KEYBOARD))); inputManager.register(); + + ClientLoginManager.init(); } public InputManager getInputManager() { diff --git a/src/client/java/com/koralix/oneforall/commands/ClientCommands.java b/src/client/java/com/koralix/oneforall/commands/ClientCommands.java new file mode 100644 index 0000000..a30a9ec --- /dev/null +++ b/src/client/java/com/koralix/oneforall/commands/ClientCommands.java @@ -0,0 +1,16 @@ +package com.koralix.oneforall.commands; + +import com.mojang.brigadier.CommandDispatcher; +import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; +import net.minecraft.command.CommandRegistryAccess; + +public final class ClientCommands { + private ClientCommands() { + throw new UnsupportedOperationException("This class cannot be instantiated"); + } + + + public static void register(CommandDispatcher dispatcher, CommandRegistryAccess registryAccess) { + ClientOfaCommand.register(dispatcher, registryAccess); + } +} diff --git a/src/client/java/com/koralix/oneforall/commands/ClientOfaCommand.java b/src/client/java/com/koralix/oneforall/commands/ClientOfaCommand.java new file mode 100644 index 0000000..894779f --- /dev/null +++ b/src/client/java/com/koralix/oneforall/commands/ClientOfaCommand.java @@ -0,0 +1,27 @@ +package com.koralix.oneforall.commands; + +import com.koralix.oneforall.settings.SettingsRegistry; +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; +import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; +import net.minecraft.command.CommandRegistryAccess; +import org.jetbrains.annotations.NotNull; + +import static com.koralix.oneforall.commands.OfaCommand.settings; + +public final class ClientOfaCommand { + private ClientOfaCommand() { + throw new UnsupportedOperationException("This class cannot be instantiated"); + } + + + public static void register( + @NotNull CommandDispatcher dispatcher, + CommandRegistryAccess registryAccess + ) { + LiteralArgumentBuilder cofa = ClientCommandManager.literal("cofa"); + cofa.then(settings(ClientCommandManager::literal, ClientCommandManager::argument, SettingsRegistry.Env.CLIENT)); + dispatcher.register(cofa); + } +} diff --git a/src/client/java/com/koralix/oneforall/network/ClientLoginManager.java b/src/client/java/com/koralix/oneforall/network/ClientLoginManager.java new file mode 100644 index 0000000..a75ff9a --- /dev/null +++ b/src/client/java/com/koralix/oneforall/network/ClientLoginManager.java @@ -0,0 +1,50 @@ +package com.koralix.oneforall.network; + +import com.koralix.oneforall.OneForAll; +import com.koralix.oneforall.serde.Serde; +import com.koralix.oneforall.settings.ClientSettings; +import com.koralix.oneforall.settings.ProtocolUsageConditions; +import io.netty.util.concurrent.Future; +import io.netty.util.concurrent.GenericFutureListener; +import net.fabricmc.fabric.api.client.networking.v1.ClientLoginConnectionEvents; +import net.fabricmc.fabric.api.client.networking.v1.ClientLoginNetworking; +import net.fabricmc.fabric.api.networking.v1.PacketByteBufs; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.network.ClientLoginNetworkHandler; +import net.minecraft.network.PacketByteBuf; + +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; + +public class ClientLoginManager { + public static void init() { + ClientLoginConnectionEvents.QUERY_START.register((handler, client) -> { + ClientLoginNetworking.registerReceiver(ServerLoginManager.getChannel("hello"), ClientLoginManager::onHello); + }); + } + + private static boolean doesClientEnableProtocol(boolean enforceProtocol) { + ProtocolUsageConditions usageConditions = ClientSettings.PROTOCOL_USAGE_CONDITIONS.value(); + + return switch (usageConditions) { + case Always -> true; + case OnlyEnforced -> enforceProtocol; + case Never -> false; + }; + } + + private static CompletableFuture onHello(MinecraftClient client, ClientLoginNetworkHandler handler, PacketByteBuf buf, Consumer>> listenerAdder) { + String version = Serde.deserialize(buf, String.class); + boolean enforceProtocol = Serde.deserialize(buf, Boolean.class); + + boolean enablesProtocol = doesClientEnableProtocol(enforceProtocol); + if (!enablesProtocol) { + return CompletableFuture.completedFuture(null); + } + + PacketByteBuf response = PacketByteBufs.create(); + Serde.serialize(response, OneForAll.getInstance().getMetadata().version()); + return CompletableFuture.completedFuture(response); + } + +} diff --git a/src/client/java/com/koralix/oneforall/settings/ClientSettings.java b/src/client/java/com/koralix/oneforall/settings/ClientSettings.java new file mode 100644 index 0000000..1db5444 --- /dev/null +++ b/src/client/java/com/koralix/oneforall/settings/ClientSettings.java @@ -0,0 +1,10 @@ +package com.koralix.oneforall.settings; + +import java.util.Objects; + +@SettingsRegistry(id = "client_settings", env = SettingsRegistry.Env.CLIENT) +public class ClientSettings { + public static final ConfigValue PROTOCOL_USAGE_CONDITIONS = ConfigValue.of(ProtocolUsageConditions.Always) + .test(Objects::nonNull) + .build(); +} diff --git a/src/client/java/com/koralix/oneforall/settings/ProtocolUsageConditions.java b/src/client/java/com/koralix/oneforall/settings/ProtocolUsageConditions.java new file mode 100644 index 0000000..cba7d9a --- /dev/null +++ b/src/client/java/com/koralix/oneforall/settings/ProtocolUsageConditions.java @@ -0,0 +1,17 @@ +package com.koralix.oneforall.settings; + +import net.minecraft.util.StringIdentifiable; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +public enum ProtocolUsageConditions implements StringIdentifiable { + Always, + OnlyEnforced, + Never; + + @Contract(pure = true) + @Override + public @NotNull String asString() { + return name(); + } +} diff --git a/src/main/java/com/koralix/oneforall/OneForAll.java b/src/main/java/com/koralix/oneforall/OneForAll.java index 3e49a96..14f464e 100644 --- a/src/main/java/com/koralix/oneforall/OneForAll.java +++ b/src/main/java/com/koralix/oneforall/OneForAll.java @@ -1,7 +1,9 @@ package com.koralix.oneforall; +import com.koralix.oneforall.commands.Commands; +import com.koralix.oneforall.platform.ModMetadata; import com.koralix.oneforall.platform.Platform; -import com.koralix.oneforall.settings.SettingsManager; +import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -10,6 +12,7 @@ public abstract class OneForAll { private final Logger logger = LoggerFactory.getLogger("OneForAll"); private final Platform platform; + private final ModMetadata metadata; public static OneForAll getInstance() { return Initializer.instance; @@ -17,6 +20,13 @@ public static OneForAll getInstance() { public OneForAll(Platform platform) { this.platform = platform; + this.metadata = platform.getMetadata(MOD_ID); + } + + public final void initialize() { + CommandRegistrationCallback.EVENT.register(Commands::register); + + onInitialize(); } abstract void onInitialize(); @@ -28,4 +38,8 @@ public Logger getLogger() { public Platform getPlatform() { return platform; } + + public ModMetadata getMetadata() { + return metadata; + } } diff --git a/src/main/java/com/koralix/oneforall/ServerOneForAll.java b/src/main/java/com/koralix/oneforall/ServerOneForAll.java index c29553a..a8fed7e 100644 --- a/src/main/java/com/koralix/oneforall/ServerOneForAll.java +++ b/src/main/java/com/koralix/oneforall/ServerOneForAll.java @@ -1,5 +1,6 @@ package com.koralix.oneforall; +import com.koralix.oneforall.network.ServerLoginManager; import com.koralix.oneforall.platform.Platform; import com.koralix.oneforall.settings.ServerSettings; import com.koralix.oneforall.settings.SettingsManager; @@ -23,5 +24,7 @@ public void onInitialize() { public void onInitializeServer() { getLogger().info("Initializing OneForAll server..."); + + ServerLoginManager.init(); } } diff --git a/src/main/java/com/koralix/oneforall/commands/ArgumentTypeHelper.java b/src/main/java/com/koralix/oneforall/commands/ArgumentTypeHelper.java new file mode 100644 index 0000000..4898f78 --- /dev/null +++ b/src/main/java/com/koralix/oneforall/commands/ArgumentTypeHelper.java @@ -0,0 +1,88 @@ +package com.koralix.oneforall.commands; + +import com.koralix.oneforall.settings.ConfigValue; +import com.mojang.brigadier.arguments.*; +import com.mojang.brigadier.builder.RequiredArgumentBuilder; +import com.mojang.brigadier.context.CommandContext; +import net.minecraft.command.CommandSource; +import org.jetbrains.annotations.NotNull; + +import java.util.function.BiFunction; + +public class ArgumentTypeHelper { + @SuppressWarnings("unchecked") + public static , S extends CommandSource> RequiredArgumentBuilder createEnumArg( + @NotNull BiFunction, RequiredArgumentBuilder> argument, + String name, + T[] values + ) { + return (RequiredArgumentBuilder) argument.apply(name, StringArgumentType.word()).suggests((context, builder) -> { + for (T value : values) { + builder.suggest(value.name().toLowerCase()); + } + return builder.buildFuture(); + }); + } + + @SuppressWarnings("unchecked") + public static , S extends CommandSource> @NotNull RequiredArgumentBuilder createEnumArg( + @NotNull BiFunction, RequiredArgumentBuilder> argument, + String name, + @NotNull Class clazz + ) { + return createEnumArg(argument, name, (T[]) clazz.getEnumConstants()); + } + + public static , S extends CommandSource> T getEnumValue( + @NotNull CommandContext context, + @NotNull String name, + @NotNull Class clazz + ) { + return Enum.valueOf(clazz, StringArgumentType.getString(context, name).toUpperCase()); + } + + @SuppressWarnings("unchecked") + public static @NotNull RequiredArgumentBuilder createSettingArg( + @NotNull BiFunction, RequiredArgumentBuilder> ofaArgument, + @NotNull ConfigValue setting + ) { + if (setting.clazz().isEnum()) + return (RequiredArgumentBuilder) createEnumArg(ofaArgument, "value", setting.clazz()); + + return (RequiredArgumentBuilder) ofaArgument.apply("value", settingArgumentType(setting.clazz())); + } + + public static T getSettingValue( + @NotNull CommandContext context, + @NotNull String name, + @NotNull ConfigValue setting + ) { + if (setting.clazz().isEnum()) { + Class clazz = setting.clazz().asSubclass(Enum.class); + return (T) getEnumValue(context, name, clazz); + } + + return context.getArgument(name, setting.clazz()); + } + + @SuppressWarnings("unchecked") + private static @NotNull ArgumentType settingArgumentType(@NotNull Class clazz) { + if (clazz == Boolean.class) + return (ArgumentType) BoolArgumentType.bool(); + if (clazz == String.class) + return (ArgumentType) StringArgumentType.string(); + if (clazz == Byte.class) + return (ArgumentType) IntegerArgumentType.integer(Byte.MIN_VALUE, Byte.MAX_VALUE); + if (clazz == Short.class) + return (ArgumentType) IntegerArgumentType.integer(Short.MIN_VALUE, Short.MAX_VALUE); + if (clazz == Integer.class) + return (ArgumentType) IntegerArgumentType.integer(); + if (clazz == Long.class) + return (ArgumentType) LongArgumentType.longArg(); + if (clazz == Float.class) + return (ArgumentType) FloatArgumentType.floatArg(); + if (clazz == Double.class) + return (ArgumentType) DoubleArgumentType.doubleArg(); + throw new UnsupportedOperationException("Unsupported setting type: " + clazz); + } +} diff --git a/src/main/java/com/koralix/oneforall/commands/Commands.java b/src/main/java/com/koralix/oneforall/commands/Commands.java new file mode 100644 index 0000000..07fdee3 --- /dev/null +++ b/src/main/java/com/koralix/oneforall/commands/Commands.java @@ -0,0 +1,45 @@ +package com.koralix.oneforall.commands; + +import com.mojang.brigadier.CommandDispatcher; +import net.minecraft.command.CommandRegistryAccess; +import net.minecraft.server.command.CommandManager; +import net.minecraft.server.command.ServerCommandSource; + +public final class Commands { + private Commands() { + throw new UnsupportedOperationException("This class cannot be instantiated"); + } + + public static void register( + CommandDispatcher dispatcher, + CommandRegistryAccess registryAccess, + CommandManager.RegistrationEnvironment environment + ) { + common(dispatcher, registryAccess, environment); + if (environment.integrated) integrated(dispatcher, registryAccess); + else if (environment.dedicated) dedicated(dispatcher, registryAccess); + else throw new UnsupportedOperationException("Unknown environment"); + } + + private static void integrated( + CommandDispatcher dispatcher, + CommandRegistryAccess registryAccess + ) { + // Register integrated commands here + } + + private static void dedicated( + CommandDispatcher dispatcher, + CommandRegistryAccess registryAccess + ) { + // Register dedicated commands here + } + + private static void common( + CommandDispatcher dispatcher, + CommandRegistryAccess registryAccess, + CommandManager.RegistrationEnvironment environment + ) { + OfaCommand.register(dispatcher, registryAccess, environment); + } +} diff --git a/src/main/java/com/koralix/oneforall/commands/OfaCommand.java b/src/main/java/com/koralix/oneforall/commands/OfaCommand.java new file mode 100644 index 0000000..9d959ee --- /dev/null +++ b/src/main/java/com/koralix/oneforall/commands/OfaCommand.java @@ -0,0 +1,157 @@ +package com.koralix.oneforall.commands; + +import com.koralix.oneforall.settings.SettingEntry; +import com.koralix.oneforall.settings.SettingsManager; +import com.koralix.oneforall.settings.SettingsRegistry; +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.arguments.*; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.builder.RequiredArgumentBuilder; +import com.mojang.brigadier.context.CommandContext; +import net.minecraft.command.CommandRegistryAccess; +import net.minecraft.command.CommandSource; +import net.minecraft.server.command.CommandManager; +import net.minecraft.server.command.ServerCommandSource; +import net.minecraft.text.Text; +import org.jetbrains.annotations.NotNull; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.function.Supplier; + +public final class OfaCommand { + private OfaCommand() { + throw new UnsupportedOperationException("This class cannot be instantiated"); + } + + public static void register( + @NotNull CommandDispatcher dispatcher, + CommandRegistryAccess registryAccess, + CommandManager.RegistrationEnvironment environment + ) { + LiteralArgumentBuilder ofa = CommandManager.literal("ofa"); + ofa.then(settings(CommandManager::literal, CommandManager::argument, SettingsRegistry.Env.SERVER)); + dispatcher.register(ofa); + } + + public static LiteralArgumentBuilder settings( + @NotNull Function> ofaLiteral, + @NotNull BiFunction, RequiredArgumentBuilder> ofaArgument, + SettingsRegistry.Env @NotNull ... envs + ) { + LiteralArgumentBuilder settings = ofaLiteral.apply("settings").executes(OfaCommand::listSettings); + LiteralArgumentBuilder def = ofaLiteral.apply("default"); + LiteralArgumentBuilder restore = ofaLiteral.apply("restore"); + for (SettingsRegistry.Env env : envs) { + SettingsManager.forEach(env, (entry, configValue) -> { + settings.then(setting(ofaLiteral, ofaArgument, entry, def, restore)); + }); + } + settings.then(def); + settings.then(restore); + return settings; + } + + private static LiteralArgumentBuilder setting( + @NotNull Function> ofaLiteral, + @NotNull BiFunction, RequiredArgumentBuilder> ofaArgument, + @NotNull SettingEntry entry, + @NotNull LiteralArgumentBuilder def, + @NotNull LiteralArgumentBuilder restore + ) { + def.then(ofaLiteral.apply(entry.id().toString()) + .requires(source -> !(source instanceof ServerCommandSource scs) || entry.setting().permission(scs)) + .executes(context -> setSetting(context, entry)) + .then(ArgumentTypeHelper.createSettingArg(ofaArgument, entry.setting()) + .executes(context -> setDefaultSetting(context, entry)))); + restore + .requires(source -> !(source instanceof ServerCommandSource scs) || entry.setting().permission(scs)) + .executes(context -> setDefaultSetting(context, entry)); + return ofaLiteral.apply(entry.id().toString()) + .executes(context -> getSetting(context, entry)) + .then(ArgumentTypeHelper.createSettingArg(ofaArgument, entry.setting()) + .executes(context -> setSetting(context, entry))); + } + + private static int listSettings(@NotNull CommandContext context) { + int[] result = {0}; // int* result; + SettingsManager.forEach(SettingsRegistry.Env.SERVER, (entry, configValue) -> + result[0] += getSetting(context, entry) + ); + return result[0]; + } + + private static int getSetting( + @NotNull CommandContext context, + @NotNull SettingEntry entry + ) { + if (context.getSource() instanceof ServerCommandSource scs && !entry.setting().permission(scs)) return 0; + + Method method = null; + final Text text = Text.translatable(entry.translation()) + .append(":\n") + .append("Value: ") + .append(entry.setting().value().toString()) + .append("\n") + .append("Default: ") + .append(entry.setting().defaultValue().toString()) + .append("\n") + .append("Nominal: ") + .append(entry.setting().nominalValue().toString()); + Object arg1 = text; + try { + method = context.getSource().getClass().getDeclaredMethod("sendFeedback", Supplier.class, boolean.class); + arg1 = (Supplier) () -> text; + method.invoke(context.getSource(), arg1, false); + } catch (NoSuchMethodException e1) { + try { + method = context.getSource().getClass().getDeclaredMethod("sendFeedback", Text.class); + method.invoke(context.getSource(), arg1); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e2) { + throw new UnsupportedOperationException("Unsupported command source: " + context.getSource().getClass().getName()); + } + } catch (IllegalAccessException | InvocationTargetException e) { + throw new UnsupportedOperationException("Failed to invoke method: " + method.getName()); + } + + return 1; + } + + private static int setSetting( + @NotNull CommandContext context, + @NotNull SettingEntry entry + ) { + try { + T value = ArgumentTypeHelper.getSettingValue(context, "value", entry.setting()); + Text text = entry.setting().value(value); + if (text != null && context.getSource() instanceof ServerCommandSource scs) { + scs.sendError(text); + return 0; + } + } catch (IllegalArgumentException e) { + entry.setting().reset(); + } + + return 1; + } + + private static int setDefaultSetting( + @NotNull CommandContext context, + @NotNull SettingEntry entry + ) { + try { + T value = ArgumentTypeHelper.getSettingValue(context, "value", entry.setting()); + Text text = entry.setting().defaultValue(value); + if (text != null && context.getSource() instanceof ServerCommandSource scs) { + scs.sendError(text); + return 0; + } + } catch (IllegalArgumentException e) { + entry.setting().restore(); + } + + return 1; + } +} diff --git a/src/main/java/com/koralix/oneforall/mixin/fapi/FixServerLoginNetworkAddonRegisterReceiver.java b/src/main/java/com/koralix/oneforall/mixin/fapi/FixServerLoginNetworkAddonRegisterReceiver.java new file mode 100644 index 0000000..21e743b --- /dev/null +++ b/src/main/java/com/koralix/oneforall/mixin/fapi/FixServerLoginNetworkAddonRegisterReceiver.java @@ -0,0 +1,21 @@ +package com.koralix.oneforall.mixin.fapi; + +import net.fabricmc.fabric.impl.networking.GlobalReceiverRegistry; +import net.fabricmc.fabric.impl.networking.server.ServerLoginNetworkAddon; +import net.minecraft.util.Identifier; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +@Mixin(ServerLoginNetworkAddon.class) +public class FixServerLoginNetworkAddonRegisterReceiver { + @Redirect( + method = "handle(ILnet/minecraft/network/PacketByteBuf;)Z", + at = @At( + value = "INVOKE", + target = "Lnet/fabricmc/fabric/impl/networking/GlobalReceiverRegistry;getHandler(Lnet/minecraft/util/Identifier;)Ljava/lang/Object;" + )) + private Object fix(GlobalReceiverRegistry instance, Identifier channelName) { + return ((ServerLoginNetworkAddon) (Object) this).getHandler(channelName); + } +} diff --git a/src/main/java/com/koralix/oneforall/network/ServerLoginManager.java b/src/main/java/com/koralix/oneforall/network/ServerLoginManager.java new file mode 100644 index 0000000..7c15aff --- /dev/null +++ b/src/main/java/com/koralix/oneforall/network/ServerLoginManager.java @@ -0,0 +1,84 @@ +package com.koralix.oneforall.network; + +import com.koralix.oneforall.OneForAll; +import com.koralix.oneforall.serde.Serde; +import com.koralix.oneforall.settings.ServerSettings; +import net.fabricmc.fabric.api.networking.v1.PacketByteBufs; +import net.fabricmc.fabric.api.networking.v1.PacketSender; +import net.fabricmc.fabric.api.networking.v1.ServerLoginConnectionEvents; +import net.fabricmc.fabric.api.networking.v1.ServerLoginNetworking; +import net.minecraft.network.PacketByteBuf; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.network.ServerLoginNetworkHandler; +import net.minecraft.text.Text; +import net.minecraft.util.Identifier; + +import java.util.Optional; +import java.util.concurrent.CompletableFuture; + + +public class ServerLoginManager { + public static void init() { + ServerLoginConnectionEvents.QUERY_START.register(ServerLoginManager::onQueryStart); + } + + private static PacketByteBuf createHelloPacket() { + String modVersion = OneForAll.getInstance().getMetadata().version(); + boolean enforceProtocol = ServerSettings.ENFORCE_PROTOCOL.value(); + + PacketByteBuf buf = PacketByteBufs.create(); + Serde.serialize(buf, modVersion); + Serde.serialize(buf, enforceProtocol); + + return buf; + } + + public static Identifier getChannel(String name) { + return Identifier.of(OneForAll.MOD_ID, name); + } + + private static void onQueryStart( + ServerLoginNetworkHandler handler, + MinecraftServer server, + PacketSender sender, + ServerLoginNetworking.LoginSynchronizer synchronizer + ) { + if (!ServerSettings.PROTOCOL_ENABLED.value()) return; + + // RECEIVE + CompletableFuture future = new CompletableFuture<>(); + + ServerLoginNetworking.registerReceiver(handler, getChannel("hello"), (server1, handler1, understood, buf, synchronizer1, responseSender) -> { + Optional errorMessage = onQueryResponse(server1, handler1, understood, buf, synchronizer1, responseSender); + errorMessage.ifPresent(s -> handler.disconnect(Text.of("Disconnected by OneForAll\n\nReason: " + s))); + + future.complete(null); + }); + + // SEND + sender.sendPacket(getChannel("hello"), createHelloPacket()); + + synchronizer.waitFor(future); + } + + private static Optional onQueryResponse( + MinecraftServer server, + ServerLoginNetworkHandler handler, + boolean understood, + PacketByteBuf buf, + ServerLoginNetworking.LoginSynchronizer synchronizer, + PacketSender responseSender + ) { + if (!understood && ServerSettings.ENFORCE_PROTOCOL.value()) { + return Optional.of("One-For-All Mod Protocol is required to connect to the server."); + } else if (!understood) { + return Optional.empty(); + } + + // If understood + String version = buf.readString(); + OneForAll.getInstance().getLogger().debug("Client connected with version: {}", version); + + return Optional.empty(); + } +} \ No newline at end of file diff --git a/src/main/java/com/koralix/oneforall/permissions/PermissionSet.java b/src/main/java/com/koralix/oneforall/permissions/PermissionSet.java deleted file mode 100644 index 03fe8d1..0000000 --- a/src/main/java/com/koralix/oneforall/permissions/PermissionSet.java +++ /dev/null @@ -1,173 +0,0 @@ -package com.koralix.oneforall.permissions; - -import org.jetbrains.annotations.NotNull; - -import java.util.*; - -public class PermissionSet implements Set { - private final Map children = new HashMap<>(); - - @Override - public int size() { - int size = children.size(); - for (PermissionSet child : children.values()) { - size += child.size(); - } - return size; - } - - @Override - public boolean isEmpty() { - return children.isEmpty(); - } - - @Override - public boolean contains(Object o) { - if (!(o instanceof String str)) return false; - if (children.containsKey(str) || children.containsKey("*")) return true; - for (PermissionSet child : children.values()) { - if (child.contains(str)) return true; - } - return false; - } - - @NotNull - @Override - public Iterator iterator() { - return new Iterator<>() { - private final Stack>> stack; - private final Stack pathStack; - private String nextPath; - - { - stack = new Stack<>(); - pathStack = new Stack<>(); - stack.push(children.entrySet().iterator()); - advance(); - } - - private void advance() { - nextPath = null; - while (!stack.isEmpty()) { - Iterator> iterator = stack.peek(); - if (!iterator.hasNext()) { - stack.pop(); - if (!pathStack.isEmpty()) { - pathStack.pop(); - } - continue; - } - - Map.Entry entry = iterator.next(); - String key = entry.getKey(); - - if (!pathStack.isEmpty()) { - pathStack.push(pathStack.peek() + "." + key); - } else { - pathStack.push(key); - } - - nextPath = pathStack.peek(); - stack.push(entry.getValue().children.entrySet().iterator()); - return; - } - } - - @Override - public boolean hasNext() { - return nextPath != null; - } - - @Override - public String next() { - if (!hasNext()) throw new NoSuchElementException("No more elements"); - String path = nextPath; - advance(); - return path; - } - }; - } - - @NotNull - @Override - public Object @NotNull [] toArray() { - return children.keySet().toArray(); - } - - @NotNull - @Override - public T @NotNull [] toArray(@NotNull T @NotNull [] a) { - return children.keySet().toArray(a); - } - - @Override - public boolean add(String s) { - if (s.isEmpty()) return false; - String[] parts = s.split("\\.", 2); - if (parts.length == 1) { - return children.putIfAbsent(parts[0], new PermissionSet()) == null; - } else { - PermissionSet child = children.computeIfAbsent(parts[0], k -> new PermissionSet()); - return child.add(parts[1]); - } - } - - @Override - public boolean remove(Object o) { - if (!(o instanceof String str)) return false; - String[] parts = str.split("\\.", 2); - if (parts.length == 1) { - return children.remove(parts[0]) != null; - } else { - PermissionSet child = children.get(parts[0]); - if (child == null) return false; - boolean removed = child.remove(parts[1]); - if (child.isEmpty()) { - children.remove(parts[0]); - } - return removed; - } - } - - @Override - public boolean containsAll(@NotNull Collection c) { - for (Object o : c) { - if (!contains(o)) return false; - } - return true; - } - - @Override - public boolean addAll(@NotNull Collection c) { - boolean modified = false; - for (String s : c) { - modified |= add(s); - } - return modified; - } - - @Override - public boolean retainAll(@NotNull Collection c) { - List toRemove = new ArrayList<>(); - for (String s : this) { - if (!c.contains(s)) { - toRemove.add(s); - } - } - return removeAll(toRemove); - } - - @Override - public boolean removeAll(@NotNull Collection c) { - boolean modified = false; - for (Object o : c) { - modified |= remove(o); - } - return modified; - } - - @Override - public void clear() { - children.clear(); - } -} diff --git a/src/main/java/com/koralix/oneforall/permissions/Permissions.java b/src/main/java/com/koralix/oneforall/permissions/Permissions.java deleted file mode 100644 index 36cf7b7..0000000 --- a/src/main/java/com/koralix/oneforall/permissions/Permissions.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.koralix.oneforall.permissions; - -import net.minecraft.server.command.ServerCommandSource; - -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; -import java.util.function.Predicate; - -public class Permissions { - private static final Map permissions = new HashMap<>(); - - public static Predicate hasPermission(String permission) { - return commandSource -> commandSource.hasPermissionLevel(4) - || (commandSource.isExecutedByPlayer() - && commandSource.getPlayer() != null - && permissions.get(commandSource.getPlayer().getUuid()).contains(permission)); - } -} diff --git a/src/main/java/com/koralix/oneforall/platform/ModMetadata.java b/src/main/java/com/koralix/oneforall/platform/ModMetadata.java new file mode 100644 index 0000000..cdf4d9e --- /dev/null +++ b/src/main/java/com/koralix/oneforall/platform/ModMetadata.java @@ -0,0 +1,9 @@ +package com.koralix.oneforall.platform; + +public record ModMetadata( + String id, + String name, + String version, + String description +) { +} diff --git a/src/main/java/com/koralix/oneforall/platform/Platform.java b/src/main/java/com/koralix/oneforall/platform/Platform.java index 018637c..ef2b0af 100644 --- a/src/main/java/com/koralix/oneforall/platform/Platform.java +++ b/src/main/java/com/koralix/oneforall/platform/Platform.java @@ -1,5 +1,5 @@ package com.koralix.oneforall.platform; public interface Platform { - + ModMetadata getMetadata(String modId); } diff --git a/src/main/java/com/koralix/oneforall/settings/ConfigValidator.java b/src/main/java/com/koralix/oneforall/settings/ConfigValidator.java new file mode 100644 index 0000000..b9c52a1 --- /dev/null +++ b/src/main/java/com/koralix/oneforall/settings/ConfigValidator.java @@ -0,0 +1,25 @@ +package com.koralix.oneforall.settings; + +import net.minecraft.text.Text; +import org.jetbrains.annotations.NotNull; + +@FunctionalInterface +public interface ConfigValidator { + Text test(T value); + + default ConfigValidator and(@NotNull ConfigValidator other) { + return value -> { + Text result = test(value); + return result == null ? other.test(value) : result; + }; + } + + default ConfigValidator or(@NotNull ConfigValidator other) { + return value -> { + Text a = test(value); + if (a == null) return null; + Text b = other.test(value); + return b == null ? null : Text.literal("- ").append(a).append("\n- ").append(b); + }; + } +} diff --git a/src/main/java/com/koralix/oneforall/settings/ConfigValue.java b/src/main/java/com/koralix/oneforall/settings/ConfigValue.java index f1fef2f..99fead5 100644 --- a/src/main/java/com/koralix/oneforall/settings/ConfigValue.java +++ b/src/main/java/com/koralix/oneforall/settings/ConfigValue.java @@ -4,7 +4,8 @@ import com.koralix.oneforall.serde.Serde; import com.koralix.oneforall.serde.Serialize; import net.minecraft.network.PacketByteBuf; -import net.minecraft.util.Identifier; +import net.minecraft.server.command.ServerCommandSource; +import net.minecraft.text.Text; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; @@ -21,54 +22,69 @@ public interface ConfigValue extends Serialize, Deserialize { /** * Get the registry of the config value + * * @return the registry of the config value */ - Identifier registry(); + SettingsRegistry registry(); /** * Get the identifier of the config value + * * @return the identifier of the config value */ - Identifier id(); + SettingEntry entry(); + + /** + * Test if the user satisfies the permission predicate + * + * @return whether the user satisfies the permission predicate + */ + boolean permission(ServerCommandSource source); /** * Reset the config value to the default value + * * @return if the config value was reset successfully */ - default boolean reset() { - return value(defaultValue()); + default void reset() { + value(defaultValue()); } /** * Restore the config value to standard settings + * * @return if the config value was restored successfully */ - default boolean restore() { - return defaultValue(nominalValue()) && reset(); + default void restore() { + defaultValue(nominalValue()); + reset(); } /** * Get the nominal value of the config value + * * @return the nominal value */ T nominalValue(); /** * Get the default value of the config value + * * @return the default value */ T defaultValue(); /** * Set the default value of the config value + * * @param value the new default value * @return if the default value was set successfully */ - - boolean defaultValue(T value); + Text defaultValue(T value); /** * Get the current value of the config value + * * @return the current value */ T value(); @@ -76,13 +92,15 @@ default boolean restore() { /** * Set the current value of the config value + * * @param value the new value * @return if the value was set successfully */ - boolean value(T value); + Text value(T value); /** * Get the class of the config value + * * @return the class of the config value */ Class clazz(); diff --git a/src/main/java/com/koralix/oneforall/settings/ConfigValueBuilder.java b/src/main/java/com/koralix/oneforall/settings/ConfigValueBuilder.java index c4843e6..880cbce 100644 --- a/src/main/java/com/koralix/oneforall/settings/ConfigValueBuilder.java +++ b/src/main/java/com/koralix/oneforall/settings/ConfigValueBuilder.java @@ -1,6 +1,7 @@ package com.koralix.oneforall.settings; -import net.minecraft.util.Identifier; +import net.minecraft.server.command.ServerCommandSource; +import net.minecraft.text.Text; import org.jetbrains.annotations.NotNull; import java.util.function.Predicate; @@ -8,9 +9,8 @@ public class ConfigValueBuilder { private final Class clazz; private final T value; - private Predicate validator = null; - private Identifier registry = null; - private Identifier id = null; + private ConfigValidator validator = null; + private Predicate permission = null; @SuppressWarnings("unchecked") ConfigValueBuilder(@NotNull T value) { @@ -23,7 +23,7 @@ public class ConfigValueBuilder { this.value = null; } - public ConfigValueBuilder test(Predicate validator) { + public ConfigValueBuilder test(ConfigValidator validator) { if (validator == null) return this; if (this.validator == null) { @@ -35,23 +35,26 @@ public ConfigValueBuilder test(Predicate validator) { return this; } - public ConfigValueBuilder registry(@NotNull Identifier registry) { - this.registry = registry; - return this; + public ConfigValueBuilder test(Predicate validator, Text message) { + ConfigValidator test = value -> validator.test(value) ? null : message; + return test(test); + } + + public ConfigValueBuilder test(Predicate validator) { + return test(validator, Text.of("Invalid value")); } - public ConfigValueBuilder id(@NotNull Identifier id) { - this.id = id; + public ConfigValueBuilder permission(Predicate permission) { + this.permission = permission; return this; } public ConfigValue build() { return new ConfigValueWrapper<>( - registry, - id, clazz, value, - validator == null ? t -> true : validator + validator == null ? t -> null : validator, + permission == null ? t -> true : permission ); } } diff --git a/src/main/java/com/koralix/oneforall/settings/ConfigValueWrapper.java b/src/main/java/com/koralix/oneforall/settings/ConfigValueWrapper.java index 45c474a..492e1c5 100644 --- a/src/main/java/com/koralix/oneforall/settings/ConfigValueWrapper.java +++ b/src/main/java/com/koralix/oneforall/settings/ConfigValueWrapper.java @@ -1,7 +1,9 @@ package com.koralix.oneforall.settings; -import net.minecraft.util.Identifier; +import net.minecraft.server.command.ServerCommandSource; +import net.minecraft.text.Text; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.function.Predicate; @@ -15,42 +17,45 @@ public class ConfigValueWrapper implements ConfigValue { private final Class clazz; private final T nominalValue; - private final Predicate validator; + private final ConfigValidator validator; + private final Predicate permission; - Identifier registry; - Identifier id; + SettingEntry entry = null; private T defaultValue; private T value; ConfigValueWrapper( - @NotNull Identifier registry, - @NotNull Identifier id, @NotNull Class clazz, T nominalValue, - @NotNull Predicate validator + @NotNull ConfigValidator validator, + @NotNull Predicate permission ) { - if (!validator.test(nominalValue)) throw new IllegalArgumentException("Invalid nominal value"); + Text err = validator.test(nominalValue); + if (err != null) throw new IllegalArgumentException(err.getString()); this.clazz = clazz; this.nominalValue = nominalValue; this.validator = validator; - - this.registry = registry; - this.id = id; + this.permission = permission; this.defaultValue = nominalValue; this.value = nominalValue; } @Override - public Identifier registry() { - return this.registry; + public SettingsRegistry registry() { + return this.entry.registry(); + } + + @Override + public SettingEntry entry() { + return this.entry; } @Override - public Identifier id() { - return this.id; + public boolean permission(ServerCommandSource source) { + return this.permission.test(source); } @Override @@ -64,10 +69,8 @@ public T defaultValue() { } @Override - public boolean defaultValue(T value) { - if (!this.test(value)) return false; - this.defaultValue = value; - return true; + public Text defaultValue(T value) { + return this.tested(value, () -> this.defaultValue = value); } @Override @@ -76,10 +79,8 @@ public T value() { } @Override - public boolean value(T value) { - if (!this.test(value)) return false; - this.value = value; - return true; + public Text value(T value) { + return this.tested(value, () -> this.value = value); } @Override @@ -87,7 +88,10 @@ public Class clazz() { return this.clazz; } - private boolean test(T value) { - return this.validator.test(value); + private @Nullable Text tested(T value, Runnable action) { + Text err = this.validator.test(value); + if (err != null) return err; + action.run(); + return null; } } diff --git a/src/main/java/com/koralix/oneforall/settings/ServerSettings.java b/src/main/java/com/koralix/oneforall/settings/ServerSettings.java index 7e96002..73ad5b6 100644 --- a/src/main/java/com/koralix/oneforall/settings/ServerSettings.java +++ b/src/main/java/com/koralix/oneforall/settings/ServerSettings.java @@ -2,7 +2,7 @@ import java.util.Objects; -@SettingsRegistry(id = "server_settings") +@SettingsRegistry(id = "server_settings", env = SettingsRegistry.Env.SERVER) public final class ServerSettings { private ServerSettings() { throw new UnsupportedOperationException("This class cannot be instantiated"); @@ -10,5 +10,11 @@ private ServerSettings() { public static final ConfigValue PROTOCOL_ENABLED = ConfigValue.of(true) .test(Objects::nonNull) + .permission(source -> source.hasPermissionLevel(4)) + .build(); + + public static final ConfigValue ENFORCE_PROTOCOL = ConfigValue.of(false) + .test(Objects::nonNull) + .permission(source -> source.hasPermissionLevel(4)) .build(); } diff --git a/src/main/java/com/koralix/oneforall/settings/SettingEntry.java b/src/main/java/com/koralix/oneforall/settings/SettingEntry.java new file mode 100644 index 0000000..1b4dee2 --- /dev/null +++ b/src/main/java/com/koralix/oneforall/settings/SettingEntry.java @@ -0,0 +1,50 @@ +package com.koralix.oneforall.settings; + +import net.minecraft.util.Identifier; +import org.jetbrains.annotations.NotNull; + +public class SettingEntry { + private final SettingsRegistry registry; + private final ConfigValue setting; + private final Identifier settingId; + + public SettingEntry( + @NotNull SettingsRegistry registry, + @NotNull Identifier settingId, + @NotNull ConfigValue setting + ) { + this.registry = registry; + this.setting = setting; + this.settingId = Identifier.of( + SettingsManager.identifier(registry).toTranslationKey(), + settingId.getNamespace().equals(SettingsManager.identifier(registry).getNamespace()) + ? settingId.getPath() + : settingId.toTranslationKey() + ); + } + + public SettingsRegistry registry() { + return registry; + } + + public ConfigValue setting() { + return setting; + } + + public Identifier registryId() { + return SettingsManager.identifier(registry); + } + + public Identifier id() { + return settingId; + } + + public String translation() { + return settingId.toTranslationKey("settings"); + } + + @Override + public String toString() { + return settingId.toString(); + } +} diff --git a/src/main/java/com/koralix/oneforall/settings/SettingsManager.java b/src/main/java/com/koralix/oneforall/settings/SettingsManager.java index d6f0266..fe0c7ab 100644 --- a/src/main/java/com/koralix/oneforall/settings/SettingsManager.java +++ b/src/main/java/com/koralix/oneforall/settings/SettingsManager.java @@ -1,31 +1,45 @@ package com.koralix.oneforall.settings; -import com.koralix.oneforall.OneForAll; import net.minecraft.util.Identifier; import org.jetbrains.annotations.NotNull; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; +import java.util.Set; +import java.util.function.BiConsumer; public final class SettingsManager { - private static final Map> CLASSES = new HashMap<>(); - private static final Map>> SETTINGS = new HashMap<>(); + private static final Map> REGISTRIES = new HashMap<>(); + private static final Map, ConfigValue>> SETTINGS = new HashMap<>(); private SettingsManager() { throw new UnsupportedOperationException("This class cannot be instantiated"); } + public static @NotNull SettingsRegistry registry(@NotNull Class clazz) { + SettingsRegistry registry = clazz.getAnnotation(SettingsRegistry.class); + if (registry == null) throw new IllegalArgumentException("Class is not annotated with @SettingsRegistry"); + return registry; + } + + public static Identifier identifier(@NotNull SettingsRegistry registry) { + return Identifier.of(registry.namespace(), registry.id()); + } + + public static Identifier identifier(@NotNull Class clazz) { + return identifier(registry(clazz)); + } + /** * Register an instance of a class with settings * @param clazz the class to register * @param the type of the class */ public static void register(@NotNull Class clazz) { - SettingsRegistry registry = clazz.getAnnotation(SettingsRegistry.class); - if (registry == null) throw new IllegalArgumentException("Class is not annotated with @SettingsRegistry"); - - Identifier registryId = Identifier.of(OneForAll.MOD_ID, registry.id()); - CLASSES.put(registryId, clazz); + SettingsRegistry registry = registry(clazz); + Identifier registryId = identifier(registry); + REGISTRIES.computeIfAbsent(registry.env(), k -> new HashSet<>()).add(registryId); for (var field : clazz.getDeclaredFields()) { if (!ConfigValue.class.isAssignableFrom(field.getType())) continue; @@ -33,38 +47,52 @@ public static void register(@NotNull Class clazz) { try { @SuppressWarnings("unchecked") ConfigValue value = (ConfigValue) field.get(null); - register(registryId, Identifier.of(OneForAll.MOD_ID, field.getName().toLowerCase()), value); + SettingEntry entry = new SettingEntry<>(registry, registryId.withPath(field.getName().toLowerCase()), value); + if (value instanceof ConfigValueWrapper wrapper) { + if (wrapper.entry == null) wrapper.entry = entry; + entry = wrapper.entry; + } + register(entry); } catch (IllegalAccessException e) { throw new IllegalArgumentException("Field '" + field.getName() + "' is not static"); } } } - private static void register(Identifier registryId, Identifier id, ConfigValue value) { - if (value instanceof ConfigValueWrapper wrapper) { - if (wrapper.registry == null) wrapper.registry = registryId; - if (wrapper.id == null) wrapper.id = id; - } - if (SETTINGS.computeIfAbsent(registryId, k -> new HashMap<>()).putIfAbsent(id, value) == null) return; - throw new IllegalArgumentException("Setting with id " + id + " is already registered"); + private static void register(@NotNull SettingEntry entry) { + if (SETTINGS.computeIfAbsent(entry.registryId(), k -> new HashMap<>()).putIfAbsent(entry, entry.setting()) == null) return; + throw new IllegalArgumentException("Setting with id " + entry + " is already registered"); } /** - * Get a setting by its registry and id + * Get a setting by its registry and entry * @param registry the registry of the setting - * @param id the id of the setting + * @param entry the entry of the setting * @return the setting */ - public static ConfigValue get(Identifier registry, Identifier id) { - return SETTINGS.getOrDefault(registry, Map.of()).get(id); + @SuppressWarnings("unchecked") + public static ConfigValue get(Identifier registry, SettingEntry entry) { + return (ConfigValue) SETTINGS.getOrDefault(registry, Map.of()).get(entry); + } + + /** + * Iterate over all settings + */ + public static void forEach(BiConsumer, ConfigValue> action) { + SETTINGS.forEach((registry, settings) -> settings.forEach(action)); + } + + /** + * Iterate over all settings in a registry + */ + public static void forEach(Identifier registry, BiConsumer, ConfigValue> action) { + SETTINGS.getOrDefault(registry, Map.of()).forEach(action); } /** - * Get a registry by its identifier - * @param registry the identifier of the registry - * @return the registry + * Iterate over all settings of a specific environment */ - public static Class registry(Identifier registry) { - return CLASSES.get(registry); + public static void forEach(SettingsRegistry.Env env, BiConsumer, ConfigValue> action) { + REGISTRIES.getOrDefault(env, Set.of()).forEach(registry -> forEach(registry, action)); } } diff --git a/src/main/java/com/koralix/oneforall/settings/SettingsRegistry.java b/src/main/java/com/koralix/oneforall/settings/SettingsRegistry.java index 838ba96..987fafe 100644 --- a/src/main/java/com/koralix/oneforall/settings/SettingsRegistry.java +++ b/src/main/java/com/koralix/oneforall/settings/SettingsRegistry.java @@ -1,5 +1,7 @@ package com.koralix.oneforall.settings; +import com.koralix.oneforall.OneForAll; + import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -14,4 +16,25 @@ @Target(ElementType.TYPE) public @interface SettingsRegistry { String id(); + String namespace() default OneForAll.MOD_ID; + Env env() default Env.SERVER; + + enum Env { + /** + * The settings registry is for the client + */ + CLIENT, + /** + * The settings registry is for any server + */ + SERVER, + /** + * The settings registry is for an integrated server + */ + INTEGRATED, + /** + * The settings registry is for a dedicated server + */ + DEDICATED + } } diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index e991e2c..44d8d38 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -26,6 +26,7 @@ "environment": "client" } ], + "accessWidener": "${id}.accesswidener", "depends": { "fabricloader": ">=0.15.0", "minecraft": "${minecraft_dependency}" diff --git a/src/main/resources/oneforall.accesswidener b/src/main/resources/oneforall.accesswidener new file mode 100644 index 0000000..9e91673 --- /dev/null +++ b/src/main/resources/oneforall.accesswidener @@ -0,0 +1,3 @@ +accessWidener v2 named + +accessible method net/minecraft/command/argument/EnumArgumentType (Lcom/mojang/serialization/Codec;Ljava/util/function/Supplier;)V \ No newline at end of file diff --git a/src/main/resources/oneforall.mixins.json b/src/main/resources/oneforall.mixins.json index e270712..72f6cea 100644 --- a/src/main/resources/oneforall.mixins.json +++ b/src/main/resources/oneforall.mixins.json @@ -4,6 +4,7 @@ "package": "com.koralix.oneforall.mixin", "compatibilityLevel": "JAVA_21", "mixins": [ + "fapi.FixServerLoginNetworkAddonRegisterReceiver" ], "injectors": { "defaultRequire": 1 diff --git a/versions/1.20.1-fabric/src/main/java/com/koralix/oneforall/Initializer.java b/versions/1.20.1-fabric/src/main/java/com/koralix/oneforall/Initializer.java index 8b06aac..f143738 100644 --- a/versions/1.20.1-fabric/src/main/java/com/koralix/oneforall/Initializer.java +++ b/versions/1.20.1-fabric/src/main/java/com/koralix/oneforall/Initializer.java @@ -25,6 +25,6 @@ private static OneForAll createInstance() { @Override public void onInitialize() { - instance.onInitialize(); + instance.initialize(); } } diff --git a/versions/1.20.1-fabric/src/main/java/com/koralix/oneforall/platform/FabricPlatform.java b/versions/1.20.1-fabric/src/main/java/com/koralix/oneforall/platform/FabricPlatform.java index 10c5b8c..bd12076 100644 --- a/versions/1.20.1-fabric/src/main/java/com/koralix/oneforall/platform/FabricPlatform.java +++ b/versions/1.20.1-fabric/src/main/java/com/koralix/oneforall/platform/FabricPlatform.java @@ -1,5 +1,17 @@ package com.koralix.oneforall.platform; -public class FabricPlatform implements Platform { +import net.fabricmc.loader.api.FabricLoader; +public class FabricPlatform implements Platform { + @Override + public ModMetadata getMetadata(String modId) { + return FabricLoader.getInstance().getModContainer(modId) + .map(container -> new ModMetadata( + container.getMetadata().getId(), + container.getMetadata().getName(), + container.getMetadata().getVersion().getFriendlyString(), + container.getMetadata().getDescription() + )) + .orElse(null); + } }