TCPShield Fabric support (#40)
Adds Fabric support (huge, huge thanks to @Draylar)
This commit is contained in:
1 parent
dd51276790
commit
740e648b92
12 files changed
+312
-1
No files matched your search
@@ -6,7 +6,7 @@ It also parses passed IP addresses so the server is aware of the real player IP
|
||||
|
||||
### Compatibility
|
||||
|
||||
TCPShield is compatible with Spigot / CraftBukkit, BungeeCord and Velocity.
|
||||
TCPShield is compatible with Spigot / CraftBukkit, BungeeCord, Velocity and Fabric.
|
||||
|
||||
When using Spigot / CraftBukkit, [ProtocolLib](https://github.com/aadnk/ProtocolLib) needs to be installed.
|
||||
This does not apply when using Paper 1.16 build #503 or higher.
|
||||
@@ -30,4 +30,5 @@ See [Contact](https://docs.tcpshield.com/about-us)
|
||||
These wonderful contributors have helped TCPShield make this plugin better!
|
||||
|
||||
* [Paul Zhang](https://github.com/paulzhng)
|
||||
* [Draylar](https://github.com/Draylar)
|
||||
* [RyanDeLap](https://github.com/RyanDeLap)
|
||||
@@ -19,6 +19,7 @@ buildscript {
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'idea'
|
||||
id 'fabric-loom' version '0.6-SNAPSHOT'
|
||||
}
|
||||
|
||||
repositories {
|
||||
@@ -40,6 +41,9 @@ repositories {
|
||||
maven {
|
||||
url = 'https://repo.velocitypowered.com/snapshots/'
|
||||
}
|
||||
maven {
|
||||
url = 'https://maven.fabricmc.net/'
|
||||
}
|
||||
flatDir {
|
||||
dirs '/libs'
|
||||
}
|
||||
@@ -59,6 +63,11 @@ dependencies {
|
||||
// Velocity
|
||||
compileOnly group: 'com.velocitypowered', name: 'velocity-api', version: '1.0.0-SNAPSHOT'
|
||||
|
||||
// Fabric
|
||||
minecraft "com.mojang:minecraft:1.16.5"
|
||||
mappings "net.fabricmc:yarn:1.16.5+build.5:v2"
|
||||
modImplementation group: 'net.fabricmc', name: 'fabric-loader', version: '0.11.2'
|
||||
|
||||
// Testing
|
||||
testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.7.0-M1'
|
||||
testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.7.0-M1'
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
pluginManagement {
|
||||
repositories {
|
||||
jcenter()
|
||||
maven {
|
||||
name = 'Fabric'
|
||||
url = 'https://maven.fabricmc.net/'
|
||||
}
|
||||
gradlePluginPortal()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package net.tcpshield.tcpshield.fabric;
|
||||
|
||||
import net.fabricmc.api.ModInitializer;
|
||||
import net.tcpshield.tcpshield.HandshakePacketHandler;
|
||||
import net.tcpshield.tcpshield.fabric.impl.FabricConfigImpl;
|
||||
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class TCPShieldFabric implements ModInitializer {
|
||||
|
||||
public static final Logger LOGGER = Logger.getLogger("TCPShield");
|
||||
public static final HandshakePacketHandler PACKET_HANDLER = new HandshakePacketHandler(LOGGER, new FabricConfigImpl());
|
||||
|
||||
@Override
|
||||
public void onInitialize() {
|
||||
LOGGER.info("TCPShield has been loaded.");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
package net.tcpshield.tcpshield.fabric.impl;
|
||||
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import net.tcpshield.tcpshield.abstraction.TCPShieldConfig;
|
||||
import net.tcpshield.tcpshield.exception.TCPShieldInitializationException;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
public class FabricConfigImpl extends TCPShieldConfig {
|
||||
|
||||
private final Path configLocation = new File(FabricLoader.getInstance().getConfigDir().toString(), "tcpshield.yml").toPath();
|
||||
|
||||
public FabricConfigImpl() {
|
||||
if(!configExists()) {
|
||||
createDefaultConfig();
|
||||
}
|
||||
|
||||
ConfigData config = loadConfig();
|
||||
|
||||
this.onlyProxy = config.onlyAllowProxyConnections;
|
||||
this.ipWhitelistFolder = new File(FabricLoader.getInstance().getGameDirectory(), "ip-whitelist");
|
||||
this.geyser = false;
|
||||
this.debug = config.debugMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether the file specified in {@link FabricConfigImpl#configLocation} exists.
|
||||
*/
|
||||
private boolean configExists() {
|
||||
return Files.exists(configLocation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the default configuration file at the location specified in {@link FabricConfigImpl#configLocation}.
|
||||
*
|
||||
* <p>
|
||||
* This operation will always override an existing configuration file if it exists.
|
||||
* Callers should check {@link FabricConfigImpl#configExists()} if they wish to avoid this behavior.
|
||||
*/
|
||||
private void createDefaultConfig() {
|
||||
// Ensure the parent directory of our config file exists.
|
||||
// If it does not exist, attempt to create it now.
|
||||
Path parentDirectory = configLocation.getParent();
|
||||
if (!Files.exists(parentDirectory) || !Files.isDirectory(parentDirectory)) {
|
||||
parentDirectory.toFile().mkdirs();
|
||||
}
|
||||
|
||||
// Copy the config.yml data from our mod jar to the loader config folder.
|
||||
try (InputStream in = getClass().getClassLoader().getResourceAsStream("config.yml")) {
|
||||
Files.copy(in, configLocation);
|
||||
} catch (Exception e) {
|
||||
throw new TCPShieldInitializationException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private ConfigData loadConfig() {
|
||||
try {
|
||||
List<String> strings = Files.readAllLines(configLocation);
|
||||
|
||||
boolean onlyAllowProxyConnections = true;
|
||||
boolean debugMode = false;
|
||||
|
||||
// Rudimentary config parsing
|
||||
for (String line : strings) {
|
||||
String[] keyValue = line.split(": ");
|
||||
|
||||
// A config option will only be valid if it is in the format 'a: b'.
|
||||
// Ensure that is the case now.
|
||||
if (keyValue.length == 2) {
|
||||
String key = keyValue[0];
|
||||
String value = keyValue[1];
|
||||
|
||||
switch (key) {
|
||||
case "only-allow-proxy-connections":
|
||||
onlyAllowProxyConnections = Boolean.parseBoolean(value);
|
||||
break;
|
||||
case "debug-mode":
|
||||
debugMode = Boolean.parseBoolean(value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new ConfigData(onlyAllowProxyConnections, debugMode);
|
||||
} catch (Exception e) {
|
||||
throw new TCPShieldInitializationException("Couldn't load config in config/tclshield.yml!");
|
||||
}
|
||||
}
|
||||
|
||||
private static class ConfigData {
|
||||
|
||||
protected boolean onlyAllowProxyConnections;
|
||||
protected boolean debugMode;
|
||||
|
||||
public ConfigData(boolean onlyAllowProxyConnections, boolean debugMode) {
|
||||
this.onlyAllowProxyConnections = onlyAllowProxyConnections;
|
||||
this.debugMode = debugMode;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package net.tcpshield.tcpshield.fabric.impl;
|
||||
|
||||
import net.minecraft.network.packet.c2s.handshake.HandshakeC2SPacket;
|
||||
import net.tcpshield.tcpshield.abstraction.IPacket;
|
||||
import net.tcpshield.tcpshield.fabric.mixin.HandshakeC2SPacketAccessor;
|
||||
|
||||
public class FabricPacketImpl implements IPacket {
|
||||
|
||||
private final HandshakeC2SPacket handshake;
|
||||
|
||||
public FabricPacketImpl(HandshakeC2SPacket handshake) {
|
||||
this.handshake = handshake;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRawPayload() {
|
||||
return ((HandshakeC2SPacketAccessor) handshake).getAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void modifyOriginalPacket(String hostname) throws Exception {
|
||||
// NO OPERATION
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package net.tcpshield.tcpshield.fabric.impl;
|
||||
|
||||
import net.minecraft.network.ClientConnection;
|
||||
import net.minecraft.network.packet.c2s.handshake.HandshakeC2SPacket;
|
||||
import net.minecraft.network.packet.s2c.login.LoginDisconnectS2CPacket;
|
||||
import net.minecraft.text.LiteralText;
|
||||
import net.tcpshield.tcpshield.abstraction.IPlayer;
|
||||
import net.tcpshield.tcpshield.exception.IPModificationFailureException;
|
||||
import net.tcpshield.tcpshield.fabric.mixin.ClientConnectionAccessor;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
|
||||
public class FabricPlayerImpl implements IPlayer {
|
||||
|
||||
private final ClientConnection connection;
|
||||
private String ip;
|
||||
|
||||
public FabricPlayerImpl(HandshakeC2SPacket packet, ClientConnection connection) {
|
||||
this.connection = connection;
|
||||
this.ip = ((InetSocketAddress) ((ClientConnectionAccessor) connection).getChannel().remoteAddress()).getAddress().getHostAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIP() {
|
||||
return ip;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIP(InetSocketAddress ip) throws IPModificationFailureException {
|
||||
// At this point, the IP/connection believe the player has the IP of TCPShield.
|
||||
// The ip passed into this method contains their CORRECT data, which we have to assign to the player network connection.
|
||||
((ClientConnectionAccessor) connection).setAddress(ip);
|
||||
this.ip = ((InetSocketAddress) ((ClientConnectionAccessor) connection).getChannel().remoteAddress()).getAddress().getHostAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnect() {
|
||||
connection.disconnect(new LiteralText("Connection failed. Please try again or contact an administrator."));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package net.tcpshield.tcpshield.fabric.mixin;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import net.minecraft.network.ClientConnection;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
import java.net.SocketAddress;
|
||||
|
||||
@Mixin(ClientConnection.class)
|
||||
public interface ClientConnectionAccessor {
|
||||
@Accessor
|
||||
void setAddress(SocketAddress address);
|
||||
|
||||
@Accessor
|
||||
Channel getChannel();
|
||||
|
||||
@Accessor
|
||||
SocketAddress getAddress();
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package net.tcpshield.tcpshield.fabric.mixin;
|
||||
|
||||
import net.minecraft.network.packet.c2s.handshake.HandshakeC2SPacket;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
@Mixin(HandshakeC2SPacket.class)
|
||||
public interface HandshakeC2SPacketAccessor {
|
||||
@Accessor
|
||||
String getAddress();
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package net.tcpshield.tcpshield.fabric.mixin;
|
||||
|
||||
import net.minecraft.network.ClientConnection;
|
||||
import net.minecraft.network.packet.c2s.handshake.HandshakeC2SPacket;
|
||||
import net.minecraft.server.network.ServerHandshakeNetworkHandler;
|
||||
import net.tcpshield.tcpshield.abstraction.IPacket;
|
||||
import net.tcpshield.tcpshield.abstraction.IPlayer;
|
||||
import net.tcpshield.tcpshield.fabric.TCPShieldFabric;
|
||||
import net.tcpshield.tcpshield.fabric.impl.FabricPacketImpl;
|
||||
import net.tcpshield.tcpshield.fabric.impl.FabricPlayerImpl;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(ServerHandshakeNetworkHandler.class)
|
||||
public class ServerHandshakeMixin {
|
||||
|
||||
@Shadow @Final private ClientConnection connection;
|
||||
|
||||
@Inject(
|
||||
method = "onHandshake",
|
||||
at = @At("HEAD"))
|
||||
private void onHandshake(HandshakeC2SPacket handshake, CallbackInfo ci) {
|
||||
IPacket packet = new FabricPacketImpl(handshake);
|
||||
IPlayer player = new FabricPlayerImpl(handshake, connection);
|
||||
|
||||
TCPShieldFabric.PACKET_HANDLER.onHandshake(packet, player);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "tcp-shield",
|
||||
"version": "2.5",
|
||||
"name": "TCPShield",
|
||||
"description": "TCPShield IP parsing capabilities for Fabric",
|
||||
"authors": [
|
||||
"TCPShield"
|
||||
],
|
||||
"contact": {
|
||||
"homepage": "https://tcpshield.com"
|
||||
},
|
||||
"license": "MIT",
|
||||
"environment": "*",
|
||||
"entrypoints": {
|
||||
"main": [
|
||||
"net.tcpshield.tcpshield.fabric.TCPShieldFabric"
|
||||
]
|
||||
},
|
||||
"mixins": [
|
||||
"tcpshield.mixins.json"
|
||||
],
|
||||
"depends": {
|
||||
"fabricloader": "*",
|
||||
"minecraft": "*"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"required": true,
|
||||
"minVersion": "0.8",
|
||||
"package": "net.tcpshield.tcpshield.fabric.mixin",
|
||||
"compatibilityLevel": "JAVA_8",
|
||||
"mixins": [
|
||||
"ClientConnectionAccessor",
|
||||
"HandshakeC2SPacketAccessor",
|
||||
"ServerHandshakeMixin"
|
||||
],
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user