SteamWar/BungeeCore
Archiviert
13
2

Mod detection refactoring #509

Zusammengeführt
Lixfel hat 5 Commits von modDetectionRework nach master 2024-03-14 11:42:23 +01:00 zusammengeführt
11 geänderte Dateien mit 261 neuen und 171 gelöschten Zeilen
Nur Änderungen aus Commit 437d1c055c werden angezeigt - Alle Commits anzeigen

Datei anzeigen

@ -71,6 +71,15 @@ repositories {
}
}
shadowJar {
exclude 'META-INF/*'
//https://imperceptiblethoughts.com/shadow/configuration/minimizing/
minimize {
exclude project(':')
}
duplicatesStrategy DuplicatesStrategy.INCLUDE
}
dependencies {
compileOnly 'org.projectlombok:lombok:1.18.22'
testCompileOnly 'org.projectlombok:lombok:1.18.22'

Datei anzeigen

@ -92,6 +92,7 @@ public class BungeeCore extends Plugin {
});
ProxyServer.getInstance().getScheduler().schedule(this, TabCompletionCache::invalidateOldEntries, 1, 1, TimeUnit.SECONDS);
new Hostname();
new ServerListPing();
new PluginMessage();
new Schematica();
@ -104,7 +105,7 @@ public class BungeeCore extends Plugin {
new ChatListener();
new BanListener();
new CheckListener();
new SubserverProtocolFixer();
new IPSanitizer();
local = new Node.LocalNode();
if(MAIN_SERVER) {

Datei anzeigen

@ -22,7 +22,10 @@ package de.steamwar.bungeecore;
import net.md_5.bungee.BungeeCord;
import net.md_5.bungee.api.ProxyServer;
import java.io.*;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.util.*;
import java.util.concurrent.TimeUnit;
@ -31,6 +34,7 @@ import java.util.logging.Level;
public abstract class Node {
//-Xquickstart Langzeitperformance testen!
private static final List<String> OPENJ9_ARGS = Arrays.asList(
"-XX:+EnableCRIUSupport", "-XX:-CRIURestoreNonPortableMode",
"-Xgc:excessiveGCratio=80", "-Xdisableexplicitgc", "-Xnoclassgc", "-Xmos128M", "-Xmns48M", "-XX:+ExitOnOutOfMemoryError", // initial heap half values of memory observed by 1.19 spectate server
@ -118,6 +122,8 @@ public abstract class Node {
cmd.add(worldDir);
cmd.add("--level-name");
cmd.add(levelName);
cmd.add("--plugins");
cmd.add("/home/lixfel/bpl");
cmd.add("--port");
cmd.add(String.valueOf(port));
cmd.add("nogui");

Datei anzeigen

@ -19,52 +19,41 @@
package de.steamwar.bungeecore.listeners;
import net.md_5.bungee.BungeeCord;
import de.steamwar.bungeecore.BungeeCore;
import net.md_5.bungee.api.event.LoginEvent;
import net.md_5.bungee.connection.InitialHandler;
import net.md_5.bungee.connection.LoginResult;
import net.md_5.bungee.event.EventHandler;
import net.md_5.bungee.protocol.Property;
import net.md_5.bungee.util.AddressUtil;
import net.md_5.bungee.netty.ChannelWrapper;
import java.lang.reflect.Field;
import java.net.InetSocketAddress;
import java.util.logging.Level;
public class SubserverProtocolFixer extends BasicListener {
public class IPSanitizer extends BasicListener {
private static final Field initialHandlerCh;
static {
try {
initialHandlerCh = InitialHandler.class.getDeclaredField("ch");
} catch (NoSuchFieldException e) {
throw new SecurityException("Could not initialize Reflection", e);
}
initialHandlerCh.setAccessible(true);
}
public static ChannelWrapper getChannelWrapper(InitialHandler handler) {
try {
return (ChannelWrapper) initialHandlerCh.get(handler);
} catch (IllegalAccessException e) {
throw new SecurityException("Could not get channel wrapper", e);
}
}
private final InetSocketAddress inetSocketAddress = new InetSocketAddress("127.127.127.127", 25565);
private Field field;
{
try {
field = InitialHandler.class.getDeclaredField("extraDataInHandshake");
field.setAccessible(true);
} catch (NoSuchFieldException e) {
BungeeCord.getInstance().getLogger().log(Level.SEVERE, e.getMessage(), e);
}
}
@EventHandler
public void loginEvent(LoginEvent e) {
InitialHandler initialHandler = ((InitialHandler) e.getConnection());
String undashedUUID = initialHandler.getUniqueId().toString().replace("-", "");
String extraData = "\00" + AddressUtil.sanitizeAddress(inetSocketAddress) + "\00" + undashedUUID;
LoginResult result = initialHandler.getLoginProfile();
if (result != null) {
Property[] properties = result.getProperties();
if (properties.length > 0) {
extraData += "\00" + BungeeCord.getInstance().gson.toJson(properties);
}
}
try {
field.set(initialHandler, extraData);
} catch (IllegalAccessException ex) {
BungeeCord.getInstance().getLogger().log(Level.SEVERE, ex.getMessage(), ex);
}
BungeeCore.get().getLogger().log(Level.INFO, e.getConnection().getSocketAddress() + " has logged in with user name " + e.getConnection().getName());
getChannelWrapper((InitialHandler) e.getConnection()).setRemoteAddress(inetSocketAddress);
}
}

Datei anzeigen

@ -28,24 +28,29 @@ import de.steamwar.messages.ChatSender;
import de.steamwar.network.packets.NetworkPacket;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.Unpooled;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.connection.Server;
import net.md_5.bungee.api.event.PluginMessageEvent;
import net.md_5.bungee.connection.InitialHandler;
import net.md_5.bungee.event.EventHandler;
import net.md_5.bungee.protocol.DefinedPacket;
import java.net.InetSocketAddress;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.*;
import java.util.logging.Level;
public class PluginMessage extends BasicListener {
public static void send(ProxiedPlayer player, String legacyChannel, String channel, byte[] data) {
// 1.12 format change
player.sendData(player.getPendingConnection().getVersion() > 340 ? channel : legacyChannel, data);
send(player, player.getPendingConnection().getVersion() > 340 ? channel : legacyChannel, data);
}
public static void send(ProxiedPlayer player, String channel, byte[] data) {
if(((InitialHandler)player.getPendingConnection()).getRegisteredChannels().contains(channel))
player.sendData(channel, data);
}
//TODO change to warning level
@ -55,6 +60,8 @@ public class PluginMessage extends BasicListener {
private final Lunar lunar = new Lunar();
private final Set<String> knownBrands = new HashSet<>();
private final Set<String> knownChannels = new HashSet<>();
private final Map<String, Parser> handlers = new HashMap<>();
public PluginMessage() {
@ -63,8 +70,31 @@ public class PluginMessage extends BasicListener {
FabricModSender fabricModSender = new FabricModSender();
WorldDownloader wdl = new WorldDownloader();
register("REGISTER", false, directional(PASS_THROUGH, this::clientRegistersChannel));
register("minecraft:register", false, directional(PASS_THROUGH, this::clientRegistersChannel));
knownBrands.add("vanilla");
knownBrands.add("fabric");
knownBrands.add("forge"); //Forge registers all channels the server registers
knownChannels.add("fabric:container/open");
knownChannels.add("fabric:registry/sync/direct");
knownChannels.add("fabric-screen-handler-api-v1:open_screen");
knownChannels.add(FML.CHANNEL);
knownChannels.add("fml:loginwrapper");
knownChannels.add("fml:handshake");
knownChannels.add("fml:play");
knownChannels.add("forge:tier_sorting");
knownChannels.add("forge:split");
knownChannels.add("forge:login");
knownChannels.add("forge:handshake");
knownChannels.add("Replay|Restrict");
knownChannels.add("replaymod:restrict");
knownChannels.add("WDL|CONTROL");
knownChannels.add("wdl:control");
knownChannels.add("worldedit:cui");
register("REGISTER", false, directional(this::serverRegistersChannel, this::clientRegistersChannel));
register("minecraft:register", false, directional(this::serverRegistersChannel, this::clientRegistersChannel));
register("BungeeCord", false, onlySWSource(PASS_THROUGH));
register("bungeecord:main", false, onlySWSource(PASS_THROUGH));
@ -73,6 +103,7 @@ public class PluginMessage extends BasicListener {
register("sw:script_syntax", false, directional(onlySWSource(PASS_THROUGH), UNKNOWN));
register("sw:bridge", false, directional(onlySWSource(async(event -> NetworkPacket.handle(new ServerMetaInfo(((Server) event.getSender()).getInfo()), event.getData()))), UNKNOWN));
register("worldedit:cui", false, PASS_THROUGH);
register("fabricmodsender:mods", true, directional(UNKNOWN, fabricModSender::handlePluginMessage));
register("WDL|REQUEST", false, DROP);
@ -83,7 +114,7 @@ public class PluginMessage extends BasicListener {
register(ApolloManager.PLUGIN_MESSAGE_CHANNEL, true, async(lunar::handlePluginMessage));
register("LMC", true, directional(UNKNOWN, async(labyMod::handlePluginMessage)));
register("labymod3:main", true, directional(UNKNOWN, async(labyMod::handlePluginMessage)));
register(FML.CHANNEL, true, directional(UNKNOWN, fml::handlePluginMessage));
register(FML.CHANNEL, true, directional(UNKNOWN, async(fml::handlePluginMessage)));
//litematica/malilib https://github.com/maruohon/litematica/issues/75 https://github.com/maruohon/malilib/blob/liteloader_1.12.2/src/main/java/malilib/network/message/ConfigLockPacketHandler.java#L65
}
@ -114,7 +145,7 @@ public class PluginMessage extends BasicListener {
if(channel.equals(ApolloManager.PLUGIN_MESSAGE_CHANNEL))
lunar.sendRestrictions(player);
if(!handlers.containsKey(channel))
if(!knownChannels.contains(channel))
//TODO change to warning level
BungeeCore.get().getLogger().log(Level.INFO, () -> player.getName() + " registered unknown channel " + channel);
}
@ -122,15 +153,22 @@ public class PluginMessage extends BasicListener {
PASS_THROUGH.handle(event);
}
private void serverRegistersChannel(PluginMessageEvent event) {
ProxiedPlayer player = (ProxiedPlayer)event.getReceiver();
List<String> channels = new ArrayList<>(Arrays.asList(new String(event.getData()).split("\0")));
channels.removeIf(channel -> channel.equals("sw:bridge"));
player.sendData((player).getPendingConnection().getVersion() > 340 ? "minecraft:register" : "REGISTER", String.join("\0", channels).getBytes());
}
private void userBrand(PluginMessageEvent event) {
ProxiedPlayer player = (ProxiedPlayer) event.getSender();
String brand = new String(event.getData());
ByteBuf buf = Unpooled.wrappedBuffer(event.getData());
String brand = DefinedPacket.readString(buf);
if(brand.equals("vanilla")) {
// nothing to do
} else if(brand.startsWith("lunarclient:")) {
if(brand.startsWith("lunarclient:")) {
lunar.sendRestrictions(player);
} else {
} else if(!knownBrands.contains(brand)) {
//TODO change to warning level
BungeeCore.get().getLogger().log(Level.INFO, () -> player.getName() + " joined with unknown brand " + brand);
}

Datei anzeigen

@ -25,6 +25,7 @@ import de.steamwar.sql.Mod;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.connection.PendingConnection;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.event.PluginMessageEvent;
import net.md_5.bungee.api.event.PostLoginEvent;
@ -38,6 +39,10 @@ import java.util.concurrent.TimeUnit;
public class FML extends BasicListener {
// https://wiki.vg/Minecraft_Forge_Handshake#FML_protocol_.281.7_-_1.12.29
public static boolean isFML(PendingConnection connection, String type) {
return ((InitialHandler)connection).getExtraDataInHandshake().equals("\0" + type);
}
public static final String CHANNEL = "FML|HS";
private final byte[] helloPacket = new byte[]{
/* Packet type: ServerHello */ 0,
@ -58,9 +63,8 @@ public class FML extends BasicListener {
}
}
if(((InitialHandler)player.getPendingConnection()).getHandshake().getHost().endsWith("\0FML\0")) {
if(isFML(player.getPendingConnection(), "FML\0"))
player.sendData(CHANNEL, helloPacket);
}
}
public void handlePluginMessage(PluginMessageEvent event) {

Datei anzeigen

@ -21,6 +21,7 @@ package de.steamwar.bungeecore.mods;
import de.steamwar.bungeecore.BungeeCore;
import de.steamwar.bungeecore.listeners.BasicListener;
import de.steamwar.bungeecore.listeners.IPSanitizer;
import de.steamwar.sql.Mod;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
@ -29,67 +30,91 @@ import net.md_5.bungee.api.connection.PendingConnection;
import net.md_5.bungee.api.event.LoginEvent;
import net.md_5.bungee.connection.InitialHandler;
import net.md_5.bungee.event.EventHandler;
import net.md_5.bungee.netty.ChannelWrapper;
import net.md_5.bungee.netty.HandlerBoss;
import net.md_5.bungee.netty.PacketHandler;
import net.md_5.bungee.protocol.DefinedPacket;
import net.md_5.bungee.protocol.packet.LoginPayloadRequest;
import net.md_5.bungee.protocol.packet.LoginPayloadResponse;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.logging.Level;
public class FML2 extends BasicListener {
// https://wiki.vg/Minecraft_Forge_Handshake#FML2_protocol_.281.13_-_Current.29
// FML2: https://wiki.vg/Minecraft_Forge_Handshake#FML2_protocol_.281.13_-_Current.29
// FML3: https://github.com/adde0109/Ambassador/tree/non-api/src/main/java/org/adde0109/ambassador/forge
public static boolean isFML2(PendingConnection connection) {
return ((InitialHandler)connection).getHandshake().getHost().endsWith("\0FML2\0");
}
// FORGE: https://github.com/MinecraftForge/MinecraftForge/blob/1.20.x/src/main/java/net/minecraftforge/network/NetworkInitialization.java
// FORGE: https://github.com/MinecraftForge/MinecraftForge/blob/1.20.x/src/main/java/net/minecraftforge/network/ForgePacketHandler.java
// FORGE: https://github.com/MinecraftForge/MinecraftForge/blob/1.20.x/src/main/java/net/minecraftforge/network/packets/ModVersions.java
private final Field initialHandlerCh;
private final byte[] fml2ModListPacket;
private final byte[] fml3ModListPacket;
private final byte[] forgeModListPacket;
public FML2() {
try {
initialHandlerCh = InitialHandler.class.getDeclaredField("ch");
} catch (NoSuchFieldException e) {
throw new SecurityException("Could not initialize Reflection", e);
}
initialHandlerCh.setAccessible(true);
}
fml2ModListPacket = generateModListPacket(false);
fml3ModListPacket = generateModListPacket(true);
//TODO current identifier is \0FORGE and fmlNetworkVersion 0 ?
//TODO links for current implementation (if current checker not working)
//https://github.com/MinecraftForge/MinecraftForge/blob/1.20.x/src/main/java/net/minecraftforge/network/ServerStatusPing.java
//https://github.com/MinecraftForge/MinecraftForge/blob/1.20.x/src/main/java/net/minecraftforge/network/NetworkContext.java#L26
//https://github.com/MinecraftForge/MinecraftForge/blob/1.20.x/src/main/java/net/minecraftforge/network/tasks/ForgeNetworkConfigurationHandler.java#L20
//https://github.com/MinecraftForge/MinecraftForge/blob/1.20.x/src/main/java/net/minecraftforge/network/NetworkRegistry.java#L177
ByteBuf packet = Unpooled.buffer();
packet.writeByte(1); // Mod list packet
DefinedPacket.writeVarInt(0, packet); // Mod amount
ByteBuf wrapper = Unpooled.buffer();
wrapper.writeByte(0); // Login wrapper packet
DefinedPacket.writeString("forge:handshake", wrapper);
DefinedPacket.writeVarInt(packet.readableBytes(), wrapper);
wrapper.writeBytes(packet);
forgeModListPacket = new byte[wrapper.readableBytes()];
wrapper.readBytes(forgeModListPacket);
}
@EventHandler
public void onLogin(LoginEvent event) {
PendingConnection connection = event.getConnection();
if(!isFML2(connection))
boolean fml2 = FML.isFML(connection, "FML2\0");
boolean fml3 = FML.isFML(connection, "FML3\0");
boolean forge = FML.isFML(connection, "FORGE");
if(!fml2 && !fml3 && !forge)
return;
try {
((ChannelWrapper) initialHandlerCh.get(connection)).getHandle().pipeline().get(HandlerBoss.class).setHandler(new FML2LoginHandler(event));
} catch (IllegalAccessException e) {
BungeeCore.get().getLogger().log(Level.SEVERE, "Could not get Channel", e);
return;
}
IPSanitizer.getChannelWrapper((InitialHandler) connection).getHandle().pipeline().get(HandlerBoss.class).setHandler(new FML2LoginHandler(event));
event.registerIntent(BungeeCore.get());
connection.unsafe().sendPacket(new LoginPayloadRequest(1, "fml:loginwrapper", new byte[] {
/* fml:handshake */ 13,102,109,108,58,104,97,110,100,115,104,97,107,101,
/* Packet length */ 4,
/* Type mod list */ 1,
/* Mod count */ 0,
/* Channel count */ 0,
/* Registry count */ 0
}));
if(forge)
connection.unsafe().sendPacket(new LoginPayloadRequest(1, "forge:login", forgeModListPacket));
else
connection.unsafe().sendPacket(new LoginPayloadRequest(1, "fml:loginwrapper", fml3 ? fml3ModListPacket : fml2ModListPacket));
}
private byte[] generateModListPacket(boolean fml3) {
ByteBuf packet = Unpooled.buffer();
packet.writeByte(1); // Mod list packet
DefinedPacket.writeVarInt(0, packet); // Mod amount
if(fml3) {
DefinedPacket.writeVarInt(1, packet); // Channel amount
DefinedPacket.writeString("forge:tier_sorting", packet);
DefinedPacket.writeString("1.0", packet);
} else {
DefinedPacket.writeVarInt(0, packet); // Channel amount
}
DefinedPacket.writeVarInt(0, packet); // Registries amount
if(fml3)
DefinedPacket.writeVarInt(0, packet); // DataPacks amount
ByteBuf wrapper = Unpooled.buffer();
DefinedPacket.writeString("fml:handshake", wrapper);
DefinedPacket.writeVarInt(packet.readableBytes(), wrapper);
wrapper.writeBytes(packet);
byte[] data = new byte[wrapper.readableBytes()];
wrapper.readBytes(data);
return data;
}
private static class FML2LoginHandler extends PacketHandler {
@ -101,40 +126,51 @@ public class FML2 extends BasicListener {
@Override
public String toString() {
return "SteamWar FML2 Handler";
return "SteamWar Forge Handler";
}
@Override
public void handle(LoginPayloadResponse response) {
boolean forge = FML.isFML(event.getConnection(), "FORGE");
byte[] data = response.getData();
if(data == null) {
abort(response, "Not FML2 client");
abort(response, "Not FML2/3 client");
return;
}
ByteBuf buf = Unpooled.wrappedBuffer(data);
if(!DefinedPacket.readString(buf).equals("fml:handshake")) {
abort(response, "Not FML2 handshake response");
if(forge && buf.readByte() != 0) {
abort(response, "Not FORGE login wrapper");
return;
}
int packetLength = DefinedPacket.readVarInt(buf);
if(packetLength != buf.readableBytes()) {
abort(response, "FML2 packet size mismatch");
if(!DefinedPacket.readString(buf).equals(forge ? "forge:handshake" : "fml:handshake")) {
abort(response, "Not FML2/3/FORGE handshake response");
return;
}
if(DefinedPacket.readVarInt(buf) != /* Mod List Reply */ 2) {
abort(response, "FML2 no mod list reply");
if(DefinedPacket.readVarInt(buf) != buf.readableBytes()) {
abort(response, "FML2/3/FORGE packet size mismatch");
return;
}
if(DefinedPacket.readVarInt(buf) != (forge ? /* Mod Versions */ 1 : /* Mod List Reply */ 2)) {
abort(response, "Not FML2/3/FORGE mod list reply");
return;
}
List<Mod> mods = new ArrayList<>();
int modCount = DefinedPacket.readVarInt(buf);
for(int i = 0; i < modCount; i++)
for(int i = 0; i < modCount; i++) {
mods.add(Mod.getOrCreate(DefinedPacket.readString(buf), Mod.Platform.FORGE));
if(forge) {
DefinedPacket.readString(buf); // Human readable name
DefinedPacket.readString(buf); // Version
}
}
if(!ModUtils.handleMods(event.getConnection().getUniqueId(), Locale.getDefault(), event::setReason, mods))
event.setCancelled(true);

Datei anzeigen

@ -0,0 +1,58 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2024 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.bungeecore.mods;
import de.steamwar.bungeecore.BungeeCore;
import de.steamwar.bungeecore.listeners.BasicListener;
import net.md_5.bungee.api.event.PlayerHandshakeEvent;
import net.md_5.bungee.connection.InitialHandler;
import net.md_5.bungee.event.EventHandler;
import java.util.HashSet;
import java.util.Set;
import java.util.logging.Level;
public class Hostname extends BasicListener {
private final Set<String> knownHostnames = new HashSet<>();
private final Set<String> knownExtraData = new HashSet<>();
public Hostname() {
knownHostnames.add("steamwar.de");
knownHostnames.add("78.31.71.136");
knownExtraData.add("");
knownExtraData.add("\0FML\0");
knownExtraData.add("\0FML2\0");
knownExtraData.add("\0FML3\0");
knownExtraData.add("\0FORGE");
}
@EventHandler
public void onHandshake(PlayerHandshakeEvent event) {
String extraDataInHandshake = ((InitialHandler) event.getConnection()).getExtraDataInHandshake();
if (!knownHostnames.contains(event.getHandshake().getHost().toLowerCase())) {
BungeeCore.get().getLogger().log(Level.WARNING, () -> event.getConnection().getSocketAddress() + " connected with unknown hostname " + event.getHandshake() + " " + extraDataInHandshake);
} else if (!knownExtraData.contains(extraDataInHandshake)) {
BungeeCore.get().getLogger().log(Level.WARNING, () -> event.getConnection().getSocketAddress() + " connected with unknown extra data " + event.getHandshake() + " " + extraDataInHandshake);
}
}
}

Datei anzeigen

@ -20,9 +20,13 @@
package de.steamwar.bungeecore.mods;
import de.steamwar.bungeecore.Bauserver;
import de.steamwar.bungeecore.Builderserver;
import de.steamwar.bungeecore.BungeeCore;
import de.steamwar.bungeecore.Subserver;
import de.steamwar.bungeecore.listeners.BasicListener;
import de.steamwar.bungeecore.listeners.PluginMessage;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.config.ServerInfo;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.event.ServerSwitchEvent;
import net.md_5.bungee.event.EventHandler;
@ -34,38 +38,36 @@ import java.util.Arrays;
public class ReplayMod extends BasicListener {
// https://gist.github.com/Johni0702/2547c463e51f65f312cb
// https://github.com/ReplayMod/replay-restrictions/blob/master/bungeecord/src/main/java/de/johni0702/replay/restrictions/BungeeCordPlugin.java
// https://github.com/ReplayMod/ReplayMod/blob/stable/src/main/java/com/replaymod/core/utils/Restrictions.java
private final byte[] restrict;
private final byte[] unlock;
public ReplayMod() {
restrict = generatePacket(true);
unlock = generatePacket(false);
}
@EventHandler
public void onPlayerJoin(ServerSwitchEvent event) {
ProxiedPlayer player = event.getPlayer();
Subserver server = Subserver.getSubserver(player.getServer().getInfo());
PluginMessage.send(
player, "Replay|Restrict", "replaymod:restrict",
(server instanceof Bauserver && ((Bauserver) server).getOwner().equals(player.getUniqueId())) ? unlock : restrict
);
}
private byte[] generatePacket(boolean restrict) {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
try {
for(String restriction : Arrays.asList("no_xray", "no_noclip", "only_first_person", "only_recording_player")) {
byte[] bytes = restriction.getBytes();
stream.write(bytes.length);
stream.write(bytes);
stream.write(restrict ? 1 : 0);
stream.write(1); // restrict
}
} catch (IOException e) {
throw new SecurityException(e);
}
return stream.toByteArray();
restrict = stream.toByteArray();
}
@EventHandler
public void onPlayerJoin(ServerSwitchEvent event) {
ProxiedPlayer player = event.getPlayer();
ServerInfo server = player.getServer().getInfo();
if(ProxyServer.getInstance().getServerInfo(BungeeCore.LOBBY_SERVER) == server)
return;
Subserver subserver = Subserver.getSubserver(server);
if(subserver instanceof Builderserver || (subserver instanceof Bauserver && ((Bauserver) subserver).getOwner().equals(player.getUniqueId())))
return;
PluginMessage.send(player, "Replay|Restrict", "replaymod:restrict", restrict);
}
}

Datei anzeigen

@ -21,31 +21,17 @@ package de.steamwar.bungeecore.mods;
import com.google.gson.*;
import de.steamwar.bungeecore.listeners.BasicListener;
import lombok.AllArgsConstructor;
import net.md_5.bungee.BungeeCord;
import net.md_5.bungee.api.ServerPing;
import net.md_5.bungee.api.connection.PendingConnection;
import net.md_5.bungee.api.event.ProxyPingEvent;
import net.md_5.bungee.event.EventHandler;
import net.md_5.bungee.event.EventPriority;
import net.md_5.bungee.protocol.ProtocolConstants;
import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class ServerListPing extends BasicListener implements JsonSerializer<ServerPing>, JsonDeserializer<ServerPing> {
// https://github.com/Aizistral-Studios/No-Chat-Reports/discussions/206
// https://github.com/Aizistral-Studios/No-Chat-Reports/wiki/How-to-Get-Safe-Server-Status
// https://wiki.vg/Minecraft_Forge_Handshake#Changes_to_Server_List_Ping_2
private final Gson gson;
private final Map<Integer, String> versionMap = new HashMap<>();
private final FML2Channel[] fml2Channels = new FML2Channel[]{new FML2Channel("minecraft:register"), new FML2Channel("minecraft:unregister"), new FML2Channel("fml:handshake")};
private final Map<ServerPing, PendingConnection> connections = new ConcurrentHashMap<>();
public ServerListPing() {
BungeeCord bungeeCord = BungeeCord.getInstance();
@ -55,25 +41,11 @@ public class ServerListPing extends BasicListener implements JsonSerializer<Serv
Field gsonField = BungeeCord.class.getDeclaredField("gson");
gsonField.setAccessible(true);
gsonField.set(bungeeCord, gson.newBuilder().registerTypeAdapter(ServerPing.class, this).create());
for(Field version : ProtocolConstants.class.getFields()) {
String name = version.getName();
if(version.getType() != int.class || !name.startsWith("MINECRAFT_"))
continue;
versionMap.put(version.getInt(null), name.substring(10).replace('_', '.'));
}
} catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
throw new SecurityException("Failed to inject ServerListPing", e);
}
}
@EventHandler(priority = EventPriority.HIGHEST)
public void onPing(ProxyPingEvent event) {
connections.put(event.getResponse(), event.getConnection());
connections.values().removeIf(connection -> !connection.isConnected()); // Prevent long term leaks
}
@Override
public JsonElement serialize(ServerPing ping, Type type, JsonSerializationContext context) {
JsonElement element = gson.toJsonTree(ping, type);
@ -81,14 +53,6 @@ public class ServerListPing extends BasicListener implements JsonSerializer<Serv
JsonObject object = element.getAsJsonObject();
object.addProperty("preventsChatReports", true);
PendingConnection connection = connections.remove(ping);
if(FML2.isFML2(connection)) {
object.add("forgeData", gson.toJsonTree(new FML2Data(fml2Channels, new FML2Mod[]{
new FML2Mod("forge", "ANY"),
new FML2Mod("minecraft", versionMap.get(connection.getVersion()))
}), FML2Data.class));
}
return element;
}
@ -96,24 +60,4 @@ public class ServerListPing extends BasicListener implements JsonSerializer<Serv
public ServerPing deserialize(JsonElement element, Type type, JsonDeserializationContext context) throws JsonParseException {
return gson.fromJson(element, ServerPing.class);
}
@AllArgsConstructor
private static class FML2Data {
private final int fmlNetworkVersion = 2;
private final FML2Channel[] channels;
private final FML2Mod[] mods;
}
@AllArgsConstructor
private static class FML2Channel {
private final String res;
private final String version = "FML2";
private final boolean required = true;
}
@AllArgsConstructor
private static class FML2Mod {
private final String modId;
private final String modmarker;
}
}

Datei anzeigen

@ -28,6 +28,9 @@ import java.io.DataOutputStream;
import java.io.IOException;
public class WorldDownloader {
// https://wiki.vg/Plugin_channels/World_downloader
// https://github.com/Pokechu22/WorldDownloader-Serverside-Companion
// https://github.com/Pokechu22/WorldDownloader
private final byte[] controlPacket;