├── .gitignore ├── src ├── main │ ├── resources │ │ ├── icon.png │ │ ├── config │ │ │ ├── client │ │ │ │ └── InertiaAntiCheat.toml │ │ │ └── server │ │ │ │ └── InertiaAntiCheat.toml │ │ ├── assets │ │ │ └── inertiaanticheat │ │ │ │ ├── textures │ │ │ │ └── gui │ │ │ │ │ ├── cross.png │ │ │ │ │ ├── tick.png │ │ │ │ │ ├── enabled.png │ │ │ │ │ ├── modpack.png │ │ │ │ │ ├── blacklist.png │ │ │ │ │ └── whitelist.png │ │ │ │ └── lang │ │ │ │ └── en_us.json │ │ ├── inertiaanticheat.mixins.json │ │ └── fabric.mod.json │ └── java │ │ └── com │ │ └── diffusehyperion │ │ └── inertiaanticheat │ │ ├── common │ │ ├── util │ │ │ ├── TransferMethod.java │ │ │ ├── ValidationMethod.java │ │ │ ├── AnticheatDetails.java │ │ │ ├── HashAlgorithm.java │ │ │ ├── GroupAnticheatDetails.java │ │ │ ├── IndividualAnticheatDetails.java │ │ │ └── InertiaAntiCheatConstants.java │ │ ├── interfaces │ │ │ ├── UpgradedConnectScreen.java │ │ │ ├── UpgradedServerLoginNetworkHandler.java │ │ │ ├── UpgradedClientLoginNetworkHandler.java │ │ │ └── UpgradedServerInfo.java │ │ ├── networking │ │ │ └── packets │ │ │ │ ├── UpgradedClientQueryPacketListener.java │ │ │ │ └── S2C │ │ │ │ └── AnticheatDetailsS2CPacket.java │ │ └── InertiaAntiCheat.java │ │ ├── server │ │ ├── networking │ │ │ ├── method │ │ │ │ ├── CheckingTypes.java │ │ │ │ ├── ValidatorHandler.java │ │ │ │ ├── id │ │ │ │ │ ├── handlers │ │ │ │ │ │ ├── IdReceiverHandler.java │ │ │ │ │ │ └── IdValidationHandler.java │ │ │ │ │ ├── ServerIdIndividualValidatorHandler.java │ │ │ │ │ ├── ServerIdGroupValidatorHandler.java │ │ │ │ │ └── ServerIdReceiverHandler.java │ │ │ │ ├── name │ │ │ │ │ ├── handlers │ │ │ │ │ │ ├── NameReceiverHandler.java │ │ │ │ │ │ └── NameValidationHandler.java │ │ │ │ │ ├── ServerNameIndividualValidatorHandler.java │ │ │ │ │ ├── ServerNameGroupValidatorHandler.java │ │ │ │ │ └── ServerNameReceiverHandler.java │ │ │ │ ├── data │ │ │ │ │ ├── handlers │ │ │ │ │ │ ├── DataReceiverHandler.java │ │ │ │ │ │ └── DataValidationHandler.java │ │ │ │ │ ├── ServerDataIndividualValidatorHandler.java │ │ │ │ │ ├── ServerDataGroupValidatorHandler.java │ │ │ │ │ └── ServerDataReceiverHandler.java │ │ │ │ └── ReceiverHandler.java │ │ │ └── packets │ │ │ │ └── AnticheatPackets.java │ │ ├── InertiaAntiCheatServer.java │ │ └── ServerLoginModlistTransferHandler.java │ │ ├── utils │ │ └── QuadConsumer.java │ │ └── mixins │ │ ├── server │ │ ├── ServerLoginNetworkHandlerMixin.java │ │ └── ServerQueryNetworkHandlerMixin.java │ │ └── common │ │ └── QueryStatesMixin.java └── client │ ├── java │ └── com │ │ └── diffusehyperion │ │ └── inertiaanticheat │ │ ├── client │ │ ├── interfaces │ │ │ └── UpgradedClientCollection.java │ │ ├── networking │ │ │ ├── method │ │ │ │ ├── id │ │ │ │ │ └── ClientIdTransferHandler.java │ │ │ │ ├── name │ │ │ │ │ └── ClientNameTransferHandler.java │ │ │ │ ├── TransferHandler.java │ │ │ │ └── data │ │ │ │ │ └── ClientDataTransferHandler.java │ │ │ └── packets │ │ │ │ └── UpgradedClientQueryNetworkHandler.java │ │ ├── InertiaAntiCheatClient.java │ │ └── ClientLoginModlistTransferHandler.java │ │ └── mixins │ │ └── client │ │ ├── ConnectScreenAccessor.java │ │ ├── ClientLoginNetworkHandlerMixin.java │ │ ├── ServerInfoMixin.java │ │ ├── ClientConnectionMixin.java │ │ ├── ConnectScreenMixin.java │ │ ├── ConnectScreenThreadMixin.java │ │ ├── MultiplayerServerListPingerMixin.java │ │ └── MultiplayerServerListWidgetServerEntryMixin.java │ └── resources │ └── inertiaanticheat.client.mixins.json ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── settings.gradle ├── .github └── ISSUE_TEMPLATE │ ├── update-version.md │ ├── feature_request.md │ └── bug_report.md ├── gradle.properties ├── README.md ├── gradlew.bat ├── gradlew └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | # Project exclude paths 2 | /.gradle/ 3 | /build/ 4 | /run/ 5 | /.idea/ 6 | /runClient/ 7 | /runServer/ 8 | -------------------------------------------------------------------------------- /src/main/resources/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiffuseHyperion/InertiaAntiCheat/HEAD/src/main/resources/icon.png -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiffuseHyperion/InertiaAntiCheat/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /src/main/resources/config/client/InertiaAntiCheat.toml: -------------------------------------------------------------------------------- 1 | [debug] 2 | # Show additional information in server logs. 3 | debug = false 4 | # do not touch pls :) 5 | version = 2 -------------------------------------------------------------------------------- /src/main/resources/assets/inertiaanticheat/textures/gui/cross.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiffuseHyperion/InertiaAntiCheat/HEAD/src/main/resources/assets/inertiaanticheat/textures/gui/cross.png -------------------------------------------------------------------------------- /src/main/resources/assets/inertiaanticheat/textures/gui/tick.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiffuseHyperion/InertiaAntiCheat/HEAD/src/main/resources/assets/inertiaanticheat/textures/gui/tick.png -------------------------------------------------------------------------------- /src/main/resources/assets/inertiaanticheat/textures/gui/enabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiffuseHyperion/InertiaAntiCheat/HEAD/src/main/resources/assets/inertiaanticheat/textures/gui/enabled.png -------------------------------------------------------------------------------- /src/main/resources/assets/inertiaanticheat/textures/gui/modpack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiffuseHyperion/InertiaAntiCheat/HEAD/src/main/resources/assets/inertiaanticheat/textures/gui/modpack.png -------------------------------------------------------------------------------- /src/main/resources/assets/inertiaanticheat/textures/gui/blacklist.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiffuseHyperion/InertiaAntiCheat/HEAD/src/main/resources/assets/inertiaanticheat/textures/gui/blacklist.png -------------------------------------------------------------------------------- /src/main/resources/assets/inertiaanticheat/textures/gui/whitelist.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DiffuseHyperion/InertiaAntiCheat/HEAD/src/main/resources/assets/inertiaanticheat/textures/gui/whitelist.png -------------------------------------------------------------------------------- /src/main/java/com/diffusehyperion/inertiaanticheat/common/util/TransferMethod.java: -------------------------------------------------------------------------------- 1 | package com.diffusehyperion.inertiaanticheat.common.util; 2 | 3 | public enum TransferMethod { 4 | DATA, 5 | NAME, 6 | ID 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/com/diffusehyperion/inertiaanticheat/common/util/ValidationMethod.java: -------------------------------------------------------------------------------- 1 | package com.diffusehyperion.inertiaanticheat.common.util; 2 | 3 | public enum ValidationMethod { 4 | INDIVIDUAL, 5 | GROUP 6 | } 7 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | maven { 4 | name = 'Fabric' 5 | url = 'https://maven.fabricmc.net/' 6 | } 7 | gradlePluginPortal() 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/com/diffusehyperion/inertiaanticheat/server/networking/method/CheckingTypes.java: -------------------------------------------------------------------------------- 1 | package com.diffusehyperion.inertiaanticheat.server.networking.method; 2 | 3 | public enum CheckingTypes { 4 | DATA, 5 | NAME, 6 | ID 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/com/diffusehyperion/inertiaanticheat/utils/QuadConsumer.java: -------------------------------------------------------------------------------- 1 | package com.diffusehyperion.inertiaanticheat.utils; 2 | 3 | @FunctionalInterface 4 | public interface QuadConsumer { 5 | void accept(A a, B b, C c, D d); 6 | } 7 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /src/main/resources/assets/inertiaanticheat/lang/en_us.json: -------------------------------------------------------------------------------- 1 | { 2 | "login.key.no_possible_key": "You have not successfully verified your mod-list yet!\nPlease ping the server in the server-list before rejoining.", 3 | "login.key.mismatched_key": "Something went wrong while communicating with the anti-cheat!\nPlease ping the server in the server-list before rejoining." 4 | } -------------------------------------------------------------------------------- /src/main/java/com/diffusehyperion/inertiaanticheat/common/interfaces/UpgradedConnectScreen.java: -------------------------------------------------------------------------------- 1 | package com.diffusehyperion.inertiaanticheat.common.interfaces; 2 | 3 | import net.minecraft.text.Text; 4 | import org.jetbrains.annotations.Nullable; 5 | 6 | public interface UpgradedConnectScreen { 7 | void inertiaAntiCheat$setSecondaryStatus(@Nullable Text secondaryStatus); 8 | } 9 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/update-version.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Update version 3 | about: Request for an update to the latest Minecraft version 4 | title: 'UPDATE: Minecraft (version) ' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Latest Minecraft version** 11 | The latest Minecraft version currently. (e.g. 1.21) 12 | 13 | **Current mod version** 14 | The version of the mod are you using currently. (e.g. 0.0.3.1) 15 | -------------------------------------------------------------------------------- /src/main/java/com/diffusehyperion/inertiaanticheat/common/interfaces/UpgradedServerLoginNetworkHandler.java: -------------------------------------------------------------------------------- 1 | package com.diffusehyperion.inertiaanticheat.common.interfaces; 2 | 3 | import com.mojang.authlib.GameProfile; 4 | import net.minecraft.network.ClientConnection; 5 | 6 | public interface UpgradedServerLoginNetworkHandler { 7 | ClientConnection inertiaAntiCheat$getConnection(); 8 | GameProfile inertiaAntiCheat$getGameProfile(); 9 | } 10 | -------------------------------------------------------------------------------- /src/client/java/com/diffusehyperion/inertiaanticheat/client/interfaces/UpgradedClientCollection.java: -------------------------------------------------------------------------------- 1 | package com.diffusehyperion.inertiaanticheat.client.interfaces; 2 | 3 | import com.diffusehyperion.inertiaanticheat.common.networking.packets.UpgradedClientQueryPacketListener; 4 | 5 | public interface UpgradedClientCollection { 6 | void inertiaAntiCheat$connect(String string, int i, UpgradedClientQueryPacketListener clientUpgradedStatusPacketListener); 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/com/diffusehyperion/inertiaanticheat/common/interfaces/UpgradedClientLoginNetworkHandler.java: -------------------------------------------------------------------------------- 1 | package com.diffusehyperion.inertiaanticheat.common.interfaces; 2 | 3 | import net.minecraft.text.Text; 4 | 5 | import java.util.function.Consumer; 6 | 7 | public interface UpgradedClientLoginNetworkHandler { 8 | void inertiaAntiCheat$setSecondaryStatusConsumer(Consumer consumer); 9 | 10 | Consumer inertiaAntiCheat$getSecondaryStatusConsumer(); 11 | } 12 | -------------------------------------------------------------------------------- /src/main/resources/inertiaanticheat.mixins.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "minVersion": "0.8", 4 | "package": "com.diffusehyperion.inertiaanticheat.mixins", 5 | "compatibilityLevel": "JAVA_21", 6 | "injectors": { 7 | "defaultRequire": 1 8 | }, 9 | "overwrites": { 10 | "requireAnnotations": true 11 | }, 12 | "server": [ 13 | "server.ServerLoginNetworkHandlerMixin", 14 | "server.ServerQueryNetworkHandlerMixin" 15 | ], 16 | "mixins": [ 17 | "common.QueryStatesMixin" 18 | ] 19 | } -------------------------------------------------------------------------------- /src/main/java/com/diffusehyperion/inertiaanticheat/common/util/AnticheatDetails.java: -------------------------------------------------------------------------------- 1 | package com.diffusehyperion.inertiaanticheat.common.util; 2 | 3 | public abstract class AnticheatDetails { 4 | private final boolean showInstalled; 5 | 6 | public abstract ValidationMethod getValidationMethod(); 7 | 8 | public AnticheatDetails(boolean showInstalled) { 9 | this.showInstalled = showInstalled; 10 | } 11 | 12 | public boolean showInstalled() { 13 | return showInstalled; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/diffusehyperion/inertiaanticheat/common/networking/packets/UpgradedClientQueryPacketListener.java: -------------------------------------------------------------------------------- 1 | package com.diffusehyperion.inertiaanticheat.common.networking.packets; 2 | 3 | import com.diffusehyperion.inertiaanticheat.common.networking.packets.S2C.AnticheatDetailsS2CPacket; 4 | import net.minecraft.network.listener.ClientQueryPacketListener; 5 | 6 | public interface UpgradedClientQueryPacketListener extends ClientQueryPacketListener { 7 | void onReceiveAnticheatDetails(AnticheatDetailsS2CPacket var1); 8 | } 9 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Done to increase the memory available to gradle. 2 | org.gradle.jvmargs=-Xmx1G 3 | 4 | # Fabric Properties 5 | # check these on https://modmuss50.me/fabric.html 6 | minecraft_version=1.21.11 7 | yarn_mappings=1.21.11+build.2 8 | loader_version=0.18.2 9 | loom_version=1.14-SNAPSHOT 10 | 11 | # Mod Properties 12 | mod_version=1.1.1.2 13 | maven_group = com.diffusehyperion 14 | archives_base_name = InertiaAntiCheat 15 | 16 | # Dependencies 17 | # check this on https://modmuss50.me/fabric.html 18 | fabric_version=0.139.4+1.21.11 -------------------------------------------------------------------------------- /src/main/java/com/diffusehyperion/inertiaanticheat/common/interfaces/UpgradedServerInfo.java: -------------------------------------------------------------------------------- 1 | package com.diffusehyperion.inertiaanticheat.common.interfaces; 2 | 3 | import com.diffusehyperion.inertiaanticheat.common.util.AnticheatDetails; 4 | 5 | public interface UpgradedServerInfo { 6 | AnticheatDetails inertiaAntiCheat$getAnticheatDetails(); 7 | Boolean inertiaAntiCheat$isInertiaInstalled(); 8 | 9 | void inertiaAntiCheat$setAnticheatDetails(AnticheatDetails anticheatDetails); 10 | void inertiaAntiCheat$setInertiaInstalled(Boolean value); 11 | } 12 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: 'SUGGESTION: ' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Additional context** 17 | Add any other context or screenshots about the feature request here. 18 | -------------------------------------------------------------------------------- /src/main/java/com/diffusehyperion/inertiaanticheat/common/util/HashAlgorithm.java: -------------------------------------------------------------------------------- 1 | package com.diffusehyperion.inertiaanticheat.common.util; 2 | 3 | public enum HashAlgorithm { 4 | MD5("MD5", 32), 5 | SHA1("SHA-1", 40), 6 | SHA256("SHA-256", 64); 7 | 8 | private final String name; 9 | private final int length; 10 | 11 | HashAlgorithm(String name, int length) { 12 | this.name = name; 13 | this.length = length; 14 | } 15 | 16 | @Override 17 | public String toString() { 18 | return name; 19 | } 20 | 21 | public int getLength() { 22 | return length; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/client/resources/inertiaanticheat.client.mixins.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "minVersion": "0.8", 4 | "package": "com.diffusehyperion.inertiaanticheat.mixins.client", 5 | "compatibilityLevel": "JAVA_21", 6 | "injectors": { 7 | "defaultRequire": 1 8 | }, 9 | "overwrites": { 10 | "requireAnnotations": true 11 | }, 12 | "client": [ 13 | "ClientConnectionMixin", 14 | "ClientLoginNetworkHandlerMixin", 15 | "ConnectScreenAccessor", 16 | "ConnectScreenMixin", 17 | "ConnectScreenThreadMixin", 18 | "MultiplayerServerListPingerMixin", 19 | "MultiplayerServerListWidgetServerEntryMixin", 20 | "ServerInfoMixin" 21 | ] 22 | } -------------------------------------------------------------------------------- /src/main/java/com/diffusehyperion/inertiaanticheat/server/networking/packets/AnticheatPackets.java: -------------------------------------------------------------------------------- 1 | package com.diffusehyperion.inertiaanticheat.server.networking.packets; 2 | 3 | import com.diffusehyperion.inertiaanticheat.common.networking.packets.S2C.AnticheatDetailsS2CPacket; 4 | import com.diffusehyperion.inertiaanticheat.common.util.InertiaAntiCheatConstants; 5 | import net.minecraft.network.NetworkSide; 6 | import net.minecraft.network.packet.PacketType; 7 | 8 | public class AnticheatPackets { 9 | public static final PacketType DETAILS_RESPONSE = new PacketType<>(NetworkSide.CLIENTBOUND, InertiaAntiCheatConstants.ANTICHEAT_DETAILS_ID); 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/diffusehyperion/inertiaanticheat/common/util/GroupAnticheatDetails.java: -------------------------------------------------------------------------------- 1 | package com.diffusehyperion.inertiaanticheat.common.util; 2 | 3 | import java.util.List; 4 | 5 | public class GroupAnticheatDetails extends AnticheatDetails { 6 | private final List modpackDetails; 7 | 8 | public GroupAnticheatDetails(boolean showInstalled, List modpackDetails) { 9 | super(showInstalled); 10 | this.modpackDetails = modpackDetails; 11 | } 12 | 13 | public List getModpackDetails() { 14 | return modpackDetails; 15 | } 16 | 17 | @Override 18 | public ValidationMethod getValidationMethod() { 19 | return ValidationMethod.GROUP; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/diffusehyperion/inertiaanticheat/server/networking/method/ValidatorHandler.java: -------------------------------------------------------------------------------- 1 | package com.diffusehyperion.inertiaanticheat.server.networking.method; 2 | 3 | import java.util.concurrent.CompletableFuture; 4 | 5 | public abstract class ValidatorHandler { 6 | public final CompletableFuture future; 7 | public final Runnable failureTask; 8 | public final Runnable successTask; 9 | public final Runnable finishTask; 10 | 11 | public ValidatorHandler(Runnable failureTask, Runnable successTask, Runnable finishTask) { 12 | this.failureTask = failureTask; 13 | this.successTask = successTask; 14 | this.finishTask = finishTask; 15 | this.future = new CompletableFuture<>(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/diffusehyperion/inertiaanticheat/server/networking/method/id/handlers/IdReceiverHandler.java: -------------------------------------------------------------------------------- 1 | package com.diffusehyperion.inertiaanticheat.server.networking.method.id.handlers; 2 | 3 | import com.diffusehyperion.inertiaanticheat.server.networking.method.ReceiverHandler; 4 | import net.minecraft.server.network.ServerLoginNetworkHandler; 5 | import net.minecraft.util.Identifier; 6 | 7 | import java.security.KeyPair; 8 | 9 | public abstract class IdReceiverHandler extends ReceiverHandler { 10 | protected final IdValidationHandler validator; 11 | 12 | public IdReceiverHandler(KeyPair keyPair, Identifier modTransferID, ServerLoginNetworkHandler handler, IdValidationHandler validator) { 13 | super(keyPair, modTransferID, handler); 14 | this.validator = validator; 15 | } 16 | } -------------------------------------------------------------------------------- /src/client/java/com/diffusehyperion/inertiaanticheat/mixins/client/ConnectScreenAccessor.java: -------------------------------------------------------------------------------- 1 | package com.diffusehyperion.inertiaanticheat.mixins.client; 2 | 3 | import net.minecraft.client.gui.screen.Screen; 4 | import net.minecraft.client.gui.screen.multiplayer.ConnectScreen; 5 | import net.minecraft.network.ClientConnection; 6 | import net.minecraft.text.Text; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.gen.Accessor; 9 | import org.spongepowered.asm.mixin.gen.Invoker; 10 | 11 | @Mixin(ConnectScreen.class) 12 | public interface ConnectScreenAccessor { 13 | @Accessor("connection") 14 | ClientConnection getConnection(); 15 | 16 | @Accessor("parent") 17 | Screen getParent(); 18 | 19 | @Invoker("setStatus") 20 | void invokeSetStatus(Text status); 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/diffusehyperion/inertiaanticheat/server/networking/method/name/handlers/NameReceiverHandler.java: -------------------------------------------------------------------------------- 1 | package com.diffusehyperion.inertiaanticheat.server.networking.method.name.handlers; 2 | 3 | import com.diffusehyperion.inertiaanticheat.server.networking.method.ReceiverHandler; 4 | import net.minecraft.server.network.ServerLoginNetworkHandler; 5 | import net.minecraft.util.Identifier; 6 | 7 | import java.security.KeyPair; 8 | 9 | public abstract class NameReceiverHandler extends ReceiverHandler { 10 | protected final NameValidationHandler validator; 11 | 12 | public NameReceiverHandler(KeyPair keyPair, Identifier modTransferID, ServerLoginNetworkHandler handler, NameValidationHandler validator) { 13 | super(keyPair, modTransferID, handler); 14 | this.validator = validator; 15 | } 16 | } -------------------------------------------------------------------------------- /src/main/java/com/diffusehyperion/inertiaanticheat/server/networking/method/data/handlers/DataReceiverHandler.java: -------------------------------------------------------------------------------- 1 | package com.diffusehyperion.inertiaanticheat.server.networking.method.data.handlers; 2 | 3 | import com.diffusehyperion.inertiaanticheat.server.networking.method.ReceiverHandler; 4 | import net.minecraft.server.network.ServerLoginNetworkHandler; 5 | import net.minecraft.util.Identifier; 6 | 7 | import java.security.KeyPair; 8 | 9 | public abstract class DataReceiverHandler extends ReceiverHandler { 10 | protected final DataValidationHandler validator; 11 | 12 | public DataReceiverHandler(KeyPair keyPair, Identifier modTransferID, ServerLoginNetworkHandler handler, DataValidationHandler validator) { 13 | super(keyPair, modTransferID, handler); 14 | this.validator = validator; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/diffusehyperion/inertiaanticheat/common/util/IndividualAnticheatDetails.java: -------------------------------------------------------------------------------- 1 | package com.diffusehyperion.inertiaanticheat.common.util; 2 | 3 | import java.util.List; 4 | 5 | public class IndividualAnticheatDetails extends AnticheatDetails { 6 | 7 | private final List blacklistedMods; 8 | private final List whitelistedMods; 9 | 10 | public IndividualAnticheatDetails(boolean showInstalled, List blacklistedMods, List whitelistedMods) { 11 | super(showInstalled); 12 | this.blacklistedMods = blacklistedMods; 13 | this.whitelistedMods = whitelistedMods; 14 | } 15 | 16 | public List getBlacklistedMods() { 17 | return blacklistedMods; 18 | } 19 | 20 | public List getWhitelistedMods() { 21 | return whitelistedMods; 22 | } 23 | 24 | @Override 25 | public ValidationMethod getValidationMethod() { 26 | return ValidationMethod.INDIVIDUAL; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/resources/fabric.mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 1, 3 | "id": "inertiaanticheat", 4 | "version": "${version}", 5 | "name": "InertiaAntiCheat", 6 | "description": "Stop people from using unwanted mods on your server!", 7 | "authors": [ 8 | "DiffuseHyperion" 9 | ], 10 | "contact": { 11 | "repo": "https://github.com/DiffuseHyperion/InertiaAntiCheat" 12 | }, 13 | "license": "GPL-3.0", 14 | "icon": "icon.png", 15 | "environment": "*", 16 | "entrypoints": { 17 | "client": [ 18 | "com.diffusehyperion.inertiaanticheat.client.InertiaAntiCheatClient" 19 | ], 20 | "main": [ 21 | "com.diffusehyperion.inertiaanticheat.common.InertiaAntiCheat" 22 | ], 23 | "server": [ 24 | "com.diffusehyperion.inertiaanticheat.server.InertiaAntiCheatServer" 25 | ] 26 | }, 27 | "mixins": [ 28 | "inertiaanticheat.mixins.json", 29 | { 30 | "config": "inertiaanticheat.client.mixins.json", 31 | "environment": "client" 32 | } 33 | ], 34 | "depends": { 35 | "fabricloader": "*", 36 | "fabric": "*", 37 | "minecraft": "=1.21.11" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/diffusehyperion/inertiaanticheat/common/util/InertiaAntiCheatConstants.java: -------------------------------------------------------------------------------- 1 | package com.diffusehyperion.inertiaanticheat.common.util; 2 | 3 | import net.minecraft.util.Identifier; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | 7 | public class InertiaAntiCheatConstants { 8 | public static final Identifier ANTICHEAT_DETAILS_ID = Identifier.of("inertiaanticheat", "anticheat_details"); 9 | 10 | public static final Identifier CHECK_CONNECTION = Identifier.of("inertiaanticheat", "check_connection"); 11 | public static final Identifier INITIATE_E2EE = Identifier.of("inertiaanticheat", "initiate_e2ee"); 12 | public static final Identifier SET_ADAPTOR = Identifier.of("inertiaanticheat", "set_adapter"); 13 | public static final Identifier SEND_MOD = Identifier.of("inertiaanticheat", "send_mod"); 14 | 15 | public static final Logger MODLOGGER = LoggerFactory.getLogger("InertiaAntiCheat"); 16 | public static final String MODID = "inertiaanticheat"; 17 | 18 | public static final long CURRENT_SERVER_CONFIG_VERSION = 9; 19 | public static final long CURRENT_CLIENT_CONFIG_VERSION = 2; 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/diffusehyperion/inertiaanticheat/mixins/server/ServerLoginNetworkHandlerMixin.java: -------------------------------------------------------------------------------- 1 | package com.diffusehyperion.inertiaanticheat.mixins.server; 2 | 3 | import com.diffusehyperion.inertiaanticheat.common.interfaces.UpgradedServerLoginNetworkHandler; 4 | import com.mojang.authlib.GameProfile; 5 | import net.minecraft.network.ClientConnection; 6 | import net.minecraft.server.network.ServerLoginNetworkHandler; 7 | import org.jetbrains.annotations.Nullable; 8 | import org.spongepowered.asm.mixin.Final; 9 | import org.spongepowered.asm.mixin.Mixin; 10 | import org.spongepowered.asm.mixin.Shadow; 11 | 12 | @Mixin(ServerLoginNetworkHandler.class) 13 | public abstract class ServerLoginNetworkHandlerMixin implements UpgradedServerLoginNetworkHandler { 14 | @Shadow @Final 15 | ClientConnection connection; 16 | 17 | @Shadow private @Nullable GameProfile profile; 18 | 19 | @Override 20 | public ClientConnection inertiaAntiCheat$getConnection() { 21 | return this.connection; 22 | } 23 | 24 | @Override 25 | public GameProfile inertiaAntiCheat$getGameProfile() { 26 | return this.profile; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: 'BUG: ' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the problem: 15 | 1. Enable option '...' 16 | 2. Join the server with '...' 17 | 3. See error 18 | 19 | **Expected behavior** 20 | A clear and concise description of what you expected to happen. 21 | 22 | **Screenshots** 23 | If applicable, add screenshots to help explain your problem. 24 | 25 | **Server information (please complete the following information):** 26 | - Server software: (Fabric/Quilt) 27 | - Server software loader version: (e.g. 0.14.19) 28 | - Minecraft version: (e.g. 1.20.1) 29 | - Mod version: (e.g. 0.0.3.1) 30 | - Fabric API version: (e.g. 0.83.1+1.20.1) 31 | 32 | **Mod config** 33 | ``` 34 | Paste your server's InertiaAntiCheat.toml file contents here! 35 | ``` 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | Here would also be the place where you put any other mods you were running when the problem occured. 40 | -------------------------------------------------------------------------------- /src/main/java/com/diffusehyperion/inertiaanticheat/mixins/common/QueryStatesMixin.java: -------------------------------------------------------------------------------- 1 | package com.diffusehyperion.inertiaanticheat.mixins.common; 2 | 3 | import com.diffusehyperion.inertiaanticheat.common.networking.packets.S2C.AnticheatDetailsS2CPacket; 4 | import com.diffusehyperion.inertiaanticheat.server.networking.packets.AnticheatPackets; 5 | import net.minecraft.network.state.NetworkStateBuilder; 6 | import net.minecraft.network.state.QueryStates; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.injection.At; 9 | import org.spongepowered.asm.mixin.injection.Inject; 10 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 11 | 12 | /* 13 | * Purpose of this is to register the custom query response packet, otherwise io.netty.handler.codec.EncoderException will be thrown 14 | */ 15 | @Mixin(QueryStates.class) 16 | public class QueryStatesMixin { 17 | 18 | @Inject(method = "method_56029", at = @At(value = "TAIL")) 19 | private static void registerClientbound(NetworkStateBuilder builder, CallbackInfo ci) { 20 | builder.add(AnticheatPackets.DETAILS_RESPONSE, AnticheatDetailsS2CPacket.CODEC); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/client/java/com/diffusehyperion/inertiaanticheat/mixins/client/ClientLoginNetworkHandlerMixin.java: -------------------------------------------------------------------------------- 1 | package com.diffusehyperion.inertiaanticheat.mixins.client; 2 | 3 | import com.diffusehyperion.inertiaanticheat.common.interfaces.UpgradedClientLoginNetworkHandler; 4 | import net.minecraft.client.network.ClientLoginNetworkHandler; 5 | import net.minecraft.text.Text; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | import org.spongepowered.asm.mixin.Unique; 8 | 9 | import java.util.function.Consumer; 10 | 11 | @Mixin(ClientLoginNetworkHandler.class) 12 | public abstract class ClientLoginNetworkHandlerMixin implements UpgradedClientLoginNetworkHandler { 13 | @Unique 14 | private Consumer secondaryStatusConsumer = (Text text) -> { 15 | throw new RuntimeException("Tried setting secondary status to " + text + " when it was uninitialized"); 16 | }; 17 | 18 | @Override 19 | public void inertiaAntiCheat$setSecondaryStatusConsumer(Consumer consumer) { 20 | this.secondaryStatusConsumer = consumer; 21 | } 22 | 23 | @Override 24 | public Consumer inertiaAntiCheat$getSecondaryStatusConsumer() { 25 | return this.secondaryStatusConsumer; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/diffusehyperion/inertiaanticheat/server/networking/method/id/handlers/IdValidationHandler.java: -------------------------------------------------------------------------------- 1 | package com.diffusehyperion.inertiaanticheat.server.networking.method.id.handlers; 2 | 3 | import com.diffusehyperion.inertiaanticheat.server.networking.method.ValidatorHandler; 4 | 5 | import java.util.List; 6 | 7 | import static com.diffusehyperion.inertiaanticheat.server.InertiaAntiCheatServer.debugInfo; 8 | import static com.diffusehyperion.inertiaanticheat.server.InertiaAntiCheatServer.debugLine; 9 | 10 | public abstract class IdValidationHandler extends ValidatorHandler { 11 | 12 | public IdValidationHandler(Runnable failureTask, Runnable successTask, Runnable finishTask) { 13 | super(failureTask, successTask, finishTask); 14 | } 15 | 16 | protected abstract boolean validateMods(List modlist); 17 | 18 | public void checkModlist(List modlist) { 19 | debugInfo("Finishing transfer, checking mods now"); 20 | if (!validateMods(modlist)) { 21 | failureTask.run(); 22 | } else { 23 | successTask.run(); 24 | } 25 | 26 | finishTask.run(); 27 | this.future.complete(null); 28 | debugLine(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/diffusehyperion/inertiaanticheat/server/networking/method/name/handlers/NameValidationHandler.java: -------------------------------------------------------------------------------- 1 | package com.diffusehyperion.inertiaanticheat.server.networking.method.name.handlers; 2 | 3 | import com.diffusehyperion.inertiaanticheat.server.networking.method.ValidatorHandler; 4 | 5 | import java.util.List; 6 | 7 | import static com.diffusehyperion.inertiaanticheat.server.InertiaAntiCheatServer.debugInfo; 8 | import static com.diffusehyperion.inertiaanticheat.server.InertiaAntiCheatServer.debugLine; 9 | 10 | public abstract class NameValidationHandler extends ValidatorHandler { 11 | 12 | public NameValidationHandler(Runnable failureTask, Runnable successTask, Runnable finishTask) { 13 | super(failureTask, successTask, finishTask); 14 | } 15 | 16 | protected abstract boolean validateMods(List modlist); 17 | 18 | public void checkModlist(List modlist) { 19 | debugInfo("Finishing transfer, checking mods now"); 20 | if (!validateMods(modlist)) { 21 | failureTask.run(); 22 | } else { 23 | successTask.run(); 24 | } 25 | 26 | finishTask.run(); 27 | this.future.complete(null); 28 | debugLine(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/diffusehyperion/inertiaanticheat/server/networking/method/data/handlers/DataValidationHandler.java: -------------------------------------------------------------------------------- 1 | package com.diffusehyperion.inertiaanticheat.server.networking.method.data.handlers; 2 | 3 | import com.diffusehyperion.inertiaanticheat.server.networking.method.ValidatorHandler; 4 | 5 | import java.util.List; 6 | 7 | import static com.diffusehyperion.inertiaanticheat.server.InertiaAntiCheatServer.debugInfo; 8 | import static com.diffusehyperion.inertiaanticheat.server.InertiaAntiCheatServer.debugLine; 9 | 10 | public abstract class DataValidationHandler extends ValidatorHandler { 11 | 12 | public DataValidationHandler(Runnable failureTask, Runnable successTask, Runnable finishTask) { 13 | super(failureTask, successTask, finishTask); 14 | } 15 | 16 | protected abstract boolean validateMods(List modlist); 17 | 18 | 19 | public void checkModlist(List modlist) { 20 | debugInfo("Finishing transfer, checking mods now"); 21 | if (!validateMods(modlist)) { 22 | failureTask.run(); 23 | } else { 24 | successTask.run(); 25 | } 26 | 27 | finishTask.run(); 28 | this.future.complete(null); 29 | debugLine(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/diffusehyperion/inertiaanticheat/server/networking/method/ReceiverHandler.java: -------------------------------------------------------------------------------- 1 | package com.diffusehyperion.inertiaanticheat.server.networking.method; 2 | 3 | import com.diffusehyperion.inertiaanticheat.common.util.InertiaAntiCheatConstants; 4 | import net.fabricmc.fabric.api.networking.v1.PacketSender; 5 | import net.fabricmc.fabric.api.networking.v1.ServerLoginNetworking; 6 | import net.minecraft.network.PacketByteBuf; 7 | import net.minecraft.server.MinecraftServer; 8 | import net.minecraft.server.network.ServerLoginNetworkHandler; 9 | import net.minecraft.util.Identifier; 10 | 11 | import java.security.KeyPair; 12 | 13 | public abstract class ReceiverHandler { 14 | protected final KeyPair keyPair; 15 | protected final Identifier modTransferID; 16 | 17 | public ReceiverHandler(KeyPair keyPair, Identifier modTransferID, ServerLoginNetworkHandler handler) { 18 | this.keyPair = keyPair; 19 | this.modTransferID = modTransferID; 20 | 21 | ServerLoginNetworking.registerReceiver(handler, InertiaAntiCheatConstants.SEND_MOD, this::receiveMod); 22 | } 23 | 24 | protected abstract void receiveMod(MinecraftServer minecraftServer, ServerLoginNetworkHandler serverLoginNetworkHandler, boolean b, PacketByteBuf buf, ServerLoginNetworking.LoginSynchronizer synchronizer, PacketSender packetSender); 25 | } 26 | -------------------------------------------------------------------------------- /src/client/java/com/diffusehyperion/inertiaanticheat/mixins/client/ServerInfoMixin.java: -------------------------------------------------------------------------------- 1 | package com.diffusehyperion.inertiaanticheat.mixins.client; 2 | 3 | import com.diffusehyperion.inertiaanticheat.common.interfaces.UpgradedServerInfo; 4 | import com.diffusehyperion.inertiaanticheat.common.util.AnticheatDetails; 5 | import net.minecraft.client.network.ServerInfo; 6 | import org.jetbrains.annotations.Nullable; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.Unique; 9 | 10 | @Mixin(ServerInfo.class) 11 | public abstract class ServerInfoMixin implements UpgradedServerInfo { 12 | @Unique 13 | @Nullable 14 | private Boolean inertiaInstalled; 15 | @Unique 16 | @Nullable 17 | private AnticheatDetails anticheatDetails; 18 | 19 | @Override 20 | public AnticheatDetails inertiaAntiCheat$getAnticheatDetails() { 21 | return this.anticheatDetails; 22 | } 23 | 24 | @Override 25 | @Nullable 26 | public Boolean inertiaAntiCheat$isInertiaInstalled() { 27 | return this.inertiaInstalled; 28 | } 29 | 30 | @Override 31 | public void inertiaAntiCheat$setAnticheatDetails(AnticheatDetails anticheatDetails) { 32 | this.anticheatDetails = anticheatDetails; 33 | } 34 | 35 | @Override 36 | public void inertiaAntiCheat$setInertiaInstalled(@Nullable Boolean value) { 37 | this.inertiaInstalled = value; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/client/java/com/diffusehyperion/inertiaanticheat/mixins/client/ClientConnectionMixin.java: -------------------------------------------------------------------------------- 1 | package com.diffusehyperion.inertiaanticheat.mixins.client; 2 | 3 | import com.diffusehyperion.inertiaanticheat.client.interfaces.UpgradedClientCollection; 4 | import com.diffusehyperion.inertiaanticheat.common.networking.packets.UpgradedClientQueryPacketListener; 5 | import net.minecraft.network.ClientConnection; 6 | import net.minecraft.network.listener.ClientPacketListener; 7 | import net.minecraft.network.listener.ServerPacketListener; 8 | import net.minecraft.network.packet.c2s.handshake.ConnectionIntent; 9 | import net.minecraft.network.state.NetworkState; 10 | import net.minecraft.network.state.QueryStates; 11 | import org.spongepowered.asm.mixin.Mixin; 12 | import org.spongepowered.asm.mixin.Shadow; 13 | import org.spongepowered.asm.mixin.Unique; 14 | 15 | @Mixin(ClientConnection.class) 16 | public abstract class ClientConnectionMixin implements UpgradedClientCollection { 17 | @Unique 18 | @Override 19 | public void inertiaAntiCheat$connect(String address, int i, UpgradedClientQueryPacketListener upgradedClientQueryPacketListener) { 20 | this.connect(address, i, QueryStates.C2S, QueryStates.S2C, upgradedClientQueryPacketListener, ConnectionIntent.STATUS); 21 | } 22 | 23 | @Shadow 24 | private void connect(String address, int port, NetworkState outboundState, NetworkState inboundState, C prePlayStateListener, ConnectionIntent intent) {} 25 | } 26 | -------------------------------------------------------------------------------- /src/client/java/com/diffusehyperion/inertiaanticheat/mixins/client/ConnectScreenMixin.java: -------------------------------------------------------------------------------- 1 | package com.diffusehyperion.inertiaanticheat.mixins.client; 2 | 3 | import com.diffusehyperion.inertiaanticheat.common.interfaces.UpgradedConnectScreen; 4 | import net.minecraft.client.gui.DrawContext; 5 | import net.minecraft.client.gui.screen.Screen; 6 | import net.minecraft.client.gui.screen.multiplayer.ConnectScreen; 7 | import net.minecraft.text.Text; 8 | import org.jetbrains.annotations.Nullable; 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 | import java.util.Objects; 16 | 17 | @Mixin(ConnectScreen.class) 18 | public abstract class ConnectScreenMixin extends Screen implements UpgradedConnectScreen { 19 | @Unique 20 | private @Nullable Text secondaryStatus; 21 | 22 | protected ConnectScreenMixin(Text title) { 23 | super(title); 24 | } 25 | 26 | @Unique 27 | @Override 28 | public void inertiaAntiCheat$setSecondaryStatus(@Nullable Text secondaryStatus) { 29 | this.secondaryStatus = secondaryStatus; 30 | } 31 | 32 | @Inject(method = "render", at = @At(value = "TAIL")) 33 | private void render(DrawContext context, int mouseX, int mouseY, float deltaTicks, CallbackInfo ci) { 34 | if (Objects.nonNull(this.secondaryStatus)) { 35 | context.drawCenteredTextWithShadow(this.textRenderer, this.secondaryStatus, this.width / 2, this.height / 2 - 35, 16777215); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Inertia Anti Cheat 2 | 3 | ![Stop people from using unwanted mods on your server!](https://cdn.modrinth.com/data/cached_images/1028d552bad40c52fd94cd12eefbddc17cbe4826.png) 4 | 5 | ![Require players to use your modpack on your server!](https://cdn.modrinth.com/data/cached_images/0487061e47d8ba4f102d479f853a0242315a86fe.png) 6 | 7 | *This was heavily inspired and named after https://www.curseforge.com/minecraft/mc-mods/kinetic-anti-cheat* 8 | 9 | ## How does it work? 10 | 11 | When someone joins your server, the server mod will ask for the client's mod list. 12 | 13 | If the client does not respond to this request (most likely because the client mod is not installed), the server will kick the player. 14 | 15 | If the client mod responds, the server mod will check the mod list against a configurable blacklist/whitelist, or a hash for mod packs! It will kick the player if the mod list is not acceptable. 16 | 17 | ## Is this foolproof? 18 | 19 | *Probably not.* This mod was only designed to combat script kiddies, and does not use any special method to check the verity of the mod list. In short, it would take a bit of effort to bypass this, but it will stop most regular cheaters from joining. 20 | 21 | At the moment, resource packs are not checked. This allows people to use advantageous resource packs like Xray resource packs on the server. You will need to install an anti-xray mod yourself. 22 | 23 | ## Installation 24 | 25 | Download the mod from [Modrinth](https://modrinth.com/mod/inertiaanticheat). 26 | 27 | Check [here](https://github.com/DiffuseHyperion/InertiaAntiCheat/wiki) for more details after downloading the mod. 28 | 29 | ## Permissions 30 | 31 | You are free to use this in modpacks or wherever you like! 32 | 33 | I would appreciate it if you could give a link back to this page or the project's github. 34 | -------------------------------------------------------------------------------- /src/main/java/com/diffusehyperion/inertiaanticheat/server/networking/method/id/ServerIdIndividualValidatorHandler.java: -------------------------------------------------------------------------------- 1 | package com.diffusehyperion.inertiaanticheat.server.networking.method.id; 2 | 3 | import com.diffusehyperion.inertiaanticheat.server.InertiaAntiCheatServer; 4 | import com.diffusehyperion.inertiaanticheat.server.networking.method.id.handlers.IdValidationHandler; 5 | 6 | import java.util.List; 7 | 8 | import static com.diffusehyperion.inertiaanticheat.server.InertiaAntiCheatServer.*; 9 | 10 | public class ServerIdIndividualValidatorHandler extends IdValidationHandler { 11 | public ServerIdIndividualValidatorHandler(Runnable failureTask, Runnable successTask, Runnable finishTask) { 12 | super(failureTask, successTask, finishTask); 13 | } 14 | 15 | @Override 16 | protected boolean validateMods(List modlist) { 17 | debugLine2(); 18 | debugInfo("Checking modlist now, using individual method"); 19 | debugInfo("Mod list size: " + modlist.size()); 20 | 21 | List blacklistedMods = InertiaAntiCheatServer.serverConfig.getList("validation.individual.blacklist"); 22 | debugInfo("Blacklisted mods: " + String.join(", ", blacklistedMods)); 23 | 24 | List whitelistedMods = InertiaAntiCheatServer.serverConfig.getList("validation.individual.whitelist"); 25 | debugInfo("Whitelisted mods: " + String.join(", ", whitelistedMods)); 26 | 27 | debugLine(); 28 | for (String mod : modlist) { 29 | if (blacklistedMods.contains(mod)) { 30 | debugInfo("Found in blacklist"); 31 | debugLine(); 32 | return false; 33 | } 34 | if (whitelistedMods.contains(mod)) { 35 | debugInfo("Found in whitelist"); 36 | debugLine(); 37 | whitelistedMods.remove(mod); 38 | } 39 | } 40 | if (!whitelistedMods.isEmpty()) { 41 | debugInfo("Whitelist not fulfilled"); 42 | debugLine(); 43 | return false; 44 | } 45 | debugInfo("Passed"); 46 | debugLine2(); 47 | return true; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/diffusehyperion/inertiaanticheat/server/networking/method/name/ServerNameIndividualValidatorHandler.java: -------------------------------------------------------------------------------- 1 | package com.diffusehyperion.inertiaanticheat.server.networking.method.name; 2 | 3 | import com.diffusehyperion.inertiaanticheat.server.InertiaAntiCheatServer; 4 | import com.diffusehyperion.inertiaanticheat.server.networking.method.name.handlers.NameValidationHandler; 5 | 6 | import java.util.List; 7 | 8 | import static com.diffusehyperion.inertiaanticheat.server.InertiaAntiCheatServer.*; 9 | 10 | public class ServerNameIndividualValidatorHandler extends NameValidationHandler { 11 | public ServerNameIndividualValidatorHandler(Runnable failureTask, Runnable successTask, Runnable finishTask) { 12 | super(failureTask, successTask, finishTask); 13 | } 14 | 15 | @Override 16 | protected boolean validateMods(List modlist) { 17 | debugLine2(); 18 | debugInfo("Checking modlist now, using individual method"); 19 | debugInfo("Mod list size: " + modlist.size()); 20 | 21 | List blacklistedMods = InertiaAntiCheatServer.serverConfig.getList("validation.individual.blacklist"); 22 | blacklistedMods.replaceAll((mod) -> (mod.endsWith(".jar") ? mod : mod + ".jar")); 23 | debugInfo("Blacklisted mods: " + String.join(", ", blacklistedMods)); 24 | 25 | List whitelistedMods = InertiaAntiCheatServer.serverConfig.getList("validation.individual.whitelist"); 26 | whitelistedMods.replaceAll((mod) -> (mod.endsWith(".jar") ? mod : mod + ".jar")); 27 | debugInfo("Whitelisted mods: " + String.join(", ", whitelistedMods)); 28 | 29 | debugLine(); 30 | for (String mod : modlist) { 31 | if (blacklistedMods.contains(mod)) { 32 | debugInfo("Found in blacklist"); 33 | debugLine(); 34 | return false; 35 | } 36 | if (whitelistedMods.contains(mod)) { 37 | debugInfo("Found in whitelist"); 38 | debugLine(); 39 | whitelistedMods.remove(mod); 40 | } 41 | } 42 | if (!whitelistedMods.isEmpty()) { 43 | debugInfo("Whitelist not fulfilled"); 44 | debugLine(); 45 | return false; 46 | } 47 | debugInfo("Passed"); 48 | debugLine2(); 49 | return true; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/diffusehyperion/inertiaanticheat/mixins/server/ServerQueryNetworkHandlerMixin.java: -------------------------------------------------------------------------------- 1 | package com.diffusehyperion.inertiaanticheat.mixins.server; 2 | 3 | import com.diffusehyperion.inertiaanticheat.common.networking.packets.S2C.AnticheatDetailsS2CPacket; 4 | import com.diffusehyperion.inertiaanticheat.common.util.GroupAnticheatDetails; 5 | import com.diffusehyperion.inertiaanticheat.common.util.IndividualAnticheatDetails; 6 | import com.diffusehyperion.inertiaanticheat.common.util.ValidationMethod; 7 | import com.diffusehyperion.inertiaanticheat.server.InertiaAntiCheatServer; 8 | import net.minecraft.network.ClientConnection; 9 | import net.minecraft.network.packet.c2s.query.QueryPingC2SPacket; 10 | import net.minecraft.server.network.ServerQueryNetworkHandler; 11 | import org.spongepowered.asm.mixin.Final; 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 | @Mixin(ServerQueryNetworkHandler.class) 19 | public abstract class ServerQueryNetworkHandlerMixin { 20 | @Shadow @Final 21 | private ClientConnection connection; 22 | 23 | @Inject(method = "onQueryPing", 24 | at = @At(value = "HEAD")) 25 | private void injectSendAnticheatDetails(QueryPingC2SPacket packet, CallbackInfo ci) { 26 | if (InertiaAntiCheatServer.validationMethod == ValidationMethod.INDIVIDUAL) { 27 | IndividualAnticheatDetails details = 28 | new IndividualAnticheatDetails( 29 | InertiaAntiCheatServer.serverConfig.getBoolean("motd.showInstalled"), 30 | InertiaAntiCheatServer.serverConfig.getList("motd.blacklist"), 31 | InertiaAntiCheatServer.serverConfig.getList("motd.whitelist")); 32 | this.connection.send(new AnticheatDetailsS2CPacket(details)); 33 | } else { 34 | GroupAnticheatDetails details = 35 | new GroupAnticheatDetails( 36 | InertiaAntiCheatServer.serverConfig.getBoolean("motd.showInstalled"), 37 | InertiaAntiCheatServer.serverConfig.getList("motd.hash")); 38 | this.connection.send(new AnticheatDetailsS2CPacket(details)); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/diffusehyperion/inertiaanticheat/server/networking/method/data/ServerDataIndividualValidatorHandler.java: -------------------------------------------------------------------------------- 1 | package com.diffusehyperion.inertiaanticheat.server.networking.method.data; 2 | 3 | import com.diffusehyperion.inertiaanticheat.common.InertiaAntiCheat; 4 | import com.diffusehyperion.inertiaanticheat.server.InertiaAntiCheatServer; 5 | import com.diffusehyperion.inertiaanticheat.server.networking.method.data.handlers.DataValidationHandler; 6 | 7 | import java.util.List; 8 | 9 | import static com.diffusehyperion.inertiaanticheat.server.InertiaAntiCheatServer.*; 10 | 11 | public class ServerDataIndividualValidatorHandler extends DataValidationHandler { 12 | 13 | public ServerDataIndividualValidatorHandler(Runnable failureTask, Runnable successTask, Runnable finishTask) { 14 | super(failureTask, successTask, finishTask); 15 | } 16 | 17 | @Override 18 | public boolean validateMods(List modlist) { 19 | debugLine2(); 20 | debugInfo("Checking modlist now, using individual method"); 21 | debugInfo("Mod list size: " + modlist.size()); 22 | List blacklistedMods = InertiaAntiCheatServer.serverConfig.getList("validation.individual.blacklist"); 23 | debugInfo("Blacklisted mods: " + String.join(", ", blacklistedMods)); 24 | List whitelistedMods = InertiaAntiCheatServer.serverConfig.getList("validation.individual.whitelist"); 25 | debugInfo("Whitelisted mods: " + String.join(", ", whitelistedMods)); 26 | debugLine(); 27 | for (byte[] mod : modlist) { 28 | String fileHash = InertiaAntiCheat.getHash(mod, InertiaAntiCheatServer.hashAlgorithm); 29 | debugInfo("File hash: " + fileHash + "; with algorithm " + InertiaAntiCheatServer.hashAlgorithm); 30 | 31 | if (blacklistedMods.contains(fileHash)) { 32 | debugInfo("Found in blacklist"); 33 | debugLine(); 34 | return false; 35 | } 36 | if (whitelistedMods.contains(fileHash)) { 37 | debugInfo("Found in whitelist"); 38 | whitelistedMods.remove(fileHash); 39 | } 40 | debugLine(); 41 | } 42 | if (!whitelistedMods.isEmpty()) { 43 | debugInfo("Whitelist not fulfilled"); 44 | debugLine(); 45 | return false; 46 | } 47 | debugInfo("Passed"); 48 | debugLine2(); 49 | return true; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/diffusehyperion/inertiaanticheat/server/networking/method/id/ServerIdGroupValidatorHandler.java: -------------------------------------------------------------------------------- 1 | package com.diffusehyperion.inertiaanticheat.server.networking.method.id; 2 | 3 | import com.diffusehyperion.inertiaanticheat.common.InertiaAntiCheat; 4 | import com.diffusehyperion.inertiaanticheat.common.util.HashAlgorithm; 5 | import com.diffusehyperion.inertiaanticheat.server.InertiaAntiCheatServer; 6 | import com.diffusehyperion.inertiaanticheat.server.networking.method.id.handlers.IdValidationHandler; 7 | 8 | import java.util.ArrayList; 9 | import java.util.Collections; 10 | import java.util.List; 11 | 12 | import static com.diffusehyperion.inertiaanticheat.server.InertiaAntiCheatServer.debugInfo; 13 | import static com.diffusehyperion.inertiaanticheat.server.InertiaAntiCheatServer.debugLine2; 14 | 15 | public class ServerIdGroupValidatorHandler extends IdValidationHandler { 16 | public ServerIdGroupValidatorHandler(Runnable failureTask, Runnable successTask, Runnable finishTask) { 17 | super(failureTask, successTask, finishTask); 18 | } 19 | 20 | @Override 21 | protected boolean validateMods(List modlist) { 22 | debugLine2(); 23 | debugInfo("Checking modlist now, using group method"); 24 | 25 | List softWhitelistedMods = InertiaAntiCheatServer.serverConfig.getList("validation.group.softWhitelist"); 26 | debugInfo("Soft whitelisted mods: " + String.join(", ", softWhitelistedMods)); 27 | 28 | List hashes = new ArrayList<>(); 29 | List copySoftWhitelistedMods = new ArrayList<>(softWhitelistedMods); 30 | for (String mod : modlist) { 31 | if (copySoftWhitelistedMods.contains(mod)) { 32 | copySoftWhitelistedMods.remove(mod); 33 | } else { 34 | hashes.add(mod); 35 | } 36 | } 37 | Collections.sort(hashes); 38 | String combinedHash = String.join("|", hashes); 39 | String finalHash = InertiaAntiCheat.getHash(combinedHash.getBytes(), HashAlgorithm.MD5); // no need to be cryptographically safe here 40 | debugInfo("Final hash: " + finalHash); 41 | debugInfo("Combined hash: " + combinedHash); 42 | 43 | 44 | boolean success = InertiaAntiCheatServer.serverConfig.getList("validation.group.hash").contains(finalHash); 45 | if (success) { 46 | debugInfo("Passed"); 47 | } else { 48 | debugInfo("Failed"); 49 | } 50 | debugLine2(); 51 | return success; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/diffusehyperion/inertiaanticheat/server/networking/method/name/ServerNameGroupValidatorHandler.java: -------------------------------------------------------------------------------- 1 | package com.diffusehyperion.inertiaanticheat.server.networking.method.name; 2 | 3 | import com.diffusehyperion.inertiaanticheat.common.InertiaAntiCheat; 4 | import com.diffusehyperion.inertiaanticheat.common.util.HashAlgorithm; 5 | import com.diffusehyperion.inertiaanticheat.server.InertiaAntiCheatServer; 6 | import com.diffusehyperion.inertiaanticheat.server.networking.method.name.handlers.NameValidationHandler; 7 | 8 | import java.util.ArrayList; 9 | import java.util.Collections; 10 | import java.util.List; 11 | 12 | import static com.diffusehyperion.inertiaanticheat.server.InertiaAntiCheatServer.debugInfo; 13 | import static com.diffusehyperion.inertiaanticheat.server.InertiaAntiCheatServer.debugLine2; 14 | 15 | public class ServerNameGroupValidatorHandler extends NameValidationHandler { 16 | public ServerNameGroupValidatorHandler(Runnable failureTask, Runnable successTask, Runnable finishTask) { 17 | super(failureTask, successTask, finishTask); 18 | } 19 | 20 | @Override 21 | protected boolean validateMods(List modlist) { 22 | debugLine2(); 23 | debugInfo("Checking modlist now, using group method"); 24 | 25 | List softWhitelistedMods = InertiaAntiCheatServer.serverConfig.getList("validation.group.softWhitelist"); 26 | softWhitelistedMods.replaceAll((mod) -> (mod.endsWith(".jar") ? mod : mod + ".jar")); 27 | debugInfo("Soft whitelisted mods: " + String.join(", ", softWhitelistedMods)); 28 | 29 | List hashes = new ArrayList<>(); 30 | List copySoftWhitelistedMods = new ArrayList<>(softWhitelistedMods); 31 | for (String mod : modlist) { 32 | if (copySoftWhitelistedMods.contains(mod)) { 33 | copySoftWhitelistedMods.remove(mod); 34 | } else { 35 | hashes.add(mod); 36 | } 37 | } 38 | Collections.sort(hashes); 39 | String combinedHash = String.join("|", hashes); 40 | String finalHash = InertiaAntiCheat.getHash(combinedHash.getBytes(), HashAlgorithm.MD5); // no need to be cryptographically safe here 41 | debugInfo("Final hash: " + finalHash); 42 | debugInfo("Combined hash: " + combinedHash); 43 | 44 | 45 | boolean success = InertiaAntiCheatServer.serverConfig.getList("validation.group.hash").contains(finalHash); 46 | if (success) { 47 | debugInfo("Passed"); 48 | } else { 49 | debugInfo("Failed"); 50 | } 51 | debugLine2(); 52 | return success; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/com/diffusehyperion/inertiaanticheat/server/networking/method/data/ServerDataGroupValidatorHandler.java: -------------------------------------------------------------------------------- 1 | package com.diffusehyperion.inertiaanticheat.server.networking.method.data; 2 | 3 | import com.diffusehyperion.inertiaanticheat.common.InertiaAntiCheat; 4 | import com.diffusehyperion.inertiaanticheat.common.util.HashAlgorithm; 5 | import com.diffusehyperion.inertiaanticheat.server.InertiaAntiCheatServer; 6 | import com.diffusehyperion.inertiaanticheat.server.networking.method.data.handlers.DataValidationHandler; 7 | 8 | import java.util.ArrayList; 9 | import java.util.Collections; 10 | import java.util.List; 11 | 12 | import static com.diffusehyperion.inertiaanticheat.server.InertiaAntiCheatServer.debugInfo; 13 | import static com.diffusehyperion.inertiaanticheat.server.InertiaAntiCheatServer.debugLine2; 14 | 15 | public class ServerDataGroupValidatorHandler extends DataValidationHandler { 16 | public ServerDataGroupValidatorHandler(Runnable failureTask, Runnable successTask, Runnable finishTask) { 17 | super(failureTask, successTask, finishTask); 18 | } 19 | 20 | @Override 21 | public boolean validateMods(List modlist) { 22 | debugLine2(); 23 | debugInfo("Checking modlist now, using group method"); 24 | List softWhitelistedMods = InertiaAntiCheatServer.serverConfig.getList("validation.group.softWhitelist"); 25 | debugInfo("Soft whitelisted mods: " + String.join(", ", softWhitelistedMods)); 26 | List hashes = new ArrayList<>(); 27 | List copySoftWhitelistedMods = new ArrayList<>(softWhitelistedMods); 28 | for (byte[] mod : modlist) { 29 | String fileHash = InertiaAntiCheat.getHash(mod, InertiaAntiCheatServer.hashAlgorithm); 30 | if (copySoftWhitelistedMods.contains(fileHash)) { 31 | copySoftWhitelistedMods.remove(fileHash); 32 | } else { 33 | hashes.add(fileHash); 34 | } 35 | } 36 | Collections.sort(hashes); 37 | String combinedHash = String.join("|", hashes); 38 | String finalHash = InertiaAntiCheat.getHash(combinedHash.getBytes(), HashAlgorithm.MD5); // no need to be cryptographically safe here 39 | debugInfo("Final hash: " + finalHash); 40 | debugInfo("Combined hash: " + combinedHash); 41 | 42 | 43 | boolean success = InertiaAntiCheatServer.serverConfig.getList("validation.group.hash").contains(finalHash); 44 | if (success) { 45 | debugInfo("Passed"); 46 | } else { 47 | debugInfo("Failed"); 48 | } 49 | debugLine2(); 50 | return success; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/diffusehyperion/inertiaanticheat/server/networking/method/id/ServerIdReceiverHandler.java: -------------------------------------------------------------------------------- 1 | package com.diffusehyperion.inertiaanticheat.server.networking.method.id; 2 | 3 | import com.diffusehyperion.inertiaanticheat.common.InertiaAntiCheat; 4 | import com.diffusehyperion.inertiaanticheat.common.util.InertiaAntiCheatConstants; 5 | import com.diffusehyperion.inertiaanticheat.server.networking.method.id.handlers.IdReceiverHandler; 6 | import com.diffusehyperion.inertiaanticheat.server.networking.method.id.handlers.IdValidationHandler; 7 | import net.fabricmc.fabric.api.networking.v1.LoginPacketSender; 8 | import net.fabricmc.fabric.api.networking.v1.PacketByteBufs; 9 | import net.fabricmc.fabric.api.networking.v1.PacketSender; 10 | import net.fabricmc.fabric.api.networking.v1.ServerLoginNetworking; 11 | import net.minecraft.network.PacketByteBuf; 12 | import net.minecraft.server.MinecraftServer; 13 | import net.minecraft.server.network.ServerLoginNetworkHandler; 14 | import net.minecraft.util.Identifier; 15 | 16 | import java.nio.charset.StandardCharsets; 17 | import java.security.KeyPair; 18 | import java.util.ArrayList; 19 | import java.util.List; 20 | 21 | import static com.diffusehyperion.inertiaanticheat.server.InertiaAntiCheatServer.debugInfo; 22 | import static com.diffusehyperion.inertiaanticheat.server.InertiaAntiCheatServer.debugLine; 23 | 24 | public class ServerIdReceiverHandler extends IdReceiverHandler { 25 | private int currentIndex = 0; 26 | private final List collectedMods = new ArrayList<>(); 27 | 28 | public ServerIdReceiverHandler(KeyPair keyPair, Identifier modTransferID, ServerLoginNetworkHandler handler, IdValidationHandler validator) { 29 | super(keyPair, modTransferID, handler, validator); 30 | } 31 | 32 | @Override 33 | protected void receiveMod(MinecraftServer minecraftServer, ServerLoginNetworkHandler handler, boolean b, PacketByteBuf buf, ServerLoginNetworking.LoginSynchronizer synchronizer, PacketSender packetSender) { 34 | if (!b) { 35 | ServerLoginNetworking.unregisterReceiver(handler, InertiaAntiCheatConstants.SEND_MOD); 36 | this.validator.checkModlist(collectedMods); 37 | return; 38 | } 39 | 40 | LoginPacketSender sender = (LoginPacketSender) packetSender; 41 | debugInfo("Receiving mod " + this.currentIndex); 42 | 43 | byte[] name = InertiaAntiCheat.decryptAESRSAEncodedBuf(buf, super.keyPair.getPrivate()); 44 | 45 | this.collectedMods.add(new String(name, StandardCharsets.UTF_8)); 46 | this.currentIndex += 1; 47 | 48 | debugInfo("Continuing transfer"); 49 | sender.sendPacket(this.modTransferID, PacketByteBufs.empty()); 50 | 51 | debugLine(); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/diffusehyperion/inertiaanticheat/server/networking/method/name/ServerNameReceiverHandler.java: -------------------------------------------------------------------------------- 1 | package com.diffusehyperion.inertiaanticheat.server.networking.method.name; 2 | 3 | import com.diffusehyperion.inertiaanticheat.common.InertiaAntiCheat; 4 | import com.diffusehyperion.inertiaanticheat.common.util.InertiaAntiCheatConstants; 5 | import com.diffusehyperion.inertiaanticheat.server.networking.method.name.handlers.NameReceiverHandler; 6 | import com.diffusehyperion.inertiaanticheat.server.networking.method.name.handlers.NameValidationHandler; 7 | import net.fabricmc.fabric.api.networking.v1.LoginPacketSender; 8 | import net.fabricmc.fabric.api.networking.v1.PacketByteBufs; 9 | import net.fabricmc.fabric.api.networking.v1.PacketSender; 10 | import net.fabricmc.fabric.api.networking.v1.ServerLoginNetworking; 11 | import net.minecraft.network.PacketByteBuf; 12 | import net.minecraft.server.MinecraftServer; 13 | import net.minecraft.server.network.ServerLoginNetworkHandler; 14 | import net.minecraft.util.Identifier; 15 | 16 | import java.nio.charset.StandardCharsets; 17 | import java.security.KeyPair; 18 | import java.util.ArrayList; 19 | import java.util.List; 20 | 21 | import static com.diffusehyperion.inertiaanticheat.server.InertiaAntiCheatServer.debugInfo; 22 | import static com.diffusehyperion.inertiaanticheat.server.InertiaAntiCheatServer.debugLine; 23 | 24 | public class ServerNameReceiverHandler extends NameReceiverHandler { 25 | private int currentIndex = 0; 26 | private final List collectedMods = new ArrayList<>(); 27 | 28 | public ServerNameReceiverHandler(KeyPair keyPair, Identifier modTransferID, ServerLoginNetworkHandler handler, NameValidationHandler validator) { 29 | super(keyPair, modTransferID, handler, validator); 30 | } 31 | 32 | @Override 33 | protected void receiveMod(MinecraftServer minecraftServer, ServerLoginNetworkHandler handler, boolean b, PacketByteBuf buf, ServerLoginNetworking.LoginSynchronizer synchronizer, PacketSender packetSender) { 34 | if (!b) { 35 | ServerLoginNetworking.unregisterReceiver(handler, InertiaAntiCheatConstants.SEND_MOD); 36 | this.validator.checkModlist(collectedMods); 37 | return; 38 | } 39 | 40 | LoginPacketSender sender = (LoginPacketSender) packetSender; 41 | debugInfo("Receiving mod " + this.currentIndex); 42 | 43 | byte[] name = InertiaAntiCheat.decryptAESRSAEncodedBuf(buf, super.keyPair.getPrivate()); 44 | 45 | this.collectedMods.add(new String(name, StandardCharsets.UTF_8)); 46 | this.currentIndex += 1; 47 | 48 | debugInfo("Continuing transfer"); 49 | sender.sendPacket(this.modTransferID, PacketByteBufs.empty()); 50 | 51 | debugLine(); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/client/java/com/diffusehyperion/inertiaanticheat/client/networking/method/id/ClientIdTransferHandler.java: -------------------------------------------------------------------------------- 1 | package com.diffusehyperion.inertiaanticheat.client.networking.method.id; 2 | 3 | import com.diffusehyperion.inertiaanticheat.client.InertiaAntiCheatClient; 4 | import com.diffusehyperion.inertiaanticheat.client.networking.method.TransferHandler; 5 | import com.diffusehyperion.inertiaanticheat.common.util.InertiaAntiCheatConstants; 6 | import io.netty.channel.ChannelFutureListener; 7 | import net.fabricmc.fabric.api.client.networking.v1.ClientLoginNetworking; 8 | import net.minecraft.client.MinecraftClient; 9 | import net.minecraft.client.network.ClientLoginNetworkHandler; 10 | import net.minecraft.network.PacketByteBuf; 11 | import net.minecraft.text.Text; 12 | import net.minecraft.util.Identifier; 13 | 14 | import java.nio.charset.StandardCharsets; 15 | import java.security.PublicKey; 16 | import java.util.concurrent.CompletableFuture; 17 | import java.util.function.Consumer; 18 | 19 | import static com.diffusehyperion.inertiaanticheat.client.InertiaAntiCheatClient.debugInfo; 20 | import static com.diffusehyperion.inertiaanticheat.client.InertiaAntiCheatClient.debugLine; 21 | 22 | public class ClientIdTransferHandler extends TransferHandler { 23 | private final int maxIndex; 24 | private int currentIndex; 25 | 26 | public ClientIdTransferHandler(PublicKey publicKey, Identifier modTransferID, Consumer secondaryStatusConsumer) { 27 | super(publicKey, modTransferID, secondaryStatusConsumer, InertiaAntiCheatClient.allModIds.size()); 28 | 29 | debugInfo("Creating id transfer handler"); 30 | 31 | this.maxIndex = InertiaAntiCheatClient.allModIds.size(); 32 | this.currentIndex = 0; 33 | } 34 | 35 | @Override 36 | public CompletableFuture transferMod(MinecraftClient client, ClientLoginNetworkHandler handler, PacketByteBuf buf, Consumer callbacksConsumer) { 37 | debugInfo("Sending mod ID " + this.currentIndex); 38 | 39 | if (this.currentIndex >= this.maxIndex) { 40 | // All files have been sent, returning null to signify goodbye 41 | debugInfo("Sending final packet"); 42 | debugLine(); 43 | 44 | this.setCompleteTransferStatus(); 45 | 46 | ClientLoginNetworking.unregisterGlobalReceiver(InertiaAntiCheatConstants.SEND_MOD); 47 | return CompletableFuture.completedFuture(null); 48 | } 49 | PacketByteBuf responseBuf = this.preparePacket(InertiaAntiCheatClient.allModIds.get(currentIndex).getBytes(StandardCharsets.UTF_8)); 50 | 51 | this.increaseSentModsStatus(); 52 | this.currentIndex++; 53 | 54 | debugLine(); 55 | return CompletableFuture.completedFuture(responseBuf); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/client/java/com/diffusehyperion/inertiaanticheat/client/networking/method/name/ClientNameTransferHandler.java: -------------------------------------------------------------------------------- 1 | package com.diffusehyperion.inertiaanticheat.client.networking.method.name; 2 | 3 | import com.diffusehyperion.inertiaanticheat.client.InertiaAntiCheatClient; 4 | import com.diffusehyperion.inertiaanticheat.client.networking.method.TransferHandler; 5 | import com.diffusehyperion.inertiaanticheat.common.util.InertiaAntiCheatConstants; 6 | import io.netty.channel.ChannelFutureListener; 7 | import net.fabricmc.fabric.api.client.networking.v1.ClientLoginNetworking; 8 | import net.minecraft.client.MinecraftClient; 9 | import net.minecraft.client.network.ClientLoginNetworkHandler; 10 | import net.minecraft.network.PacketByteBuf; 11 | import net.minecraft.text.Text; 12 | import net.minecraft.util.Identifier; 13 | 14 | import java.nio.charset.StandardCharsets; 15 | import java.security.PublicKey; 16 | import java.util.concurrent.CompletableFuture; 17 | import java.util.function.Consumer; 18 | 19 | import static com.diffusehyperion.inertiaanticheat.client.InertiaAntiCheatClient.debugInfo; 20 | import static com.diffusehyperion.inertiaanticheat.client.InertiaAntiCheatClient.debugLine; 21 | 22 | public class ClientNameTransferHandler extends TransferHandler { 23 | private final int maxIndex; 24 | private int currentIndex; 25 | 26 | public ClientNameTransferHandler(PublicKey publicKey, Identifier modTransferID, Consumer secondaryStatusConsumer) { 27 | super(publicKey, modTransferID, secondaryStatusConsumer, InertiaAntiCheatClient.allModNames.size()); 28 | 29 | debugInfo("Creating name transfer handler"); 30 | 31 | this.maxIndex = InertiaAntiCheatClient.allModNames.size(); 32 | this.currentIndex = 0; 33 | } 34 | 35 | @Override 36 | public CompletableFuture transferMod(MinecraftClient client, ClientLoginNetworkHandler handler, PacketByteBuf buf, Consumer callbacksConsumer) { 37 | debugInfo("Sending mod " + this.currentIndex); 38 | 39 | if (this.currentIndex >= this.maxIndex) { 40 | // All files have been sent, returning null to signify goodbye 41 | debugInfo("Sending final packet"); 42 | debugLine(); 43 | 44 | this.setCompleteTransferStatus(); 45 | 46 | ClientLoginNetworking.unregisterGlobalReceiver(InertiaAntiCheatConstants.SEND_MOD); 47 | return CompletableFuture.completedFuture(null); 48 | } 49 | PacketByteBuf responseBuf = this.preparePacket(InertiaAntiCheatClient.allModNames.get(currentIndex).getBytes(StandardCharsets.UTF_8)); 50 | 51 | this.increaseSentModsStatus(); 52 | this.currentIndex++; 53 | 54 | debugLine(); 55 | return CompletableFuture.completedFuture(responseBuf); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/com/diffusehyperion/inertiaanticheat/common/networking/packets/S2C/AnticheatDetailsS2CPacket.java: -------------------------------------------------------------------------------- 1 | package com.diffusehyperion.inertiaanticheat.common.networking.packets.S2C; 2 | 3 | import com.diffusehyperion.inertiaanticheat.common.networking.packets.UpgradedClientQueryPacketListener; 4 | import com.diffusehyperion.inertiaanticheat.common.util.AnticheatDetails; 5 | import com.diffusehyperion.inertiaanticheat.common.util.GroupAnticheatDetails; 6 | import com.diffusehyperion.inertiaanticheat.common.util.IndividualAnticheatDetails; 7 | import com.diffusehyperion.inertiaanticheat.server.networking.packets.AnticheatPackets; 8 | import net.minecraft.network.PacketByteBuf; 9 | import net.minecraft.network.codec.PacketCodec; 10 | import net.minecraft.network.packet.Packet; 11 | import net.minecraft.network.packet.PacketType; 12 | 13 | import java.util.ArrayList; 14 | import java.util.Arrays; 15 | 16 | public record AnticheatDetailsS2CPacket(AnticheatDetails details) implements Packet { 17 | public static final PacketCodec CODEC = Packet.createCodec(AnticheatDetailsS2CPacket::write, AnticheatDetailsS2CPacket::new); 18 | 19 | private AnticheatDetailsS2CPacket(PacketByteBuf packetByteBuf) { 20 | this(bufToDetails(packetByteBuf)); 21 | } 22 | 23 | private static AnticheatDetails bufToDetails(PacketByteBuf buf) { 24 | int ordinal = buf.readInt(); 25 | if (ordinal == 0) { 26 | return new IndividualAnticheatDetails( 27 | buf.readBoolean(), 28 | new ArrayList<>(Arrays.asList(buf.readString().split(","))), 29 | new ArrayList<>(Arrays.asList(buf.readString().split(",")))); 30 | } else if (ordinal == 1) { 31 | return new GroupAnticheatDetails( 32 | buf.readBoolean(), 33 | new ArrayList<>(Arrays.asList(buf.readString().split(","))) 34 | ); 35 | } else { 36 | throw new RuntimeException("Unknown ordinal given"); 37 | } 38 | } 39 | 40 | public void write(PacketByteBuf buf) { 41 | buf.writeInt(this.details.getValidationMethod().ordinal()); 42 | if (this.details instanceof IndividualAnticheatDetails individualDetails) { 43 | buf.writeBoolean(individualDetails.showInstalled()); 44 | buf.writeString(String.join(",", individualDetails.getBlacklistedMods())); 45 | buf.writeString(String.join(",", individualDetails.getWhitelistedMods())); 46 | } else if (this.details instanceof GroupAnticheatDetails groupDetails) { 47 | buf.writeBoolean(groupDetails.showInstalled()); 48 | buf.writeString(String.join(",", groupDetails.getModpackDetails())); 49 | } 50 | } 51 | 52 | @Override 53 | public PacketType> getPacketType() { 54 | return AnticheatPackets.DETAILS_RESPONSE; 55 | } 56 | 57 | public void apply(UpgradedClientQueryPacketListener listener) { 58 | listener.onReceiveAnticheatDetails(this); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | @rem SPDX-License-Identifier: Apache-2.0 17 | @rem 18 | 19 | @if "%DEBUG%"=="" @echo off 20 | @rem ########################################################################## 21 | @rem 22 | @rem Gradle startup script for Windows 23 | @rem 24 | @rem ########################################################################## 25 | 26 | @rem Set local scope for the variables with windows NT shell 27 | if "%OS%"=="Windows_NT" setlocal 28 | 29 | set DIRNAME=%~dp0 30 | if "%DIRNAME%"=="" set DIRNAME=. 31 | @rem This is normally unused 32 | set APP_BASE_NAME=%~n0 33 | set APP_HOME=%DIRNAME% 34 | 35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 37 | 38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 40 | 41 | @rem Find java.exe 42 | if defined JAVA_HOME goto findJavaFromJavaHome 43 | 44 | set JAVA_EXE=java.exe 45 | %JAVA_EXE% -version >NUL 2>&1 46 | if %ERRORLEVEL% equ 0 goto execute 47 | 48 | echo. 1>&2 49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 50 | echo. 1>&2 51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 52 | echo location of your Java installation. 1>&2 53 | 54 | goto fail 55 | 56 | :findJavaFromJavaHome 57 | set JAVA_HOME=%JAVA_HOME:"=% 58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 59 | 60 | if exist "%JAVA_EXE%" goto execute 61 | 62 | echo. 1>&2 63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 64 | echo. 1>&2 65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 66 | echo location of your Java installation. 1>&2 67 | 68 | goto fail 69 | 70 | :execute 71 | @rem Setup the command line 72 | 73 | 74 | 75 | @rem Execute Gradle 76 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* 77 | 78 | :end 79 | @rem End local scope for the variables with windows NT shell 80 | if %ERRORLEVEL% equ 0 goto mainEnd 81 | 82 | :fail 83 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 84 | rem the _cmd.exe /c_ return code! 85 | set EXIT_CODE=%ERRORLEVEL% 86 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 87 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 88 | exit /b %EXIT_CODE% 89 | 90 | :mainEnd 91 | if "%OS%"=="Windows_NT" endlocal 92 | 93 | :omega 94 | -------------------------------------------------------------------------------- /src/main/java/com/diffusehyperion/inertiaanticheat/server/networking/method/data/ServerDataReceiverHandler.java: -------------------------------------------------------------------------------- 1 | package com.diffusehyperion.inertiaanticheat.server.networking.method.data; 2 | 3 | import com.diffusehyperion.inertiaanticheat.common.InertiaAntiCheat; 4 | import com.diffusehyperion.inertiaanticheat.common.util.HashAlgorithm; 5 | import com.diffusehyperion.inertiaanticheat.common.util.InertiaAntiCheatConstants; 6 | import com.diffusehyperion.inertiaanticheat.server.networking.method.data.handlers.DataReceiverHandler; 7 | import com.diffusehyperion.inertiaanticheat.server.networking.method.data.handlers.DataValidationHandler; 8 | import net.fabricmc.fabric.api.networking.v1.LoginPacketSender; 9 | import net.fabricmc.fabric.api.networking.v1.PacketByteBufs; 10 | import net.fabricmc.fabric.api.networking.v1.PacketSender; 11 | import net.fabricmc.fabric.api.networking.v1.ServerLoginNetworking; 12 | import net.minecraft.network.PacketByteBuf; 13 | import net.minecraft.server.MinecraftServer; 14 | import net.minecraft.server.network.ServerLoginNetworkHandler; 15 | import net.minecraft.util.Identifier; 16 | import org.apache.commons.lang3.ArrayUtils; 17 | 18 | import java.security.KeyPair; 19 | import java.util.ArrayList; 20 | import java.util.List; 21 | 22 | import static com.diffusehyperion.inertiaanticheat.server.InertiaAntiCheatServer.debugInfo; 23 | import static com.diffusehyperion.inertiaanticheat.server.InertiaAntiCheatServer.debugLine; 24 | 25 | public class ServerDataReceiverHandler extends DataReceiverHandler { 26 | private int currentIndex = 0; 27 | private byte[] buffer; 28 | 29 | private final List collectedMods = new ArrayList<>(); 30 | 31 | public ServerDataReceiverHandler(KeyPair keyPair, Identifier modTransferID, ServerLoginNetworkHandler handler, DataValidationHandler validator) { 32 | super(keyPair, modTransferID, handler, validator); 33 | } 34 | 35 | @Override 36 | public void receiveMod(MinecraftServer minecraftServer, ServerLoginNetworkHandler handler, boolean b, PacketByteBuf buf, ServerLoginNetworking.LoginSynchronizer synchronizer, PacketSender packetSender) { 37 | if (!b) { 38 | ServerLoginNetworking.unregisterReceiver(handler, InertiaAntiCheatConstants.SEND_MOD); 39 | this.validator.checkModlist(collectedMods); 40 | return; 41 | } 42 | 43 | LoginPacketSender sender = (LoginPacketSender) packetSender; 44 | debugInfo("Receiving mod " + this.currentIndex); 45 | 46 | boolean isFinalChunk = buf.readBoolean(); 47 | debugInfo("Final chunk: " + isFinalChunk); 48 | 49 | byte[] fileData = InertiaAntiCheat.decryptAESRSAEncodedBuf(buf, super.keyPair.getPrivate()); 50 | debugInfo("Checksum of chunk: " + InertiaAntiCheat.getHash(fileData, HashAlgorithm.MD5)); 51 | this.buffer = ArrayUtils.addAll(this.buffer, fileData); 52 | 53 | if (isFinalChunk) { 54 | debugInfo("Adding mod, checksum: " + InertiaAntiCheat.getHash(this.buffer, HashAlgorithm.MD5)); 55 | 56 | this.collectedMods.add(this.buffer); 57 | this.buffer = new byte[]{}; 58 | this.currentIndex += 1; 59 | } 60 | 61 | debugInfo("Continuing transfer"); 62 | sender.sendPacket(this.modTransferID, PacketByteBufs.empty()); 63 | 64 | debugLine(); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/client/java/com/diffusehyperion/inertiaanticheat/client/networking/method/TransferHandler.java: -------------------------------------------------------------------------------- 1 | package com.diffusehyperion.inertiaanticheat.client.networking.method; 2 | 3 | import com.diffusehyperion.inertiaanticheat.common.InertiaAntiCheat; 4 | import com.diffusehyperion.inertiaanticheat.common.util.InertiaAntiCheatConstants; 5 | import io.netty.channel.ChannelFutureListener; 6 | import net.fabricmc.fabric.api.client.networking.v1.ClientLoginNetworking; 7 | import net.fabricmc.fabric.api.networking.v1.PacketByteBufs; 8 | import net.minecraft.client.MinecraftClient; 9 | import net.minecraft.client.network.ClientLoginNetworkHandler; 10 | import net.minecraft.network.PacketByteBuf; 11 | import net.minecraft.text.Text; 12 | import net.minecraft.util.Identifier; 13 | 14 | import javax.crypto.SecretKey; 15 | import java.security.PublicKey; 16 | import java.util.concurrent.CompletableFuture; 17 | import java.util.function.Consumer; 18 | 19 | public abstract class TransferHandler { 20 | protected final PublicKey publicKey; 21 | protected final Identifier modTransferID; 22 | protected final Consumer secondaryStatusConsumer; 23 | 24 | private int sentMods; 25 | private final int totalMods; 26 | 27 | public TransferHandler(PublicKey publicKey, Identifier modTransferID, Consumer secondaryStatusConsumer, int totalMods) { 28 | this.publicKey = publicKey; 29 | this.modTransferID = modTransferID; 30 | this.secondaryStatusConsumer = secondaryStatusConsumer; 31 | 32 | this.sentMods = 0; 33 | this.totalMods = totalMods; 34 | this.updateSecondaryStatus("Sent 0/" + totalMods + " mods"); 35 | 36 | ClientLoginNetworking.registerReceiver(InertiaAntiCheatConstants.SEND_MOD, this::transferMod); 37 | } 38 | 39 | protected abstract CompletableFuture transferMod(MinecraftClient client, ClientLoginNetworkHandler handler, PacketByteBuf buf, Consumer callbacksConsumer); 40 | 41 | public void onDisconnect(ClientLoginNetworkHandler ignored1, MinecraftClient ignored2) { 42 | ClientLoginNetworking.unregisterReceiver(this.modTransferID); 43 | } 44 | 45 | protected PacketByteBuf preparePacket(byte[] data) { 46 | PacketByteBuf buf = PacketByteBufs.create(); 47 | 48 | return this.preparePacket(buf, data); 49 | } 50 | 51 | protected PacketByteBuf preparePacket(PacketByteBuf buf, byte[] data) { 52 | SecretKey secretKey = InertiaAntiCheat.createAESKey(); 53 | 54 | byte[] encryptedRSASecretKey = InertiaAntiCheat.encryptRSABytes(secretKey.getEncoded(), this.publicKey); 55 | byte[] encryptedAESNameData = InertiaAntiCheat.encryptAESBytes(data, secretKey); 56 | buf.writeInt(encryptedRSASecretKey.length); 57 | buf.writeBytes(encryptedRSASecretKey); 58 | buf.writeBytes(encryptedAESNameData); 59 | 60 | return buf; 61 | } 62 | 63 | protected void setCompleteTransferStatus() { 64 | this.secondaryStatusConsumer.accept(Text.of("Waiting for validation...")); 65 | } 66 | 67 | protected void increaseSentModsStatus() { 68 | this.sentMods++; 69 | this.updateSecondaryStatus("Sent " + this.sentMods + "/" + this.totalMods + " mods"); 70 | } 71 | 72 | private void updateSecondaryStatus(String message) { 73 | this.secondaryStatusConsumer.accept(Text.of(message)); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/client/java/com/diffusehyperion/inertiaanticheat/mixins/client/ConnectScreenThreadMixin.java: -------------------------------------------------------------------------------- 1 | package com.diffusehyperion.inertiaanticheat.mixins.client; 2 | 3 | import com.diffusehyperion.inertiaanticheat.common.interfaces.UpgradedClientLoginNetworkHandler; 4 | import com.diffusehyperion.inertiaanticheat.common.interfaces.UpgradedConnectScreen; 5 | import com.llamalad7.mixinextras.sugar.Share; 6 | import com.llamalad7.mixinextras.sugar.ref.LocalRef; 7 | import net.minecraft.client.MinecraftClient; 8 | import net.minecraft.client.gui.screen.multiplayer.ConnectScreen; 9 | import net.minecraft.client.network.ClientLoginNetworkHandler; 10 | import net.minecraft.client.network.CookieStorage; 11 | import net.minecraft.client.network.ServerInfo; 12 | import net.minecraft.client.world.ClientChunkLoadProgress; 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.injection.At; 17 | import org.spongepowered.asm.mixin.injection.Inject; 18 | import org.spongepowered.asm.mixin.injection.ModifyArgs; 19 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 20 | import org.spongepowered.asm.mixin.injection.invoke.arg.Args; 21 | 22 | // targets anonymous thread class in ConnectScreen.connect(client, address, ...) 23 | @Mixin(targets = "net.minecraft.client.gui.screen.multiplayer.ConnectScreen$1") 24 | public class ConnectScreenThreadMixin { 25 | @Shadow 26 | @Final 27 | MinecraftClient field_33738; 28 | 29 | @Shadow 30 | @Final 31 | ServerInfo field_40415; 32 | 33 | @Shadow 34 | @Final 35 | CookieStorage field_48396; 36 | 37 | @Shadow 38 | @Final 39 | ConnectScreen field_2416; 40 | 41 | @Inject( 42 | method = "run", 43 | at = @At( 44 | value = "INVOKE", 45 | target = "Lnet/minecraft/network/ClientConnection;connect(Ljava/lang/String;ILnet/minecraft/network/state/NetworkState;Lnet/minecraft/network/state/NetworkState;Lnet/minecraft/network/listener/ClientPacketListener;Z)V" 46 | ) 47 | ) 48 | private void createUpgradedLoginNetworkHandler( 49 | CallbackInfo ci, 50 | @Share("loginNetworkHandler") LocalRef loginNetworkHandlerLocalRef 51 | ) { 52 | ConnectScreenAccessor accessor = (ConnectScreenAccessor) field_2416; 53 | ClientLoginNetworkHandler handler = new ClientLoginNetworkHandler( 54 | accessor.getConnection(), 55 | field_33738, 56 | field_40415, 57 | accessor.getParent(), 58 | false, 59 | null, 60 | accessor::invokeSetStatus, 61 | new ClientChunkLoadProgress(), 62 | field_48396 63 | ); 64 | 65 | UpgradedClientLoginNetworkHandler upgradedHandler = (UpgradedClientLoginNetworkHandler) handler; 66 | UpgradedConnectScreen upgradedScreen = (UpgradedConnectScreen) field_2416; 67 | 68 | upgradedHandler.inertiaAntiCheat$setSecondaryStatusConsumer(upgradedScreen::inertiaAntiCheat$setSecondaryStatus); 69 | 70 | loginNetworkHandlerLocalRef.set(handler); 71 | } 72 | 73 | // ModifyArg had weird generic issues, this will work 74 | @ModifyArgs( 75 | method = "run", 76 | at = @At( 77 | value = "INVOKE", 78 | target = "Lnet/minecraft/network/ClientConnection;connect(Ljava/lang/String;ILnet/minecraft/network/state/NetworkState;Lnet/minecraft/network/state/NetworkState;Lnet/minecraft/network/listener/ClientPacketListener;Z)V" 79 | ) 80 | ) 81 | private void replaceLoginNetworkHandler(Args args, @Share("loginNetworkHandler") LocalRef loginNetworkHandlerLocalRef) { 82 | args.set(4, loginNetworkHandlerLocalRef.get()); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/client/java/com/diffusehyperion/inertiaanticheat/mixins/client/MultiplayerServerListPingerMixin.java: -------------------------------------------------------------------------------- 1 | package com.diffusehyperion.inertiaanticheat.mixins.client; 2 | 3 | import com.diffusehyperion.inertiaanticheat.client.interfaces.UpgradedClientCollection; 4 | import com.diffusehyperion.inertiaanticheat.client.networking.packets.UpgradedClientQueryNetworkHandler; 5 | import com.diffusehyperion.inertiaanticheat.common.interfaces.UpgradedServerInfo; 6 | import com.diffusehyperion.inertiaanticheat.common.networking.packets.UpgradedClientQueryPacketListener; 7 | import com.llamalad7.mixinextras.sugar.Local; 8 | import com.llamalad7.mixinextras.sugar.Share; 9 | import com.llamalad7.mixinextras.sugar.ref.LocalRef; 10 | import net.minecraft.client.network.MultiplayerServerListPinger; 11 | import net.minecraft.client.network.ServerAddress; 12 | import net.minecraft.client.network.ServerInfo; 13 | import net.minecraft.network.ClientConnection; 14 | import net.minecraft.network.NetworkingBackend; 15 | import net.minecraft.network.listener.ClientQueryPacketListener; 16 | import net.minecraft.text.Text; 17 | import org.spongepowered.asm.mixin.Mixin; 18 | import org.spongepowered.asm.mixin.Shadow; 19 | import org.spongepowered.asm.mixin.injection.At; 20 | import org.spongepowered.asm.mixin.injection.Inject; 21 | import org.spongepowered.asm.mixin.injection.Redirect; 22 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 23 | 24 | import java.net.InetSocketAddress; 25 | 26 | @Mixin(MultiplayerServerListPinger.class) 27 | public abstract class MultiplayerServerListPingerMixin { 28 | @Shadow 29 | void showError(Text error, ServerInfo info) {} 30 | @Shadow 31 | void ping(InetSocketAddress socketAddress, ServerAddress address, ServerInfo serverInfo, NetworkingBackend backend) {} 32 | 33 | @Inject(method = "add", 34 | at = @At(value = "HEAD")) 35 | private void setUpgradedServerPingRefs(ServerInfo entry, Runnable saver, Runnable pingCallback, NetworkingBackend backend, CallbackInfo ci, 36 | @Share("serverInfo") LocalRef serverDataLocalRef, 37 | @Share("saver") LocalRef saverLocalRef, 38 | @Share("pingCallback") LocalRef pingCallbackLocalRef, 39 | @Share("backend") LocalRef backendLocalRef) { 40 | serverDataLocalRef.set(entry); 41 | saverLocalRef.set(saver); 42 | pingCallbackLocalRef.set(pingCallback); 43 | backendLocalRef.set(backend); 44 | } 45 | 46 | @Redirect(method = "add", 47 | at = @At(value = "INVOKE", target = "Lnet/minecraft/network/ClientConnection;connect(Ljava/lang/String;ILnet/minecraft/network/listener/ClientQueryPacketListener;)V")) 48 | private void upgradeServerPing( 49 | ClientConnection connection, String host, int port, ClientQueryPacketListener clientQueryPacketListener, 50 | @Share("serverInfo") LocalRef serverDataLocalRef, 51 | @Share("saver") LocalRef runnableLocalRef, 52 | @Share("pingCallback") LocalRef pingCallbackLocalRef, 53 | @Share("backend") LocalRef backendLocalRef, 54 | @Local InetSocketAddress inetSocketAddress, 55 | @Local ServerAddress serverAddress) { 56 | ServerInfo serverInfo = serverDataLocalRef.get(); 57 | Runnable saver = runnableLocalRef.get(); 58 | Runnable pingCallback = pingCallbackLocalRef.get(); 59 | NetworkingBackend backend = backendLocalRef.get(); 60 | 61 | UpgradedClientQueryPacketListener listener = 62 | new UpgradedClientQueryNetworkHandler(serverInfo, saver, pingCallback, backend, 63 | connection, inetSocketAddress, serverAddress, 64 | this::showError, 65 | this::ping); 66 | 67 | ((UpgradedServerInfo) serverInfo).inertiaAntiCheat$setInertiaInstalled(null); 68 | ((UpgradedServerInfo) serverInfo).inertiaAntiCheat$setAnticheatDetails(null); 69 | ((UpgradedClientCollection) connection).inertiaAntiCheat$connect(host, port, listener); 70 | } 71 | } -------------------------------------------------------------------------------- /src/main/resources/config/server/InertiaAntiCheat.toml: -------------------------------------------------------------------------------- 1 | # IMPORTANT! 2 | # Please check https://github.com/DiffuseHyperion/InertiaAntiCheat/wiki/Modern-(Version-1.0.0-and-above) on how to configure this mod. 3 | # You will likely need https://iac.diffusehyperion.com/ in the process of configuring this mod. 4 | 5 | [transfer] 6 | # The method clients should use in transfering their modlist to the server. 7 | # "data" will have clients send the entirety of each mod in their mod list. This is the most secure. 8 | # "name" will have clients send the file name of each mod in their mod list. This is the fastest. 9 | # "id" will have clients send the id of each mod in their mod list. This is a good compromise. 10 | # Use "name" if you are using large modpacks to make login times reasonable. 11 | # Accepted types: "data", "name", "id" 12 | method = "data" 13 | 14 | [validation] 15 | # The method to check client's mod list. 16 | # If you prefer blacklisting/whitelisting certain mods from being used, use "individual". 17 | # If you prefer ensuring client's have a modpack installed, use "group". 18 | # Accepted types: "individual", "group" 19 | method = "individual" 20 | # The hashing algorithm for the mod to use. 21 | # MD5 is the fastest but most insecure. 22 | # SHA256 is the slowest but most secure. 23 | # Accepted algorithms: "MD5", "SHA1", "SHA256" 24 | algorithm = "MD5" 25 | # The kick message to show to clients when they try to connect without InertiaAntiCheat installed. 26 | vanillaKickMessage = "You were kicked for not sending a response to the anti-cheat!\nThis is probably due to missing the anti-cheat client mod.\nInstall it here: https://modrinth.com/mod/inertiaanticheat" 27 | # The kick message to show to clients when they fail the modlist check. 28 | deniedKickMessage = "You were kicked by the anti-cheat!\nThis is due to having illegal mods.\nPlease contact your server administrator for more details." 29 | 30 | [validation.individual] 31 | # Immediately kick players who have these mods in their modlist. 32 | # The mod checks (and kicks) for blacklisted mods first. 33 | # If you are using the "data" transfer method, refer to https://iac.diffusehyperion.com 34 | # If you are using the "name" transfer method, add the names of each file you want to blacklist. 35 | # If you are using the "id" transfer method, refer to https://iac.diffusehyperion.com 36 | blacklist = [] 37 | # Immediately kick players who do not have these mods in their modlist. 38 | # If you are using the "data" transfer method, refer to https://iac.diffusehyperion.com 39 | # If you are using the "name" transfer method, add the names of each file you want to whitelist. 40 | # If you are using the "id" transfer method, refer to https://iac.diffusehyperion.com 41 | whitelist = [] 42 | 43 | [validation.group] 44 | # The hash for the server to check against. 45 | # You can add multiple hashes into the list, and the server will check against all of them. 46 | hash = [] 47 | # Allow certain mods to be used by players, in addition to your modpack. 48 | # This allows players to make use of optional client mods if you allow it. 49 | # If you are using the "data" transfer method, refer to https://iac.diffusehyperion.com 50 | # If you are using the "name" transfer method, add the names of each file you want to soft-whitelist. 51 | # If you are using the "id" transfer method, refer to https://iac.diffusehyperion.com 52 | softWhitelist = [] 53 | 54 | [motd] 55 | # Whether to show an icon on client's server list indicating that InertiaAntiCheat is installed. 56 | showInstalled = true 57 | # The names of blacklisted mods to report to clients. 58 | # Clients will be able to see these mod names in their server browser. 59 | # You may choose to put arbituary names, extra names, fake names or no names at all here. 60 | # Setting this to be an empty list will cause the icon to not show up. 61 | blacklist = ["Banned mods: ", "None"] 62 | # The names of whitelisted mods to report to clients. 63 | # Clients will be able to see these mod names in their server browser. 64 | # You may choose to put arbituary names, fake names or no names at all here. 65 | # Setting this to be an empty list will cause the icon to not show up. 66 | whitelist = ["Whitelisted mods: ", "None"] 67 | # The name of the modpack to report to clients. 68 | # Clients will be able to see the modpack name in their server browser. 69 | # Setting this to be an empty list will cause the icon to not show up. 70 | hash = ["Requires modpack: ", "Modpack Name"] 71 | 72 | [debug] 73 | # Show additional information in server logs. 74 | debug = false 75 | # do not touch pls :) 76 | version = 9 -------------------------------------------------------------------------------- /src/client/java/com/diffusehyperion/inertiaanticheat/client/InertiaAntiCheatClient.java: -------------------------------------------------------------------------------- 1 | package com.diffusehyperion.inertiaanticheat.client; 2 | 3 | import com.diffusehyperion.inertiaanticheat.common.InertiaAntiCheat; 4 | import com.diffusehyperion.inertiaanticheat.common.util.InertiaAntiCheatConstants; 5 | import com.google.gson.Gson; 6 | import com.google.gson.JsonObject; 7 | import com.moandjiezana.toml.Toml; 8 | import net.fabricmc.api.ClientModInitializer; 9 | import net.fabricmc.loader.api.FabricLoader; 10 | 11 | import java.io.File; 12 | import java.io.IOException; 13 | import java.io.InputStream; 14 | import java.io.InputStreamReader; 15 | import java.nio.file.Path; 16 | import java.util.ArrayList; 17 | import java.util.List; 18 | import java.util.Objects; 19 | import java.util.jar.JarFile; 20 | import java.util.zip.ZipEntry; 21 | 22 | import static com.diffusehyperion.inertiaanticheat.common.InertiaAntiCheat.*; 23 | 24 | public class InertiaAntiCheatClient implements ClientModInitializer { 25 | public static Toml clientConfig; 26 | public static final List allModNames = new ArrayList<>(); 27 | public static final List allModPaths = new ArrayList<>(); 28 | public static final List allModIds = new ArrayList<>(); 29 | 30 | @Override 31 | public void onInitializeClient() { 32 | InertiaAntiCheatClient.clientConfig = InertiaAntiCheat.initializeConfig("/config/client/InertiaAntiCheat.toml", InertiaAntiCheatConstants.CURRENT_CLIENT_CONFIG_VERSION); 33 | 34 | this.setupModlist(); 35 | ClientLoginModlistTransferHandler.init(); 36 | } 37 | 38 | public static void debugInfo(String info) { 39 | if (inDebug()) { 40 | info(info); 41 | } 42 | } 43 | 44 | public static void debugWarn(String info) { 45 | if (inDebug()) { 46 | warn(info); 47 | } 48 | } 49 | 50 | public static void debugError(String info) { 51 | if (inDebug()) { 52 | error(info); 53 | } 54 | } 55 | 56 | public static void debugLine() { 57 | if (inDebug()) { 58 | info("--------------------"); // lol 59 | } 60 | } 61 | 62 | public static void debugLine2() { 63 | if (inDebug()) { 64 | info("===================="); // lol 2 65 | } 66 | } 67 | 68 | public static boolean inDebug() { 69 | return FabricLoader.getInstance().isDevelopmentEnvironment() || clientConfig.getBoolean("debug.debug"); 70 | } 71 | 72 | private void setupModlist() { 73 | File modDirectory = FabricLoader.getInstance().getGameDir().resolve("mods").toFile(); 74 | for (File modFile : Objects.requireNonNull(modDirectory.listFiles())) { 75 | debugInfo("Attempting to load mod " + modFile.getName()); 76 | if (modFile.isDirectory()) { 77 | debugWarn("Skipping mod " + modFile.getName() + " as it is a directory"); 78 | continue; 79 | } 80 | if (!modFile.getAbsolutePath().endsWith(".jar")) { 81 | debugWarn("Skipping mod " + modFile.getName() + " as it does not end with .jar"); 82 | continue; 83 | } 84 | 85 | try (JarFile jarFile = new JarFile(modFile)) { 86 | ZipEntry entry = jarFile.getEntry("fabric.mod.json"); 87 | if (Objects.isNull(entry)) { 88 | debugWarn("Skipping mod " + modFile.getName() + " as it does not contain \"fabric.mod.json\""); 89 | continue; 90 | } 91 | 92 | Gson gson = new Gson(); 93 | try (InputStream input = jarFile.getInputStream(entry)) { 94 | JsonObject root = gson.fromJson(new InputStreamReader(input), JsonObject.class); 95 | 96 | if (!root.has("id")) { 97 | debugWarn("Skipping mod " + modFile.getName() + " as it does not contain a mod ID"); 98 | continue; 99 | } 100 | 101 | InertiaAntiCheatClient.allModNames.add(modFile.getName()); 102 | InertiaAntiCheatClient.allModPaths.add(modFile.toPath()); 103 | InertiaAntiCheatClient.allModIds.add(root.get("id").getAsString()); 104 | debugInfo("Successfully loaded " + modFile.getName()); 105 | } 106 | } catch (IOException e) { 107 | debugWarn("Skipping mod " + modFile.getName() + " as it could not be read"); 108 | } 109 | } 110 | InertiaAntiCheat.info("Loaded " + InertiaAntiCheatClient.allModNames.size() + " mods"); 111 | } 112 | } -------------------------------------------------------------------------------- /src/main/java/com/diffusehyperion/inertiaanticheat/server/InertiaAntiCheatServer.java: -------------------------------------------------------------------------------- 1 | package com.diffusehyperion.inertiaanticheat.server; 2 | 3 | import com.diffusehyperion.inertiaanticheat.common.InertiaAntiCheat; 4 | import com.diffusehyperion.inertiaanticheat.common.util.HashAlgorithm; 5 | import com.diffusehyperion.inertiaanticheat.common.util.InertiaAntiCheatConstants; 6 | import com.diffusehyperion.inertiaanticheat.common.util.TransferMethod; 7 | import com.diffusehyperion.inertiaanticheat.common.util.ValidationMethod; 8 | import com.moandjiezana.toml.Toml; 9 | import net.fabricmc.api.DedicatedServerModInitializer; 10 | import net.fabricmc.loader.api.FabricLoader; 11 | 12 | import static com.diffusehyperion.inertiaanticheat.common.InertiaAntiCheat.*; 13 | 14 | public class InertiaAntiCheatServer implements DedicatedServerModInitializer { 15 | 16 | public static Toml serverConfig; 17 | public static TransferMethod transferMethod; 18 | public static ValidationMethod validationMethod; 19 | public static HashAlgorithm hashAlgorithm; 20 | 21 | @Override 22 | public void onInitializeServer() { 23 | InertiaAntiCheatServer.serverConfig = InertiaAntiCheat.initializeConfig("/config/server/InertiaAntiCheat.toml", InertiaAntiCheatConstants.CURRENT_SERVER_CONFIG_VERSION); 24 | 25 | switch (InertiaAntiCheatServer.serverConfig.getString("transfer.method").toLowerCase()) { 26 | case "data" -> { 27 | InertiaAntiCheat.info("Using data transfer method"); 28 | InertiaAntiCheatServer.transferMethod = TransferMethod.DATA; 29 | } 30 | case "name" -> { 31 | InertiaAntiCheat.info("Using name transfer method"); 32 | InertiaAntiCheatServer.transferMethod = TransferMethod.NAME; 33 | } 34 | case "id" -> { 35 | InertiaAntiCheat.info("Using id transfer method"); 36 | InertiaAntiCheatServer.transferMethod = TransferMethod.ID; 37 | } 38 | default -> { 39 | InertiaAntiCheat.error("There was an error in your config! Invalid transfer method specified under \"transfer.method\"! "); 40 | InertiaAntiCheat.error("Defaulting to data transfer method for now."); 41 | InertiaAntiCheatServer.transferMethod = TransferMethod.DATA; 42 | } 43 | } 44 | 45 | switch (InertiaAntiCheatServer.serverConfig.getString("validation.method").toLowerCase()) { 46 | case "individual" -> { 47 | InertiaAntiCheat.info("Using individual validation method"); 48 | InertiaAntiCheatServer.validationMethod = ValidationMethod.INDIVIDUAL; 49 | } 50 | case "group" -> { 51 | InertiaAntiCheat.info("Using group validation method"); 52 | InertiaAntiCheatServer.validationMethod = ValidationMethod.GROUP; 53 | } 54 | default -> { 55 | InertiaAntiCheat.error("There was an error in your config! Invalid validation method specified under \"validation.method\"! "); 56 | InertiaAntiCheat.error("Defaulting to individual method for now."); 57 | InertiaAntiCheatServer.validationMethod = ValidationMethod.INDIVIDUAL; 58 | } 59 | } 60 | 61 | switch (InertiaAntiCheatServer.serverConfig.getString("validation.algorithm").toLowerCase()) { 62 | case "md5" -> { 63 | InertiaAntiCheat.info("Using MD5 algorithm for validation"); 64 | InertiaAntiCheatServer.hashAlgorithm = HashAlgorithm.MD5; 65 | } 66 | case "sha1" -> { 67 | InertiaAntiCheat.info("Using SHA1 algorithm for validation"); 68 | InertiaAntiCheatServer.hashAlgorithm = HashAlgorithm.SHA1; 69 | } 70 | case "sha256" -> { 71 | InertiaAntiCheat.info("Using SHA256 algorithm for validation"); 72 | InertiaAntiCheatServer.hashAlgorithm = HashAlgorithm.SHA256; 73 | } 74 | default -> { 75 | InertiaAntiCheat.error("There was an error in your config! Invalid algorithm specified under \"validation.algorithm\"! "); 76 | InertiaAntiCheat.error("Defaulting to MD5 algorithm for now."); 77 | InertiaAntiCheatServer.hashAlgorithm = HashAlgorithm.MD5; 78 | } 79 | } 80 | 81 | ServerLoginModlistTransferHandler.init(); 82 | } 83 | 84 | public static void debugInfo(String info) { 85 | if (inDebug()) { 86 | info(info); 87 | } 88 | } 89 | 90 | public static void debugWarn(String info) { 91 | if (inDebug()) { 92 | warn(info); 93 | } 94 | } 95 | 96 | public static void debugError(String info) { 97 | if (inDebug()) { 98 | error(info); 99 | } 100 | } 101 | 102 | public static void debugLine() { 103 | if (inDebug()) { 104 | info("--------------------"); // lol 105 | } 106 | } 107 | 108 | public static void debugLine2() { 109 | if (inDebug()) { 110 | info("===================="); // lol 2 111 | } 112 | } 113 | 114 | public static boolean inDebug() { 115 | return FabricLoader.getInstance().isDevelopmentEnvironment() || serverConfig.getBoolean("debug.debug"); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/client/java/com/diffusehyperion/inertiaanticheat/mixins/client/MultiplayerServerListWidgetServerEntryMixin.java: -------------------------------------------------------------------------------- 1 | package com.diffusehyperion.inertiaanticheat.mixins.client; 2 | 3 | import com.diffusehyperion.inertiaanticheat.common.interfaces.UpgradedServerInfo; 4 | import com.diffusehyperion.inertiaanticheat.common.util.AnticheatDetails; 5 | import com.diffusehyperion.inertiaanticheat.common.util.GroupAnticheatDetails; 6 | import com.diffusehyperion.inertiaanticheat.common.util.IndividualAnticheatDetails; 7 | import com.diffusehyperion.inertiaanticheat.common.util.ValidationMethod; 8 | import net.minecraft.client.gl.RenderPipelines; 9 | import net.minecraft.client.gui.DrawContext; 10 | import net.minecraft.client.gui.screen.multiplayer.MultiplayerServerListWidget; 11 | import net.minecraft.client.network.ServerInfo; 12 | import net.minecraft.text.Text; 13 | import net.minecraft.util.Identifier; 14 | import org.spongepowered.asm.mixin.Final; 15 | import org.spongepowered.asm.mixin.Mixin; 16 | import org.spongepowered.asm.mixin.Shadow; 17 | import org.spongepowered.asm.mixin.Unique; 18 | import org.spongepowered.asm.mixin.injection.At; 19 | import org.spongepowered.asm.mixin.injection.Inject; 20 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 21 | 22 | import java.util.Objects; 23 | 24 | import static com.diffusehyperion.inertiaanticheat.common.util.InertiaAntiCheatConstants.MODID; 25 | 26 | @Mixin(MultiplayerServerListWidget.ServerEntry.class) 27 | public abstract class MultiplayerServerListWidgetServerEntryMixin extends MultiplayerServerListWidget.Entry { 28 | @Shadow @Final private ServerInfo server; 29 | 30 | @Unique 31 | private static final Identifier ICON_ENABLED = Identifier.of(MODID, "textures/gui/enabled.png"); 32 | @Unique 33 | private static final Identifier ICON_WHITELIST = Identifier.of(MODID, "textures/gui/whitelist.png"); 34 | @Unique 35 | private static final Identifier ICON_BLACKLIST = Identifier.of(MODID, "textures/gui/blacklist.png"); 36 | @Unique 37 | private static final Identifier ICON_MODPACK = Identifier.of(MODID, "textures/gui/modpack.png"); 38 | 39 | @Inject( 40 | method = "render", 41 | at = @At(value = "TAIL") 42 | ) 43 | private void render(DrawContext context, int mouseX, int mouseY, boolean hovered, float deltaTicks, CallbackInfo ci) { 44 | UpgradedServerInfo upgradedServerInfo = ((UpgradedServerInfo) server); 45 | Boolean installed = upgradedServerInfo.inertiaAntiCheat$isInertiaInstalled(); 46 | AnticheatDetails anticheatDetails = upgradedServerInfo.inertiaAntiCheat$getAnticheatDetails(); 47 | if (Objects.nonNull(installed) && installed.equals(true) && anticheatDetails.showInstalled()) { 48 | int iconX = this.getContentRightEnd() - 10 - 5; 49 | int iconY = this.getContentY() + 8 + 2; 50 | context.drawTexture(RenderPipelines.GUI_TEXTURED, ICON_ENABLED, iconX, iconY, 0.0f, 0.0f, 10, 10, 10, 10); 51 | if (mouseX > iconX && mouseX < iconX + 10 && mouseY > iconY && mouseY < iconY + 10) { 52 | context.drawTooltip(Text.of("InertiaAntiCheat installed"), mouseX, mouseY); 53 | } 54 | } 55 | if (Objects.nonNull(anticheatDetails)) { 56 | if (anticheatDetails.getValidationMethod() == ValidationMethod.INDIVIDUAL) { 57 | IndividualAnticheatDetails details = (IndividualAnticheatDetails) anticheatDetails; 58 | 59 | if ((details.getWhitelistedMods().size() == 1 && !Objects.equals(details.getWhitelistedMods().getFirst(), "")) || details.getWhitelistedMods().size() >= 2) { 60 | int whitelistIconX = this.getContentRightEnd() - 10 - 5 - 10; 61 | int whitelistIconY = this.getContentY() + 8 + 2 + 10 + 2; 62 | context.drawTexture(RenderPipelines.GUI_TEXTURED, ICON_WHITELIST, whitelistIconX, whitelistIconY, 0.0f, 0.0f, 10, 10, 10, 10); 63 | if (mouseX > whitelistIconX && mouseX < whitelistIconX + 10 && mouseY > whitelistIconY && mouseY < whitelistIconY + 10) { 64 | context.drawTooltip(details.getWhitelistedMods().stream().map(Text::of).map(Text::asOrderedText).toList(), mouseX, mouseY); 65 | } 66 | } 67 | 68 | if ((details.getBlacklistedMods().size() == 1 && !Objects.equals(details.getBlacklistedMods().getFirst(), "")) || details.getBlacklistedMods().size() >= 2) { 69 | int blacklistIconX = this.getContentRightEnd() - 10 - 5; 70 | int blacklistIconY = this.getContentY() + 8 + 2 + 10 + 2; 71 | context.drawTexture(RenderPipelines.GUI_TEXTURED, ICON_BLACKLIST, blacklistIconX, blacklistIconY, 0.0f, 0.0f, 10, 10, 10, 10); 72 | if (mouseX > blacklistIconX && mouseX < blacklistIconX + 10 && mouseY > blacklistIconY && mouseY < blacklistIconY + 10) { 73 | context.drawTooltip(details.getBlacklistedMods().stream().map(Text::of).map(Text::asOrderedText).toList(), mouseX, mouseY); 74 | } 75 | } 76 | } else { 77 | GroupAnticheatDetails details = (GroupAnticheatDetails) anticheatDetails; 78 | if ((details.getModpackDetails().size() == 1 && !Objects.equals(details.getModpackDetails().getFirst(), "")) || details.getModpackDetails().size() >= 2) { 79 | int blacklistIconX = this.getContentRightEnd() - 10 - 5; 80 | int blacklistIconY = this.getContentY() + 8 + 2 + 10 + 2; 81 | context.drawTexture(RenderPipelines.GUI_TEXTURED, ICON_MODPACK, blacklistIconX, blacklistIconY, 0.0f, 0.0f, 10, 10, 10, 10); 82 | if (mouseX > blacklistIconX && mouseX < blacklistIconX + 10 && mouseY > blacklistIconY && mouseY < blacklistIconY + 10) { 83 | context.drawTooltip(details.getModpackDetails().stream().map(Text::of).map(Text::asOrderedText).toList(), mouseX, mouseY); 84 | } 85 | } 86 | } 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/client/java/com/diffusehyperion/inertiaanticheat/client/ClientLoginModlistTransferHandler.java: -------------------------------------------------------------------------------- 1 | package com.diffusehyperion.inertiaanticheat.client; 2 | 3 | import com.diffusehyperion.inertiaanticheat.client.networking.method.TransferHandler; 4 | import com.diffusehyperion.inertiaanticheat.client.networking.method.data.ClientDataTransferHandler; 5 | import com.diffusehyperion.inertiaanticheat.client.networking.method.id.ClientIdTransferHandler; 6 | import com.diffusehyperion.inertiaanticheat.client.networking.method.name.ClientNameTransferHandler; 7 | import com.diffusehyperion.inertiaanticheat.common.InertiaAntiCheat; 8 | import com.diffusehyperion.inertiaanticheat.common.interfaces.UpgradedClientLoginNetworkHandler; 9 | import com.diffusehyperion.inertiaanticheat.common.util.InertiaAntiCheatConstants; 10 | import com.diffusehyperion.inertiaanticheat.server.networking.method.CheckingTypes; 11 | import io.netty.channel.ChannelFutureListener; 12 | import net.fabricmc.fabric.api.client.networking.v1.ClientLoginConnectionEvents; 13 | import net.fabricmc.fabric.api.client.networking.v1.ClientLoginNetworking; 14 | import net.fabricmc.fabric.api.networking.v1.PacketByteBufs; 15 | import net.minecraft.client.MinecraftClient; 16 | import net.minecraft.client.network.ClientLoginNetworkHandler; 17 | import net.minecraft.network.PacketByteBuf; 18 | import net.minecraft.text.Text; 19 | import org.jetbrains.annotations.Nullable; 20 | 21 | import java.security.KeyPair; 22 | import java.security.PublicKey; 23 | import java.util.concurrent.CompletableFuture; 24 | import java.util.function.Consumer; 25 | 26 | import static com.diffusehyperion.inertiaanticheat.client.InertiaAntiCheatClient.debugInfo; 27 | import static com.diffusehyperion.inertiaanticheat.client.InertiaAntiCheatClient.debugLine; 28 | 29 | public class ClientLoginModlistTransferHandler { 30 | private PublicKey serverPublicKey; 31 | private KeyPair clientKeyPair; 32 | 33 | public static void init() { 34 | debugInfo("Creating mod transfer handler"); 35 | ClientLoginNetworking.registerGlobalReceiver(InertiaAntiCheatConstants.CHECK_CONNECTION, ClientLoginModlistTransferHandler::confirmConnection); 36 | } 37 | 38 | /** 39 | * Responds to any connection check packets 40 | * This also creates an instance of this class and begins listening for key exchange requests. 41 | */ 42 | private static CompletableFuture<@Nullable PacketByteBuf> 43 | confirmConnection(MinecraftClient client, ClientLoginNetworkHandler handler, 44 | PacketByteBuf buf, Consumer callbacksConsumer) { 45 | UpgradedClientLoginNetworkHandler upgradedHandler = (UpgradedClientLoginNetworkHandler) handler; 46 | 47 | debugLine(); 48 | debugInfo("Received request to start mod transfer"); 49 | 50 | upgradedHandler.inertiaAntiCheat$getSecondaryStatusConsumer().accept(Text.of("Preparing mod transfer...")); 51 | 52 | ClientLoginModlistTransferHandler transferHandler = new ClientLoginModlistTransferHandler(); 53 | ClientLoginNetworking.registerReceiver(InertiaAntiCheatConstants.INITIATE_E2EE, transferHandler::exchangeKey); 54 | return CompletableFuture.completedFuture(PacketByteBufs.empty()); 55 | } 56 | 57 | /** 58 | * Responds to key exchange requests 59 | * Saves server's public key and generates a client keypair to send 60 | */ 61 | private CompletableFuture<@Nullable PacketByteBuf> 62 | exchangeKey(MinecraftClient client, ClientLoginNetworkHandler handler, 63 | PacketByteBuf buf, Consumer callbacksConsumer) { 64 | UpgradedClientLoginNetworkHandler upgradedHandler = (UpgradedClientLoginNetworkHandler) handler; 65 | 66 | debugInfo("Exchanging keys with server"); 67 | 68 | upgradedHandler.inertiaAntiCheat$getSecondaryStatusConsumer().accept(Text.of("Transferring keys...")); 69 | 70 | this.serverPublicKey = InertiaAntiCheat.retrievePublicKey(buf); 71 | 72 | PacketByteBuf responseBuf = PacketByteBufs.create(); 73 | this.clientKeyPair = InertiaAntiCheat.createRSAPair(); 74 | responseBuf.writeBytes(this.clientKeyPair.getPublic().getEncoded()); 75 | 76 | ClientLoginNetworking.registerReceiver(InertiaAntiCheatConstants.SET_ADAPTOR, this::createAdaptors); 77 | return CompletableFuture.completedFuture(responseBuf); 78 | } 79 | 80 | /** 81 | * Responds to server's chosen adaptor and creates appropriate instances 82 | */ 83 | private CompletableFuture<@Nullable PacketByteBuf> 84 | createAdaptors(MinecraftClient client, ClientLoginNetworkHandler handler, 85 | PacketByteBuf buf, Consumer callbacksConsumer) { 86 | UpgradedClientLoginNetworkHandler upgradedHandler = (UpgradedClientLoginNetworkHandler) handler; 87 | 88 | upgradedHandler.inertiaAntiCheat$getSecondaryStatusConsumer().accept(Text.of("Starting for mod transfer...")); 89 | 90 | int transferAdaptorIndex = buf.readInt(); 91 | Consumer secondaryStatusConsumer = upgradedHandler.inertiaAntiCheat$getSecondaryStatusConsumer(); 92 | debugInfo("Received adapter index of " + transferAdaptorIndex); 93 | 94 | CheckingTypes transferAdaptorType = CheckingTypes.values()[transferAdaptorIndex]; 95 | 96 | TransferHandler transferAdaptor = switch (transferAdaptorType) { 97 | case DATA -> new ClientDataTransferHandler(this.serverPublicKey, InertiaAntiCheatConstants.SEND_MOD, secondaryStatusConsumer); 98 | case NAME -> new ClientNameTransferHandler(this.serverPublicKey, InertiaAntiCheatConstants.SEND_MOD, secondaryStatusConsumer); 99 | case ID -> new ClientIdTransferHandler(this.serverPublicKey, InertiaAntiCheatConstants.SEND_MOD, secondaryStatusConsumer); 100 | }; 101 | 102 | ClientLoginConnectionEvents.DISCONNECT.register(transferAdaptor::onDisconnect); 103 | 104 | debugInfo("Registered new handler for channel"); 105 | debugLine(); 106 | 107 | return CompletableFuture.completedFuture(PacketByteBufs.empty()); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/client/java/com/diffusehyperion/inertiaanticheat/client/networking/method/data/ClientDataTransferHandler.java: -------------------------------------------------------------------------------- 1 | package com.diffusehyperion.inertiaanticheat.client.networking.method.data; 2 | 3 | import com.diffusehyperion.inertiaanticheat.client.InertiaAntiCheatClient; 4 | import com.diffusehyperion.inertiaanticheat.client.networking.method.TransferHandler; 5 | import com.diffusehyperion.inertiaanticheat.common.InertiaAntiCheat; 6 | import com.diffusehyperion.inertiaanticheat.common.util.HashAlgorithm; 7 | import com.diffusehyperion.inertiaanticheat.common.util.InertiaAntiCheatConstants; 8 | import io.netty.channel.ChannelFutureListener; 9 | import net.fabricmc.fabric.api.client.networking.v1.ClientLoginNetworking; 10 | import net.fabricmc.fabric.api.networking.v1.PacketByteBufs; 11 | import net.minecraft.client.MinecraftClient; 12 | import net.minecraft.client.network.ClientLoginNetworkHandler; 13 | import net.minecraft.network.PacketByteBuf; 14 | import net.minecraft.text.Text; 15 | import net.minecraft.util.Identifier; 16 | 17 | import java.io.IOException; 18 | import java.nio.file.Files; 19 | import java.nio.file.Path; 20 | import java.security.PublicKey; 21 | import java.util.ArrayDeque; 22 | import java.util.Arrays; 23 | import java.util.Deque; 24 | import java.util.Objects; 25 | import java.util.concurrent.CompletableFuture; 26 | import java.util.function.Consumer; 27 | 28 | import static com.diffusehyperion.inertiaanticheat.client.InertiaAntiCheatClient.debugInfo; 29 | import static com.diffusehyperion.inertiaanticheat.client.InertiaAntiCheatClient.debugLine; 30 | 31 | public class ClientDataTransferHandler extends TransferHandler { 32 | 33 | private int allModPathsIndex; 34 | private final Deque loadedFiles; 35 | private static final int MAX_LOADED_FILES = 10; 36 | private boolean completed; 37 | private byte[] currentFile; 38 | 39 | public static final int MAX_SIZE = 1000000; 40 | 41 | public ClientDataTransferHandler(PublicKey publicKey, Identifier modTransferID, Consumer secondaryStatusConsumer) { 42 | super(publicKey, modTransferID, secondaryStatusConsumer, InertiaAntiCheatClient.allModPaths.size()); 43 | 44 | debugInfo("Creating data transfer handler"); 45 | 46 | this.completed = false; 47 | this.loadedFiles = new ArrayDeque<>(MAX_LOADED_FILES); 48 | this.allModPathsIndex = 0; 49 | 50 | Thread fileLoaderThread = new Thread(this::fileLoaderThreadMethod); 51 | fileLoaderThread.start(); 52 | } 53 | 54 | @Override 55 | public CompletableFuture transferMod(MinecraftClient ignored1, ClientLoginNetworkHandler ignored2, PacketByteBuf ignored3, Consumer ignored4) { 56 | if (this.completed && this.loadedFiles.isEmpty() && Objects.isNull(currentFile)) { 57 | // All files have been sent, returning null to signify goodbye 58 | debugInfo("Sending final packet"); 59 | debugLine(); 60 | 61 | this.setCompleteTransferStatus(); 62 | 63 | ClientLoginNetworking.unregisterGlobalReceiver(InertiaAntiCheatConstants.SEND_MOD); 64 | return CompletableFuture.completedFuture(null); 65 | } else if (Objects.isNull(currentFile)) { 66 | this.increaseSentModsStatus(); 67 | this.currentFile = stageNextFile(); 68 | } 69 | 70 | PacketByteBuf buf = PacketByteBufs.create(); 71 | byte[] chunk; 72 | 73 | if (this.currentFile.length > ClientDataTransferHandler.MAX_SIZE) { 74 | debugInfo("Sending part of next file"); 75 | 76 | chunk = Arrays.copyOf(this.currentFile, ClientDataTransferHandler.MAX_SIZE); 77 | debugInfo("Hash of chunk: " + InertiaAntiCheat.getHash(chunk, HashAlgorithm.MD5)); 78 | 79 | this.currentFile = Arrays.copyOfRange(this.currentFile, ClientDataTransferHandler.MAX_SIZE, this.currentFile.length); 80 | buf.writeBoolean(false); 81 | } else { 82 | debugInfo("Sending entirety of next file"); 83 | 84 | chunk = this.currentFile; 85 | debugInfo("Hash of chunk: " + InertiaAntiCheat.getHash(this.currentFile, HashAlgorithm.MD5)); 86 | 87 | this.currentFile = null; 88 | buf.writeBoolean(true); 89 | } 90 | PacketByteBuf responseBuf = this.preparePacket(buf, chunk); 91 | 92 | debugLine(); 93 | 94 | return CompletableFuture.completedFuture(responseBuf); 95 | } 96 | 97 | private synchronized void fileLoaderThreadMethod() { 98 | try { 99 | while (this.allModPathsIndex < InertiaAntiCheatClient.allModPaths.size()) { 100 | while (this.loadedFiles.size() >= MAX_LOADED_FILES) { 101 | wait(); 102 | } 103 | 104 | Path path = InertiaAntiCheatClient.allModPaths.get(this.allModPathsIndex); 105 | this.allModPathsIndex++; 106 | 107 | try { 108 | this.loadedFiles.addLast(Files.readAllBytes(path)); 109 | debugInfo("Loaded mod file: " + path); 110 | notifyAll(); 111 | } catch (IOException e) { 112 | throw new RuntimeException("Could not read mod file at path: " + path, e); 113 | } catch (IllegalStateException e) { 114 | throw new RuntimeException("Could not load mod file into deque as it was full", e); 115 | } 116 | } 117 | 118 | debugInfo("Mod file loader thread cleaning up at index " + this.allModPathsIndex); 119 | this.completed = true; 120 | } catch (InterruptedException e) { 121 | Thread.currentThread().interrupt(); 122 | } 123 | } 124 | 125 | private synchronized byte[] stageNextFile() { 126 | try { 127 | while (this.loadedFiles.isEmpty()) { 128 | wait(); 129 | } 130 | 131 | byte[] loadedFile = this.loadedFiles.remove(); 132 | debugInfo("Staged mod file"); 133 | notifyAll(); 134 | return loadedFile; 135 | } catch (InterruptedException e) { 136 | throw new RuntimeException("Could not stage next mod file from memory", e); 137 | } 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /src/client/java/com/diffusehyperion/inertiaanticheat/client/networking/packets/UpgradedClientQueryNetworkHandler.java: -------------------------------------------------------------------------------- 1 | package com.diffusehyperion.inertiaanticheat.client.networking.packets; 2 | 3 | import com.diffusehyperion.inertiaanticheat.common.interfaces.UpgradedServerInfo; 4 | import com.diffusehyperion.inertiaanticheat.common.networking.packets.S2C.AnticheatDetailsS2CPacket; 5 | import com.diffusehyperion.inertiaanticheat.common.networking.packets.UpgradedClientQueryPacketListener; 6 | import com.diffusehyperion.inertiaanticheat.utils.QuadConsumer; 7 | import net.minecraft.client.network.MultiplayerServerListPinger; 8 | import net.minecraft.client.network.ServerAddress; 9 | import net.minecraft.client.network.ServerInfo; 10 | import net.minecraft.network.ClientConnection; 11 | import net.minecraft.network.DisconnectionInfo; 12 | import net.minecraft.network.NetworkingBackend; 13 | import net.minecraft.network.packet.c2s.query.QueryPingC2SPacket; 14 | import net.minecraft.network.packet.s2c.query.PingResultS2CPacket; 15 | import net.minecraft.network.packet.s2c.query.QueryResponseS2CPacket; 16 | import net.minecraft.server.MinecraftServer; 17 | import net.minecraft.server.PlayerConfigEntry; 18 | import net.minecraft.server.ServerMetadata; 19 | import net.minecraft.text.Text; 20 | import net.minecraft.util.Formatting; 21 | import net.minecraft.util.Util; 22 | 23 | import java.net.InetSocketAddress; 24 | import java.util.ArrayList; 25 | import java.util.Arrays; 26 | import java.util.List; 27 | import java.util.function.BiConsumer; 28 | 29 | public class UpgradedClientQueryNetworkHandler implements UpgradedClientQueryPacketListener { 30 | /* ---------- vanilla fields ----------*/ 31 | 32 | private final ServerInfo entry; 33 | private final Runnable saver; 34 | private final Runnable pingCallback; 35 | private final NetworkingBackend backend; 36 | 37 | private final ClientConnection clientConnection; 38 | 39 | private final InetSocketAddress inetSocketAddress; 40 | private final ServerAddress serverAddress; 41 | 42 | private final BiConsumer showErrorMethod; 43 | private final QuadConsumer pingMethod; 44 | 45 | private boolean sentQuery; 46 | private boolean received; 47 | private long startTime; 48 | 49 | public UpgradedClientQueryNetworkHandler(ServerInfo entry, Runnable saver, Runnable pingCallback, NetworkingBackend backend, 50 | ClientConnection clientConnection, 51 | InetSocketAddress inetSocketAddress, ServerAddress serverAddress, 52 | BiConsumer showErrorMethod, 53 | QuadConsumer pingMethod) { 54 | /* ---------- vanilla fields ----------*/ 55 | 56 | this.entry = entry; 57 | this.saver = saver; 58 | this.pingCallback = pingCallback; 59 | this.backend = backend; 60 | 61 | this.clientConnection = clientConnection; 62 | 63 | this.inetSocketAddress = inetSocketAddress; 64 | this.serverAddress = serverAddress; 65 | 66 | this.showErrorMethod = showErrorMethod; 67 | this.pingMethod = pingMethod; 68 | } 69 | 70 | @Override 71 | public void onReceiveAnticheatDetails(AnticheatDetailsS2CPacket var1) { 72 | ((UpgradedServerInfo) entry).inertiaAntiCheat$setInertiaInstalled(true); 73 | ((UpgradedServerInfo) entry).inertiaAntiCheat$setAnticheatDetails(var1.details()); 74 | } 75 | 76 | 77 | /* ---------- (Mostly) vanilla stuff below ----------*/ 78 | 79 | @Override 80 | public void onResponse(QueryResponseS2CPacket packet) { 81 | if (this.received) { 82 | clientConnection.disconnect(Text.translatable("multiplayer.status.unrequested")); 83 | } else { 84 | this.received = true; 85 | ServerMetadata serverMetadata = packet.metadata(); 86 | entry.label = serverMetadata.description(); 87 | serverMetadata.version().ifPresentOrElse(version -> { 88 | entry.version = Text.literal(version.gameVersion()); 89 | entry.protocolVersion = version.protocolVersion(); 90 | }, () -> { 91 | entry.version = Text.translatable("multiplayer.status.old"); 92 | entry.protocolVersion = 0; 93 | }); 94 | serverMetadata.players().ifPresentOrElse(players -> { 95 | entry.playerCountLabel = MultiplayerServerListPinger.createPlayerCountText(players.online(), players.max()); 96 | entry.players = players; 97 | if (!players.sample().isEmpty()) { 98 | List list = new ArrayList<>(players.sample().size()); 99 | 100 | for (PlayerConfigEntry playerConfigEntry : players.sample()) { 101 | Text text; 102 | if (playerConfigEntry.equals(MinecraftServer.ANONYMOUS_PLAYER_PROFILE)) { 103 | text = Text.translatable("multiplayer.status.anonymous_player"); 104 | } else { 105 | text = Text.literal(playerConfigEntry.name()); 106 | } 107 | 108 | list.add(text); 109 | } 110 | 111 | if (players.sample().size() < players.online()) { 112 | list.add(Text.translatable("multiplayer.status.and_more", players.online() - players.sample().size())); 113 | } 114 | 115 | entry.playerListSummary = list; 116 | } else { 117 | entry.playerListSummary = List.of(); 118 | } 119 | }, () -> entry.playerCountLabel = Text.translatable("multiplayer.status.unknown").formatted(Formatting.DARK_GRAY)); 120 | serverMetadata.favicon().ifPresent(favicon -> { 121 | if (!Arrays.equals(favicon.iconBytes(), entry.getFavicon())) { 122 | entry.setFavicon(ServerInfo.validateFavicon(favicon.iconBytes())); 123 | saver.run(); 124 | } 125 | }); 126 | this.startTime = Util.getMeasuringTimeMs(); 127 | clientConnection.send(new QueryPingC2SPacket(this.startTime)); 128 | this.sentQuery = true; 129 | } 130 | } 131 | 132 | @Override 133 | public void onPingResult(PingResultS2CPacket packet) { 134 | long l = this.startTime; 135 | long m = Util.getMeasuringTimeMs(); 136 | entry.ping = m - l; 137 | this.clientConnection.disconnect(Text.translatable("multiplayer.status.finished")); 138 | this.pingCallback.run(); 139 | } 140 | 141 | @Override 142 | public void onDisconnected(DisconnectionInfo info) { 143 | if (!this.sentQuery) { 144 | showErrorMethod.accept(info.reason(), entry); 145 | pingMethod.accept(inetSocketAddress, serverAddress, entry, backend); 146 | } 147 | } 148 | 149 | @Override 150 | public boolean isConnectionOpen() { 151 | return this.clientConnection.isOpen(); 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /src/main/java/com/diffusehyperion/inertiaanticheat/common/InertiaAntiCheat.java: -------------------------------------------------------------------------------- 1 | package com.diffusehyperion.inertiaanticheat.common; 2 | 3 | import com.diffusehyperion.inertiaanticheat.common.util.HashAlgorithm; 4 | import com.diffusehyperion.inertiaanticheat.server.InertiaAntiCheatServer; 5 | import com.moandjiezana.toml.Toml; 6 | import net.fabricmc.api.ModInitializer; 7 | import net.fabricmc.loader.api.FabricLoader; 8 | import net.minecraft.network.PacketByteBuf; 9 | 10 | import javax.crypto.*; 11 | import javax.crypto.spec.SecretKeySpec; 12 | import java.io.File; 13 | import java.io.IOException; 14 | import java.math.BigInteger; 15 | import java.nio.file.Files; 16 | import java.nio.file.Path; 17 | import java.security.*; 18 | import java.security.spec.InvalidKeySpecException; 19 | import java.security.spec.X509EncodedKeySpec; 20 | import java.util.Objects; 21 | 22 | import static com.diffusehyperion.inertiaanticheat.common.util.InertiaAntiCheatConstants.MODLOGGER; 23 | 24 | public class InertiaAntiCheat implements ModInitializer { 25 | 26 | @Override 27 | public void onInitialize() { 28 | info("Initializing InertiaAntiCheat!"); 29 | try { 30 | Files.createDirectories(getConfigDir()); 31 | } catch (IOException e) { 32 | throw new RuntimeException(e); 33 | } 34 | } 35 | 36 | public static void info(String info) {MODLOGGER.info("[InertiaAntiCheat] {}", info);} 37 | public static void warn(String info) {MODLOGGER.warn("[InertiaAntiCheat] {}", info);} 38 | public static void error(String info) {MODLOGGER.error("[InertiaAntiCheat] {}", info);} 39 | 40 | public static String getHash(byte[] input, HashAlgorithm algorithm) { 41 | try { 42 | MessageDigest md = MessageDigest.getInstance(algorithm.toString()); 43 | byte[] hash = md.digest(input); 44 | StringBuilder hashBuilder = new StringBuilder(new BigInteger(1, hash).toString(16)); 45 | while (hashBuilder.length() < algorithm.getLength()) { 46 | hashBuilder.insert(0, "0"); 47 | } 48 | return hashBuilder.toString(); 49 | } catch (NoSuchAlgorithmException e){ 50 | throw new RuntimeException("Invalid algorithm provided! Please report this on this project's Github!", e); 51 | } 52 | } 53 | 54 | public static Toml initializeConfig(String defaultConfigPath, Long currentConfigVersion) { 55 | File configFile = getConfigDir().resolve("./InertiaAntiCheat.toml").toFile(); 56 | if (!configFile.exists()) { 57 | warn("No config file found! Creating a new one now..."); 58 | try { 59 | Files.copy(Objects.requireNonNull(InertiaAntiCheatServer.class.getResourceAsStream(defaultConfigPath)), configFile.toPath()); 60 | } catch (IOException e) { 61 | throw new RuntimeException("Couldn't a create default config!", e); 62 | } 63 | } 64 | Toml config = new Toml().read(configFile); 65 | if (!Objects.equals(config.getLong("debug.version", 0L), currentConfigVersion)) { 66 | warn("Looks like your config file is outdated! Backing up current config, then creating an updated config."); 67 | warn("Your config file will be backed up to \"BACKUP-InertiaAntiCheat.toml\"."); 68 | File backupFile = getConfigDir().resolve("BACKUP-InertiaAntiCheat.toml").toFile(); 69 | try { 70 | Files.copy(configFile.toPath(), backupFile.toPath()); 71 | } catch (IOException e) { 72 | throw new RuntimeException("Couldn't copy existing config file into a backup config file! Please do it manually.", e); 73 | } 74 | if (!configFile.delete()) { 75 | throw new RuntimeException("Couldn't delete config file! Please delete it manually."); 76 | } 77 | try { 78 | Files.copy(Objects.requireNonNull(InertiaAntiCheatServer.class.getResourceAsStream(defaultConfigPath)), configFile.toPath()); 79 | } catch (IOException e) { 80 | throw new RuntimeException("Couldn't create a default config!", e); 81 | } 82 | config = new Toml().read(configFile); // update config to new file 83 | info("Done! Please readjust the configs in the new file accordingly."); 84 | } 85 | return config; 86 | } 87 | 88 | public static Path getConfigDir() { 89 | return FabricLoader.getInstance().getConfigDir().resolve("InertiaAntiCheat"); 90 | } 91 | 92 | public static PublicKey retrievePublicKey(PacketByteBuf packetByteBuf) { 93 | byte[] rawPublicKeyBytes = new byte[packetByteBuf.readableBytes()]; 94 | packetByteBuf.readBytes(rawPublicKeyBytes); 95 | X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(rawPublicKeyBytes); 96 | try { 97 | KeyFactory factory = KeyFactory.getInstance("RSA"); 98 | return factory.generatePublic(publicKeySpec); 99 | } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { 100 | throw new RuntimeException(e); 101 | } 102 | } 103 | 104 | public static byte[] encryptAESBytes(byte[] input, SecretKey secretKey) { 105 | try { 106 | Cipher cipher = Cipher.getInstance("AES"); 107 | cipher.init(Cipher.ENCRYPT_MODE, secretKey); 108 | return cipher.doFinal(input); 109 | } catch (NoSuchAlgorithmException | NoSuchPaddingException | IllegalBlockSizeException | BadPaddingException | 110 | InvalidKeyException e) { 111 | throw new RuntimeException(e); 112 | } 113 | } 114 | 115 | public static byte[] decryptAESBytes(byte[] input, SecretKey secretKey) { 116 | try { 117 | Cipher cipher = Cipher.getInstance("AES"); 118 | cipher.init(Cipher.DECRYPT_MODE, secretKey); 119 | return cipher.doFinal(input); 120 | } catch (NoSuchAlgorithmException | NoSuchPaddingException | IllegalBlockSizeException | BadPaddingException | 121 | InvalidKeyException e) { 122 | throw new RuntimeException(e); 123 | } 124 | } 125 | 126 | public static byte[] encryptRSABytes(byte[] input, PublicKey publicKey) { 127 | try { 128 | Cipher cipher = Cipher.getInstance("RSA"); 129 | cipher.init(Cipher.ENCRYPT_MODE, publicKey); 130 | return cipher.doFinal(input); 131 | } catch (NoSuchAlgorithmException | NoSuchPaddingException | IllegalBlockSizeException | BadPaddingException | 132 | InvalidKeyException e) { 133 | throw new RuntimeException(e); 134 | } 135 | } 136 | 137 | public static byte[] decryptRSABytes(byte[] input, PrivateKey privateKey) { 138 | try { 139 | Cipher cipher = Cipher.getInstance("RSA"); 140 | cipher.init(Cipher.DECRYPT_MODE, privateKey); 141 | return cipher.doFinal(input); 142 | } catch (NoSuchAlgorithmException | NoSuchPaddingException | IllegalBlockSizeException | BadPaddingException | 143 | InvalidKeyException e) { 144 | throw new RuntimeException(e); 145 | } 146 | } 147 | 148 | public static SecretKey createAESKey() { 149 | try { 150 | KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); 151 | keyGenerator.init(256); 152 | return keyGenerator.generateKey(); 153 | } catch (NoSuchAlgorithmException e) { 154 | throw new RuntimeException(e); 155 | } 156 | } 157 | 158 | public static KeyPair createRSAPair() { 159 | try { 160 | KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); 161 | keyPairGenerator.initialize(2048); 162 | return keyPairGenerator.generateKeyPair(); 163 | } catch (NoSuchAlgorithmException e) { 164 | throw new RuntimeException("Something went wrong while generating new key pairs!", e); 165 | } 166 | } 167 | 168 | public static byte[] decryptAESRSAEncodedBuf(PacketByteBuf buf, PrivateKey privateKey) { 169 | int encryptedSecretKeyLength = buf.readInt(); 170 | byte[] encryptedSecretKey = new byte[encryptedSecretKeyLength]; 171 | buf.readBytes(encryptedSecretKey); 172 | SecretKey secretKey = new SecretKeySpec(InertiaAntiCheat.decryptRSABytes(encryptedSecretKey, privateKey), "AES"); 173 | 174 | byte[] encryptedData = new byte[buf.readableBytes()]; 175 | buf.readBytes(encryptedData); 176 | return InertiaAntiCheat.decryptAESBytes(encryptedData, secretKey); 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | # SPDX-License-Identifier: Apache-2.0 19 | # 20 | 21 | ############################################################################## 22 | # 23 | # Gradle start up script for POSIX generated by Gradle. 24 | # 25 | # Important for running: 26 | # 27 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 28 | # noncompliant, but you have some other compliant shell such as ksh or 29 | # bash, then to run this script, type that shell name before the whole 30 | # command line, like: 31 | # 32 | # ksh Gradle 33 | # 34 | # Busybox and similar reduced shells will NOT work, because this script 35 | # requires all of these POSIX shell features: 36 | # * functions; 37 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 38 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 39 | # * compound commands having a testable exit status, especially «case»; 40 | # * various built-in commands including «command», «set», and «ulimit». 41 | # 42 | # Important for patching: 43 | # 44 | # (2) This script targets any POSIX shell, so it avoids extensions provided 45 | # by Bash, Ksh, etc; in particular arrays are avoided. 46 | # 47 | # The "traditional" practice of packing multiple parameters into a 48 | # space-separated string is a well documented source of bugs and security 49 | # problems, so this is (mostly) avoided, by progressively accumulating 50 | # options in "$@", and eventually passing that to Java. 51 | # 52 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 53 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 54 | # see the in-line comments for details. 55 | # 56 | # There are tweaks for specific operating systems such as AIX, CygWin, 57 | # Darwin, MinGW, and NonStop. 58 | # 59 | # (3) This script is generated from the Groovy template 60 | # https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 61 | # within the Gradle project. 62 | # 63 | # You can find Gradle at https://github.com/gradle/gradle/. 64 | # 65 | ############################################################################## 66 | 67 | # Attempt to set APP_HOME 68 | 69 | # Resolve links: $0 may be a link 70 | app_path=$0 71 | 72 | # Need this for daisy-chained symlinks. 73 | while 74 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 75 | [ -h "$app_path" ] 76 | do 77 | ls=$( ls -ld "$app_path" ) 78 | link=${ls#*' -> '} 79 | case $link in #( 80 | /*) app_path=$link ;; #( 81 | *) app_path=$APP_HOME$link ;; 82 | esac 83 | done 84 | 85 | # This is normally unused 86 | # shellcheck disable=SC2034 87 | APP_BASE_NAME=${0##*/} 88 | # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) 89 | APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit 90 | 91 | # Use the maximum available, or set MAX_FD != -1 to use that value. 92 | MAX_FD=maximum 93 | 94 | warn () { 95 | echo "$*" 96 | } >&2 97 | 98 | die () { 99 | echo 100 | echo "$*" 101 | echo 102 | exit 1 103 | } >&2 104 | 105 | # OS specific support (must be 'true' or 'false'). 106 | cygwin=false 107 | msys=false 108 | darwin=false 109 | nonstop=false 110 | case "$( uname )" in #( 111 | CYGWIN* ) cygwin=true ;; #( 112 | Darwin* ) darwin=true ;; #( 113 | MSYS* | MINGW* ) msys=true ;; #( 114 | NONSTOP* ) nonstop=true ;; 115 | esac 116 | 117 | 118 | 119 | # Determine the Java command to use to start the JVM. 120 | if [ -n "$JAVA_HOME" ] ; then 121 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 122 | # IBM's JDK on AIX uses strange locations for the executables 123 | JAVACMD=$JAVA_HOME/jre/sh/java 124 | else 125 | JAVACMD=$JAVA_HOME/bin/java 126 | fi 127 | if [ ! -x "$JAVACMD" ] ; then 128 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 129 | 130 | Please set the JAVA_HOME variable in your environment to match the 131 | location of your Java installation." 132 | fi 133 | else 134 | JAVACMD=java 135 | if ! command -v java >/dev/null 2>&1 136 | then 137 | die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 138 | 139 | Please set the JAVA_HOME variable in your environment to match the 140 | location of your Java installation." 141 | fi 142 | fi 143 | 144 | # Increase the maximum file descriptors if we can. 145 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 146 | case $MAX_FD in #( 147 | max*) 148 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 149 | # shellcheck disable=SC2039,SC3045 150 | MAX_FD=$( ulimit -H -n ) || 151 | warn "Could not query maximum file descriptor limit" 152 | esac 153 | case $MAX_FD in #( 154 | '' | soft) :;; #( 155 | *) 156 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 157 | # shellcheck disable=SC2039,SC3045 158 | ulimit -n "$MAX_FD" || 159 | warn "Could not set maximum file descriptor limit to $MAX_FD" 160 | esac 161 | fi 162 | 163 | # Collect all arguments for the java command, stacking in reverse order: 164 | # * args from the command line 165 | # * the main class name 166 | # * -classpath 167 | # * -D...appname settings 168 | # * --module-path (only if needed) 169 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 170 | 171 | # For Cygwin or MSYS, switch paths to Windows format before running java 172 | if "$cygwin" || "$msys" ; then 173 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 174 | 175 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 176 | 177 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 178 | for arg do 179 | if 180 | case $arg in #( 181 | -*) false ;; # don't mess with options #( 182 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 183 | [ -e "$t" ] ;; #( 184 | *) false ;; 185 | esac 186 | then 187 | arg=$( cygpath --path --ignore --mixed "$arg" ) 188 | fi 189 | # Roll the args list around exactly as many times as the number of 190 | # args, so each arg winds up back in the position where it started, but 191 | # possibly modified. 192 | # 193 | # NB: a `for` loop captures its iteration list before it begins, so 194 | # changing the positional parameters here affects neither the number of 195 | # iterations, nor the values presented in `arg`. 196 | shift # remove old arg 197 | set -- "$@" "$arg" # push replacement arg 198 | done 199 | fi 200 | 201 | 202 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 203 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 204 | 205 | # Collect all arguments for the java command: 206 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, 207 | # and any embedded shellness will be escaped. 208 | # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be 209 | # treated as '${Hostname}' itself on the command line. 210 | 211 | set -- \ 212 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 213 | -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ 214 | "$@" 215 | 216 | # Stop when "xargs" is not available. 217 | if ! command -v xargs >/dev/null 2>&1 218 | then 219 | die "xargs is not available" 220 | fi 221 | 222 | # Use "xargs" to parse quoted args. 223 | # 224 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 225 | # 226 | # In Bash we could simply go: 227 | # 228 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 229 | # set -- "${ARGS[@]}" "$@" 230 | # 231 | # but POSIX shell has neither arrays nor command substitution, so instead we 232 | # post-process each arg (as a line of input to sed) to backslash-escape any 233 | # character that might be a shell metacharacter, then use eval to reverse 234 | # that process (while maintaining the separation between arguments), and wrap 235 | # the whole thing up as a single "set" statement. 236 | # 237 | # This will of course break if any of these variables contains a newline or 238 | # an unmatched quote. 239 | # 240 | 241 | eval "set -- $( 242 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 243 | xargs -n1 | 244 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 245 | tr '\n' ' ' 246 | )" '"$@"' 247 | 248 | exec "$JAVACMD" "$@" 249 | -------------------------------------------------------------------------------- /src/main/java/com/diffusehyperion/inertiaanticheat/server/ServerLoginModlistTransferHandler.java: -------------------------------------------------------------------------------- 1 | package com.diffusehyperion.inertiaanticheat.server; 2 | 3 | import com.diffusehyperion.inertiaanticheat.common.InertiaAntiCheat; 4 | import com.diffusehyperion.inertiaanticheat.common.interfaces.UpgradedServerLoginNetworkHandler; 5 | import com.diffusehyperion.inertiaanticheat.common.util.InertiaAntiCheatConstants; 6 | import com.diffusehyperion.inertiaanticheat.server.networking.method.ValidatorHandler; 7 | import com.diffusehyperion.inertiaanticheat.server.networking.method.data.ServerDataGroupValidatorHandler; 8 | import com.diffusehyperion.inertiaanticheat.server.networking.method.data.ServerDataIndividualValidatorHandler; 9 | import com.diffusehyperion.inertiaanticheat.server.networking.method.data.ServerDataReceiverHandler; 10 | import com.diffusehyperion.inertiaanticheat.server.networking.method.data.handlers.DataValidationHandler; 11 | import com.diffusehyperion.inertiaanticheat.server.networking.method.id.ServerIdGroupValidatorHandler; 12 | import com.diffusehyperion.inertiaanticheat.server.networking.method.id.ServerIdIndividualValidatorHandler; 13 | import com.diffusehyperion.inertiaanticheat.server.networking.method.id.ServerIdReceiverHandler; 14 | import com.diffusehyperion.inertiaanticheat.server.networking.method.id.handlers.IdValidationHandler; 15 | import com.diffusehyperion.inertiaanticheat.server.networking.method.name.ServerNameGroupValidatorHandler; 16 | import com.diffusehyperion.inertiaanticheat.server.networking.method.name.ServerNameIndividualValidatorHandler; 17 | import com.diffusehyperion.inertiaanticheat.server.networking.method.name.ServerNameReceiverHandler; 18 | import com.diffusehyperion.inertiaanticheat.server.networking.method.name.handlers.NameValidationHandler; 19 | import me.lucko.fabric.api.permissions.v0.Permissions; 20 | import net.fabricmc.fabric.api.networking.v1.*; 21 | import net.minecraft.network.PacketByteBuf; 22 | import net.minecraft.server.MinecraftServer; 23 | import net.minecraft.server.network.ServerLoginNetworkHandler; 24 | import net.minecraft.text.Text; 25 | 26 | import java.security.KeyPair; 27 | import java.security.PublicKey; 28 | import java.util.concurrent.CompletableFuture; 29 | 30 | import static com.diffusehyperion.inertiaanticheat.server.InertiaAntiCheatServer.debugInfo; 31 | import static com.diffusehyperion.inertiaanticheat.server.InertiaAntiCheatServer.debugLine; 32 | 33 | public class ServerLoginModlistTransferHandler { 34 | private KeyPair serverKeyPair; 35 | private PublicKey clientKey; 36 | 37 | private final CompletableFuture loginBlocker = new CompletableFuture<>(); 38 | 39 | public static void init() { 40 | ServerLoginConnectionEvents.QUERY_START.register(ServerLoginModlistTransferHandler::initiateConnection); 41 | } 42 | 43 | /** 44 | * Creates an instance of this class to have an instance of loginBlocker to delay logins 45 | * Afterward, this does preliminary checks to see if the client has permissions to bypass the mod 46 | * If not, sends a packet to check if the client understands custom packets from this mod 47 | */ 48 | private static void initiateConnection(ServerLoginNetworkHandler handler, MinecraftServer minecraftServer, LoginPacketSender sender, ServerLoginNetworking.LoginSynchronizer synchronizer) { 49 | UpgradedServerLoginNetworkHandler upgradedHandler = (UpgradedServerLoginNetworkHandler) handler; 50 | 51 | ServerLoginModlistTransferHandler transferHandler = new ServerLoginModlistTransferHandler(); 52 | synchronizer.waitFor(transferHandler.loginBlocker); 53 | 54 | debugLine(); 55 | debugInfo("Checking if " + handler.getConnectionInfo() + " has bypass permissions"); 56 | boolean allowed = Permissions.check(upgradedHandler.inertiaAntiCheat$getGameProfile(), "inertiaanticheat.bypass").join(); 57 | if (allowed) { 58 | debugInfo(handler.getConnectionInfo() + " is allowed to bypass"); 59 | debugLine(); 60 | transferHandler.loginBlocker.complete(null); 61 | return; 62 | } 63 | debugInfo("Not allowed to bypass, checking if address " + handler.getConnectionInfo() + " responds to mod messages"); 64 | 65 | ServerLoginNetworking.registerReceiver(handler, InertiaAntiCheatConstants.CHECK_CONNECTION, transferHandler::checkConnection); 66 | sender.sendPacket(InertiaAntiCheatConstants.CHECK_CONNECTION, PacketByteBufs.empty()); 67 | } 68 | 69 | /** 70 | * Confirms whether the client understood the custom packet (meaning he has inertia installed too) 71 | * Afterward, this starts the key exchanging process 72 | */ 73 | private void 74 | checkConnection(MinecraftServer minecraftServer, ServerLoginNetworkHandler handler, 75 | boolean b, PacketByteBuf buf, 76 | ServerLoginNetworking.LoginSynchronizer synchronizer, PacketSender packetSender) { 77 | LoginPacketSender sender = (LoginPacketSender) packetSender; 78 | 79 | if (!b) { 80 | debugInfo(handler.getConnectionInfo() + " does not respond to mod messages, kicking now"); 81 | handler.disconnect(Text.of(InertiaAntiCheatServer.serverConfig.getString("validation.vanillaKickMessage"))); 82 | return; 83 | } 84 | debugInfo(handler.getConnectionInfo() + " responds to mod messages, creating handler"); 85 | 86 | 87 | PacketByteBuf response = PacketByteBufs.create(); 88 | KeyPair keyPair = InertiaAntiCheat.createRSAPair(); 89 | this.serverKeyPair = keyPair; 90 | response.writeBytes(keyPair.getPublic().getEncoded()); 91 | 92 | ServerLoginNetworking.registerReceiver(handler, InertiaAntiCheatConstants.INITIATE_E2EE, this::setAdaptor); 93 | sender.sendPacket(InertiaAntiCheatConstants.INITIATE_E2EE, response); 94 | } 95 | 96 | /** 97 | * Retrieves and stores the client's public key 98 | * Afterward, inform client on which transfer method to use 99 | */ 100 | private void 101 | setAdaptor(MinecraftServer server, ServerLoginNetworkHandler handler, 102 | boolean b, PacketByteBuf buf, 103 | ServerLoginNetworking.LoginSynchronizer synchronizer, PacketSender packetSender) { 104 | debugInfo("Received " + handler.getConnectionInfo() + " keypair"); 105 | LoginPacketSender sender = (LoginPacketSender) packetSender; 106 | 107 | this.clientKey = InertiaAntiCheat.retrievePublicKey(buf); 108 | 109 | PacketByteBuf response = PacketByteBufs.create(); 110 | 111 | response.writeInt(InertiaAntiCheatServer.transferMethod.ordinal()); 112 | 113 | ServerLoginNetworking.registerReceiver(handler, InertiaAntiCheatConstants.SET_ADAPTOR, this::beginModTransfer); 114 | sender.sendPacket(InertiaAntiCheatConstants.SET_ADAPTOR, response); 115 | } 116 | 117 | /** 118 | * Creates transfer and validator adaptor instances 119 | */ 120 | private void 121 | beginModTransfer(MinecraftServer server, ServerLoginNetworkHandler handler, 122 | boolean b, PacketByteBuf packetByteBuf, 123 | ServerLoginNetworking.LoginSynchronizer synchronizer, PacketSender packetSender) { 124 | LoginPacketSender sender = (LoginPacketSender) packetSender; 125 | UpgradedServerLoginNetworkHandler upgradedHandler = (UpgradedServerLoginNetworkHandler) handler; 126 | 127 | Runnable failureTask = () -> { 128 | debugInfo("Address " + upgradedHandler.inertiaAntiCheat$getConnection().getAddress() + " failed modlist check"); 129 | handler.disconnect(Text.of(InertiaAntiCheatServer.serverConfig.getString("validation.deniedKickMessage"))); 130 | }; 131 | Runnable successTask = () -> debugInfo("Address " + upgradedHandler.inertiaAntiCheat$getConnection().getAddress() + " passed modlist check"); 132 | Runnable finishTask = () -> { 133 | debugInfo("Finishing transfer, checking mods now"); 134 | ServerLoginNetworking.unregisterReceiver(handler, InertiaAntiCheatConstants.SEND_MOD); 135 | }; 136 | 137 | ValidatorHandler validatorAdaptor; 138 | 139 | switch (InertiaAntiCheatServer.transferMethod) { 140 | case DATA -> { 141 | validatorAdaptor = switch (InertiaAntiCheatServer.validationMethod) { 142 | case INDIVIDUAL -> 143 | new ServerDataIndividualValidatorHandler(failureTask, successTask, finishTask); 144 | case GROUP -> 145 | new ServerDataGroupValidatorHandler(failureTask, successTask, finishTask); 146 | }; 147 | 148 | new ServerDataReceiverHandler(this.serverKeyPair, InertiaAntiCheatConstants.SEND_MOD, handler, (DataValidationHandler) validatorAdaptor); 149 | sender.sendPacket(InertiaAntiCheatConstants.SEND_MOD, PacketByteBufs.empty()); 150 | } 151 | case NAME -> { 152 | validatorAdaptor = switch (InertiaAntiCheatServer.validationMethod) { 153 | case INDIVIDUAL -> 154 | new ServerNameIndividualValidatorHandler(failureTask, successTask, finishTask); 155 | case GROUP -> 156 | new ServerNameGroupValidatorHandler(failureTask, successTask, finishTask); 157 | }; 158 | 159 | new ServerNameReceiverHandler(this.serverKeyPair, InertiaAntiCheatConstants.SEND_MOD, handler, (NameValidationHandler) validatorAdaptor); 160 | sender.sendPacket(InertiaAntiCheatConstants.SEND_MOD, PacketByteBufs.empty()); 161 | } 162 | case ID -> { 163 | validatorAdaptor = switch (InertiaAntiCheatServer.validationMethod) { 164 | case INDIVIDUAL -> 165 | new ServerIdIndividualValidatorHandler(failureTask, successTask, finishTask); 166 | case GROUP -> 167 | new ServerIdGroupValidatorHandler(failureTask, successTask, finishTask); 168 | }; 169 | 170 | new ServerIdReceiverHandler(this.serverKeyPair, InertiaAntiCheatConstants.SEND_MOD, handler, (IdValidationHandler) validatorAdaptor); 171 | sender.sendPacket(InertiaAntiCheatConstants.SEND_MOD, PacketByteBufs.empty()); 172 | } 173 | default -> // should never happen since this would get caught in server initialization, but java needs this 174 | throw new RuntimeException("Invalid or no given checking method type given in server config!"); 175 | } 176 | 177 | validatorAdaptor.future.whenComplete((ignored1, ignored2) -> this.loginBlocker.complete(null)); 178 | debugLine(); 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2023 DiffuseHyperion 2 | 3 | This program is free software: you can redistribute it and/or modify 4 | it under the terms of the GNU General Public License as published by 5 | the Free Software Foundation, either version 3 of the License, or 6 | (at your option) any later version. 7 | 8 | This program is distributed in the hope that it will be useful, 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | GNU General Public License for more details. 12 | 13 | You should have received a copy of the GNU General Public License 14 | along with this program. If not, see 15 | 16 | 17 | 18 | GNU GENERAL PUBLIC LICENSE 19 | Version 3, 29 June 2007 20 | 21 | Copyright (C) 2007 Free Software Foundation, Inc. 22 | Everyone is permitted to copy and distribute verbatim copies 23 | of this license document, but changing it is not allowed. 24 | 25 | Preamble 26 | 27 | The GNU General Public License is a free, copyleft license for 28 | software and other kinds of works. 29 | 30 | The licenses for most software and other practical works are designed 31 | to take away your freedom to share and change the works. By contrast, 32 | the GNU General Public License is intended to guarantee your freedom to 33 | share and change all versions of a program--to make sure it remains free 34 | software for all its users. We, the Free Software Foundation, use the 35 | GNU General Public License for most of our software; it applies also to 36 | any other work released this way by its authors. You can apply it to 37 | your programs, too. 38 | 39 | When we speak of free software, we are referring to freedom, not 40 | price. Our General Public Licenses are designed to make sure that you 41 | have the freedom to distribute copies of free software (and charge for 42 | them if you wish), that you receive source code or can get it if you 43 | want it, that you can change the software or use pieces of it in new 44 | free programs, and that you know you can do these things. 45 | 46 | To protect your rights, we need to prevent others from denying you 47 | these rights or asking you to surrender the rights. Therefore, you have 48 | certain responsibilities if you distribute copies of the software, or if 49 | you modify it: responsibilities to respect the freedom of others. 50 | 51 | For example, if you distribute copies of such a program, whether 52 | gratis or for a fee, you must pass on to the recipients the same 53 | freedoms that you received. You must make sure that they, too, receive 54 | or can get the source code. And you must show them these terms so they 55 | know their rights. 56 | 57 | Developers that use the GNU GPL protect your rights with two steps: 58 | (1) assert copyright on the software, and (2) offer you this License 59 | giving you legal permission to copy, distribute and/or modify it. 60 | 61 | For the developers' and authors' protection, the GPL clearly explains 62 | that there is no warranty for this free software. For both users' and 63 | authors' sake, the GPL requires that modified versions be marked as 64 | changed, so that their problems will not be attributed erroneously to 65 | authors of previous versions. 66 | 67 | Some devices are designed to deny users access to install or run 68 | modified versions of the software inside them, although the manufacturer 69 | can do so. This is fundamentally incompatible with the aim of 70 | protecting users' freedom to change the software. The systematic 71 | pattern of such abuse occurs in the area of products for individuals to 72 | use, which is precisely where it is most unacceptable. Therefore, we 73 | have designed this version of the GPL to prohibit the practice for those 74 | products. If such problems arise substantially in other domains, we 75 | stand ready to extend this provision to those domains in future versions 76 | of the GPL, as needed to protect the freedom of users. 77 | 78 | Finally, every program is threatened constantly by software patents. 79 | States should not allow patents to restrict development and use of 80 | software on general-purpose computers, but in those that do, we wish to 81 | avoid the special danger that patents applied to a free program could 82 | make it effectively proprietary. To prevent this, the GPL assures that 83 | patents cannot be used to render the program non-free. 84 | 85 | The precise terms and conditions for copying, distribution and 86 | modification follow. 87 | 88 | TERMS AND CONDITIONS 89 | 90 | 0. Definitions. 91 | 92 | "This License" refers to version 3 of the GNU General Public License. 93 | 94 | "Copyright" also means copyright-like laws that apply to other kinds of 95 | works, such as semiconductor masks. 96 | 97 | "The Program" refers to any copyrightable work licensed under this 98 | License. Each licensee is addressed as "you". "Licensees" and 99 | "recipients" may be individuals or organizations. 100 | 101 | To "modify" a work means to copy from or adapt all or part of the work 102 | in a fashion requiring copyright permission, other than the making of an 103 | exact copy. The resulting work is called a "modified version" of the 104 | earlier work or a work "based on" the earlier work. 105 | 106 | A "covered work" means either the unmodified Program or a work based 107 | on the Program. 108 | 109 | To "propagate" a work means to do anything with it that, without 110 | permission, would make you directly or secondarily liable for 111 | infringement under applicable copyright law, except executing it on a 112 | computer or modifying a private copy. Propagation includes copying, 113 | distribution (with or without modification), making available to the 114 | public, and in some countries other activities as well. 115 | 116 | To "convey" a work means any kind of propagation that enables other 117 | parties to make or receive copies. Mere interaction with a user through 118 | a computer network, with no transfer of a copy, is not conveying. 119 | 120 | An interactive user interface displays "Appropriate Legal Notices" 121 | to the extent that it includes a convenient and prominently visible 122 | feature that (1) displays an appropriate copyright notice, and (2) 123 | tells the user that there is no warranty for the work (except to the 124 | extent that warranties are provided), that licensees may convey the 125 | work under this License, and how to view a copy of this License. If 126 | the interface presents a list of user commands or options, such as a 127 | menu, a prominent item in the list meets this criterion. 128 | 129 | 1. Source Code. 130 | 131 | The "source code" for a work means the preferred form of the work 132 | for making modifications to it. "Object code" means any non-source 133 | form of a work. 134 | 135 | A "Standard Interface" means an interface that either is an official 136 | standard defined by a recognized standards body, or, in the case of 137 | interfaces specified for a particular programming language, one that 138 | is widely used among developers working in that language. 139 | 140 | The "System Libraries" of an executable work include anything, other 141 | than the work as a whole, that (a) is included in the normal form of 142 | packaging a Major Component, but which is not part of that Major 143 | Component, and (b) serves only to enable use of the work with that 144 | Major Component, or to implement a Standard Interface for which an 145 | implementation is available to the public in source code form. A 146 | "Major Component", in this context, means a major essential component 147 | (kernel, window system, and so on) of the specific operating system 148 | (if any) on which the executable work runs, or a compiler used to 149 | produce the work, or an object code interpreter used to run it. 150 | 151 | The "Corresponding Source" for a work in object code form means all 152 | the source code needed to generate, install, and (for an executable 153 | work) run the object code and to modify the work, including scripts to 154 | control those activities. However, it does not include the work's 155 | System Libraries, or general-purpose tools or generally available free 156 | programs which are used unmodified in performing those activities but 157 | which are not part of the work. For example, Corresponding Source 158 | includes interface definition files associated with source files for 159 | the work, and the source code for shared libraries and dynamically 160 | linked subprograms that the work is specifically designed to require, 161 | such as by intimate data communication or control flow between those 162 | subprograms and other parts of the work. 163 | 164 | The Corresponding Source need not include anything that users 165 | can regenerate automatically from other parts of the Corresponding 166 | Source. 167 | 168 | The Corresponding Source for a work in source code form is that 169 | same work. 170 | 171 | 2. Basic Permissions. 172 | 173 | All rights granted under this License are granted for the term of 174 | copyright on the Program, and are irrevocable provided the stated 175 | conditions are met. This License explicitly affirms your unlimited 176 | permission to run the unmodified Program. The output from running a 177 | covered work is covered by this License only if the output, given its 178 | content, constitutes a covered work. This License acknowledges your 179 | rights of fair use or other equivalent, as provided by copyright law. 180 | 181 | You may make, run and propagate covered works that you do not 182 | convey, without conditions so long as your license otherwise remains 183 | in force. You may convey covered works to others for the sole purpose 184 | of having them make modifications exclusively for you, or provide you 185 | with facilities for running those works, provided that you comply with 186 | the terms of this License in conveying all material for which you do 187 | not control copyright. Those thus making or running the covered works 188 | for you must do so exclusively on your behalf, under your direction 189 | and control, on terms that prohibit them from making any copies of 190 | your copyrighted material outside their relationship with you. 191 | 192 | Conveying under any other circumstances is permitted solely under 193 | the conditions stated below. Sublicensing is not allowed; section 10 194 | makes it unnecessary. 195 | 196 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 197 | 198 | No covered work shall be deemed part of an effective technological 199 | measure under any applicable law fulfilling obligations under article 200 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 201 | similar laws prohibiting or restricting circumvention of such 202 | measures. 203 | 204 | When you convey a covered work, you waive any legal power to forbid 205 | circumvention of technological measures to the extent such circumvention 206 | is effected by exercising rights under this License with respect to 207 | the covered work, and you disclaim any intention to limit operation or 208 | modification of the work as a means of enforcing, against the work's 209 | users, your or third parties' legal rights to forbid circumvention of 210 | technological measures. 211 | 212 | 4. Conveying Verbatim Copies. 213 | 214 | You may convey verbatim copies of the Program's source code as you 215 | receive it, in any medium, provided that you conspicuously and 216 | appropriately publish on each copy an appropriate copyright notice; 217 | keep intact all notices stating that this License and any 218 | non-permissive terms added in accord with section 7 apply to the code; 219 | keep intact all notices of the absence of any warranty; and give all 220 | recipients a copy of this License along with the Program. 221 | 222 | You may charge any price or no price for each copy that you convey, 223 | and you may offer support or warranty protection for a fee. 224 | 225 | 5. Conveying Modified Source Versions. 226 | 227 | You may convey a work based on the Program, or the modifications to 228 | produce it from the Program, in the form of source code under the 229 | terms of section 4, provided that you also meet all of these conditions: 230 | 231 | a) The work must carry prominent notices stating that you modified 232 | it, and giving a relevant date. 233 | 234 | b) The work must carry prominent notices stating that it is 235 | released under this License and any conditions added under section 236 | 7. This requirement modifies the requirement in section 4 to 237 | "keep intact all notices". 238 | 239 | c) You must license the entire work, as a whole, under this 240 | License to anyone who comes into possession of a copy. This 241 | License will therefore apply, along with any applicable section 7 242 | additional terms, to the whole of the work, and all its parts, 243 | regardless of how they are packaged. This License gives no 244 | permission to license the work in any other way, but it does not 245 | invalidate such permission if you have separately received it. 246 | 247 | d) If the work has interactive user interfaces, each must display 248 | Appropriate Legal Notices; however, if the Program has interactive 249 | interfaces that do not display Appropriate Legal Notices, your 250 | work need not make them do so. 251 | 252 | A compilation of a covered work with other separate and independent 253 | works, which are not by their nature extensions of the covered work, 254 | and which are not combined with it such as to form a larger program, 255 | in or on a volume of a storage or distribution medium, is called an 256 | "aggregate" if the compilation and its resulting copyright are not 257 | used to limit the access or legal rights of the compilation's users 258 | beyond what the individual works permit. Inclusion of a covered work 259 | in an aggregate does not cause this License to apply to the other 260 | parts of the aggregate. 261 | 262 | 6. Conveying Non-Source Forms. 263 | 264 | You may convey a covered work in object code form under the terms 265 | of sections 4 and 5, provided that you also convey the 266 | machine-readable Corresponding Source under the terms of this License, 267 | in one of these ways: 268 | 269 | a) Convey the object code in, or embodied in, a physical product 270 | (including a physical distribution medium), accompanied by the 271 | Corresponding Source fixed on a durable physical medium 272 | customarily used for software interchange. 273 | 274 | b) Convey the object code in, or embodied in, a physical product 275 | (including a physical distribution medium), accompanied by a 276 | written offer, valid for at least three years and valid for as 277 | long as you offer spare parts or customer support for that product 278 | model, to give anyone who possesses the object code either (1) a 279 | copy of the Corresponding Source for all the software in the 280 | product that is covered by this License, on a durable physical 281 | medium customarily used for software interchange, for a price no 282 | more than your reasonable cost of physically performing this 283 | conveying of source, or (2) access to copy the 284 | Corresponding Source from a network server at no charge. 285 | 286 | c) Convey individual copies of the object code with a copy of the 287 | written offer to provide the Corresponding Source. This 288 | alternative is allowed only occasionally and noncommercially, and 289 | only if you received the object code with such an offer, in accord 290 | with subsection 6b. 291 | 292 | d) Convey the object code by offering access from a designated 293 | place (gratis or for a charge), and offer equivalent access to the 294 | Corresponding Source in the same way through the same place at no 295 | further charge. You need not require recipients to copy the 296 | Corresponding Source along with the object code. If the place to 297 | copy the object code is a network server, the Corresponding Source 298 | may be on a different server (operated by you or a third party) 299 | that supports equivalent copying facilities, provided you maintain 300 | clear directions next to the object code saying where to find the 301 | Corresponding Source. Regardless of what server hosts the 302 | Corresponding Source, you remain obligated to ensure that it is 303 | available for as long as needed to satisfy these requirements. 304 | 305 | e) Convey the object code using peer-to-peer transmission, provided 306 | you inform other peers where the object code and Corresponding 307 | Source of the work are being offered to the general public at no 308 | charge under subsection 6d. 309 | 310 | A separable portion of the object code, whose source code is excluded 311 | from the Corresponding Source as a System Library, need not be 312 | included in conveying the object code work. 313 | 314 | A "User Product" is either (1) a "consumer product", which means any 315 | tangible personal property which is normally used for personal, family, 316 | or household purposes, or (2) anything designed or sold for incorporation 317 | into a dwelling. In determining whether a product is a consumer product, 318 | doubtful cases shall be resolved in favor of coverage. For a particular 319 | product received by a particular user, "normally used" refers to a 320 | typical or common use of that class of product, regardless of the status 321 | of the particular user or of the way in which the particular user 322 | actually uses, or expects or is expected to use, the product. A product 323 | is a consumer product regardless of whether the product has substantial 324 | commercial, industrial or non-consumer uses, unless such uses represent 325 | the only significant mode of use of the product. 326 | 327 | "Installation Information" for a User Product means any methods, 328 | procedures, authorization keys, or other information required to install 329 | and execute modified versions of a covered work in that User Product from 330 | a modified version of its Corresponding Source. The information must 331 | suffice to ensure that the continued functioning of the modified object 332 | code is in no case prevented or interfered with solely because 333 | modification has been made. 334 | 335 | If you convey an object code work under this section in, or with, or 336 | specifically for use in, a User Product, and the conveying occurs as 337 | part of a transaction in which the right of possession and use of the 338 | User Product is transferred to the recipient in perpetuity or for a 339 | fixed term (regardless of how the transaction is characterized), the 340 | Corresponding Source conveyed under this section must be accompanied 341 | by the Installation Information. But this requirement does not apply 342 | if neither you nor any third party retains the ability to install 343 | modified object code on the User Product (for example, the work has 344 | been installed in ROM). 345 | 346 | The requirement to provide Installation Information does not include a 347 | requirement to continue to provide support service, warranty, or updates 348 | for a work that has been modified or installed by the recipient, or for 349 | the User Product in which it has been modified or installed. Access to a 350 | network may be denied when the modification itself materially and 351 | adversely affects the operation of the network or violates the rules and 352 | protocols for communication across the network. 353 | 354 | Corresponding Source conveyed, and Installation Information provided, 355 | in accord with this section must be in a format that is publicly 356 | documented (and with an implementation available to the public in 357 | source code form), and must require no special password or key for 358 | unpacking, reading or copying. 359 | 360 | 7. Additional Terms. 361 | 362 | "Additional permissions" are terms that supplement the terms of this 363 | License by making exceptions from one or more of its conditions. 364 | Additional permissions that are applicable to the entire Program shall 365 | be treated as though they were included in this License, to the extent 366 | that they are valid under applicable law. If additional permissions 367 | apply only to part of the Program, that part may be used separately 368 | under those permissions, but the entire Program remains governed by 369 | this License without regard to the additional permissions. 370 | 371 | When you convey a copy of a covered work, you may at your option 372 | remove any additional permissions from that copy, or from any part of 373 | it. (Additional permissions may be written to require their own 374 | removal in certain cases when you modify the work.) You may place 375 | additional permissions on material, added by you to a covered work, 376 | for which you have or can give appropriate copyright permission. 377 | 378 | Notwithstanding any other provision of this License, for material you 379 | add to a covered work, you may (if authorized by the copyright holders of 380 | that material) supplement the terms of this License with terms: 381 | 382 | a) Disclaiming warranty or limiting liability differently from the 383 | terms of sections 15 and 16 of this License; or 384 | 385 | b) Requiring preservation of specified reasonable legal notices or 386 | author attributions in that material or in the Appropriate Legal 387 | Notices displayed by works containing it; or 388 | 389 | c) Prohibiting misrepresentation of the origin of that material, or 390 | requiring that modified versions of such material be marked in 391 | reasonable ways as different from the original version; or 392 | 393 | d) Limiting the use for publicity purposes of names of licensors or 394 | authors of the material; or 395 | 396 | e) Declining to grant rights under trademark law for use of some 397 | trade names, trademarks, or service marks; or 398 | 399 | f) Requiring indemnification of licensors and authors of that 400 | material by anyone who conveys the material (or modified versions of 401 | it) with contractual assumptions of liability to the recipient, for 402 | any liability that these contractual assumptions directly impose on 403 | those licensors and authors. 404 | 405 | All other non-permissive additional terms are considered "further 406 | restrictions" within the meaning of section 10. If the Program as you 407 | received it, or any part of it, contains a notice stating that it is 408 | governed by this License along with a term that is a further 409 | restriction, you may remove that term. If a license document contains 410 | a further restriction but permits relicensing or conveying under this 411 | License, you may add to a covered work material governed by the terms 412 | of that license document, provided that the further restriction does 413 | not survive such relicensing or conveying. 414 | 415 | If you add terms to a covered work in accord with this section, you 416 | must place, in the relevant source files, a statement of the 417 | additional terms that apply to those files, or a notice indicating 418 | where to find the applicable terms. 419 | 420 | Additional terms, permissive or non-permissive, may be stated in the 421 | form of a separately written license, or stated as exceptions; 422 | the above requirements apply either way. 423 | 424 | 8. Termination. 425 | 426 | You may not propagate or modify a covered work except as expressly 427 | provided under this License. Any attempt otherwise to propagate or 428 | modify it is void, and will automatically terminate your rights under 429 | this License (including any patent licenses granted under the third 430 | paragraph of section 11). 431 | 432 | However, if you cease all violation of this License, then your 433 | license from a particular copyright holder is reinstated (a) 434 | provisionally, unless and until the copyright holder explicitly and 435 | finally terminates your license, and (b) permanently, if the copyright 436 | holder fails to notify you of the violation by some reasonable means 437 | prior to 60 days after the cessation. 438 | 439 | Moreover, your license from a particular copyright holder is 440 | reinstated permanently if the copyright holder notifies you of the 441 | violation by some reasonable means, this is the first time you have 442 | received notice of violation of this License (for any work) from that 443 | copyright holder, and you cure the violation prior to 30 days after 444 | your receipt of the notice. 445 | 446 | Termination of your rights under this section does not terminate the 447 | licenses of parties who have received copies or rights from you under 448 | this License. If your rights have been terminated and not permanently 449 | reinstated, you do not qualify to receive new licenses for the same 450 | material under section 10. 451 | 452 | 9. Acceptance Not Required for Having Copies. 453 | 454 | You are not required to accept this License in order to receive or 455 | run a copy of the Program. Ancillary propagation of a covered work 456 | occurring solely as a consequence of using peer-to-peer transmission 457 | to receive a copy likewise does not require acceptance. However, 458 | nothing other than this License grants you permission to propagate or 459 | modify any covered work. These actions infringe copyright if you do 460 | not accept this License. Therefore, by modifying or propagating a 461 | covered work, you indicate your acceptance of this License to do so. 462 | 463 | 10. Automatic Licensing of Downstream Recipients. 464 | 465 | Each time you convey a covered work, the recipient automatically 466 | receives a license from the original licensors, to run, modify and 467 | propagate that work, subject to this License. You are not responsible 468 | for enforcing compliance by third parties with this License. 469 | 470 | An "entity transaction" is a transaction transferring control of an 471 | organization, or substantially all assets of one, or subdividing an 472 | organization, or merging organizations. If propagation of a covered 473 | work results from an entity transaction, each party to that 474 | transaction who receives a copy of the work also receives whatever 475 | licenses to the work the party's predecessor in interest had or could 476 | give under the previous paragraph, plus a right to possession of the 477 | Corresponding Source of the work from the predecessor in interest, if 478 | the predecessor has it or can get it with reasonable efforts. 479 | 480 | You may not impose any further restrictions on the exercise of the 481 | rights granted or affirmed under this License. For example, you may 482 | not impose a license fee, royalty, or other charge for exercise of 483 | rights granted under this License, and you may not initiate litigation 484 | (including a cross-claim or counterclaim in a lawsuit) alleging that 485 | any patent claim is infringed by making, using, selling, offering for 486 | sale, or importing the Program or any portion of it. 487 | 488 | 11. Patents. 489 | 490 | A "contributor" is a copyright holder who authorizes use under this 491 | License of the Program or a work on which the Program is based. The 492 | work thus licensed is called the contributor's "contributor version". 493 | 494 | A contributor's "essential patent claims" are all patent claims 495 | owned or controlled by the contributor, whether already acquired or 496 | hereafter acquired, that would be infringed by some manner, permitted 497 | by this License, of making, using, or selling its contributor version, 498 | but do not include claims that would be infringed only as a 499 | consequence of further modification of the contributor version. For 500 | purposes of this definition, "control" includes the right to grant 501 | patent sublicenses in a manner consistent with the requirements of 502 | this License. 503 | 504 | Each contributor grants you a non-exclusive, worldwide, royalty-free 505 | patent license under the contributor's essential patent claims, to 506 | make, use, sell, offer for sale, import and otherwise run, modify and 507 | propagate the contents of its contributor version. 508 | 509 | In the following three paragraphs, a "patent license" is any express 510 | agreement or commitment, however denominated, not to enforce a patent 511 | (such as an express permission to practice a patent or covenant not to 512 | sue for patent infringement). To "grant" such a patent license to a 513 | party means to make such an agreement or commitment not to enforce a 514 | patent against the party. 515 | 516 | If you convey a covered work, knowingly relying on a patent license, 517 | and the Corresponding Source of the work is not available for anyone 518 | to copy, free of charge and under the terms of this License, through a 519 | publicly available network server or other readily accessible means, 520 | then you must either (1) cause the Corresponding Source to be so 521 | available, or (2) arrange to deprive yourself of the benefit of the 522 | patent license for this particular work, or (3) arrange, in a manner 523 | consistent with the requirements of this License, to extend the patent 524 | license to downstream recipients. "Knowingly relying" means you have 525 | actual knowledge that, but for the patent license, your conveying the 526 | covered work in a country, or your recipient's use of the covered work 527 | in a country, would infringe one or more identifiable patents in that 528 | country that you have reason to believe are valid. 529 | 530 | If, pursuant to or in connection with a single transaction or 531 | arrangement, you convey, or propagate by procuring conveyance of, a 532 | covered work, and grant a patent license to some of the parties 533 | receiving the covered work authorizing them to use, propagate, modify 534 | or convey a specific copy of the covered work, then the patent license 535 | you grant is automatically extended to all recipients of the covered 536 | work and works based on it. 537 | 538 | A patent license is "discriminatory" if it does not include within 539 | the scope of its coverage, prohibits the exercise of, or is 540 | conditioned on the non-exercise of one or more of the rights that are 541 | specifically granted under this License. You may not convey a covered 542 | work if you are a party to an arrangement with a third party that is 543 | in the business of distributing software, under which you make payment 544 | to the third party based on the extent of your activity of conveying 545 | the work, and under which the third party grants, to any of the 546 | parties who would receive the covered work from you, a discriminatory 547 | patent license (a) in connection with copies of the covered work 548 | conveyed by you (or copies made from those copies), or (b) primarily 549 | for and in connection with specific products or compilations that 550 | contain the covered work, unless you entered into that arrangement, 551 | or that patent license was granted, prior to 28 March 2007. 552 | 553 | Nothing in this License shall be construed as excluding or limiting 554 | any implied license or other defenses to infringement that may 555 | otherwise be available to you under applicable patent law. 556 | 557 | 12. No Surrender of Others' Freedom. 558 | 559 | If conditions are imposed on you (whether by court order, agreement or 560 | otherwise) that contradict the conditions of this License, they do not 561 | excuse you from the conditions of this License. If you cannot convey a 562 | covered work so as to satisfy simultaneously your obligations under this 563 | License and any other pertinent obligations, then as a consequence you may 564 | not convey it at all. For example, if you agree to terms that obligate you 565 | to collect a royalty for further conveying from those to whom you convey 566 | the Program, the only way you could satisfy both those terms and this 567 | License would be to refrain entirely from conveying the Program. 568 | 569 | 13. Use with the GNU Affero General Public License. 570 | 571 | Notwithstanding any other provision of this License, you have 572 | permission to link or combine any covered work with a work licensed 573 | under version 3 of the GNU Affero General Public License into a single 574 | combined work, and to convey the resulting work. The terms of this 575 | License will continue to apply to the part which is the covered work, 576 | but the special requirements of the GNU Affero General Public License, 577 | section 13, concerning interaction through a network will apply to the 578 | combination as such. 579 | 580 | 14. Revised Versions of this License. 581 | 582 | The Free Software Foundation may publish revised and/or new versions of 583 | the GNU General Public License from time to time. Such new versions will 584 | be similar in spirit to the present version, but may differ in detail to 585 | address new problems or concerns. 586 | 587 | Each version is given a distinguishing version number. If the 588 | Program specifies that a certain numbered version of the GNU General 589 | Public License "or any later version" applies to it, you have the 590 | option of following the terms and conditions either of that numbered 591 | version or of any later version published by the Free Software 592 | Foundation. If the Program does not specify a version number of the 593 | GNU General Public License, you may choose any version ever published 594 | by the Free Software Foundation. 595 | 596 | If the Program specifies that a proxy can decide which future 597 | versions of the GNU General Public License can be used, that proxy's 598 | public statement of acceptance of a version permanently authorizes you 599 | to choose that version for the Program. 600 | 601 | Later license versions may give you additional or different 602 | permissions. However, no additional obligations are imposed on any 603 | author or copyright holder as a result of your choosing to follow a 604 | later version. 605 | 606 | 15. Disclaimer of Warranty. 607 | 608 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 609 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 610 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 611 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 612 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 613 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 614 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 615 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 616 | 617 | 16. Limitation of Liability. 618 | 619 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 620 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 621 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 622 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 623 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 624 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 625 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 626 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 627 | SUCH DAMAGES. 628 | 629 | 17. Interpretation of Sections 15 and 16. 630 | 631 | If the disclaimer of warranty and limitation of liability provided 632 | above cannot be given local legal effect according to their terms, 633 | reviewing courts shall apply local law that most closely approximates 634 | an absolute waiver of all civil liability in connection with the 635 | Program, unless a warranty or assumption of liability accompanies a 636 | copy of the Program in return for a fee. 637 | 638 | END OF TERMS AND CONDITIONS 639 | 640 | How to Apply These Terms to Your New Programs 641 | 642 | If you develop a new program, and you want it to be of the greatest 643 | possible use to the public, the best way to achieve this is to make it 644 | free software which everyone can redistribute and change under these terms. 645 | 646 | To do so, attach the following notices to the program. It is safest 647 | to attach them to the start of each source file to most effectively 648 | state the exclusion of warranty; and each file should have at least 649 | the "copyright" line and a pointer to where the full notice is found. 650 | 651 | {one line to give the program's name and a brief idea of what it does.} 652 | Copyright (C) {year} {name of author} 653 | 654 | This program is free software: you can redistribute it and/or modify 655 | it under the terms of the GNU General Public License as published by 656 | the Free Software Foundation, either version 3 of the License, or 657 | (at your option) any later version. 658 | 659 | This program is distributed in the hope that it will be useful, 660 | but WITHOUT ANY WARRANTY; without even the implied warranty of 661 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 662 | GNU General Public License for more details. 663 | 664 | You should have received a copy of the GNU General Public License 665 | along with this program. If not, see . 666 | 667 | Also add information on how to contact you by electronic and paper mail. 668 | 669 | If the program does terminal interaction, make it output a short 670 | notice like this when it starts in an interactive mode: 671 | 672 | {project} Copyright (C) {year} {fullname} 673 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 674 | This is free software, and you are welcome to redistribute it 675 | under certain conditions; type `show c' for details. 676 | 677 | The hypothetical commands `show w' and `show c' should show the appropriate 678 | parts of the General Public License. Of course, your program's commands 679 | might be different; for a GUI interface, you would use an "about box". 680 | 681 | You should also get your employer (if you work as a programmer) or school, 682 | if any, to sign a "copyright disclaimer" for the program, if necessary. 683 | For more information on this, and how to apply and follow the GNU GPL, see 684 | . 685 | 686 | The GNU General Public License does not permit incorporating your program 687 | into proprietary programs. If your program is a subroutine library, you 688 | may consider it more useful to permit linking proprietary applications with 689 | the library. If this is what you want to do, use the GNU Lesser General 690 | Public License instead of this License. But first, please read 691 | . 692 | 693 | --------------------------------------------------------------------------------