diff --git a/src/main/java/me/libraryaddict/disguise/commands/libsdisguises/LDMods.java b/src/main/java/me/libraryaddict/disguise/commands/libsdisguises/LDMods.java index ace58130..f24b02f8 100644 --- a/src/main/java/me/libraryaddict/disguise/commands/libsdisguises/LDMods.java +++ b/src/main/java/me/libraryaddict/disguise/commands/libsdisguises/LDMods.java @@ -1,6 +1,6 @@ package me.libraryaddict.disguise.commands.libsdisguises; -import me.libraryaddict.disguise.LibsDisguises; +import me.libraryaddict.disguise.utilities.modded.ModdedManager; import me.libraryaddict.disguise.utilities.translations.LibsMsg; import org.apache.commons.lang.StringUtils; import org.bukkit.Bukkit; @@ -26,7 +26,7 @@ public class LDMods implements LDCommand { @Override public void onCommand(CommandSender sender, String[] args) { - if (!Bukkit.getMessenger().isOutgoingChannelRegistered(LibsDisguises.getInstance(), "fml:handshake")) { + if (ModdedManager.getEntities().isEmpty()) { sender.sendMessage(LibsMsg.NO_MODS_LISTENING.get()); return; } diff --git a/src/main/java/me/libraryaddict/disguise/utilities/listeners/DisguiseListener.java b/src/main/java/me/libraryaddict/disguise/utilities/listeners/DisguiseListener.java index 821c2e96..7d838241 100644 --- a/src/main/java/me/libraryaddict/disguise/utilities/listeners/DisguiseListener.java +++ b/src/main/java/me/libraryaddict/disguise/utilities/listeners/DisguiseListener.java @@ -316,19 +316,6 @@ public class DisguiseListener implements Listener { } } - @EventHandler - public void onLogin(PlayerRegisterChannelEvent event) { - Player player = event.getPlayer(); - - // If it's not a forge handshake, or we didn't register it - if (!event.getChannel().equals("fml:handshake") || - !Bukkit.getMessenger().isOutgoingChannelRegistered(LibsDisguises.getInstance(), "fml:handshake")) { - return; - } - - player.sendPluginMessage(LibsDisguises.getInstance(), "fml:handshake", ModdedManager.getFmlHandshake()); - } - @EventHandler public void onJoin(PlayerJoinEvent event) { Player p = event.getPlayer(); diff --git a/src/main/java/me/libraryaddict/disguise/utilities/listeners/ModdedListener.java b/src/main/java/me/libraryaddict/disguise/utilities/listeners/ModdedListener.java new file mode 100644 index 00000000..982073e7 --- /dev/null +++ b/src/main/java/me/libraryaddict/disguise/utilities/listeners/ModdedListener.java @@ -0,0 +1,44 @@ +package me.libraryaddict.disguise.utilities.listeners; + +import me.libraryaddict.disguise.LibsDisguises; +import me.libraryaddict.disguise.utilities.modded.ModdedEntity; +import me.libraryaddict.disguise.utilities.modded.ModdedManager; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerLoginEvent; +import org.bukkit.metadata.FixedMetadataValue; + +import java.util.ArrayList; + +/** + * Created by libraryaddict on 11/06/2020. + */ +public class ModdedListener implements Listener { + @EventHandler(priority = EventPriority.LOWEST) + public void onLogin(PlayerLoginEvent event) { + Player player = event.getPlayer(); + + ArrayList mods = ModdedManager.getForgeMods().getIfPresent(player.getName()); + + player.setMetadata("forge_mods", new FixedMetadataValue(LibsDisguises.getInstance(), mods)); + + for (ModdedEntity e : ModdedManager.getEntities().values()) { + if (e.getMod() == null) { + continue; + } + + if (mods.contains(e.getMod().toLowerCase())) { + continue; + } + + if (e.getRequired() == null) { + continue; + } + + player.kickPlayer(e.getRequired()); + break; + } + } +} diff --git a/src/main/java/me/libraryaddict/disguise/utilities/modded/ModdedManager.java b/src/main/java/me/libraryaddict/disguise/utilities/modded/ModdedManager.java index d6703dcc..620780ec 100644 --- a/src/main/java/me/libraryaddict/disguise/utilities/modded/ModdedManager.java +++ b/src/main/java/me/libraryaddict/disguise/utilities/modded/ModdedManager.java @@ -1,43 +1,51 @@ package me.libraryaddict.disguise.utilities.modded; +import com.comphenix.protocol.ProtocolLibrary; import com.comphenix.protocol.utility.StreamSerializer; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; import lombok.Getter; import me.libraryaddict.disguise.LibsDisguises; import me.libraryaddict.disguise.disguisetypes.DisguiseType; +import me.libraryaddict.disguise.utilities.listeners.ModdedListener; +import me.libraryaddict.disguise.utilities.packets.packetlisteners.PacketListenerModdedClient; import me.libraryaddict.disguise.utilities.parser.DisguisePerm; import me.libraryaddict.disguise.utilities.reflection.ReflectionManager; import org.bukkit.Bukkit; import org.bukkit.NamespacedKey; -import org.bukkit.entity.Player; -import org.bukkit.metadata.FixedMetadataValue; -import org.bukkit.plugin.messaging.PluginMessageListener; -import java.io.*; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.TimeUnit; /** * Created by libraryaddict on 14/04/2020. */ -public class ModdedManager implements PluginMessageListener { +public class ModdedManager { @Getter private static final HashMap entities = new HashMap<>(); @Getter private static byte[] fmlHandshake; + @Getter + private static byte[] fmlRegistries; + @Getter + private static final Cache> forgeMods = CacheBuilder.newBuilder() + .expireAfterWrite(1, TimeUnit.MINUTES).build(); public ModdedManager(ArrayList channels) { if (getEntities().isEmpty()) { return; } - if (getEntities().values().stream().noneMatch(e -> e.getMod() != null)) { - return; + if (fmlRegistries == null) { + ProtocolLibrary.getProtocolManager().addPacketListener(new PacketListenerModdedClient()); + Bukkit.getPluginManager().registerEvents(new ModdedListener(), LibsDisguises.getInstance()); } - Bukkit.getMessenger().registerOutgoingPluginChannel(LibsDisguises.getInstance(), "fml:handshake"); - Bukkit.getMessenger().registerIncomingPluginChannel(LibsDisguises.getInstance(), "fml:handshake", this); - ByteArrayOutputStream stream = new ByteArrayOutputStream(); DataOutputStream output = new DataOutputStream(stream); @@ -57,14 +65,58 @@ public class ModdedManager implements PluginMessageListener { s.serializeString(output, channel.substring(channel.indexOf("|") + 1)); } - // We have no resources to declare + // We want to declare some entities s.serializeVarInt(output, 0); + + // Only this one thx + // s.serializeString(output, "minecraft:entity_type"); } catch (IOException e) { e.printStackTrace(); } fmlHandshake = stream.toByteArray(); + + stream = new ByteArrayOutputStream(); + output = new DataOutputStream(stream); + + s = StreamSerializer.getDefault(); + + try { + // Packet id 3 + s.serializeVarInt(output, 3); + + // What registry we're modifying + s.serializeString(output, "minecraft:entity_type"); + // Yes... We're doing custom data + s.serializeVarInt(output, 1); + + // We have this many entities + s.serializeVarInt(output, entities.size()); + + // Write the entity names and ids + for (Map.Entry entry : entities.entrySet()) { + s.serializeString(output, entry.getKey().toString()); + s.serializeVarInt(output, entry.getValue().getTypeId()); + } + + // Sir, we do not want to declare aliases + s.serializeVarInt(output, 0); + + // Or overrides + s.serializeVarInt(output, 0); + + // No.. Not even blocked + s.serializeVarInt(output, 0); + + // Or dummied + s.serializeVarInt(output, 0); + } + catch (IOException e) { + e.printStackTrace(); + } + + fmlRegistries = stream.toByteArray(); } public static void registerModdedEntity(NamespacedKey name, ModdedEntity entity, boolean register) { @@ -76,20 +128,22 @@ public class ModdedManager implements PluginMessageListener { throw new IllegalArgumentException("Modded entity " + entity.getName() + " has already been registered"); } + Object entityType; + if (register) { - Object entityType = ReflectionManager.registerEntityType(name); + entityType = ReflectionManager.registerEntityType(name); int entityId = ReflectionManager.getEntityTypeId(entityType); entity.setTypeId(entityId); - entity.setEntityType(entityType); } else { - Object entityType = ReflectionManager.getEntityType(name); + entityType = ReflectionManager.getEntityType(name); int entityId = ReflectionManager.getEntityTypeId(entityType); entity.setTypeId(entityId); - entity.setEntityType(entityType); } + entity.setEntityType(entityType); + entities.put(name, entity); } @@ -120,54 +174,4 @@ public class ModdedManager implements PluginMessageListener { return perms; } - - @Override - public void onPluginMessageReceived(String channel, Player player, byte[] bytes) { - if (player.hasMetadata("forge_mods")) { - return; - } - - DataInputStream stream = new DataInputStream(new ByteArrayInputStream(bytes)); - - try { - StreamSerializer s = StreamSerializer.getDefault(); - int packetId = s.deserializeVarInt(stream); - - if (packetId != 2) { - return; - } - - int count = s.deserializeVarInt(stream); - - ArrayList mods = new ArrayList<>(); - - for (int i = 0; i < count; i++) { - mods.add(s.deserializeString(stream, 256)); - } - - player.setMetadata("forge_mods", new FixedMetadataValue(LibsDisguises.getInstance(), mods)); - - for (ModdedEntity e : getEntities().values()) { - if (e.getMod() == null) { - continue; - } - - if (mods.contains(e.getMod().toLowerCase())) { - continue; - } - - // TODO Idk, something because they don't have a mod? - - if (e.getRequired() == null) { - continue; - } - - player.kickPlayer(e.getRequired()); - break; - } - } - catch (IOException e) { - e.printStackTrace(); - } - } } diff --git a/src/main/java/me/libraryaddict/disguise/utilities/packets/packetlisteners/PacketListenerModdedClient.java b/src/main/java/me/libraryaddict/disguise/utilities/packets/packetlisteners/PacketListenerModdedClient.java new file mode 100644 index 00000000..70db4a97 --- /dev/null +++ b/src/main/java/me/libraryaddict/disguise/utilities/packets/packetlisteners/PacketListenerModdedClient.java @@ -0,0 +1,161 @@ +package me.libraryaddict.disguise.utilities.packets.packetlisteners; + +import com.comphenix.protocol.PacketType; +import com.comphenix.protocol.ProtocolLibrary; +import com.comphenix.protocol.events.PacketAdapter; +import com.comphenix.protocol.events.PacketContainer; +import com.comphenix.protocol.events.PacketEvent; +import com.comphenix.protocol.wrappers.MinecraftKey; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.mojang.authlib.GameProfile; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.handler.codec.DecoderException; +import me.libraryaddict.disguise.LibsDisguises; +import me.libraryaddict.disguise.utilities.modded.ModdedManager; +import net.minecraft.server.v1_15_R1.PacketDataSerializer; +import org.bukkit.entity.Player; + +import java.lang.reflect.InvocationTargetException; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.concurrent.TimeUnit; + +/** + * Created by libraryaddict on 11/06/2020. + */ +public class PacketListenerModdedClient extends PacketAdapter { + private final Cache loginAttempts = CacheBuilder.newBuilder().expireAfterWrite(15, TimeUnit.SECONDS) + .build(); + private final int packetId1 = 5555554, packetId2 = 5555555; + + public PacketListenerModdedClient() { + super(LibsDisguises.getInstance(), PacketType.Login.Client.START, PacketType.Login.Client.CUSTOM_PAYLOAD); + } + + private int getInt(ByteBuf buf) { + int i = 0; + int j = 0; + + byte b0; + do { + b0 = buf.readByte(); + i |= (b0 & 127) << j++ * 7; + if (j > 5) { + throw new RuntimeException("VarInt too big"); + } + } while ((b0 & 128) == 128); + + return i; + } + + private void handleModlist(Player player, String name, byte[] data) { + ByteBuf buf = Unpooled.copiedBuffer(data); + + int packetId = getInt(buf); + + if (packetId != 2) { + return; + } + + int count = getInt(buf); + ArrayList mods = new ArrayList<>(); + + for (int i = 0; i < count; i++) { + mods.add(getString(buf)); + } + + ModdedManager.getForgeMods().put(name, mods); + } + + public String getString(ByteBuf buf) { + int j = getInt(buf); + int size = 256; + + if (j > size * 4) { + throw new DecoderException( + "The received encoded string buffer length is longer than maximum allowed (" + j + " > " + + size * 4 + ")"); + } else if (j < 0) { + throw new DecoderException("The received encoded string buffer length is less than zero! Weird string!"); + } else { + String s = buf.toString(buf.readerIndex(), j, StandardCharsets.UTF_8); + buf.readerIndex(buf.readerIndex() + j); + if (s.length() > size) { + throw new DecoderException( + "The received string length is longer than maximum allowed (" + j + " > " + size + ")"); + } else { + return s; + } + } + } + + private void handleDataReceived(Player player, String name) { + // Continue + PacketContainer packet = new PacketContainer(PacketType.Login.Client.START); + packet.getModifier().write(0, new GameProfile(null, name)); + + try { + ProtocolLibrary.getProtocolManager().recieveClientPacket(player, packet, false); + } + catch (IllegalAccessException | InvocationTargetException e) { + e.printStackTrace(); + } + } + + @Override + public void onPacketReceiving(PacketEvent event) { + event.setCancelled(true); + + if (event.getPacketType() != PacketType.Login.Client.START) { + String address = event.getPlayer().getAddress().toString(); + + String name = loginAttempts.getIfPresent(address); + + if (name == null) { + return; + } + + if (event.getPacket().getIntegers().read(0) == packetId2) { + loginAttempts.invalidate(address); + handleDataReceived(event.getPlayer(), name); + return; + } else if (event.getPacket().getIntegers().read(0) == packetId1) { + ByteBuf buf = (ByteBuf) event.getPacket().getModifier().read(1); + + if (buf != null) { + byte[] bytes = new byte[buf.readableBytes()]; + buf.readBytes(bytes); + + handleModlist(event.getPlayer(), name, bytes); + } + } + + return; + } + + loginAttempts + .put(event.getPlayer().getAddress().toString(), event.getPacket().getGameProfiles().read(0).getName()); + + PacketContainer packet1 = new PacketContainer(PacketType.Login.Server.CUSTOM_PAYLOAD); + packet1.getIntegers().write(0, packetId1); + packet1.getMinecraftKeys().write(0, new com.comphenix.protocol.wrappers.MinecraftKey("fml", "handshake")); + packet1.getModifier() + .write(2, new PacketDataSerializer(Unpooled.wrappedBuffer(ModdedManager.getFmlHandshake()))); + + PacketContainer packet2 = new PacketContainer(PacketType.Login.Server.CUSTOM_PAYLOAD); + packet2.getIntegers().write(0, packetId2); + packet2.getMinecraftKeys().write(0, new MinecraftKey("fml", "handshake")); + packet2.getModifier() + .write(2, new PacketDataSerializer(Unpooled.wrappedBuffer(ModdedManager.getFmlRegistries()))); + + try { + ProtocolLibrary.getProtocolManager().sendServerPacket(event.getPlayer(), packet1); + ProtocolLibrary.getProtocolManager().sendServerPacket(event.getPlayer(), packet2); + } + catch (InvocationTargetException e) { + e.printStackTrace(); + } + } +} diff --git a/src/main/java/me/libraryaddict/disguise/utilities/params/ParamInfoTypes.java b/src/main/java/me/libraryaddict/disguise/utilities/params/ParamInfoTypes.java index e07feb52..397f297c 100644 --- a/src/main/java/me/libraryaddict/disguise/utilities/params/ParamInfoTypes.java +++ b/src/main/java/me/libraryaddict/disguise/utilities/params/ParamInfoTypes.java @@ -10,7 +10,6 @@ import me.libraryaddict.disguise.utilities.params.types.ParamInfoEnum; import me.libraryaddict.disguise.utilities.params.types.base.*; import me.libraryaddict.disguise.utilities.params.types.custom.*; import me.libraryaddict.disguise.utilities.reflection.NmsVersion; -import me.libraryaddict.disguise.utilities.sounds.SoundGroup; import org.apache.commons.lang.StringUtils; import org.bukkit.*; import org.bukkit.block.BlockFace; diff --git a/src/main/java/me/libraryaddict/disguise/utilities/params/types/custom/ParamInfoSoundGroup.java b/src/main/java/me/libraryaddict/disguise/utilities/params/types/custom/ParamInfoSoundGroup.java index 424cf429..582bba58 100644 --- a/src/main/java/me/libraryaddict/disguise/utilities/params/types/custom/ParamInfoSoundGroup.java +++ b/src/main/java/me/libraryaddict/disguise/utilities/params/types/custom/ParamInfoSoundGroup.java @@ -6,7 +6,6 @@ import me.libraryaddict.disguise.utilities.sounds.SoundGroup; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashMap; -import java.util.Map; /** * Created by libraryaddict on 23/05/2020. diff --git a/src/main/java/me/libraryaddict/disguise/utilities/sounds/SoundGroup.java b/src/main/java/me/libraryaddict/disguise/utilities/sounds/SoundGroup.java index 45004b8e..25be8149 100644 --- a/src/main/java/me/libraryaddict/disguise/utilities/sounds/SoundGroup.java +++ b/src/main/java/me/libraryaddict/disguise/utilities/sounds/SoundGroup.java @@ -7,10 +7,8 @@ import me.libraryaddict.disguise.utilities.reflection.ReflectionManager; import org.apache.commons.lang.math.RandomUtils; import org.bukkit.Sound; -import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; -import java.util.Random; /** * Created by libraryaddict on 23/05/2020. diff --git a/src/main/java/me/libraryaddict/disguise/utilities/sounds/SoundManager.java b/src/main/java/me/libraryaddict/disguise/utilities/sounds/SoundManager.java index d926c0e1..eccecfcd 100644 --- a/src/main/java/me/libraryaddict/disguise/utilities/sounds/SoundManager.java +++ b/src/main/java/me/libraryaddict/disguise/utilities/sounds/SoundManager.java @@ -3,7 +3,6 @@ package me.libraryaddict.disguise.utilities.sounds; import me.libraryaddict.disguise.LibsDisguises; import me.libraryaddict.disguise.utilities.DisguiseUtilities; import me.libraryaddict.disguise.utilities.params.ParamInfoManager; -import me.libraryaddict.disguise.utilities.params.ParamInfoTypes; import org.bukkit.Sound; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.file.YamlConfiguration; diff --git a/src/main/resources/disguises.yml b/src/main/resources/disguises.yml index 9f3ca5b6..42a63c80 100644 --- a/src/main/resources/disguises.yml +++ b/src/main/resources/disguises.yml @@ -55,4 +55,5 @@ Custom-Entities: # Mod: LibAttacks # The mod they need installed # If exists, will prevent anyone without the mod from joining with this error # Required: 'Install LibAttacks! Download it from our site!' -# Channels: librarian:channel|1 # Sometimes a mod needs a channel enabled.. Seperate each channel with a comma \ No newline at end of file +# Channels: librarian:channel|1 # Sometimes a mod needs a channel enabled.. Seperate each channel with a comma. +# The channels also want a protocol version, which is normally 1 or 1.0 \ No newline at end of file