├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── src ├── main │ ├── resources │ │ ├── badpackets.png │ │ └── badpackets.mixins.json │ └── java │ │ └── lol │ │ └── bai │ │ └── badpackets │ │ ├── api │ │ ├── PacketReadyCallback.java │ │ ├── PacketReceiver.java │ │ ├── play │ │ │ ├── ClientPlayContext.java │ │ │ ├── ServerPlayContext.java │ │ │ └── PlayPackets.java │ │ ├── config │ │ │ ├── ServerConfigContext.java │ │ │ ├── ConfigTaskExecutor.java │ │ │ ├── ClientConfigContext.java │ │ │ └── ConfigPackets.java │ │ └── PacketSender.java │ │ └── impl │ │ ├── handler │ │ ├── PacketHandlerHolder.java │ │ ├── ClientConfigPacketHandler.java │ │ ├── ClientPlayPacketHandler.java │ │ ├── ServerPlayPacketHandler.java │ │ ├── ServerConfigPacketHandler.java │ │ └── AbstractPacketHandler.java │ │ ├── mixin │ │ ├── client │ │ │ ├── AccessClientCommonPacketListenerImpl.java │ │ │ ├── MixinClientCommonPacketListenerImpl.java │ │ │ ├── MixinClientPacketListener.java │ │ │ └── MixinClientConfigurationPacketListenerImpl.java │ │ ├── MixinServerCommonPacketListenerImpl.java │ │ ├── MixinPlayerList.java │ │ ├── MixinServerGamePacketListenerImpl.java │ │ ├── MixinConnection.java │ │ ├── MixinServerboundCustomPayloadPacket.java │ │ ├── MixinCustomPacketPayload_1.java │ │ ├── MixinClientboundCustomPayloadPacket.java │ │ └── MixinServerConfigurationPacketListenerImpl.java │ │ ├── marker │ │ └── ApiSide.java │ │ ├── Constants.java │ │ ├── registry │ │ ├── ChannelCodecFinder.java │ │ ├── CallbackRegistry.java │ │ └── ChannelRegistry.java │ │ ├── payload │ │ └── UntypedPayload.java │ │ └── platform │ │ └── PlatformProxy.java └── testmod │ └── java │ └── lol │ └── bai │ └── badpackets │ └── test │ ├── TestPlayPayload.java │ ├── TestConfigPayload.java │ ├── TestTaskPayload.java │ └── BadPacketTest.java ├── platform ├── neo │ ├── src │ │ ├── main │ │ │ ├── resources │ │ │ │ ├── pack.mcmeta │ │ │ │ └── META-INF │ │ │ │ │ └── neoforge.mods.toml │ │ │ └── java │ │ │ │ └── lol │ │ │ │ └── bai │ │ │ │ └── badpackets │ │ │ │ └── impl │ │ │ │ └── neo │ │ │ │ └── NeoBadPackets.java │ │ └── testmod │ │ │ └── java │ │ │ └── lol │ │ │ └── badpackets │ │ │ └── test │ │ │ └── neo │ │ │ ├── NeoBadPacketTest.java │ │ │ └── ForgeBadPacketTestClient.java │ ├── gradle.properties │ └── build.gradle.kts ├── forge │ ├── src │ │ ├── main │ │ │ ├── resources │ │ │ │ ├── pack.mcmeta │ │ │ │ └── META-INF │ │ │ │ │ └── mods.toml │ │ │ └── java │ │ │ │ └── lol │ │ │ │ └── bai │ │ │ │ └── badpackets │ │ │ │ └── impl │ │ │ │ └── forge │ │ │ │ └── ForgeBadPackets.java │ │ └── testmod │ │ │ ├── resources │ │ │ ├── pack.mcmeta │ │ │ └── META-INF │ │ │ │ └── mods.toml │ │ │ └── java │ │ │ └── lol │ │ │ └── bai │ │ │ └── badpackets │ │ │ └── test │ │ │ └── forge │ │ │ ├── ForgeBadPacketTestClient.java │ │ │ └── ForgeBadPacketTest.java │ ├── gradle.properties │ └── build.gradle.kts ├── fabric │ ├── gradle.properties │ ├── src │ │ ├── testmod │ │ │ ├── resources │ │ │ │ └── fabric.mod.json │ │ │ └── java │ │ │ │ └── lol │ │ │ │ └── bai │ │ │ │ └── badpackets │ │ │ │ └── test │ │ │ │ └── fabric │ │ │ │ ├── FabricBadPacketTestClient.java │ │ │ │ └── FabricBadPacketTest.java │ │ └── main │ │ │ └── resources │ │ │ └── fabric.mod.json │ └── build.gradle.kts └── mojmap │ └── build.gradle.kts ├── .gitignore ├── gradle.properties ├── settings.gradle.kts ├── .github └── workflows │ ├── build_head.yml │ └── build_release.yml ├── README.md ├── gradlew.bat ├── gradlew ├── LICENSE └── .editorconfig /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/badasintended/badpackets/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /src/main/resources/badpackets.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/badasintended/badpackets/HEAD/src/main/resources/badpackets.png -------------------------------------------------------------------------------- /platform/neo/src/main/resources/pack.mcmeta: -------------------------------------------------------------------------------- 1 | { 2 | "pack": { 3 | "description": "badpackets", 4 | "pack_format": 8 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /platform/forge/src/main/resources/pack.mcmeta: -------------------------------------------------------------------------------- 1 | { 2 | "pack": { 3 | "description": "badpackets", 4 | "pack_format": 8 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /platform/forge/src/testmod/resources/pack.mcmeta: -------------------------------------------------------------------------------- 1 | { 2 | "pack": { 3 | "description": "badpackets", 4 | "pack_format": 8 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/main/java/lol/bai/badpackets/api/PacketReadyCallback.java: -------------------------------------------------------------------------------- 1 | package lol.bai.badpackets.api; 2 | 3 | @FunctionalInterface 4 | public interface PacketReadyCallback { 5 | 6 | void onReady(C context); 7 | 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/lol/bai/badpackets/api/PacketReceiver.java: -------------------------------------------------------------------------------- 1 | package lol.bai.badpackets.api; 2 | 3 | @FunctionalInterface 4 | public interface PacketReceiver { 5 | 6 | void receive(C context, P payload); 7 | 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/lol/bai/badpackets/impl/handler/PacketHandlerHolder.java: -------------------------------------------------------------------------------- 1 | package lol.bai.badpackets.impl.handler; 2 | 3 | public interface PacketHandlerHolder> { 4 | 5 | T badpackets_handler(); 6 | 7 | } 8 | -------------------------------------------------------------------------------- /platform/neo/src/main/java/lol/bai/badpackets/impl/neo/NeoBadPackets.java: -------------------------------------------------------------------------------- 1 | package lol.bai.badpackets.impl.neo; 2 | 3 | import lol.bai.badpackets.impl.Constants; 4 | import net.neoforged.fml.common.Mod; 5 | 6 | @Mod(Constants.MOD_ID) 7 | public class NeoBadPackets { 8 | 9 | } 10 | -------------------------------------------------------------------------------- /platform/forge/src/main/java/lol/bai/badpackets/impl/forge/ForgeBadPackets.java: -------------------------------------------------------------------------------- 1 | package lol.bai.badpackets.impl.forge; 2 | 3 | import lol.bai.badpackets.impl.Constants; 4 | import net.minecraftforge.fml.common.Mod; 5 | 6 | @Mod(Constants.MOD_ID) 7 | public class ForgeBadPackets { 8 | 9 | } 10 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Sep 14 12:28:28 PDT 2015 2 | distributionBase = GRADLE_USER_HOME 3 | distributionPath = wrapper/dists 4 | zipStoreBase = GRADLE_USER_HOME 5 | zipStorePath = wrapper/dists 6 | distributionUrl = https\://services.gradle.org/distributions/gradle-8.14-bin.zip 7 | -------------------------------------------------------------------------------- /platform/forge/gradle.properties: -------------------------------------------------------------------------------- 1 | # CurseForge 2 | cf.endpoint = minecraft.curseforge.com 3 | cf.prefix = [Forge] 4 | cf.releaseType = beta 5 | cf.loader = Forge 6 | cf.gameVersion = 1.21.11 7 | 8 | # Modrinth 9 | mr.releaseType = stable 10 | mr.loader = forge 11 | mr.gameVersion = 1.21.11 12 | -------------------------------------------------------------------------------- /platform/neo/gradle.properties: -------------------------------------------------------------------------------- 1 | # CurseForge 2 | cf.endpoint = minecraft.curseforge.com 3 | cf.prefix = [NeoForge] 4 | cf.releaseType = beta 5 | cf.loader = NeoForge 6 | cf.gameVersion = 1.21.11 7 | 8 | # Modrinth 9 | mr.releaseType = stable 10 | mr.loader = neoforge 11 | mr.gameVersion = 1.21.11 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .architectury-transformer/ 2 | .vscode/ 3 | .gradle/ 4 | build/ 5 | eclipse/ 6 | run/ 7 | runs/ 8 | out/ 9 | asm/ 10 | bin/ 11 | .idea/ 12 | .metadata/ 13 | .settings/ 14 | libs/ 15 | logs/ 16 | 17 | *.psd 18 | *.iml 19 | *.ipr 20 | *.iws 21 | *.classpath 22 | *.project 23 | *.class 24 | 25 | Thumbs.db 26 | 27 | .b2/ 28 | -------------------------------------------------------------------------------- /platform/fabric/gradle.properties: -------------------------------------------------------------------------------- 1 | # CurseForge 2 | cf.endpoint = minecraft.curseforge.com 3 | cf.prefix = [Fabric/Quilt] 4 | cf.releaseType = stable 5 | cf.loader = Fabric, Quilt 6 | cf.gameVersion = 1.21.11 7 | 8 | # Modrinth 9 | mr.releaseType = stable 10 | mr.loader = fabric, quilt 11 | mr.gameVersion = 1.21.11 12 | -------------------------------------------------------------------------------- /platform/fabric/src/testmod/resources/fabric.mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 1, 3 | "id" : "badpackets_testmod", 4 | "version" : "1", 5 | 6 | "entrypoints" : { 7 | "main" : ["lol.bai.badpackets.test.fabric.FabricBadPacketTest"], 8 | "client": ["lol.bai.badpackets.test.fabric.FabricBadPacketTestClient"] 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /platform/mojmap/build.gradle.kts: -------------------------------------------------------------------------------- 1 | tasks.jar { 2 | from(rootProject.sourceSets["main"].output) 3 | } 4 | 5 | tasks.sourcesJar { 6 | from(rootProject.sourceSets["main"].allSource) 7 | archiveClassifier.set("sources") 8 | } 9 | 10 | afterEvaluate { 11 | upload { 12 | maven(tasks.jar.get(), tasks.sourcesJar.get()) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /platform/forge/src/testmod/resources/META-INF/mods.toml: -------------------------------------------------------------------------------- 1 | modLoader = "javafml" 2 | loaderVersion = "[38,)" 3 | 4 | license = "Apache-2.0" 5 | issueTrackerURL="https://github.com/badasintended/badpackets/issues" 6 | showAsResourcePack=false 7 | 8 | [[mods]] 9 | modId = "badpackets_testmod" 10 | version = "1" 11 | displayName = "Bad Packets Testmod" 12 | description = "Bad Packets Testmod" 13 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs = -Xmx3G 2 | 3 | minecraft = 1.21.11 4 | 5 | archiveBaseName = badpackets 6 | group = lol.bai 7 | majorVersion = 0 8 | 9 | fabricLoader = 0.17.3 10 | fabricApi = 0.139.4+1.21.11 11 | forge = 61.0.2 12 | neo = 21.11.0-beta 13 | 14 | cf.projectId = 615134 15 | mr.projectId = ftdbN0KK 16 | -------------------------------------------------------------------------------- /src/main/java/lol/bai/badpackets/impl/mixin/client/AccessClientCommonPacketListenerImpl.java: -------------------------------------------------------------------------------- 1 | package lol.bai.badpackets.impl.mixin.client; 2 | 3 | import net.minecraft.client.multiplayer.ClientCommonPacketListenerImpl; 4 | import net.minecraft.network.Connection; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.gen.Accessor; 7 | 8 | @Mixin(ClientCommonPacketListenerImpl.class) 9 | public interface AccessClientCommonPacketListenerImpl { 10 | 11 | @Accessor("connection") 12 | Connection badpackets_connection(); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/lol/bai/badpackets/api/play/ClientPlayContext.java: -------------------------------------------------------------------------------- 1 | package lol.bai.badpackets.api.play; 2 | 3 | import lol.bai.badpackets.api.PacketSender; 4 | import lol.bai.badpackets.impl.marker.ApiSide; 5 | import net.minecraft.client.Minecraft; 6 | import net.minecraft.client.multiplayer.ClientPacketListener; 7 | import org.jetbrains.annotations.ApiStatus; 8 | 9 | @ApiSide.ClientOnly 10 | @ApiStatus.NonExtendable 11 | public interface ClientPlayContext extends PacketSender { 12 | 13 | Minecraft client(); 14 | 15 | ClientPacketListener handler(); 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/lol/bai/badpackets/api/play/ServerPlayContext.java: -------------------------------------------------------------------------------- 1 | package lol.bai.badpackets.api.play; 2 | 3 | import lol.bai.badpackets.api.PacketSender; 4 | import net.minecraft.server.MinecraftServer; 5 | import net.minecraft.server.level.ServerPlayer; 6 | import net.minecraft.server.network.ServerGamePacketListenerImpl; 7 | import org.jetbrains.annotations.ApiStatus; 8 | 9 | @ApiStatus.NonExtendable 10 | public interface ServerPlayContext extends PacketSender { 11 | 12 | MinecraftServer server(); 13 | 14 | ServerPlayer player(); 15 | 16 | ServerGamePacketListenerImpl handler(); 17 | 18 | } 19 | -------------------------------------------------------------------------------- /platform/fabric/src/testmod/java/lol/bai/badpackets/test/fabric/FabricBadPacketTestClient.java: -------------------------------------------------------------------------------- 1 | package lol.bai.badpackets.test.fabric; 2 | 3 | import lol.bai.badpackets.test.BadPacketTest; 4 | import net.fabricmc.api.ClientModInitializer; 5 | 6 | public class FabricBadPacketTestClient implements ClientModInitializer { 7 | 8 | @Override 9 | public void onInitializeClient() { 10 | BadPacketTest.client(); 11 | 12 | // TODO 13 | // ClientConfigurationNetworking.registerGlobalReceiver(FabricBadPacketTest.FABRIC_TEST, (client, handler, buf, responseSender) -> {}); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /platform/neo/src/testmod/java/lol/badpackets/test/neo/NeoBadPacketTest.java: -------------------------------------------------------------------------------- 1 | package lol.badpackets.test.neo; 2 | 3 | import lol.bai.badpackets.impl.Constants; 4 | import lol.bai.badpackets.test.BadPacketTest; 5 | import net.neoforged.bus.api.SubscribeEvent; 6 | import net.neoforged.fml.common.EventBusSubscriber; 7 | import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent; 8 | 9 | @EventBusSubscriber(modid = Constants.MOD_ID) 10 | public class NeoBadPacketTest { 11 | 12 | @SubscribeEvent 13 | static void setup(FMLCommonSetupEvent event) { 14 | BadPacketTest.common(); 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/lol/bai/badpackets/impl/marker/ApiSide.java: -------------------------------------------------------------------------------- 1 | package lol.bai.badpackets.impl.marker; 2 | 3 | import java.lang.annotation.Documented; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | 7 | public class ApiSide { 8 | 9 | /** 10 | * Anything that has this annotation means that it function only available on the logical client. 11 | *

12 | * Marker only annotation, doesn't actually restrict access on runtime. 13 | */ 14 | @Documented 15 | @Retention(RetentionPolicy.CLASS) 16 | public @interface ClientOnly { 17 | 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /platform/forge/src/main/resources/META-INF/mods.toml: -------------------------------------------------------------------------------- 1 | modLoader = "javafml" 2 | loaderVersion = "[38,)" 3 | 4 | license = "Apache-2.0" 5 | issueTrackerURL = "https://github.com/badasintended/badpackets/issues" 6 | showAsResourcePack = false 7 | 8 | [[mods]] 9 | modId = "badpackets" 10 | version = "${version}" 11 | displayName = "Bad Packets" 12 | description = "Allow packet messaging between multiple modding platform." 13 | authors = "deirn" 14 | logoFile = "badpackets.png" 15 | 16 | [[dependencies.badpackets]] 17 | modId = "minecraft" 18 | mandatory = true 19 | versionRange = "[1.21.11,)" 20 | ordering = "NONE" 21 | side = "BOTH" 22 | -------------------------------------------------------------------------------- /src/main/java/lol/bai/badpackets/api/config/ServerConfigContext.java: -------------------------------------------------------------------------------- 1 | package lol.bai.badpackets.api.config; 2 | 3 | import lol.bai.badpackets.api.PacketSender; 4 | import net.minecraft.resources.Identifier; 5 | import net.minecraft.server.MinecraftServer; 6 | import net.minecraft.server.network.ServerConfigurationPacketListenerImpl; 7 | import org.jetbrains.annotations.ApiStatus; 8 | 9 | @ApiStatus.NonExtendable 10 | public interface ServerConfigContext extends PacketSender { 11 | 12 | MinecraftServer server(); 13 | 14 | ServerConfigurationPacketListenerImpl handler(); 15 | 16 | void finishTask(Identifier taskId); 17 | 18 | } 19 | -------------------------------------------------------------------------------- /platform/fabric/src/main/resources/fabric.mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 1, 3 | "id" : "badpackets", 4 | "version" : "${version}", 5 | 6 | "name" : "Bad Packets", 7 | "description" : "Allow packet messaging between multiple modding platform.", 8 | "icon" : "badpackets.png", 9 | "authors" : ["deirn"], 10 | "license" : "Apache-2.0", 11 | "mixins" : ["badpackets.mixins.json"], 12 | "depends" : { 13 | "minecraft" : ">=1.21.11-", 14 | "fabricloader": ">=0.17.3" 15 | }, 16 | "custom" : { 17 | "modmenu": { 18 | "badges": ["library"] 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /platform/neo/src/main/resources/META-INF/neoforge.mods.toml: -------------------------------------------------------------------------------- 1 | modLoader = "javafml" 2 | loaderVersion = "[1,)" 3 | 4 | license = "Apache-2.0" 5 | issueTrackerURL = "https://github.com/badasintended/badpackets/issues" 6 | 7 | [[mods]] 8 | modId = "badpackets" 9 | version = "${version}" 10 | displayName = "Bad Packets" 11 | description = "Allow packet messaging between multiple modding platform." 12 | authors = "deirn" 13 | logoFile = "badpackets.png" 14 | 15 | [[mixins]] 16 | config = "badpackets.mixins.json" 17 | 18 | [[dependencies.badpackets]] 19 | modId = "minecraft" 20 | type = "required" 21 | versionRange = "[1.21.11,)" 22 | ordering = "NONE" 23 | side = "BOTH" 24 | -------------------------------------------------------------------------------- /platform/neo/src/testmod/java/lol/badpackets/test/neo/ForgeBadPacketTestClient.java: -------------------------------------------------------------------------------- 1 | package lol.badpackets.test.neo; 2 | 3 | import lol.bai.badpackets.impl.Constants; 4 | import lol.bai.badpackets.test.BadPacketTest; 5 | import net.neoforged.api.distmarker.Dist; 6 | import net.neoforged.bus.api.SubscribeEvent; 7 | import net.neoforged.fml.common.EventBusSubscriber; 8 | import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent; 9 | 10 | @EventBusSubscriber(modid = Constants.MOD_ID, value = Dist.CLIENT) 11 | public class ForgeBadPacketTestClient { 12 | 13 | @SubscribeEvent 14 | static void setup(FMLClientSetupEvent event) { 15 | BadPacketTest.client(); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /platform/forge/src/testmod/java/lol/bai/badpackets/test/forge/ForgeBadPacketTestClient.java: -------------------------------------------------------------------------------- 1 | package lol.bai.badpackets.test.forge; 2 | 3 | import lol.bai.badpackets.test.BadPacketTest; 4 | import net.minecraftforge.api.distmarker.Dist; 5 | import net.minecraftforge.eventbus.api.listener.SubscribeEvent; 6 | import net.minecraftforge.fml.common.Mod.EventBusSubscriber; 7 | import net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus; 8 | import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; 9 | 10 | @EventBusSubscriber(modid = ForgeBadPacketTest.MOD_ID, bus = Bus.MOD, value = Dist.CLIENT) 11 | public class ForgeBadPacketTestClient { 12 | 13 | @SubscribeEvent 14 | static void setup(FMLClientSetupEvent event) { 15 | BadPacketTest.client(); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/lol/bai/badpackets/api/config/ConfigTaskExecutor.java: -------------------------------------------------------------------------------- 1 | package lol.bai.badpackets.api.config; 2 | 3 | import net.minecraft.resources.Identifier; 4 | 5 | @FunctionalInterface 6 | public interface ConfigTaskExecutor { 7 | 8 | /** 9 | * Return whether the task is run or not. 10 | *

11 | * Make use of {@link ServerConfigContext#canSend(Identifier)} to check whether the client can process the task, 12 | * and return {@code false} otherwise. 13 | *

14 | * Once the client received the task packet, it should send a response packet to the server. 15 | * After the server received the response packet, it should call {@link ServerConfigContext#finishTask(Identifier)} 16 | * to allow the client to join the server. 17 | */ 18 | boolean runTask(ServerConfigContext context); 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/lol/bai/badpackets/impl/Constants.java: -------------------------------------------------------------------------------- 1 | package lol.bai.badpackets.impl; 2 | 3 | import net.minecraft.resources.Identifier; 4 | 5 | public class Constants { 6 | 7 | public static final String MOD_ID = "badpackets"; 8 | 9 | public static final Identifier CHANNEL_SYNC = id("channel_sync"); 10 | public static final Identifier MC_REGISTER_CHANNEL = Identifier.parse("minecraft:register"); 11 | public static final Identifier MC_UNREGISTER_CHANNEL = Identifier.parse("minecraft:unregister"); 12 | 13 | public static final int PING_PONG = 0xBAD4C7_01; 14 | 15 | public static final byte CHANNEL_SYNC_SINGLE = 0; 16 | public static final byte CHANNEL_SYNC_INITIAL = 1; 17 | 18 | public static Identifier id(String path) { 19 | return Identifier.fromNamespaceAndPath(MOD_ID, path); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/lol/bai/badpackets/api/config/ClientConfigContext.java: -------------------------------------------------------------------------------- 1 | package lol.bai.badpackets.api.config; 2 | 3 | import lol.bai.badpackets.api.PacketSender; 4 | import lol.bai.badpackets.impl.marker.ApiSide; 5 | import net.minecraft.client.Minecraft; 6 | import net.minecraft.client.multiplayer.ClientConfigurationPacketListenerImpl; 7 | import net.minecraft.network.chat.Component; 8 | import org.jetbrains.annotations.ApiStatus; 9 | 10 | @ApiSide.ClientOnly 11 | @ApiStatus.NonExtendable 12 | public interface ClientConfigContext extends PacketSender { 13 | 14 | Minecraft client(); 15 | 16 | ClientConfigurationPacketListenerImpl handler(); 17 | 18 | /** 19 | * Helper method to disconnect client-to-server connection. 20 | * 21 | * @param reason the disconnection reason 22 | */ 23 | void disconnect(Component reason); 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/lol/bai/badpackets/impl/registry/ChannelCodecFinder.java: -------------------------------------------------------------------------------- 1 | package lol.bai.badpackets.impl.registry; 2 | 3 | import net.minecraft.network.FriendlyByteBuf; 4 | import net.minecraft.network.codec.StreamCodec; 5 | import net.minecraft.network.protocol.common.custom.CustomPacketPayload; 6 | import net.minecraft.resources.Identifier; 7 | import org.jetbrains.annotations.Nullable; 8 | 9 | @FunctionalInterface 10 | public interface ChannelCodecFinder { 11 | 12 | static void attach(StreamCodec codec, ChannelCodecFinder finder) { 13 | ((Holder) codec).badpackets_setChannelCodecFinder(finder); 14 | } 15 | 16 | @Nullable 17 | StreamCodec getCodec(Identifier id, FriendlyByteBuf buf); 18 | 19 | interface Holder { 20 | 21 | void badpackets_setChannelCodecFinder(ChannelCodecFinder finder); 22 | 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/resources/badpackets.mixins.json: -------------------------------------------------------------------------------- 1 | { 2 | "required" : true, 3 | "minVersion" : "0.8", 4 | "package" : "lol.bai.badpackets.impl.mixin", 5 | "compatibilityLevel": "JAVA_16", 6 | "mixins" : [ 7 | "MixinClientboundCustomPayloadPacket", 8 | "MixinConnection", 9 | "MixinCustomPacketPayload_1", 10 | "MixinPlayerList", 11 | "MixinServerboundCustomPayloadPacket", 12 | "MixinServerCommonPacketListenerImpl", 13 | "MixinServerConfigurationPacketListenerImpl", 14 | "MixinServerGamePacketListenerImpl" 15 | ], 16 | "client" : [ 17 | "client.AccessClientCommonPacketListenerImpl", 18 | "client.MixinClientCommonPacketListenerImpl", 19 | "client.MixinClientConfigurationPacketListenerImpl", 20 | "client.MixinClientPacketListener" 21 | ], 22 | "injectors" : { 23 | "defaultRequire": 1 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /platform/forge/src/testmod/java/lol/bai/badpackets/test/forge/ForgeBadPacketTest.java: -------------------------------------------------------------------------------- 1 | package lol.bai.badpackets.test.forge; 2 | 3 | import lol.bai.badpackets.impl.Constants; 4 | import lol.bai.badpackets.test.BadPacketTest; 5 | import net.minecraftforge.eventbus.api.listener.SubscribeEvent; 6 | import net.minecraftforge.fml.common.Mod; 7 | import net.minecraftforge.fml.common.Mod.EventBusSubscriber; 8 | import net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus; 9 | import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; 10 | 11 | @Mod(ForgeBadPacketTest.MOD_ID) 12 | @EventBusSubscriber(modid = ForgeBadPacketTest.MOD_ID, bus = Bus.MOD) 13 | public class ForgeBadPacketTest { 14 | 15 | public static final String MOD_ID = Constants.MOD_ID + "_testmod"; 16 | 17 | @SubscribeEvent 18 | static void setup(FMLCommonSetupEvent event) { 19 | BadPacketTest.common(); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /platform/fabric/src/testmod/java/lol/bai/badpackets/test/fabric/FabricBadPacketTest.java: -------------------------------------------------------------------------------- 1 | package lol.bai.badpackets.test.fabric; 2 | 3 | import lol.bai.badpackets.impl.Constants; 4 | import lol.bai.badpackets.test.BadPacketTest; 5 | import net.fabricmc.api.ModInitializer; 6 | import net.minecraft.resources.Identifier; 7 | 8 | public class FabricBadPacketTest implements ModInitializer { 9 | 10 | public static final Identifier FABRIC_TEST = Constants.id("test/fabric"); 11 | 12 | @Override 13 | public void onInitialize() { 14 | BadPacketTest.common(); 15 | 16 | // TODO 17 | // ServerConfigurationConnectionEvents.CONFIGURE.register((handler, server) -> { 18 | // if (!ServerConfigurationNetworking.canSend(handler, FABRIC_TEST)) { 19 | // handler.disconnect(Component.literal("fabric test channel not found")); 20 | // } 21 | // }); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/lol/bai/badpackets/impl/mixin/MixinServerCommonPacketListenerImpl.java: -------------------------------------------------------------------------------- 1 | package lol.bai.badpackets.impl.mixin; 2 | 3 | import net.minecraft.network.protocol.common.ServerboundPongPacket; 4 | import net.minecraft.server.network.ServerCommonPacketListenerImpl; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.Unique; 7 | import org.spongepowered.asm.mixin.injection.At; 8 | import org.spongepowered.asm.mixin.injection.Inject; 9 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 10 | 11 | @Mixin(ServerCommonPacketListenerImpl.class) 12 | public class MixinServerCommonPacketListenerImpl { 13 | 14 | @Inject(method = "handlePong", at = @At("HEAD")) 15 | private void badpackets_onPong(ServerboundPongPacket packet, CallbackInfo ci) { 16 | badpackets_onPong(packet.getId()); 17 | } 18 | 19 | @Unique 20 | protected void badpackets_onPong(int id) { 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/lol/bai/badpackets/impl/registry/CallbackRegistry.java: -------------------------------------------------------------------------------- 1 | package lol.bai.badpackets.impl.registry; 2 | 3 | import java.util.ArrayList; 4 | 5 | import lol.bai.badpackets.api.PacketReadyCallback; 6 | import lol.bai.badpackets.api.config.ClientConfigContext; 7 | import lol.bai.badpackets.api.config.ServerConfigContext; 8 | import lol.bai.badpackets.api.play.ClientPlayContext; 9 | import lol.bai.badpackets.api.play.ServerPlayContext; 10 | 11 | public class CallbackRegistry { 12 | 13 | public static final ArrayList> CLIENT_READY_CONFIG = new ArrayList<>(); 14 | public static final ArrayList> SERVER_READY_CONFIG = new ArrayList<>(); 15 | 16 | public static final ArrayList> CLIENT_PLAY = new ArrayList<>(); 17 | public static final ArrayList> SERVER_PLAY = new ArrayList<>(); 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/lol/bai/badpackets/impl/mixin/MixinPlayerList.java: -------------------------------------------------------------------------------- 1 | package lol.bai.badpackets.impl.mixin; 2 | 3 | import lol.bai.badpackets.impl.handler.ServerPlayPacketHandler; 4 | import net.minecraft.network.Connection; 5 | import net.minecraft.server.level.ServerPlayer; 6 | import net.minecraft.server.network.CommonListenerCookie; 7 | import net.minecraft.server.players.PlayerList; 8 | import org.spongepowered.asm.mixin.Mixin; 9 | import org.spongepowered.asm.mixin.injection.At; 10 | import org.spongepowered.asm.mixin.injection.Inject; 11 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 12 | 13 | @Mixin(PlayerList.class) 14 | public class MixinPlayerList { 15 | 16 | @Inject(method = "placeNewPlayer", at = @At("TAIL")) 17 | private void badpackets_initServerPlayPacketHandler(Connection netManager, ServerPlayer player, CommonListenerCookie cookie, CallbackInfo ci) { 18 | ServerPlayPacketHandler.get(player).sendInitialChannelSyncPacket(); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /platform/fabric/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("fabric-loom") version "1.13.4" 3 | } 4 | 5 | setupPlatform() 6 | 7 | dependencies { 8 | minecraft("com.mojang:minecraft:${rootProp["minecraft"]}") 9 | mappings(loom.officialMojangMappings()) 10 | 11 | modImplementation("net.fabricmc:fabric-loader:${rootProp["fabricLoader"]}") 12 | modImplementation("net.fabricmc.fabric-api:fabric-api:${rootProp["fabricApi"]}") 13 | } 14 | 15 | loom { 16 | runs.forEach { 17 | it.isIdeConfigGenerated = true 18 | } 19 | } 20 | 21 | tasks.processResources { 22 | inputs.property("version", project.version) 23 | 24 | filesMatching("fabric.mod.json") { 25 | expand("version" to project.version) 26 | } 27 | } 28 | 29 | afterEvaluate { 30 | val remapJar = tasks.remapJar.get() 31 | val remapSourcesJar = tasks.remapSourcesJar.get() 32 | 33 | upload { 34 | curseforge(remapJar) 35 | modrinth(remapJar) 36 | maven(remapJar, remapSourcesJar) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | mavenCentral() 4 | gradlePluginPortal() 5 | maven("https://maven.fabricmc.net") 6 | maven("https://maven.minecraftforge.net") 7 | maven("https://maven.neoforged.net/releases") 8 | maven("https://maven.quiltmc.org/repository/release") 9 | maven("https://repo.spongepowered.org/repository/maven-public") 10 | } 11 | 12 | resolutionStrategy.eachPlugin { 13 | when (requested.id.id) { 14 | "org.spongepowered.mixin" -> useModule("org.spongepowered:mixingradle:${requested.version}") 15 | } 16 | } 17 | } 18 | 19 | plugins { 20 | id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0" 21 | } 22 | 23 | rootProject.name = "badpackets" 24 | 25 | fun platform(name: String) { 26 | include(name) 27 | project(":${name}").projectDir = file("platform/${name}") 28 | } 29 | 30 | platform("mojmap") 31 | 32 | platform("fabric") 33 | platform("forge") 34 | platform("neo") 35 | -------------------------------------------------------------------------------- /src/main/java/lol/bai/badpackets/impl/mixin/client/MixinClientCommonPacketListenerImpl.java: -------------------------------------------------------------------------------- 1 | package lol.bai.badpackets.impl.mixin.client; 2 | 3 | import net.minecraft.client.multiplayer.ClientCommonPacketListenerImpl; 4 | import net.minecraft.network.protocol.common.ClientboundPingPacket; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.Unique; 7 | import org.spongepowered.asm.mixin.injection.At; 8 | import org.spongepowered.asm.mixin.injection.Inject; 9 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 10 | 11 | @Mixin(ClientCommonPacketListenerImpl.class) 12 | public abstract class MixinClientCommonPacketListenerImpl { 13 | 14 | @Inject(method = "handlePing", at = @At("HEAD"), cancellable = true) 15 | private void badpackets_handlePing(ClientboundPingPacket packet, CallbackInfo ci) { 16 | if (badpackets_handlePing(packet.getId())) ci.cancel(); 17 | } 18 | 19 | @Unique 20 | protected boolean badpackets_handlePing(int id) { 21 | return false; 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/testmod/java/lol/bai/badpackets/test/TestPlayPayload.java: -------------------------------------------------------------------------------- 1 | package lol.bai.badpackets.test; 2 | 3 | import lol.bai.badpackets.impl.Constants; 4 | import net.minecraft.network.FriendlyByteBuf; 5 | import net.minecraft.network.codec.StreamCodec; 6 | import net.minecraft.network.protocol.common.custom.CustomPacketPayload; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | public record TestPlayPayload( 10 | String msg 11 | ) implements CustomPacketPayload { 12 | 13 | public static final Type TYPE = new Type<>(Constants.id("test/play/payload")); 14 | public static final StreamCodec CODEC = CustomPacketPayload.codec(TestPlayPayload::write, TestPlayPayload::new); 15 | 16 | public TestPlayPayload(FriendlyByteBuf buf) { 17 | this(buf.readUtf()); 18 | } 19 | 20 | public void write(@NotNull FriendlyByteBuf buf) { 21 | buf.writeUtf(msg); 22 | } 23 | 24 | @Override 25 | public @NotNull Type type() { 26 | return TYPE; 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /.github/workflows/build_head.yml: -------------------------------------------------------------------------------- 1 | name: head 2 | on: 3 | push: 4 | branches: 5 | - '**' 6 | tags-ignore: 7 | - '*.*' 8 | pull_request: 9 | branches: 10 | - '**' 11 | tags-ignore: 12 | - '*.*' 13 | jobs: 14 | build: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - id: vars 18 | run: | 19 | echo "git_hash=${GITHUB_SHA:0:7}" >> $GITHUB_OUTPUT 20 | - uses: actions/checkout@v4 21 | - uses: actions/setup-java@v4 22 | with: 23 | java-version: 21 24 | distribution: temurin 25 | - run: chmod +x gradlew 26 | - uses: actions/cache@v4 27 | with: 28 | path: ~/.gradle/caches 29 | key: ${{ runner.os }}-${{ github.ref }}-${{ hashFiles('**/*.gradle.kts') }} 30 | - run: ./gradlew build 31 | env: 32 | GIT_HASH: ${{ steps.vars.outputs.git_hash }} 33 | - uses: actions/upload-artifact@v4 34 | with: 35 | name: badpackets-${{ steps.vars.outputs.git_hash }} 36 | path: "./platform/*/build/libs/**.jar" 37 | -------------------------------------------------------------------------------- /src/testmod/java/lol/bai/badpackets/test/TestConfigPayload.java: -------------------------------------------------------------------------------- 1 | package lol.bai.badpackets.test; 2 | 3 | import lol.bai.badpackets.impl.Constants; 4 | import net.minecraft.network.FriendlyByteBuf; 5 | import net.minecraft.network.codec.StreamCodec; 6 | import net.minecraft.network.protocol.common.custom.CustomPacketPayload; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | public record TestConfigPayload( 10 | String msg 11 | ) implements CustomPacketPayload { 12 | 13 | public static final Type TYPE = new Type<>(Constants.id("test/config/payload")); 14 | public static final StreamCodec CODEC = CustomPacketPayload.codec(TestConfigPayload::write, TestConfigPayload::new); 15 | 16 | public TestConfigPayload(FriendlyByteBuf buf) { 17 | this(buf.readUtf()); 18 | } 19 | 20 | public void write(@NotNull FriendlyByteBuf buf) { 21 | buf.writeUtf(msg); 22 | } 23 | 24 | @Override 25 | public @NotNull Type type() { 26 | return TYPE; 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /platform/neo/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | idea 3 | id("net.neoforged.moddev") version "2.0.110" 4 | } 5 | 6 | setupPlatform() 7 | 8 | neoForge { 9 | version = rootProp["neo"] 10 | 11 | runs { 12 | create("server") { server() } 13 | create("client") { 14 | client() 15 | programArguments.addAll("--username", "A") 16 | } 17 | 18 | configureEach { 19 | gameDirectory = file("run/${namer.determineName(this)}") 20 | } 21 | 22 | mods { 23 | create("badpackets") { 24 | sourceSet(sourceSets["main"]) 25 | sourceSet(sourceSets["testmod"]) 26 | } 27 | } 28 | } 29 | } 30 | 31 | tasks.processResources { 32 | inputs.property("version", project.version) 33 | 34 | filesMatching("META-INF/neoforge.mods.toml") { 35 | expand("version" to project.version) 36 | } 37 | } 38 | 39 | afterEvaluate { 40 | val jar = tasks.jar.get() 41 | val sourcesJar = tasks.sourcesJar.get() 42 | 43 | upload { 44 | curseforge(jar) 45 | modrinth(jar) 46 | maven(jar, sourcesJar) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/testmod/java/lol/bai/badpackets/test/TestTaskPayload.java: -------------------------------------------------------------------------------- 1 | package lol.bai.badpackets.test; 2 | 3 | import lol.bai.badpackets.impl.Constants; 4 | import net.minecraft.network.FriendlyByteBuf; 5 | import net.minecraft.network.codec.StreamCodec; 6 | import net.minecraft.network.protocol.common.custom.CustomPacketPayload; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | public record TestTaskPayload( 10 | Stage stage 11 | ) implements CustomPacketPayload { 12 | 13 | public static final Type TYPE = new Type<>(Constants.id("test/config/task/payload")); 14 | public static final StreamCodec CODEC = CustomPacketPayload.codec(TestTaskPayload::write, TestTaskPayload::new); 15 | 16 | public enum Stage { 17 | QUESTION_1, 18 | QUESTION_2, 19 | QUESTION_3, 20 | ANSWER_1, 21 | ANSWER_2, 22 | ANSWER_3, 23 | } 24 | 25 | public TestTaskPayload(FriendlyByteBuf buf) { 26 | this(buf.readEnum(Stage.class)); 27 | } 28 | 29 | public void write(@NotNull FriendlyByteBuf buf) { 30 | buf.writeEnum(stage); 31 | } 32 | 33 | @Override 34 | public Type type() { 35 | return TYPE; 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /.github/workflows/build_release.yml: -------------------------------------------------------------------------------- 1 | name: release 2 | on: 3 | push: 4 | tags: "*" 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - id: vars 10 | run: | 11 | echo "version=${GITHUB_REF#refs/*/}" >> $GITHUB_OUTPUT 12 | - uses: actions/checkout@v4 13 | - uses: actions/setup-java@v4 14 | with: 15 | java-version: 21 16 | distribution: temurin 17 | - run: chmod +x gradlew 18 | - run: ./gradlew build publishMods publish 19 | env: 20 | MOD_VERSION: ${{ steps.vars.outputs.version }} 21 | GITHUB_TOKEN: ${{ github.token }} 22 | CURSEFORGE_API: ${{ secrets.CURSEFORGE_API }} 23 | MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }} 24 | MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }} 25 | MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }} 26 | - uses: actions/upload-artifact@v4 27 | with: 28 | name: badpackets-${{ steps.vars.outputs.git_hash }} 29 | path: "./platform/*/build/libs/**.jar" 30 | - uses: softprops/action-gh-release@v2 31 | env: 32 | GITHUB_TOKEN: ${{ github.token }} 33 | with: 34 | tag_name: ${{ steps.vars.outputs.version }} 35 | files: | 36 | ./platform/*/build/libs/*[0-9.].jar 37 | ./platform/*/build/libs/*[0-9.]-api.jar 38 | -------------------------------------------------------------------------------- /src/main/java/lol/bai/badpackets/impl/mixin/client/MixinClientPacketListener.java: -------------------------------------------------------------------------------- 1 | package lol.bai.badpackets.impl.mixin.client; 2 | 3 | import lol.bai.badpackets.impl.handler.ClientPlayPacketHandler; 4 | import lol.bai.badpackets.impl.handler.PacketHandlerHolder; 5 | import net.minecraft.client.Minecraft; 6 | import net.minecraft.client.multiplayer.ClientPacketListener; 7 | import net.minecraft.client.multiplayer.CommonListenerCookie; 8 | import net.minecraft.network.Connection; 9 | import org.spongepowered.asm.mixin.Mixin; 10 | import org.spongepowered.asm.mixin.Unique; 11 | import org.spongepowered.asm.mixin.injection.At; 12 | import org.spongepowered.asm.mixin.injection.Inject; 13 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 14 | 15 | @Mixin(ClientPacketListener.class) 16 | public abstract class MixinClientPacketListener extends MixinClientCommonPacketListenerImpl implements PacketHandlerHolder { 17 | 18 | @Unique 19 | private ClientPlayPacketHandler badpacket_packetHandler; 20 | 21 | @Inject(method = "", at = @At("TAIL")) 22 | private void badpackets_createClientPacketHandler(Minecraft client, Connection connection, CommonListenerCookie cookie, CallbackInfo ci) { 23 | badpacket_packetHandler = new ClientPlayPacketHandler(client, (ClientPacketListener) (Object) this); 24 | } 25 | 26 | @Override 27 | public ClientPlayPacketHandler badpackets_handler() { 28 | return badpacket_packetHandler; 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/lol/bai/badpackets/impl/mixin/MixinServerGamePacketListenerImpl.java: -------------------------------------------------------------------------------- 1 | package lol.bai.badpackets.impl.mixin; 2 | 3 | import lol.bai.badpackets.impl.handler.PacketHandlerHolder; 4 | import lol.bai.badpackets.impl.handler.ServerPlayPacketHandler; 5 | import net.minecraft.network.Connection; 6 | import net.minecraft.server.MinecraftServer; 7 | import net.minecraft.server.level.ServerPlayer; 8 | import net.minecraft.server.network.CommonListenerCookie; 9 | import net.minecraft.server.network.ServerGamePacketListenerImpl; 10 | import org.spongepowered.asm.mixin.Mixin; 11 | import org.spongepowered.asm.mixin.Unique; 12 | import org.spongepowered.asm.mixin.injection.At; 13 | import org.spongepowered.asm.mixin.injection.Inject; 14 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 15 | 16 | @Mixin(ServerGamePacketListenerImpl.class) 17 | public class MixinServerGamePacketListenerImpl extends MixinServerCommonPacketListenerImpl implements PacketHandlerHolder { 18 | 19 | @Unique 20 | private ServerPlayPacketHandler badpacket_packetHandler; 21 | 22 | @Inject(method = "", at = @At("TAIL")) 23 | private void badpackets_createServerPacketHandler(MinecraftServer minecraftServer, Connection connection, ServerPlayer serverPlayer, CommonListenerCookie cookie, CallbackInfo ci) { 24 | badpacket_packetHandler = new ServerPlayPacketHandler(minecraftServer, (ServerGamePacketListenerImpl) (Object) this, connection); 25 | } 26 | 27 | @Override 28 | public ServerPlayPacketHandler badpackets_handler() { 29 | return badpacket_packetHandler; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/lol/bai/badpackets/impl/payload/UntypedPayload.java: -------------------------------------------------------------------------------- 1 | package lol.bai.badpackets.impl.payload; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | import io.netty.buffer.Unpooled; 7 | import net.minecraft.network.FriendlyByteBuf; 8 | import net.minecraft.network.codec.StreamCodec; 9 | import net.minecraft.network.protocol.common.custom.CustomPacketPayload; 10 | import net.minecraft.resources.Identifier; 11 | import org.jetbrains.annotations.NotNull; 12 | 13 | public record UntypedPayload( 14 | Type type, 15 | FriendlyByteBuf buffer 16 | ) implements CustomPacketPayload { 17 | 18 | private static final Map> TYPES = new HashMap<>(); 19 | 20 | public static Type type(Identifier id) { 21 | return TYPES.computeIfAbsent(id, Type::new); 22 | } 23 | 24 | public UntypedPayload(Identifier id, FriendlyByteBuf buf) { 25 | this(type(id), buf); 26 | } 27 | 28 | public static StreamCodec codec(Identifier id) { 29 | return CustomPacketPayload.codec(UntypedPayload::write, buf -> { 30 | FriendlyByteBuf copy = new FriendlyByteBuf(Unpooled.buffer()); 31 | copy.writeBytes(buf); 32 | return new UntypedPayload(id, copy); 33 | }); 34 | } 35 | 36 | public void write(@NotNull FriendlyByteBuf buf) { 37 | buf.writeBytes(buffer.copy()); 38 | } 39 | 40 | @Override 41 | public @NotNull Type type() { 42 | return type; 43 | } 44 | 45 | @Override 46 | public FriendlyByteBuf buffer() { 47 | return new FriendlyByteBuf(buffer.copy()); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/lol/bai/badpackets/impl/mixin/client/MixinClientConfigurationPacketListenerImpl.java: -------------------------------------------------------------------------------- 1 | package lol.bai.badpackets.impl.mixin.client; 2 | 3 | import lol.bai.badpackets.impl.Constants; 4 | import lol.bai.badpackets.impl.handler.ClientConfigPacketHandler; 5 | import lol.bai.badpackets.impl.handler.PacketHandlerHolder; 6 | import net.minecraft.client.Minecraft; 7 | import net.minecraft.client.multiplayer.ClientConfigurationPacketListenerImpl; 8 | import net.minecraft.client.multiplayer.CommonListenerCookie; 9 | import net.minecraft.network.Connection; 10 | import org.spongepowered.asm.mixin.Mixin; 11 | import org.spongepowered.asm.mixin.Unique; 12 | import org.spongepowered.asm.mixin.injection.At; 13 | import org.spongepowered.asm.mixin.injection.Inject; 14 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 15 | 16 | @Mixin(ClientConfigurationPacketListenerImpl.class) 17 | public abstract class MixinClientConfigurationPacketListenerImpl extends MixinClientCommonPacketListenerImpl implements PacketHandlerHolder { 18 | 19 | @Unique 20 | private ClientConfigPacketHandler badpackets_packetHandler; 21 | 22 | @Inject(method = "", at = @At("TAIL")) 23 | private void badpackets_createPacketHandler(Minecraft minecraft, Connection connection, CommonListenerCookie cookie, CallbackInfo ci) { 24 | badpackets_packetHandler = new ClientConfigPacketHandler(minecraft, (ClientConfigurationPacketListenerImpl) (Object) this, connection); 25 | } 26 | 27 | @Override 28 | protected boolean badpackets_handlePing(int id) { 29 | return id == Constants.PING_PONG; 30 | } 31 | 32 | @Override 33 | public ClientConfigPacketHandler badpackets_handler() { 34 | return badpackets_packetHandler; 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/lol/bai/badpackets/impl/mixin/MixinConnection.java: -------------------------------------------------------------------------------- 1 | package lol.bai.badpackets.impl.mixin; 2 | 3 | import io.netty.channel.ChannelHandlerContext; 4 | import lol.bai.badpackets.impl.handler.PacketHandlerHolder; 5 | import net.minecraft.network.Connection; 6 | import net.minecraft.network.PacketListener; 7 | import net.minecraft.network.ProtocolInfo; 8 | import org.jetbrains.annotations.Nullable; 9 | import org.spongepowered.asm.mixin.Mixin; 10 | import org.spongepowered.asm.mixin.Shadow; 11 | import org.spongepowered.asm.mixin.Unique; 12 | import org.spongepowered.asm.mixin.injection.At; 13 | import org.spongepowered.asm.mixin.injection.Inject; 14 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 15 | 16 | @Mixin(Connection.class) 17 | public class MixinConnection { 18 | 19 | @Shadow 20 | @Nullable 21 | private volatile PacketListener packetListener; 22 | 23 | @Inject(method = "setupInboundProtocol", at = @At("HEAD")) 24 | private void badpackets_cleanOldListener(ProtocolInfo info, T listener, CallbackInfo ci) { 25 | badpackets_cleanListener(); 26 | } 27 | 28 | @Inject(method = "channelInactive", at = @At("HEAD")) 29 | private void badpackets_cleanListener(ChannelHandlerContext ctx, CallbackInfo ci) { 30 | badpackets_cleanListener(); 31 | } 32 | 33 | @Inject(method = "handleDisconnection", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/PacketListener;onDisconnect(Lnet/minecraft/network/DisconnectionDetails;)V")) 34 | private void badpackets_cleanListener(CallbackInfo ci) { 35 | badpackets_cleanListener(); 36 | } 37 | 38 | @Unique 39 | private void badpackets_cleanListener() { 40 | if (this.packetListener instanceof PacketHandlerHolder holder) { 41 | holder.badpackets_handler().remove(); 42 | } 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/lol/bai/badpackets/impl/mixin/MixinServerboundCustomPayloadPacket.java: -------------------------------------------------------------------------------- 1 | package lol.bai.badpackets.impl.mixin; 2 | 3 | import com.llamalad7.mixinextras.injector.wrapoperation.Operation; 4 | import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; 5 | import lol.bai.badpackets.impl.handler.PacketHandlerHolder; 6 | import lol.bai.badpackets.impl.registry.ChannelCodecFinder; 7 | import lol.bai.badpackets.impl.registry.ChannelRegistry; 8 | import net.minecraft.network.RegistryFriendlyByteBuf; 9 | import net.minecraft.network.codec.StreamCodec; 10 | import net.minecraft.network.protocol.common.ServerCommonPacketListener; 11 | import net.minecraft.network.protocol.common.ServerboundCustomPayloadPacket; 12 | import net.minecraft.network.protocol.common.custom.CustomPacketPayload; 13 | import org.spongepowered.asm.mixin.Mixin; 14 | import org.spongepowered.asm.mixin.Shadow; 15 | import org.spongepowered.asm.mixin.injection.At; 16 | import org.spongepowered.asm.mixin.injection.Inject; 17 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 18 | 19 | import java.util.function.Function; 20 | 21 | @Mixin(ServerboundCustomPayloadPacket.class) 22 | public abstract class MixinServerboundCustomPayloadPacket { 23 | 24 | @Shadow 25 | public abstract CustomPacketPayload payload(); 26 | 27 | @Inject(method = "handle(Lnet/minecraft/network/protocol/common/ServerCommonPacketListener;)V", at = @At("HEAD"), cancellable = true) 28 | private void badpackets_handle(ServerCommonPacketListener listener, CallbackInfo ci) { 29 | if (listener instanceof PacketHandlerHolder holder && holder.badpackets_handler().receive(payload())) { 30 | ci.cancel(); 31 | } 32 | } 33 | 34 | @WrapOperation(method = "", at = @At(value = "INVOKE", target = "net/minecraft/network/codec/StreamCodec.map(Ljava/util/function/Function;Ljava/util/function/Function;)Lnet/minecraft/network/codec/StreamCodec;")) 35 | private static StreamCodec badpackets_attachChannelCodecs(StreamCodec codec, Function to, Function from, Operation> map) { 36 | ChannelCodecFinder.attach(codec, (id, buf) -> { 37 | var registry = buf instanceof RegistryFriendlyByteBuf ? ChannelRegistry.PLAY_C2S : ChannelRegistry.CONFIG_C2S; 38 | return registry.getCodec(id, buf); 39 | }); 40 | return map.call(codec, to, from); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/lol/bai/badpackets/impl/platform/PlatformProxy.java: -------------------------------------------------------------------------------- 1 | package lol.bai.badpackets.impl.platform; 2 | 3 | import java.util.ServiceLoader; 4 | import java.util.Set; 5 | import java.util.function.Consumer; 6 | 7 | import io.netty.buffer.Unpooled; 8 | import lol.bai.badpackets.impl.Constants; 9 | import net.minecraft.core.RegistryAccess; 10 | import net.minecraft.network.FriendlyByteBuf; 11 | import net.minecraft.network.RegistryFriendlyByteBuf; 12 | import net.minecraft.network.protocol.Packet; 13 | import net.minecraft.network.protocol.common.ClientboundCustomPayloadPacket; 14 | import net.minecraft.network.protocol.common.ServerboundCustomPayloadPacket; 15 | import net.minecraft.resources.Identifier; 16 | import org.jetbrains.annotations.Nullable; 17 | 18 | public class PlatformProxy { 19 | 20 | public static final PlatformProxy INSTANCE = ServiceLoader.load(PlatformProxy.class).findFirst().orElseGet(PlatformProxy::new); 21 | 22 | public boolean canSendVanillaRegisterPackets() { 23 | return true; 24 | } 25 | 26 | @SuppressWarnings("unchecked") 27 | private static T createBuf(@Nullable RegistryAccess registry, Consumer writer) { 28 | var buf = (T) (registry != null ? new RegistryFriendlyByteBuf(Unpooled.buffer(), registry) : new FriendlyByteBuf(Unpooled.buffer())); 29 | buf.writeIdentifier(Constants.MC_REGISTER_CHANNEL); 30 | writer.accept(buf); 31 | return buf; 32 | } 33 | 34 | public Packet createVanillaRegisterConfigC2SPacket(Set channels, Consumer writer) { 35 | return ServerboundCustomPayloadPacket.STREAM_CODEC.decode(createBuf(null, writer)); 36 | } 37 | 38 | public Packet createVanillaRegisterConfigS2CPacket(Set channels, Consumer writer) { 39 | return ClientboundCustomPayloadPacket.CONFIG_STREAM_CODEC.decode(createBuf(null, writer)); 40 | } 41 | 42 | public Packet createVanillaRegisterPlayC2SPacket(RegistryAccess registry, Set channels, Consumer writer) { 43 | return ServerboundCustomPayloadPacket.STREAM_CODEC.decode(createBuf(registry, writer)); 44 | } 45 | 46 | public Packet createVanillaRegisterPlayS2CPacket(RegistryAccess registry, Set channels, Consumer writer) { 47 | return ClientboundCustomPayloadPacket.GAMEPLAY_STREAM_CODEC.decode(createBuf(registry, writer)); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # bad packets [![3][3]][5] 2 | Bad packets allows packet messaging between multiple modding platforms. 3 | 4 | This means a Fabric client version of a mod that uses Bad Packets can send a packet to a server that uses the Forge version and vice versa. 5 | 6 | This mod **DOES NOT** magically make all mods that have versions for different platforms to be compatible with each other, the developer needs to use BP's API to send its packet. Even then there are more complicated things like game object id sync that this mod doesn't handle which makes it unrealistic to support in the first place. 7 | 8 | ## Project Setup 9 | ```gradle 10 | repositories { 11 | maven { 12 | url "https://maven2.bai.lol" 13 | content { 14 | includeGroup "lol.bai" 15 | } 16 | } 17 | } 18 | ``` 19 | 20 | ### Fabric 21 | ```gradle 22 | dependencies { 23 | modImplementation "lol.bai:badpackets:fabric-${badpackets_version}" 24 | } 25 | ``` 26 | 27 | ### Forge 28 | ```gradle 29 | buildscript { 30 | dependencies { 31 | classpath "org.spongepowered:mixingradle:0.7.+" 32 | } 33 | } 34 | 35 | apply plugin: "org.spongepowered.mixin" 36 | 37 | dependencies { 38 | implementation fg.deobf("lol.bai:badpackets:forge-${badpackets_version}") 39 | } 40 | ``` 41 | 42 | ### Architectury 43 | #### Common 44 | ```gradle 45 | dependencies { 46 | modCompileOnly "lol.bai:badpackets:fabric-${badpackets_version}" 47 | } 48 | ``` 49 | 50 | #### Fabric 51 | ```gradle 52 | dependencies { 53 | modRuntimeOnly "lol.bai:badpackets:fabric-${badpackets_version}" 54 | } 55 | ``` 56 | 57 | #### Forge 58 | ```gradle 59 | dependencies { 60 | modRuntimeOnly "lol.bai:badpackets:forge-${badpackets_version}" 61 | } 62 | ``` 63 | 64 | ### Sponge VanillaGradle based multiplatform 65 | #### Common 66 | ```gradle 67 | dependencies { 68 | compileOnly "lol.bai:badpackets:mojmap-${badpackets_version}" 69 | } 70 | ``` 71 | 72 | #### Fabric 73 | ```gradle 74 | dependencies { 75 | modRuntimeOnly "lol.bai:badpackets:fabric-${badpackets_version}" 76 | } 77 | ``` 78 | 79 | #### Forge 80 | ```gradle 81 | buildscript { 82 | dependencies { 83 | classpath "org.spongepowered:mixingradle:0.7.+" 84 | } 85 | } 86 | 87 | apply plugin: "org.spongepowered.mixin" 88 | 89 | dependencies { 90 | runtimeOnly fg.deobf("lol.bai:badpackets:forge-${badpackets_version}") 91 | } 92 | ``` 93 | 94 | [3]: https://img.shields.io/badge/code_quality-F-red 95 | [5]: https://git.io/code-quality 96 | -------------------------------------------------------------------------------- /src/main/java/lol/bai/badpackets/impl/mixin/MixinCustomPacketPayload_1.java: -------------------------------------------------------------------------------- 1 | package lol.bai.badpackets.impl.mixin; 2 | 3 | import lol.bai.badpackets.impl.registry.ChannelCodecFinder; 4 | import net.minecraft.network.FriendlyByteBuf; 5 | import net.minecraft.network.protocol.common.custom.CustomPacketPayload; 6 | import net.minecraft.resources.Identifier; 7 | import org.jetbrains.annotations.Nullable; 8 | import org.spongepowered.asm.mixin.Mixin; 9 | import org.spongepowered.asm.mixin.Unique; 10 | import org.spongepowered.asm.mixin.injection.At; 11 | import org.spongepowered.asm.mixin.injection.Inject; 12 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 13 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 14 | import org.spongepowered.asm.mixin.injection.callback.LocalCapture; 15 | 16 | @Mixin(targets = "net.minecraft.network.protocol.common.custom.CustomPacketPayload$1") 17 | public class MixinCustomPacketPayload_1 implements ChannelCodecFinder.Holder { 18 | 19 | @Unique 20 | @Nullable 21 | private ChannelCodecFinder badpackets_channelCodecFinder = null; 22 | 23 | @Override 24 | public void badpackets_setChannelCodecFinder(ChannelCodecFinder finder) { 25 | this.badpackets_channelCodecFinder = finder; 26 | } 27 | 28 | @Inject(method = "writeCap", cancellable = true, at = @At(value = "INVOKE", target = "net/minecraft/network/protocol/common/custom/CustomPacketPayload$1.findCodec(Lnet/minecraft/resources/Identifier;)Lnet/minecraft/network/codec/StreamCodec;")) 29 | private void badpackets_encode(FriendlyByteBuf buf, CustomPacketPayload.Type type, CustomPacketPayload payload, CallbackInfo ci) { 30 | if (badpackets_channelCodecFinder == null) return; 31 | 32 | var codec = badpackets_channelCodecFinder.getCodec(type.id(), buf); 33 | if (codec != null) { 34 | codec.encode(buf, payload); 35 | ci.cancel(); 36 | } 37 | } 38 | 39 | @Inject(method = "decode", cancellable = true, locals = LocalCapture.CAPTURE_FAILHARD, at = @At(value = "INVOKE", target = "net/minecraft/network/protocol/common/custom/CustomPacketPayload$1.findCodec(Lnet/minecraft/resources/Identifier;)Lnet/minecraft/network/codec/StreamCodec;")) 40 | private void badpackets_decode(FriendlyByteBuf buf, CallbackInfoReturnable cir, Identifier id) { 41 | if (badpackets_channelCodecFinder == null) return; 42 | 43 | var codec = badpackets_channelCodecFinder.getCodec(id, buf); 44 | if (codec != null) cir.setReturnValue(codec.decode(buf)); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /src/main/java/lol/bai/badpackets/impl/mixin/MixinClientboundCustomPayloadPacket.java: -------------------------------------------------------------------------------- 1 | package lol.bai.badpackets.impl.mixin; 2 | 3 | import com.llamalad7.mixinextras.injector.wrapoperation.Operation; 4 | import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; 5 | import lol.bai.badpackets.impl.handler.PacketHandlerHolder; 6 | import lol.bai.badpackets.impl.registry.ChannelCodecFinder; 7 | import lol.bai.badpackets.impl.registry.ChannelRegistry; 8 | import net.minecraft.network.codec.StreamCodec; 9 | import net.minecraft.network.protocol.common.ClientCommonPacketListener; 10 | import net.minecraft.network.protocol.common.ClientboundCustomPayloadPacket; 11 | import net.minecraft.network.protocol.common.custom.CustomPacketPayload; 12 | import org.spongepowered.asm.mixin.Mixin; 13 | import org.spongepowered.asm.mixin.Shadow; 14 | import org.spongepowered.asm.mixin.injection.At; 15 | import org.spongepowered.asm.mixin.injection.Inject; 16 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 17 | 18 | import java.util.function.Function; 19 | 20 | @Mixin(ClientboundCustomPayloadPacket.class) 21 | public abstract class MixinClientboundCustomPayloadPacket { 22 | 23 | @Shadow 24 | public abstract CustomPacketPayload payload(); 25 | 26 | @Inject(method = "handle(Lnet/minecraft/network/protocol/common/ClientCommonPacketListener;)V", at = @At("HEAD"), cancellable = true) 27 | private void badpackets_handle(ClientCommonPacketListener listener, CallbackInfo ci) { 28 | if (listener instanceof PacketHandlerHolder holder && holder.badpackets_handler().receive(payload())) { 29 | ci.cancel(); 30 | } 31 | } 32 | 33 | @WrapOperation(method = "", at = @At(value = "INVOKE", ordinal = 0, target = "net/minecraft/network/codec/StreamCodec.map(Ljava/util/function/Function;Ljava/util/function/Function;)Lnet/minecraft/network/codec/StreamCodec;")) 34 | private static StreamCodec badpackets_attachPlayChannelCodecs(StreamCodec codec, Function to, Function from, Operation> map) { 35 | ChannelCodecFinder.attach(codec, ChannelRegistry.PLAY_S2C); 36 | return map.call(codec, to, from); 37 | } 38 | 39 | @WrapOperation(method = "", at = @At(value = "INVOKE", ordinal = 1, target = "net/minecraft/network/codec/StreamCodec.map(Ljava/util/function/Function;Ljava/util/function/Function;)Lnet/minecraft/network/codec/StreamCodec;")) 40 | private static StreamCodec badpackets_attachConfigChannelCodecs(StreamCodec codec, Function to, Function from, Operation> map) { 41 | ChannelCodecFinder.attach(codec, ChannelRegistry.CONFIG_S2C); 42 | return map.call(codec, to, from); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/lol/bai/badpackets/impl/handler/ClientConfigPacketHandler.java: -------------------------------------------------------------------------------- 1 | package lol.bai.badpackets.impl.handler; 2 | 3 | import java.util.Set; 4 | import java.util.function.Consumer; 5 | 6 | import lol.bai.badpackets.api.PacketReceiver; 7 | import lol.bai.badpackets.api.config.ClientConfigContext; 8 | import lol.bai.badpackets.api.config.ConfigPackets; 9 | import lol.bai.badpackets.impl.platform.PlatformProxy; 10 | import lol.bai.badpackets.impl.registry.CallbackRegistry; 11 | import lol.bai.badpackets.impl.registry.ChannelRegistry; 12 | import net.minecraft.client.Minecraft; 13 | import net.minecraft.client.multiplayer.ClientConfigurationPacketListenerImpl; 14 | import net.minecraft.network.Connection; 15 | import net.minecraft.network.FriendlyByteBuf; 16 | import net.minecraft.network.chat.Component; 17 | import net.minecraft.network.protocol.Packet; 18 | import net.minecraft.network.protocol.common.ServerboundCustomPayloadPacket; 19 | import net.minecraft.network.protocol.common.custom.CustomPacketPayload; 20 | import net.minecraft.resources.Identifier; 21 | 22 | public class ClientConfigPacketHandler extends AbstractPacketHandler implements ClientConfigContext { 23 | 24 | private final Minecraft client; 25 | private final ClientConfigurationPacketListenerImpl listener; 26 | 27 | public ClientConfigPacketHandler(Minecraft client, ClientConfigurationPacketListenerImpl listener, Connection connection) { 28 | super("ClientConfigPacketHandler", ChannelRegistry.CONFIG_S2C, ServerboundCustomPayloadPacket::new, client, connection); 29 | 30 | this.client = client; 31 | this.listener = listener; 32 | } 33 | 34 | @Override 35 | protected Packet createVanillaRegisterPacket(Set channels, Consumer buf) { 36 | return PlatformProxy.INSTANCE.createVanillaRegisterConfigC2SPacket(channels, buf); 37 | } 38 | 39 | @Override 40 | protected void onInitialChannelSyncPacketReceived() { 41 | for (var callback : CallbackRegistry.CLIENT_READY_CONFIG) { 42 | callback.onReady(this); 43 | } 44 | 45 | sendInitialChannelSyncPacket(); 46 | } 47 | 48 | @Override 49 | protected void receiveUnsafe(PacketReceiver receiver, CustomPacketPayload payload) { 50 | receiver.receive(this, payload); 51 | } 52 | 53 | @Override 54 | public Minecraft client() { 55 | return client; 56 | } 57 | 58 | @Override 59 | public ClientConfigurationPacketListenerImpl handler() { 60 | return listener; 61 | } 62 | 63 | @Override 64 | public void disconnect(Component reason) { 65 | ConfigPackets.disconnect(listener, reason); 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/lol/bai/badpackets/impl/handler/ClientPlayPacketHandler.java: -------------------------------------------------------------------------------- 1 | package lol.bai.badpackets.impl.handler; 2 | 3 | import lol.bai.badpackets.api.PacketReceiver; 4 | import lol.bai.badpackets.api.play.ClientPlayContext; 5 | import lol.bai.badpackets.impl.platform.PlatformProxy; 6 | import lol.bai.badpackets.impl.registry.CallbackRegistry; 7 | import lol.bai.badpackets.impl.registry.ChannelRegistry; 8 | import net.minecraft.client.Minecraft; 9 | import net.minecraft.client.multiplayer.ClientPacketListener; 10 | import net.minecraft.network.RegistryFriendlyByteBuf; 11 | import net.minecraft.network.protocol.Packet; 12 | import net.minecraft.network.protocol.common.ServerboundCustomPayloadPacket; 13 | import net.minecraft.network.protocol.common.custom.CustomPacketPayload; 14 | import net.minecraft.resources.Identifier; 15 | 16 | import java.util.Set; 17 | import java.util.function.Consumer; 18 | 19 | public class ClientPlayPacketHandler extends AbstractPacketHandler implements ClientPlayContext { 20 | 21 | private final Minecraft client; 22 | private final ClientPacketListener listener; 23 | 24 | public ClientPlayPacketHandler(Minecraft client, ClientPacketListener listener) { 25 | super("ClientPlayPacketHandler", ChannelRegistry.PLAY_S2C, ServerboundCustomPayloadPacket::new, client, listener.getConnection()); 26 | 27 | this.client = client; 28 | this.listener = listener; 29 | } 30 | 31 | public static ClientPlayPacketHandler get() { 32 | ClientPacketListener listener = Minecraft.getInstance().getConnection(); 33 | if (listener == null) { 34 | throw new IllegalStateException("Cannot get c2s sender when not in play stage!"); 35 | } 36 | 37 | return ((PacketHandlerHolder) listener).badpackets_handler(); 38 | } 39 | 40 | @Override 41 | protected Packet createVanillaRegisterPacket(Set channels, Consumer buf) { 42 | return PlatformProxy.INSTANCE.createVanillaRegisterPlayC2SPacket(listener.registryAccess(), channels, buf); 43 | } 44 | 45 | @Override 46 | protected void onInitialChannelSyncPacketReceived() { 47 | sendInitialChannelSyncPacket(); 48 | for (var callback : CallbackRegistry.CLIENT_PLAY) { 49 | callback.onReady(this); 50 | } 51 | } 52 | 53 | @Override 54 | protected void receiveUnsafe(PacketReceiver receiver, CustomPacketPayload payload) { 55 | receiver.receive(this, payload); 56 | } 57 | 58 | @Override 59 | public Minecraft client() { 60 | return client; 61 | } 62 | 63 | @Override 64 | public ClientPacketListener handler() { 65 | return listener; 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/lol/bai/badpackets/impl/handler/ServerPlayPacketHandler.java: -------------------------------------------------------------------------------- 1 | package lol.bai.badpackets.impl.handler; 2 | 3 | import lol.bai.badpackets.api.PacketReceiver; 4 | import lol.bai.badpackets.api.play.ServerPlayContext; 5 | import lol.bai.badpackets.impl.platform.PlatformProxy; 6 | import lol.bai.badpackets.impl.registry.CallbackRegistry; 7 | import lol.bai.badpackets.impl.registry.ChannelRegistry; 8 | import net.minecraft.network.Connection; 9 | import net.minecraft.network.RegistryFriendlyByteBuf; 10 | import net.minecraft.network.protocol.Packet; 11 | import net.minecraft.network.protocol.common.ClientboundCustomPayloadPacket; 12 | import net.minecraft.network.protocol.common.custom.CustomPacketPayload; 13 | import net.minecraft.resources.Identifier; 14 | import net.minecraft.server.MinecraftServer; 15 | import net.minecraft.server.level.ServerPlayer; 16 | import net.minecraft.server.network.ServerGamePacketListenerImpl; 17 | 18 | import java.util.Set; 19 | import java.util.function.Consumer; 20 | 21 | public class ServerPlayPacketHandler extends AbstractPacketHandler implements ServerPlayContext { 22 | 23 | private final MinecraftServer server; 24 | private final ServerGamePacketListenerImpl handler; 25 | 26 | public ServerPlayPacketHandler(MinecraftServer server, ServerGamePacketListenerImpl handler, Connection connection) { 27 | super("ServerPlayPacketHandler for " + handler.getPlayer().getScoreboardName(), ChannelRegistry.PLAY_C2S, ClientboundCustomPayloadPacket::new, server, connection); 28 | this.server = server; 29 | this.handler = handler; 30 | } 31 | 32 | public static ServerPlayPacketHandler get(ServerPlayer player) { 33 | return ((PacketHandlerHolder) player.connection).badpackets_handler(); 34 | } 35 | 36 | @Override 37 | protected Packet createVanillaRegisterPacket(Set channels, Consumer buf) { 38 | return PlatformProxy.INSTANCE.createVanillaRegisterPlayS2CPacket(server.registryAccess(), channels, buf); 39 | } 40 | 41 | @Override 42 | protected void onInitialChannelSyncPacketReceived() { 43 | for (var callback : CallbackRegistry.SERVER_PLAY) { 44 | callback.onReady(this); 45 | } 46 | } 47 | 48 | @Override 49 | protected void receiveUnsafe(PacketReceiver receiver, CustomPacketPayload payload) { 50 | receiver.receive(this, payload); 51 | } 52 | 53 | @Override 54 | public MinecraftServer server() { 55 | return server; 56 | } 57 | 58 | @Override 59 | public ServerPlayer player() { 60 | return handler.player; 61 | } 62 | 63 | @Override 64 | public ServerGamePacketListenerImpl handler() { 65 | return handler; 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /platform/forge/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import net.minecraftforge.gradle.common.util.RunConfig 2 | 3 | plugins { 4 | id("net.minecraftforge.gradle") version "6.0.44" 5 | id("org.spongepowered.mixin") version "0.7.38" 6 | } 7 | 8 | setupPlatform() 9 | 10 | repositories { 11 | maven("https://maven.minecraftforge.net") 12 | maven("https://maven.fabricmc.net/") 13 | mavenCentral() 14 | } 15 | 16 | jarJar.enable() 17 | 18 | dependencies { 19 | minecraft("net.minecraftforge:forge:${rootProp["minecraft"]}-${rootProp["forge"]}") 20 | annotationProcessor("net.minecraftforge:eventbus-validator:7.0-beta.7") 21 | 22 | implementation("org.jetbrains:annotations:19.0.0") 23 | 24 | compileOnly("io.github.llamalad7:mixinextras-common:0.3.5") 25 | implementation(jarJar("io.github.llamalad7:mixinextras-forge:0.3.5")) { 26 | jarJar.ranged(this, "[0.3.5,)") 27 | } 28 | 29 | // https://github.com/MinecraftForge/MinecraftForge/blob/7b782e5b05d0059836b39fa072d49f63679d1782/mdk/build.gradle#L143C5-L143C92 30 | implementation("net.sf.jopt-simple:jopt-simple:5.0.4") { version { strictly("5.0.4") } } 31 | } 32 | 33 | minecraft { 34 | mappings("official", rootProp["minecraft"]) 35 | reobf = false 36 | 37 | runs { 38 | val runConfig = Action { 39 | workingDirectory(rootProject.file("run")) 40 | ideaModule("${rootProject.name}.${project.name}.testmod") 41 | property("eventbus.api.strictRuntimeChecks", "true") 42 | 43 | mods { 44 | create("badpackets") { 45 | source(sourceSets["main"]) 46 | } 47 | 48 | create("badpackets_testmod") { 49 | source(sourceSets["testmod"]) 50 | } 51 | } 52 | } 53 | create("client", runConfig) 54 | create("server", runConfig) 55 | } 56 | } 57 | 58 | mixin { 59 | add(sourceSets["main"], "badpackets.refmap.json") 60 | config("badpackets.mixins.json") 61 | } 62 | 63 | tasks.processResources { 64 | inputs.property("version", project.version) 65 | 66 | filesMatching("META-INF/mods.toml") { 67 | expand("version" to project.version) 68 | } 69 | } 70 | 71 | tasks.jar { 72 | archiveClassifier = "dev" 73 | finalizedBy("jarJar") 74 | 75 | manifest.attributes(mapOf( 76 | "MixinConfigs" to "badpackets.mixins.json" 77 | )) 78 | } 79 | 80 | tasks.jarJar { 81 | archiveClassifier = "" 82 | } 83 | 84 | afterEvaluate { 85 | val jarJar = tasks.jarJar.get() 86 | val sourcesJar = tasks.sourcesJar.get() 87 | 88 | upload { 89 | curseforge(jarJar) 90 | modrinth(jarJar) 91 | maven(jarJar, sourcesJar) 92 | } 93 | } 94 | 95 | sourceSets.forEach { 96 | val dir = layout.buildDirectory.dir("sourcesSets/${it.name}") 97 | it.output.setResourcesDir(dir) 98 | it.java.destinationDirectory.set(dir) 99 | } 100 | -------------------------------------------------------------------------------- /src/main/java/lol/bai/badpackets/api/PacketSender.java: -------------------------------------------------------------------------------- 1 | package lol.bai.badpackets.api; 2 | 3 | import io.netty.channel.ChannelFutureListener; 4 | import lol.bai.badpackets.api.play.ClientPlayContext; 5 | import lol.bai.badpackets.api.play.PlayPackets; 6 | import lol.bai.badpackets.api.play.ServerPlayContext; 7 | import lol.bai.badpackets.impl.handler.ClientPlayPacketHandler; 8 | import lol.bai.badpackets.impl.handler.ServerPlayPacketHandler; 9 | import lol.bai.badpackets.impl.marker.ApiSide; 10 | import lol.bai.badpackets.impl.payload.UntypedPayload; 11 | import net.minecraft.network.FriendlyByteBuf; 12 | import net.minecraft.network.codec.StreamCodec; 13 | import net.minecraft.network.protocol.common.custom.CustomPacketPayload; 14 | import net.minecraft.resources.Identifier; 15 | import net.minecraft.server.level.ServerPlayer; 16 | import org.jetbrains.annotations.ApiStatus; 17 | import org.jetbrains.annotations.Nullable; 18 | 19 | @ApiStatus.NonExtendable 20 | public interface PacketSender { 21 | 22 | /** 23 | * Returns client-to-server play packet sender. 24 | *

25 | * Only available when on game. 26 | * 27 | * @see PlayPackets#registerServerChannel(Identifier) 28 | * @see PlayPackets#registerServerChannel(CustomPacketPayload.Type, StreamCodec) 29 | */ 30 | @ApiSide.ClientOnly 31 | static ClientPlayContext c2s() { 32 | return ClientPlayPacketHandler.get(); 33 | } 34 | 35 | /** 36 | * Returns a server-to-client play packet sender. 37 | * 38 | * @param player the player that we want to send packets to. 39 | * 40 | * @see PlayPackets#registerClientChannel(Identifier) 41 | * @see PlayPackets#registerClientChannel(CustomPacketPayload.Type, StreamCodec) 42 | */ 43 | static ServerPlayContext s2c(ServerPlayer player) { 44 | return ServerPlayPacketHandler.get(player); 45 | } 46 | 47 | /** 48 | * Returns whether the target can receive a packet with the specified id. 49 | *

50 | * Note: Only works for Bad Packets channels. 51 | * 52 | * @see PlayPackets#registerClientReadyCallback(PacketReadyCallback) 53 | * @see PlayPackets#registerServerReadyCallback(PacketReadyCallback) 54 | */ 55 | boolean canSend(Identifier id); 56 | 57 | /** 58 | * Returns whether the target can receive a packet with the specified id. 59 | *

60 | * Note: Only works for Bad Packets channels. 61 | * 62 | * @see PlayPackets#registerClientReadyCallback(PacketReadyCallback) 63 | * @see PlayPackets#registerServerReadyCallback(PacketReadyCallback) 64 | */ 65 | default boolean canSend(CustomPacketPayload.Type type) { 66 | return canSend(type.id()); 67 | } 68 | 69 | /** 70 | * Send a packet to the target. 71 | */ 72 | default void send(CustomPacketPayload payload) { 73 | send(payload, null); 74 | } 75 | 76 | /** 77 | * Send a packet to the target. 78 | * 79 | * @param callback a callback in which will be called after the packet sent to the target. 80 | */ 81 | void send(CustomPacketPayload payload, @Nullable ChannelFutureListener callback); 82 | 83 | /** 84 | * Send a packet to the target. 85 | */ 86 | default void send(Identifier id, FriendlyByteBuf buf) { 87 | send(id, buf, null); 88 | } 89 | 90 | /** 91 | * Send a packet to the target. 92 | * 93 | * @param callback a callback in which will be called after the packet sent to the target. 94 | */ 95 | default void send(Identifier id, FriendlyByteBuf buf, @Nullable ChannelFutureListener callback) { 96 | send(new UntypedPayload(UntypedPayload.type(id), buf), callback); 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /src/main/java/lol/bai/badpackets/impl/mixin/MixinServerConfigurationPacketListenerImpl.java: -------------------------------------------------------------------------------- 1 | package lol.bai.badpackets.impl.mixin; 2 | 3 | import lol.bai.badpackets.impl.Constants; 4 | import lol.bai.badpackets.impl.handler.PacketHandlerHolder; 5 | import lol.bai.badpackets.impl.handler.ServerConfigPacketHandler; 6 | import net.minecraft.network.Connection; 7 | import net.minecraft.server.MinecraftServer; 8 | import net.minecraft.server.network.CommonListenerCookie; 9 | import net.minecraft.server.network.ConfigurationTask; 10 | import net.minecraft.server.network.ServerConfigurationPacketListenerImpl; 11 | import org.jetbrains.annotations.Nullable; 12 | import org.objectweb.asm.Opcodes; 13 | import org.spongepowered.asm.mixin.Final; 14 | import org.spongepowered.asm.mixin.Mixin; 15 | import org.spongepowered.asm.mixin.Shadow; 16 | import org.spongepowered.asm.mixin.Unique; 17 | import org.spongepowered.asm.mixin.injection.At; 18 | import org.spongepowered.asm.mixin.injection.Inject; 19 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 20 | import org.spongepowered.asm.mixin.injection.callback.LocalCapture; 21 | 22 | import java.util.Queue; 23 | 24 | @Mixin(ServerConfigurationPacketListenerImpl.class) 25 | public abstract class MixinServerConfigurationPacketListenerImpl extends MixinServerCommonPacketListenerImpl implements ServerConfigPacketHandler.TaskFinisher, PacketHandlerHolder { 26 | 27 | @Shadow 28 | @Final 29 | private Queue configurationTasks; 30 | 31 | @Shadow 32 | protected abstract void finishCurrentTask(ConfigurationTask.Type type); 33 | 34 | @Shadow 35 | @Nullable 36 | private ConfigurationTask currentTask; 37 | 38 | @Unique 39 | private ServerConfigPacketHandler badpackets_packetHandler; 40 | 41 | @Inject(method = "", at = @At("TAIL")) 42 | private void badpackets_createPacketHandler(MinecraftServer server, Connection connection, CommonListenerCookie cookie, CallbackInfo ci) { 43 | badpackets_packetHandler = new ServerConfigPacketHandler(server, (ServerConfigurationPacketListenerImpl) (Object) this, connection); 44 | } 45 | 46 | @Inject(method = "startConfiguration", at = @At("HEAD")) 47 | private void badpackets_initPacketHandler(CallbackInfo ci) { 48 | configurationTasks.add(badpackets_packetHandler.createCallbackTask()); 49 | configurationTasks.addAll(ServerConfigPacketHandler.CUSTOM_TASKS.values()); 50 | } 51 | 52 | @Inject(method = "startNextTask", locals = LocalCapture.CAPTURE_FAILHARD, at = @At(value = "FIELD", opcode = Opcodes.PUTFIELD, target = "Lnet/minecraft/server/network/ServerConfigurationPacketListenerImpl;currentTask:Lnet/minecraft/server/network/ConfigurationTask;")) 53 | private void badpackets_attachCustomTaskContext(CallbackInfo ci, ConfigurationTask task) { 54 | if (task instanceof ServerConfigPacketHandler.CustomTask custom) { 55 | custom.setHandler(badpackets_packetHandler); 56 | } 57 | } 58 | 59 | @Inject(method = "finishCurrentTask", at = @At(value = "FIELD", opcode = Opcodes.PUTFIELD, target = "Lnet/minecraft/server/network/ServerConfigurationPacketListenerImpl;currentTask:Lnet/minecraft/server/network/ConfigurationTask;")) 60 | private void badpackets_detachCustomTaskContext(ConfigurationTask.Type type, CallbackInfo ci) { 61 | if (currentTask instanceof ServerConfigPacketHandler.CustomTask custom) { 62 | custom.setHandler(null); 63 | } 64 | } 65 | 66 | @Override 67 | public ServerConfigPacketHandler badpackets_handler() { 68 | return badpackets_packetHandler; 69 | } 70 | 71 | @Override 72 | protected void badpackets_onPong(int id) { 73 | if (id == Constants.PING_PONG) { 74 | finishCurrentTask(ServerConfigPacketHandler.CallbackTask.TYPE); 75 | } 76 | } 77 | 78 | @Override 79 | public void badpackets_finishTask(ConfigurationTask.Type type) { 80 | finishCurrentTask(type); 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/lol/bai/badpackets/impl/handler/ServerConfigPacketHandler.java: -------------------------------------------------------------------------------- 1 | package lol.bai.badpackets.impl.handler; 2 | 3 | import lol.bai.badpackets.api.PacketReceiver; 4 | import lol.bai.badpackets.api.config.ConfigTaskExecutor; 5 | import lol.bai.badpackets.api.config.ServerConfigContext; 6 | import lol.bai.badpackets.impl.Constants; 7 | import lol.bai.badpackets.impl.platform.PlatformProxy; 8 | import lol.bai.badpackets.impl.registry.CallbackRegistry; 9 | import lol.bai.badpackets.impl.registry.ChannelRegistry; 10 | import net.minecraft.network.Connection; 11 | import net.minecraft.network.FriendlyByteBuf; 12 | import net.minecraft.network.protocol.Packet; 13 | import net.minecraft.network.protocol.common.ClientboundCustomPayloadPacket; 14 | import net.minecraft.network.protocol.common.ClientboundPingPacket; 15 | import net.minecraft.network.protocol.common.custom.CustomPacketPayload; 16 | import net.minecraft.resources.Identifier; 17 | import net.minecraft.server.MinecraftServer; 18 | import net.minecraft.server.network.ConfigurationTask; 19 | import net.minecraft.server.network.ServerConfigurationPacketListenerImpl; 20 | import org.jetbrains.annotations.NotNull; 21 | 22 | import java.util.HashMap; 23 | import java.util.Map; 24 | import java.util.Set; 25 | import java.util.function.Consumer; 26 | 27 | public class ServerConfigPacketHandler extends AbstractPacketHandler implements ServerConfigContext { 28 | 29 | public static final Map CUSTOM_TASKS = new HashMap<>(); 30 | 31 | private final MinecraftServer server; 32 | private final ServerConfigurationPacketListenerImpl listener; 33 | 34 | public ServerConfigPacketHandler(MinecraftServer server, ServerConfigurationPacketListenerImpl listener, Connection connection) { 35 | super("ServerConfigPacketHandler for " + listener.getOwner().name(), ChannelRegistry.CONFIG_C2S, ClientboundCustomPayloadPacket::new, server, connection); 36 | 37 | this.server = server; 38 | this.listener = listener; 39 | } 40 | 41 | public static void registerTask(Identifier id, ConfigTaskExecutor executor) { 42 | CUSTOM_TASKS.put(id, new CustomTask(id, executor)); 43 | } 44 | 45 | public CallbackTask createCallbackTask() { 46 | return new CallbackTask(); 47 | } 48 | 49 | @Override 50 | protected Packet createVanillaRegisterPacket(Set channels, Consumer buf) { 51 | return PlatformProxy.INSTANCE.createVanillaRegisterConfigS2CPacket(channels, buf); 52 | } 53 | 54 | @Override 55 | protected void onInitialChannelSyncPacketReceived() { 56 | for (var callback : CallbackRegistry.SERVER_READY_CONFIG) { 57 | callback.onReady(this); 58 | } 59 | 60 | ((TaskFinisher) listener).badpackets_finishTask(CallbackTask.TYPE); 61 | } 62 | 63 | @Override 64 | protected void receiveUnsafe(PacketReceiver receiver, CustomPacketPayload payload) { 65 | receiver.receive(this, payload); 66 | } 67 | 68 | @Override 69 | public MinecraftServer server() { 70 | return server; 71 | } 72 | 73 | @Override 74 | public ServerConfigurationPacketListenerImpl handler() { 75 | return listener; 76 | } 77 | 78 | @Override 79 | public void finishTask(Identifier taskId) { 80 | ((TaskFinisher) listener).badpackets_finishTask(CUSTOM_TASKS.get(taskId).type); 81 | } 82 | 83 | public interface TaskFinisher { 84 | 85 | void badpackets_finishTask(ConfigurationTask.Type type); 86 | 87 | } 88 | 89 | public class CallbackTask implements ConfigurationTask { 90 | 91 | public static final Type TYPE = new Type(Constants.MOD_ID); 92 | 93 | @Override 94 | public void start(@NotNull Consumer> sender) { 95 | sendInitialChannelSyncPacket(); 96 | sender.accept(new ClientboundPingPacket(Constants.PING_PONG)); 97 | } 98 | 99 | @Override 100 | public @NotNull Type type() { 101 | return TYPE; 102 | } 103 | 104 | } 105 | 106 | public static class CustomTask implements ConfigurationTask { 107 | 108 | private final Type type; 109 | private final ConfigTaskExecutor executor; 110 | 111 | private ServerConfigPacketHandler handler; 112 | 113 | public CustomTask(Identifier id, ConfigTaskExecutor executor) { 114 | this.type = new Type(id.toString()); 115 | this.executor = executor; 116 | } 117 | 118 | public void setHandler(ServerConfigPacketHandler handler) { 119 | this.handler = handler; 120 | } 121 | 122 | @Override 123 | public void start(@NotNull Consumer> consumer) { 124 | if (!executor.runTask(handler)) { 125 | ((TaskFinisher) handler.listener).badpackets_finishTask(type); 126 | } 127 | } 128 | 129 | @Override 130 | public @NotNull Type type() { 131 | return type; 132 | } 133 | 134 | } 135 | 136 | } 137 | -------------------------------------------------------------------------------- /src/main/java/lol/bai/badpackets/impl/registry/ChannelRegistry.java: -------------------------------------------------------------------------------- 1 | package lol.bai.badpackets.impl.registry; 2 | 3 | import java.util.HashMap; 4 | import java.util.HashSet; 5 | import java.util.Map; 6 | import java.util.Set; 7 | import java.util.concurrent.locks.Lock; 8 | import java.util.concurrent.locks.ReentrantReadWriteLock; 9 | 10 | import lol.bai.badpackets.api.PacketReceiver; 11 | import lol.bai.badpackets.api.config.ClientConfigContext; 12 | import lol.bai.badpackets.api.config.ServerConfigContext; 13 | import lol.bai.badpackets.api.play.ClientPlayContext; 14 | import lol.bai.badpackets.api.play.ServerPlayContext; 15 | import lol.bai.badpackets.impl.Constants; 16 | import lol.bai.badpackets.impl.handler.AbstractPacketHandler; 17 | import lol.bai.badpackets.impl.payload.UntypedPayload; 18 | import net.minecraft.network.FriendlyByteBuf; 19 | import net.minecraft.network.RegistryFriendlyByteBuf; 20 | import net.minecraft.network.codec.StreamCodec; 21 | import net.minecraft.network.protocol.common.custom.CustomPacketPayload; 22 | import net.minecraft.resources.Identifier; 23 | import org.jetbrains.annotations.Nullable; 24 | 25 | public class ChannelRegistry implements ChannelCodecFinder { 26 | 27 | private static final Set RESERVED_CHANNELS = Set.of( 28 | Constants.CHANNEL_SYNC, 29 | Constants.MC_REGISTER_CHANNEL, 30 | Constants.MC_UNREGISTER_CHANNEL); 31 | 32 | public static final ChannelRegistry CONFIG_S2C = new ChannelRegistry<>(RESERVED_CHANNELS); 33 | public static final ChannelRegistry CONFIG_C2S = new ChannelRegistry<>(RESERVED_CHANNELS); 34 | 35 | public static final ChannelRegistry PLAY_S2C = new ChannelRegistry<>(RESERVED_CHANNELS); 36 | public static final ChannelRegistry PLAY_C2S = new ChannelRegistry<>(RESERVED_CHANNELS); 37 | 38 | private final Set reservedChannels; 39 | 40 | private final Map> codecs = new HashMap<>(); 41 | private final Map> receivers = new HashMap<>(); 42 | private final Set> handlers = new HashSet<>(); 43 | 44 | private final ReentrantReadWriteLock locks = new ReentrantReadWriteLock(); 45 | 46 | private ChannelRegistry(Set reservedChannels) { 47 | this.reservedChannels = reservedChannels; 48 | 49 | codecs.put(Constants.CHANNEL_SYNC, UntypedPayload.codec(Constants.CHANNEL_SYNC)); 50 | } 51 | 52 | public

void registerCodec(Identifier id, StreamCodec codec) { 53 | Lock lock = locks.writeLock(); 54 | lock.lock(); 55 | 56 | try { 57 | if (reservedChannels.contains(id)) { 58 | throw new IllegalArgumentException("Reserved channel id " + id); 59 | } 60 | 61 | codecs.put(id, codec); 62 | } finally { 63 | lock.unlock(); 64 | } 65 | } 66 | 67 | public void registerReceiver(Identifier id, PacketReceiver receiver) { 68 | Lock lock = locks.writeLock(); 69 | lock.lock(); 70 | 71 | try { 72 | if (!codecs.containsKey(id)) { 73 | throw new IllegalArgumentException("Unknown channel id " + id); 74 | } 75 | 76 | receivers.put(id, receiver); 77 | for (AbstractPacketHandler handler : handlers) { 78 | handler.onRegister(id); 79 | } 80 | } finally { 81 | lock.unlock(); 82 | } 83 | } 84 | 85 | public boolean has(Identifier id) { 86 | Lock lock = locks.readLock(); 87 | lock.lock(); 88 | 89 | try { 90 | return receivers.containsKey(id); 91 | } finally { 92 | lock.unlock(); 93 | } 94 | } 95 | 96 | public PacketReceiver get(Identifier id) { 97 | Lock lock = locks.readLock(); 98 | lock.lock(); 99 | 100 | try { 101 | return receivers.get(id); 102 | } finally { 103 | lock.unlock(); 104 | } 105 | } 106 | 107 | @Override 108 | @SuppressWarnings("unchecked") 109 | public @Nullable StreamCodec getCodec(Identifier id, FriendlyByteBuf buf) { 110 | Lock lock = locks.readLock(); 111 | lock.lock(); 112 | 113 | try { 114 | return (StreamCodec) codecs.get(id); 115 | } finally { 116 | lock.unlock(); 117 | } 118 | } 119 | 120 | public Set getChannels() { 121 | Lock lock = locks.readLock(); 122 | lock.lock(); 123 | 124 | try { 125 | return new HashSet<>(receivers.keySet()); 126 | } finally { 127 | lock.unlock(); 128 | } 129 | } 130 | 131 | public void addHandler(AbstractPacketHandler handler) { 132 | Lock lock = locks.writeLock(); 133 | lock.lock(); 134 | 135 | try { 136 | handlers.add(handler); 137 | } finally { 138 | lock.unlock(); 139 | } 140 | } 141 | 142 | public void removeHandler(AbstractPacketHandler handler) { 143 | Lock lock = locks.writeLock(); 144 | lock.lock(); 145 | 146 | try { 147 | handlers.remove(handler); 148 | } finally { 149 | lock.unlock(); 150 | } 151 | } 152 | 153 | } 154 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn ( ) { 37 | echo "$*" 38 | } 39 | 40 | die ( ) { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /src/main/java/lol/bai/badpackets/impl/handler/AbstractPacketHandler.java: -------------------------------------------------------------------------------- 1 | package lol.bai.badpackets.impl.handler; 2 | 3 | import io.netty.buffer.Unpooled; 4 | import io.netty.channel.ChannelFutureListener; 5 | import lol.bai.badpackets.api.PacketReceiver; 6 | import lol.bai.badpackets.api.PacketSender; 7 | import lol.bai.badpackets.impl.Constants; 8 | import lol.bai.badpackets.impl.payload.UntypedPayload; 9 | import lol.bai.badpackets.impl.platform.PlatformProxy; 10 | import lol.bai.badpackets.impl.registry.ChannelRegistry; 11 | import net.minecraft.network.Connection; 12 | import net.minecraft.network.FriendlyByteBuf; 13 | import net.minecraft.network.protocol.Packet; 14 | import net.minecraft.network.protocol.common.custom.CustomPacketPayload; 15 | import net.minecraft.resources.Identifier; 16 | import net.minecraft.util.thread.BlockableEventLoop; 17 | import org.apache.logging.log4j.LogManager; 18 | import org.apache.logging.log4j.Logger; 19 | import org.jetbrains.annotations.Nullable; 20 | 21 | import java.nio.charset.StandardCharsets; 22 | import java.util.Collections; 23 | import java.util.HashSet; 24 | import java.util.List; 25 | import java.util.Map; 26 | import java.util.Set; 27 | import java.util.function.Consumer; 28 | import java.util.function.Function; 29 | import java.util.stream.Collectors; 30 | 31 | public abstract class AbstractPacketHandler implements PacketSender { 32 | 33 | protected final ChannelRegistry registry; 34 | protected final Logger logger; 35 | 36 | private final Function> packetFactory; 37 | private final Set sendableChannels = Collections.synchronizedSet(new HashSet<>()); 38 | 39 | private final BlockableEventLoop eventLoop; 40 | private final Connection connection; 41 | 42 | private boolean initialized = false; 43 | 44 | protected AbstractPacketHandler(String desc, ChannelRegistry registry, Function> packetFactory, BlockableEventLoop eventLoop, Connection connection) { 45 | this.logger = LogManager.getLogger(desc); 46 | this.registry = registry; 47 | this.packetFactory = packetFactory; 48 | this.eventLoop = eventLoop; 49 | this.connection = connection; 50 | 51 | registry.addHandler(this); 52 | } 53 | 54 | private void receiveChannelSyncPacket(FriendlyByteBuf buf) { 55 | switch (buf.readByte()) { 56 | case Constants.CHANNEL_SYNC_SINGLE -> sendableChannels.add(buf.readIdentifier()); 57 | case Constants.CHANNEL_SYNC_INITIAL -> { 58 | int groupSize = buf.readVarInt(); 59 | for (int i = 0; i < groupSize; i++) { 60 | String namespace = buf.readUtf(); 61 | 62 | int pathSize = buf.readVarInt(); 63 | for (int j = 0; j < pathSize; j++) { 64 | String path = buf.readUtf(); 65 | sendableChannels.add(Identifier.fromNamespaceAndPath(namespace, path)); 66 | } 67 | } 68 | 69 | eventLoop.execute(this::onInitialChannelSyncPacketReceived); 70 | } 71 | } 72 | } 73 | 74 | public boolean receive(CustomPacketPayload payload) { 75 | Identifier id = payload.type().id(); 76 | 77 | if (id.equals(Constants.CHANNEL_SYNC)) { 78 | UntypedPayload untyped = (UntypedPayload) payload; 79 | receiveChannelSyncPacket(untyped.buffer()); 80 | return true; 81 | } 82 | 83 | if (registry.has(id)) { 84 | try { 85 | PacketReceiver receiver = registry.get(id); 86 | 87 | if (payload instanceof UntypedPayload || eventLoop.isSameThread()) { 88 | receiveUnsafe(receiver, payload); 89 | } else eventLoop.execute(() -> { 90 | if (connection.isConnected()) receiveUnsafe(receiver, payload); 91 | }); 92 | } catch (Throwable t) { 93 | logger.error("Error when receiving packet {}", id, t); 94 | throw t; 95 | } 96 | return true; 97 | } 98 | 99 | return false; 100 | } 101 | 102 | protected abstract void onInitialChannelSyncPacketReceived(); 103 | 104 | protected abstract void receiveUnsafe(PacketReceiver receiver, CustomPacketPayload payload); 105 | 106 | public void sendInitialChannelSyncPacket() { 107 | if (!initialized) { 108 | initialized = true; 109 | sendVanillaChannelRegisterPacket(Set.of(Constants.CHANNEL_SYNC)); 110 | 111 | Set channels = registry.getChannels(); 112 | FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer()); 113 | buf.writeByte(Constants.CHANNEL_SYNC_INITIAL); 114 | 115 | Map> group = channels.stream().collect(Collectors.groupingBy(Identifier::getNamespace)); 116 | buf.writeVarInt(group.size()); 117 | 118 | for (Map.Entry> entry : group.entrySet()) { 119 | buf.writeUtf(entry.getKey()); 120 | buf.writeVarInt(entry.getValue().size()); 121 | 122 | for (Identifier value : entry.getValue()) { 123 | buf.writeUtf(value.getPath()); 124 | } 125 | } 126 | 127 | send(Constants.CHANNEL_SYNC, buf); 128 | sendVanillaChannelRegisterPacket(channels); 129 | } 130 | } 131 | 132 | public void onRegister(Identifier id) { 133 | FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer()); 134 | buf.writeByte(Constants.CHANNEL_SYNC_SINGLE); 135 | buf.writeIdentifier(id); 136 | send(Constants.CHANNEL_SYNC, buf); 137 | sendVanillaChannelRegisterPacket(Set.of(id)); 138 | } 139 | 140 | public void remove() { 141 | registry.removeHandler(this); 142 | } 143 | 144 | private void sendVanillaChannelRegisterPacket(Set channels) { 145 | if (PlatformProxy.INSTANCE.canSendVanillaRegisterPackets() && !channels.isEmpty()) { 146 | connection.send(createVanillaRegisterPacket(channels, buf -> { 147 | boolean first = true; 148 | for (Identifier channel : channels) { 149 | if (first) { 150 | first = false; 151 | } else { 152 | buf.writeByte(0); 153 | } 154 | buf.writeBytes(channel.toString().getBytes(StandardCharsets.US_ASCII)); 155 | } 156 | })); 157 | } 158 | } 159 | 160 | protected abstract Packet createVanillaRegisterPacket(Set channels, Consumer buf); 161 | 162 | @Override 163 | public void send(CustomPacketPayload payload, @Nullable ChannelFutureListener callback) { 164 | connection.send(packetFactory.apply(payload), callback); 165 | } 166 | 167 | @Override 168 | public boolean canSend(Identifier id) { 169 | return sendableChannels.contains(id); 170 | } 171 | 172 | } 173 | -------------------------------------------------------------------------------- /src/main/java/lol/bai/badpackets/api/play/PlayPackets.java: -------------------------------------------------------------------------------- 1 | package lol.bai.badpackets.api.play; 2 | 3 | import lol.bai.badpackets.api.PacketReadyCallback; 4 | import lol.bai.badpackets.api.PacketReceiver; 5 | import lol.bai.badpackets.api.PacketSender; 6 | import lol.bai.badpackets.impl.marker.ApiSide; 7 | import lol.bai.badpackets.impl.payload.UntypedPayload; 8 | import lol.bai.badpackets.impl.registry.CallbackRegistry; 9 | import lol.bai.badpackets.impl.registry.ChannelRegistry; 10 | import net.minecraft.client.Minecraft; 11 | import net.minecraft.network.FriendlyByteBuf; 12 | import net.minecraft.network.RegistryFriendlyByteBuf; 13 | import net.minecraft.network.codec.StreamCodec; 14 | import net.minecraft.network.protocol.common.custom.CustomPacketPayload; 15 | import net.minecraft.resources.Identifier; 16 | import net.minecraft.server.MinecraftServer; 17 | 18 | /** 19 | * Utility for working with play packets. 20 | */ 21 | public final class PlayPackets { 22 | 23 | /** 24 | * Register a client-to-server packet channel. 25 | *

26 | * This method needs to be called on all sides. 27 | *

28 | * Register the receiver on server side with {@link #registerServerReceiver(Identifier, PacketReceiver)} 29 | * 30 | * @param id the packet id 31 | */ 32 | public static void registerServerChannel(Identifier id) { 33 | ChannelRegistry.PLAY_C2S.registerCodec(id, UntypedPayload.codec(id)); 34 | } 35 | 36 | /** 37 | * Register a client-to-server packet channel. 38 | *

39 | * This method needs to be called on all sides. 40 | *

41 | * Register the receiver on server side with {@link #registerServerReceiver(CustomPacketPayload.Type, PacketReceiver)} 42 | * 43 | * @param type the {@linkplain CustomPacketPayload#type() packet type} 44 | * @param codec the payload codec 45 | */ 46 | public static

void registerServerChannel(CustomPacketPayload.Type

type, StreamCodec codec) { 47 | ChannelRegistry.PLAY_C2S.registerCodec(type.id(), codec); 48 | } 49 | 50 | /** 51 | * Register a server-to-client packet receiver. 52 | *

53 | * The channel needs to be {@linkplain #registerServerChannel(Identifier) registered} first. 54 | *

55 | * Raw packet receiver is run on Netty event-loop. Read the buffer on it and run 56 | * the operation on {@linkplain MinecraftServer#execute(Runnable) server thread}. 57 | * 58 | * @param id the packet id 59 | * @param receiver the receiver 60 | */ 61 | public static void registerServerReceiver(Identifier id, PacketReceiver receiver) { 62 | ChannelRegistry.PLAY_C2S.registerReceiver(id, (context, payload) -> receiver.receive(context, ((UntypedPayload) payload).buffer())); 63 | } 64 | 65 | /** 66 | * Register a client-to-server packet receiver. 67 | *

68 | * The channel needs to be {@linkplain #registerServerChannel(CustomPacketPayload.Type, StreamCodec) registered} first. 69 | *

70 | * Typed packet receiver is run on the main server thread. 71 | * 72 | * @param type the {@linkplain CustomPacketPayload#type() packet type} 73 | * @param receiver the receiver 74 | */ 75 | @SuppressWarnings("unchecked") 76 | public static

void registerServerReceiver(CustomPacketPayload.Type

type, PacketReceiver receiver) { 77 | ChannelRegistry.PLAY_C2S.registerReceiver(type.id(), (PacketReceiver) receiver); 78 | } 79 | 80 | /** 81 | * Register a callback that will be called when player that has Bad Packets joined. 82 | *

83 | * This is the first point that {@link PacketSender#canSend} will behave properly. 84 | *

85 | * Not a general-purpose player join callback, use platform specific API for that. 86 | */ 87 | public static void registerServerReadyCallback(PacketReadyCallback callback) { 88 | CallbackRegistry.SERVER_PLAY.add(callback); 89 | } 90 | 91 | /** 92 | * Register a server-to-client packet channel. 93 | *

94 | * This method needs to be called on all sides. 95 | *

96 | * Register the receiver on client side with {@link #registerClientReceiver(Identifier, PacketReceiver)} 97 | * 98 | * @param id the packet id 99 | */ 100 | public static void registerClientChannel(Identifier id) { 101 | ChannelRegistry.PLAY_S2C.registerCodec(id, UntypedPayload.codec(id)); 102 | } 103 | 104 | /** 105 | * Register a server-to-client packet channel. 106 | *

107 | * This method needs to be called on all sides. 108 | *

109 | * Register the receiver on client side with {@link #registerClientReceiver(CustomPacketPayload.Type, PacketReceiver)} 110 | * 111 | * @param type the {@linkplain CustomPacketPayload#type() packet type} 112 | * @param codec the payload codec 113 | */ 114 | public static

void registerClientChannel(CustomPacketPayload.Type

type, StreamCodec codec) { 115 | ChannelRegistry.PLAY_S2C.registerCodec(type.id(), codec); 116 | } 117 | 118 | /** 119 | * Register a server-to-client packet receiver. 120 | *

121 | * The channel needs to be {@linkplain #registerClientChannel(Identifier) registered} first. 122 | *

123 | * Raw packet receiver is run on Netty event-loop. Read the buffer on it and run 124 | * the operation on {@linkplain Minecraft#execute(Runnable) client thread}. 125 | * 126 | * @param id the packet id 127 | * @param receiver the receiver 128 | */ 129 | @ApiSide.ClientOnly 130 | public static void registerClientReceiver(Identifier id, PacketReceiver receiver) { 131 | ChannelRegistry.PLAY_S2C.registerReceiver(id, (context, payload) -> receiver.receive(context, ((UntypedPayload) payload).buffer())); 132 | } 133 | 134 | /** 135 | * Register a server-to-client packet receiver. 136 | *

137 | * The channel needs to be {@linkplain #registerClientChannel(CustomPacketPayload.Type, StreamCodec) registered} first. 138 | *

139 | * Typed packet receiver is run on the main client thread. 140 | * 141 | * @param type the {@linkplain CustomPacketPayload#type() packet type} 142 | * @param receiver the receiver 143 | */ 144 | @ApiSide.ClientOnly 145 | @SuppressWarnings("unchecked") 146 | public static

void registerClientReceiver(CustomPacketPayload.Type

type, PacketReceiver receiver) { 147 | ChannelRegistry.PLAY_S2C.registerReceiver(type.id(), (PacketReceiver) receiver); 148 | } 149 | 150 | /** 151 | * Register a callback that will be called when the client joined a server that has Bad Packets installed. 152 | *

153 | * This is the first point that {@link PacketSender#canSend} will behave properly. 154 | *

155 | * Not a general-purpose player join callback, use platform specific API for that. 156 | */ 157 | @ApiSide.ClientOnly 158 | public static void registerClientReadyCallback(PacketReadyCallback callback) { 159 | CallbackRegistry.CLIENT_PLAY.add(callback); 160 | } 161 | 162 | private PlayPackets() { 163 | } 164 | 165 | } 166 | -------------------------------------------------------------------------------- /src/testmod/java/lol/bai/badpackets/test/BadPacketTest.java: -------------------------------------------------------------------------------- 1 | package lol.bai.badpackets.test; 2 | 3 | import io.netty.buffer.Unpooled; 4 | import lol.bai.badpackets.api.config.ConfigPackets; 5 | import lol.bai.badpackets.api.play.PlayPackets; 6 | import lol.bai.badpackets.impl.Constants; 7 | import net.minecraft.network.FriendlyByteBuf; 8 | import net.minecraft.network.chat.Component; 9 | import net.minecraft.resources.Identifier; 10 | import org.apache.commons.lang3.Validate; 11 | import org.slf4j.Logger; 12 | import org.slf4j.LoggerFactory; 13 | 14 | public class BadPacketTest { 15 | 16 | public static final Identifier CONFIG_TASK = Constants.id("test/config/task"); 17 | 18 | public static final Identifier CONFIG_C2S = Constants.id("test/config/c2s"); 19 | public static final Identifier CONFIG_S2C = Constants.id("test/config/s2c"); 20 | 21 | public static final Identifier PLAY_C2S = Constants.id("test/play/c2s"); 22 | public static final Identifier PLAY_S2C = Constants.id("test/play/s2c"); 23 | 24 | public static final Logger LOGGER = LoggerFactory.getLogger(BadPacketTest.class); 25 | 26 | public static void common() { 27 | // TASK -------------------------------------------------------------------------------------------------------- 28 | 29 | ConfigPackets.registerServerChannel(TestTaskPayload.TYPE, TestTaskPayload.CODEC); 30 | ConfigPackets.registerClientChannel(TestTaskPayload.TYPE, TestTaskPayload.CODEC); 31 | 32 | ConfigPackets.registerTask(CONFIG_TASK, context -> { 33 | if (context.canSend(TestTaskPayload.TYPE)) { 34 | context.send(new TestTaskPayload(TestTaskPayload.Stage.QUESTION_1)); 35 | return true; 36 | } 37 | 38 | return false; 39 | }); 40 | 41 | ConfigPackets.registerServerReceiver(TestTaskPayload.TYPE, (context, payload) -> { 42 | LOGGER.info("[config task] client -> server " + payload.stage().name()); 43 | 44 | switch (payload.stage()) { 45 | case ANSWER_1 -> context.send(new TestTaskPayload(TestTaskPayload.Stage.QUESTION_2)); 46 | case ANSWER_2 -> context.send(new TestTaskPayload(TestTaskPayload.Stage.QUESTION_3)); 47 | case ANSWER_3 -> context.finishTask(CONFIG_TASK); 48 | default -> context.handler().disconnect(Component.literal("invalid stage")); 49 | } 50 | }); 51 | 52 | // CONFIG ------------------------------------------------------------------------------------------------------ 53 | 54 | ConfigPackets.registerServerChannel(CONFIG_C2S); 55 | ConfigPackets.registerClientChannel(CONFIG_S2C); 56 | ConfigPackets.registerServerChannel(TestConfigPayload.TYPE, TestConfigPayload.CODEC); 57 | ConfigPackets.registerClientChannel(TestConfigPayload.TYPE, TestConfigPayload.CODEC); 58 | 59 | ConfigPackets.registerServerReceiver(CONFIG_C2S, (context, buf) -> 60 | LOGGER.info(buf.readUtf())); 61 | 62 | ConfigPackets.registerServerReceiver(TestConfigPayload.TYPE, (context, payload) -> 63 | LOGGER.info(payload.msg())); 64 | 65 | ConfigPackets.registerServerReadyCallback(context -> { 66 | Validate.isTrue(context.canSend(BadPacketTest.CONFIG_S2C)); 67 | Validate.isTrue(context.canSend(TestConfigPayload.TYPE)); 68 | 69 | FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer()); 70 | buf.writeUtf("[config untyped] server -> client"); 71 | context.send(BadPacketTest.CONFIG_S2C, buf); 72 | 73 | context.send(new TestConfigPayload("[config typed] server -> client")); 74 | }); 75 | 76 | // PLAY -------------------------------------------------------------------------------------------------------- 77 | 78 | PlayPackets.registerServerChannel(PLAY_C2S); 79 | PlayPackets.registerClientChannel(PLAY_S2C); 80 | PlayPackets.registerServerChannel(TestPlayPayload.TYPE, TestPlayPayload.CODEC); 81 | PlayPackets.registerClientChannel(TestPlayPayload.TYPE, TestPlayPayload.CODEC); 82 | 83 | PlayPackets.registerServerReceiver(PLAY_C2S, (context, buf) -> 84 | LOGGER.info(buf.readUtf())); 85 | 86 | PlayPackets.registerServerReceiver(TestPlayPayload.TYPE, (context, payload) -> 87 | LOGGER.info(payload.msg())); 88 | 89 | PlayPackets.registerServerReadyCallback(context -> { 90 | Validate.isTrue(context.canSend(BadPacketTest.PLAY_S2C)); 91 | Validate.isTrue(context.canSend(TestPlayPayload.TYPE)); 92 | 93 | FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer()); 94 | buf.writeUtf("[play untyped] server -> client"); 95 | context.send(BadPacketTest.PLAY_S2C, buf); 96 | 97 | context.send(new TestPlayPayload("[play typed] server -> client")); 98 | }); 99 | 100 | } 101 | 102 | public static void client() { 103 | // TASK -------------------------------------------------------------------------------------------------------- 104 | 105 | ConfigPackets.registerClientReceiver(TestTaskPayload.TYPE, (context, payload) -> { 106 | LOGGER.info("[config task] server -> client " + payload.stage().name()); 107 | 108 | switch (payload.stage()) { 109 | case QUESTION_1 -> context.send(new TestTaskPayload(TestTaskPayload.Stage.ANSWER_1)); 110 | case QUESTION_2 -> context.send(new TestTaskPayload(TestTaskPayload.Stage.ANSWER_2)); 111 | case QUESTION_3 -> context.send(new TestTaskPayload(TestTaskPayload.Stage.ANSWER_3)); 112 | default -> context.disconnect(Component.literal("invalid stage")); 113 | } 114 | }); 115 | 116 | // CONFIG ------------------------------------------------------------------------------------------------------ 117 | 118 | ConfigPackets.registerClientReceiver(CONFIG_S2C, (context, buf) -> 119 | LOGGER.info(buf.readUtf())); 120 | 121 | ConfigPackets.registerClientReceiver(TestConfigPayload.TYPE, (context, payload) -> 122 | LOGGER.info(payload.msg())); 123 | 124 | ConfigPackets.registerClientReadyCallback(context -> { 125 | Validate.isTrue(context.canSend(BadPacketTest.CONFIG_C2S)); 126 | Validate.isTrue(context.canSend(TestConfigPayload.TYPE)); 127 | 128 | FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer()); 129 | buf.writeUtf("[config untyped] client -> server"); 130 | context.send(BadPacketTest.CONFIG_C2S, buf); 131 | 132 | context.send(new TestConfigPayload("[config typed] client -> server")); 133 | }); 134 | 135 | // PLAY -------------------------------------------------------------------------------------------------------- 136 | 137 | PlayPackets.registerClientReceiver(PLAY_S2C, (context, buf) -> 138 | LOGGER.info(buf.readUtf())); 139 | 140 | PlayPackets.registerClientReceiver(TestPlayPayload.TYPE, (context, payload) -> 141 | LOGGER.info(payload.msg())); 142 | 143 | PlayPackets.registerClientReadyCallback(context -> { 144 | Validate.isTrue(context.canSend(BadPacketTest.PLAY_C2S)); 145 | Validate.isTrue(context.canSend(TestPlayPayload.TYPE)); 146 | 147 | FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer()); 148 | buf.writeUtf("[play untyped] client -> server"); 149 | context.send(BadPacketTest.PLAY_C2S, buf); 150 | 151 | context.send(new TestPlayPayload("[play typed] client -> server")); 152 | }); 153 | } 154 | 155 | } 156 | -------------------------------------------------------------------------------- /src/main/java/lol/bai/badpackets/api/config/ConfigPackets.java: -------------------------------------------------------------------------------- 1 | package lol.bai.badpackets.api.config; 2 | 3 | import lol.bai.badpackets.api.PacketReadyCallback; 4 | import lol.bai.badpackets.api.PacketReceiver; 5 | import lol.bai.badpackets.api.PacketSender; 6 | import lol.bai.badpackets.impl.handler.ServerConfigPacketHandler; 7 | import lol.bai.badpackets.impl.marker.ApiSide; 8 | import lol.bai.badpackets.impl.mixin.client.AccessClientCommonPacketListenerImpl; 9 | import lol.bai.badpackets.impl.payload.UntypedPayload; 10 | import lol.bai.badpackets.impl.registry.CallbackRegistry; 11 | import lol.bai.badpackets.impl.registry.ChannelRegistry; 12 | import net.minecraft.client.Minecraft; 13 | import net.minecraft.client.multiplayer.ClientConfigurationPacketListenerImpl; 14 | import net.minecraft.network.FriendlyByteBuf; 15 | import net.minecraft.network.chat.Component; 16 | import net.minecraft.network.codec.StreamCodec; 17 | import net.minecraft.network.protocol.common.custom.CustomPacketPayload; 18 | import net.minecraft.resources.Identifier; 19 | import net.minecraft.server.MinecraftServer; 20 | 21 | /** 22 | * Utility for working with configuration packets. 23 | */ 24 | public final class ConfigPackets { 25 | 26 | /** 27 | * Register a configuration task. 28 | * 29 | * @param id the task id 30 | * @param executor the task executor 31 | * 32 | * @see ConfigTaskExecutor#runTask 33 | */ 34 | public static void registerTask(Identifier id, ConfigTaskExecutor executor) { 35 | ServerConfigPacketHandler.registerTask(id, executor); 36 | } 37 | 38 | /** 39 | * Register a client-to-server packet channel. 40 | *

41 | * This method needs to be called on all sides. 42 | *

43 | * Register the receiver on server side with {@link #registerServerReceiver(Identifier, PacketReceiver)} 44 | * 45 | * @param id the packet id 46 | */ 47 | public static void registerServerChannel(Identifier id) { 48 | ChannelRegistry.CONFIG_C2S.registerCodec(id, UntypedPayload.codec(id)); 49 | } 50 | 51 | /** 52 | * Register a client-to-server packet channel. 53 | *

54 | * This method needs to be called on all sides. 55 | *

56 | * Register the receiver on server side with {@link #registerServerReceiver(CustomPacketPayload.Type, PacketReceiver)} 57 | * 58 | * @param type the {@linkplain CustomPacketPayload#type() packet type} 59 | * @param codec the payload codec 60 | */ 61 | public static

void registerServerChannel(CustomPacketPayload.Type

type, StreamCodec codec) { 62 | ChannelRegistry.CONFIG_C2S.registerCodec(type.id(), codec); 63 | } 64 | 65 | /** 66 | * Register a client-to-server packet receiver. 67 | *

68 | * The channel needs to be {@linkplain #registerServerChannel(Identifier) registered} first. 69 | *

70 | * Raw packet receiver is run on Netty event-loop. Read the buffer on it and run 71 | * the operation on {@linkplain MinecraftServer#execute(Runnable) client thread}. 72 | * 73 | * @param id the packet id 74 | * @param receiver the receiver 75 | */ 76 | public static void registerServerReceiver(Identifier id, PacketReceiver receiver) { 77 | ChannelRegistry.CONFIG_C2S.registerReceiver(id, (context, payload) -> receiver.receive(context, ((UntypedPayload) payload).buffer())); 78 | } 79 | 80 | /** 81 | * Register a client-to-server packet receiver. 82 | *

83 | * The channel needs to be {@linkplain #registerServerChannel(CustomPacketPayload.Type, StreamCodec) registered} first. 84 | *

85 | * Typed packet receiver is run on the main server thread. 86 | * 87 | * @param type the {@linkplain CustomPacketPayload#type() packet type} 88 | * @param receiver the receiver 89 | */ 90 | @SuppressWarnings("unchecked") 91 | public static

void registerServerReceiver(CustomPacketPayload.Type

type, PacketReceiver receiver) { 92 | ChannelRegistry.CONFIG_C2S.registerReceiver(type.id(), (PacketReceiver) receiver); 93 | } 94 | 95 | /** 96 | * Register a callback that will be called when player that has Bad Packets joined. 97 | *

98 | * This is the first point that {@link PacketSender#canSend} will behave properly. 99 | *

100 | * Not a general-purpose player join callback, use platform specific API for that. 101 | */ 102 | public static void registerServerReadyCallback(PacketReadyCallback callback) { 103 | CallbackRegistry.SERVER_READY_CONFIG.add(callback); 104 | } 105 | 106 | /** 107 | * Register a server-to-client packet channel. 108 | *

109 | * This method needs to be called on all sides. 110 | *

111 | * Register the receiver on client side with {@link #registerClientReceiver(Identifier, PacketReceiver)} 112 | * 113 | * @param id the packet id 114 | */ 115 | public static void registerClientChannel(Identifier id) { 116 | ChannelRegistry.CONFIG_S2C.registerCodec(id, UntypedPayload.codec(id)); 117 | } 118 | 119 | /** 120 | * Register a server-to-client packet channel. 121 | *

122 | * This method needs to be called on all sides. 123 | *

124 | * Register the receiver on client side with {@link #registerClientReceiver(CustomPacketPayload.Type, PacketReceiver)} 125 | * 126 | * @param type the {@linkplain CustomPacketPayload#type() packet type} 127 | * @param codec the payload codec 128 | */ 129 | public static

void registerClientChannel(CustomPacketPayload.Type

type, StreamCodec codec) { 130 | ChannelRegistry.CONFIG_S2C.registerCodec(type.id(), codec); 131 | } 132 | 133 | /** 134 | * Register a server-to-client packet receiver. 135 | *

136 | * The channel needs to be {@linkplain #registerClientChannel(Identifier) registered} first. 137 | *

138 | * Raw packet receiver is run on Netty event-loop. Read the buffer on it and run 139 | * the operation on {@linkplain Minecraft#execute(Runnable) client thread}. 140 | * 141 | * @param id the packet id 142 | * @param receiver the receiver 143 | */ 144 | @ApiSide.ClientOnly 145 | public static void registerClientReceiver(Identifier id, PacketReceiver receiver) { 146 | ChannelRegistry.CONFIG_S2C.registerReceiver(id, (context, payload) -> receiver.receive(context, ((UntypedPayload) payload).buffer())); 147 | } 148 | 149 | /** 150 | * Register a server-to-client packet receiver. 151 | *

152 | * The channel needs to be {@linkplain #registerClientChannel(CustomPacketPayload.Type, StreamCodec) registered} first. 153 | *

154 | * Typed packet receiver is run on the main client thread. 155 | * 156 | * @param type the {@linkplain CustomPacketPayload#type() packet type} 157 | * @param receiver the receiver 158 | */ 159 | @ApiSide.ClientOnly 160 | @SuppressWarnings("unchecked") 161 | public static

void registerClientReceiver(CustomPacketPayload.Type

type, PacketReceiver receiver) { 162 | ChannelRegistry.CONFIG_S2C.registerReceiver(type.id(), (PacketReceiver) receiver); 163 | } 164 | 165 | /** 166 | * Register a callback that will be called when the client joined a server that has Bad Packets installed. 167 | *

168 | * This is the first point that {@link PacketSender#canSend} will behave properly. 169 | *

170 | * Not a general-purpose player join callback, use platform specific API for that. 171 | * 172 | * @see #disconnect 173 | */ 174 | @ApiSide.ClientOnly 175 | public static void registerClientReadyCallback(PacketReadyCallback callback) { 176 | CallbackRegistry.CLIENT_READY_CONFIG.add(callback); 177 | } 178 | 179 | /** 180 | * Helper method to disconnect client-to-server connection. 181 | * 182 | * @param handler the handler instance 183 | * @param reason the disconnection reason 184 | */ 185 | @ApiSide.ClientOnly 186 | public static void disconnect(ClientConfigurationPacketListenerImpl handler, Component reason) { 187 | ((AccessClientCommonPacketListenerImpl) handler).badpackets_connection().disconnect(reason); 188 | } 189 | 190 | private ConfigPackets() { 191 | } 192 | 193 | } 194 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | [*] 2 | charset = utf-8 3 | end_of_line = crlf 4 | indent_size = 4 5 | indent_style = space 6 | insert_final_newline = true 7 | max_line_length = 150 8 | tab_width = 4 9 | ij_continuation_indent_size = 8 10 | ij_formatter_off_tag = @formatter:off 11 | ij_formatter_on_tag = @formatter:on 12 | ij_formatter_tags_enabled = true 13 | ij_smart_tabs = false 14 | ij_wrap_on_typing = false 15 | 16 | [*.java] 17 | ij_continuation_indent_size = 4 18 | ij_java_align_consecutive_assignments = false 19 | ij_java_align_consecutive_variable_declarations = false 20 | ij_java_align_group_field_declarations = false 21 | ij_java_align_multiline_annotation_parameters = false 22 | ij_java_align_multiline_array_initializer_expression = false 23 | ij_java_align_multiline_assignment = false 24 | ij_java_align_multiline_binary_operation = false 25 | ij_java_align_multiline_chained_methods = false 26 | ij_java_align_multiline_extends_list = false 27 | ij_java_align_multiline_for = true 28 | ij_java_align_multiline_method_parentheses = false 29 | ij_java_align_multiline_parameters = true 30 | ij_java_align_multiline_parameters_in_calls = false 31 | ij_java_align_multiline_parenthesized_expression = false 32 | ij_java_align_multiline_records = true 33 | ij_java_align_multiline_resources = true 34 | ij_java_align_multiline_ternary_operation = false 35 | ij_java_align_multiline_text_blocks = false 36 | ij_java_align_multiline_throws_list = false 37 | ij_java_align_subsequent_simple_methods = false 38 | ij_java_align_throws_keyword = false 39 | ij_java_annotation_parameter_wrap = off 40 | ij_java_array_initializer_new_line_after_left_brace = false 41 | ij_java_array_initializer_right_brace_on_new_line = false 42 | ij_java_array_initializer_wrap = off 43 | ij_java_assert_statement_colon_on_next_line = false 44 | ij_java_assert_statement_wrap = off 45 | ij_java_assignment_wrap = off 46 | ij_java_binary_operation_sign_on_next_line = false 47 | ij_java_binary_operation_wrap = off 48 | ij_java_blank_lines_after_anonymous_class_header = 0 49 | ij_java_blank_lines_after_class_header = 1 50 | ij_java_blank_lines_after_imports = 1 51 | ij_java_blank_lines_after_package = 1 52 | ij_java_blank_lines_around_class = 1 53 | ij_java_blank_lines_around_field = 0 54 | ij_java_blank_lines_around_field_in_interface = 0 55 | ij_java_blank_lines_around_initializer = 1 56 | ij_java_blank_lines_around_method = 1 57 | ij_java_blank_lines_around_method_in_interface = 1 58 | ij_java_blank_lines_before_class_end = 1 59 | ij_java_blank_lines_before_imports = 1 60 | ij_java_blank_lines_before_method_body = 0 61 | ij_java_blank_lines_before_package = 0 62 | ij_java_block_brace_style = end_of_line 63 | ij_java_block_comment_at_first_column = true 64 | ij_java_call_parameters_new_line_after_left_paren = false 65 | ij_java_call_parameters_right_paren_on_new_line = false 66 | ij_java_call_parameters_wrap = off 67 | ij_java_case_statement_on_separate_line = true 68 | ij_java_catch_on_new_line = false 69 | ij_java_class_annotation_wrap = split_into_lines 70 | ij_java_class_brace_style = end_of_line 71 | ij_java_class_count_to_use_import_on_demand = 9999 72 | ij_java_class_names_in_javadoc = 3 73 | ij_java_do_not_indent_top_level_class_members = false 74 | ij_java_do_not_wrap_after_single_annotation = false 75 | ij_java_do_while_brace_force = never 76 | ij_java_doc_add_blank_line_after_description = true 77 | ij_java_doc_add_blank_line_after_param_comments = true 78 | ij_java_doc_add_blank_line_after_return = true 79 | ij_java_doc_add_p_tag_on_empty_lines = true 80 | ij_java_doc_align_exception_comments = true 81 | ij_java_doc_align_param_comments = true 82 | ij_java_doc_do_not_wrap_if_one_line = true 83 | ij_java_doc_enable_formatting = true 84 | ij_java_doc_enable_leading_asterisks = true 85 | ij_java_doc_indent_on_continuation = false 86 | ij_java_doc_keep_empty_lines = true 87 | ij_java_doc_keep_empty_parameter_tag = false 88 | ij_java_doc_keep_empty_return_tag = false 89 | ij_java_doc_keep_empty_throws_tag = false 90 | ij_java_doc_keep_invalid_tags = true 91 | ij_java_doc_param_description_on_new_line = false 92 | ij_java_doc_preserve_line_breaks = true 93 | ij_java_doc_use_throws_not_exception_tag = true 94 | ij_java_else_on_new_line = false 95 | ij_java_enum_constants_wrap = off 96 | ij_java_extends_keyword_wrap = off 97 | ij_java_extends_list_wrap = off 98 | ij_java_field_annotation_wrap = split_into_lines 99 | ij_java_finally_on_new_line = false 100 | ij_java_for_brace_force = never 101 | ij_java_for_statement_new_line_after_left_paren = false 102 | ij_java_for_statement_right_paren_on_new_line = false 103 | ij_java_for_statement_wrap = off 104 | ij_java_generate_final_locals = false 105 | ij_java_generate_final_parameters = false 106 | ij_java_if_brace_force = never 107 | # ij_java_imports_layout = java.**, javax.**, |, *, |, $* 108 | ij_java_indent_case_from_switch = true 109 | ij_java_insert_inner_class_imports = false 110 | ij_java_insert_override_annotation = true 111 | ij_java_keep_blank_lines_before_right_brace = 2 112 | ij_java_keep_blank_lines_between_package_declaration_and_header = 2 113 | ij_java_keep_blank_lines_in_code = 2 114 | ij_java_keep_blank_lines_in_declarations = 2 115 | ij_java_keep_control_statement_in_one_line = true 116 | ij_java_keep_first_column_comment = false 117 | ij_java_keep_indents_on_empty_lines = false 118 | ij_java_keep_line_breaks = true 119 | ij_java_keep_multiple_expressions_in_one_line = false 120 | ij_java_keep_simple_blocks_in_one_line = false 121 | ij_java_keep_simple_classes_in_one_line = false 122 | ij_java_keep_simple_lambdas_in_one_line = true 123 | ij_java_keep_simple_methods_in_one_line = false 124 | ij_java_label_indent_absolute = false 125 | ij_java_label_indent_size = 0 126 | ij_java_lambda_brace_style = end_of_line 127 | ij_java_layout_static_imports_separately = true 128 | ij_java_line_comment_add_space = false 129 | ij_java_line_comment_at_first_column = true 130 | ij_java_method_annotation_wrap = split_into_lines 131 | ij_java_method_brace_style = end_of_line 132 | ij_java_method_call_chain_wrap = off 133 | ij_java_method_parameters_new_line_after_left_paren = false 134 | ij_java_method_parameters_right_paren_on_new_line = false 135 | ij_java_method_parameters_wrap = off 136 | ij_java_modifier_list_wrap = false 137 | ij_java_names_count_to_use_import_on_demand = 9999 138 | ij_java_new_line_after_lparen_in_record_header = false 139 | ij_java_parameter_annotation_wrap = off 140 | ij_java_parentheses_expression_new_line_after_left_paren = false 141 | ij_java_parentheses_expression_right_paren_on_new_line = false 142 | ij_java_place_assignment_sign_on_next_line = false 143 | ij_java_prefer_longer_names = true 144 | ij_java_prefer_parameters_wrap = false 145 | ij_java_record_components_wrap = normal 146 | ij_java_repeat_synchronized = true 147 | ij_java_replace_instanceof_and_cast = false 148 | ij_java_replace_null_check = true 149 | ij_java_replace_sum_lambda_with_method_ref = true 150 | ij_java_resource_list_new_line_after_left_paren = false 151 | ij_java_resource_list_right_paren_on_new_line = false 152 | ij_java_resource_list_wrap = off 153 | ij_java_rparen_on_new_line_in_record_header = false 154 | ij_java_space_after_closing_angle_bracket_in_type_argument = false 155 | ij_java_space_after_colon = true 156 | ij_java_space_after_comma = true 157 | ij_java_space_after_comma_in_type_arguments = true 158 | ij_java_space_after_for_semicolon = true 159 | ij_java_space_after_quest = true 160 | ij_java_space_after_type_cast = true 161 | ij_java_space_before_annotation_array_initializer_left_brace = false 162 | ij_java_space_before_annotation_parameter_list = false 163 | ij_java_space_before_array_initializer_left_brace = false 164 | ij_java_space_before_catch_keyword = true 165 | ij_java_space_before_catch_left_brace = true 166 | ij_java_space_before_catch_parentheses = true 167 | ij_java_space_before_class_left_brace = true 168 | ij_java_space_before_colon = true 169 | ij_java_space_before_colon_in_foreach = true 170 | ij_java_space_before_comma = false 171 | ij_java_space_before_do_left_brace = true 172 | ij_java_space_before_else_keyword = true 173 | ij_java_space_before_else_left_brace = true 174 | ij_java_space_before_finally_keyword = true 175 | ij_java_space_before_finally_left_brace = true 176 | ij_java_space_before_for_left_brace = true 177 | ij_java_space_before_for_parentheses = true 178 | ij_java_space_before_for_semicolon = false 179 | ij_java_space_before_if_left_brace = true 180 | ij_java_space_before_if_parentheses = true 181 | ij_java_space_before_method_call_parentheses = false 182 | ij_java_space_before_method_left_brace = true 183 | ij_java_space_before_method_parentheses = false 184 | ij_java_space_before_opening_angle_bracket_in_type_parameter = false 185 | ij_java_space_before_quest = true 186 | ij_java_space_before_switch_left_brace = true 187 | ij_java_space_before_switch_parentheses = true 188 | ij_java_space_before_synchronized_left_brace = true 189 | ij_java_space_before_synchronized_parentheses = true 190 | ij_java_space_before_try_left_brace = true 191 | ij_java_space_before_try_parentheses = true 192 | ij_java_space_before_type_parameter_list = false 193 | ij_java_space_before_while_keyword = true 194 | ij_java_space_before_while_left_brace = true 195 | ij_java_space_before_while_parentheses = true 196 | ij_java_space_inside_one_line_enum_braces = false 197 | ij_java_space_within_empty_array_initializer_braces = false 198 | ij_java_space_within_empty_method_call_parentheses = false 199 | ij_java_space_within_empty_method_parentheses = false 200 | ij_java_spaces_around_additive_operators = true 201 | ij_java_spaces_around_assignment_operators = true 202 | ij_java_spaces_around_bitwise_operators = true 203 | ij_java_spaces_around_equality_operators = true 204 | ij_java_spaces_around_lambda_arrow = true 205 | ij_java_spaces_around_logical_operators = true 206 | ij_java_spaces_around_method_ref_dbl_colon = false 207 | ij_java_spaces_around_multiplicative_operators = true 208 | ij_java_spaces_around_relational_operators = true 209 | ij_java_spaces_around_shift_operators = true 210 | ij_java_spaces_around_type_bounds_in_type_parameters = true 211 | ij_java_spaces_around_unary_operator = false 212 | ij_java_spaces_within_angle_brackets = false 213 | ij_java_spaces_within_annotation_parentheses = false 214 | ij_java_spaces_within_array_initializer_braces = false 215 | ij_java_spaces_within_braces = false 216 | ij_java_spaces_within_brackets = false 217 | ij_java_spaces_within_cast_parentheses = false 218 | ij_java_spaces_within_catch_parentheses = false 219 | ij_java_spaces_within_for_parentheses = false 220 | ij_java_spaces_within_if_parentheses = false 221 | ij_java_spaces_within_method_call_parentheses = false 222 | ij_java_spaces_within_method_parentheses = false 223 | ij_java_spaces_within_parentheses = false 224 | ij_java_spaces_within_switch_parentheses = false 225 | ij_java_spaces_within_synchronized_parentheses = false 226 | ij_java_spaces_within_try_parentheses = false 227 | ij_java_spaces_within_while_parentheses = false 228 | ij_java_special_else_if_treatment = true 229 | ij_java_subclass_name_suffix = Impl 230 | ij_java_ternary_operation_signs_on_next_line = false 231 | ij_java_ternary_operation_wrap = off 232 | ij_java_test_name_suffix = Test 233 | ij_java_throws_keyword_wrap = off 234 | ij_java_throws_list_wrap = off 235 | ij_java_use_external_annotations = false 236 | ij_java_use_fq_class_names = false 237 | ij_java_use_relative_indents = false 238 | ij_java_use_single_class_imports = true 239 | ij_java_variable_annotation_wrap = off 240 | ij_java_visibility = public 241 | ij_java_while_brace_force = never 242 | ij_java_while_on_new_line = false 243 | ij_java_wrap_comments = true 244 | ij_java_wrap_first_method_in_call_chain = false 245 | ij_java_wrap_long_lines = false 246 | 247 | [*.properties] 248 | ij_properties_align_group_field_declarations = true 249 | ij_properties_keep_blank_lines = true 250 | ij_properties_key_value_delimiter = equals 251 | ij_properties_spaces_around_key_value_delimiter = true 252 | 253 | [.editorconfig] 254 | ij_editorconfig_align_group_field_declarations = false 255 | ij_editorconfig_space_after_colon = false 256 | ij_editorconfig_space_after_comma = true 257 | ij_editorconfig_space_before_colon = false 258 | ij_editorconfig_space_before_comma = false 259 | ij_editorconfig_spaces_around_assignment_operators = true 260 | 261 | [{*.ant, *.fxml, *.jhm, *.jnlp, *.jrxml, *.jspx, *.pom, *.rng, *.tagx, *.tld, *.wsdl, *.xml, *.xsd, *.xsl, *.xslt, *.xul}] 262 | ij_xml_align_attributes = true 263 | ij_xml_align_text = false 264 | ij_xml_attribute_wrap = normal 265 | ij_xml_block_comment_at_first_column = true 266 | ij_xml_keep_blank_lines = 2 267 | ij_xml_keep_indents_on_empty_lines = false 268 | ij_xml_keep_line_breaks = true 269 | ij_xml_keep_line_breaks_in_text = true 270 | ij_xml_keep_whitespaces = false 271 | ij_xml_keep_whitespaces_around_cdata = preserve 272 | ij_xml_keep_whitespaces_inside_cdata = false 273 | ij_xml_line_comment_at_first_column = true 274 | ij_xml_space_after_tag_name = false 275 | ij_xml_space_around_equals_in_attribute = false 276 | ij_xml_space_inside_empty_tag = false 277 | ij_xml_text_wrap = normal 278 | ij_xml_use_custom_settings = false 279 | 280 | [{*.gant, *.gradle, *.groovy, *.gy}] 281 | ij_continuation_indent_size = 4 282 | ij_groovy_align_group_field_declarations = false 283 | ij_groovy_align_multiline_array_initializer_expression = false 284 | ij_groovy_align_multiline_assignment = false 285 | ij_groovy_align_multiline_binary_operation = false 286 | ij_groovy_align_multiline_chained_methods = false 287 | ij_groovy_align_multiline_extends_list = false 288 | ij_groovy_align_multiline_for = true 289 | ij_groovy_align_multiline_list_or_map = true 290 | ij_groovy_align_multiline_method_parentheses = false 291 | ij_groovy_align_multiline_parameters = true 292 | ij_groovy_align_multiline_parameters_in_calls = false 293 | ij_groovy_align_multiline_resources = true 294 | ij_groovy_align_multiline_ternary_operation = false 295 | ij_groovy_align_multiline_throws_list = false 296 | ij_groovy_align_named_args_in_map = true 297 | ij_groovy_align_throws_keyword = false 298 | ij_groovy_array_initializer_new_line_after_left_brace = false 299 | ij_groovy_array_initializer_right_brace_on_new_line = false 300 | ij_groovy_array_initializer_wrap = off 301 | ij_groovy_assert_statement_wrap = off 302 | ij_groovy_assignment_wrap = off 303 | ij_groovy_binary_operation_wrap = off 304 | ij_groovy_blank_lines_after_class_header = 0 305 | ij_groovy_blank_lines_after_imports = 1 306 | ij_groovy_blank_lines_after_package = 1 307 | ij_groovy_blank_lines_around_class = 1 308 | ij_groovy_blank_lines_around_field = 0 309 | ij_groovy_blank_lines_around_field_in_interface = 0 310 | ij_groovy_blank_lines_around_method = 1 311 | ij_groovy_blank_lines_around_method_in_interface = 1 312 | ij_groovy_blank_lines_before_imports = 1 313 | ij_groovy_blank_lines_before_method_body = 0 314 | ij_groovy_blank_lines_before_package = 0 315 | ij_groovy_block_brace_style = end_of_line 316 | ij_groovy_block_comment_at_first_column = true 317 | ij_groovy_call_parameters_new_line_after_left_paren = false 318 | ij_groovy_call_parameters_right_paren_on_new_line = false 319 | ij_groovy_call_parameters_wrap = off 320 | ij_groovy_catch_on_new_line = false 321 | ij_groovy_class_annotation_wrap = split_into_lines 322 | ij_groovy_class_brace_style = end_of_line 323 | ij_groovy_class_count_to_use_import_on_demand = 9999 324 | ij_groovy_do_while_brace_force = never 325 | ij_groovy_else_on_new_line = false 326 | ij_groovy_enum_constants_wrap = off 327 | ij_groovy_extends_keyword_wrap = off 328 | ij_groovy_extends_list_wrap = off 329 | ij_groovy_field_annotation_wrap = split_into_lines 330 | ij_groovy_finally_on_new_line = false 331 | ij_groovy_for_brace_force = never 332 | ij_groovy_for_statement_new_line_after_left_paren = false 333 | ij_groovy_for_statement_right_paren_on_new_line = false 334 | ij_groovy_for_statement_wrap = off 335 | ij_groovy_if_brace_force = never 336 | ij_groovy_import_annotation_wrap = 2 337 | ij_groovy_imports_layout = java.**, javax.**, |, *, |, $* 338 | ij_groovy_indent_case_from_switch = true 339 | ij_groovy_indent_label_blocks = true 340 | ij_groovy_insert_inner_class_imports = false 341 | ij_groovy_keep_blank_lines_before_right_brace = 2 342 | ij_groovy_keep_blank_lines_in_code = 2 343 | ij_groovy_keep_blank_lines_in_declarations = 2 344 | ij_groovy_keep_control_statement_in_one_line = true 345 | ij_groovy_keep_first_column_comment = true 346 | ij_groovy_keep_indents_on_empty_lines = false 347 | ij_groovy_keep_line_breaks = true 348 | ij_groovy_keep_multiple_expressions_in_one_line = false 349 | ij_groovy_keep_simple_blocks_in_one_line = false 350 | ij_groovy_keep_simple_classes_in_one_line = true 351 | ij_groovy_keep_simple_lambdas_in_one_line = true 352 | ij_groovy_keep_simple_methods_in_one_line = true 353 | ij_groovy_label_indent_absolute = false 354 | ij_groovy_label_indent_size = 0 355 | ij_groovy_lambda_brace_style = end_of_line 356 | ij_groovy_layout_static_imports_separately = true 357 | ij_groovy_line_comment_add_space = false 358 | ij_groovy_line_comment_at_first_column = true 359 | ij_groovy_method_annotation_wrap = split_into_lines 360 | ij_groovy_method_brace_style = end_of_line 361 | ij_groovy_method_call_chain_wrap = off 362 | ij_groovy_method_parameters_new_line_after_left_paren = false 363 | ij_groovy_method_parameters_right_paren_on_new_line = false 364 | ij_groovy_method_parameters_wrap = off 365 | ij_groovy_modifier_list_wrap = false 366 | ij_groovy_names_count_to_use_import_on_demand = 9999 367 | ij_groovy_parameter_annotation_wrap = off 368 | ij_groovy_parentheses_expression_new_line_after_left_paren = false 369 | ij_groovy_parentheses_expression_right_paren_on_new_line = false 370 | ij_groovy_prefer_parameters_wrap = false 371 | ij_groovy_resource_list_new_line_after_left_paren = false 372 | ij_groovy_resource_list_right_paren_on_new_line = false 373 | ij_groovy_resource_list_wrap = off 374 | ij_groovy_space_after_assert_separator = true 375 | ij_groovy_space_after_colon = true 376 | ij_groovy_space_after_comma = true 377 | ij_groovy_space_after_comma_in_type_arguments = true 378 | ij_groovy_space_after_for_semicolon = true 379 | ij_groovy_space_after_quest = true 380 | ij_groovy_space_after_type_cast = true 381 | ij_groovy_space_before_annotation_parameter_list = false 382 | ij_groovy_space_before_array_initializer_left_brace = false 383 | ij_groovy_space_before_assert_separator = false 384 | ij_groovy_space_before_catch_keyword = true 385 | ij_groovy_space_before_catch_left_brace = true 386 | ij_groovy_space_before_catch_parentheses = true 387 | ij_groovy_space_before_class_left_brace = true 388 | ij_groovy_space_before_closure_left_brace = true 389 | ij_groovy_space_before_colon = true 390 | ij_groovy_space_before_comma = false 391 | ij_groovy_space_before_do_left_brace = true 392 | ij_groovy_space_before_else_keyword = true 393 | ij_groovy_space_before_else_left_brace = true 394 | ij_groovy_space_before_finally_keyword = true 395 | ij_groovy_space_before_finally_left_brace = true 396 | ij_groovy_space_before_for_left_brace = true 397 | ij_groovy_space_before_for_parentheses = true 398 | ij_groovy_space_before_for_semicolon = false 399 | ij_groovy_space_before_if_left_brace = true 400 | ij_groovy_space_before_if_parentheses = true 401 | ij_groovy_space_before_method_call_parentheses = false 402 | ij_groovy_space_before_method_left_brace = true 403 | ij_groovy_space_before_method_parentheses = false 404 | ij_groovy_space_before_quest = true 405 | ij_groovy_space_before_switch_left_brace = true 406 | ij_groovy_space_before_switch_parentheses = true 407 | ij_groovy_space_before_synchronized_left_brace = true 408 | ij_groovy_space_before_synchronized_parentheses = true 409 | ij_groovy_space_before_try_left_brace = true 410 | ij_groovy_space_before_try_parentheses = true 411 | ij_groovy_space_before_while_keyword = true 412 | ij_groovy_space_before_while_left_brace = true 413 | ij_groovy_space_before_while_parentheses = true 414 | ij_groovy_space_in_named_argument = true 415 | ij_groovy_space_in_named_argument_before_colon = false 416 | ij_groovy_space_within_empty_array_initializer_braces = false 417 | ij_groovy_space_within_empty_method_call_parentheses = false 418 | ij_groovy_spaces_around_additive_operators = true 419 | ij_groovy_spaces_around_assignment_operators = true 420 | ij_groovy_spaces_around_bitwise_operators = true 421 | ij_groovy_spaces_around_equality_operators = true 422 | ij_groovy_spaces_around_lambda_arrow = true 423 | ij_groovy_spaces_around_logical_operators = true 424 | ij_groovy_spaces_around_multiplicative_operators = true 425 | ij_groovy_spaces_around_regex_operators = true 426 | ij_groovy_spaces_around_relational_operators = true 427 | ij_groovy_spaces_around_shift_operators = true 428 | ij_groovy_spaces_within_annotation_parentheses = false 429 | ij_groovy_spaces_within_array_initializer_braces = false 430 | ij_groovy_spaces_within_braces = true 431 | ij_groovy_spaces_within_brackets = false 432 | ij_groovy_spaces_within_cast_parentheses = false 433 | ij_groovy_spaces_within_catch_parentheses = false 434 | ij_groovy_spaces_within_for_parentheses = false 435 | ij_groovy_spaces_within_gstring_injection_braces = false 436 | ij_groovy_spaces_within_if_parentheses = false 437 | ij_groovy_spaces_within_list_or_map = false 438 | ij_groovy_spaces_within_method_call_parentheses = false 439 | ij_groovy_spaces_within_method_parentheses = false 440 | ij_groovy_spaces_within_parentheses = false 441 | ij_groovy_spaces_within_switch_parentheses = false 442 | ij_groovy_spaces_within_synchronized_parentheses = false 443 | ij_groovy_spaces_within_try_parentheses = false 444 | ij_groovy_spaces_within_tuple_expression = false 445 | ij_groovy_spaces_within_while_parentheses = false 446 | ij_groovy_special_else_if_treatment = true 447 | ij_groovy_ternary_operation_wrap = off 448 | ij_groovy_throws_keyword_wrap = off 449 | ij_groovy_throws_list_wrap = off 450 | ij_groovy_use_flying_geese_braces = false 451 | ij_groovy_use_fq_class_names = false 452 | ij_groovy_use_fq_class_names_in_javadoc = true 453 | ij_groovy_use_relative_indents = false 454 | ij_groovy_use_single_class_imports = true 455 | ij_groovy_variable_annotation_wrap = off 456 | ij_groovy_while_brace_force = never 457 | ij_groovy_while_on_new_line = false 458 | ij_groovy_wrap_long_lines = false 459 | 460 | [*.json] 461 | indent_size = 2 462 | tab_width = 2 463 | ij_continuation_indent_size = 2 464 | ij_json_keep_blank_lines_in_code = 1 465 | ij_json_keep_indents_on_empty_lines = false 466 | ij_json_keep_line_breaks = true 467 | ij_json_property_alignment = align_on_colon 468 | ij_json_space_after_colon = true 469 | ij_json_space_after_comma = true 470 | ij_json_space_before_colon = false 471 | ij_json_space_before_comma = false 472 | ij_json_spaces_within_braces = false 473 | ij_json_spaces_within_brackets = false 474 | ij_json_wrap_long_lines = false 475 | 476 | [{*.yaml,*.yml}] 477 | indent_size = 2 478 | ij_yaml_keep_indents_on_empty_lines = false 479 | ij_yaml_keep_line_breaks = true 480 | ij_yaml_spaces_within_braces = true 481 | ij_yaml_spaces_within_brackets = true --------------------------------------------------------------------------------