├── .gitignore ├── LICENSE ├── blccpsapibukkit ├── pom.xml └── src │ └── main │ ├── java │ └── net │ │ └── badlion │ │ └── blccpsapibukkit │ │ ├── BlcCpsApiBukkit.java │ │ ├── Conf.java │ │ └── listener │ │ └── PlayerListener.java │ └── resources │ └── plugin.yml ├── blccpsapibungee ├── pom.xml └── src │ └── main │ ├── java │ └── net │ │ └── badlion │ │ └── blccpsapibungee │ │ ├── BlcCpsApiBungee.java │ │ ├── Conf.java │ │ └── listener │ │ └── PlayerListener.java │ └── resources │ └── plugin.yml ├── pom.xml └── readme.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .settings 3 | .project 4 | .classpath 5 | *.class 6 | bin/* 7 | lib/* 8 | include/* 9 | bin/ 10 | .idea/ 11 | *.iml 12 | out/* 13 | 14 | # maven 15 | target/ 16 | dependency-reduced-pom.xml -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018-2020 ESL Gaming Online, Inc 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /blccpsapibukkit/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | badlionclientclickspersecondapi 7 | net.badlion 8 | 1.2.1 9 | 10 | 4.0.0 11 | 12 | blccpsapibukkit 13 | 14 | 15 | UTF-8 16 | 17 | 18 | 19 | clean install 20 | badlionclientcpsapibukkit 21 | src/main/java 22 | 23 | 24 | . 25 | true 26 | src/main/resources/ 27 | 28 | *.yml 29 | 30 | 31 | 32 | 33 | 34 | 35 | org.apache.maven.plugins 36 | maven-compiler-plugin 37 | 38 | 1.6 39 | 1.6 40 | 41 | 42 | 43 | org.apache.maven.plugins 44 | maven-shade-plugin 45 | 3.1.0 46 | 47 | 48 | package 49 | 50 | shade 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | org.bukkit 61 | bukkit 62 | 1.12.2-R0.1-SNAPSHOT 63 | provided 64 | 65 | 66 | com.google.code.gson 67 | gson 68 | 2.6.2 69 | compile 70 | 71 | 72 | 73 | 74 | 75 | spigot-repo 76 | https://hub.spigotmc.org/nexus/content/repositories/snapshots/ 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /blccpsapibukkit/src/main/java/net/badlion/blccpsapibukkit/BlcCpsApiBukkit.java: -------------------------------------------------------------------------------- 1 | package net.badlion.blccpsapibukkit; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.GsonBuilder; 5 | import net.badlion.blccpsapibukkit.listener.PlayerListener; 6 | import org.bukkit.plugin.java.JavaPlugin; 7 | 8 | import java.io.File; 9 | import java.io.FileNotFoundException; 10 | import java.io.FileReader; 11 | import java.io.FileWriter; 12 | import java.io.IOException; 13 | import java.util.logging.Level; 14 | 15 | public class BlcCpsApiBukkit extends JavaPlugin { 16 | 17 | public static final Gson GSON_NON_PRETTY = new GsonBuilder().enableComplexMapKeySerialization().disableHtmlEscaping().create(); 18 | private static final Gson GSON_PRETTY = new GsonBuilder().enableComplexMapKeySerialization().disableHtmlEscaping().setPrettyPrinting().create(); 19 | 20 | private Conf conf; 21 | 22 | @Override 23 | public void onEnable() { 24 | 25 | if (!this.getDataFolder().exists()) { 26 | if (!this.getDataFolder().mkdir()) { 27 | this.getLogger().log(Level.SEVERE, "Failed to create plugin directory."); 28 | } 29 | } 30 | 31 | try { 32 | this.conf = loadConf(new File(this.getDataFolder(), "config.json")); 33 | 34 | // Register channel 35 | this.getServer().getMessenger().registerOutgoingPluginChannel(this, "badlion:cps"); 36 | 37 | // Only register the listener if the config loads successfully 38 | this.getServer().getPluginManager().registerEvents(new PlayerListener(this), this); 39 | 40 | this.getLogger().log(Level.INFO, "Successfully setup BadlionClientCPSAPI plugin."); 41 | } catch (Exception ex) { 42 | this.getLogger().log(Level.SEVERE, "Error with config for BadlionClientCPSAPI plugin : " + ex.getMessage(), ex); 43 | } 44 | } 45 | 46 | @Override 47 | public void onDisable() { 48 | 49 | } 50 | 51 | private Conf loadConf(File file) { 52 | FileReader fileReader = null; 53 | 54 | try { 55 | fileReader = new FileReader(file); 56 | return BlcCpsApiBukkit.GSON_NON_PRETTY.fromJson(fileReader, Conf.class); 57 | } catch (FileNotFoundException ex) { 58 | this.getLogger().log(Level.INFO,"No Config Found: Saving default..."); 59 | Conf conf = new Conf(); 60 | this.saveConf(conf, file); 61 | return conf; 62 | } finally { 63 | if (fileReader != null) { 64 | try { 65 | fileReader.close(); 66 | } catch (IOException ignored) { 67 | 68 | } 69 | } 70 | } 71 | } 72 | 73 | private void saveConf(Conf conf, File file) { 74 | FileWriter fileWriter = null; 75 | 76 | try { 77 | fileWriter = new FileWriter(file); 78 | BlcCpsApiBukkit.GSON_PRETTY.toJson(conf, fileWriter); 79 | } catch (Exception ex) { 80 | ex.printStackTrace(); 81 | } finally { 82 | if (fileWriter != null) { 83 | try { 84 | fileWriter.close(); 85 | } catch (IOException ignored) { 86 | 87 | } 88 | } 89 | } 90 | } 91 | 92 | public Conf getConf() { 93 | return this.conf; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /blccpsapibukkit/src/main/java/net/badlion/blccpsapibukkit/Conf.java: -------------------------------------------------------------------------------- 1 | package net.badlion.blccpsapibukkit; 2 | 3 | public class Conf { 4 | private int clicksPerSecondLimitRight = 20; 5 | private int clicksPerSecondLimit = 20; 6 | } 7 | -------------------------------------------------------------------------------- /blccpsapibukkit/src/main/java/net/badlion/blccpsapibukkit/listener/PlayerListener.java: -------------------------------------------------------------------------------- 1 | package net.badlion.blccpsapibukkit.listener; 2 | 3 | import net.badlion.blccpsapibukkit.BlcCpsApiBukkit; 4 | import org.bukkit.entity.Player; 5 | import org.bukkit.event.EventHandler; 6 | import org.bukkit.event.Listener; 7 | import org.bukkit.event.player.PlayerJoinEvent; 8 | 9 | import java.lang.reflect.Constructor; 10 | import java.lang.reflect.Field; 11 | import java.lang.reflect.InvocationTargetException; 12 | import java.lang.reflect.Method; 13 | import java.util.Arrays; 14 | 15 | public class PlayerListener implements Listener { 16 | 17 | private BlcCpsApiBukkit plugin; 18 | private String versionSuffix; 19 | 20 | private Method getHandleMethod; 21 | 22 | private Field playerConnectionField; 23 | 24 | private Method sendPacketMethod; 25 | 26 | private Constructor packetPlayOutCustomPayloadConstructor; 27 | 28 | // Bukkit 1.8+ support 29 | private Class packetDataSerializerClass; 30 | private Constructor packetDataSerializerConstructor; 31 | 32 | // Bukkit 1.13+ support 33 | private Class minecraftKeyClass; 34 | private Constructor minecraftKeyConstructor; 35 | 36 | private Method wrappedBufferMethod; 37 | 38 | public PlayerListener(BlcCpsApiBukkit plugin) { 39 | this.plugin = plugin; 40 | 41 | // Get the v1_X_Y from the end of the package name, e.g. v_1_7_R4 or v_1_12_R1 42 | String packageName = plugin.getServer().getClass().getPackage().getName(); 43 | String[] parts = packageName.split("\\."); 44 | 45 | if (parts.length > 0) { 46 | String suffix = parts[parts.length - 1]; 47 | if (!suffix.startsWith("v")) { 48 | throw new RuntimeException("Failed to find version for running Minecraft server, got suffix " + suffix); 49 | } 50 | 51 | this.versionSuffix = suffix; 52 | 53 | this.plugin.getLogger().info("Found version " + this.versionSuffix); 54 | } 55 | 56 | // We need to use reflection because Bukkit by default handles plugin messages in a really silly way 57 | // Reflection stuff 58 | Class craftPlayerClass = this.getClass("org.bukkit.craftbukkit." + this.versionSuffix + ".entity.CraftPlayer"); 59 | if (craftPlayerClass == null) { 60 | throw new RuntimeException("Failed to find CraftPlayer class"); 61 | } 62 | 63 | Class nmsPlayerClass = this.getClass("net.minecraft.server." + this.versionSuffix + ".EntityPlayer"); 64 | if (nmsPlayerClass == null) { 65 | throw new RuntimeException("Failed to find EntityPlayer class"); 66 | } 67 | 68 | Class playerConnectionClass = this.getClass("net.minecraft.server." + this.versionSuffix + ".PlayerConnection"); 69 | if (playerConnectionClass == null) { 70 | throw new RuntimeException("Failed to find PlayerConnection class"); 71 | } 72 | 73 | Class packetPlayOutCustomPayloadClass = this.getClass("net.minecraft.server." + this.versionSuffix + ".PacketPlayOutCustomPayload"); 74 | if (packetPlayOutCustomPayloadClass == null) { 75 | throw new RuntimeException("Failed to find PacketPlayOutCustomPayload class"); 76 | } 77 | 78 | this.packetPlayOutCustomPayloadConstructor = this.getConstructor(packetPlayOutCustomPayloadClass, String.class, byte[].class); 79 | if (this.packetPlayOutCustomPayloadConstructor == null) { 80 | // Newer versions of Minecraft use a different custom packet system 81 | this.packetDataSerializerClass = this.getClass("net.minecraft.server." + this.versionSuffix + ".PacketDataSerializer"); 82 | if (this.packetDataSerializerClass == null) { 83 | throw new RuntimeException("Failed to find PacketPlayOutCustomPayload constructor or PacketDataSerializer class"); 84 | } 85 | 86 | // Netty classes used by newer 1.8 and newer 87 | Class byteBufClass = this.getClass("io.netty.buffer.ByteBuf"); 88 | if (byteBufClass == null) { 89 | throw new RuntimeException("Failed to find PacketPlayOutCustomPayload constructor or ByteBuf class"); 90 | } 91 | 92 | this.packetDataSerializerConstructor = this.getConstructor(this.packetDataSerializerClass, byteBufClass); 93 | if (this.packetDataSerializerConstructor == null) { 94 | throw new RuntimeException("Failed to find PacketPlayOutCustomPayload constructor or PacketDataSerializer constructor"); 95 | } 96 | 97 | Class unpooledClass = this.getClass("io.netty.buffer.Unpooled"); 98 | if (unpooledClass == null) { 99 | throw new RuntimeException("Failed to find PacketPlayOutCustomPayload constructor or Unpooled class"); 100 | } 101 | 102 | this.wrappedBufferMethod = this.getMethod(unpooledClass, "wrappedBuffer", byte[].class); 103 | if (this.wrappedBufferMethod == null) { 104 | throw new RuntimeException("Failed to find PacketPlayOutCustomPayload constructor or wrappedBuffer()"); 105 | } 106 | 107 | // If we made it this far in theory we are on at least 1.8 108 | this.packetPlayOutCustomPayloadConstructor = this.getConstructor(packetPlayOutCustomPayloadClass, String.class, this.packetDataSerializerClass); 109 | if (this.packetPlayOutCustomPayloadConstructor == null) { 110 | // Ok we are in 1.13 or higher now... 111 | this.minecraftKeyClass = this.getClass("net.minecraft.server." + this.versionSuffix + ".MinecraftKey"); 112 | if (this.minecraftKeyClass == null) { 113 | throw new RuntimeException("Failed to find PacketPlayOutCustomPayload constructor or MinecraftKey class"); 114 | } 115 | 116 | this.minecraftKeyConstructor = this.getConstructor(this.minecraftKeyClass, String.class, String.class); 117 | if (this.minecraftKeyConstructor == null) { 118 | throw new RuntimeException("Failed to find PacketPlayOutCustomPayload constructor or MinecraftKey constructor"); 119 | } 120 | 121 | // If we still can't find this...unknown version 122 | this.packetPlayOutCustomPayloadConstructor = this.getConstructor(packetPlayOutCustomPayloadClass, this.minecraftKeyClass, this.packetDataSerializerClass); 123 | if (this.packetPlayOutCustomPayloadConstructor == null) { 124 | throw new RuntimeException("Failed to find PacketPlayOutCustomPayload constructor"); 125 | } 126 | } 127 | } 128 | 129 | this.getHandleMethod = this.getMethod(craftPlayerClass, "getHandle"); 130 | if (this.getHandleMethod == null) { 131 | throw new RuntimeException("Failed to find CraftPlayer.getHandle()"); 132 | } 133 | 134 | this.playerConnectionField = this.getField(nmsPlayerClass, "playerConnection"); 135 | if (this.playerConnectionField == null) { 136 | throw new RuntimeException("Failed to find EntityPlayer.playerConnection"); 137 | } 138 | 139 | this.sendPacketMethod = this.getMethod(playerConnectionClass, "sendPacket"); 140 | if (this.sendPacketMethod == null) { 141 | throw new RuntimeException("Failed to find PlayerConnection.sendPacket()"); 142 | } 143 | } 144 | 145 | @EventHandler 146 | public void onPlayerJoin(PlayerJoinEvent event) { 147 | Player player = event.getPlayer(); 148 | 149 | // Create data we need for packet; 150 | String channel = "badlion:cps"; 151 | byte[] message = BlcCpsApiBukkit.GSON_NON_PRETTY.toJson(this.plugin.getConf()).getBytes(); 152 | 153 | try { 154 | Object packet; 155 | 156 | // 1.13+ 157 | if (this.minecraftKeyClass != null) { 158 | Object minecraftKey = this.minecraftKeyConstructor.newInstance("badlion", "cps"); 159 | Object byteBuf = this.wrappedBufferMethod.invoke(null, (Object) message); 160 | Object packetDataSerializer = this.packetDataSerializerConstructor.newInstance(byteBuf); 161 | 162 | packet = this.packetPlayOutCustomPayloadConstructor.newInstance(minecraftKey, packetDataSerializer); 163 | } else if (this.packetDataSerializerClass != null) { // 1.8+ 164 | Object byteBuf = this.wrappedBufferMethod.invoke(null, (Object) message); 165 | Object packetDataSerializer = this.packetDataSerializerConstructor.newInstance(byteBuf); 166 | 167 | packet = this.packetPlayOutCustomPayloadConstructor.newInstance(channel, packetDataSerializer); 168 | } else { // 1.7 169 | // Work our magic to make the packet 170 | packet = this.packetPlayOutCustomPayloadConstructor.newInstance(channel, message); 171 | } 172 | 173 | // Work our magic to send the packet 174 | Object nmsPlayer = this.getHandleMethod.invoke(player); 175 | Object playerConnection = this.playerConnectionField.get(nmsPlayer); 176 | this.sendPacketMethod.invoke(playerConnection, packet); 177 | } catch (IllegalAccessException e) { 178 | this.plugin.getLogger().severe("Failed to send BLC CPS packet"); 179 | e.printStackTrace(); 180 | } catch (InvocationTargetException e) { 181 | this.plugin.getLogger().severe("Failed to send BLC CPS packet"); 182 | e.printStackTrace(); 183 | } catch (InstantiationException e) { 184 | this.plugin.getLogger().severe("Failed to send BLC CPS packet"); 185 | e.printStackTrace(); 186 | } 187 | } 188 | 189 | private Class getClass(String className) { 190 | try { 191 | return Class.forName(className); 192 | } catch (ClassNotFoundException e) { 193 | return null; 194 | } 195 | } 196 | 197 | private Constructor getConstructor(Class clazz, Class... params) { 198 | for (final Constructor constructor : clazz.getDeclaredConstructors()) { 199 | if (Arrays.equals(constructor.getParameterTypes(), params)) { 200 | constructor.setAccessible(true); 201 | return constructor; 202 | } 203 | } 204 | 205 | return null; 206 | } 207 | 208 | private Method getMethod(Class clazz, String methodName, Class... params) { 209 | for (final Method method : clazz.getDeclaredMethods()) { 210 | if (method.getName().equals(methodName)) { 211 | if (params.length > 0) { 212 | if (Arrays.equals(method.getParameterTypes(), params)) { 213 | method.setAccessible(true); 214 | return method; 215 | } 216 | } else { 217 | method.setAccessible(true); 218 | return method; 219 | } 220 | } 221 | } 222 | 223 | return null; 224 | } 225 | 226 | private Field getField(Class clazz, String fieldName) { 227 | for (final Field field : clazz.getDeclaredFields()) { 228 | if (field.getName().equals(fieldName)) { 229 | field.setAccessible(true); 230 | return field; 231 | } 232 | } 233 | 234 | return null; 235 | } 236 | 237 | } 238 | -------------------------------------------------------------------------------- /blccpsapibukkit/src/main/resources/plugin.yml: -------------------------------------------------------------------------------- 1 | name: BadlionClientCPSAPI 2 | main: net.badlion.blccpsapibukkit.BlcCpsApiBukkit 3 | version: 1.0 4 | author: Badlion -------------------------------------------------------------------------------- /blccpsapibungee/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | badlionclientclickspersecondapi 7 | net.badlion 8 | 1.2.1 9 | 10 | 4.0.0 11 | 12 | blccpsapibungee 13 | 14 | 15 | UTF-8 16 | 17 | 18 | 19 | clean install 20 | badlionclientcpsapibungee 21 | src/main/java 22 | 23 | 24 | . 25 | true 26 | src/main/resources/ 27 | 28 | *.yml 29 | 30 | 31 | 32 | 33 | 34 | 35 | org.apache.maven.plugins 36 | maven-compiler-plugin 37 | 38 | 1.7 39 | 1.7 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | net.md-5 48 | bungeecord-api 49 | 1.12-SNAPSHOT 50 | jar 51 | provided 52 | 53 | 54 | net.md-5 55 | bungeecord-api 56 | 1.12-SNAPSHOT 57 | javadoc 58 | provided 59 | 60 | 61 | 62 | 63 | 64 | bungeecord-repo 65 | https://oss.sonatype.org/content/repositories/snapshots 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /blccpsapibungee/src/main/java/net/badlion/blccpsapibungee/BlcCpsApiBungee.java: -------------------------------------------------------------------------------- 1 | package net.badlion.blccpsapibungee; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.GsonBuilder; 5 | import net.badlion.blccpsapibungee.listener.PlayerListener; 6 | import net.md_5.bungee.api.plugin.Plugin; 7 | 8 | import java.io.File; 9 | import java.io.FileNotFoundException; 10 | import java.io.FileReader; 11 | import java.io.FileWriter; 12 | import java.io.IOException; 13 | import java.util.logging.Level; 14 | 15 | public class BlcCpsApiBungee extends Plugin { 16 | 17 | public static final Gson GSON_NON_PRETTY = new GsonBuilder().enableComplexMapKeySerialization().disableHtmlEscaping().create(); 18 | private static final Gson GSON_PRETTY = new GsonBuilder().enableComplexMapKeySerialization().disableHtmlEscaping().setPrettyPrinting().create(); 19 | 20 | private Conf conf; 21 | 22 | @Override 23 | public void onEnable() { 24 | if (!this.getDataFolder().exists()) { 25 | if (!this.getDataFolder().mkdir()) { 26 | this.getLogger().log(Level.SEVERE, "Failed to create plugin directory."); 27 | } 28 | } 29 | 30 | try { 31 | this.conf = loadConf(new File(this.getDataFolder(), "config.json")); 32 | 33 | // Only register the listener if the config loads successfully 34 | this.getProxy().getPluginManager().registerListener(this, new PlayerListener(this)); 35 | 36 | this.getLogger().log(Level.INFO, "Successfully setup BadlionClientCPSAPI plugin."); 37 | } catch (Exception e) { 38 | this.getLogger().log(Level.SEVERE, "Error with config for BadlionClientCPSAPI plugin."); 39 | e.printStackTrace(); 40 | } 41 | } 42 | 43 | @Override 44 | public void onDisable() { 45 | 46 | } 47 | 48 | private Conf loadConf(File file) throws IOException { 49 | 50 | try (FileReader fileReader = new FileReader(file)) { 51 | return BlcCpsApiBungee.GSON_NON_PRETTY.fromJson(fileReader, Conf.class); 52 | } catch (FileNotFoundException ex) { 53 | this.getLogger().log(Level.INFO, "No Config Found: Saving default..."); 54 | Conf conf = new Conf(); 55 | this.saveConf(conf, file); 56 | return conf; 57 | } 58 | } 59 | 60 | private void saveConf(Conf conf, File file) { 61 | 62 | try (FileWriter fileWriter = new FileWriter(file)) { 63 | BlcCpsApiBungee.GSON_PRETTY.toJson(conf, fileWriter); 64 | } catch (Exception ex) { 65 | ex.printStackTrace(); 66 | } 67 | } 68 | 69 | public Conf getConf() { 70 | return this.conf; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /blccpsapibungee/src/main/java/net/badlion/blccpsapibungee/Conf.java: -------------------------------------------------------------------------------- 1 | package net.badlion.blccpsapibungee; 2 | 3 | public class Conf { 4 | private int clicksPerSecondLimitRight = 20; 5 | private int clicksPerSecondLimit = 20; 6 | } 7 | -------------------------------------------------------------------------------- /blccpsapibungee/src/main/java/net/badlion/blccpsapibungee/listener/PlayerListener.java: -------------------------------------------------------------------------------- 1 | package net.badlion.blccpsapibungee.listener; 2 | 3 | import net.badlion.blccpsapibungee.BlcCpsApiBungee; 4 | import net.md_5.bungee.api.connection.ProxiedPlayer; 5 | import net.md_5.bungee.api.event.PostLoginEvent; 6 | import net.md_5.bungee.api.plugin.Listener; 7 | import net.md_5.bungee.event.EventHandler; 8 | import net.md_5.bungee.protocol.packet.PluginMessage; 9 | 10 | public class PlayerListener implements Listener { 11 | 12 | private BlcCpsApiBungee plugin; 13 | 14 | public PlayerListener(BlcCpsApiBungee plugin) { 15 | this.plugin = plugin; 16 | } 17 | 18 | @EventHandler 19 | public void onLogin(PostLoginEvent event) { 20 | // Send the click speed limitation to players when they login to the proxy. A notification will appear on the Badlion Client so they know their CPS has been limited 21 | ProxiedPlayer player = event.getPlayer(); 22 | player.unsafe().sendPacket(new PluginMessage("badlion:cps", BlcCpsApiBungee.GSON_NON_PRETTY.toJson(this.plugin.getConf()).getBytes(), false)); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /blccpsapibungee/src/main/resources/plugin.yml: -------------------------------------------------------------------------------- 1 | name: BadlionClientCPSAPI 2 | main: net.badlion.blccpsapibungee.BlcCpsApiBungee 3 | version: 1.0 4 | author: Badlion -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 4.0.0 7 | 8 | net.badlion 9 | badlionclientclickspersecondapi 10 | pom 11 | 1.2.1 12 | 13 | 14 | blccpsapibungee 15 | blccpsapibukkit 16 | 17 | 18 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Badlion Client CPS API 2 | 3 | This repository explains how to limit the clicks per second (cps) of a Badlion Client user via a simple Bungee/Bukkit plugin. 4 | 5 | By default there is no limitation to how fast you can click with the Badlion Client. 6 | 7 | ### Installation 8 | 9 | How to install the Badlion Client CPS API on your server. 10 | 11 | #### Quick Installation (for non-programmers) 12 | 13 | 1. Download **either** the latest bukkit or bungee plugin from our releases (you don't need both, we recommend using the BungeeCord plugin if you are running BungeeCord): https://github.com/BadlionNetwork/BadlionClientCPSAPI/releases 14 | 2. Place the downloaded plugin into your `plugins` directory on your server. 15 | 3. Turn on the BungeeCord or Bukkit server and a default config will be automatically made in `plugins/BadlionClientCPSAPI/config.json` 16 | 4. Edit the config as you see fit and reboot the server after you have finished editing the config (see below for more information). 17 | 18 | #### Do it yourself (for programmers) 19 | 20 | 1. Clone this repository 21 | 2. Compile the plugin(s) you want to use (you only need one per Minecraft network). 22 | 2. Place the compiled plugins from the `target` directories into your `plugins` directory on your server. 23 | 3. Turn on the BungeeCord or Bukkit server and a default config will be automatically made in `plugins/BadlionClientCPSAPI/config.json` 24 | 4. Edit the config as you see fit and reboot the server after you have finished editing the config (see below for more information). 25 | 26 | ### Example Config 27 | 28 | To make a config place the information below example into `plugins/BadlionClientCPSAPI/config.json`. If you have any JSON errors it will not load the plugin properly. A quick and easy way to test that your JSON config is valid is to use this tool: https://jsonformatter.curiousconcept.com/ 29 | 30 | This example config will limit a user to 20 clicks per second (cps) when playing on your server with the Badlion Client. 31 | 32 | ```json 33 | { 34 | "clicksPerSecondLimit": 20, 35 | "clicksPerSecondLimitRight": 20 36 | } 37 | ``` 38 | 39 | The first value `clicksPerSecondLimit` is for left click and the second value `clicksPerSecondLimitRight` is for right click. 40 | --------------------------------------------------------------------------------