From a835c7de2d38ce6e7f8f7139aefa0907b5f6597c Mon Sep 17 00:00:00 2001 From: Sonic2423 Date: Fri, 7 Jun 2024 03:21:10 +0200 Subject: [PATCH] First version --- gradle.properties | 12 +- .../com/sonic2423/neoforwarding/Config.java | 47 ++++++++ .../neoforwarding/NeoForwarding.java | 24 ++++ .../neoforwarding/PlayerDataForwarding.java | 98 ++++++++++++++++ .../mixin/ISetAddressInConnection.java | 7 ++ .../mixin/mixins/CrossStitchSupport.java | 56 +++++++++ .../ServerLoginPacketListenerImplMixin.java | 107 ++++++++++++++++++ ...rverboundCustomQueryAnswerPacketMixin.java | 38 +++++++ .../mixin/mixins/SetAddressInConnection.java | 19 ++++ src/main/resources/META-INF/mods.toml | 100 ++++++++++++++++ src/main/resources/neoforwarding.mixins.json | 15 +++ 11 files changed, 517 insertions(+), 6 deletions(-) create mode 100644 src/main/java/com/sonic2423/neoforwarding/Config.java create mode 100644 src/main/java/com/sonic2423/neoforwarding/NeoForwarding.java create mode 100644 src/main/java/com/sonic2423/neoforwarding/PlayerDataForwarding.java create mode 100644 src/main/java/com/sonic2423/neoforwarding/mixin/ISetAddressInConnection.java create mode 100644 src/main/java/com/sonic2423/neoforwarding/mixin/mixins/CrossStitchSupport.java create mode 100644 src/main/java/com/sonic2423/neoforwarding/mixin/mixins/ServerLoginPacketListenerImplMixin.java create mode 100644 src/main/java/com/sonic2423/neoforwarding/mixin/mixins/ServerboundCustomQueryAnswerPacketMixin.java create mode 100644 src/main/java/com/sonic2423/neoforwarding/mixin/mixins/SetAddressInConnection.java create mode 100644 src/main/resources/META-INF/mods.toml create mode 100644 src/main/resources/neoforwarding.mixins.json diff --git a/gradle.properties b/gradle.properties index 9ed8c2e..402645c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -26,18 +26,18 @@ loader_version_range=[2,) # The unique mod identifier for the mod. Must be lowercase in English locale. Must fit the regex [a-z][a-z0-9_]{1,63} # Must match the String constant located in the main mod class annotated with @Mod. -mod_id=examplemod +mod_id=neoforwarding # The human-readable display name for the mod. -mod_name=Example Mod +mod_name=NeoForwarding # The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default. -mod_license=All Rights Reserved +mod_license=GNU GPLv3 # The mod version. See https://semver.org/ mod_version=1.0.0 # The group ID for the mod. It is only important when publishing as an artifact to a Maven repository. # This should match the base package used for the mod sources. # See https://maven.apache.org/guides/mini/guide-naming-conventions.html -mod_group_id=com.example.examplemod +mod_group_id=com.sonic2423.neoforwarding # The authors of the mod. This is a simple text string that is used for display purposes in the mod list. -mod_authors=YourNameHere, OtherNameHere +mod_authors=Sonic2423 # The description of the mod. This is a simple multiline text string that is used for display purposes in the mod list. -mod_description=Example mod description.\nNewline characters can be used and will be replaced properly. +mod_description=Enables modern player information forwarding in NeoForge servers for use with Velocity. diff --git a/src/main/java/com/sonic2423/neoforwarding/Config.java b/src/main/java/com/sonic2423/neoforwarding/Config.java new file mode 100644 index 0000000..f19e0b7 --- /dev/null +++ b/src/main/java/com/sonic2423/neoforwarding/Config.java @@ -0,0 +1,47 @@ +package com.sonic2423.neoforwarding; + +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.fml.common.Mod; +import net.neoforged.fml.event.config.ModConfigEvent; +import net.neoforged.neoforge.common.ModConfigSpec; + +import static com.sonic2423.neoforwarding.NeoForwarding.LOGGER; + +@Mod.EventBusSubscriber(modid = NeoForwarding.MODID, bus = Mod.EventBusSubscriber.Bus.MOD) +public class Config { + public static boolean enableForwarding; + public static String forwardingSecret; + + private static final ModConfigSpec.Builder BUILDER = new ModConfigSpec.Builder(); + + public static final ModConfigSpec.ConfigValue FORWARDING_SECRET = BUILDER + .comment("Use the 'forwarding.secret' from Velocity (and not the default value of '') and insert it here") + .define("forwardingSecret", ""); + + private static final ModConfigSpec.BooleanValue ENABLE_FORWARDING = BUILDER + .comment("This must be enabled after you inserted your forwarding secret for the server to accept and send forwarding requests.\nIf disabled the server will act as if the mod is not installed.") + .define("enableForwarding", false); + + static final ModConfigSpec SPEC = BUILDER.build(); + + @SubscribeEvent + static void onLoad(final ModConfigEvent event) { + enableForwarding = ENABLE_FORWARDING.get(); + forwardingSecret = FORWARDING_SECRET.get(); + + if (enableForwarding) { + if (Config.forwardingSecret.isEmpty()) { + LOGGER.warn("Please specify a forwarding secret."); + LOGGER.warn("NeoForwarding will be disabled!"); + enableForwarding = false; + } else if (Config.forwardingSecret.length() != 12) { + LOGGER.error("Malformed modern forwarding secret."); + LOGGER.error("It is very likely that no one can log in!"); + } else { + LOGGER.info("Modern forwarding enabled."); + } + } else { + LOGGER.info("Modern forwarding disabled."); + } + } +} diff --git a/src/main/java/com/sonic2423/neoforwarding/NeoForwarding.java b/src/main/java/com/sonic2423/neoforwarding/NeoForwarding.java new file mode 100644 index 0000000..1afe499 --- /dev/null +++ b/src/main/java/com/sonic2423/neoforwarding/NeoForwarding.java @@ -0,0 +1,24 @@ +package com.sonic2423.neoforwarding; + +import com.mojang.logging.LogUtils; +import net.neoforged.bus.api.IEventBus; +import net.neoforged.fml.ModLoadingContext; +import net.neoforged.fml.common.Mod; +import net.neoforged.fml.config.ModConfig; +import org.slf4j.Logger; + +// The value here should match an entry in the META-INF/mods.toml file +@Mod(NeoForwarding.MODID) +public class NeoForwarding { + // Define mod id in a common place for everything to reference + public static final String MODID = "neoforwarding"; + // Directly reference a slf4j logger + public static final Logger LOGGER = LogUtils.getLogger(); + + // The constructor for the mod class is the first code that is run when your mod is loaded. + // FML will recognize some parameter types like IEventBus or ModContainer and pass them in automatically. + public NeoForwarding(IEventBus modEventBus) { + // Register our mod's ModConfigSpec so that FML can create and load the config file for us + ModLoadingContext.get().registerConfig(ModConfig.Type.SERVER, Config.SPEC); + } +} diff --git a/src/main/java/com/sonic2423/neoforwarding/PlayerDataForwarding.java b/src/main/java/com/sonic2423/neoforwarding/PlayerDataForwarding.java new file mode 100644 index 0000000..a456300 --- /dev/null +++ b/src/main/java/com/sonic2423/neoforwarding/PlayerDataForwarding.java @@ -0,0 +1,98 @@ +package com.sonic2423.neoforwarding; + +import com.google.common.net.InetAddresses; +import com.mojang.authlib.GameProfile; +import com.mojang.authlib.properties.Property; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.protocol.login.custom.CustomQueryAnswerPayload; +import net.minecraft.network.protocol.login.custom.CustomQueryPayload; +import net.minecraft.resources.ResourceLocation; +import org.jetbrains.annotations.NotNull; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; +import java.net.InetAddress; +import java.security.InvalidKeyException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +import static com.sonic2423.neoforwarding.Config.forwardingSecret; + +/* + * The following is ported from "Paper" with slight modifications to work with NeoForge as Mixin. + * See: https://github.com/PaperMC/Paper/blob/bd5867a96f792f0eb32c1d249bb4bbc1d8338d14/patches/server/0748-Add-Velocity-IP-Forwarding-Support.patch + * PlayerDataForwarding: Lines 51-107. + */ +public class PlayerDataForwarding { + private static final int SUPPORTED_FORWARDING_VERSION = 1; + public static final int MODERN_FORWARDING_WITH_KEY = 2; + public static final int MODERN_FORWARDING_WITH_KEY_V2 = 3; + public static final int MODERN_LAZY_SESSION = 4; + public static final byte MAX_SUPPORTED_FORWARDING_VERSION = MODERN_LAZY_SESSION; + public static final ResourceLocation PLAYER_INFO_CHANNEL = new ResourceLocation("velocity", "player_info"); + + public static boolean checkIntegrity(final FriendlyByteBuf buf) { + + final byte[] signature = new byte[32]; + buf.readBytes(signature); + + final byte[] data = new byte[buf.readableBytes()]; + buf.getBytes(buf.readerIndex(), data); + + try { + final Mac mac = Mac.getInstance("HmacSHA256"); + mac.init(new SecretKeySpec(forwardingSecret.getBytes(java.nio.charset.StandardCharsets.UTF_8), "HmacSHA256")); + final byte[] mySignature = mac.doFinal(data); + if (!MessageDigest.isEqual(signature, mySignature)) { + return false; + } + } catch (final InvalidKeyException | NoSuchAlgorithmException e) { + throw new AssertionError(e); + } + + return true; + } + + public static InetAddress readAddress(final FriendlyByteBuf buf) { + return InetAddresses.forString(buf.readUtf(Short.MAX_VALUE)); + } + + public static GameProfile createProfile(final FriendlyByteBuf buf) { + final GameProfile profile = new GameProfile(buf.readUUID(), buf.readUtf(16)); + readProperties(buf, profile); + return profile; + } + + private static void readProperties(final FriendlyByteBuf buf, final GameProfile profile) { + final int properties = buf.readVarInt(); + for (int i1 = 0; i1 < properties; i1++) { + final String name = buf.readUtf(Short.MAX_VALUE); + final String value = buf.readUtf(Short.MAX_VALUE); + final String signature = buf.readBoolean() ? buf.readUtf(Short.MAX_VALUE) : null; + profile.getProperties().put(name, new Property(name, value, signature)); + } + } + + public record VelocityPlayerInfoPayload(FriendlyByteBuf buffer) implements CustomQueryPayload { + + public static final ResourceLocation id = PLAYER_INFO_CHANNEL; + + @Override + public void write(final FriendlyByteBuf buf) { + buf.writeBytes(this.buffer); + } + + @Override + public @NotNull ResourceLocation id() { + return id; + } + } + + public record QueryAnswerPayload(FriendlyByteBuf buffer) implements CustomQueryAnswerPayload { + + @Override + public void write(final FriendlyByteBuf buf) { + buf.writeBytes(this.buffer); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/sonic2423/neoforwarding/mixin/ISetAddressInConnection.java b/src/main/java/com/sonic2423/neoforwarding/mixin/ISetAddressInConnection.java new file mode 100644 index 0000000..57d5609 --- /dev/null +++ b/src/main/java/com/sonic2423/neoforwarding/mixin/ISetAddressInConnection.java @@ -0,0 +1,7 @@ +package com.sonic2423.neoforwarding.mixin; + +import java.net.SocketAddress; + +public interface ISetAddressInConnection { + void neoproxy$setAddress(SocketAddress address); +} diff --git a/src/main/java/com/sonic2423/neoforwarding/mixin/mixins/CrossStitchSupport.java b/src/main/java/com/sonic2423/neoforwarding/mixin/mixins/CrossStitchSupport.java new file mode 100644 index 0000000..ed53f02 --- /dev/null +++ b/src/main/java/com/sonic2423/neoforwarding/mixin/mixins/CrossStitchSupport.java @@ -0,0 +1,56 @@ +package com.sonic2423.neoforwarding.mixin.mixins; + +import com.mojang.brigadier.arguments.ArgumentType; +import io.netty.buffer.Unpooled; +import net.minecraft.commands.synchronization.ArgumentTypeInfo; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.resources.ResourceLocation; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import static com.sonic2423.neoforwarding.NeoForwarding.LOGGER; + +/* + * The following is ported from "CrossStitch" with slight modifications to work with NeoForge. + * See: https://github.com/VelocityPowered/CrossStitch/blob/f8d6be1128cb049e5c5a93068b9069e0838a2200/src/main/java/com/velocitypowered/crossstitch/mixin/command/CommandTreeSerializationMixin.java + */ +@Mixin(targets = "net.minecraft.network.protocol.game.ClientboundCommandsPacket$ArgumentNodeStub") +public abstract class CrossStitchSupport { + + @Unique + private static final int MOD_ARGUMENT_INDICATOR = -256; + + @Inject(method = "serializeCap(Lnet/minecraft/network/FriendlyByteBuf;Lnet/minecraft/commands/synchronization/ArgumentTypeInfo;Lnet/minecraft/commands/synchronization/ArgumentTypeInfo$Template;)V", at = @At("HEAD"), cancellable = true) + private static , T extends ArgumentTypeInfo.Template> void writeNode$wrapInVelocityModArgument(FriendlyByteBuf pBuffer, ArgumentTypeInfo pArgumentInfo, ArgumentTypeInfo.Template pArgumentInfoTemplate, CallbackInfo ci) { + ResourceLocation key = BuiltInRegistries.COMMAND_ARGUMENT_TYPE.getKey(pArgumentInfo); + int id = BuiltInRegistries.COMMAND_ARGUMENT_TYPE.getId(pArgumentInfo); + + if (key == null || key.getNamespace().equals("brigadier") || (key.getNamespace().equals("minecraft") && !key.getPath().equals("test_argument") && !key.getPath().equals("test_class"))) { + return; + } + + LOGGER.debug("Mod argument type: {}: {}:{}", id, key.getNamespace(), key.getPath()); + + ci.cancel(); + + // Not a standard Minecraft argument type - so we need to wrap it + neoproxy$serializeWrappedArgumentType(pBuffer, pArgumentInfo, pArgumentInfoTemplate); + } + + @Unique + private static , T extends ArgumentTypeInfo.Template> void neoproxy$serializeWrappedArgumentType(FriendlyByteBuf pBuffer, ArgumentTypeInfo pArgumentInfo, ArgumentTypeInfo.Template pArgumentInfoTemplate) { + pBuffer.writeVarInt(MOD_ARGUMENT_INDICATOR); + pBuffer.writeVarInt(BuiltInRegistries.COMMAND_ARGUMENT_TYPE.getId(pArgumentInfo)); + + FriendlyByteBuf extraData = new FriendlyByteBuf(Unpooled.buffer()); + pArgumentInfo.serializeToNetwork((T) pArgumentInfoTemplate, extraData); + + pBuffer.writeVarInt(extraData.readableBytes()); + pBuffer.writeBytes(extraData); + } +} + diff --git a/src/main/java/com/sonic2423/neoforwarding/mixin/mixins/ServerLoginPacketListenerImplMixin.java b/src/main/java/com/sonic2423/neoforwarding/mixin/mixins/ServerLoginPacketListenerImplMixin.java new file mode 100644 index 0000000..1a1e8c7 --- /dev/null +++ b/src/main/java/com/sonic2423/neoforwarding/mixin/mixins/ServerLoginPacketListenerImplMixin.java @@ -0,0 +1,107 @@ +package com.sonic2423.neoforwarding.mixin.mixins; + +import com.mojang.authlib.GameProfile; +import com.sonic2423.neoforwarding.Config; +import com.sonic2423.neoforwarding.PlayerDataForwarding; +import com.sonic2423.neoforwarding.mixin.ISetAddressInConnection; +import net.minecraft.network.Connection; +import net.minecraft.network.TickablePacketListener; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.login.ServerLoginPacketListener; +import net.minecraft.network.protocol.login.ServerboundCustomQueryAnswerPacket; +import net.minecraft.network.protocol.login.ServerboundHelloPacket; +import net.minecraft.server.network.ServerLoginPacketListenerImpl; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import javax.annotation.Nullable; + +import static com.sonic2423.neoforwarding.NeoForwarding.LOGGER; + +/* + * The following is ported from "Paper" with slight modifications to work with NeoForge as Mixin. + * See: https://github.com/PaperMC/Paper/blob/bd5867a96f792f0eb32c1d249bb4bbc1d8338d14/patches/server/0748-Add-Velocity-IP-Forwarding-Support.patch + * handleHello: Lines 152-161. + * onHandleCustomQueryPacket: Lines 182-226. + */ +@Mixin(ServerLoginPacketListenerImpl.class) +public abstract class ServerLoginPacketListenerImplMixin implements ServerLoginPacketListener, TickablePacketListener { + + @Unique + private int neoforwarding$velocityLoginMessageId = -1; + @Final + @Shadow + private Connection connection; + @Shadow + @Nullable + private GameProfile authenticatedProfile; + + @Shadow + private void disconnect(Component pReason) { + } + + @Shadow + private void startClientVerification(GameProfile authenticatedProfile) { + } + + // using specific injection target because it is very unlikely that something else will inject in this function. + @Inject(method = "handleHello", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/network/ServerLoginPacketListenerImpl;startClientVerification(Lcom/mojang/authlib/GameProfile;)V", ordinal = 1), cancellable = true) + public void handleHello(ServerboundHelloPacket pPacket, CallbackInfo ci) { + if (Config.enableForwarding) { + this.neoforwarding$velocityLoginMessageId = java.util.concurrent.ThreadLocalRandom.current().nextInt(); + net.minecraft.network.FriendlyByteBuf buf = new net.minecraft.network.FriendlyByteBuf(io.netty.buffer.Unpooled.buffer()); + buf.writeByte(PlayerDataForwarding.MAX_SUPPORTED_FORWARDING_VERSION); + net.minecraft.network.protocol.login.ClientboundCustomQueryPacket packet1 = + new net.minecraft.network.protocol.login.ClientboundCustomQueryPacket( + this.neoforwarding$velocityLoginMessageId, new PlayerDataForwarding.VelocityPlayerInfoPayload(buf) + ); + this.connection.send(packet1); + ci.cancel(); + } + } + + @Inject(method = "handleCustomQueryPacket", at = @At("HEAD"), cancellable = true) + private void onHandleCustomQueryPacket(ServerboundCustomQueryAnswerPacket packet, CallbackInfo ci) { + if (Config.enableForwarding && packet.transactionId() == this.neoforwarding$velocityLoginMessageId) { + + if (packet.payload() == null) { + this.disconnect(Component.literal("This server requires you to connect with Velocity.")); + return; + } + + PlayerDataForwarding.QueryAnswerPayload payload = (PlayerDataForwarding.QueryAnswerPayload) packet.payload(); + + net.minecraft.network.FriendlyByteBuf buf = payload.buffer(); + + if (!PlayerDataForwarding.checkIntegrity(buf)) { + this.disconnect(Component.literal("Unable to verify player details")); + return; + } + + int version = buf.readVarInt(); + if (version > PlayerDataForwarding.MAX_SUPPORTED_FORWARDING_VERSION) { + throw new IllegalStateException("Unsupported forwarding version " + version + ", wanted upto " + PlayerDataForwarding.MAX_SUPPORTED_FORWARDING_VERSION); + } + + java.net.SocketAddress listening = this.connection.getRemoteAddress(); + int port = 0; + if (listening instanceof java.net.InetSocketAddress) { + port = ((java.net.InetSocketAddress) listening).getPort(); + } + + ISetAddressInConnection setAddressMixin = (ISetAddressInConnection) this.connection; + setAddressMixin.neoproxy$setAddress(new java.net.InetSocketAddress(PlayerDataForwarding.readAddress(buf), port)); + + startClientVerification(PlayerDataForwarding.createProfile(buf)); + + LOGGER.info("UUID of player {} is {}", this.authenticatedProfile.getName(), this.authenticatedProfile.getId()); + + ci.cancel(); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/sonic2423/neoforwarding/mixin/mixins/ServerboundCustomQueryAnswerPacketMixin.java b/src/main/java/com/sonic2423/neoforwarding/mixin/mixins/ServerboundCustomQueryAnswerPacketMixin.java new file mode 100644 index 0000000..4b1a861 --- /dev/null +++ b/src/main/java/com/sonic2423/neoforwarding/mixin/mixins/ServerboundCustomQueryAnswerPacketMixin.java @@ -0,0 +1,38 @@ +package com.sonic2423.neoforwarding.mixin.mixins; + +import com.sonic2423.neoforwarding.PlayerDataForwarding; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.protocol.login.ServerboundCustomQueryAnswerPacket; +import net.minecraft.network.protocol.login.custom.CustomQueryAnswerPayload; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +/* + * The following is ported from "Paper" with slight modifications to work with NeoForge as Mixin. + * See: https://github.com/PaperMC/Paper/blob/bd5867a96f792f0eb32c1d249bb4bbc1d8338d14/patches/server/0009-MC-Utils.patch + * Lines 6040-6050. + */ +@Mixin(ServerboundCustomQueryAnswerPacket.class) +public abstract class ServerboundCustomQueryAnswerPacketMixin { + + @Shadow @Final private static int MAX_PAYLOAD_SIZE; + + @Inject(method = "readUnknownPayload", at = @At("HEAD"), cancellable = true) + private static void onReadPayload(FriendlyByteBuf pBuffer, CallbackInfoReturnable cir) { + FriendlyByteBuf buffer = pBuffer.readNullable((buf2) -> { + int i = buf2.readableBytes(); + if (i >= 0 && i <= MAX_PAYLOAD_SIZE) { + return new FriendlyByteBuf(buf2.readBytes(i)); + } else { + throw new IllegalArgumentException("Payload may not be larger than " + MAX_PAYLOAD_SIZE + " bytes"); + } + }); + + cir.setReturnValue(buffer == null ? null : new PlayerDataForwarding.QueryAnswerPayload(buffer)); + cir.cancel(); + } +} diff --git a/src/main/java/com/sonic2423/neoforwarding/mixin/mixins/SetAddressInConnection.java b/src/main/java/com/sonic2423/neoforwarding/mixin/mixins/SetAddressInConnection.java new file mode 100644 index 0000000..0bf4632 --- /dev/null +++ b/src/main/java/com/sonic2423/neoforwarding/mixin/mixins/SetAddressInConnection.java @@ -0,0 +1,19 @@ +package com.sonic2423.neoforwarding.mixin.mixins; + +import com.sonic2423.neoforwarding.mixin.ISetAddressInConnection; +import net.minecraft.network.Connection; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +import java.net.SocketAddress; + +@Mixin(Connection.class) +public abstract class SetAddressInConnection implements ISetAddressInConnection { + @Shadow + private SocketAddress address; + + @Override + public void neoproxy$setAddress(SocketAddress address) { + this.address = address; + } +} diff --git a/src/main/resources/META-INF/mods.toml b/src/main/resources/META-INF/mods.toml new file mode 100644 index 0000000..741797f --- /dev/null +++ b/src/main/resources/META-INF/mods.toml @@ -0,0 +1,100 @@ +# This is an example mods.toml file. It contains the data relating to the loading mods. +# There are several mandatory fields (#mandatory), and many more that are optional (#optional). +# The overall format is standard TOML format, v0.5.0. +# Note that there are a couple of TOML lists in this file. +# Find more information on toml format here: https://github.com/toml-lang/toml +# The name of the mod loader type to load - for regular FML @Mod mods it should be javafml +modLoader = "javafml" #mandatory + +# A version range to match for said mod loader - for regular FML @Mod it will be the the FML version. This is currently 47. +loaderVersion = "${loader_version_range}" #mandatory + +# The license for you mod. This is mandatory metadata and allows for easier comprehension of your redistributive properties. +# Review your options at https://choosealicense.com/. All rights reserved is the default copyright stance, and is thus the default here. +license = "${mod_license}" + +# A URL to refer people to when problems occur with this mod +#issueTrackerURL="https://change.me.to.your.issue.tracker.example.invalid/" #optional + +# A list of mods - how many allowed here is determined by the individual mod loader +[[mods]] #mandatory + +# The modid of the mod +modId = "${mod_id}" #mandatory + +# The version number of the mod +version = "${mod_version}" #mandatory + +# A display name for the mod +displayName = "${mod_name}" #mandatory + +# A URL to query for updates for this mod. See the JSON update specification https://docs.neoforged.net/docs/misc/updatechecker/ +#updateJSONURL="https://change.me.example.invalid/updates.json" #optional + +# A URL for the "homepage" for this mod, displayed in the mod UI +#displayURL="https://change.me.to.your.mods.homepage.example.invalid/" #optional + +# A file name (in the root of the mod JAR) containing a logo for display +#logoFile="examplemod.png" #optional + +# A text field displayed in the mod UI +#credits="" #optional + +# A text field displayed in the mod UI +authors = "${mod_authors}" #optional + +# Display Test controls the display for your mod in the server connection screen +# MATCH_VERSION means that your mod will cause a red X if the versions on client and server differ. This is the default behaviour and should be what you choose if you have server and client elements to your mod. +# IGNORE_SERVER_VERSION means that your mod will not cause a red X if it's present on the server but not on the client. This is what you should use if you're a server only mod. +# IGNORE_ALL_VERSION means that your mod will not cause a red X if it's present on the client or the server. This is a special case and should only be used if your mod has no server component. +# NONE means that no display test is set on your mod. You need to do this yourself, see IExtensionPoint.DisplayTest for more information. You can define any scheme you wish with this value. +# IMPORTANT NOTE: this is NOT an instruction as to which environments (CLIENT or DEDICATED SERVER) your mod loads on. Your mod should load (and maybe do nothing!) whereever it finds itself. +#displayTest="MATCH_VERSION" # MATCH_VERSION is the default if nothing is specified (#optional) + +# The description text for the mod (multi line!) (#mandatory) +description = '''${mod_description}''' + +# The [[mixins]] block allows you to declare your mixin config to FML so that it gets loaded. +[[mixins]] +config = "${mod_id}.mixins.json" + +# The [[accessTransformers]] block allows you to declare where your AT file is. +# If this block is omitted, a fallback attempt will be made to load an AT from META-INF/accesstransformer.cfg +#[[accessTransformers]] +#file="META-INF/accesstransformer.cfg" + +# The coremods config file path is not configurable and is always loaded from META-INF/coremods.json + +# A dependency - use the . to indicate dependency for a specific modid. Dependencies are optional. +[[dependencies.${ mod_id }]] #optional +# the modid of the dependency +modId = "neoforge" #mandatory +# The type of the dependency. Can be one of "required", "optional", "incompatible" or "discouraged" (case insensitive). +# 'required' requires the mod to exist, 'optional' does not +# 'incompatible' will prevent the game from loading when the mod exists, and 'discouraged' will show a warning +type = "required" #mandatory +# Optional field describing why the dependency is required or why it is incompatible +# reason="..." +# The version range of the dependency +versionRange = "${neo_version_range}" #mandatory +# An ordering relationship for the dependency. +# BEFORE - This mod is loaded BEFORE the dependency +# AFTER - This mod is loaded AFTER the dependency +ordering = "NONE" +# Side this dependency is applied on - BOTH, CLIENT, or SERVER +side = "SERVER" + +# Here's another dependency +[[dependencies.${ mod_id }]] +modId = "minecraft" +type = "required" +# This version range declares a minimum of the current minecraft version up to but not including the next major version +versionRange = "${minecraft_version_range}" +ordering = "NONE" +side = "BOTH" + +# Features are specific properties of the game environment, that you may want to declare you require. This example declares +# that your mod requires GL version 3.2 or higher. Other features will be added. They are side aware so declaring this won't +# stop your mod loading on the server for example. +#[features.${mod_id}] +#openGLVersion="[3.2,)" diff --git a/src/main/resources/neoforwarding.mixins.json b/src/main/resources/neoforwarding.mixins.json new file mode 100644 index 0000000..0468fd5 --- /dev/null +++ b/src/main/resources/neoforwarding.mixins.json @@ -0,0 +1,15 @@ +{ + "required": true, + "minVersion": "0.8", + "package": "com.sonic2423.neoforwarding.mixin.mixins", + "compatibilityLevel": "JAVA_17", + "injectors": { + "defaultRequire": 1 + }, + "server": [ + "SetAddressInConnection", + "CrossStitchSupport", + "ServerboundCustomQueryAnswerPacketMixin", + "ServerLoginPacketListenerImplMixin" + ] +} \ No newline at end of file