diff --git a/blcmodapibukkit-1.17/pom.xml b/blcmodapibukkit-1.17/pom.xml new file mode 100644 index 0000000..f203977 --- /dev/null +++ b/blcmodapibukkit-1.17/pom.xml @@ -0,0 +1,81 @@ + + + + badlionclientmodapi + net.badlion + 1.2.3 + + 4.0.0 + + blcmodapibukkit-1.17 + + + UTF-8 + + + + clean install + badlionclientmodapibukkit-1.17 + src/main/java + + + . + true + src/main/resources/ + + *.yml + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.7 + 1.7 + + + + org.apache.maven.plugins + maven-shade-plugin + 3.1.0 + + + package + + shade + + + + + + + + + + org.spigotmc + spigot-api + 1.17-R0.1-SNAPSHOT + provided + + + com.google.code.gson + gson + 2.6.2 + compile + + + + + + spigot-repo + https://hub.spigotmc.org/nexus/content/repositories/snapshots/ + + + + + \ No newline at end of file diff --git a/blcmodapibukkit-1.17/src/main/java/net/badlion/blcmodapibukkit/BlcModApiBukkit.java b/blcmodapibukkit-1.17/src/main/java/net/badlion/blcmodapibukkit/BlcModApiBukkit.java new file mode 100644 index 0000000..ab3b7f2 --- /dev/null +++ b/blcmodapibukkit-1.17/src/main/java/net/badlion/blcmodapibukkit/BlcModApiBukkit.java @@ -0,0 +1,76 @@ +package net.badlion.blcmodapibukkit; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import net.badlion.blcmodapibukkit.listener.PlayerListener; +import org.bukkit.plugin.java.JavaPlugin; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.Reader; +import java.util.logging.Level; + +public class BlcModApiBukkit extends JavaPlugin { + + public static final Gson GSON_NON_PRETTY = new GsonBuilder().enableComplexMapKeySerialization().disableHtmlEscaping().create(); + public static final Gson GSON_PRETTY = new GsonBuilder().enableComplexMapKeySerialization().disableHtmlEscaping().setPrettyPrinting().create(); + + private Conf conf; + + @Override + public void onEnable() { + if (!this.getDataFolder().exists()) { + if (!this.getDataFolder().mkdir()) { + this.getLogger().log(Level.SEVERE, "Failed to create plugin directory."); + } + } + + try { + this.conf = this.loadConf(new File(this.getDataFolder(), "config.json")); + + // Register channel + this.getServer().getMessenger().registerOutgoingPluginChannel(this, "badlion:mods"); + + // Only register the listener if the config loads successfully + this.getServer().getPluginManager().registerEvents(new PlayerListener(this), this); + + this.getLogger().log(Level.INFO, "Successfully setup BadlionClientModAPI plugin."); + } catch (IOException e) { + this.getLogger().log(Level.SEVERE, "Error with config for BadlionClientModAPI plugin."); + e.printStackTrace(); + } + } + + @Override + public void onDisable() { + + } + + public Conf loadConf(File file) throws IOException { + try (Reader reader = new FileReader(file)) { + return BlcModApiBukkit.GSON_NON_PRETTY.fromJson(reader, Conf.class); + + } catch (FileNotFoundException ex) { + this.getLogger().log(Level.INFO, "No Config Found: Saving default..."); + Conf conf = new Conf(); + this.saveConf(conf, new File(this.getDataFolder(), "config.json")); + return conf; + } + } + + private void saveConf(Conf conf, File file) { + try (FileWriter writer = new FileWriter(file)) { + BlcModApiBukkit.GSON_PRETTY.toJson(conf, writer); + + } catch (Exception ex) { + ex.printStackTrace(); + } + } + + public Conf getConf() { + return this.conf; + } +} diff --git a/blcmodapibukkit-1.17/src/main/java/net/badlion/blcmodapibukkit/Conf.java b/blcmodapibukkit-1.17/src/main/java/net/badlion/blcmodapibukkit/Conf.java new file mode 100644 index 0000000..832f167 --- /dev/null +++ b/blcmodapibukkit-1.17/src/main/java/net/badlion/blcmodapibukkit/Conf.java @@ -0,0 +1,20 @@ +package net.badlion.blcmodapibukkit; + +import com.google.gson.JsonObject; + +import java.util.HashMap; +import java.util.Map; + +public class Conf { + private Map modsDisallowed = new HashMap(); + + public Map getModsDisallowed() { + return this.modsDisallowed; + } + + private static class DisallowedMods { + private boolean disabled; + private JsonObject extra_data; + private JsonObject settings; + } +} diff --git a/blcmodapibukkit-1.17/src/main/java/net/badlion/blcmodapibukkit/listener/PlayerListener.java b/blcmodapibukkit-1.17/src/main/java/net/badlion/blcmodapibukkit/listener/PlayerListener.java new file mode 100644 index 0000000..0b3ecff --- /dev/null +++ b/blcmodapibukkit-1.17/src/main/java/net/badlion/blcmodapibukkit/listener/PlayerListener.java @@ -0,0 +1,222 @@ +package net.badlion.blcmodapibukkit.listener; + +import net.badlion.blcmodapibukkit.BlcModApiBukkit; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerJoinEvent; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Arrays; + +public class PlayerListener implements Listener { + private final BlcModApiBukkit plugin; + private String versionSuffix; + + private final Method getHandleMethod; + + private final Field playerConnectionField; + + private final Method sendPacketMethod; + + private Constructor packetPlayOutCustomPayloadConstructor; + private Constructor packetPlayOutMinecraftKeyConstructor; + private boolean useMinecraftKey; + + // Bukkit 1.8+ support + private Class packetDataSerializerClass; + private Constructor packetDataSerializerConstructor; + + private Method wrappedBufferMethod; + + public PlayerListener(BlcModApiBukkit plugin) { + this.plugin = plugin; + + // Get the v1_X_Y from the end of the package name, e.g. v_1_7_R4 or v_1_12_R1 + String packageName = Bukkit.getServer().getClass().getPackage().getName(); + String[] parts = packageName.split("\\."); + + if (parts.length > 0) { + String suffix = parts[parts.length - 1]; + if (!suffix.startsWith("v")) { + throw new RuntimeException("Failed to find version for running Minecraft server, got suffix " + suffix); + } + + this.versionSuffix = suffix; + + this.plugin.getLogger().info("Found version " + this.versionSuffix); + } + + // We need to use reflection because Bukkit by default handles plugin messages in a really silly way + // Reflection stuff + Class craftPlayerClass = this.getClass("org.bukkit.craftbukkit." + this.versionSuffix + ".entity.CraftPlayer"); + if (craftPlayerClass == null) { + throw new RuntimeException("Failed to find CraftPlayer class"); + } + + Class nmsPlayerClass = this.getClass("net.minecraft.server.level.EntityPlayer"); + if (nmsPlayerClass == null) { + throw new RuntimeException("Failed to find EntityPlayer class"); + } + + Class playerConnectionClass = this.getClass("net.minecraft.server.network.PlayerConnection"); + if (playerConnectionClass == null) { + throw new RuntimeException("Failed to find PlayerConnection class"); + } + + Class packetPlayOutCustomPayloadClass = this.getClass("net.minecraft.network.protocol.game.PacketPlayOutCustomPayload"); + if (packetPlayOutCustomPayloadClass == null) { + throw new RuntimeException("Failed to find PacketPlayOutCustomPayload class"); + } + + this.packetPlayOutCustomPayloadConstructor = this.getConstructor(packetPlayOutCustomPayloadClass, String.class, byte[].class); + if (this.packetPlayOutCustomPayloadConstructor == null) { + // Newer versions of Minecraft use a different custom packet system + this.packetDataSerializerClass = this.getClass("net.minecraft.network.PacketDataSerializer"); + if (this.packetDataSerializerClass == null) { + throw new RuntimeException("Failed to find PacketPlayOutCustomPayload constructor or PacketDataSerializer class"); + } + + // Netty classes used by newer 1.8 and newer + Class byteBufClass = this.getClass("io.netty.buffer.ByteBuf"); + if (byteBufClass == null) { + throw new RuntimeException("Failed to find PacketPlayOutCustomPayload constructor or ByteBuf class"); + } + + this.packetDataSerializerConstructor = this.getConstructor(this.packetDataSerializerClass, byteBufClass); + if (this.packetDataSerializerConstructor == null) { + throw new RuntimeException("Failed to find PacketPlayOutCustomPayload constructor or PacketDataSerializer constructor"); + } + + Class unpooledClass = this.getClass("io.netty.buffer.Unpooled"); + if (unpooledClass == null) { + throw new RuntimeException("Failed to find PacketPlayOutCustomPayload constructor or Unpooled class"); + } + + this.wrappedBufferMethod = this.getMethod(unpooledClass, "wrappedBuffer", byte[].class); + if (this.wrappedBufferMethod == null) { + throw new RuntimeException("Failed to find PacketPlayOutCustomPayload constructor or wrappedBuffer()"); + } + + // If we made it this far in theory we are on at least 1.8 + this.packetPlayOutCustomPayloadConstructor = this.getConstructor(packetPlayOutCustomPayloadClass, String.class, this.packetDataSerializerClass); + if (this.packetPlayOutCustomPayloadConstructor == null) { + Class minecraftKeyClass = this.getClass("net.minecraft.resources.MinecraftKey"); + + // Fix for Paper in newer versions + this.packetPlayOutCustomPayloadConstructor = this.getConstructor(packetPlayOutCustomPayloadClass, minecraftKeyClass, this.packetDataSerializerClass); + + if (this.packetPlayOutCustomPayloadConstructor == null) { + throw new RuntimeException("Failed to find PacketPlayOutCustomPayload constructor 2x"); + } else { + this.useMinecraftKey = true; + this.packetPlayOutMinecraftKeyConstructor = this.getConstructor(minecraftKeyClass, String.class); + } + } + } + + this.getHandleMethod = this.getMethod(craftPlayerClass, "getHandle"); + if (this.getHandleMethod == null) { + throw new RuntimeException("Failed to find CraftPlayer.getHandle()"); + } + + this.playerConnectionField = this.getField(nmsPlayerClass, "b"); + if (this.playerConnectionField == null) { + throw new RuntimeException("Failed to find EntityPlayer.playerConnection"); + } + + this.sendPacketMethod = this.getMethod(playerConnectionClass, "sendPacket"); + if (this.sendPacketMethod == null) { + throw new RuntimeException("Failed to find PlayerConnection.sendPacket()"); + } + } + + @EventHandler + public void onPlayerJoin(PlayerJoinEvent event) { + Player player = event.getPlayer(); + + // Create data we need for packet; + String channel = "badlion:mods"; + Object message = BlcModApiBukkit.GSON_NON_PRETTY.toJson(this.plugin.getConf().getModsDisallowed()).getBytes(); + + try { + Object packet; + // Newer MC version, setup ByteBuf object + if (this.packetDataSerializerClass != null) { + Object byteBuf = this.wrappedBufferMethod.invoke(null, message); + Object packetDataSerializer = this.packetDataSerializerConstructor.newInstance(byteBuf); + + if (this.useMinecraftKey) { + Object key = this.packetPlayOutMinecraftKeyConstructor.newInstance(channel); + packet = this.packetPlayOutCustomPayloadConstructor.newInstance(key, packetDataSerializer); + } else { + packet = this.packetPlayOutCustomPayloadConstructor.newInstance(channel, packetDataSerializer); + } + } else { + // Work our magic to make the packet + packet = this.packetPlayOutCustomPayloadConstructor.newInstance(channel, message); + } + + // Work our magic to send the packet + Object nmsPlayer = this.getHandleMethod.invoke(player); + Object playerConnection = this.playerConnectionField.get(nmsPlayer); + this.sendPacketMethod.invoke(playerConnection, packet); + } catch (IllegalAccessException | InvocationTargetException | InstantiationException e) { + this.plugin.getLogger().severe("Failed to send BLC mod packet"); + e.printStackTrace(); + } + } + + public Class getClass(String className) { + try { + return Class.forName(className); + } catch (ClassNotFoundException e) { + return null; + } + } + + public Constructor getConstructor(Class clazz, Class... params) { + for (final Constructor constructor : clazz.getDeclaredConstructors()) { + if (Arrays.equals(constructor.getParameterTypes(), params)) { + constructor.setAccessible(true); + return constructor; + } + } + + return null; + } + + public Method getMethod(Class clazz, String methodName, Class... params) { + for (final Method method : clazz.getDeclaredMethods()) { + if (method.getName().equals(methodName)) { + if (params.length > 0) { + if (Arrays.equals(method.getParameterTypes(), params)) { + method.setAccessible(true); + return method; + } + } else { + method.setAccessible(true); + return method; + } + } + } + + return null; + } + + public Field getField(Class clazz, String fieldName) { + for (final Field field : clazz.getDeclaredFields()) { + if (field.getName().equals(fieldName)) { + field.setAccessible(true); + return field; + } + } + + return null; + } + +} diff --git a/blcmodapibukkit-1.17/src/main/resources/plugin.yml b/blcmodapibukkit-1.17/src/main/resources/plugin.yml new file mode 100644 index 0000000..c5bfea7 --- /dev/null +++ b/blcmodapibukkit-1.17/src/main/resources/plugin.yml @@ -0,0 +1,5 @@ +name: BadlionClientModAPI +main: net.badlion.blcmodapibukkit.BlcModApiBukkit +version: 1.0 +author: Badlion +api-version: 1.17 \ No newline at end of file diff --git a/blcmodapibukkit/pom.xml b/blcmodapibukkit/pom.xml index b0db901..ad2ba59 100644 --- a/blcmodapibukkit/pom.xml +++ b/blcmodapibukkit/pom.xml @@ -5,7 +5,7 @@ badlionclientmodapi net.badlion - 1.2.2 + 1.2.3 4.0.0 @@ -35,8 +35,8 @@ org.apache.maven.plugins maven-compiler-plugin - 1.6 - 1.6 + 1.7 + 1.7 diff --git a/blcmodapibukkit/src/main/java/net/badlion/blcmodapibukkit/BlcModApiBukkit.java b/blcmodapibukkit/src/main/java/net/badlion/blcmodapibukkit/BlcModApiBukkit.java index c2e7102..1c69041 100644 --- a/blcmodapibukkit/src/main/java/net/badlion/blcmodapibukkit/BlcModApiBukkit.java +++ b/blcmodapibukkit/src/main/java/net/badlion/blcmodapibukkit/BlcModApiBukkit.java @@ -5,7 +5,6 @@ import net.badlion.blcmodapibukkit.listener.PlayerListener; import org.bukkit.plugin.java.JavaPlugin; -import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; @@ -31,7 +30,7 @@ public void onEnable() { } try { - this.conf = loadConf(new File(this.getDataFolder(), "config.json")); + this.conf = this.loadConf(new File(this.getDataFolder(), "config.json")); // Register channel this.getServer().getMessenger().registerOutgoingPluginChannel(this, "badlion:mods"); @@ -52,9 +51,9 @@ public void onDisable() { } public Conf loadConf(File file) throws IOException { - try { - Reader reader = new BufferedReader(new FileReader(file)); + try (Reader reader = new FileReader(file)) { return BlcModApiBukkit.GSON_NON_PRETTY.fromJson(reader, Conf.class); + } catch (FileNotFoundException ex) { this.getLogger().log(Level.INFO,"No Config Found: Saving default..."); Conf conf = new Conf(); @@ -64,9 +63,9 @@ public Conf loadConf(File file) throws IOException { } private void saveConf(Conf conf, File file) { - try { - FileWriter writer = new FileWriter(file); + try (FileWriter writer = new FileWriter(file)) { BlcModApiBukkit.GSON_PRETTY.toJson(conf, writer); + } catch (Exception ex) { ex.printStackTrace(); } diff --git a/blcmodapibungee/pom.xml b/blcmodapibungee/pom.xml index 7f247da..abe916b 100644 --- a/blcmodapibungee/pom.xml +++ b/blcmodapibungee/pom.xml @@ -5,7 +5,7 @@ badlionclientmodapi net.badlion - 1.2.2 + 1.2.3 4.0.0 diff --git a/blcmodapibungee/src/main/java/net/badlion/blcmodapibungee/BlcModApiBungee.java b/blcmodapibungee/src/main/java/net/badlion/blcmodapibungee/BlcModApiBungee.java index 711a71c..be06ee9 100644 --- a/blcmodapibungee/src/main/java/net/badlion/blcmodapibungee/BlcModApiBungee.java +++ b/blcmodapibungee/src/main/java/net/badlion/blcmodapibungee/BlcModApiBungee.java @@ -5,7 +5,6 @@ import net.badlion.blcmodapibungee.listener.PlayerListener; import net.md_5.bungee.api.plugin.Plugin; -import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; @@ -48,8 +47,9 @@ public void onDisable() { } public Conf loadConf(File file) throws IOException { - try (Reader reader = new BufferedReader(new FileReader(file))) { + try (Reader reader = new FileReader(file)) { return BlcModApiBungee.GSON_NON_PRETTY.fromJson(reader, Conf.class); + } catch (FileNotFoundException ex) { this.getLogger().log(Level.INFO,"No Config Found: Saving default..."); Conf conf = new Conf(); @@ -61,6 +61,7 @@ public Conf loadConf(File file) throws IOException { private void saveConf(Conf conf, File file) { try (FileWriter writer = new FileWriter(file)) { BlcModApiBungee.GSON_PRETTY.toJson(conf, writer); + } catch (Exception ex) { ex.printStackTrace(); } diff --git a/pom.xml b/pom.xml index 2e6e6a5..b88a14f 100644 --- a/pom.xml +++ b/pom.xml @@ -7,10 +7,11 @@ net.badlion badlionclientmodapi pom - 1.2.2 + 1.2.3 blcmodapibungee blcmodapibukkit + blcmodapibukkit-1.17