From 6243727ebfc0e26c28b9b47bd91f0c99c0ca3506 Mon Sep 17 00:00:00 2001 From: Dawson Date: Mon, 17 Mar 2025 11:24:56 -0400 Subject: [PATCH 01/20] Fixing bug where player is not kicked when using proxy on Bukkit servers --- Assembly/pom.xml | 8 +- Bukkit/pom.xml | 4 +- .../antivpn/bukkit/BukkitListener.java | 143 ++++++++++-------- Bungee/pom.xml | 4 +- Common/pom.xml | 4 +- Sponge/pom.xml | 4 +- Velocity/pom.xml | 4 +- pom.xml | 2 +- 8 files changed, 96 insertions(+), 77 deletions(-) diff --git a/Assembly/pom.xml b/Assembly/pom.xml index 9e3fa66..a118190 100644 --- a/Assembly/pom.xml +++ b/Assembly/pom.xml @@ -5,7 +5,7 @@ AntiVPN dev.brighten.antivpn - 1.9.3 + 1.9.4-DEV 4.0.0 @@ -65,6 +65,12 @@ ${version} compile + + org.xerial + sqlite-jdbc + 3.41.2.2 + compile + dev.brighten.antivpn Bukkit diff --git a/Bukkit/pom.xml b/Bukkit/pom.xml index 15cd426..0cc59d1 100644 --- a/Bukkit/pom.xml +++ b/Bukkit/pom.xml @@ -5,7 +5,7 @@ AntiVPN dev.brighten.antivpn - 1.9.3 + 1.9.4-DEV 4.0.0 @@ -78,7 +78,7 @@ dev.brighten.antivpn Common - 1.9.3 + 1.9.4-DEV provided diff --git a/Bukkit/src/main/java/dev/brighten/antivpn/bukkit/BukkitListener.java b/Bukkit/src/main/java/dev/brighten/antivpn/bukkit/BukkitListener.java index 0f2f730..0cc5093 100644 --- a/Bukkit/src/main/java/dev/brighten/antivpn/bukkit/BukkitListener.java +++ b/Bukkit/src/main/java/dev/brighten/antivpn/bukkit/BukkitListener.java @@ -86,6 +86,7 @@ public class BukkitListener extends VPNExecutor implements Listener { event.setResult(PlayerLoginEvent.Result.KICK_BANNED); event.setKickMessage(org.bukkit.ChatColor.translateAlternateColorCodes('&', AntiVPN.getInstance().getVpnConfig().getKickString())); + proxyKick(event.getPlayer(), cached); return; } } @@ -123,49 +124,21 @@ public class BukkitListener extends VPNExecutor implements Listener { && AntiVPN.getInstance().getVpnConfig().countryList() .contains(result.getCountryCode()) != AntiVPN.getInstance().getVpnConfig().whitelistCountries()) { - final String kickReason = AntiVPN.getInstance().getVpnConfig() - .countryVanillaKickReason(); + // Start "online" fix // In case the response was so fast from API the player wouldn't be "online". event.setResult(PlayerLoginEvent.Result.KICK_BANNED); event.setKickMessage(ChatColor .translateAlternateColorCodes('&', - kickReason + AntiVPN.getInstance().getVpnConfig().countryVanillaKickReason() .replace("%player%", event.getPlayer().getName()) .replace("%country%", result.getCountryName()) .replace("%code%", result.getCountryCode()))); // End "online" fix - //Using our built in kicking system if no commands are configured - if(AntiVPN.getInstance().getVpnConfig().countryKickCommands().isEmpty()) { - // Kicking our player - new BukkitRunnable() { - public void run() { - event.getPlayer().kickPlayer(ChatColor - .translateAlternateColorCodes('&', - kickReason - .replace("%player%", event.getPlayer().getName()) - .replace("%country%", result.getCountryName()) - .replace("%code%", result.getCountryCode()))); - } - }.runTask(BukkitPlugin.pluginInstance); - } else { - final String playerName = event.getPlayer().getName(); + countryKick(player, result); - BukkitPlugin.pluginInstance.getPlayerCommandRunner() - .addAction(event.getPlayer().getUniqueId(), () -> { - for (String cmd : AntiVPN.getInstance().getVpnConfig().countryKickCommands()) { - final String formattedCommand = ChatColor.translateAlternateColorCodes('&', - cmd.replace("%player%", playerName) - .replace("%country%", result.getCountryName()) - .replace("%code%", result.getCountryCode())); - - // Runs our command from console - Bukkit.dispatchCommand(Bukkit.getConsoleSender(), formattedCommand); - } - }); - } } else if(result.isProxy()) { // Start "online" fix @@ -179,41 +152,7 @@ public class BukkitListener extends VPNExecutor implements Listener { .replace("%code%", result.getCountryCode()))); // End "online" fix - if(AntiVPN.getInstance().getVpnConfig().kickPlayersOnDetect()) { - new BukkitRunnable() { - public void run() { - player.kickPlayer(org.bukkit.ChatColor.translateAlternateColorCodes('&', - AntiVPN.getInstance().getVpnConfig().getKickString())); - } - }.runTask(BukkitPlugin.pluginInstance); - } - log(Level.INFO, event.getPlayer().getName() - + " joined on a VPN/Proxy (" + result.getMethod() + ")"); - - //Ensuring the user wishes to alert to staff - if(AntiVPN.getInstance().getVpnConfig().alertToStaff()) - AntiVPN.getInstance().getPlayerExecutor().getOnlinePlayers().stream() - .filter(APIPlayer::isAlertsEnabled) - .forEach(pl -> pl.sendMessage(AntiVPN.getInstance().getVpnConfig().alertMessage() - .replace("%player%", event.getPlayer().getName()) - .replace("%reason%", result.getMethod()) - .replace("%country%", result.getCountryName()) - .replace("%city%", result.getCity()))); - - //In case the user wants to run their own commands instead of using the built in kicking - if(AntiVPN.getInstance().getVpnConfig().runCommands()) { - String playerName = event.getPlayer().getName(); - BukkitPlugin.pluginInstance.getPlayerCommandRunner() - .addAction(event.getPlayer().getUniqueId(), () -> { - for (String command : AntiVPN.getInstance().getVpnConfig().commands()) { - Bukkit.dispatchCommand(Bukkit.getConsoleSender(), - ChatColor.translateAlternateColorCodes('&', - command.replace("%player%", - playerName))); - } - }); - } - AntiVPN.getInstance().detections++; + proxyKick(player, result); } } else { log(Level.WARNING, @@ -224,6 +163,78 @@ public class BukkitListener extends VPNExecutor implements Listener { }); } + private void countryKick(Player player, VPNResponse result) { + final String kickReason = AntiVPN.getInstance().getVpnConfig() + .countryVanillaKickReason(); + //Using our built in kicking system if no commands are configured + if(AntiVPN.getInstance().getVpnConfig().countryKickCommands().isEmpty()) { + // Kicking our player + new BukkitRunnable() { + public void run() { + player.kickPlayer(ChatColor + .translateAlternateColorCodes('&', + kickReason + .replace("%player%", player.getName()) + .replace("%country%", result.getCountryName()) + .replace("%code%", result.getCountryCode()))); + } + }.runTask(BukkitPlugin.pluginInstance); + } else { + final String playerName = player.getName(); + + BukkitPlugin.pluginInstance.getPlayerCommandRunner() + .addAction(player.getUniqueId(), () -> { + for (String cmd : AntiVPN.getInstance().getVpnConfig().countryKickCommands()) { + final String formattedCommand = ChatColor.translateAlternateColorCodes('&', + cmd.replace("%player%", playerName) + .replace("%country%", result.getCountryName()) + .replace("%code%", result.getCountryCode())); + + // Runs our command from console + Bukkit.dispatchCommand(Bukkit.getConsoleSender(), formattedCommand); + } + }); + } + } + + private void proxyKick(Player player, VPNResponse result) { + if(AntiVPN.getInstance().getVpnConfig().kickPlayersOnDetect()) { + new BukkitRunnable() { + public void run() { + player.kickPlayer(org.bukkit.ChatColor.translateAlternateColorCodes('&', + AntiVPN.getInstance().getVpnConfig().getKickString())); + } + }.runTask(BukkitPlugin.pluginInstance); + } + log(Level.INFO, player.getPlayer().getName() + + " joined on a VPN/Proxy (" + result.getMethod() + ")"); + + //Ensuring the user wishes to alert to staff + if(AntiVPN.getInstance().getVpnConfig().alertToStaff()) + AntiVPN.getInstance().getPlayerExecutor().getOnlinePlayers().stream() + .filter(APIPlayer::isAlertsEnabled) + .forEach(pl -> pl.sendMessage(AntiVPN.getInstance().getVpnConfig().alertMessage() + .replace("%player%", player.getName()) + .replace("%reason%", result.getMethod()) + .replace("%country%", result.getCountryName()) + .replace("%city%", result.getCity()))); + + //In case the user wants to run their own commands instead of using the built in kicking + if(AntiVPN.getInstance().getVpnConfig().runCommands()) { + String playerName = player.getName(); + BukkitPlugin.pluginInstance.getPlayerCommandRunner() + .addAction(player.getUniqueId(), () -> { + for (String command : AntiVPN.getInstance().getVpnConfig().commands()) { + Bukkit.dispatchCommand(Bukkit.getConsoleSender(), + ChatColor.translateAlternateColorCodes('&', + command.replace("%player%", + playerName))); + } + }); + } + AntiVPN.getInstance().detections++; + } + @EventHandler public void onQuit(PlayerQuitEvent event) { AntiVPN.getInstance().getPlayerExecutor().unloadPlayer(event.getPlayer().getUniqueId()); diff --git a/Bungee/pom.xml b/Bungee/pom.xml index 74b9836..06e4cff 100644 --- a/Bungee/pom.xml +++ b/Bungee/pom.xml @@ -5,7 +5,7 @@ AntiVPN dev.brighten.antivpn - 1.9.3 + 1.9.4-DEV 4.0.0 @@ -71,7 +71,7 @@ dev.brighten.antivpn Common - 1.9.3 + 1.9.4-DEV provided diff --git a/Common/pom.xml b/Common/pom.xml index 3c8ee46..82f313f 100644 --- a/Common/pom.xml +++ b/Common/pom.xml @@ -5,7 +5,7 @@ AntiVPN dev.brighten.antivpn - 1.9.3 + 1.9.4-DEV 4.0.0 @@ -86,6 +86,8 @@ com.mysql mysql-connector-j 9.1.0 + jar + compile com.h2database diff --git a/Sponge/pom.xml b/Sponge/pom.xml index 51be0b0..de32988 100644 --- a/Sponge/pom.xml +++ b/Sponge/pom.xml @@ -5,7 +5,7 @@ AntiVPN dev.brighten.antivpn - 1.9.3 + 1.9.4-DEV 4.0.0 @@ -28,7 +28,7 @@ dev.brighten.antivpn Common - 1.9.3 + 1.9.4-DEV provided diff --git a/Velocity/pom.xml b/Velocity/pom.xml index 93d2f38..045296f 100644 --- a/Velocity/pom.xml +++ b/Velocity/pom.xml @@ -5,7 +5,7 @@ AntiVPN dev.brighten.antivpn - 1.9.3 + 1.9.4-DEV 4.0.0 @@ -33,7 +33,7 @@ dev.brighten.antivpn Common - 1.9.3 + 1.9.4-DEV provided diff --git a/pom.xml b/pom.xml index eacb2aa..1bab415 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ dev.brighten.antivpn AntiVPN pom - 1.9.3 + 1.9.4-DEV Common From 6453898ca494d20f9fdc62b1a0393706de61f555 Mon Sep 17 00:00:00 2001 From: Dawson Date: Thu, 20 Mar 2025 10:29:21 -0400 Subject: [PATCH 02/20] Removing from PlayerLoginEvent and doing PlayerJoinEvent only instead. --- .../antivpn/bukkit/BukkitListener.java | 123 +++++++----------- Common/src/main/resources/config.yml | 3 +- 2 files changed, 48 insertions(+), 78 deletions(-) diff --git a/Bukkit/src/main/java/dev/brighten/antivpn/bukkit/BukkitListener.java b/Bukkit/src/main/java/dev/brighten/antivpn/bukkit/BukkitListener.java index 0cc5093..8c1c105 100644 --- a/Bukkit/src/main/java/dev/brighten/antivpn/bukkit/BukkitListener.java +++ b/Bukkit/src/main/java/dev/brighten/antivpn/bukkit/BukkitListener.java @@ -11,14 +11,12 @@ import net.md_5.bungee.api.ChatColor; import org.bukkit.Bukkit; 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.PlayerJoinEvent; -import org.bukkit.event.player.PlayerLoginEvent; import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.scheduler.BukkitRunnable; -import java.net.InetAddress; -import java.net.InetSocketAddress; import java.util.UUID; import java.util.concurrent.TimeUnit; import java.util.logging.Level; @@ -56,7 +54,7 @@ public class BukkitListener extends VPNExecutor implements Listener { Bukkit.getLogger().log(Level.SEVERE, message, ex); } - @EventHandler + @EventHandler(priority = EventPriority.HIGH) public void onJoin(final PlayerJoinEvent event) { AntiVPN.getInstance().getPlayerExecutor().getPlayer(event.getPlayer().getUniqueId()) .ifPresent(player -> AntiVPN.getInstance().getDatabase().alertsState(player.getUuid(), enabled -> { @@ -67,15 +65,20 @@ public class BukkitListener extends VPNExecutor implements Listener { .getFormattedMessage(new VpnString.Var<>("state", true))); } })); - } - @EventHandler - public void onListener(final PlayerLoginEvent event) { - //If they're exempt, don't check. + String address; + + if(event.getPlayer().getAddress() != null) { + address = event.getPlayer().getAddress().getAddress().getHostAddress(); + } else { + log(Level.WARNING, "Player %s address is null! This is a bug and should be reported!", event.getPlayer().getName()); + return; + } + if(event.getPlayer().hasPermission("antivpn.bypass") //Has bypass permission || AntiVPN.getInstance().getExecutor().isWhitelisted(event.getPlayer().getUniqueId()) //Is exempt //Or has a name that starts with a certain prefix. This is for Bedrock exempting. - || AntiVPN.getInstance().getExecutor().isWhitelisted(event.getAddress().getHostAddress()) + || AntiVPN.getInstance().getExecutor().isWhitelisted(address) || AntiVPN.getInstance().getVpnConfig().getPrefixWhitelists().stream() .anyMatch(prefix -> event.getPlayer().getName().startsWith(prefix))) return; @@ -83,81 +86,46 @@ public class BukkitListener extends VPNExecutor implements Listener { VPNResponse cached = responseCache.getIfPresent(event.getPlayer().getUniqueId()); if (cached != null && cached.isProxy()) { - event.setResult(PlayerLoginEvent.Result.KICK_BANNED); - event.setKickMessage(org.bukkit.ChatColor.translateAlternateColorCodes('&', - AntiVPN.getInstance().getVpnConfig().getKickString())); proxyKick(event.getPlayer(), cached); return; } } final Player player = event.getPlayer(); - checkIp(event.getAddress().getHostAddress(), + checkIp(address, AntiVPN.getInstance().getVpnConfig().cachedResults(), result -> { if(result.isSuccess()) { //We need to run on main thread or kicking and running commands will cause errors //If the player is whitelisted, we don't want to kick them if(AntiVPN.getInstance().getExecutor().isWhitelisted(event.getPlayer().getUniqueId())) { - log("UUID is whitelisted: %s", - event.getPlayer().getUniqueId().toString()); + log("UUID is whitelisted: %s", event.getPlayer().getUniqueId().toString()); return; } //If the IP is whitelisted, we don't want to kick them - InetSocketAddress address = event.getPlayer().getAddress(); - if (address != null){ - InetAddress address1 = address.getAddress(); - if (address1 != null && AntiVPN.getInstance().getExecutor() - .isWhitelisted(address1.getHostAddress())) { - log("IP is whitelisted: %s", - address1.getHostAddress()); - return; - } + if (AntiVPN.getInstance().getExecutor().isWhitelisted(address)) { + log("IP is whitelisted: %s", + address); + return; } // If the countryList() size is zero, no need to check. // Running country check first if(!AntiVPN.getInstance().getVpnConfig().countryList().isEmpty() - // This bit of code will decide whether or not to kick the player - // If it contains the code and it is set to whitelist, it will not kick as they are equal + // This bit of code will decide whether to kick the player + // If contains the code, and set to whitelist, it will not kick as they are equal // and vise versa. However, if the contains does not match the state, it will kick. && AntiVPN.getInstance().getVpnConfig().countryList() .contains(result.getCountryCode()) != AntiVPN.getInstance().getVpnConfig().whitelistCountries()) { - - - // Start "online" fix - // In case the response was so fast from API the player wouldn't be "online". - event.setResult(PlayerLoginEvent.Result.KICK_BANNED); - event.setKickMessage(ChatColor - .translateAlternateColorCodes('&', - AntiVPN.getInstance().getVpnConfig().countryVanillaKickReason() - .replace("%player%", event.getPlayer().getName()) - .replace("%country%", result.getCountryName()) - .replace("%code%", result.getCountryCode()))); - // End "online" fix - countryKick(player, result); - } else if(result.isProxy()) { - - // Start "online" fix - // In case the response was so fast from API the player wouldn't be "online". - event.setResult(PlayerLoginEvent.Result.KICK_BANNED); - event.setKickMessage(ChatColor - .translateAlternateColorCodes('&', - AntiVPN.getInstance().getVpnConfig().getKickString() - .replace("%player%", event.getPlayer().getName()) - .replace("%country%", result.getCountryName()) - .replace("%code%", result.getCountryCode()))); - // End "online" fix - proxyKick(player, result); } } else { log(Level.WARNING, - "The API query was not a success! " + - "You may need to upgrade your license on https://funkemunky.cc/shop"); + "The API query was not a success! " + + "You may need to upgrade your license on https://funkemunky.cc/shop"); } AntiVPN.getInstance().checked++; }); @@ -166,7 +134,7 @@ public class BukkitListener extends VPNExecutor implements Listener { private void countryKick(Player player, VPNResponse result) { final String kickReason = AntiVPN.getInstance().getVpnConfig() .countryVanillaKickReason(); - //Using our built in kicking system if no commands are configured + //Using our built-in kicking system if no commands are configured if(AntiVPN.getInstance().getVpnConfig().countryKickCommands().isEmpty()) { // Kicking our player new BukkitRunnable() { @@ -198,15 +166,7 @@ public class BukkitListener extends VPNExecutor implements Listener { } private void proxyKick(Player player, VPNResponse result) { - if(AntiVPN.getInstance().getVpnConfig().kickPlayersOnDetect()) { - new BukkitRunnable() { - public void run() { - player.kickPlayer(org.bukkit.ChatColor.translateAlternateColorCodes('&', - AntiVPN.getInstance().getVpnConfig().getKickString())); - } - }.runTask(BukkitPlugin.pluginInstance); - } - log(Level.INFO, player.getPlayer().getName() + log(Level.INFO, player.getName() + " joined on a VPN/Proxy (" + result.getMethod() + ")"); //Ensuring the user wishes to alert to staff @@ -219,20 +179,29 @@ public class BukkitListener extends VPNExecutor implements Listener { .replace("%country%", result.getCountryName()) .replace("%city%", result.getCity()))); - //In case the user wants to run their own commands instead of using the built in kicking - if(AntiVPN.getInstance().getVpnConfig().runCommands()) { - String playerName = player.getName(); - BukkitPlugin.pluginInstance.getPlayerCommandRunner() - .addAction(player.getUniqueId(), () -> { - for (String command : AntiVPN.getInstance().getVpnConfig().commands()) { - Bukkit.dispatchCommand(Bukkit.getConsoleSender(), - ChatColor.translateAlternateColorCodes('&', - command.replace("%player%", - playerName))); - } - }); + if(AntiVPN.getInstance().getVpnConfig().kickPlayersOnDetect()) { + new BukkitRunnable() { + public void run() { + player.kickPlayer(org.bukkit.ChatColor.translateAlternateColorCodes('&', + AntiVPN.getInstance().getVpnConfig().getKickString())); + } + }.runTask(BukkitPlugin.pluginInstance); + } else { + //In case the user wants to run their own commands instead of using the built-in kicking + if(AntiVPN.getInstance().getVpnConfig().runCommands()) { + String playerName = player.getName(); + BukkitPlugin.pluginInstance.getPlayerCommandRunner() + .addAction(player.getUniqueId(), () -> { + for (String command : AntiVPN.getInstance().getVpnConfig().commands()) { + Bukkit.dispatchCommand(Bukkit.getConsoleSender(), + ChatColor.translateAlternateColorCodes('&', + command.replace("%player%", + playerName))); + } + }); + } + AntiVPN.getInstance().detections++; } - AntiVPN.getInstance().detections++; } @EventHandler diff --git a/Common/src/main/resources/config.yml b/Common/src/main/resources/config.yml index a28421a..4827f1e 100644 --- a/Common/src/main/resources/config.yml +++ b/Common/src/main/resources/config.yml @@ -11,7 +11,8 @@ cachedResults: true # All players with any of the following characters in the beginning of their name will be whitelisted # This is a useful feature for servers that allow players through Geyser and their IPs are not forwarded, causing # players to be removed falsely for use of proxy. -prefixWhitelists: [] +prefixWhitelists: + - "*" # Configure your database here. database: # Enable to cache queries and save alerts state beyond restarts From 9cbeed1df379076ca34a49204e9a9e92dc608903 Mon Sep 17 00:00:00 2001 From: Dawson <30784509+funkemunky@users.noreply.github.com> Date: Thu, 27 Mar 2025 10:44:34 -0400 Subject: [PATCH 03/20] 58-bug-plugin-fails-to-load-after-upgrading-to-193-on-velocity (#60) * Moving to previous H2 version that was in 1.9.2 that somehow got downgraded * Reverting back to Java 8 compile target * removing minimize on shade for Common * Clearly something here changed something, cause reverting it this way seems to have fixed it * Cleaning up code here --------- Co-authored-by: Dawson --- Assembly/pom.xml | 4 ++-- Bukkit/pom.xml | 9 ++++----- Bungee/pom.xml | 8 ++++---- Common/pom.xml | 10 +++++----- .../dev/brighten/antivpn/database/sql/utils/MySQL.java | 7 +++---- Sponge/pom.xml | 8 ++++---- Velocity/pom.xml | 8 ++++---- pom.xml | 4 ++-- 8 files changed, 28 insertions(+), 30 deletions(-) diff --git a/Assembly/pom.xml b/Assembly/pom.xml index 9e3fa66..dc917eb 100644 --- a/Assembly/pom.xml +++ b/Assembly/pom.xml @@ -5,7 +5,7 @@ AntiVPN dev.brighten.antivpn - 1.9.3 + 1.9.3.1 4.0.0 @@ -21,7 +21,7 @@ org.apache.maven.plugins maven-shade-plugin - 3.2.4 + 3.5.2 package diff --git a/Bukkit/pom.xml b/Bukkit/pom.xml index 15cd426..c29e119 100644 --- a/Bukkit/pom.xml +++ b/Bukkit/pom.xml @@ -5,7 +5,7 @@ AntiVPN dev.brighten.antivpn - 1.9.3 + 1.9.3.1 4.0.0 @@ -16,7 +16,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.7.0 + 3.13.0 8 8 @@ -26,7 +26,7 @@ org.apache.maven.plugins maven-shade-plugin - 3.2.4 + 3.5.2 package @@ -34,7 +34,6 @@ shade - true org.bstats @@ -78,7 +77,7 @@ dev.brighten.antivpn Common - 1.9.3 + 1.9.3.1 provided diff --git a/Bungee/pom.xml b/Bungee/pom.xml index 74b9836..298323c 100644 --- a/Bungee/pom.xml +++ b/Bungee/pom.xml @@ -5,7 +5,7 @@ AntiVPN dev.brighten.antivpn - 1.9.3 + 1.9.3.1 4.0.0 @@ -16,7 +16,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.7.0 + 3.13.0 8 8 @@ -26,7 +26,7 @@ org.apache.maven.plugins maven-shade-plugin - 3.1.0 + 3.5.2 @@ -71,7 +71,7 @@ dev.brighten.antivpn Common - 1.9.3 + 1.9.3.1 provided diff --git a/Common/pom.xml b/Common/pom.xml index 3c8ee46..11471d2 100644 --- a/Common/pom.xml +++ b/Common/pom.xml @@ -5,7 +5,7 @@ AntiVPN dev.brighten.antivpn - 1.9.3 + 1.9.3.1 4.0.0 @@ -21,7 +21,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.10.1 + 3.13.0 8 8 @@ -38,7 +38,7 @@ org.apache.maven.plugins maven-shade-plugin - 3.4.1 + 3.5.2 package @@ -46,7 +46,7 @@ shade - true + false org.yaml.snakeyaml @@ -90,7 +90,7 @@ com.h2database h2 - 2.2.220 + 2.2.224 compile diff --git a/Common/src/main/java/dev/brighten/antivpn/database/sql/utils/MySQL.java b/Common/src/main/java/dev/brighten/antivpn/database/sql/utils/MySQL.java index 5184ef4..1a264c2 100644 --- a/Common/src/main/java/dev/brighten/antivpn/database/sql/utils/MySQL.java +++ b/Common/src/main/java/dev/brighten/antivpn/database/sql/utils/MySQL.java @@ -2,7 +2,6 @@ package dev.brighten.antivpn.database.sql.utils; import com.mysql.cj.jdbc.Driver; import dev.brighten.antivpn.AntiVPN; -import org.h2.jdbc.JdbcConnection; import java.io.File; import java.sql.Connection; @@ -38,15 +37,15 @@ public class MySQL { File dataFolder = new File(AntiVPN.getInstance().getPluginFolder(), "databases"); File databaseFile = new File(dataFolder, "database"); try { - conn = new NonClosableConnection(new JdbcConnection("jdbc:h2:file:" + - databaseFile.getAbsolutePath(), + conn = new NonClosableConnection(new org.h2.jdbc.JdbcConnection("jdbc:h2:file:" + + databaseFile.getAbsolutePath(), new Properties(), AntiVPN.getInstance().getVpnConfig().getUsername(), AntiVPN.getInstance().getVpnConfig().getPassword(), false)); conn.setAutoCommit(true); Query.use(conn); AntiVPN.getInstance().getExecutor().log("Connection to H2 has been established."); } catch (SQLException ex) { - AntiVPN.getInstance().getExecutor().logException("H2 exception on initialize: " + ex.getMessage(), ex); + AntiVPN.getInstance().getExecutor().logException("H2 exception on initialize", ex); } } diff --git a/Sponge/pom.xml b/Sponge/pom.xml index 51be0b0..984d2d2 100644 --- a/Sponge/pom.xml +++ b/Sponge/pom.xml @@ -5,7 +5,7 @@ AntiVPN dev.brighten.antivpn - 1.9.3 + 1.9.3.1 4.0.0 @@ -28,14 +28,14 @@ dev.brighten.antivpn Common - 1.9.3 + 1.9.3.1 provided - 17 - 17 + 8 + 8 \ No newline at end of file diff --git a/Velocity/pom.xml b/Velocity/pom.xml index 93d2f38..817e5d7 100644 --- a/Velocity/pom.xml +++ b/Velocity/pom.xml @@ -5,7 +5,7 @@ AntiVPN dev.brighten.antivpn - 1.9.3 + 1.9.3.1 4.0.0 @@ -33,7 +33,7 @@ dev.brighten.antivpn Common - 1.9.3 + 1.9.3.1 provided @@ -49,7 +49,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.7.0 + 3.13.0 8 8 @@ -59,7 +59,7 @@ org.apache.maven.plugins maven-shade-plugin - 3.1.0 + 3.5.2 diff --git a/pom.xml b/pom.xml index eacb2aa..f48ca17 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ dev.brighten.antivpn AntiVPN pom - 1.9.3 + 1.9.3.1 Common @@ -28,7 +28,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.7.0 + 3.13.0 8 8 From 311f1e198bf4dc0562a5303e739ace2d58f11ddd Mon Sep 17 00:00:00 2001 From: Dawson Date: Mon, 21 Apr 2025 13:20:13 -0400 Subject: [PATCH 04/20] Backing up and resetting database on versioning compatibility --- Assembly/pom.xml | 2 +- Bukkit/pom.xml | 4 +- Bungee/pom.xml | 4 +- Common/pom.xml | 2 +- .../antivpn/database/sql/utils/MySQL.java | 58 ++++++++++++++++++- Sponge/pom.xml | 4 +- Velocity/pom.xml | 4 +- pom.xml | 2 +- 8 files changed, 68 insertions(+), 12 deletions(-) diff --git a/Assembly/pom.xml b/Assembly/pom.xml index dc917eb..cb132a6 100644 --- a/Assembly/pom.xml +++ b/Assembly/pom.xml @@ -5,7 +5,7 @@ AntiVPN dev.brighten.antivpn - 1.9.3.1 + 1.9.4-DEV 4.0.0 diff --git a/Bukkit/pom.xml b/Bukkit/pom.xml index c29e119..182d3d6 100644 --- a/Bukkit/pom.xml +++ b/Bukkit/pom.xml @@ -5,7 +5,7 @@ AntiVPN dev.brighten.antivpn - 1.9.3.1 + 1.9.4-DEV 4.0.0 @@ -77,7 +77,7 @@ dev.brighten.antivpn Common - 1.9.3.1 + 1.9.4-DEV provided diff --git a/Bungee/pom.xml b/Bungee/pom.xml index 298323c..c067297 100644 --- a/Bungee/pom.xml +++ b/Bungee/pom.xml @@ -5,7 +5,7 @@ AntiVPN dev.brighten.antivpn - 1.9.3.1 + 1.9.4-DEV 4.0.0 @@ -71,7 +71,7 @@ dev.brighten.antivpn Common - 1.9.3.1 + 1.9.4-DEV provided diff --git a/Common/pom.xml b/Common/pom.xml index 11471d2..44eaa80 100644 --- a/Common/pom.xml +++ b/Common/pom.xml @@ -5,7 +5,7 @@ AntiVPN dev.brighten.antivpn - 1.9.3.1 + 1.9.4-DEV 4.0.0 diff --git a/Common/src/main/java/dev/brighten/antivpn/database/sql/utils/MySQL.java b/Common/src/main/java/dev/brighten/antivpn/database/sql/utils/MySQL.java index 1a264c2..8355e2a 100644 --- a/Common/src/main/java/dev/brighten/antivpn/database/sql/utils/MySQL.java +++ b/Common/src/main/java/dev/brighten/antivpn/database/sql/utils/MySQL.java @@ -2,12 +2,17 @@ package dev.brighten.antivpn.database.sql.utils; import com.mysql.cj.jdbc.Driver; import dev.brighten.antivpn.AntiVPN; +import org.h2.jdbc.JdbcSQLFeatureNotSupportedException; +import org.h2.jdbc.JdbcSQLNonTransientConnectionException; import java.io.File; +import java.io.IOException; +import java.nio.file.Files; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.util.Properties; +import java.util.logging.Level; public class MySQL { private static Connection conn; @@ -33,12 +38,25 @@ public class MySQL { } } + private static boolean didRetry = false; + public static void initH2() { + if(didRetry) { + AntiVPN.getInstance().getExecutor().log(Level.WARNING, + "Already attempted to retry H2 connection, skipping."); + return; + } File dataFolder = new File(AntiVPN.getInstance().getPluginFolder(), "databases"); + if (!dataFolder.exists() && dataFolder.mkdirs()) { + AntiVPN.getInstance().getExecutor().log("Created database directory"); + } + + File dbFile = new File(dataFolder, "database.mv.db"); + File databaseFile = new File(dataFolder, "database"); try { conn = new NonClosableConnection(new org.h2.jdbc.JdbcConnection("jdbc:h2:file:" + - databaseFile.getAbsolutePath(), + databaseFile.getAbsolutePath(), new Properties(), AntiVPN.getInstance().getVpnConfig().getUsername(), AntiVPN.getInstance().getVpnConfig().getPassword(), false)); conn.setAutoCommit(true); @@ -46,6 +64,44 @@ public class MySQL { AntiVPN.getInstance().getExecutor().log("Connection to H2 has been established."); } catch (SQLException ex) { AntiVPN.getInstance().getExecutor().logException("H2 exception on initialize", ex); + if(ex instanceof JdbcSQLFeatureNotSupportedException + || ex instanceof JdbcSQLNonTransientConnectionException) { + AntiVPN.getInstance().getExecutor() + .log("H2 database file is incompatible with this version of AntiVPN. " + + "Backing up old database file..."); + backupOldDB(dbFile, dataFolder); + initH2(); + didRetry = true; + } else { + AntiVPN.getInstance().getExecutor().logException("Failed to load H2 database: " + ex.getCause().toString(), ex); + } + } + } + + private static void backupOldDB(File dbFile, File dataFolder) { + if (dbFile.exists()) { + try { + // Optional: Make backup first + File backupDir = new File(dataFolder, "backups"); + if(backupDir.mkdirs()) { + AntiVPN.getInstance().getExecutor().log("Created backup directory"); + } else { + AntiVPN.getInstance().getExecutor().log("Backup directory already exists"); + } + File backupFile = new File(backupDir, "database.mv.db.backup_" + System.currentTimeMillis()); + Files.copy(dbFile.toPath(), backupFile.toPath()); + + // Actually delete the file + if (!dbFile.delete()) { + // If normal delete fails, try force delete on JVM exit + dbFile.deleteOnExit(); + AntiVPN.getInstance().getExecutor().log("Could not delete database file - will try again on shutdown"); + } else { + AntiVPN.getInstance().getExecutor().log("Successfully deleted incompatible database file"); + } + } catch (IOException ex) { + AntiVPN.getInstance().getExecutor().logException("Failed to handle database file", ex); + } } } diff --git a/Sponge/pom.xml b/Sponge/pom.xml index 984d2d2..4d60ce4 100644 --- a/Sponge/pom.xml +++ b/Sponge/pom.xml @@ -5,7 +5,7 @@ AntiVPN dev.brighten.antivpn - 1.9.3.1 + 1.9.4-DEV 4.0.0 @@ -28,7 +28,7 @@ dev.brighten.antivpn Common - 1.9.3.1 + 1.9.4-DEV provided diff --git a/Velocity/pom.xml b/Velocity/pom.xml index 817e5d7..dc8ef95 100644 --- a/Velocity/pom.xml +++ b/Velocity/pom.xml @@ -5,7 +5,7 @@ AntiVPN dev.brighten.antivpn - 1.9.3.1 + 1.9.4-DEV 4.0.0 @@ -33,7 +33,7 @@ dev.brighten.antivpn Common - 1.9.3.1 + 1.9.4-DEV provided diff --git a/pom.xml b/pom.xml index f48ca17..141fada 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ dev.brighten.antivpn AntiVPN pom - 1.9.3.1 + 1.9.4-DEV Common From 3aae8d8f49b223218b6d9bef917c755e0bd64ecb Mon Sep 17 00:00:00 2001 From: Dawson <30784509+funkemunky@users.noreply.github.com> Date: Tue, 22 Apr 2025 09:54:43 -0400 Subject: [PATCH 05/20] Backing up and resetting database on versioning compatibility (#61) Co-authored-by: Dawson --- Assembly/pom.xml | 2 +- Bukkit/pom.xml | 4 +- Bungee/pom.xml | 4 +- Common/pom.xml | 2 +- .../antivpn/database/sql/utils/MySQL.java | 58 ++++++++++++++++++- Sponge/pom.xml | 4 +- Velocity/pom.xml | 4 +- pom.xml | 2 +- 8 files changed, 68 insertions(+), 12 deletions(-) diff --git a/Assembly/pom.xml b/Assembly/pom.xml index dc917eb..cb132a6 100644 --- a/Assembly/pom.xml +++ b/Assembly/pom.xml @@ -5,7 +5,7 @@ AntiVPN dev.brighten.antivpn - 1.9.3.1 + 1.9.4-DEV 4.0.0 diff --git a/Bukkit/pom.xml b/Bukkit/pom.xml index c29e119..182d3d6 100644 --- a/Bukkit/pom.xml +++ b/Bukkit/pom.xml @@ -5,7 +5,7 @@ AntiVPN dev.brighten.antivpn - 1.9.3.1 + 1.9.4-DEV 4.0.0 @@ -77,7 +77,7 @@ dev.brighten.antivpn Common - 1.9.3.1 + 1.9.4-DEV provided diff --git a/Bungee/pom.xml b/Bungee/pom.xml index 298323c..c067297 100644 --- a/Bungee/pom.xml +++ b/Bungee/pom.xml @@ -5,7 +5,7 @@ AntiVPN dev.brighten.antivpn - 1.9.3.1 + 1.9.4-DEV 4.0.0 @@ -71,7 +71,7 @@ dev.brighten.antivpn Common - 1.9.3.1 + 1.9.4-DEV provided diff --git a/Common/pom.xml b/Common/pom.xml index 11471d2..44eaa80 100644 --- a/Common/pom.xml +++ b/Common/pom.xml @@ -5,7 +5,7 @@ AntiVPN dev.brighten.antivpn - 1.9.3.1 + 1.9.4-DEV 4.0.0 diff --git a/Common/src/main/java/dev/brighten/antivpn/database/sql/utils/MySQL.java b/Common/src/main/java/dev/brighten/antivpn/database/sql/utils/MySQL.java index 1a264c2..8355e2a 100644 --- a/Common/src/main/java/dev/brighten/antivpn/database/sql/utils/MySQL.java +++ b/Common/src/main/java/dev/brighten/antivpn/database/sql/utils/MySQL.java @@ -2,12 +2,17 @@ package dev.brighten.antivpn.database.sql.utils; import com.mysql.cj.jdbc.Driver; import dev.brighten.antivpn.AntiVPN; +import org.h2.jdbc.JdbcSQLFeatureNotSupportedException; +import org.h2.jdbc.JdbcSQLNonTransientConnectionException; import java.io.File; +import java.io.IOException; +import java.nio.file.Files; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.util.Properties; +import java.util.logging.Level; public class MySQL { private static Connection conn; @@ -33,12 +38,25 @@ public class MySQL { } } + private static boolean didRetry = false; + public static void initH2() { + if(didRetry) { + AntiVPN.getInstance().getExecutor().log(Level.WARNING, + "Already attempted to retry H2 connection, skipping."); + return; + } File dataFolder = new File(AntiVPN.getInstance().getPluginFolder(), "databases"); + if (!dataFolder.exists() && dataFolder.mkdirs()) { + AntiVPN.getInstance().getExecutor().log("Created database directory"); + } + + File dbFile = new File(dataFolder, "database.mv.db"); + File databaseFile = new File(dataFolder, "database"); try { conn = new NonClosableConnection(new org.h2.jdbc.JdbcConnection("jdbc:h2:file:" + - databaseFile.getAbsolutePath(), + databaseFile.getAbsolutePath(), new Properties(), AntiVPN.getInstance().getVpnConfig().getUsername(), AntiVPN.getInstance().getVpnConfig().getPassword(), false)); conn.setAutoCommit(true); @@ -46,6 +64,44 @@ public class MySQL { AntiVPN.getInstance().getExecutor().log("Connection to H2 has been established."); } catch (SQLException ex) { AntiVPN.getInstance().getExecutor().logException("H2 exception on initialize", ex); + if(ex instanceof JdbcSQLFeatureNotSupportedException + || ex instanceof JdbcSQLNonTransientConnectionException) { + AntiVPN.getInstance().getExecutor() + .log("H2 database file is incompatible with this version of AntiVPN. " + + "Backing up old database file..."); + backupOldDB(dbFile, dataFolder); + initH2(); + didRetry = true; + } else { + AntiVPN.getInstance().getExecutor().logException("Failed to load H2 database: " + ex.getCause().toString(), ex); + } + } + } + + private static void backupOldDB(File dbFile, File dataFolder) { + if (dbFile.exists()) { + try { + // Optional: Make backup first + File backupDir = new File(dataFolder, "backups"); + if(backupDir.mkdirs()) { + AntiVPN.getInstance().getExecutor().log("Created backup directory"); + } else { + AntiVPN.getInstance().getExecutor().log("Backup directory already exists"); + } + File backupFile = new File(backupDir, "database.mv.db.backup_" + System.currentTimeMillis()); + Files.copy(dbFile.toPath(), backupFile.toPath()); + + // Actually delete the file + if (!dbFile.delete()) { + // If normal delete fails, try force delete on JVM exit + dbFile.deleteOnExit(); + AntiVPN.getInstance().getExecutor().log("Could not delete database file - will try again on shutdown"); + } else { + AntiVPN.getInstance().getExecutor().log("Successfully deleted incompatible database file"); + } + } catch (IOException ex) { + AntiVPN.getInstance().getExecutor().logException("Failed to handle database file", ex); + } } } diff --git a/Sponge/pom.xml b/Sponge/pom.xml index 984d2d2..4d60ce4 100644 --- a/Sponge/pom.xml +++ b/Sponge/pom.xml @@ -5,7 +5,7 @@ AntiVPN dev.brighten.antivpn - 1.9.3.1 + 1.9.4-DEV 4.0.0 @@ -28,7 +28,7 @@ dev.brighten.antivpn Common - 1.9.3.1 + 1.9.4-DEV provided diff --git a/Velocity/pom.xml b/Velocity/pom.xml index 817e5d7..dc8ef95 100644 --- a/Velocity/pom.xml +++ b/Velocity/pom.xml @@ -5,7 +5,7 @@ AntiVPN dev.brighten.antivpn - 1.9.3.1 + 1.9.4-DEV 4.0.0 @@ -33,7 +33,7 @@ dev.brighten.antivpn Common - 1.9.3.1 + 1.9.4-DEV provided diff --git a/pom.xml b/pom.xml index f48ca17..141fada 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ dev.brighten.antivpn AntiVPN pom - 1.9.3.1 + 1.9.4-DEV Common From 2dbe465b9e1772467ee8976ad6bf2ea2a7e48c63 Mon Sep 17 00:00:00 2001 From: Dawson <30784509+funkemunky@users.noreply.github.com> Date: Tue, 22 Apr 2025 09:58:46 -0400 Subject: [PATCH 06/20] Fixing Bukkit player kicking not functioning properly (#62) * Fixing bug where player is not kicked when using proxy on Bukkit servers * Removing from PlayerLoginEvent and doing PlayerJoinEvent only instead. --------- Co-authored-by: Dawson --- Assembly/pom.xml | 8 +- Bukkit/pom.xml | 5 +- .../antivpn/bukkit/BukkitListener.java | 216 ++++++++---------- Bungee/pom.xml | 4 +- Common/pom.xml | 10 +- Common/src/main/resources/config.yml | 3 +- Sponge/pom.xml | 4 +- Velocity/pom.xml | 4 +- pom.xml | 2 +- 9 files changed, 123 insertions(+), 133 deletions(-) diff --git a/Assembly/pom.xml b/Assembly/pom.xml index cb132a6..a118190 100644 --- a/Assembly/pom.xml +++ b/Assembly/pom.xml @@ -21,7 +21,7 @@ org.apache.maven.plugins maven-shade-plugin - 3.5.2 + 3.2.4 package @@ -65,6 +65,12 @@ ${version} compile + + org.xerial + sqlite-jdbc + 3.41.2.2 + compile + dev.brighten.antivpn Bukkit diff --git a/Bukkit/pom.xml b/Bukkit/pom.xml index 182d3d6..0cc59d1 100644 --- a/Bukkit/pom.xml +++ b/Bukkit/pom.xml @@ -16,7 +16,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.13.0 + 3.7.0 8 8 @@ -26,7 +26,7 @@ org.apache.maven.plugins maven-shade-plugin - 3.5.2 + 3.2.4 package @@ -34,6 +34,7 @@ shade + true org.bstats diff --git a/Bukkit/src/main/java/dev/brighten/antivpn/bukkit/BukkitListener.java b/Bukkit/src/main/java/dev/brighten/antivpn/bukkit/BukkitListener.java index 0f2f730..8c1c105 100644 --- a/Bukkit/src/main/java/dev/brighten/antivpn/bukkit/BukkitListener.java +++ b/Bukkit/src/main/java/dev/brighten/antivpn/bukkit/BukkitListener.java @@ -11,14 +11,12 @@ import net.md_5.bungee.api.ChatColor; import org.bukkit.Bukkit; 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.PlayerJoinEvent; -import org.bukkit.event.player.PlayerLoginEvent; import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.scheduler.BukkitRunnable; -import java.net.InetAddress; -import java.net.InetSocketAddress; import java.util.UUID; import java.util.concurrent.TimeUnit; import java.util.logging.Level; @@ -56,7 +54,7 @@ public class BukkitListener extends VPNExecutor implements Listener { Bukkit.getLogger().log(Level.SEVERE, message, ex); } - @EventHandler + @EventHandler(priority = EventPriority.HIGH) public void onJoin(final PlayerJoinEvent event) { AntiVPN.getInstance().getPlayerExecutor().getPlayer(event.getPlayer().getUniqueId()) .ifPresent(player -> AntiVPN.getInstance().getDatabase().alertsState(player.getUuid(), enabled -> { @@ -67,15 +65,20 @@ public class BukkitListener extends VPNExecutor implements Listener { .getFormattedMessage(new VpnString.Var<>("state", true))); } })); - } - @EventHandler - public void onListener(final PlayerLoginEvent event) { - //If they're exempt, don't check. + String address; + + if(event.getPlayer().getAddress() != null) { + address = event.getPlayer().getAddress().getAddress().getHostAddress(); + } else { + log(Level.WARNING, "Player %s address is null! This is a bug and should be reported!", event.getPlayer().getName()); + return; + } + if(event.getPlayer().hasPermission("antivpn.bypass") //Has bypass permission || AntiVPN.getInstance().getExecutor().isWhitelisted(event.getPlayer().getUniqueId()) //Is exempt //Or has a name that starts with a certain prefix. This is for Bedrock exempting. - || AntiVPN.getInstance().getExecutor().isWhitelisted(event.getAddress().getHostAddress()) + || AntiVPN.getInstance().getExecutor().isWhitelisted(address) || AntiVPN.getInstance().getVpnConfig().getPrefixWhitelists().stream() .anyMatch(prefix -> event.getPlayer().getName().startsWith(prefix))) return; @@ -83,147 +86,124 @@ public class BukkitListener extends VPNExecutor implements Listener { VPNResponse cached = responseCache.getIfPresent(event.getPlayer().getUniqueId()); if (cached != null && cached.isProxy()) { - event.setResult(PlayerLoginEvent.Result.KICK_BANNED); - event.setKickMessage(org.bukkit.ChatColor.translateAlternateColorCodes('&', - AntiVPN.getInstance().getVpnConfig().getKickString())); + proxyKick(event.getPlayer(), cached); return; } } final Player player = event.getPlayer(); - checkIp(event.getAddress().getHostAddress(), + checkIp(address, AntiVPN.getInstance().getVpnConfig().cachedResults(), result -> { if(result.isSuccess()) { //We need to run on main thread or kicking and running commands will cause errors //If the player is whitelisted, we don't want to kick them if(AntiVPN.getInstance().getExecutor().isWhitelisted(event.getPlayer().getUniqueId())) { - log("UUID is whitelisted: %s", - event.getPlayer().getUniqueId().toString()); + log("UUID is whitelisted: %s", event.getPlayer().getUniqueId().toString()); return; } //If the IP is whitelisted, we don't want to kick them - InetSocketAddress address = event.getPlayer().getAddress(); - if (address != null){ - InetAddress address1 = address.getAddress(); - if (address1 != null && AntiVPN.getInstance().getExecutor() - .isWhitelisted(address1.getHostAddress())) { - log("IP is whitelisted: %s", - address1.getHostAddress()); - return; - } + if (AntiVPN.getInstance().getExecutor().isWhitelisted(address)) { + log("IP is whitelisted: %s", + address); + return; } // If the countryList() size is zero, no need to check. // Running country check first if(!AntiVPN.getInstance().getVpnConfig().countryList().isEmpty() - // This bit of code will decide whether or not to kick the player - // If it contains the code and it is set to whitelist, it will not kick as they are equal + // This bit of code will decide whether to kick the player + // If contains the code, and set to whitelist, it will not kick as they are equal // and vise versa. However, if the contains does not match the state, it will kick. && AntiVPN.getInstance().getVpnConfig().countryList() .contains(result.getCountryCode()) != AntiVPN.getInstance().getVpnConfig().whitelistCountries()) { - final String kickReason = AntiVPN.getInstance().getVpnConfig() - .countryVanillaKickReason(); - - // Start "online" fix - // In case the response was so fast from API the player wouldn't be "online". - event.setResult(PlayerLoginEvent.Result.KICK_BANNED); - event.setKickMessage(ChatColor - .translateAlternateColorCodes('&', - kickReason - .replace("%player%", event.getPlayer().getName()) - .replace("%country%", result.getCountryName()) - .replace("%code%", result.getCountryCode()))); - // End "online" fix - - //Using our built in kicking system if no commands are configured - if(AntiVPN.getInstance().getVpnConfig().countryKickCommands().isEmpty()) { - // Kicking our player - new BukkitRunnable() { - public void run() { - event.getPlayer().kickPlayer(ChatColor - .translateAlternateColorCodes('&', - kickReason - .replace("%player%", event.getPlayer().getName()) - .replace("%country%", result.getCountryName()) - .replace("%code%", result.getCountryCode()))); - } - }.runTask(BukkitPlugin.pluginInstance); - } else { - final String playerName = event.getPlayer().getName(); - - BukkitPlugin.pluginInstance.getPlayerCommandRunner() - .addAction(event.getPlayer().getUniqueId(), () -> { - for (String cmd : AntiVPN.getInstance().getVpnConfig().countryKickCommands()) { - final String formattedCommand = ChatColor.translateAlternateColorCodes('&', - cmd.replace("%player%", playerName) - .replace("%country%", result.getCountryName()) - .replace("%code%", result.getCountryCode())); - - // Runs our command from console - Bukkit.dispatchCommand(Bukkit.getConsoleSender(), formattedCommand); - } - }); - } + countryKick(player, result); } else if(result.isProxy()) { - - // Start "online" fix - // In case the response was so fast from API the player wouldn't be "online". - event.setResult(PlayerLoginEvent.Result.KICK_BANNED); - event.setKickMessage(ChatColor - .translateAlternateColorCodes('&', - AntiVPN.getInstance().getVpnConfig().getKickString() - .replace("%player%", event.getPlayer().getName()) - .replace("%country%", result.getCountryName()) - .replace("%code%", result.getCountryCode()))); - // End "online" fix - - if(AntiVPN.getInstance().getVpnConfig().kickPlayersOnDetect()) { - new BukkitRunnable() { - public void run() { - player.kickPlayer(org.bukkit.ChatColor.translateAlternateColorCodes('&', - AntiVPN.getInstance().getVpnConfig().getKickString())); - } - }.runTask(BukkitPlugin.pluginInstance); - } - log(Level.INFO, event.getPlayer().getName() - + " joined on a VPN/Proxy (" + result.getMethod() + ")"); - - //Ensuring the user wishes to alert to staff - if(AntiVPN.getInstance().getVpnConfig().alertToStaff()) - AntiVPN.getInstance().getPlayerExecutor().getOnlinePlayers().stream() - .filter(APIPlayer::isAlertsEnabled) - .forEach(pl -> pl.sendMessage(AntiVPN.getInstance().getVpnConfig().alertMessage() - .replace("%player%", event.getPlayer().getName()) - .replace("%reason%", result.getMethod()) - .replace("%country%", result.getCountryName()) - .replace("%city%", result.getCity()))); - - //In case the user wants to run their own commands instead of using the built in kicking - if(AntiVPN.getInstance().getVpnConfig().runCommands()) { - String playerName = event.getPlayer().getName(); - BukkitPlugin.pluginInstance.getPlayerCommandRunner() - .addAction(event.getPlayer().getUniqueId(), () -> { - for (String command : AntiVPN.getInstance().getVpnConfig().commands()) { - Bukkit.dispatchCommand(Bukkit.getConsoleSender(), - ChatColor.translateAlternateColorCodes('&', - command.replace("%player%", - playerName))); - } - }); - } - AntiVPN.getInstance().detections++; + proxyKick(player, result); } } else { log(Level.WARNING, - "The API query was not a success! " + - "You may need to upgrade your license on https://funkemunky.cc/shop"); + "The API query was not a success! " + + "You may need to upgrade your license on https://funkemunky.cc/shop"); } AntiVPN.getInstance().checked++; }); } + private void countryKick(Player player, VPNResponse result) { + final String kickReason = AntiVPN.getInstance().getVpnConfig() + .countryVanillaKickReason(); + //Using our built-in kicking system if no commands are configured + if(AntiVPN.getInstance().getVpnConfig().countryKickCommands().isEmpty()) { + // Kicking our player + new BukkitRunnable() { + public void run() { + player.kickPlayer(ChatColor + .translateAlternateColorCodes('&', + kickReason + .replace("%player%", player.getName()) + .replace("%country%", result.getCountryName()) + .replace("%code%", result.getCountryCode()))); + } + }.runTask(BukkitPlugin.pluginInstance); + } else { + final String playerName = player.getName(); + + BukkitPlugin.pluginInstance.getPlayerCommandRunner() + .addAction(player.getUniqueId(), () -> { + for (String cmd : AntiVPN.getInstance().getVpnConfig().countryKickCommands()) { + final String formattedCommand = ChatColor.translateAlternateColorCodes('&', + cmd.replace("%player%", playerName) + .replace("%country%", result.getCountryName()) + .replace("%code%", result.getCountryCode())); + + // Runs our command from console + Bukkit.dispatchCommand(Bukkit.getConsoleSender(), formattedCommand); + } + }); + } + } + + private void proxyKick(Player player, VPNResponse result) { + log(Level.INFO, player.getName() + + " joined on a VPN/Proxy (" + result.getMethod() + ")"); + + //Ensuring the user wishes to alert to staff + if(AntiVPN.getInstance().getVpnConfig().alertToStaff()) + AntiVPN.getInstance().getPlayerExecutor().getOnlinePlayers().stream() + .filter(APIPlayer::isAlertsEnabled) + .forEach(pl -> pl.sendMessage(AntiVPN.getInstance().getVpnConfig().alertMessage() + .replace("%player%", player.getName()) + .replace("%reason%", result.getMethod()) + .replace("%country%", result.getCountryName()) + .replace("%city%", result.getCity()))); + + if(AntiVPN.getInstance().getVpnConfig().kickPlayersOnDetect()) { + new BukkitRunnable() { + public void run() { + player.kickPlayer(org.bukkit.ChatColor.translateAlternateColorCodes('&', + AntiVPN.getInstance().getVpnConfig().getKickString())); + } + }.runTask(BukkitPlugin.pluginInstance); + } else { + //In case the user wants to run their own commands instead of using the built-in kicking + if(AntiVPN.getInstance().getVpnConfig().runCommands()) { + String playerName = player.getName(); + BukkitPlugin.pluginInstance.getPlayerCommandRunner() + .addAction(player.getUniqueId(), () -> { + for (String command : AntiVPN.getInstance().getVpnConfig().commands()) { + Bukkit.dispatchCommand(Bukkit.getConsoleSender(), + ChatColor.translateAlternateColorCodes('&', + command.replace("%player%", + playerName))); + } + }); + } + AntiVPN.getInstance().detections++; + } + } + @EventHandler public void onQuit(PlayerQuitEvent event) { AntiVPN.getInstance().getPlayerExecutor().unloadPlayer(event.getPlayer().getUniqueId()); diff --git a/Bungee/pom.xml b/Bungee/pom.xml index c067297..06e4cff 100644 --- a/Bungee/pom.xml +++ b/Bungee/pom.xml @@ -16,7 +16,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.13.0 + 3.7.0 8 8 @@ -26,7 +26,7 @@ org.apache.maven.plugins maven-shade-plugin - 3.5.2 + 3.1.0 diff --git a/Common/pom.xml b/Common/pom.xml index 44eaa80..82f313f 100644 --- a/Common/pom.xml +++ b/Common/pom.xml @@ -21,7 +21,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.13.0 + 3.10.1 8 8 @@ -38,7 +38,7 @@ org.apache.maven.plugins maven-shade-plugin - 3.5.2 + 3.4.1 package @@ -46,7 +46,7 @@ shade - false + true org.yaml.snakeyaml @@ -86,11 +86,13 @@ com.mysql mysql-connector-j 9.1.0 + jar + compile com.h2database h2 - 2.2.224 + 2.2.220 compile diff --git a/Common/src/main/resources/config.yml b/Common/src/main/resources/config.yml index a28421a..4827f1e 100644 --- a/Common/src/main/resources/config.yml +++ b/Common/src/main/resources/config.yml @@ -11,7 +11,8 @@ cachedResults: true # All players with any of the following characters in the beginning of their name will be whitelisted # This is a useful feature for servers that allow players through Geyser and their IPs are not forwarded, causing # players to be removed falsely for use of proxy. -prefixWhitelists: [] +prefixWhitelists: + - "*" # Configure your database here. database: # Enable to cache queries and save alerts state beyond restarts diff --git a/Sponge/pom.xml b/Sponge/pom.xml index 4d60ce4..de32988 100644 --- a/Sponge/pom.xml +++ b/Sponge/pom.xml @@ -34,8 +34,8 @@ - 8 - 8 + 17 + 17 \ No newline at end of file diff --git a/Velocity/pom.xml b/Velocity/pom.xml index dc8ef95..045296f 100644 --- a/Velocity/pom.xml +++ b/Velocity/pom.xml @@ -49,7 +49,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.13.0 + 3.7.0 8 8 @@ -59,7 +59,7 @@ org.apache.maven.plugins maven-shade-plugin - 3.5.2 + 3.1.0 diff --git a/pom.xml b/pom.xml index 141fada..1bab415 100644 --- a/pom.xml +++ b/pom.xml @@ -28,7 +28,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.13.0 + 3.7.0 8 8 From 2b7f043eb930ec33fc052cc554615479ce422e1b Mon Sep 17 00:00:00 2001 From: Dawson <30784509+funkemunky@users.noreply.github.com> Date: Tue, 22 Apr 2025 12:07:30 -0400 Subject: [PATCH 07/20] Improving shutdown and error handling (#63) Co-authored-by: Dawson --- .../antivpn/bukkit/BukkitListener.java | 9 +- .../antivpn/bungee/BungeeListener.java | 14 +- .../java/dev/brighten/antivpn/AntiVPN.java | 13 +- .../dev/brighten/antivpn/api/VPNExecutor.java | 4 +- .../antivpn/velocity/VelocityListener.java | 291 +++++++++--------- .../antivpn/velocity/VelocityPlugin.java | 13 +- 6 files changed, 194 insertions(+), 150 deletions(-) diff --git a/Bukkit/src/main/java/dev/brighten/antivpn/bukkit/BukkitListener.java b/Bukkit/src/main/java/dev/brighten/antivpn/bukkit/BukkitListener.java index 8c1c105..8736537 100644 --- a/Bukkit/src/main/java/dev/brighten/antivpn/bukkit/BukkitListener.java +++ b/Bukkit/src/main/java/dev/brighten/antivpn/bukkit/BukkitListener.java @@ -12,6 +12,7 @@ import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; +import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerQuitEvent; @@ -35,7 +36,7 @@ public class BukkitListener extends VPNExecutor implements Listener { } @Override - public void shutdown() { + public void onShutdown() { } @@ -54,6 +55,12 @@ public class BukkitListener extends VPNExecutor implements Listener { Bukkit.getLogger().log(Level.SEVERE, message, ex); } + @Override + public void disablePlugin() { + HandlerList.unregisterAll(this); + BukkitPlugin.pluginInstance.getServer().getPluginManager().disablePlugin(BukkitPlugin.pluginInstance); + } + @EventHandler(priority = EventPriority.HIGH) public void onJoin(final PlayerJoinEvent event) { AntiVPN.getInstance().getPlayerExecutor().getPlayer(event.getPlayer().getUniqueId()) diff --git a/Bungee/src/main/java/dev/brighten/antivpn/bungee/BungeeListener.java b/Bungee/src/main/java/dev/brighten/antivpn/bungee/BungeeListener.java index 826d04e..30d6250 100644 --- a/Bungee/src/main/java/dev/brighten/antivpn/bungee/BungeeListener.java +++ b/Bungee/src/main/java/dev/brighten/antivpn/bungee/BungeeListener.java @@ -37,12 +37,11 @@ public class BungeeListener extends VPNExecutor implements Listener { } @Override - public void shutdown() { + public void onShutdown() { if(cacheResetTask != null) { cacheResetTask.cancel(); cacheResetTask = null; } - threadExecutor.shutdown(); BungeePlugin.pluginInstance.getProxy().getPluginManager().unregisterListener(this); } @@ -61,6 +60,17 @@ public class BungeeListener extends VPNExecutor implements Listener { BungeeCord.getInstance().getLogger().log(Level.SEVERE, message, ex); } + @Override + public void disablePlugin() { + BungeeCord.getInstance().getPluginManager().unregisterListeners(BungeePlugin.pluginInstance); + if(cacheResetTask != null) { + cacheResetTask.cancel(); + cacheResetTask = null; + } + BungeeCord.getInstance().getPluginManager().unregisterCommands(BungeePlugin.pluginInstance); + BungeePlugin.pluginInstance.onDisable(); + } + @EventHandler(priority = EventPriority.LOWEST) public void onListener(final PreLoginEvent event) { if(!responseCache.asMap().containsKey(event.getConnection().getUniqueId())) return; diff --git a/Common/src/main/java/dev/brighten/antivpn/AntiVPN.java b/Common/src/main/java/dev/brighten/antivpn/AntiVPN.java index 2bc20f4..9446bf8 100644 --- a/Common/src/main/java/dev/brighten/antivpn/AntiVPN.java +++ b/Common/src/main/java/dev/brighten/antivpn/AntiVPN.java @@ -54,13 +54,17 @@ public class AntiVPN { try { File configFile = new File(pluginFolder, "config.yml"); if(!configFile.exists()){ - configFile.getParentFile().mkdirs(); + if(configFile.getParentFile().mkdirs()) { + AntiVPN.getInstance().getExecutor().log("Created plugin folder!"); + } MiscUtils.copy(INSTANCE.getResource( "config.yml"), configFile); } INSTANCE.config = ConfigurationProvider.getProvider(YamlConfiguration.class) .load(configFile); } catch (IOException e) { - e.printStackTrace(); + AntiVPN.getInstance().getExecutor().logException("Could not load config.yml, plugin disabling...", e); + executor.disablePlugin(); + return; } INSTANCE.vpnConfig = new VPNConfig(); @@ -139,7 +143,8 @@ public class AntiVPN { } public void stop() { - executor.shutdown(); + executor.onShutdown(); + VPNExecutor.threadExecutor.shutdown(); if(database != null) database.shutdown(); } @@ -188,7 +193,7 @@ public class AntiVPN { ConfigurationProvider.getProvider(YamlConfiguration.class) .save(getConfig(), new File(pluginFolder.getPath() + File.separator + "config.yml")); } catch (IOException e) { - e.printStackTrace(); + AntiVPN.getInstance().getExecutor().logException(e); } } diff --git a/Common/src/main/java/dev/brighten/antivpn/api/VPNExecutor.java b/Common/src/main/java/dev/brighten/antivpn/api/VPNExecutor.java index 84d89f1..1ae8cd7 100644 --- a/Common/src/main/java/dev/brighten/antivpn/api/VPNExecutor.java +++ b/Common/src/main/java/dev/brighten/antivpn/api/VPNExecutor.java @@ -32,7 +32,7 @@ public abstract class VPNExecutor { public abstract void registerListeners(); - public abstract void shutdown(); + public abstract void onShutdown(); public abstract void log(Level level, String log, Object... objects); @@ -97,4 +97,6 @@ public abstract class VPNExecutor { } } } + + public abstract void disablePlugin(); } diff --git a/Velocity/src/main/java/dev/brighten/antivpn/velocity/VelocityListener.java b/Velocity/src/main/java/dev/brighten/antivpn/velocity/VelocityListener.java index ea8b57a..04ab467 100644 --- a/Velocity/src/main/java/dev/brighten/antivpn/velocity/VelocityListener.java +++ b/Velocity/src/main/java/dev/brighten/antivpn/velocity/VelocityListener.java @@ -2,6 +2,7 @@ package dev.brighten.antivpn.velocity; import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; +import com.velocitypowered.api.event.EventHandler; import com.velocitypowered.api.event.ResultedEvent; import com.velocitypowered.api.event.connection.DisconnectEvent; import com.velocitypowered.api.event.connection.LoginEvent; @@ -26,6 +27,145 @@ public class VelocityListener extends VPNExecutor { .maximumSize(2000) .build(); + private final EventHandler loginEvent = event -> { + if (event.getResult().isAllowed()) { + if (event.getPlayer().hasPermission("antivpn.bypass") //Has bypass permission + //Is exempt + || AntiVPN.getInstance().getExecutor().isWhitelisted(event.getPlayer().getUniqueId()) + //Or has a name that starts with a certain prefix. This is for Bedrock exempting. + || AntiVPN.getInstance().getExecutor().isWhitelisted(event.getPlayer().getRemoteAddress() + .getAddress().getHostAddress()) + || AntiVPN.getInstance().getVpnConfig().getPrefixWhitelists().stream() + .anyMatch(prefix -> event.getPlayer().getUsername().startsWith(prefix))) return; + + if(responseCache.asMap().containsKey(event.getPlayer().getUniqueId())) { + VPNResponse cached = responseCache.getIfPresent(event.getPlayer().getUniqueId()); + + if (cached != null && cached.isProxy()) { + event.setResult(ResultedEvent.ComponentResult.denied(Component.text("No"))); + return; + } + } + + checkIp(event.getPlayer().getRemoteAddress().getAddress().getHostAddress(), + AntiVPN.getInstance().getVpnConfig().cachedResults(), result -> { + if (result.isSuccess()) { + // If the countryList() size is zero, no need to check. + // Running country check first + if (!AntiVPN.getInstance().getVpnConfig().countryList().isEmpty() + && !(AntiVPN.getInstance().getExecutor() + .isWhitelisted(event.getPlayer().getUniqueId()) //Is exempt + //Or has a name that starts with a certain prefix. This is for Bedrock exempting. + || AntiVPN.getInstance().getExecutor().isWhitelisted(event.getPlayer() + .getRemoteAddress().getAddress().getHostAddress())) + // This bit of code will decide whether or not to kick the player + // If it contains the code and it is set to whitelist, it will not kick + // as they are equal and vise versa. However, if the contains does not match + // the state, it will kick. + && AntiVPN.getInstance().getVpnConfig().countryList() + .contains(result.getCountryCode()) + != AntiVPN.getInstance().getVpnConfig().whitelistCountries()) { + //Using our built in kicking system if no commands are configured + if (AntiVPN.getInstance().getVpnConfig().countryKickCommands().isEmpty()) { + final String kickReason = AntiVPN.getInstance().getVpnConfig() + .countryVanillaKickReason(); + // Kicking our player + event.setResult(ResultedEvent.ComponentResult.denied(LegacyComponentSerializer.builder() + .character('&') + .build().deserialize(kickReason + .replace("%player%", event.getPlayer().getUsername()) + .replace("%country%", result.getCountryName()) + .replace("%code%", result.getCountryCode())))); + VelocityPlugin.INSTANCE.getServer().getScheduler() + .buildTask(VelocityPlugin.INSTANCE, () -> + event.getPlayer().disconnect(LegacyComponentSerializer.builder() + .character('&') + .build().deserialize(kickReason + .replace("%player%", event.getPlayer().getUsername()) + .replace("%country%", result.getCountryName()) + .replace("%code%", result.getCountryCode())))); + } else { + for (String cmd : AntiVPN.getInstance().getVpnConfig().countryKickCommands()) { + final String formattedCommand = StringUtils + .translateAlternateColorCodes('&', + cmd.replace("%player%", + event.getPlayer().getUsername()) + .replace("%country%", result.getCountryName()) + .replace("%code%", result.getCountryCode())); + // Running the command from console + VelocityPlugin.INSTANCE.getServer().getCommandManager() + .executeAsync(VelocityPlugin.INSTANCE.getServer() + .getConsoleCommandSource(), + StringUtils.translateAlternateColorCodes('&', + formattedCommand)); + } + } + } else if (result.isProxy()) { + if (AntiVPN.getInstance().getVpnConfig().kickPlayersOnDetect()) { + // Delay code execution + event.setResult(ResultedEvent.ComponentResult.denied(LegacyComponentSerializer.builder() + .character('&') + .build().deserialize(AntiVPN.getInstance().getVpnConfig() + .getKickString() + .replace("%player%", event.getPlayer().getUsername()) + .replace("%country%", result.getCountryName()) + .replace("%code%", result.getCountryCode())))); + + VelocityPlugin.INSTANCE.getServer().getScheduler() + .buildTask(VelocityPlugin.INSTANCE, () -> + event.getPlayer().disconnect(LegacyComponentSerializer.builder() + .character('&') + .build().deserialize(AntiVPN.getInstance().getVpnConfig() + .getKickString() + .replace("%player%", event.getPlayer().getUsername()) + .replace("%country%", result.getCountryName()) + .replace("%code%", result.getCountryCode())))) + .delay(1, TimeUnit.SECONDS).schedule(); + } + VelocityPlugin.INSTANCE.getLogger().info(event.getPlayer().getUsername() + + " joined on a VPN/Proxy (" + result.getMethod() + ")"); + //Ensuring the user wishes to alert to staff + if (AntiVPN.getInstance().getVpnConfig().alertToStaff()) + AntiVPN.getInstance().getPlayerExecutor().getOnlinePlayers().stream() + .filter(APIPlayer::isAlertsEnabled) + .forEach(pl -> + pl.sendMessage(AntiVPN.getInstance().getVpnConfig() + .alertMessage() + .replace("%player%", + event.getPlayer().getUsername()) + .replace("%reason%", + result.getMethod()) + .replace("%country%", + result.getCountryName()) + .replace("%city%", + result.getCity()))); + + //In case the user wants to run their own commands instead of using the + // built in kicking + if (AntiVPN.getInstance().getVpnConfig().runCommands()) { + for (String command : AntiVPN.getInstance().getVpnConfig().commands()) { + VelocityPlugin.INSTANCE.getServer().getCommandManager() + .executeAsync(VelocityPlugin.INSTANCE.getServer() + .getConsoleCommandSource(), + StringUtils.translateAlternateColorCodes('&', + command.replace("%player%", + event.getPlayer().getUsername()))); + } + } + AntiVPN.getInstance().detections++; + } + } else { + VelocityPlugin.INSTANCE.getLogger() + .log(Level.WARNING, + "The API query was not a success! " + + "You may need to upgrade your license on " + + "https://funkemunky.cc/shop"); + } + AntiVPN.getInstance().checked++; + }); + } + }; + @Override public void registerListeners() { @@ -33,156 +173,19 @@ public class VelocityListener extends VPNExecutor { .register(VelocityPlugin.INSTANCE, this); VelocityPlugin.INSTANCE.getServer().getEventManager().register(VelocityPlugin.INSTANCE, DisconnectEvent.class, - event -> AntiVPN.getInstance().getPlayerExecutor().unloadPlayer(event.getPlayer().getUniqueId())); + event -> AntiVPN.getInstance().getPlayerExecutor() + .unloadPlayer(event.getPlayer().getUniqueId())); VelocityPlugin.INSTANCE.getServer().getEventManager().register(VelocityPlugin.INSTANCE, LoginEvent.class, - event -> { - if (event.getResult().isAllowed()) { - if (event.getPlayer().hasPermission("antivpn.bypass") //Has bypass permission - //Is exempt - || AntiVPN.getInstance().getExecutor().isWhitelisted(event.getPlayer().getUniqueId()) - //Or has a name that starts with a certain prefix. This is for Bedrock exempting. - || AntiVPN.getInstance().getExecutor().isWhitelisted(event.getPlayer().getRemoteAddress() - .getAddress().getHostAddress()) - || AntiVPN.getInstance().getVpnConfig().getPrefixWhitelists().stream() - .anyMatch(prefix -> event.getPlayer().getUsername().startsWith(prefix))) return; - - if(responseCache.asMap().containsKey(event.getPlayer().getUniqueId())) { - VPNResponse cached = responseCache.getIfPresent(event.getPlayer().getUniqueId()); - - if (cached != null && cached.isProxy()) { - event.setResult(ResultedEvent.ComponentResult.denied(Component.text("No"))); - return; - } - } - - checkIp(event.getPlayer().getRemoteAddress().getAddress().getHostAddress(), - AntiVPN.getInstance().getVpnConfig().cachedResults(), result -> { - if (result.isSuccess()) { - // If the countryList() size is zero, no need to check. - // Running country check first - if (!AntiVPN.getInstance().getVpnConfig().countryList().isEmpty() - && !(AntiVPN.getInstance().getExecutor() - .isWhitelisted(event.getPlayer().getUniqueId()) //Is exempt - //Or has a name that starts with a certain prefix. This is for Bedrock exempting. - || AntiVPN.getInstance().getExecutor().isWhitelisted(event.getPlayer() - .getRemoteAddress().getAddress().getHostAddress())) - // This bit of code will decide whether or not to kick the player - // If it contains the code and it is set to whitelist, it will not kick - // as they are equal and vise versa. However, if the contains does not match - // the state, it will kick. - && AntiVPN.getInstance().getVpnConfig().countryList() - .contains(result.getCountryCode()) - != AntiVPN.getInstance().getVpnConfig().whitelistCountries()) { - //Using our built in kicking system if no commands are configured - if (AntiVPN.getInstance().getVpnConfig().countryKickCommands().isEmpty()) { - final String kickReason = AntiVPN.getInstance().getVpnConfig() - .countryVanillaKickReason(); - // Kicking our player - event.setResult(ResultedEvent.ComponentResult.denied(LegacyComponentSerializer.builder() - .character('&') - .build().deserialize(kickReason - .replace("%player%", event.getPlayer().getUsername()) - .replace("%country%", result.getCountryName()) - .replace("%code%", result.getCountryCode())))); - VelocityPlugin.INSTANCE.getServer().getScheduler() - .buildTask(VelocityPlugin.INSTANCE, () -> - event.getPlayer().disconnect(LegacyComponentSerializer.builder() - .character('&') - .build().deserialize(kickReason - .replace("%player%", event.getPlayer().getUsername()) - .replace("%country%", result.getCountryName()) - .replace("%code%", result.getCountryCode())))); - } else { - for (String cmd : AntiVPN.getInstance().getVpnConfig().countryKickCommands()) { - final String formattedCommand = StringUtils - .translateAlternateColorCodes('&', - cmd.replace("%player%", - event.getPlayer().getUsername()) - .replace("%country%", result.getCountryName()) - .replace("%code%", result.getCountryCode())); - // Running the command from console - VelocityPlugin.INSTANCE.getServer().getCommandManager() - .executeAsync(VelocityPlugin.INSTANCE.getServer() - .getConsoleCommandSource(), - StringUtils.translateAlternateColorCodes('&', - formattedCommand)); - } - } - } else if (result.isProxy()) { - if (AntiVPN.getInstance().getVpnConfig().kickPlayersOnDetect()) { - // Delay code execution - event.setResult(ResultedEvent.ComponentResult.denied(LegacyComponentSerializer.builder() - .character('&') - .build().deserialize(AntiVPN.getInstance().getVpnConfig() - .getKickString() - .replace("%player%", event.getPlayer().getUsername()) - .replace("%country%", result.getCountryName()) - .replace("%code%", result.getCountryCode())))); - - VelocityPlugin.INSTANCE.getServer().getScheduler() - .buildTask(VelocityPlugin.INSTANCE, () -> - event.getPlayer().disconnect(LegacyComponentSerializer.builder() - .character('&') - .build().deserialize(AntiVPN.getInstance().getVpnConfig() - .getKickString() - .replace("%player%", event.getPlayer().getUsername()) - .replace("%country%", result.getCountryName()) - .replace("%code%", result.getCountryCode())))) - .delay(1, TimeUnit.SECONDS).schedule(); - } - VelocityPlugin.INSTANCE.getLogger().info(event.getPlayer().getUsername() - + " joined on a VPN/Proxy (" + result.getMethod() + ")"); - //Ensuring the user wishes to alert to staff - if (AntiVPN.getInstance().getVpnConfig().alertToStaff()) - AntiVPN.getInstance().getPlayerExecutor().getOnlinePlayers().stream() - .filter(APIPlayer::isAlertsEnabled) - .forEach(pl -> - pl.sendMessage(AntiVPN.getInstance().getVpnConfig() - .alertMessage() - .replace("%player%", - event.getPlayer().getUsername()) - .replace("%reason%", - result.getMethod()) - .replace("%country%", - result.getCountryName()) - .replace("%city%", - result.getCity()))); - - //In case the user wants to run their own commands instead of using the - // built in kicking - if (AntiVPN.getInstance().getVpnConfig().runCommands()) { - for (String command : AntiVPN.getInstance().getVpnConfig().commands()) { - VelocityPlugin.INSTANCE.getServer().getCommandManager() - .executeAsync(VelocityPlugin.INSTANCE.getServer() - .getConsoleCommandSource(), - StringUtils.translateAlternateColorCodes('&', - command.replace("%player%", - event.getPlayer().getUsername()))); - } - } - AntiVPN.getInstance().detections++; - } - } else { - VelocityPlugin.INSTANCE.getLogger() - .log(Level.WARNING, - "The API query was not a success! " + - "You may need to upgrade your license on " + - "https://funkemunky.cc/shop"); - } - AntiVPN.getInstance().checked++; - }); - } - }); + loginEvent); } @Override - public void shutdown() { + public void onShutdown() { if (cacheResetTask != null) { cacheResetTask.cancel(); cacheResetTask = null; } - threadExecutor.shutdown(); VelocityPlugin.INSTANCE.getServer().getEventManager().unregisterListener(VelocityPlugin.INSTANCE, this); } @@ -200,4 +203,10 @@ public class VelocityListener extends VPNExecutor { public void logException(String message, Exception ex) { VelocityPlugin.INSTANCE.getLogger().log(Level.SEVERE, message, ex); } + + @Override + public void disablePlugin() { + VelocityPlugin.INSTANCE.getServer().getEventManager().unregisterListener(VelocityPlugin.INSTANCE, this); + VelocityPlugin.INSTANCE.getServer().getCommandManager().unregister("antivpn"); + } } diff --git a/Velocity/src/main/java/dev/brighten/antivpn/velocity/VelocityPlugin.java b/Velocity/src/main/java/dev/brighten/antivpn/velocity/VelocityPlugin.java index afecfa9..33e8e89 100644 --- a/Velocity/src/main/java/dev/brighten/antivpn/velocity/VelocityPlugin.java +++ b/Velocity/src/main/java/dev/brighten/antivpn/velocity/VelocityPlugin.java @@ -3,6 +3,7 @@ package dev.brighten.antivpn.velocity; import com.google.inject.Inject; import com.velocitypowered.api.event.Subscribe; import com.velocitypowered.api.event.proxy.ProxyInitializeEvent; +import com.velocitypowered.api.event.proxy.ProxyShutdownEvent; import com.velocitypowered.api.plugin.Plugin; import com.velocitypowered.api.plugin.annotation.DataDirectory; import com.velocitypowered.api.proxy.ProxyServer; @@ -12,6 +13,7 @@ import dev.brighten.antivpn.velocity.command.VelocityCommand; import lombok.Getter; import org.bstats.velocity.Metrics; +import javax.annotation.Nullable; import java.nio.file.Path; import java.util.logging.Logger; @@ -24,6 +26,9 @@ public class VelocityPlugin { private final Metrics.Factory metricsFactory; private final Path configDir; + @Nullable + private Metrics metrics; + public static VelocityPlugin INSTANCE; @Inject @@ -45,7 +50,7 @@ public class VelocityPlugin { if(AntiVPN.getInstance().getVpnConfig().metrics()) { logger.info("Starting metrics..."); - Metrics metrics = metricsFactory.make(this, 12791); + metrics = metricsFactory.make(this, 12791); } logger.info("Registering commands..."); @@ -54,4 +59,10 @@ public class VelocityPlugin { .aliases(command.aliases()).build(), new VelocityCommand(command)); } } + + @Subscribe + public void onShutdown(ProxyShutdownEvent event) { + AntiVPN.getInstance().stop(); + INSTANCE = null; + } } From ea33a34b3d3193893aeb52bbbba626f00d1eca1c Mon Sep 17 00:00:00 2001 From: Dawson <30784509+funkemunky@users.noreply.github.com> Date: Mon, 12 May 2025 11:20:23 -0400 Subject: [PATCH 08/20] Shrinking Jar File Size Again (#64) * Adding back dynamic library support and adding some metrics, velocity first * Removing guava, using caffiene instead * Merge cleanup * Maybe this will get caches working properly now? * Refactored to be more clean and reliable * Fixing bungee compile --------- Co-authored-by: Dawson --- .github/workflows/maven.yml | 15 +- Bukkit/pom.xml | 23 +- .../antivpn/bukkit/BukkitListener.java | 82 ++-- .../brighten/antivpn/bukkit/BukkitPlugin.java | 45 ++- Bungee/pom.xml | 23 +- .../antivpn/bungee/BungeeListener.java | 186 +++++---- .../antivpn/bungee/BungeePlayerExecutor.java | 7 +- .../brighten/antivpn/bungee/BungeePlugin.java | 38 +- Common/pom.xml | 91 ++++- .../java/dev/brighten/antivpn/AntiVPN.java | 35 +- .../dev/brighten/antivpn/api/VPNExecutor.java | 75 ++-- .../antivpn/command/impl/LookupCommand.java | 41 +- .../antivpn/database/local/H2VPN.java | 55 +-- .../antivpn/database/mongo/MongoVPN.java | 58 +-- .../antivpn/database/sql/MySqlVPN.java | 113 +++--- .../antivpn/depends/LibraryLoader.java | 365 ++++++++++++++++++ .../antivpn/depends/MavenLibraries.java | 40 ++ .../antivpn/depends/MavenLibrary.java | 69 ++++ .../brighten/antivpn/depends/Relocate.java | 14 + .../brighten/antivpn/depends/Repository.java | 45 +++ .../antivpn/depends/URLClassLoaderAccess.java | 175 +++++++++ .../utils/ExtraObjectsMethodsForWeb.java | 21 + .../antivpn/utils/NonnullByDefault.java | 36 ++ .../brighten/antivpn/utils/NullnessCasts.java | 53 +++ .../brighten/antivpn/utils/Preconditions.java | 244 ++++++++++++ .../dev/brighten/antivpn/utils/Supplier.java | 11 + .../dev/brighten/antivpn/utils/Suppliers.java | 143 +++++++ {Assembly => Universal}/pom.xml | 89 +++-- Velocity/pom.xml | 12 +- .../antivpn/velocity/VelocityListener.java | 293 ++++++-------- .../antivpn/velocity/VelocityPlugin.java | 37 +- pom.xml | 14 +- 32 files changed, 1942 insertions(+), 606 deletions(-) create mode 100644 Common/src/main/java/dev/brighten/antivpn/depends/LibraryLoader.java create mode 100644 Common/src/main/java/dev/brighten/antivpn/depends/MavenLibraries.java create mode 100644 Common/src/main/java/dev/brighten/antivpn/depends/MavenLibrary.java create mode 100644 Common/src/main/java/dev/brighten/antivpn/depends/Relocate.java create mode 100644 Common/src/main/java/dev/brighten/antivpn/depends/Repository.java create mode 100644 Common/src/main/java/dev/brighten/antivpn/depends/URLClassLoaderAccess.java create mode 100644 Common/src/main/java/dev/brighten/antivpn/utils/ExtraObjectsMethodsForWeb.java create mode 100644 Common/src/main/java/dev/brighten/antivpn/utils/NonnullByDefault.java create mode 100644 Common/src/main/java/dev/brighten/antivpn/utils/NullnessCasts.java create mode 100644 Common/src/main/java/dev/brighten/antivpn/utils/Preconditions.java create mode 100644 Common/src/main/java/dev/brighten/antivpn/utils/Supplier.java create mode 100644 Common/src/main/java/dev/brighten/antivpn/utils/Suppliers.java rename {Assembly => Universal}/pom.xml (58%) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 640dd04..f4dd1cb 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -7,16 +7,21 @@ on: jobs: build: + name: Build and Test runs-on: ubuntu-latest - + steps: - uses: actions/checkout@v4 - - name: Set up JDK 17.0.2 + - name: Set up JDK 17 uses: actions/setup-java@v4 with: - java-version: 17.0 + java-version: '17' distribution: 'zulu' - cache: 'maven' + cache: maven + - name: Set up Maven + uses: stCarolas/setup-maven@v5 + with: + maven-version: 3.9.6 - name: Compile run: mvn -B package --file pom.xml env: @@ -25,4 +30,4 @@ jobs: uses: actions/upload-artifact@v4 with: name: AntiVPN - path: Assembly/target/Assembly-*.jar + path: Universal/target/AntiVPN-*.jar diff --git a/Bukkit/pom.xml b/Bukkit/pom.xml index 0cc59d1..cda1424 100644 --- a/Bukkit/pom.xml +++ b/Bukkit/pom.xml @@ -16,17 +16,17 @@ org.apache.maven.plugins maven-compiler-plugin - 3.7.0 + 3.13.0 - 8 - 8 + 17 + 17 -XDignore.symbol.file org.apache.maven.plugins maven-shade-plugin - 3.2.4 + 3.6.0 package @@ -45,10 +45,6 @@ org.yaml.snakeyaml dev.brighten.antivpn.shaded.org.yaml.snakeyaml - - com.google.common - dev.brighten.antivpn.shaded.com.google.common - @@ -64,10 +60,17 @@ - 8 - 8 + 17 + 17 + + + spigot-repo + https://hub.spigotmc.org/nexus/content/repositories/snapshots/ + + + org.spigotmc diff --git a/Bukkit/src/main/java/dev/brighten/antivpn/bukkit/BukkitListener.java b/Bukkit/src/main/java/dev/brighten/antivpn/bukkit/BukkitListener.java index 8736537..1ded919 100644 --- a/Bukkit/src/main/java/dev/brighten/antivpn/bukkit/BukkitListener.java +++ b/Bukkit/src/main/java/dev/brighten/antivpn/bukkit/BukkitListener.java @@ -1,7 +1,7 @@ package dev.brighten.antivpn.bukkit; -import com.google.common.cache.Cache; -import com.google.common.cache.CacheBuilder; +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; import dev.brighten.antivpn.AntiVPN; import dev.brighten.antivpn.api.APIPlayer; import dev.brighten.antivpn.api.VPNExecutor; @@ -24,7 +24,7 @@ import java.util.logging.Level; @SuppressWarnings("unchecked") public class BukkitListener extends VPNExecutor implements Listener { - private final Cache responseCache = CacheBuilder.newBuilder() + private final Cache responseCache = Caffeine.newBuilder() .expireAfterWrite(5, TimeUnit.MINUTES) .maximumSize(2000) .build(); @@ -35,11 +35,6 @@ public class BukkitListener extends VPNExecutor implements Listener { .registerEvents(this, BukkitPlugin.pluginInstance); } - @Override - public void onShutdown() { - - } - @Override public void log(Level level, String log, Object... objects) { Bukkit.getLogger().log(level, String.format(log, objects)); @@ -51,7 +46,7 @@ public class BukkitListener extends VPNExecutor implements Listener { } @Override - public void logException(String message, Exception ex) { + public void logException(String message, Throwable ex) { Bukkit.getLogger().log(Level.SEVERE, message, ex); } @@ -99,43 +94,42 @@ public class BukkitListener extends VPNExecutor implements Listener { } final Player player = event.getPlayer(); - checkIp(address, - AntiVPN.getInstance().getVpnConfig().cachedResults(), result -> { - if(result.isSuccess()) { - //We need to run on main thread or kicking and running commands will cause errors - //If the player is whitelisted, we don't want to kick them - if(AntiVPN.getInstance().getExecutor().isWhitelisted(event.getPlayer().getUniqueId())) { - log("UUID is whitelisted: %s", event.getPlayer().getUniqueId().toString()); - return; - } + checkIp(address).thenAccept(result -> { + if(result.isSuccess()) { + //We need to run on main thread or kicking and running commands will cause errors + //If the player is whitelisted, we don't want to kick them + if(AntiVPN.getInstance().getExecutor().isWhitelisted(event.getPlayer().getUniqueId())) { + log("UUID is whitelisted: %s", event.getPlayer().getUniqueId().toString()); + return; + } - //If the IP is whitelisted, we don't want to kick them - if (AntiVPN.getInstance().getExecutor().isWhitelisted(address)) { - log("IP is whitelisted: %s", - address); - return; - } + //If the IP is whitelisted, we don't want to kick them + if (AntiVPN.getInstance().getExecutor().isWhitelisted(address)) { + log("IP is whitelisted: %s", + address); + return; + } - // If the countryList() size is zero, no need to check. - // Running country check first - if(!AntiVPN.getInstance().getVpnConfig().countryList().isEmpty() - // This bit of code will decide whether to kick the player - // If contains the code, and set to whitelist, it will not kick as they are equal - // and vise versa. However, if the contains does not match the state, it will kick. - && AntiVPN.getInstance().getVpnConfig().countryList() - .contains(result.getCountryCode()) - != AntiVPN.getInstance().getVpnConfig().whitelistCountries()) { - countryKick(player, result); - } else if(result.isProxy()) { - proxyKick(player, result); - } - } else { - log(Level.WARNING, - "The API query was not a success! " + - "You may need to upgrade your license on https://funkemunky.cc/shop"); - } - AntiVPN.getInstance().checked++; - }); + // If the countryList() size is zero, no need to check. + // Running country check first + if(!AntiVPN.getInstance().getVpnConfig().countryList().isEmpty() + // This bit of code will decide whether to kick the player + // If contains the code, and set to whitelist, it will not kick as they are equal + // and vise versa. However, if the contains does not match the state, it will kick. + && AntiVPN.getInstance().getVpnConfig().countryList() + .contains(result.getCountryCode()) + != AntiVPN.getInstance().getVpnConfig().whitelistCountries()) { + countryKick(player, result); + } else if(result.isProxy()) { + proxyKick(player, result); + } + } else { + log(Level.WARNING, + "The API query was not a success! " + + "You may need to upgrade your license on https://funkemunky.cc/shop"); + } + AntiVPN.getInstance().checked++; + }); } private void countryKick(Player player, VPNResponse result) { diff --git a/Bukkit/src/main/java/dev/brighten/antivpn/bukkit/BukkitPlugin.java b/Bukkit/src/main/java/dev/brighten/antivpn/bukkit/BukkitPlugin.java index 2b67350..fcd48a0 100644 --- a/Bukkit/src/main/java/dev/brighten/antivpn/bukkit/BukkitPlugin.java +++ b/Bukkit/src/main/java/dev/brighten/antivpn/bukkit/BukkitPlugin.java @@ -3,9 +3,13 @@ package dev.brighten.antivpn.bukkit; import dev.brighten.antivpn.AntiVPN; import dev.brighten.antivpn.bukkit.command.BukkitCommand; import dev.brighten.antivpn.command.Command; +import dev.brighten.antivpn.database.VPNDatabase; +import dev.brighten.antivpn.database.local.H2VPN; +import dev.brighten.antivpn.database.mongo.MongoVPN; +import dev.brighten.antivpn.database.sql.MySqlVPN; import lombok.Getter; import org.bstats.bukkit.Metrics; -import org.bstats.charts.SingleLineChart; +import org.bstats.charts.SimplePie; import org.bukkit.Bukkit; import org.bukkit.command.SimpleCommandMap; import org.bukkit.event.HandlerList; @@ -24,8 +28,6 @@ public class BukkitPlugin extends JavaPlugin { private SimpleCommandMap commandMap; private final List registeredCommands = new ArrayList<>(); - @Getter - private SingleLineChart vpnDetections, ipsChecked; @Getter private PlayerCommandRunner playerCommandRunner; @@ -42,10 +44,7 @@ public class BukkitPlugin extends JavaPlugin { if(AntiVPN.getInstance().getVpnConfig().metrics()) { Bukkit.getLogger().info("Starting bStats metrics..."); Metrics metrics = new Metrics(this, 12615); - metrics.addCustomChart(vpnDetections = new SingleLineChart("vpn_detections", - () -> AntiVPN.getInstance().detections)); - metrics.addCustomChart(ipsChecked = new SingleLineChart("ips_checked", - () -> AntiVPN.getInstance().checked)); + metrics.addCustomChart(new SimplePie("database_used", this::getDatabaseType)); new BukkitRunnable() { public void run() { AntiVPN.getInstance().checked = AntiVPN.getInstance().detections = 0; @@ -55,14 +54,13 @@ public class BukkitPlugin extends JavaPlugin { Bukkit.getLogger().info("Setting up and registering commands..."); // We need access to the commandMap to register our commands without using the "proper" method - if (pluginInstance.getServer().getPluginManager() instanceof SimplePluginManager) { - SimplePluginManager manager = (SimplePluginManager) pluginInstance.getServer().getPluginManager(); + if (pluginInstance.getServer().getPluginManager() instanceof SimplePluginManager manager) { try { Field field = SimplePluginManager.class.getDeclaredField("commandMap"); field.setAccessible(true); commandMap = (SimpleCommandMap) field.get(manager); } catch (IllegalArgumentException | SecurityException | NoSuchFieldException | IllegalAccessException e) { - e.printStackTrace(); + AntiVPN.getInstance().getExecutor().logException(e); } } @@ -89,6 +87,7 @@ public class BukkitPlugin extends JavaPlugin { } @Override + @SuppressWarnings("unchecked") public void onDisable() { Bukkit.getLogger().info("Stopping plugin services..."); AntiVPN.getInstance().stop(); @@ -98,13 +97,15 @@ public class BukkitPlugin extends JavaPlugin { try { Field field = SimpleCommandMap.class.getDeclaredField("knownCommands"); field.setAccessible(true); - Map knownCommands = - (Map) field.get(commandMap); - knownCommands.values().removeAll(registeredCommands); - registeredCommands.clear(); + if(field.get(commandMap) instanceof Map knownCommands) { + Map casted = (Map) knownCommands; + casted.values().removeAll(registeredCommands); + registeredCommands.clear(); + } + } catch (IllegalAccessException | NoSuchFieldException e) { - e.printStackTrace(); + AntiVPN.getInstance().getExecutor().logException(e); } Bukkit.getLogger().info("Unregistering listeners..."); @@ -113,4 +114,18 @@ public class BukkitPlugin extends JavaPlugin { Bukkit.getLogger().info("Cancelling any running tasks..."); Bukkit.getScheduler().cancelTasks(this); } + + private String getDatabaseType() { + VPNDatabase database = AntiVPN.getInstance().getDatabase(); + + if(database instanceof H2VPN) { + return "H2"; + } else if(database instanceof MySqlVPN) { + return "MySQL"; + } else if(database instanceof MongoVPN) { + return "MongoDB"; + } else { + return "No-Database"; + } + } } diff --git a/Bungee/pom.xml b/Bungee/pom.xml index 06e4cff..9135c8d 100644 --- a/Bungee/pom.xml +++ b/Bungee/pom.xml @@ -16,17 +16,17 @@ org.apache.maven.plugins maven-compiler-plugin - 3.7.0 + 3.13.0 - 8 - 8 + 17 + 17 -XDignore.symbol.file org.apache.maven.plugins maven-shade-plugin - 3.1.0 + 3.6.0 @@ -38,10 +38,6 @@ org.yaml.snakeyaml dev.brighten.antivpn.shaded.org.yaml.snakeyaml - - com.google - dev.brighten.antivpn.shaded.com.google - @@ -63,8 +59,8 @@ - 8 - 8 + 17 + 17 @@ -75,9 +71,10 @@ provided - org.github.bungee - BungeeCord-1.8 - 1.8 + net.md-5 + bungeecord-api + 1.21-R0.2 + jar provided diff --git a/Bungee/src/main/java/dev/brighten/antivpn/bungee/BungeeListener.java b/Bungee/src/main/java/dev/brighten/antivpn/bungee/BungeeListener.java index 30d6250..ea8344b 100644 --- a/Bungee/src/main/java/dev/brighten/antivpn/bungee/BungeeListener.java +++ b/Bungee/src/main/java/dev/brighten/antivpn/bungee/BungeeListener.java @@ -1,12 +1,11 @@ package dev.brighten.antivpn.bungee; -import com.google.common.cache.Cache; -import com.google.common.cache.CacheBuilder; +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; import dev.brighten.antivpn.AntiVPN; import dev.brighten.antivpn.api.APIPlayer; import dev.brighten.antivpn.api.VPNExecutor; import dev.brighten.antivpn.web.objects.VPNResponse; -import net.md_5.bungee.BungeeCord; import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.chat.TextComponent; import net.md_5.bungee.api.event.PlayerDisconnectEvent; @@ -25,7 +24,7 @@ public class BungeeListener extends VPNExecutor implements Listener { private ScheduledTask cacheResetTask; - private final Cache responseCache = CacheBuilder.newBuilder() + private final Cache responseCache = Caffeine.newBuilder() .expireAfterWrite(5, TimeUnit.MINUTES) .maximumSize(2000) .build(); @@ -36,18 +35,9 @@ public class BungeeListener extends VPNExecutor implements Listener { .registerListener(BungeePlugin.pluginInstance, this); } - @Override - public void onShutdown() { - if(cacheResetTask != null) { - cacheResetTask.cancel(); - cacheResetTask = null; - } - BungeePlugin.pluginInstance.getProxy().getPluginManager().unregisterListener(this); - } - @Override public void log(Level level, String log, Object... objects) { - BungeeCord.getInstance().getLogger().log(Level.INFO, String.format(log, objects)); + BungeePlugin.pluginInstance.getProxy().getLogger().log(Level.INFO, String.format(log, objects)); } @Override @@ -56,18 +46,18 @@ public class BungeeListener extends VPNExecutor implements Listener { } @Override - public void logException(String message, Exception ex) { - BungeeCord.getInstance().getLogger().log(Level.SEVERE, message, ex); + public void logException(String message, Throwable ex) { + BungeePlugin.pluginInstance.getProxy().getLogger().log(Level.SEVERE, message, ex); } @Override public void disablePlugin() { - BungeeCord.getInstance().getPluginManager().unregisterListeners(BungeePlugin.pluginInstance); + BungeePlugin.pluginInstance.getProxy().getPluginManager().unregisterListeners(BungeePlugin.pluginInstance); if(cacheResetTask != null) { cacheResetTask.cancel(); cacheResetTask = null; } - BungeeCord.getInstance().getPluginManager().unregisterCommands(BungeePlugin.pluginInstance); + BungeePlugin.pluginInstance.getProxy().getPluginManager().unregisterCommands(BungeePlugin.pluginInstance); BungeePlugin.pluginInstance.onDisable(); } @@ -79,7 +69,7 @@ public class BungeeListener extends VPNExecutor implements Listener { if(cached != null && cached.isProxy()) { event.setCancelled(true); - event.setCancelReason(TextComponent.fromLegacyText(ChatColor + event.setReason(TextComponent.fromLegacy(ChatColor .translateAlternateColorCodes('&', AntiVPN.getInstance().getVpnConfig().getKickString()))); AntiVPN.getInstance().getExecutor().log(Level.INFO, @@ -94,92 +84,92 @@ public class BungeeListener extends VPNExecutor implements Listener { || AntiVPN.getInstance().getVpnConfig().getPrefixWhitelists().stream() .anyMatch(prefix -> event.getPlayer().getName().startsWith(prefix))) return; - checkIp(event.getPlayer().getAddress().getAddress().getHostAddress(), - AntiVPN.getInstance().getVpnConfig().cachedResults(), result -> { - if(result.isSuccess()) { - //If the player is whitelisted, we don't want to kick them - if(AntiVPN.getInstance().getExecutor().isWhitelisted(event.getPlayer().getUniqueId())) { - AntiVPN.getInstance().getExecutor().log("UUID is whitelisted: %s", - event.getPlayer().getUniqueId().toString()); - return; - } + String address = event.getPlayer().getSocketAddress().toString(); - //If the IP is whitelisted, we don't want to kick them - if(AntiVPN.getInstance().getExecutor().isWhitelisted(event.getPlayer().getAddress().getAddress() - .getHostAddress())) { - AntiVPN.getInstance().getExecutor().log("IP is whitelisted: %s", - event.getPlayer().getAddress().getAddress().getHostAddress()); - return; - } + if(AntiVPN.getInstance().getExecutor().isWhitelisted(event.getPlayer().getUniqueId())) { + AntiVPN.getInstance().getExecutor().log("UUID is whitelisted: %s", + event.getPlayer().getUniqueId().toString()); + return; + } - responseCache.put(event.getPlayer().getUniqueId(), result); + //If the IP is whitelisted, we don't want to kick them + if(AntiVPN.getInstance().getExecutor().isWhitelisted(address)) { + AntiVPN.getInstance().getExecutor().log("IP is whitelisted: %s", address); + return; + } - if(!AntiVPN.getInstance().getVpnConfig().countryList().isEmpty() - // This bit of code will decide whether or not to kick the player - // If it contains the code and it is set to whitelist, it will not kick as they are equal - // and vise versa. However, if the contains does not match the state, it will kick. - && AntiVPN.getInstance().getVpnConfig().countryList() - .contains(result.getCountryCode()) != AntiVPN.getInstance().getVpnConfig().whitelistCountries()) { - //Using our built in kicking system if no commands are configured - if(AntiVPN.getInstance().getVpnConfig().countryKickCommands().isEmpty()) { - final String kickReason = AntiVPN.getInstance().getVpnConfig() - .countryVanillaKickReason(); - // Kicking our player - event.getPlayer().disconnect(TextComponent.fromLegacyText(ChatColor - .translateAlternateColorCodes('&', - kickReason + checkIp(address) + .thenAccept(result -> { + if(result.isSuccess()) { + //If the player is whitelisted, we don't want to kick them + responseCache.put(event.getPlayer().getUniqueId(), result); + if(!AntiVPN.getInstance().getVpnConfig().countryList().isEmpty() + // This bit of code will decide whether or not to kick the player + // If it contains the code and it is set to whitelist, it will not kick as they are equal + // and vise versa. However, if the contains does not match the state, it will kick. + && AntiVPN.getInstance().getVpnConfig().countryList() + .contains(result.getCountryCode()) != AntiVPN.getInstance().getVpnConfig().whitelistCountries()) { + //Using our built in kicking system if no commands are configured + if(AntiVPN.getInstance().getVpnConfig().countryKickCommands().isEmpty()) { + final String kickReason = AntiVPN.getInstance().getVpnConfig() + .countryVanillaKickReason(); + // Kicking our player + event.getPlayer().disconnect(TextComponent.fromLegacy(ChatColor + .translateAlternateColorCodes('&', + kickReason + .replace("%player%", event.getPlayer().getName()) + .replace("%country%", result.getCountryName()) + .replace("%code%", result.getCountryCode())))); + } else { + for (String cmd : AntiVPN.getInstance().getVpnConfig().countryKickCommands()) { + final String formattedCommand = ChatColor.translateAlternateColorCodes('&', + cmd.replace("%player%", event.getPlayer().getName()) + .replace("%country%", result.getCountryName()) + .replace("%code%", result.getCountryCode())); + + // Runs our command from console + BungeePlugin.pluginInstance.getProxy().getPluginManager().dispatchCommand( + BungeePlugin.pluginInstance.getProxy().getConsole(), formattedCommand); + } + } + } else if(result.isProxy()) { + if(AntiVPN.getInstance().getVpnConfig().kickPlayersOnDetect()) + event.getPlayer().disconnect(TextComponent.fromLegacy(ChatColor + .translateAlternateColorCodes('&', + AntiVPN.getInstance().getVpnConfig().getKickString()))); + BungeePlugin.pluginInstance.getProxy().getLogger().info(event.getPlayer().getName() + + " joined on a VPN/Proxy (" + result.getMethod() + ")"); + + if(AntiVPN.getInstance().getVpnConfig().alertToStaff()) //Ensuring the user wishes to alert to staff + AntiVPN.getInstance().getPlayerExecutor().getOnlinePlayers().stream() + .filter(APIPlayer::isAlertsEnabled) + .forEach(pl -> pl.sendMessage(AntiVPN.getInstance().getVpnConfig() + .alertMessage() .replace("%player%", event.getPlayer().getName()) + .replace("%reason%", result.getMethod()) .replace("%country%", result.getCountryName()) - .replace("%code%", result.getCountryCode())))); + .replace("%city%", result.getCity()))); + + //In case the user wants to run their own commands instead of using the built in kicking + if(AntiVPN.getInstance().getVpnConfig().runCommands()) { + for (String command : AntiVPN.getInstance().getVpnConfig().commands()) { + BungeePlugin.pluginInstance.getProxy().getPluginManager() + .dispatchCommand(BungeePlugin.pluginInstance.getProxy().getConsole(), + ChatColor.translateAlternateColorCodes('&', + command.replace("%player%", event.getPlayer().getName()))); + } + } + AntiVPN.getInstance().detections++; + } + } else { - for (String cmd : AntiVPN.getInstance().getVpnConfig().countryKickCommands()) { - final String formattedCommand = ChatColor.translateAlternateColorCodes('&', - cmd.replace("%player%", event.getPlayer().getName()) - .replace("%country%", result.getCountryName()) - .replace("%code%", result.getCountryCode())); - - // Runs our command from console - BungeeCord.getInstance().getPluginManager().dispatchCommand( - BungeeCord.getInstance().getConsole(), formattedCommand); - } + BungeePlugin.pluginInstance.getProxy().getLogger() + .log(Level.WARNING, + "The API query was not a success! " + + "You may need to upgrade your license on https://funkemunky.cc/shop"); } - } else if(result.isProxy()) { - if(AntiVPN.getInstance().getVpnConfig().kickPlayersOnDetect()) - event.getPlayer().disconnect(TextComponent.fromLegacyText(ChatColor - .translateAlternateColorCodes('&', - AntiVPN.getInstance().getVpnConfig().getKickString()))); - BungeeCord.getInstance().getLogger().info(event.getPlayer().getName() - + " joined on a VPN/Proxy (" + result.getMethod() + ")"); - - if(AntiVPN.getInstance().getVpnConfig().alertToStaff()) //Ensuring the user wishes to alert to staff - AntiVPN.getInstance().getPlayerExecutor().getOnlinePlayers().stream() - .filter(APIPlayer::isAlertsEnabled) - .forEach(pl -> pl.sendMessage(AntiVPN.getInstance().getVpnConfig().alertMessage() - .replace("%player%", event.getPlayer().getName()) - .replace("%reason%", result.getMethod()) - .replace("%country%", result.getCountryName()) - .replace("%city%", result.getCity()))); - - //In case the user wants to run their own commands instead of using the built in kicking - if(AntiVPN.getInstance().getVpnConfig().runCommands()) { - for (String command : AntiVPN.getInstance().getVpnConfig().commands()) { - BungeeCord.getInstance().getPluginManager() - .dispatchCommand(BungeeCord.getInstance().getConsole(), - ChatColor.translateAlternateColorCodes('&', - command.replace("%player%", event.getPlayer().getName()))); - } - } - AntiVPN.getInstance().detections++; - } - - } else { - BungeeCord.getInstance().getLogger() - .log(Level.WARNING, - "The API query was not a success! " + - "You may need to upgrade your license on https://funkemunky.cc/shop"); - } - AntiVPN.getInstance().checked++; - }); + AntiVPN.getInstance().checked++; + }); } @EventHandler diff --git a/Bungee/src/main/java/dev/brighten/antivpn/bungee/BungeePlayerExecutor.java b/Bungee/src/main/java/dev/brighten/antivpn/bungee/BungeePlayerExecutor.java index b5e41fb..ab77f95 100644 --- a/Bungee/src/main/java/dev/brighten/antivpn/bungee/BungeePlayerExecutor.java +++ b/Bungee/src/main/java/dev/brighten/antivpn/bungee/BungeePlayerExecutor.java @@ -2,7 +2,6 @@ package dev.brighten.antivpn.bungee; import dev.brighten.antivpn.api.APIPlayer; import dev.brighten.antivpn.api.PlayerExecutor; -import net.md_5.bungee.BungeeCord; import net.md_5.bungee.api.connection.ProxiedPlayer; import java.util.*; @@ -14,7 +13,7 @@ public class BungeePlayerExecutor implements PlayerExecutor { @Override public Optional getPlayer(String name) { - ProxiedPlayer player = BungeeCord.getInstance().getPlayer(name); + ProxiedPlayer player = BungeePlugin.pluginInstance.getProxy().getPlayer(name); if(player == null) return Optional.empty(); @@ -23,7 +22,7 @@ public class BungeePlayerExecutor implements PlayerExecutor { @Override public Optional getPlayer(UUID uuid) { - ProxiedPlayer player = BungeeCord.getInstance().getPlayer(uuid); + ProxiedPlayer player = BungeePlugin.pluginInstance.getProxy().getPlayer(uuid); if(player == null) return Optional.empty(); @@ -37,7 +36,7 @@ public class BungeePlayerExecutor implements PlayerExecutor { @Override public List getOnlinePlayers() { - return BungeeCord.getInstance().getPlayers().stream() + return BungeePlugin.pluginInstance.getProxy().getPlayers().stream() .map(pl -> cachedPlayers.computeIfAbsent(pl.getUniqueId(), key -> new BungeePlayer(pl))) .collect(Collectors.toList()); } diff --git a/Bungee/src/main/java/dev/brighten/antivpn/bungee/BungeePlugin.java b/Bungee/src/main/java/dev/brighten/antivpn/bungee/BungeePlugin.java index bb54d83..15e29bc 100644 --- a/Bungee/src/main/java/dev/brighten/antivpn/bungee/BungeePlugin.java +++ b/Bungee/src/main/java/dev/brighten/antivpn/bungee/BungeePlugin.java @@ -3,10 +3,13 @@ package dev.brighten.antivpn.bungee; import dev.brighten.antivpn.AntiVPN; import dev.brighten.antivpn.bungee.command.BungeeCommand; import dev.brighten.antivpn.command.Command; -import net.md_5.bungee.BungeeCord; +import dev.brighten.antivpn.database.VPNDatabase; +import dev.brighten.antivpn.database.local.H2VPN; +import dev.brighten.antivpn.database.mongo.MongoVPN; +import dev.brighten.antivpn.database.sql.MySqlVPN; import net.md_5.bungee.api.plugin.Plugin; import org.bstats.bungeecord.Metrics; -import org.bstats.charts.SingleLineChart; +import org.bstats.charts.SimplePie; import java.util.concurrent.TimeUnit; @@ -14,34 +17,29 @@ public class BungeePlugin extends Plugin { public static BungeePlugin pluginInstance; - private SingleLineChart vpnDetections, ipsChecked; - @Override public void onEnable() { pluginInstance = this; //Setting up config - BungeeCord.getInstance().getLogger().info("Loading config..."); + getProxy().getLogger().info("Loading config..."); //Loading plugin - BungeeCord.getInstance().getLogger().info("Starting AntiVPN services..."); + getProxy().getLogger().info("Starting AntiVPN services..."); AntiVPN.start(new BungeeListener(), new BungeePlayerExecutor(), getDataFolder()); if(AntiVPN.getInstance().getVpnConfig().metrics()) { - BungeeCord.getInstance().getLogger().info("Starting bStats metrics..."); + getProxy().getLogger().info("Starting bStats metrics..."); Metrics metrics = new Metrics(this, 12616); - metrics.addCustomChart(vpnDetections = new SingleLineChart("vpn_detections", - () -> AntiVPN.getInstance().detections)); - metrics.addCustomChart(ipsChecked = new SingleLineChart("ips_checked", - () -> AntiVPN.getInstance().checked)); - BungeeCord.getInstance().getScheduler().schedule(this, + metrics.addCustomChart(new SimplePie("database_used", this::getDatabaseType)); + getProxy().getScheduler().schedule(this, () -> AntiVPN.getInstance().checked = AntiVPN.getInstance().detections = 0, 10, 10, TimeUnit.MINUTES); } for (Command command : AntiVPN.getInstance().getCommands()) { - BungeeCord.getInstance().getPluginManager().registerCommand(pluginInstance, new BungeeCommand(command)); + getProxy().getPluginManager().registerCommand(pluginInstance, new BungeeCommand(command)); } } @@ -49,4 +47,18 @@ public class BungeePlugin extends Plugin { public void onDisable() { AntiVPN.getInstance().stop(); } + + private String getDatabaseType() { + VPNDatabase database = AntiVPN.getInstance().getDatabase(); + + if(database instanceof H2VPN) { + return "H2"; + } else if(database instanceof MySqlVPN) { + return "MySQL"; + } else if(database instanceof MongoVPN) { + return "MongoDB"; + } else { + return "No-Database"; + } + } } diff --git a/Common/pom.xml b/Common/pom.xml index 82f313f..230cf7d 100644 --- a/Common/pom.xml +++ b/Common/pom.xml @@ -12,8 +12,8 @@ Common - 8 - 8 + 17 + 17 @@ -21,10 +21,10 @@ org.apache.maven.plugins maven-compiler-plugin - 3.10.1 + 3.13.0 - 8 - 8 + 17 + 17 -XDignore.symbol.file @@ -38,7 +38,7 @@ org.apache.maven.plugins maven-shade-plugin - 3.4.1 + 3.6.0 package @@ -51,14 +51,65 @@ org.yaml.snakeyaml dev.brighten.antivpn.shaded.org.yaml.snakeyaml + + + dev.brighten.antivpn.depends.Relocate + dev.brighten.antivpn.depends.MavenLibraries + - com.google.common - dev.brighten.antivpn.shaded.com.google.common + com.google + dev.brighten.antivpn.shaded.com.google + + + dev.brighten.antivpn.depends.Relocate + dev.brighten.antivpn.depends.MavenLibraries + org.h2 dev.brighten.antivpn.shaded.org.h2 + + + dev.brighten.antivpn.depends.Relocate + dev.brighten.antivpn.depends.MavenLibraries + + + + org.bson + dev.brighten.antivpn.shaded.org.bson + + + dev.brighten.antivpn.depends.Relocate + dev.brighten.antivpn.depends.MavenLibraries + + + + com.mongodb + dev.brighten.antivpn.shaded.com.mongodb + + + dev.brighten.antivpn.depends.Relocate + dev.brighten.antivpn.depends.MavenLibraries + + + + com.mysql.cj + dev.brighten.antivpn.shaded.com.mysql.cj + + + dev.brighten.antivpn.depends.Relocate + dev.brighten.antivpn.depends.MavenLibraries + + + + com.mysql.jdbc + dev.brighten.antivpn.shaded.com.mysql.jdbc + + + dev.brighten.antivpn.depends.Relocate + dev.brighten.antivpn.depends.MavenLibraries + @@ -87,12 +138,24 @@ mysql-connector-j 9.1.0 jar - compile + provided com.h2database h2 2.2.220 + provided + + + org.ow2.asm + asm + 9.8 + compile + + + org.ow2.asm + asm-commons + 9.8 compile @@ -102,15 +165,17 @@ compile - com.google.guava - guava - 32.1.3-jre + com.github.ben-manes.caffeine + caffeine + 3.1.8 + compile + org.mongodb mongo-java-driver 3.12.14 - compile + provided diff --git a/Common/src/main/java/dev/brighten/antivpn/AntiVPN.java b/Common/src/main/java/dev/brighten/antivpn/AntiVPN.java index 9446bf8..3a24d96 100644 --- a/Common/src/main/java/dev/brighten/antivpn/AntiVPN.java +++ b/Common/src/main/java/dev/brighten/antivpn/AntiVPN.java @@ -9,6 +9,9 @@ import dev.brighten.antivpn.database.VPNDatabase; import dev.brighten.antivpn.database.local.H2VPN; import dev.brighten.antivpn.database.mongo.MongoVPN; import dev.brighten.antivpn.database.sql.MySqlVPN; +import dev.brighten.antivpn.depends.LibraryLoader; +import dev.brighten.antivpn.depends.MavenLibrary; +import dev.brighten.antivpn.depends.Relocate; import dev.brighten.antivpn.message.MessageHandler; import dev.brighten.antivpn.utils.ConfigDefault; import dev.brighten.antivpn.utils.MiscUtils; @@ -29,6 +32,21 @@ import java.util.List; @Getter @Setter(AccessLevel.PRIVATE) +@MavenLibrary(groupId = "com.h2database", artifactId ="h2", version = "2.2.220", relocations = { + @Relocate(from ="org" + ".\\h2", to ="dev.brighten.antivpn.shaded.org.h2")}) +@MavenLibrary(groupId = "org.mongodb", artifactId = "mongo-java-driver", version = "3.12.14", relocations = { + @Relocate(from = "com." + "\\mongodb", to = "dev.brighten.antivpn.shaded.com.mongodb"), + @Relocate(from = "org" + "\\.bson", to = "dev.brighten.antivpn.shaded.org.bson") +}) +@MavenLibrary( + groupId = "com.mysql", + artifactId = "mysql-connector-j", + version = "9.1.0", + relocations = { + @Relocate(from = "com.my\\" + "sql.cj", to = "dev.brighten.antivpn.shaded.com.mysql.cj"), + @Relocate(from = "com.my\\" + "sql.jdbc", to = "dev.brighten.antivpn.shaded.com.mysql.jdbc") + } +) public class AntiVPN { private static AntiVPN INSTANCE; @@ -51,6 +69,8 @@ public class AntiVPN { INSTANCE.executor = executor; INSTANCE.playerExecutor = playerExecutor; + LibraryLoader.loadAll(INSTANCE); + try { File configFile = new File(pluginFolder, "config.yml"); if(!configFile.exists()){ @@ -143,7 +163,20 @@ public class AntiVPN { } public void stop() { - executor.onShutdown(); + if (database instanceof H2VPN) { + database.shutdown(); + + // Try to deregister driver + try { + java.sql.Driver driver = java.sql.DriverManager.getDriver("jdbc:h2:"); + if (driver != null) { + java.sql.DriverManager.deregisterDriver(driver); + } + } catch (Exception e) { + // Log but don't throw + executor.log("Failed to deregister H2 driver: " + e.getMessage()); + } + } VPNExecutor.threadExecutor.shutdown(); if(database != null) database.shutdown(); } diff --git a/Common/src/main/java/dev/brighten/antivpn/api/VPNExecutor.java b/Common/src/main/java/dev/brighten/antivpn/api/VPNExecutor.java index 1ae8cd7..838e6d8 100644 --- a/Common/src/main/java/dev/brighten/antivpn/api/VPNExecutor.java +++ b/Common/src/main/java/dev/brighten/antivpn/api/VPNExecutor.java @@ -1,7 +1,5 @@ package dev.brighten.antivpn.api; -import com.google.common.cache.Cache; -import com.google.common.cache.CacheBuilder; import dev.brighten.antivpn.AntiVPN; import dev.brighten.antivpn.utils.json.JSONException; import dev.brighten.antivpn.web.FunkemunkyAPI; @@ -10,11 +8,9 @@ import lombok.Getter; import java.io.IOException; import java.util.*; -import java.util.concurrent.ExecutionException; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; -import java.util.function.Consumer; import java.util.logging.Level; public abstract class VPNExecutor { @@ -24,23 +20,15 @@ public abstract class VPNExecutor { private final Set whitelisted = Collections.synchronizedSet(new HashSet<>()); @Getter private final Set whitelistedIps = Collections.synchronizedSet(new HashSet<>()); - - private final Cache responseCache = CacheBuilder.newBuilder() - .expireAfterWrite(20, TimeUnit.MINUTES) - .maximumSize(4000) - .build(); - public abstract void registerListeners(); - public abstract void onShutdown(); - public abstract void log(Level level, String log, Object... objects); public abstract void log(String log, Object... objects); - public abstract void logException(String message, Exception ex); + public abstract void logException(String message, Throwable ex); - public void logException(Exception ex) { + public void logException(Throwable ex) { logException("An exception occurred: " + ex.getMessage(), ex); } @@ -58,44 +46,31 @@ public abstract class VPNExecutor { return whitelistedIps.contains(ip); } - public void checkIp(String ip, boolean cachedResults, Consumer result) { - threadExecutor.execute(() -> { - if(cachedResults) { + public CompletableFuture checkIp(String ip) { + return CompletableFuture.supplyAsync(() -> { + Optional cachedRes = AntiVPN.getInstance().getDatabase().getStoredResponse(ip); + + if(cachedRes.isPresent()) { + return cachedRes.get(); + } + else { try { - result.accept(responseCache.get(ip, () -> checkIp(ip))); - } catch (ExecutionException e) { - log("Failed to process checkIp() method! Reason: " + e.getMessage()); - result.accept(VPNResponse.FAILED_RESPONSE); + VPNResponse response = FunkemunkyAPI + .getVPNResponse(ip, AntiVPN.getInstance().getVpnConfig().getLicense(), true); + + if (response.isSuccess()) { + AntiVPN.getInstance().getDatabase().cacheResponse(response); + } else { + log("Query to VPN API failed! Reason: " + response.getFailureReason()); + } + + return response; + } catch (JSONException | IOException e) { + log("Query to VPN API failed! Reason: " + e.getMessage()); + return VPNResponse.FAILED_RESPONSE; } - } else { - result.accept(checkIp(ip)); } - }); - } - - public VPNResponse checkIp(String ip) { - Optional cachedRes = AntiVPN.getInstance().getDatabase().getStoredResponse(ip); - - if(cachedRes.isPresent()) { - return cachedRes.get(); - } - else { - try { - VPNResponse response = FunkemunkyAPI - .getVPNResponse(ip, AntiVPN.getInstance().getVpnConfig().getLicense(), true); - - if (response.isSuccess()) { - AntiVPN.getInstance().getDatabase().cacheResponse(response); - } else { - log("Query to VPN API failed! Reason: " + response.getFailureReason()); - } - - return response; - } catch (JSONException | IOException e) { - log("Query to VPN API failed! Reason: " + e.getMessage()); - return VPNResponse.FAILED_RESPONSE; - } - } + }, threadExecutor); } public abstract void disablePlugin(); diff --git a/Common/src/main/java/dev/brighten/antivpn/command/impl/LookupCommand.java b/Common/src/main/java/dev/brighten/antivpn/command/impl/LookupCommand.java index cd2ba12..9949d40 100644 --- a/Common/src/main/java/dev/brighten/antivpn/command/impl/LookupCommand.java +++ b/Common/src/main/java/dev/brighten/antivpn/command/impl/LookupCommand.java @@ -55,28 +55,31 @@ public class LookupCommand extends Command { Optional player = AntiVPN.getInstance().getPlayerExecutor().getPlayer(args[0]); - if(!player.isPresent()) { + if(player.isEmpty()) { return String.format("&cNo player found with the name \"%s\"", args[0]); } - AntiVPN.getInstance().getExecutor().checkIp(player.get().getIp().getHostAddress(), - false, result -> { - if(!result.isSuccess()) { - executor.sendMessage("&cThere was an error trying to find the information of this player."); - } else { - executor.sendMessage(StringUtil.line("&8")); - executor.sendMessage("&6&l" + player.get().getName() + "&7&l's Connection Information"); - executor.sendMessage(""); - executor.sendMessage("&e%s&8: &f%s", "Proxy", result.isProxy() - ? "&a" + result.getMethod() : "&cNo"); - executor.sendMessage("&e%s&8: &f%s", "ISP", result.getIsp()); - executor.sendMessage("&e%s&8: &f%s", "Country", result.getCountryName()); - executor.sendMessage("&e%s&8: &f%s", "City", result.getCity()); - executor.sendMessage("&e%s&8: &f%s", "Coordinates", result.getLatitude() - + "&7/&f" + result.getLongitude()); - executor.sendMessage(StringUtil.line("&8")); - } - }); + AntiVPN.getInstance().getExecutor() + .checkIp(player.get().getIp().getHostAddress()) + .thenAccept(result -> { + if(!result.isSuccess()) { + executor.sendMessage("&cThere was an error trying to find the " + + "information of this player."); + return; + } + + executor.sendMessage(StringUtil.line("&8")); + executor.sendMessage("&6&l" + player.get().getName() + "&7&l's Connection Information"); + executor.sendMessage(""); + executor.sendMessage("&e%s&8: &f%s", "Proxy", result.isProxy() + ? "&a" + result.getMethod() : "&cNo"); + executor.sendMessage("&e%s&8: &f%s", "ISP", result.getIsp()); + executor.sendMessage("&e%s&8: &f%s", "Country", result.getCountryName()); + executor.sendMessage("&e%s&8: &f%s", "City", result.getCity()); + executor.sendMessage("&e%s&8: &f%s", "Coordinates", result.getLatitude() + + "&7/&f" + result.getLongitude()); + executor.sendMessage(StringUtil.line("&8")); + }); return "&7Looking up the IP information for player " + player.get().getName() + "..."; diff --git a/Common/src/main/java/dev/brighten/antivpn/database/local/H2VPN.java b/Common/src/main/java/dev/brighten/antivpn/database/local/H2VPN.java index c2cd904..4dfcdf9 100644 --- a/Common/src/main/java/dev/brighten/antivpn/database/local/H2VPN.java +++ b/Common/src/main/java/dev/brighten/antivpn/database/local/H2VPN.java @@ -1,5 +1,7 @@ package dev.brighten.antivpn.database.local; +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; import dev.brighten.antivpn.AntiVPN; import dev.brighten.antivpn.api.VPNExecutor; import dev.brighten.antivpn.database.VPNDatabase; @@ -20,6 +22,12 @@ import java.util.function.Consumer; public class H2VPN implements VPNDatabase { + private final Cache cachedResponses = Caffeine.newBuilder() + .expireAfterWrite(20, TimeUnit.MINUTES) + .maximumSize(4000) + .build(); + + public H2VPN() { VPNExecutor.threadExecutor.scheduleAtFixedRate(() -> { if(!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed()) return; @@ -41,29 +49,26 @@ public class H2VPN implements VPNDatabase { if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled()|| MySQL.isClosed()) return Optional.empty(); - ResultSet rs = Query.prepare("select * from `responses` where `ip` = ? limit 1").append(ip).executeQuery(); - - try { - if (rs != null && rs.next()) { - VPNResponse response = new VPNResponse(rs.getString("asn"), rs.getString("ip"), - rs.getString("countryName"), rs.getString("countryCode"), - rs.getString("city"), rs.getString("timeZone"), - rs.getString("method"), rs.getString("isp"), "N/A", - rs.getBoolean("proxy"), rs.getBoolean("cached"), true, - rs.getDouble("latitude"), rs.getDouble("longitude"), - rs.getTimestamp("inserted").getTime(), -1); - - if(System.currentTimeMillis() - response.getLastAccess() > TimeUnit.HOURS.toMillis(1)) { - VPNExecutor.threadExecutor.execute(() -> deleteResponse(ip)); - return Optional.empty(); + VPNResponse response = cachedResponses.get(ip, ip2 -> { + try(ResultSet rs = Query.prepare("select * from `responses` where `ip` = ? limit 1").append(ip). + executeQuery()) { + if (rs != null && rs.next()) { + return new VPNResponse(rs.getString("asn"), rs.getString("ip"), + rs.getString("countryName"), rs.getString("countryCode"), + rs.getString("city"), rs.getString("timeZone"), + rs.getString("method"), rs.getString("isp"), "N/A", + rs.getBoolean("proxy"), rs.getBoolean("cached"), true, + rs.getDouble("latitude"), rs.getDouble("longitude"), + rs.getTimestamp("inserted").getTime(), -1); } - return Optional.of(response); + } catch (SQLException e) { + AntiVPN.getInstance().getExecutor().logException("There was a problem getting a response for " + + ip, e); } - } catch (SQLException throwables) { - throwables.printStackTrace(); - } + return null; + }); - return Optional.empty(); + return Optional.ofNullable(response); } /* @@ -80,6 +85,8 @@ public class H2VPN implements VPNDatabase { if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed()) return; + cachedResponses.put(toCache.getIp(), toCache); + Query.prepare("insert into `responses` (`ip`,`asn`,`countryName`,`countryCode`,`city`,`timeZone`," + "`method`,`isp`,`proxy`,`cached`,`inserted`,`latitude`,`longitude`) values (?,?,?,?,?,?,?,?,?,?,?,?,?)") .append(toCache.getIp()).append(toCache.getAsn()).append(toCache.getCountryName()) @@ -198,13 +205,12 @@ public class H2VPN implements VPNDatabase { if(MySQL.isClosed()) return; VPNExecutor.threadExecutor.execute(() -> { - ResultSet set = Query.prepare("select * from `alerts` where `uuid` = ? limit 1") - .append(uuid.toString()).executeQuery(); - try { + try(ResultSet set = Query.prepare("select * from `alerts` where `uuid` = ? limit 1") + .append(uuid.toString()).executeQuery()) { result.accept(set != null && set.next() && set.getString("uuid") != null); } catch (SQLException e) { - e.printStackTrace(); + AntiVPN.getInstance().getExecutor().logException("There was a problem getting alerts state for " + uuid, e); result.accept(false); } }); @@ -271,6 +277,7 @@ public class H2VPN implements VPNDatabase { public void shutdown() { if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled()) return; + MySQL.shutdown(); } } diff --git a/Common/src/main/java/dev/brighten/antivpn/database/mongo/MongoVPN.java b/Common/src/main/java/dev/brighten/antivpn/database/mongo/MongoVPN.java index 900abd4..2e7c4eb 100644 --- a/Common/src/main/java/dev/brighten/antivpn/database/mongo/MongoVPN.java +++ b/Common/src/main/java/dev/brighten/antivpn/database/mongo/MongoVPN.java @@ -1,5 +1,7 @@ package dev.brighten.antivpn.database.mongo; +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; import com.mongodb.*; import com.mongodb.client.MongoClient; import com.mongodb.client.MongoClients; @@ -24,6 +26,11 @@ public class MongoVPN implements VPNDatabase { private MongoCollection settingsDocument, cacheDocument; private MongoClient client; + private final Cache cachedResponses = Caffeine.newBuilder() + .expireAfterWrite(20, TimeUnit.MINUTES) + .maximumSize(4000) + .build(); + public MongoVPN() { VPNExecutor.threadExecutor.scheduleAtFixedRate(() -> { if(!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled()) return; @@ -41,32 +48,37 @@ public class MongoVPN implements VPNDatabase { } @Override public Optional getStoredResponse(String ip) { - Document rdoc = cacheDocument.find(Filters.eq("ip", ip)).first(); + VPNResponse response = cachedResponses.get(ip, ip2 -> { + Document rdoc = cacheDocument.find(Filters.eq("ip", ip)).first(); - if(rdoc != null) { - long lastUpdate = rdoc.get("lastAccess", 0L); + if(rdoc != null) { + long lastUpdate = rdoc.get("lastAccess", 0L); - if(System.currentTimeMillis() - lastUpdate > TimeUnit.HOURS.toMillis(1)) { - VPNExecutor.threadExecutor.execute(() -> deleteResponse(ip)); - return Optional.empty(); + if(System.currentTimeMillis() - lastUpdate > TimeUnit.HOURS.toMillis(1)) { + VPNExecutor.threadExecutor.execute(() -> deleteResponse(ip)); + return null; + } + + return VPNResponse.builder().asn(rdoc.getString("asn")).ip(ip) + .countryName(rdoc.getString("countryName")) + .countryCode(rdoc.getString("countryCode")) + .city(rdoc.getString("city")) + .isp(rdoc.getString("isp")) + .method(rdoc.getString("method")) + .timeZone(rdoc.getString("timeZone")) + .proxy(rdoc.getBoolean("proxy")) + .cached(rdoc.getBoolean("cached")) + .success(true) + .latitude(rdoc.getDouble("latitude")) + .longitude(rdoc.getDouble("longitude")) + .lastAccess(rdoc.get("lastAccess", 0L)) + .build(); } + return null; + }); - return Optional.of(VPNResponse.builder().asn(rdoc.getString("asn")).ip(ip) - .countryName(rdoc.getString("countryName")) - .countryCode(rdoc.getString("countryCode")) - .city(rdoc.getString("city")) - .isp(rdoc.getString("isp")) - .method(rdoc.getString("method")) - .timeZone(rdoc.getString("timeZone")) - .proxy(rdoc.getBoolean("proxy")) - .cached(rdoc.getBoolean("cached")) - .success(true) - .latitude(rdoc.getDouble("latitude")) - .longitude(rdoc.getDouble("longitude")) - .lastAccess(rdoc.get("lastAccess", 0L)) - .build()); - } - return Optional.empty(); + + return Optional.ofNullable(response); } @Override @@ -87,6 +99,8 @@ public class MongoVPN implements VPNDatabase { rdoc.put("longitude", toCache.getLongitude()); rdoc.put("lastAccess", System.currentTimeMillis()); + cachedResponses.put(toCache.getIp(), toCache); + VPNExecutor.threadExecutor.execute(() -> { Bson update = new Document("$set", rdoc); cacheDocument.updateOne(Filters.eq("ip", toCache.getIp()), update, diff --git a/Common/src/main/java/dev/brighten/antivpn/database/sql/MySqlVPN.java b/Common/src/main/java/dev/brighten/antivpn/database/sql/MySqlVPN.java index 1b3babd..39a9cea 100644 --- a/Common/src/main/java/dev/brighten/antivpn/database/sql/MySqlVPN.java +++ b/Common/src/main/java/dev/brighten/antivpn/database/sql/MySqlVPN.java @@ -1,5 +1,7 @@ package dev.brighten.antivpn.database.sql; +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; import dev.brighten.antivpn.AntiVPN; import dev.brighten.antivpn.api.VPNExecutor; import dev.brighten.antivpn.database.VPNDatabase; @@ -20,6 +22,12 @@ import java.util.function.Consumer; public class MySqlVPN implements VPNDatabase { + private final Cache cachedResponses = Caffeine.newBuilder() + .expireAfterWrite(20, TimeUnit.MINUTES) + .maximumSize(4000) + .build(); + + public MySqlVPN() { VPNExecutor.threadExecutor.scheduleAtFixedRate(() -> { if(!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed()) return; @@ -41,30 +49,34 @@ public class MySqlVPN implements VPNDatabase { if (isDisabled()) return Optional.empty(); - ResultSet rs = Query.prepare("select * from `responses` where `ip` = ? limit 1").append(ip).executeQuery(); + VPNResponse response = cachedResponses.get(ip, ip2 -> { + try(ResultSet rs = Query.prepare("select * from `responses` where `ip` = ? limit 1").append(ip) + .executeQuery()) { + if (rs != null && rs.next()) { + VPNResponse responseFromDoc = new VPNResponse(rs.getString("asn"), + rs.getString("ip"), + rs.getString("countryName"), rs.getString("countryCode"), + rs.getString("city"), rs.getString("timeZone"), + rs.getString("method"), rs.getString("isp"), "N/A", + rs.getBoolean("proxy"), rs.getBoolean("cached"), true, + rs.getDouble("latitude"), rs.getDouble("longitude"), + rs.getTimestamp("inserted").getTime(), -1); - try { - if (rs != null && rs.next()) { - VPNResponse response = new VPNResponse(rs.getString("asn"), rs.getString("ip"), - rs.getString("countryName"), rs.getString("countryCode"), - rs.getString("city"), rs.getString("timeZone"), - rs.getString("method"), rs.getString("isp"), "N/A", - rs.getBoolean("proxy"), rs.getBoolean("cached"), true, - rs.getDouble("latitude"), rs.getDouble("longitude"), - rs.getTimestamp("inserted").getTime(), -1); + if(System.currentTimeMillis() - responseFromDoc.getLastAccess() > TimeUnit.HOURS.toMillis(1)) { + VPNExecutor.threadExecutor.execute(() -> deleteResponse(ip)); + return null; + } - if(System.currentTimeMillis() - response.getLastAccess() > TimeUnit.HOURS.toMillis(1)) { - VPNExecutor.threadExecutor.execute(() -> deleteResponse(ip)); - return Optional.empty(); + return responseFromDoc; } - - return Optional.of(response); + } catch (SQLException e) { + AntiVPN.getInstance().getExecutor() + .logException("Failed to get response from cache due to SQL error for: " + ip, e); } - } catch (SQLException throwables) { - throwables.printStackTrace(); - } + return null; + }); - return Optional.empty(); + return Optional.ofNullable(response); } /* @@ -81,6 +93,8 @@ public class MySqlVPN implements VPNDatabase { if (isDisabled()) return; + cachedResponses.put(toCache.getIp(), toCache); + Query.prepare("insert into `responses` (`ip`,`asn`,`countryName`,`countryCode`,`city`,`timeZone`," + "`method`,`isp`,`proxy`,`cached`,`inserted`,`latitude`,`longitude`) values (?,?,?,?,?,?,?,?,?,?,?,?,?)") .append(toCache.getIp()).append(toCache.getAsn()).append(toCache.getCountryName()) @@ -103,10 +117,10 @@ public class MySqlVPN implements VPNDatabase { public boolean isWhitelisted(UUID uuid) { if (isDisabled()) return false; - ResultSet set = Query.prepare("select uuid from `whitelisted` where `uuid` = ? limit 1") - .append(uuid.toString()).executeQuery(); - - return set != null && set.next() && set.getString("uuid") != null; + try(ResultSet set = Query.prepare("select uuid from `whitelisted` where `uuid` = ? limit 1") + .append(uuid.toString()).executeQuery()) { + return set != null && set.next() && set.getString("uuid") != null; + } } @SneakyThrows @@ -114,11 +128,10 @@ public class MySqlVPN implements VPNDatabase { public boolean isWhitelisted(String ip) { if (isDisabled()) return false; - ResultSet set = Query.prepare("select `ip` from `whitelisted-ips` where `ip` = ? limit 1") - .append(ip).executeQuery(); - - - return set != null && set.next() && set.getString("ip") != null; + try(ResultSet set = Query.prepare("select `ip` from `whitelisted-ips` where `ip` = ? limit 1") + .append(ip).executeQuery()) { + return set != null && set.next() && set.getString("ip") != null; + } } @Override @@ -199,13 +212,13 @@ public class MySqlVPN implements VPNDatabase { if(MySQL.isClosed()) return; VPNExecutor.threadExecutor.execute(() -> { - ResultSet set = Query.prepare("select * from `alerts` where `uuid` = ? limit 1") - .append(uuid.toString()).executeQuery(); - try { + try(ResultSet set = Query.prepare("select * from `alerts` where `uuid` = ? limit 1") + .append(uuid.toString()).executeQuery()) { result.accept(set != null && set.next() && set.getString("uuid") != null); } catch (SQLException e) { - e.printStackTrace(); + AntiVPN.getInstance().getExecutor() + .logException("Failed to get alerts state from database for: " + uuid, e); result.accept(false); } }); @@ -247,18 +260,16 @@ public class MySqlVPN implements VPNDatabase { AntiVPN.getInstance().getExecutor().log("Creating tables..."); //Running check for old table types to update - oldTableCheck: { - Query.prepare("select `DATA_TYPE` from INFORMATION_SCHEMA.COLUMNS " + - "WHERE table_name = 'responses' AND COLUMN_NAME = 'isp';").execute(set -> { - if(set.getObject("DATA_TYPE").toString().contains("varchar")) { - AntiVPN.getInstance().getExecutor().log("Using old database format for storing responses! " + - "Dropping table and creating a new one..."); - if(Query.prepare("drop table `responses`").execute() > 0) { - AntiVPN.getInstance().getExecutor().log("Successfully dropped table!"); - } - } - }); - } + Query.prepare("select `DATA_TYPE` from INFORMATION_SCHEMA.COLUMNS " + + "WHERE table_name = 'responses' AND COLUMN_NAME = 'isp';").execute(set -> { + if(set.getObject("DATA_TYPE").toString().contains("varchar")) { + AntiVPN.getInstance().getExecutor().log("Using old database format for storing responses! " + + "Dropping table and creating a new one..."); + if(Query.prepare("drop table `responses`").execute() > 0) { + AntiVPN.getInstance().getExecutor().log("Successfully dropped table!"); + } + } + }); Query.prepare("create table if not exists `whitelisted` (`uuid` varchar(36) not null)").execute(); Query.prepare("create table if not exists `whitelisted-ips` (`ip` varchar(45) not null)").execute(); @@ -277,15 +288,13 @@ public class MySqlVPN implements VPNDatabase { " AND table_name='whitelisted' AND index_name='uuid_1';"; ResultSet rs = Query.prepare(query).executeQuery(); int id = 0; - whitelistedIndex: { - while (rs.next()) { - id = rs.getInt("IndexExists"); - } - if (id == 0) { - Query.prepare("create index `uuid_1` on `whitelisted` (`uuid`)").execute(); - } - id = 0; + while (rs.next()) { + id = rs.getInt("IndexExists"); } + if (id == 0) { + Query.prepare("create index `uuid_1` on `whitelisted` (`uuid`)").execute(); + } + id = 0; responsesIndex: { query = "SELECT COUNT(1) IndexExists FROM INFORMATION_SCHEMA.STATISTICS WHERE table_schema=DATABASE() " + "AND table_name='responses' AND index_name='ip_1';"; diff --git a/Common/src/main/java/dev/brighten/antivpn/depends/LibraryLoader.java b/Common/src/main/java/dev/brighten/antivpn/depends/LibraryLoader.java new file mode 100644 index 0000000..2ef3bca --- /dev/null +++ b/Common/src/main/java/dev/brighten/antivpn/depends/LibraryLoader.java @@ -0,0 +1,365 @@ +/* + * This file is part of helper, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package dev.brighten.antivpn.depends; + +import dev.brighten.antivpn.AntiVPN; +import dev.brighten.antivpn.utils.NonnullByDefault; +import dev.brighten.antivpn.utils.Supplier; +import dev.brighten.antivpn.utils.Suppliers; +import lombok.Getter; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.commons.ClassRemapper; +import org.objectweb.asm.commons.Remapper; + +import java.io.*; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Files; +import java.util.*; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.JarOutputStream; + +/** + * Resolves {@link MavenLibrary} annotations for a class, and loads the dependency + * into the classloader. + */ +@SuppressWarnings("CallToPrintStackTrace") +@NonnullByDefault +public final class LibraryLoader { + + @SuppressWarnings("Guava") + private static final Supplier URL_INJECTOR = Suppliers.memoize(() -> + URLClassLoaderAccess.create((URLClassLoader) AntiVPN.getInstance().getClass().getClassLoader())); + + public static void loadAll(Object object) { + loadAll(object.getClass()); + } + + public static void loadAll(Class clazz) { + MavenLibrary[] libs = clazz.getDeclaredAnnotationsByType(MavenLibrary.class); + + for (MavenLibrary lib : libs) { + // Create relocations map if any are defined + Map relocations = new HashMap<>(); + for (Relocate relocate : lib.relocations()) { + relocations.put(relocate.from().replace("\\", ""), relocate.to()); + } + + load(lib.groupId().replace("\\", ""), lib.artifactId(), lib.version(), lib.repo().url(), relocations); + } + } + + public static void load(String groupId, String artifactId, String version, String repoUrl, + Map relocations) { + load(new Dependency(groupId, artifactId, version, repoUrl), relocations); + } + + public static void load(Dependency d, Map relocations) { + System.out.printf("Loading dependency %s:%s:%s from %s%n", + d.getGroupId(), d.getArtifactId(), d.getVersion(), d.getRepoUrl()); + String name = d.getArtifactId() + "-" + d.getVersion(); + + // If we have relocations, add a suffix to identify the relocated version + String fileName = name + ".jar"; + if (!relocations.isEmpty()) { + fileName = name + "-relocated.jar"; + } + + File saveLocation = new File(getLibFolder(), fileName); + File originalJar = new File(getLibFolder(), name + ".jar"); + + // Download the original jar if it doesn't exist + if (!originalJar.exists()) { + try { + System.out.println("Dependency '" + name + + "' is not already in the libraries folder. Attempting to download..."); + URL url = d.getUrl(); + + try (InputStream is = url.openStream()) { + Files.copy(is, originalJar.toPath()); + } + System.out.println("Dependency '" + name + "' successfully downloaded."); + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException("Unable to download dependency: " + d, e); + } + } + + // If we have relocations, create a relocated jar + if (!relocations.isEmpty() && !saveLocation.exists()) { + try { + System.out.println("Relocating packages for " + name + "..."); + relocateJar(originalJar, saveLocation, relocations); + System.out.println("Successfully relocated packages for " + name); + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException("Failed to relocate packages for dependency: " + d, e); + } + } + + // Load the appropriate jar (original or relocated) + File jarToLoad = relocations.isEmpty() ? originalJar : saveLocation; + + if (!jarToLoad.exists()) { + throw new RuntimeException("Unable to find dependency jar: " + jarToLoad.getAbsolutePath()); + } + + try { + URL_INJECTOR.get().addURL(jarToLoad.toURI().toURL()); + } catch (Exception e) { + throw new RuntimeException("Unable to load dependency: " + jarToLoad, e); + } + + System.out.println("Loaded dependency '" + name + "' successfully."); + } + + private static void relocateJar(File sourceJar, File targetJar, Map relocations) + throws IOException { + // Track service files to avoid duplicates + Map serviceFiles = new HashMap<>(); + + try (JarFile jar = new JarFile(sourceJar); + JarOutputStream jos = new JarOutputStream(Files.newOutputStream(targetJar.toPath()))) { + + Enumeration entries = jar.entries(); + + while (entries.hasMoreElements()) { + JarEntry entry = entries.nextElement(); + String name = entry.getName(); + + // Skip directories + if (entry.isDirectory()) { + continue; + } + + try (InputStream is = jar.getInputStream(entry)) { + if (name.startsWith("META-INF/services/")) { + // Process service files but don't write yet + processServiceFile(name, is, serviceFiles, relocations); + } else if (name.endsWith(".class")) { + // Relocate class file path as well as content + String relocatedPath = relocateClassPath(name, relocations); + + JarEntry newEntry = new JarEntry(relocatedPath); + jos.putNextEntry(newEntry); + + byte[] classBytes = readAllBytes(is); + byte[] relocatedBytes = relocateClass(classBytes, relocations); + jos.write(relocatedBytes); + jos.closeEntry(); + } else { + // Copy other files as-is + JarEntry newEntry = new JarEntry(name); + jos.putNextEntry(newEntry); + copyStream(is, jos); + jos.closeEntry(); + } + } + } + + // Now write all service files after processing + for (Map.Entry entry : serviceFiles.entrySet()) { + try { + JarEntry serviceEntry = new JarEntry(entry.getKey()); + jos.putNextEntry(serviceEntry); + jos.write(entry.getValue().toString().getBytes()); + jos.closeEntry(); + } catch (Exception e) { + // Log but continue with other service files + System.out.println("Warning: Could not write service file " + + entry.getKey() + ": " + e.getMessage()); + } + } + } + } + + private static void processServiceFile(String name, InputStream is, + Map serviceFiles, + Map relocations) throws IOException { + // Read service file content + String content = new String(readAllBytes(is)); + StringBuilder contentBuilder = serviceFiles.computeIfAbsent(name, k -> new StringBuilder()); + + // Process and relocate service implementations + for (String line : content.split("\n")) { + String trimmed = line.trim(); + if (!trimmed.isEmpty() && !trimmed.startsWith("#")) { + for (Map.Entry relocation : relocations.entrySet()) { + if (trimmed.startsWith(relocation.getKey())) { + trimmed = relocation.getValue() + + trimmed.substring(relocation.getKey().length()); + break; + } + } + } + contentBuilder.append(trimmed).append("\n"); + } + } + + private static byte[] relocateClass(byte[] classBytes, Map relocations) { + try { + // Convert to slash notation for ASM + Remapper prefixRemapper = getPrefixRemapper(relocations); + + // Create custom ClassWriter to handle missing classes + ClassReader reader = new ClassReader(classBytes); + ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS) { + @Override + protected String getCommonSuperClass(String type1, String type2) { + try { + return super.getCommonSuperClass(type1, type2); + } catch (RuntimeException e) { + // Fall back to Object when classes can't be loaded + return "java/lang/Object"; + } + } + }; + + ClassVisitor visitor = new ClassRemapper(writer, prefixRemapper); + + // Process class with remapper + reader.accept(visitor, ClassReader.EXPAND_FRAMES); + + return writer.toByteArray(); + } catch (Exception e) { + e.printStackTrace(); + return classBytes; + } + } + + private static Remapper getPrefixRemapper(Map relocations) { + Map slashMappings = new HashMap<>(); + for (Map.Entry entry : relocations.entrySet()) { + String fromSlash = entry.getKey().replace('.', '/'); + String toSlash = entry.getValue().replace('.', '/'); + slashMappings.put(fromSlash, toSlash); + } + + // Create customized remapper for package prefixes + return new Remapper() { + @Override + public String map(String typeName) { + if (typeName == null) return null; + + for (Map.Entry entry : slashMappings.entrySet()) { + String from = entry.getKey(); + String to = entry.getValue(); + + if (typeName.startsWith(from)) { + return to + typeName.substring(from.length()); + } + } + return typeName; + } + }; + } + + private static String relocateClassPath(String path, Map relocations) { + // Convert path to package format (replacing / with .) + String packagePath = path.substring(0, path.length() - 6).replace('/', '.'); + + // Apply relocations + for (Map.Entry relocation : relocations.entrySet()) { + if (packagePath.startsWith(relocation.getKey())) { + packagePath = relocation.getValue() + packagePath.substring(relocation.getKey().length()); + break; + } + } + + // Convert back to path format + return packagePath.replace('.', '/') + ".class"; + } + + private static byte[] readAllBytes(InputStream is) throws IOException { + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + int bytesRead; + byte[] data = new byte[1024]; + while ((bytesRead = is.read(data, 0, data.length)) != -1) { + buffer.write(data, 0, bytesRead); + } + return buffer.toByteArray(); + } + + private static void copyStream(InputStream is, OutputStream os) throws IOException { + byte[] buffer = new byte[1024]; + int bytesRead; + while ((bytesRead = is.read(buffer)) != -1) { + os.write(buffer, 0, bytesRead); + } + } + + private static File getLibFolder() { + File pluginDataFolder = AntiVPN.getInstance().getPluginFolder(); + File libs = new File(pluginDataFolder, "libraries"); + if(libs.mkdirs()) { + System.out.println("Created libraries folder!"); + } + return libs; + } + + @Getter + @NonnullByDefault +// Fix the Dependency class to preserve original groupId for downloading + public static final class Dependency { + private final String groupId; + private final String artifactId; + private final String version; + private final String repoUrl; + // Keep the original groupId/artifactId for Maven downloads + private final String originalGroupId; + private final String originalArtifactId; + + public Dependency(String groupId, String artifactId, String version, String repoUrl) { + this.originalGroupId = Objects.requireNonNull(groupId, "groupId"); + this.originalArtifactId = Objects.requireNonNull(artifactId, "artifactId"); + this.groupId = this.originalGroupId; + this.artifactId = this.originalArtifactId; + this.version = Objects.requireNonNull(version, "version"); + this.repoUrl = Objects.requireNonNull(repoUrl, "repoUrl"); + } + + public URL getUrl() throws MalformedURLException { + String repo = this.repoUrl; + if (!repo.endsWith("/")) { + repo += "/"; + } + repo += "%s/%s/%s/%s-%s.jar"; + + // Always use original groupId for Maven repository URL + String url = String.format(repo, this.originalGroupId.replace(".", "/"), + this.originalArtifactId, this.version, this.originalArtifactId, this.version); + return new URL(url); + } + + // Rest of the class unchanged + } + + +} diff --git a/Common/src/main/java/dev/brighten/antivpn/depends/MavenLibraries.java b/Common/src/main/java/dev/brighten/antivpn/depends/MavenLibraries.java new file mode 100644 index 0000000..189f737 --- /dev/null +++ b/Common/src/main/java/dev/brighten/antivpn/depends/MavenLibraries.java @@ -0,0 +1,40 @@ +/* + * This file is part of helper, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package dev.brighten.antivpn.depends; + +import java.lang.annotation.*; + +/** + * Annotation to indicate the required libraries for a class. + */ +@Documented +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface MavenLibraries { + + MavenLibrary[] value() default {}; + +} diff --git a/Common/src/main/java/dev/brighten/antivpn/depends/MavenLibrary.java b/Common/src/main/java/dev/brighten/antivpn/depends/MavenLibrary.java new file mode 100644 index 0000000..eed8117 --- /dev/null +++ b/Common/src/main/java/dev/brighten/antivpn/depends/MavenLibrary.java @@ -0,0 +1,69 @@ +/* + * This file is part of helper, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package dev.brighten.antivpn.depends; + +import java.lang.annotation.*; + +/** + * Annotation to indicate a required library for a class. + */ +@Documented +@Repeatable(MavenLibraries.class) +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface MavenLibrary { + + /** + * The group id of the library + * + * @return the group id of the library + */ + String groupId(); + + /** + * The artifact id of the library + * + * @return the artifact id of the library + */ + String artifactId(); + + /** + * The version of the library + * + * @return the version of the library + */ + String version(); + + /** + * The repo where the library can be obtained from + * + * @return the repo where the library can be obtained from + */ + Repository repo() default @Repository(url = "https://repo1.maven.org/maven2"); + + Relocate[] relocations() default {}; // Add this line + +} diff --git a/Common/src/main/java/dev/brighten/antivpn/depends/Relocate.java b/Common/src/main/java/dev/brighten/antivpn/depends/Relocate.java new file mode 100644 index 0000000..32b8af5 --- /dev/null +++ b/Common/src/main/java/dev/brighten/antivpn/depends/Relocate.java @@ -0,0 +1,14 @@ +package dev.brighten.antivpn.depends; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({}) +public @interface Relocate { + String from(); + String to(); +} \ No newline at end of file diff --git a/Common/src/main/java/dev/brighten/antivpn/depends/Repository.java b/Common/src/main/java/dev/brighten/antivpn/depends/Repository.java new file mode 100644 index 0000000..4381225 --- /dev/null +++ b/Common/src/main/java/dev/brighten/antivpn/depends/Repository.java @@ -0,0 +1,45 @@ +/* + * This file is part of helper, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package dev.brighten.antivpn.depends; + +import java.lang.annotation.*; + +/** + * Represents a maven repository. + */ +@Documented +@Target(ElementType.LOCAL_VARIABLE) +@Retention(RetentionPolicy.RUNTIME) +public @interface Repository { + + /** + * Gets the base url of the repository. + * + * @return the base url of the repository + */ + String url(); + +} diff --git a/Common/src/main/java/dev/brighten/antivpn/depends/URLClassLoaderAccess.java b/Common/src/main/java/dev/brighten/antivpn/depends/URLClassLoaderAccess.java new file mode 100644 index 0000000..a0159f0 --- /dev/null +++ b/Common/src/main/java/dev/brighten/antivpn/depends/URLClassLoaderAccess.java @@ -0,0 +1,175 @@ +/* + * This file is part of helper, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package dev.brighten.antivpn.depends; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.Collection; + +/** + * Provides access to {@link URLClassLoader}#addURL. + */ +public abstract class URLClassLoaderAccess { + + /** + * Creates a {@link URLClassLoaderAccess} for the given class loader. + * + * @param classLoader the class loader + * @return the access object + */ + static URLClassLoaderAccess create(URLClassLoader classLoader) { + if (Reflection.isSupported()) { + return new Reflection(classLoader); + } else if (Unsafe.isSupported()) { + return new Unsafe(classLoader); + } else { + return Noop.INSTANCE; + } + } + + private final URLClassLoader classLoader; + + protected URLClassLoaderAccess(URLClassLoader classLoader) { + this.classLoader = classLoader; + } + + + /** + * Adds the given URL to the class loader. + * + * @param url the URL to add + */ + public abstract void addURL(URL url); + + /** + * Accesses using reflection, not supported on Java 9+. + */ + private static class Reflection extends URLClassLoaderAccess { + private static final Method ADD_URL_METHOD; + + static { + Method addUrlMethod; + try { + addUrlMethod = URLClassLoader.class.getDeclaredMethod("addURL", URL.class); + addUrlMethod.setAccessible(true); + } catch (Exception e) { + addUrlMethod = null; + } + ADD_URL_METHOD = addUrlMethod; + } + + private static boolean isSupported() { + return ADD_URL_METHOD != null; + } + + Reflection(URLClassLoader classLoader) { + super(classLoader); + } + + @Override + public void addURL(URL url) { + try { + ADD_URL_METHOD.invoke(super.classLoader, url); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + } + + /** + * Accesses using sun.misc.Unsafe, supported on Java 9+. + * + * @author Vaishnav Anil (...) + */ + private static class Unsafe extends URLClassLoaderAccess { + private static final sun.misc.Unsafe UNSAFE; + + static { + sun.misc.Unsafe unsafe; + try { + Field unsafeField = sun.misc.Unsafe.class.getDeclaredField("theUnsafe"); + unsafeField.setAccessible(true); + unsafe = (sun.misc.Unsafe) unsafeField.get(null); + } catch (Throwable t) { + unsafe = null; + } + UNSAFE = unsafe; + } + + private static boolean isSupported() { + return UNSAFE != null; + } + + private final Collection unopenedURLs; + private final Collection pathURLs; + + @SuppressWarnings("unchecked") + Unsafe(URLClassLoader classLoader) { + super(classLoader); + + Collection unopenedURLs; + Collection pathURLs; + try { + Object ucp = fetchField(URLClassLoader.class, classLoader, "ucp"); + unopenedURLs = (Collection) fetchField(ucp.getClass(), ucp, "unopenedUrls"); + pathURLs = (Collection) fetchField(ucp.getClass(), ucp, "path"); + } catch (Throwable e) { + unopenedURLs = null; + pathURLs = null; + } + this.unopenedURLs = unopenedURLs; + this.pathURLs = pathURLs; + } + + private static Object fetchField(final Class clazz, final Object object, final String name) throws NoSuchFieldException { + Field field = clazz.getDeclaredField(name); + long offset = UNSAFE.objectFieldOffset(field); + return UNSAFE.getObject(object, offset); + } + + @Override + public void addURL(URL url) { + this.unopenedURLs.add(url); + this.pathURLs.add(url); + } + } + + private static class Noop extends URLClassLoaderAccess { + private static final Noop INSTANCE = new Noop(); + + private Noop() { + super(null); + } + + @Override + public void addURL(URL url) { + throw new UnsupportedOperationException(); + } + } + +} diff --git a/Common/src/main/java/dev/brighten/antivpn/utils/ExtraObjectsMethodsForWeb.java b/Common/src/main/java/dev/brighten/antivpn/utils/ExtraObjectsMethodsForWeb.java new file mode 100644 index 0000000..484f35e --- /dev/null +++ b/Common/src/main/java/dev/brighten/antivpn/utils/ExtraObjectsMethodsForWeb.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2016 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package dev.brighten.antivpn.utils; + +/** + * Holder for extra methods of {@code Objects} only in web. Intended to be empty for regular + * version. + */ +abstract class ExtraObjectsMethodsForWeb {} diff --git a/Common/src/main/java/dev/brighten/antivpn/utils/NonnullByDefault.java b/Common/src/main/java/dev/brighten/antivpn/utils/NonnullByDefault.java new file mode 100644 index 0000000..48a1f73 --- /dev/null +++ b/Common/src/main/java/dev/brighten/antivpn/utils/NonnullByDefault.java @@ -0,0 +1,36 @@ +/* + * This file is part of helper, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package dev.brighten.antivpn.utils; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Documented +@Retention(RetentionPolicy.RUNTIME) +public @interface NonnullByDefault { + +} diff --git a/Common/src/main/java/dev/brighten/antivpn/utils/NullnessCasts.java b/Common/src/main/java/dev/brighten/antivpn/utils/NullnessCasts.java new file mode 100644 index 0000000..24d68c9 --- /dev/null +++ b/Common/src/main/java/dev/brighten/antivpn/utils/NullnessCasts.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2021 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package dev.brighten.antivpn.utils; + +/** A utility method to perform unchecked casts to suppress errors produced by nullness analyses. */ +final class NullnessCasts { + /** + * Accepts a {@code @Nullable T} and returns a plain {@code T}, without performing any check that + * that conversion is safe. + * + *

This method is intended to help with usages of type parameters that have { + * ParametricNullness parametric nullness}. If a type parameter instead ranges over only non-null + * types (or if the type is a non-variable type, like {@code String}), then code should almost + * never use this method, preferring instead to call {@code requireNonNull} so as to benefit from + * its runtime check. + * + *

An example use case for this method is in implementing an {@code Iterator} whose {@code + * next} field is lazily initialized. The type of that field would be {@code @Nullable T}, and the + * code would be responsible for populating a "real" {@code T} (which might still be the value + * {@code null}!) before returning it to callers. Depending on how the code is structured, a + * nullness analysis might not understand that the field has been populated. To avoid that problem + * without having to add {@code @SuppressWarnings}, the code can call this method. + * + *

Why not just add {@code SuppressWarnings}? The problem is that this method is + * typically useful for {@code return} statements. That leaves the code with two options: Either + * add the suppression to the whole method (which turns off checking for a large section of code), + * or extract a variable, and put the suppression on that. However, a local variable typically + * doesn't work: Because nullness analyses typically infer the nullness of local variables, + * there's no way to assign a {@code @Nullable T} to a field {@code T foo;} and instruct the + * analysis that that means "plain {@code T}" rather than the inferred type {@code @Nullable T}. + * (Even if supported added {@code @NonNull}, that would not help, since the problem case + * addressed by this method is the case in which {@code T} has parametric nullness -- and thus its + * value may be legitimately {@code null}.) + */ + @SuppressWarnings("nullness") + static T uncheckedCastNullableTToT(T t) { + return t; + } + + private NullnessCasts() {} +} diff --git a/Common/src/main/java/dev/brighten/antivpn/utils/Preconditions.java b/Common/src/main/java/dev/brighten/antivpn/utils/Preconditions.java new file mode 100644 index 0000000..7d24918 --- /dev/null +++ b/Common/src/main/java/dev/brighten/antivpn/utils/Preconditions.java @@ -0,0 +1,244 @@ +// +// Source code recreated from a .class file by IntelliJ IDEA +// (powered by FernFlower decompiler) +// + +package dev.brighten.antivpn.utils; + +public final class Preconditions { + private Preconditions() { + } + + public static T checkNotNull(T reference) { + if (reference == null) { + throw new NullPointerException(); + } else { + return reference; + } + } + + public static T checkNotNull(T reference, Object errorMessage) { + if (reference == null) { + throw new NullPointerException(String.valueOf(errorMessage)); + } else { + return reference; + } + } + + public static T checkNotNull(T reference, String errorMessageTemplate, Object... errorMessageArgs) { + if (reference == null) { + throw new NullPointerException(format(errorMessageTemplate, errorMessageArgs)); + } else { + return reference; + } + } + + public static T checkNotNull(T obj, String errorMessageTemplate, char p1) { + if (obj == null) { + throw new NullPointerException(format(errorMessageTemplate, p1)); + } else { + return obj; + } + } + + public static T checkNotNull(T obj, String errorMessageTemplate, int p1) { + if (obj == null) { + throw new NullPointerException(format(errorMessageTemplate, p1)); + } else { + return obj; + } + } + + public static T checkNotNull(T obj, String errorMessageTemplate, long p1) { + if (obj == null) { + throw new NullPointerException(format(errorMessageTemplate, p1)); + } else { + return obj; + } + } + + public static T checkNotNull(T obj, String errorMessageTemplate, Object p1) { + if (obj == null) { + throw new NullPointerException(format(errorMessageTemplate, p1)); + } else { + return obj; + } + } + + public static T checkNotNull(T obj, String errorMessageTemplate, char p1, char p2) { + if (obj == null) { + throw new NullPointerException(format(errorMessageTemplate, p1, p2)); + } else { + return obj; + } + } + + public static T checkNotNull(T obj, String errorMessageTemplate, char p1, int p2) { + if (obj == null) { + throw new NullPointerException(format(errorMessageTemplate, p1, p2)); + } else { + return obj; + } + } + + public static T checkNotNull(T obj, String errorMessageTemplate, char p1, long p2) { + if (obj == null) { + throw new NullPointerException(format(errorMessageTemplate, p1, p2)); + } else { + return obj; + } + } + + public static T checkNotNull(T obj, String errorMessageTemplate, char p1, Object p2) { + if (obj == null) { + throw new NullPointerException(format(errorMessageTemplate, p1, p2)); + } else { + return obj; + } + } + + public static T checkNotNull(T obj, String errorMessageTemplate, int p1, char p2) { + if (obj == null) { + throw new NullPointerException(format(errorMessageTemplate, p1, p2)); + } else { + return obj; + } + } + + public static T checkNotNull(T obj, String errorMessageTemplate, int p1, int p2) { + if (obj == null) { + throw new NullPointerException(format(errorMessageTemplate, p1, p2)); + } else { + return obj; + } + } + + public static T checkNotNull(T obj, String errorMessageTemplate, int p1, long p2) { + if (obj == null) { + throw new NullPointerException(format(errorMessageTemplate, p1, p2)); + } else { + return obj; + } + } + + public static T checkNotNull(T obj, String errorMessageTemplate, int p1, Object p2) { + if (obj == null) { + throw new NullPointerException(format(errorMessageTemplate, p1, p2)); + } else { + return obj; + } + } + + public static T checkNotNull(T obj, String errorMessageTemplate, long p1, char p2) { + if (obj == null) { + throw new NullPointerException(format(errorMessageTemplate, p1, p2)); + } else { + return obj; + } + } + + public static T checkNotNull(T obj, String errorMessageTemplate, long p1, int p2) { + if (obj == null) { + throw new NullPointerException(format(errorMessageTemplate, p1, p2)); + } else { + return obj; + } + } + + public static T checkNotNull(T obj, String errorMessageTemplate, long p1, long p2) { + if (obj == null) { + throw new NullPointerException(format(errorMessageTemplate, p1, p2)); + } else { + return obj; + } + } + + public static T checkNotNull(T obj, String errorMessageTemplate, long p1, Object p2) { + if (obj == null) { + throw new NullPointerException(format(errorMessageTemplate, p1, p2)); + } else { + return obj; + } + } + + public static T checkNotNull(T obj, String errorMessageTemplate, Object p1, char p2) { + if (obj == null) { + throw new NullPointerException(format(errorMessageTemplate, p1, p2)); + } else { + return obj; + } + } + + public static T checkNotNull(T obj, String errorMessageTemplate, Object p1, int p2) { + if (obj == null) { + throw new NullPointerException(format(errorMessageTemplate, p1, p2)); + } else { + return obj; + } + } + + public static T checkNotNull(T obj, String errorMessageTemplate, Object p1, long p2) { + if (obj == null) { + throw new NullPointerException(format(errorMessageTemplate, p1, p2)); + } else { + return obj; + } + } + + public static T checkNotNull(T obj, String errorMessageTemplate, Object p1, Object p2) { + if (obj == null) { + throw new NullPointerException(format(errorMessageTemplate, p1, p2)); + } else { + return obj; + } + } + + public static T checkNotNull(T obj, String errorMessageTemplate, Object p1, Object p2, Object p3) { + if (obj == null) { + throw new NullPointerException(format(errorMessageTemplate, p1, p2, p3)); + } else { + return obj; + } + } + + public static T checkNotNull(T obj, String errorMessageTemplate, Object p1, Object p2, Object p3, Object p4) { + if (obj == null) { + throw new NullPointerException(format(errorMessageTemplate, p1, p2, p3, p4)); + } else { + return obj; + } + } + + static String format(String template, Object... args) { + template = String.valueOf(template); + StringBuilder builder = new StringBuilder(template.length() + 16 * args.length); + int templateStart = 0; + + int i; + int placeholderStart; + for(i = 0; i < args.length; templateStart = placeholderStart + 2) { + placeholderStart = template.indexOf("%s", templateStart); + if (placeholderStart == -1) { + break; + } + + builder.append(template, templateStart, placeholderStart); + builder.append(args[i++]); + } + + builder.append(template, templateStart, template.length()); + if (i < args.length) { + builder.append(" ["); + builder.append(args[i++]); + + while(i < args.length) { + builder.append(", "); + builder.append(args[i++]); + } + + builder.append(']'); + } + + return builder.toString(); + } +} diff --git a/Common/src/main/java/dev/brighten/antivpn/utils/Supplier.java b/Common/src/main/java/dev/brighten/antivpn/utils/Supplier.java new file mode 100644 index 0000000..c1f7365 --- /dev/null +++ b/Common/src/main/java/dev/brighten/antivpn/utils/Supplier.java @@ -0,0 +1,11 @@ +// +// Source code recreated from a .class file by IntelliJ IDEA +// (powered by FernFlower decompiler) +// + +package dev.brighten.antivpn.utils; + +@FunctionalInterface +public interface Supplier extends java.util.function.Supplier { + T get(); +} diff --git a/Common/src/main/java/dev/brighten/antivpn/utils/Suppliers.java b/Common/src/main/java/dev/brighten/antivpn/utils/Suppliers.java new file mode 100644 index 0000000..d380307 --- /dev/null +++ b/Common/src/main/java/dev/brighten/antivpn/utils/Suppliers.java @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2007 The Guava Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package dev.brighten.antivpn.utils; + +import java.io.Serializable; + +import static dev.brighten.antivpn.utils.NullnessCasts.uncheckedCastNullableTToT; +import static dev.brighten.antivpn.utils.Preconditions.checkNotNull; +import static java.util.Objects.requireNonNull; + +/** + * Useful suppliers. + * + *

All methods return serializable suppliers as long as they're given serializable parameters. + * + * @author Laurence Gonsalves + * @author Harry Heymann + * @since 2.0 + */ +public final class Suppliers { + private Suppliers() {} + + /** + * Returns a supplier which caches the instance retrieved during the first call to {@code get()} + * and returns that value on subsequent calls to {@code get()}. See: memoization + * + *

The returned supplier is thread-safe. The delegate's {@code get()} method will be invoked at + * most once unless the underlying {@code get()} throws an exception. The supplier's serialized + * form does not contain the cached value, which will be recalculated when {@code get()} is called + * on the reserialized instance. + * + *

When the underlying delegate throws an exception then this memoizing supplier will keep + * delegating calls until it returns valid data. + * + *

If {@code delegate} is an instance created by an earlier call to {@code memoize}, it is + * returned directly. + */ + public static Supplier memoize(Supplier delegate) { + if (delegate instanceof NonSerializableMemoizingSupplier + || delegate instanceof MemoizingSupplier) { + return delegate; + } + return delegate instanceof Serializable + ? new MemoizingSupplier<>(delegate) + : new NonSerializableMemoizingSupplier<>(delegate); + } + + static class MemoizingSupplier implements Supplier, Serializable { + final Supplier delegate; + transient volatile boolean initialized; + // "value" does not need to be volatile; visibility piggy-backs + // on volatile read of "initialized". + transient T value; + + MemoizingSupplier(Supplier delegate) { + this.delegate = checkNotNull(delegate); + } + + @Override + public T get() { + // A 2-field variant of Double Checked Locking. + if (!initialized) { + synchronized (this) { + if (!initialized) { + T t = delegate.get(); + value = t; + initialized = true; + return t; + } + } + } + // This is safe because we checked `initialized.` + return uncheckedCastNullableTToT(value); + } + + @Override + public String toString() { + return "Suppliers.memoize(" + + (initialized ? "" : delegate) + + ")"; + } + + private static final long serialVersionUID = 0; + } + + static class NonSerializableMemoizingSupplier implements Supplier { + volatile Supplier delegate; + volatile boolean initialized; + // "value" does not need to be volatile; visibility piggy-backs + // on volatile read of "initialized". + T value; + + NonSerializableMemoizingSupplier(Supplier delegate) { + this.delegate = checkNotNull(delegate); + } + + @Override + public T get() { + // A 2-field variant of Double Checked Locking. + if (!initialized) { + synchronized (this) { + if (!initialized) { + /* + * requireNonNull is safe because we read and write `delegate` under synchronization. + * + * TODO(cpovirk): To avoid having to check for null, replace `delegate` with a singleton + * `Supplier` that always throws an exception. + */ + T t = requireNonNull(delegate).get(); + value = t; + initialized = true; + // Release the delegate to GC. + delegate = null; + return t; + } + } + } + // This is safe because we checked `initialized.` + return uncheckedCastNullableTToT(value); + } + + @Override + public String toString() { + Supplier delegate = this.delegate; + return "Suppliers.memoize(" + + (delegate == null ? "" : delegate) + + ")"; + } + } +} diff --git a/Assembly/pom.xml b/Universal/pom.xml similarity index 58% rename from Assembly/pom.xml rename to Universal/pom.xml index a118190..ebe5ca1 100644 --- a/Assembly/pom.xml +++ b/Universal/pom.xml @@ -2,26 +2,56 @@ + 4.0.0 - AntiVPN dev.brighten.antivpn + AntiVPN 1.9.4-DEV - 4.0.0 - Assembly + Universal - 8 - 8 + 17 + 17 + UTF-8 + + + dev.brighten.antivpn + Common + ${project.version} + + + dev.brighten.antivpn + Bukkit + ${project.version} + + + dev.brighten.antivpn + Bungee + ${project.version} + + + dev.brighten.antivpn + Velocity + ${project.version} + + + + + org.apache.maven.plugins maven-shade-plugin - 3.2.4 + 3.6.0 package @@ -29,15 +59,25 @@ shade + AntiVPN-${project.version}-universal + false + + + org.yaml.snakeyaml dev.brighten.antivpn.shaded.org.yaml.snakeyaml - com.google.common - dev.brighten.antivpn.shaded.com.google.common + org.bstats + dev.brighten.antivpn.shaded.org.bstats + + com.google + dev.brighten.antivpn.shaded.com.google + + @@ -46,37 +86,4 @@ - - - dev.brighten.antivpn - Bungee - ${version} - compile - - - dev.brighten.antivpn - Velocity - ${version} - compile - - - dev.brighten.antivpn - Common - ${version} - compile - - - org.xerial - sqlite-jdbc - 3.41.2.2 - compile - - - dev.brighten.antivpn - Bukkit - ${version} - compile - - - \ No newline at end of file diff --git a/Velocity/pom.xml b/Velocity/pom.xml index 045296f..f7dc4b3 100644 --- a/Velocity/pom.xml +++ b/Velocity/pom.xml @@ -12,8 +12,8 @@ Velocity - 8 - 8 + 17 + 17 @@ -49,17 +49,17 @@ org.apache.maven.plugins maven-compiler-plugin - 3.7.0 + 3.13.0 - 8 - 8 + 17 + 17 -XDignore.symbol.file org.apache.maven.plugins maven-shade-plugin - 3.1.0 + 3.6.0 diff --git a/Velocity/src/main/java/dev/brighten/antivpn/velocity/VelocityListener.java b/Velocity/src/main/java/dev/brighten/antivpn/velocity/VelocityListener.java index 04ab467..8c6a437 100644 --- a/Velocity/src/main/java/dev/brighten/antivpn/velocity/VelocityListener.java +++ b/Velocity/src/main/java/dev/brighten/antivpn/velocity/VelocityListener.java @@ -1,192 +1,155 @@ package dev.brighten.antivpn.velocity; -import com.google.common.cache.Cache; -import com.google.common.cache.CacheBuilder; -import com.velocitypowered.api.event.EventHandler; import com.velocitypowered.api.event.ResultedEvent; import com.velocitypowered.api.event.connection.DisconnectEvent; import com.velocitypowered.api.event.connection.LoginEvent; -import com.velocitypowered.api.scheduler.ScheduledTask; import dev.brighten.antivpn.AntiVPN; import dev.brighten.antivpn.api.APIPlayer; import dev.brighten.antivpn.api.VPNExecutor; import dev.brighten.antivpn.velocity.util.StringUtils; -import dev.brighten.antivpn.web.objects.VPNResponse; -import net.kyori.adventure.text.Component; import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; -import java.util.UUID; import java.util.concurrent.TimeUnit; import java.util.logging.Level; public class VelocityListener extends VPNExecutor { - private ScheduledTask cacheResetTask; - private final Cache responseCache = CacheBuilder.newBuilder() - .expireAfterWrite(5, TimeUnit.MINUTES) - .maximumSize(2000) - .build(); - - private final EventHandler loginEvent = event -> { - if (event.getResult().isAllowed()) { - if (event.getPlayer().hasPermission("antivpn.bypass") //Has bypass permission - //Is exempt - || AntiVPN.getInstance().getExecutor().isWhitelisted(event.getPlayer().getUniqueId()) - //Or has a name that starts with a certain prefix. This is for Bedrock exempting. - || AntiVPN.getInstance().getExecutor().isWhitelisted(event.getPlayer().getRemoteAddress() - .getAddress().getHostAddress()) - || AntiVPN.getInstance().getVpnConfig().getPrefixWhitelists().stream() - .anyMatch(prefix -> event.getPlayer().getUsername().startsWith(prefix))) return; - - if(responseCache.asMap().containsKey(event.getPlayer().getUniqueId())) { - VPNResponse cached = responseCache.getIfPresent(event.getPlayer().getUniqueId()); - - if (cached != null && cached.isProxy()) { - event.setResult(ResultedEvent.ComponentResult.denied(Component.text("No"))); - return; - } - } - - checkIp(event.getPlayer().getRemoteAddress().getAddress().getHostAddress(), - AntiVPN.getInstance().getVpnConfig().cachedResults(), result -> { - if (result.isSuccess()) { - // If the countryList() size is zero, no need to check. - // Running country check first - if (!AntiVPN.getInstance().getVpnConfig().countryList().isEmpty() - && !(AntiVPN.getInstance().getExecutor() - .isWhitelisted(event.getPlayer().getUniqueId()) //Is exempt - //Or has a name that starts with a certain prefix. This is for Bedrock exempting. - || AntiVPN.getInstance().getExecutor().isWhitelisted(event.getPlayer() - .getRemoteAddress().getAddress().getHostAddress())) - // This bit of code will decide whether or not to kick the player - // If it contains the code and it is set to whitelist, it will not kick - // as they are equal and vise versa. However, if the contains does not match - // the state, it will kick. - && AntiVPN.getInstance().getVpnConfig().countryList() - .contains(result.getCountryCode()) - != AntiVPN.getInstance().getVpnConfig().whitelistCountries()) { - //Using our built in kicking system if no commands are configured - if (AntiVPN.getInstance().getVpnConfig().countryKickCommands().isEmpty()) { - final String kickReason = AntiVPN.getInstance().getVpnConfig() - .countryVanillaKickReason(); - // Kicking our player - event.setResult(ResultedEvent.ComponentResult.denied(LegacyComponentSerializer.builder() - .character('&') - .build().deserialize(kickReason - .replace("%player%", event.getPlayer().getUsername()) - .replace("%country%", result.getCountryName()) - .replace("%code%", result.getCountryCode())))); - VelocityPlugin.INSTANCE.getServer().getScheduler() - .buildTask(VelocityPlugin.INSTANCE, () -> - event.getPlayer().disconnect(LegacyComponentSerializer.builder() - .character('&') - .build().deserialize(kickReason - .replace("%player%", event.getPlayer().getUsername()) - .replace("%country%", result.getCountryName()) - .replace("%code%", result.getCountryCode())))); - } else { - for (String cmd : AntiVPN.getInstance().getVpnConfig().countryKickCommands()) { - final String formattedCommand = StringUtils - .translateAlternateColorCodes('&', - cmd.replace("%player%", - event.getPlayer().getUsername()) - .replace("%country%", result.getCountryName()) - .replace("%code%", result.getCountryCode())); - // Running the command from console - VelocityPlugin.INSTANCE.getServer().getCommandManager() - .executeAsync(VelocityPlugin.INSTANCE.getServer() - .getConsoleCommandSource(), - StringUtils.translateAlternateColorCodes('&', - formattedCommand)); - } - } - } else if (result.isProxy()) { - if (AntiVPN.getInstance().getVpnConfig().kickPlayersOnDetect()) { - // Delay code execution - event.setResult(ResultedEvent.ComponentResult.denied(LegacyComponentSerializer.builder() - .character('&') - .build().deserialize(AntiVPN.getInstance().getVpnConfig() - .getKickString() - .replace("%player%", event.getPlayer().getUsername()) - .replace("%country%", result.getCountryName()) - .replace("%code%", result.getCountryCode())))); - - VelocityPlugin.INSTANCE.getServer().getScheduler() - .buildTask(VelocityPlugin.INSTANCE, () -> - event.getPlayer().disconnect(LegacyComponentSerializer.builder() - .character('&') - .build().deserialize(AntiVPN.getInstance().getVpnConfig() - .getKickString() - .replace("%player%", event.getPlayer().getUsername()) - .replace("%country%", result.getCountryName()) - .replace("%code%", result.getCountryCode())))) - .delay(1, TimeUnit.SECONDS).schedule(); - } - VelocityPlugin.INSTANCE.getLogger().info(event.getPlayer().getUsername() - + " joined on a VPN/Proxy (" + result.getMethod() + ")"); - //Ensuring the user wishes to alert to staff - if (AntiVPN.getInstance().getVpnConfig().alertToStaff()) - AntiVPN.getInstance().getPlayerExecutor().getOnlinePlayers().stream() - .filter(APIPlayer::isAlertsEnabled) - .forEach(pl -> - pl.sendMessage(AntiVPN.getInstance().getVpnConfig() - .alertMessage() - .replace("%player%", - event.getPlayer().getUsername()) - .replace("%reason%", - result.getMethod()) - .replace("%country%", - result.getCountryName()) - .replace("%city%", - result.getCity()))); - - //In case the user wants to run their own commands instead of using the - // built in kicking - if (AntiVPN.getInstance().getVpnConfig().runCommands()) { - for (String command : AntiVPN.getInstance().getVpnConfig().commands()) { - VelocityPlugin.INSTANCE.getServer().getCommandManager() - .executeAsync(VelocityPlugin.INSTANCE.getServer() - .getConsoleCommandSource(), - StringUtils.translateAlternateColorCodes('&', - command.replace("%player%", - event.getPlayer().getUsername()))); - } - } - AntiVPN.getInstance().detections++; - } - } else { - VelocityPlugin.INSTANCE.getLogger() - .log(Level.WARNING, - "The API query was not a success! " + - "You may need to upgrade your license on " + - "https://funkemunky.cc/shop"); - } - AntiVPN.getInstance().checked++; - }); - } - }; - - @Override public void registerListeners() { VelocityPlugin.INSTANCE.getServer().getEventManager() .register(VelocityPlugin.INSTANCE, this); VelocityPlugin.INSTANCE.getServer().getEventManager().register(VelocityPlugin.INSTANCE, DisconnectEvent.class, - event -> AntiVPN.getInstance().getPlayerExecutor() - .unloadPlayer(event.getPlayer().getUniqueId())); + event -> AntiVPN.getInstance().getPlayerExecutor().unloadPlayer(event.getPlayer().getUniqueId())); VelocityPlugin.INSTANCE.getServer().getEventManager().register(VelocityPlugin.INSTANCE, LoginEvent.class, - loginEvent); - } + event -> { + if (event.getResult().isAllowed()) { + if (event.getPlayer().hasPermission("antivpn.bypass") //Has bypass permission + //Is exempt + || AntiVPN.getInstance().getExecutor().isWhitelisted(event.getPlayer().getUniqueId()) + //Or has a name that starts with a certain prefix. This is for Bedrock exempting. + || AntiVPN.getInstance().getExecutor().isWhitelisted(event.getPlayer().getRemoteAddress() + .getAddress().getHostAddress()) + || AntiVPN.getInstance().getVpnConfig().getPrefixWhitelists().stream() + .anyMatch(prefix -> event.getPlayer().getUsername().startsWith(prefix))) return; + checkIp(event.getPlayer().getRemoteAddress().getAddress().getHostAddress()) + .thenAccept(result -> { + if(!result.isSuccess()) { + VelocityPlugin.INSTANCE.getLogger() + .log(Level.WARNING, + "The API query was not a success! " + + "You may need to upgrade your license on " + + "https://funkemunky.cc/shop"); + } + // If the countryList() size is zero, no need to check. + // Running country check first + if (!AntiVPN.getInstance().getVpnConfig().countryList().isEmpty() + && !(AntiVPN.getInstance().getExecutor() + .isWhitelisted(event.getPlayer().getUniqueId()) //Is exempt + //Or has a name that starts with a certain prefix. This is for Bedrock exempting. + || AntiVPN.getInstance().getExecutor().isWhitelisted(event.getPlayer() + .getRemoteAddress().getAddress().getHostAddress())) + // This bit of code will decide whether or not to kick the player + // If it contains the code and it is set to whitelist, it will not kick + // as they are equal and vise versa. However, if the contains does not match + // the state, it will kick. + && AntiVPN.getInstance().getVpnConfig().countryList() + .contains(result.getCountryCode()) + != AntiVPN.getInstance().getVpnConfig().whitelistCountries()) { + //Using our built in kicking system if no commands are configured + if (AntiVPN.getInstance().getVpnConfig().countryKickCommands().isEmpty()) { + final String kickReason = AntiVPN.getInstance().getVpnConfig() + .countryVanillaKickReason(); + // Kicking our player + event.setResult(ResultedEvent.ComponentResult.denied(LegacyComponentSerializer.builder() + .character('&') + .build().deserialize(kickReason + .replace("%player%", event.getPlayer().getUsername()) + .replace("%country%", result.getCountryName()) + .replace("%code%", result.getCountryCode())))); + VelocityPlugin.INSTANCE.getServer().getScheduler() + .buildTask(VelocityPlugin.INSTANCE, () -> + event.getPlayer().disconnect(LegacyComponentSerializer.builder() + .character('&') + .build().deserialize(kickReason + .replace("%player%", event.getPlayer().getUsername()) + .replace("%country%", result.getCountryName()) + .replace("%code%", result.getCountryCode())))); + } else { + for (String cmd : AntiVPN.getInstance().getVpnConfig().countryKickCommands()) { + final String formattedCommand = StringUtils + .translateAlternateColorCodes('&', + cmd.replace("%player%", + event.getPlayer().getUsername()) + .replace("%country%", result.getCountryName()) + .replace("%code%", result.getCountryCode())); + // Running the command from console + VelocityPlugin.INSTANCE.getServer().getCommandManager() + .executeAsync(VelocityPlugin.INSTANCE.getServer() + .getConsoleCommandSource(), + StringUtils.translateAlternateColorCodes('&', + formattedCommand)); + } + } + } else if (result.isProxy()) { + if (AntiVPN.getInstance().getVpnConfig().kickPlayersOnDetect()) { + // Delay code execution + event.setResult(ResultedEvent.ComponentResult.denied(LegacyComponentSerializer.builder() + .character('&') + .build().deserialize(AntiVPN.getInstance().getVpnConfig() + .getKickString() + .replace("%player%", event.getPlayer().getUsername()) + .replace("%country%", result.getCountryName()) + .replace("%code%", result.getCountryCode())))); - @Override - public void onShutdown() { - if (cacheResetTask != null) { - cacheResetTask.cancel(); - cacheResetTask = null; - } - VelocityPlugin.INSTANCE.getServer().getEventManager().unregisterListener(VelocityPlugin.INSTANCE, this); + VelocityPlugin.INSTANCE.getServer().getScheduler() + .buildTask(VelocityPlugin.INSTANCE, () -> + event.getPlayer().disconnect(LegacyComponentSerializer.builder() + .character('&') + .build().deserialize(AntiVPN.getInstance().getVpnConfig() + .getKickString() + .replace("%player%", event.getPlayer().getUsername()) + .replace("%country%", result.getCountryName()) + .replace("%code%", result.getCountryCode())))) + .delay(1, TimeUnit.SECONDS).schedule(); + } + VelocityPlugin.INSTANCE.getLogger().info(event.getPlayer().getUsername() + + " joined on a VPN/Proxy (" + result.getMethod() + ")"); + //Ensuring the user wishes to alert to staff + if (AntiVPN.getInstance().getVpnConfig().alertToStaff()) + AntiVPN.getInstance().getPlayerExecutor().getOnlinePlayers().stream() + .filter(APIPlayer::isAlertsEnabled) + .forEach(pl -> + pl.sendMessage(AntiVPN.getInstance().getVpnConfig() + .alertMessage() + .replace("%player%", + event.getPlayer().getUsername()) + .replace("%reason%", + result.getMethod()) + .replace("%country%", + result.getCountryName()) + .replace("%city%", + result.getCity()))); + + //In case the user wants to run their own commands instead of using the + // built in kicking + if (AntiVPN.getInstance().getVpnConfig().runCommands()) { + for (String command : AntiVPN.getInstance().getVpnConfig().commands()) { + VelocityPlugin.INSTANCE.getServer().getCommandManager() + .executeAsync(VelocityPlugin.INSTANCE.getServer() + .getConsoleCommandSource(), + StringUtils.translateAlternateColorCodes('&', + command.replace("%player%", + event.getPlayer().getUsername()))); + } + } + AntiVPN.getInstance().detections++; + } + AntiVPN.getInstance().checked++; + }); + } + }); } @Override @@ -200,7 +163,7 @@ public class VelocityListener extends VPNExecutor { } @Override - public void logException(String message, Exception ex) { + public void logException(String message, Throwable ex) { VelocityPlugin.INSTANCE.getLogger().log(Level.SEVERE, message, ex); } diff --git a/Velocity/src/main/java/dev/brighten/antivpn/velocity/VelocityPlugin.java b/Velocity/src/main/java/dev/brighten/antivpn/velocity/VelocityPlugin.java index 33e8e89..da0de33 100644 --- a/Velocity/src/main/java/dev/brighten/antivpn/velocity/VelocityPlugin.java +++ b/Velocity/src/main/java/dev/brighten/antivpn/velocity/VelocityPlugin.java @@ -9,8 +9,13 @@ import com.velocitypowered.api.plugin.annotation.DataDirectory; import com.velocitypowered.api.proxy.ProxyServer; import dev.brighten.antivpn.AntiVPN; import dev.brighten.antivpn.command.Command; +import dev.brighten.antivpn.database.VPNDatabase; +import dev.brighten.antivpn.database.local.H2VPN; +import dev.brighten.antivpn.database.mongo.MongoVPN; +import dev.brighten.antivpn.database.sql.MySqlVPN; import dev.brighten.antivpn.velocity.command.VelocityCommand; import lombok.Getter; +import org.bstats.charts.SimplePie; import org.bstats.velocity.Metrics; import javax.annotation.Nullable; @@ -29,6 +34,7 @@ public class VelocityPlugin { @Nullable private Metrics metrics; + public static VelocityPlugin INSTANCE; @Inject @@ -51,6 +57,8 @@ public class VelocityPlugin { if(AntiVPN.getInstance().getVpnConfig().metrics()) { logger.info("Starting metrics..."); metrics = metricsFactory.make(this, 12791); + + metrics.addCustomChart(new SimplePie("database_used", this::getDatabaseType)); } logger.info("Registering commands..."); @@ -61,8 +69,33 @@ public class VelocityPlugin { } @Subscribe - public void onShutdown(ProxyShutdownEvent event) { - AntiVPN.getInstance().stop(); + public void onDisable(ProxyShutdownEvent event) { + logger.info("Disabling AntiVPN..."); + AntiVPN.getInstance().getExecutor().log("Disabling AntiVPN..."); + + if (AntiVPN.getInstance().getDatabase() != null) { + AntiVPN.getInstance().stop(); + } + + if (metrics != null) { + metrics = null; + } + INSTANCE = null; + logger.info("Disabled AntiVPN."); + } + + private String getDatabaseType() { + VPNDatabase database = AntiVPN.getInstance().getDatabase(); + + if(database instanceof H2VPN) { + return "H2"; + } else if(database instanceof MySqlVPN) { + return "MySQL"; + } else if(database instanceof MongoVPN) { + return "MongoDB"; + } else { + return "No-Database"; + } } } diff --git a/pom.xml b/pom.xml index 1bab415..05cf495 100644 --- a/pom.xml +++ b/pom.xml @@ -13,14 +13,14 @@ Common Bungee Bukkit - Assembly Velocity Sponge + Universal - 8 - 8 + 17 + 17 @@ -30,8 +30,8 @@ maven-compiler-plugin 3.7.0 - 8 - 8 + 17 + 17 -XDignore.symbol.file false @@ -53,10 +53,6 @@ - - spigot-repo - https://hub.spigotmc.org/nexus/content/repositories/snapshots/ - funkemunky-releases https://nexus.funkemunky.cc/content/repositories/releases/ From 52efc7de3f377af14b12c9a2f1c5f8f2160a88d3 Mon Sep 17 00:00:00 2001 From: Dawson Date: Mon, 19 May 2025 10:31:54 -0400 Subject: [PATCH 09/20] Ensuring queries auto-close --- .../antivpn/database/sql/MySqlVPN.java | 46 +++++++++++-------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/Common/src/main/java/dev/brighten/antivpn/database/sql/MySqlVPN.java b/Common/src/main/java/dev/brighten/antivpn/database/sql/MySqlVPN.java index 39a9cea..148f0c8 100644 --- a/Common/src/main/java/dev/brighten/antivpn/database/sql/MySqlVPN.java +++ b/Common/src/main/java/dev/brighten/antivpn/database/sql/MySqlVPN.java @@ -286,19 +286,21 @@ public class MySqlVPN implements VPNDatabase { String query = "SELECT COUNT(1) IndexExists FROM INFORMATION_SCHEMA.STATISTICS WHERE table_schema=DATABASE()" + " AND table_name='whitelisted' AND index_name='uuid_1';"; - ResultSet rs = Query.prepare(query).executeQuery(); int id = 0; - while (rs.next()) { - id = rs.getInt("IndexExists"); - } - if (id == 0) { - Query.prepare("create index `uuid_1` on `whitelisted` (`uuid`)").execute(); + try(ResultSet rs = Query.prepare(query).executeQuery()) { + while (rs.next()) { + id = rs.getInt("IndexExists"); + } + if (id == 0) { + Query.prepare("create index `uuid_1` on `whitelisted` (`uuid`)").execute(); + } } id = 0; - responsesIndex: { - query = "SELECT COUNT(1) IndexExists FROM INFORMATION_SCHEMA.STATISTICS WHERE table_schema=DATABASE() " + - "AND table_name='responses' AND index_name='ip_1';"; - rs = Query.prepare(query).executeQuery(); + + // Responses index + query = "SELECT COUNT(1) IndexExists FROM INFORMATION_SCHEMA.STATISTICS WHERE table_schema=DATABASE() " + + "AND table_name='responses' AND index_name='ip_1';"; + try(ResultSet rs = Query.prepare(query).executeQuery()) { while (rs.next()) { id = rs.getInt("IndexExists"); } @@ -306,9 +308,11 @@ public class MySqlVPN implements VPNDatabase { Query.prepare("create index `ip_1` on `responses` (`ip`)").execute(); } id = 0; - query = "SELECT COUNT(1) IndexExists FROM INFORMATION_SCHEMA.STATISTICS WHERE table_schema=DATABASE() " + - "AND table_name='responses' AND index_name='proxy_1';"; - rs = Query.prepare(query).executeQuery(); + } + + query = "SELECT COUNT(1) IndexExists FROM INFORMATION_SCHEMA.STATISTICS WHERE table_schema=DATABASE() " + + "AND table_name='responses' AND index_name='proxy_1';"; + try(ResultSet rs = Query.prepare(query).executeQuery()) { while (rs.next()) { id = rs.getInt("IndexExists"); } @@ -316,9 +320,10 @@ public class MySqlVPN implements VPNDatabase { Query.prepare("create index `proxy_1` on `responses` (`proxy`)").execute(); } id = 0; - query = "SELECT COUNT(1) IndexExists FROM INFORMATION_SCHEMA.STATISTICS WHERE table_schema=DATABASE()" + - " AND table_name='responses' AND index_name='inserted_1';"; - rs = Query.prepare(query).executeQuery(); + } + query = "SELECT COUNT(1) IndexExists FROM INFORMATION_SCHEMA.STATISTICS WHERE table_schema=DATABASE()" + + " AND table_name='responses' AND index_name='inserted_1';"; + try(ResultSet rs = Query.prepare(query).executeQuery()) { while (rs.next()) { id = rs.getInt("IndexExists"); } @@ -327,10 +332,11 @@ public class MySqlVPN implements VPNDatabase { } id = 0; } - whitelistedIpsIndex: { - query = "SELECT COUNT(1) IndexExists FROM INFORMATION_SCHEMA.STATISTICS WHERE table_schema=DATABASE()" + - " AND table_name='whitelisted-ips' AND index_name='ip_1';"; - rs = Query.prepare(query).executeQuery(); + + //Whitelisted IPs index + query = "SELECT COUNT(1) IndexExists FROM INFORMATION_SCHEMA.STATISTICS WHERE table_schema=DATABASE()" + + " AND table_name='whitelisted-ips' AND index_name='ip_1';"; + try(ResultSet rs = Query.prepare(query).executeQuery()) { while (rs.next()) { id = rs.getInt("IndexExists"); } From 6967246edb9a53282ab25c6a7a344acb78730eb3 Mon Sep 17 00:00:00 2001 From: Dawson Date: Mon, 19 May 2025 13:23:38 -0400 Subject: [PATCH 10/20] Fixing loading on velocity and removing sneakythrows from Query class --- Common/pom.xml | 9 --------- .../antivpn/database/sql/utils/Query.java | 17 ++++++----------- Universal/pom.xml | 8 ++++---- Velocity/pom.xml | 2 +- 4 files changed, 11 insertions(+), 25 deletions(-) diff --git a/Common/pom.xml b/Common/pom.xml index 230cf7d..0854ccd 100644 --- a/Common/pom.xml +++ b/Common/pom.xml @@ -57,15 +57,6 @@ dev.brighten.antivpn.depends.MavenLibraries - - com.google - dev.brighten.antivpn.shaded.com.google - - - dev.brighten.antivpn.depends.Relocate - dev.brighten.antivpn.depends.MavenLibraries - - org.h2 dev.brighten.antivpn.shaded.org.h2 diff --git a/Common/src/main/java/dev/brighten/antivpn/database/sql/utils/Query.java b/Common/src/main/java/dev/brighten/antivpn/database/sql/utils/Query.java index f2f86fe..ac57c27 100644 --- a/Common/src/main/java/dev/brighten/antivpn/database/sql/utils/Query.java +++ b/Common/src/main/java/dev/brighten/antivpn/database/sql/utils/Query.java @@ -1,8 +1,7 @@ package dev.brighten.antivpn.database.sql.utils; -import lombok.SneakyThrows; - import java.sql.Connection; +import java.sql.SQLException; public class Query { private static Connection conn; @@ -11,15 +10,11 @@ public class Query { Query.conn = conn; } - @SneakyThrows public static ExecutableStatement prepare(String query) { - return new ExecutableStatement(conn.prepareStatement(query)); - } - - - - @SneakyThrows - public static ExecutableStatement prepare(String query, Connection con) { - return new ExecutableStatement(con.prepareStatement(query)); + try { + return new ExecutableStatement(conn.prepareStatement(query)); + } catch (SQLException e) { + throw new RuntimeException(e); + } } } diff --git a/Universal/pom.xml b/Universal/pom.xml index ebe5ca1..fe9424e 100644 --- a/Universal/pom.xml +++ b/Universal/pom.xml @@ -22,21 +22,25 @@ dev.brighten.antivpn Common ${project.version} + compile dev.brighten.antivpn Bukkit ${project.version} + compile dev.brighten.antivpn Bungee ${project.version} + compile dev.brighten.antivpn Velocity ${project.version} + compile diff --git a/Velocity/pom.xml b/Velocity/pom.xml index f7dc4b3..facd283 100644 --- a/Velocity/pom.xml +++ b/Velocity/pom.xml @@ -27,7 +27,7 @@ com.velocitypowered velocity-api - 3.1.1 + 4.0.0-SNAPSHOT provided From de31d837b9507b581d8752c3d069c4ebcf6d8ee8 Mon Sep 17 00:00:00 2001 From: Dawson Date: Thu, 22 May 2025 14:29:05 -0400 Subject: [PATCH 11/20] Velocity dependency fix --- .../dev/brighten/antivpn/web/FunkemunkyAPI.java | 6 +++--- .../antivpn/web/objects/QueryResponse.java | 15 ++------------- Velocity/pom.xml | 2 +- 3 files changed, 6 insertions(+), 17 deletions(-) diff --git a/Common/src/main/java/dev/brighten/antivpn/web/FunkemunkyAPI.java b/Common/src/main/java/dev/brighten/antivpn/web/FunkemunkyAPI.java index fc35735..7710325 100644 --- a/Common/src/main/java/dev/brighten/antivpn/web/FunkemunkyAPI.java +++ b/Common/src/main/java/dev/brighten/antivpn/web/FunkemunkyAPI.java @@ -12,7 +12,7 @@ public class FunkemunkyAPI { /** * - * Queries https://funkemunky.cc/vpn API and returns information on the IP + * Queries ... API and returns information on the IP * * @param ip String * @param license String @@ -25,7 +25,7 @@ public class FunkemunkyAPI { throws JSONException, IOException { JSONObject result = JsonReader.readJsonFromUrl(String .format("https://funkemunky.cc/vpn?ip=%s&license=%s&cache=%s", - ip, license.length() == 0 ? "none" : license, cachedResults)); + ip, license.isEmpty() ? "none" : license, cachedResults)); return VPNResponse.fromJson(result); } @@ -43,7 +43,7 @@ public class FunkemunkyAPI { } /** - * Queries https://funkemunky.cc/vpn/queryCheck and returns information based on the + * Queries ... and returns information based on the * provided licence input. * * @param license String diff --git a/Common/src/main/java/dev/brighten/antivpn/web/objects/QueryResponse.java b/Common/src/main/java/dev/brighten/antivpn/web/objects/QueryResponse.java index 2b2d6dd..3dc96bb 100644 --- a/Common/src/main/java/dev/brighten/antivpn/web/objects/QueryResponse.java +++ b/Common/src/main/java/dev/brighten/antivpn/web/objects/QueryResponse.java @@ -7,7 +7,7 @@ import lombok.Data; /** - * Used to format the JSON response from https://funkemunky.cc/vpn/queryCheck into an object for project use. + * Used to format the JSON response from ... into an object for project use. */ @Data @Builder(toBuilder = true) @@ -19,18 +19,7 @@ public class QueryResponse { private long queriesMax; /** - * Takes a JSON String and feeds it into {@link QueryResponse#fromJson(JSONObject)} - * - * @param jsonString String (formatted in JSON) - * @return QueryResponse - * @throws JSONException Throws when JSON is not formatted properly. - */ - public static QueryResponse fromJson(String jsonString) throws JSONException { - return fromJson(new JSONObject(jsonString)); - } - - /** - * Formats response from https://funkemunky.cc/vpn/queryCheck into {@link QueryResponse} for project use. + * Formats response from ... into {@link QueryResponse} for project use. * * @param object JSONObject * @return QueryResponse diff --git a/Velocity/pom.xml b/Velocity/pom.xml index facd283..152db18 100644 --- a/Velocity/pom.xml +++ b/Velocity/pom.xml @@ -27,7 +27,7 @@ com.velocitypowered velocity-api - 4.0.0-SNAPSHOT + 3.4.0-SNAPSHOT provided From 3f6bb4a0e6a4b6e6bdaf373c9ebf5bc77d0532f5 Mon Sep 17 00:00:00 2001 From: Dawson Date: Tue, 27 May 2025 13:13:53 -0400 Subject: [PATCH 12/20] Refactor, code cleanup, sponge impl. BungeeCord still has problems, bad API, not my fault technically. still need to fix though --- .../antivpn/bukkit/BukkitListener.java | 213 +++++--------- .../antivpn/bungee/BungeeListener.java | 175 ++++------- .../java/dev/brighten/antivpn/AntiVPN.java | 63 ++-- .../dev/brighten/antivpn/api/APIPlayer.java | 68 ++++- .../dev/brighten/antivpn/api/CheckResult.java | 6 + .../brighten/antivpn/api/OfflinePlayer.java | 26 ++ .../dev/brighten/antivpn/api/ResultType.java | 18 ++ .../dev/brighten/antivpn/api/VPNExecutor.java | 70 +++++ .../antivpn/database/sql/utils/MySQL.java | 3 + .../dev/brighten/antivpn/utils/MiscUtils.java | 39 ++- .../brighten/antivpn/utils/StringUtil.java | 23 ++ .../dev/brighten/antivpn/utils/Tuple.java | 5 + Sponge/pom.xml | 6 + .../antivpn/sponge/SpongeListener.java | 106 +++++++ .../brighten/antivpn/sponge/SpongePlayer.java | 2 +- .../antivpn/sponge/SpongePlayerExecutor.java | 45 ++- .../brighten/antivpn/sponge/SpongePlugin.java | 26 +- .../antivpn/velocity/VelocityListener.java | 274 ++++++++++-------- .../antivpn/velocity/util/StringUtils.java | 17 -- 19 files changed, 739 insertions(+), 446 deletions(-) create mode 100644 Common/src/main/java/dev/brighten/antivpn/api/CheckResult.java create mode 100644 Common/src/main/java/dev/brighten/antivpn/api/OfflinePlayer.java create mode 100644 Common/src/main/java/dev/brighten/antivpn/api/ResultType.java create mode 100644 Common/src/main/java/dev/brighten/antivpn/utils/Tuple.java create mode 100644 Sponge/src/main/java/dev/brighten/antivpn/sponge/SpongeListener.java delete mode 100644 Velocity/src/main/java/dev/brighten/antivpn/velocity/util/StringUtils.java diff --git a/Bukkit/src/main/java/dev/brighten/antivpn/bukkit/BukkitListener.java b/Bukkit/src/main/java/dev/brighten/antivpn/bukkit/BukkitListener.java index 1ded919..fe37786 100644 --- a/Bukkit/src/main/java/dev/brighten/antivpn/bukkit/BukkitListener.java +++ b/Bukkit/src/main/java/dev/brighten/antivpn/bukkit/BukkitListener.java @@ -1,33 +1,27 @@ package dev.brighten.antivpn.bukkit; -import com.github.benmanes.caffeine.cache.Cache; -import com.github.benmanes.caffeine.cache.Caffeine; import dev.brighten.antivpn.AntiVPN; import dev.brighten.antivpn.api.APIPlayer; +import dev.brighten.antivpn.api.CheckResult; +import dev.brighten.antivpn.api.OfflinePlayer; import dev.brighten.antivpn.api.VPNExecutor; import dev.brighten.antivpn.message.VpnString; -import dev.brighten.antivpn.web.objects.VPNResponse; +import dev.brighten.antivpn.utils.StringUtil; +import dev.brighten.antivpn.utils.Tuple; import net.md_5.bungee.api.ChatColor; import org.bukkit.Bukkit; -import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerLoginEvent; import org.bukkit.event.player.PlayerQuitEvent; -import org.bukkit.scheduler.BukkitRunnable; -import java.util.UUID; -import java.util.concurrent.TimeUnit; import java.util.logging.Level; @SuppressWarnings("unchecked") public class BukkitListener extends VPNExecutor implements Listener { - private final Cache responseCache = Caffeine.newBuilder() - .expireAfterWrite(5, TimeUnit.MINUTES) - .maximumSize(2000) - .build(); @Override public void registerListeners() { @@ -50,6 +44,12 @@ public class BukkitListener extends VPNExecutor implements Listener { Bukkit.getLogger().log(Level.SEVERE, message, ex); } + @Override + public void runCommand(String command) { + Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(), + ChatColor.translateAlternateColorCodes('&', command)); + } + @Override public void disablePlugin() { HandlerList.unregisterAll(this); @@ -57,6 +57,61 @@ public class BukkitListener extends VPNExecutor implements Listener { } @EventHandler(priority = EventPriority.HIGH) + public void onLogin(final PlayerLoginEvent event) { + APIPlayer player = AntiVPN.getInstance().getPlayerExecutor().getPlayer(event.getPlayer().getUniqueId()) + .orElse(new OfflinePlayer( + event.getPlayer().getUniqueId(), + event.getPlayer().getName(), + event.getRealAddress() + )); + + CheckResult instantResult = player.checkPlayer(result -> { + if(!result.resultType().isShouldBlock()) return; + + AntiVPN.getInstance().getExecutor().log(Level.INFO, "Adding %s to kick", event.getPlayer().getName()); + AntiVPN.getInstance().getExecutor().getToKick().add(new Tuple<>(result, event.getPlayer().getUniqueId())); + }); + + if(!instantResult.resultType().isShouldBlock()) return; + + AntiVPN.getInstance().getExecutor().getToKick() + .add(new Tuple<>(instantResult, event.getPlayer().getUniqueId())); + + if(!AntiVPN.getInstance().getVpnConfig().kickPlayersOnDetect()) { + return; + } + + event.setResult(PlayerLoginEvent.Result.KICK_BANNED); + switch (instantResult.resultType()) { + case DENIED_COUNTRY -> event.setKickMessage(StringUtil.translateAlternateColorCodes('&', + StringUtil.varReplace( + AntiVPN.getInstance().getVpnConfig().countryVanillaKickReason(), + player, + instantResult.response() + ))); + case DENIED_PROXY -> { + if(AntiVPN.getInstance().getVpnConfig().alertToStaff()) { + AntiVPN.getInstance().getPlayerExecutor().getOnlinePlayers().stream() + .filter(APIPlayer::isAlertsEnabled) + .forEach(pl -> + pl.sendMessage(StringUtil.varReplace( + ChatColor.translateAlternateColorCodes( + '&', + AntiVPN.getInstance().getVpnConfig().alertMessage()), + player, + instantResult.response()))); + } + event.setKickMessage(StringUtil.translateAlternateColorCodes('&', + StringUtil.varReplace( + AntiVPN.getInstance().getVpnConfig().getKickString(), + player, + instantResult.response() + ))); + } + } + } + + @EventHandler(priority = EventPriority.MONITOR) public void onJoin(final PlayerJoinEvent event) { AntiVPN.getInstance().getPlayerExecutor().getPlayer(event.getPlayer().getUniqueId()) .ifPresent(player -> AntiVPN.getInstance().getDatabase().alertsState(player.getUuid(), enabled -> { @@ -67,142 +122,6 @@ public class BukkitListener extends VPNExecutor implements Listener { .getFormattedMessage(new VpnString.Var<>("state", true))); } })); - - String address; - - if(event.getPlayer().getAddress() != null) { - address = event.getPlayer().getAddress().getAddress().getHostAddress(); - } else { - log(Level.WARNING, "Player %s address is null! This is a bug and should be reported!", event.getPlayer().getName()); - return; - } - - if(event.getPlayer().hasPermission("antivpn.bypass") //Has bypass permission - || AntiVPN.getInstance().getExecutor().isWhitelisted(event.getPlayer().getUniqueId()) //Is exempt - //Or has a name that starts with a certain prefix. This is for Bedrock exempting. - || AntiVPN.getInstance().getExecutor().isWhitelisted(address) - || AntiVPN.getInstance().getVpnConfig().getPrefixWhitelists().stream() - .anyMatch(prefix -> event.getPlayer().getName().startsWith(prefix))) return; - - if(responseCache.asMap().containsKey(event.getPlayer().getUniqueId())) { - VPNResponse cached = responseCache.getIfPresent(event.getPlayer().getUniqueId()); - - if (cached != null && cached.isProxy()) { - proxyKick(event.getPlayer(), cached); - return; - } - } - - final Player player = event.getPlayer(); - checkIp(address).thenAccept(result -> { - if(result.isSuccess()) { - //We need to run on main thread or kicking and running commands will cause errors - //If the player is whitelisted, we don't want to kick them - if(AntiVPN.getInstance().getExecutor().isWhitelisted(event.getPlayer().getUniqueId())) { - log("UUID is whitelisted: %s", event.getPlayer().getUniqueId().toString()); - return; - } - - //If the IP is whitelisted, we don't want to kick them - if (AntiVPN.getInstance().getExecutor().isWhitelisted(address)) { - log("IP is whitelisted: %s", - address); - return; - } - - // If the countryList() size is zero, no need to check. - // Running country check first - if(!AntiVPN.getInstance().getVpnConfig().countryList().isEmpty() - // This bit of code will decide whether to kick the player - // If contains the code, and set to whitelist, it will not kick as they are equal - // and vise versa. However, if the contains does not match the state, it will kick. - && AntiVPN.getInstance().getVpnConfig().countryList() - .contains(result.getCountryCode()) - != AntiVPN.getInstance().getVpnConfig().whitelistCountries()) { - countryKick(player, result); - } else if(result.isProxy()) { - proxyKick(player, result); - } - } else { - log(Level.WARNING, - "The API query was not a success! " + - "You may need to upgrade your license on https://funkemunky.cc/shop"); - } - AntiVPN.getInstance().checked++; - }); - } - - private void countryKick(Player player, VPNResponse result) { - final String kickReason = AntiVPN.getInstance().getVpnConfig() - .countryVanillaKickReason(); - //Using our built-in kicking system if no commands are configured - if(AntiVPN.getInstance().getVpnConfig().countryKickCommands().isEmpty()) { - // Kicking our player - new BukkitRunnable() { - public void run() { - player.kickPlayer(ChatColor - .translateAlternateColorCodes('&', - kickReason - .replace("%player%", player.getName()) - .replace("%country%", result.getCountryName()) - .replace("%code%", result.getCountryCode()))); - } - }.runTask(BukkitPlugin.pluginInstance); - } else { - final String playerName = player.getName(); - - BukkitPlugin.pluginInstance.getPlayerCommandRunner() - .addAction(player.getUniqueId(), () -> { - for (String cmd : AntiVPN.getInstance().getVpnConfig().countryKickCommands()) { - final String formattedCommand = ChatColor.translateAlternateColorCodes('&', - cmd.replace("%player%", playerName) - .replace("%country%", result.getCountryName()) - .replace("%code%", result.getCountryCode())); - - // Runs our command from console - Bukkit.dispatchCommand(Bukkit.getConsoleSender(), formattedCommand); - } - }); - } - } - - private void proxyKick(Player player, VPNResponse result) { - log(Level.INFO, player.getName() - + " joined on a VPN/Proxy (" + result.getMethod() + ")"); - - //Ensuring the user wishes to alert to staff - if(AntiVPN.getInstance().getVpnConfig().alertToStaff()) - AntiVPN.getInstance().getPlayerExecutor().getOnlinePlayers().stream() - .filter(APIPlayer::isAlertsEnabled) - .forEach(pl -> pl.sendMessage(AntiVPN.getInstance().getVpnConfig().alertMessage() - .replace("%player%", player.getName()) - .replace("%reason%", result.getMethod()) - .replace("%country%", result.getCountryName()) - .replace("%city%", result.getCity()))); - - if(AntiVPN.getInstance().getVpnConfig().kickPlayersOnDetect()) { - new BukkitRunnable() { - public void run() { - player.kickPlayer(org.bukkit.ChatColor.translateAlternateColorCodes('&', - AntiVPN.getInstance().getVpnConfig().getKickString())); - } - }.runTask(BukkitPlugin.pluginInstance); - } else { - //In case the user wants to run their own commands instead of using the built-in kicking - if(AntiVPN.getInstance().getVpnConfig().runCommands()) { - String playerName = player.getName(); - BukkitPlugin.pluginInstance.getPlayerCommandRunner() - .addAction(player.getUniqueId(), () -> { - for (String command : AntiVPN.getInstance().getVpnConfig().commands()) { - Bukkit.dispatchCommand(Bukkit.getConsoleSender(), - ChatColor.translateAlternateColorCodes('&', - command.replace("%player%", - playerName))); - } - }); - } - AntiVPN.getInstance().detections++; - } } @EventHandler diff --git a/Bungee/src/main/java/dev/brighten/antivpn/bungee/BungeeListener.java b/Bungee/src/main/java/dev/brighten/antivpn/bungee/BungeeListener.java index ea8344b..7d94b55 100644 --- a/Bungee/src/main/java/dev/brighten/antivpn/bungee/BungeeListener.java +++ b/Bungee/src/main/java/dev/brighten/antivpn/bungee/BungeeListener.java @@ -1,34 +1,30 @@ package dev.brighten.antivpn.bungee; -import com.github.benmanes.caffeine.cache.Cache; -import com.github.benmanes.caffeine.cache.Caffeine; import dev.brighten.antivpn.AntiVPN; import dev.brighten.antivpn.api.APIPlayer; +import dev.brighten.antivpn.api.CheckResult; +import dev.brighten.antivpn.api.OfflinePlayer; import dev.brighten.antivpn.api.VPNExecutor; -import dev.brighten.antivpn.web.objects.VPNResponse; +import dev.brighten.antivpn.utils.MiscUtils; +import dev.brighten.antivpn.utils.StringUtil; +import dev.brighten.antivpn.utils.Tuple; import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.chat.TextComponent; import net.md_5.bungee.api.event.PlayerDisconnectEvent; -import net.md_5.bungee.api.event.PostLoginEvent; import net.md_5.bungee.api.event.PreLoginEvent; import net.md_5.bungee.api.plugin.Listener; import net.md_5.bungee.api.scheduler.ScheduledTask; import net.md_5.bungee.event.EventHandler; import net.md_5.bungee.event.EventPriority; +import java.net.InetSocketAddress; import java.util.UUID; -import java.util.concurrent.TimeUnit; import java.util.logging.Level; public class BungeeListener extends VPNExecutor implements Listener { private ScheduledTask cacheResetTask; - private final Cache responseCache = Caffeine.newBuilder() - .expireAfterWrite(5, TimeUnit.MINUTES) - .maximumSize(2000) - .build(); - @Override public void registerListeners() { BungeePlugin.pluginInstance.getProxy().getPluginManager() @@ -50,10 +46,16 @@ public class BungeeListener extends VPNExecutor implements Listener { BungeePlugin.pluginInstance.getProxy().getLogger().log(Level.SEVERE, message, ex); } + @Override + public void runCommand(String command) { + BungeePlugin.pluginInstance.getProxy().getPluginManager() + .dispatchCommand(BungeePlugin.pluginInstance.getProxy().getConsole(), command); + } + @Override public void disablePlugin() { BungeePlugin.pluginInstance.getProxy().getPluginManager().unregisterListeners(BungeePlugin.pluginInstance); - if(cacheResetTask != null) { + if (cacheResetTask != null) { cacheResetTask.cancel(); cacheResetTask = null; } @@ -61,115 +63,56 @@ public class BungeeListener extends VPNExecutor implements Listener { BungeePlugin.pluginInstance.onDisable(); } - @EventHandler(priority = EventPriority.LOWEST) + @EventHandler(priority = EventPriority.HIGH) public void onListener(final PreLoginEvent event) { - if(!responseCache.asMap().containsKey(event.getConnection().getUniqueId())) return; - VPNResponse cached = responseCache.getIfPresent(event.getConnection().getUniqueId()); + APIPlayer player = AntiVPN.getInstance().getPlayerExecutor().getPlayer(event.getConnection().getUniqueId()) + .orElseGet(() -> { + UUID uuid = MiscUtils.lookupUUID(event.getConnection().getName()); + AntiVPN.getInstance().getExecutor().log(Level.INFO, "Getting offline player for %s with name %s", + event.getConnection().getUniqueId(), uuid); - if(cached != null && cached.isProxy()) { - event.setCancelled(true); - event.setReason(TextComponent.fromLegacy(ChatColor - .translateAlternateColorCodes('&', - AntiVPN.getInstance().getVpnConfig().getKickString()))); - AntiVPN.getInstance().getExecutor().log(Level.INFO, - "%s was kicked from pre-login proxy cache.", - event.getConnection().getName()); - } - } - - @EventHandler(priority = EventPriority.LOWEST) - public void onListener(final PostLoginEvent event) { - if(event.getPlayer().hasPermission("antivpn.bypass") //Has bypass permission - || AntiVPN.getInstance().getVpnConfig().getPrefixWhitelists().stream() - .anyMatch(prefix -> event.getPlayer().getName().startsWith(prefix))) return; - - String address = event.getPlayer().getSocketAddress().toString(); - - if(AntiVPN.getInstance().getExecutor().isWhitelisted(event.getPlayer().getUniqueId())) { - AntiVPN.getInstance().getExecutor().log("UUID is whitelisted: %s", - event.getPlayer().getUniqueId().toString()); - return; - } - - //If the IP is whitelisted, we don't want to kick them - if(AntiVPN.getInstance().getExecutor().isWhitelisted(address)) { - AntiVPN.getInstance().getExecutor().log("IP is whitelisted: %s", address); - return; - } - - checkIp(address) - .thenAccept(result -> { - if(result.isSuccess()) { - //If the player is whitelisted, we don't want to kick them - responseCache.put(event.getPlayer().getUniqueId(), result); - if(!AntiVPN.getInstance().getVpnConfig().countryList().isEmpty() - // This bit of code will decide whether or not to kick the player - // If it contains the code and it is set to whitelist, it will not kick as they are equal - // and vise versa. However, if the contains does not match the state, it will kick. - && AntiVPN.getInstance().getVpnConfig().countryList() - .contains(result.getCountryCode()) != AntiVPN.getInstance().getVpnConfig().whitelistCountries()) { - //Using our built in kicking system if no commands are configured - if(AntiVPN.getInstance().getVpnConfig().countryKickCommands().isEmpty()) { - final String kickReason = AntiVPN.getInstance().getVpnConfig() - .countryVanillaKickReason(); - // Kicking our player - event.getPlayer().disconnect(TextComponent.fromLegacy(ChatColor - .translateAlternateColorCodes('&', - kickReason - .replace("%player%", event.getPlayer().getName()) - .replace("%country%", result.getCountryName()) - .replace("%code%", result.getCountryCode())))); - } else { - for (String cmd : AntiVPN.getInstance().getVpnConfig().countryKickCommands()) { - final String formattedCommand = ChatColor.translateAlternateColorCodes('&', - cmd.replace("%player%", event.getPlayer().getName()) - .replace("%country%", result.getCountryName()) - .replace("%code%", result.getCountryCode())); - - // Runs our command from console - BungeePlugin.pluginInstance.getProxy().getPluginManager().dispatchCommand( - BungeePlugin.pluginInstance.getProxy().getConsole(), formattedCommand); - } - } - } else if(result.isProxy()) { - if(AntiVPN.getInstance().getVpnConfig().kickPlayersOnDetect()) - event.getPlayer().disconnect(TextComponent.fromLegacy(ChatColor - .translateAlternateColorCodes('&', - AntiVPN.getInstance().getVpnConfig().getKickString()))); - BungeePlugin.pluginInstance.getProxy().getLogger().info(event.getPlayer().getName() - + " joined on a VPN/Proxy (" + result.getMethod() + ")"); - - if(AntiVPN.getInstance().getVpnConfig().alertToStaff()) //Ensuring the user wishes to alert to staff - AntiVPN.getInstance().getPlayerExecutor().getOnlinePlayers().stream() - .filter(APIPlayer::isAlertsEnabled) - .forEach(pl -> pl.sendMessage(AntiVPN.getInstance().getVpnConfig() - .alertMessage() - .replace("%player%", event.getPlayer().getName()) - .replace("%reason%", result.getMethod()) - .replace("%country%", result.getCountryName()) - .replace("%city%", result.getCity()))); - - //In case the user wants to run their own commands instead of using the built in kicking - if(AntiVPN.getInstance().getVpnConfig().runCommands()) { - for (String command : AntiVPN.getInstance().getVpnConfig().commands()) { - BungeePlugin.pluginInstance.getProxy().getPluginManager() - .dispatchCommand(BungeePlugin.pluginInstance.getProxy().getConsole(), - ChatColor.translateAlternateColorCodes('&', - command.replace("%player%", event.getPlayer().getName()))); - } - } - AntiVPN.getInstance().detections++; - } - - } else { - BungeePlugin.pluginInstance.getProxy().getLogger() - .log(Level.WARNING, - "The API query was not a success! " + - "You may need to upgrade your license on https://funkemunky.cc/shop"); - } - AntiVPN.getInstance().checked++; + return new OfflinePlayer(uuid, event.getConnection().getName(), + ((InetSocketAddress) event.getConnection().getSocketAddress()).getAddress()); }); + + CheckResult instantResult = player.checkPlayer(result -> { + if (!result.resultType().isShouldBlock()) return; + AntiVPN.getInstance().getExecutor().getToKick() + .add(new Tuple<>(result, event.getConnection().getUniqueId())); + }); + + if (!instantResult.resultType().isShouldBlock()) { + return; + } + + AntiVPN.getInstance().getExecutor().getToKick() + .add(new Tuple<>(instantResult, player.getUuid())); + + if (!AntiVPN.getInstance().getVpnConfig().kickPlayersOnDetect()) { + return; + } + + event.setCancelled(true); + AntiVPN.getInstance().getExecutor().log(Level.INFO, + "%s was kicked from pre-login proxy cache.", + event.getConnection().getName()); + + + switch (instantResult.resultType()) { + case DENIED_PROXY -> event.setReason(TextComponent.fromLegacy(ChatColor + .translateAlternateColorCodes('&', + StringUtil.varReplace( + AntiVPN.getInstance().getVpnConfig().getKickString(), + player, + instantResult.response())))); + case DENIED_COUNTRY -> event.setReason(TextComponent.fromLegacy(ChatColor + .translateAlternateColorCodes('&', + StringUtil.varReplace( + AntiVPN.getInstance().getVpnConfig().countryVanillaKickReason(), + player, + instantResult.response())))); + } } @EventHandler diff --git a/Common/src/main/java/dev/brighten/antivpn/AntiVPN.java b/Common/src/main/java/dev/brighten/antivpn/AntiVPN.java index 3a24d96..3eb00b2 100644 --- a/Common/src/main/java/dev/brighten/antivpn/AntiVPN.java +++ b/Common/src/main/java/dev/brighten/antivpn/AntiVPN.java @@ -94,34 +94,40 @@ public class AntiVPN { INSTANCE.messageHandler = new MessageHandler(); - switch(INSTANCE.vpnConfig.getDatabaseType().toLowerCase()) { - case "h2": - case "local": - case "flatfile": { - AntiVPN.getInstance().getExecutor().log("Using databaseType H2..."); - INSTANCE.database = new H2VPN(); - INSTANCE.database.init(); - break; - } - case "mysql": - case "sql":{ - AntiVPN.getInstance().getExecutor().log("Using databaseType MySQL..."); - INSTANCE.database = new MySqlVPN(); - INSTANCE.database.init(); - break; - } - case "mongo": - case "mongodb": - case "mongod": { - INSTANCE.database = new MongoVPN(); - INSTANCE.database.init(); - break; - } - default: { - AntiVPN.getInstance().getExecutor().log("Could not find database type \"" + INSTANCE.vpnConfig.getDatabaseType() + "\". " + - "Options: [MySQL]"); - break; + try { + switch(INSTANCE.vpnConfig.getDatabaseType().toLowerCase()) { + case "h2": + case "local": + case "flatfile": { + AntiVPN.getInstance().getExecutor().log("Using databaseType H2..."); + INSTANCE.database = new H2VPN(); + INSTANCE.database.init(); + break; + } + case "mysql": + case "sql":{ + AntiVPN.getInstance().getExecutor().log("Using databaseType MySQL..."); + INSTANCE.database = new MySqlVPN(); + INSTANCE.database.init(); + break; + } + case "mongo": + case "mongodb": + case "mongod": { + INSTANCE.database = new MongoVPN(); + INSTANCE.database.init(); + break; + } + default: { + AntiVPN.getInstance().getExecutor().log("Could not find database type \"" + INSTANCE.vpnConfig.getDatabaseType() + "\". " + + "Options: [MySQL]"); + break; + } } + } catch (Exception e) { + AntiVPN.getInstance().getExecutor().logException("Could not initialize database, plugin disabling...", e); + executor.disablePlugin(); + return; } //Registering commands @@ -141,6 +147,9 @@ public class AntiVPN { (vpnString.getDefaultMessage(), "messages." + vpnString.getKey(), AntiVPN.getInstance()) .get()); AntiVPN.getInstance().getMessageHandler().reloadStrings(); + + // Starting kick checks + AntiVPN.getInstance().getExecutor().startKickChecks(); } public InputStream getResource(String filename) { diff --git a/Common/src/main/java/dev/brighten/antivpn/api/APIPlayer.java b/Common/src/main/java/dev/brighten/antivpn/api/APIPlayer.java index 72fa742..1660a73 100644 --- a/Common/src/main/java/dev/brighten/antivpn/api/APIPlayer.java +++ b/Common/src/main/java/dev/brighten/antivpn/api/APIPlayer.java @@ -1,18 +1,30 @@ package dev.brighten.antivpn.api; +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; import dev.brighten.antivpn.AntiVPN; import lombok.Getter; +import lombok.Setter; import java.net.InetAddress; import java.util.UUID; +import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; +import java.util.logging.Level; @Getter public abstract class APIPlayer { private final UUID uuid; private final String name; private final InetAddress ip; + @Setter private boolean alertsEnabled; + private static final Cache checkResultCache = Caffeine.newBuilder() + .expireAfterWrite(5, TimeUnit.MINUTES) + .maximumSize(2000) + .build(); + public APIPlayer(UUID uuid, String name, InetAddress ip) { this.uuid = uuid; this.name = name; @@ -25,12 +37,60 @@ public abstract class APIPlayer { public abstract boolean hasPermission(String permission); - public void setAlertsEnabled(boolean alertsEnabled) { - this.alertsEnabled = alertsEnabled; - } - public void updateAlertsState() { //Updating into database so its synced across servers and saved on logout. AntiVPN.getInstance().getDatabase().updateAlertsState(uuid, alertsEnabled); } + + public CheckResult checkPlayer(Consumer onKick) { + if (hasPermission("antivpn.bypass") //Has bypass permission + //Is exempt + || (uuid != null && AntiVPN.getInstance().getExecutor().isWhitelisted(uuid)) + //Or has a name that starts with a certain prefix. This is for Bedrock exempting. + || AntiVPN.getInstance().getExecutor().isWhitelisted(ip.getHostAddress()) + || AntiVPN.getInstance().getVpnConfig().getPrefixWhitelists().stream() + .anyMatch(name::startsWith)) return new CheckResult(null, ResultType.WHITELISTED); + + CheckResult cachedResult = checkResultCache.getIfPresent(ip.getHostAddress()); + + if(cachedResult != null) { + return cachedResult; + } + + AntiVPN.getInstance().getExecutor().checkIp(ip.getHostAddress()) + .thenAccept(result -> { + if(!result.isSuccess()) { + AntiVPN.getInstance().getExecutor().log(Level.WARNING, "The API query was not a success! " + + "You may need to upgrade your license on " + + "https://funkemunky.cc/shop"); + } + // If the countryList() size is zero, no need to check. + // Running country check first + CheckResult checkResult; + if (!AntiVPN.getInstance().getVpnConfig().countryList().isEmpty() + && !((uuid != null && AntiVPN.getInstance().getExecutor() + .isWhitelisted(uuid)) + //Or has a name that starts with a certain prefix. This is for Bedrock exempting. + || AntiVPN.getInstance().getExecutor().isWhitelisted(ip.getHostAddress())) + // This bit of code will decide whether or not to kick the player + // If it contains the code and it is set to whitelist, it will not kick + // as they are equal and vise versa. However, if the contains does not match + // the state, it will kick. + && AntiVPN.getInstance().getVpnConfig().countryList() + .contains(result.getCountryCode()) + != AntiVPN.getInstance().getVpnConfig().whitelistCountries()) { + //Using our built in kicking system if no commands are configured + checkResult = new CheckResult(result, ResultType.DENIED_COUNTRY); + } else if (result.isProxy()) { + checkResult = new CheckResult(result, ResultType.DENIED_PROXY); + } else { + checkResult = new CheckResult(result, ResultType.ALLOWED); + } + + checkResultCache.put(ip.getHostAddress(), checkResult); + onKick.accept(checkResult); + AntiVPN.getInstance().checked++; + }); + return new CheckResult(null, ResultType.UNKNOWN); + } } diff --git a/Common/src/main/java/dev/brighten/antivpn/api/CheckResult.java b/Common/src/main/java/dev/brighten/antivpn/api/CheckResult.java new file mode 100644 index 0000000..c331961 --- /dev/null +++ b/Common/src/main/java/dev/brighten/antivpn/api/CheckResult.java @@ -0,0 +1,6 @@ +package dev.brighten.antivpn.api; + +import dev.brighten.antivpn.web.objects.VPNResponse; + +public record CheckResult(VPNResponse response, ResultType resultType) { +} diff --git a/Common/src/main/java/dev/brighten/antivpn/api/OfflinePlayer.java b/Common/src/main/java/dev/brighten/antivpn/api/OfflinePlayer.java new file mode 100644 index 0000000..27ce5f7 --- /dev/null +++ b/Common/src/main/java/dev/brighten/antivpn/api/OfflinePlayer.java @@ -0,0 +1,26 @@ +package dev.brighten.antivpn.api; + +import java.net.InetAddress; +import java.util.UUID; + +public class OfflinePlayer extends APIPlayer { + + public OfflinePlayer(UUID uuid, String name, InetAddress ip) { + super(uuid, name, ip); + } + + @Override + public void sendMessage(String message) { + + } + + @Override + public void kickPlayer(String reason) { + + } + + @Override + public boolean hasPermission(String permission) { + return false; + } +} diff --git a/Common/src/main/java/dev/brighten/antivpn/api/ResultType.java b/Common/src/main/java/dev/brighten/antivpn/api/ResultType.java new file mode 100644 index 0000000..e8dfea4 --- /dev/null +++ b/Common/src/main/java/dev/brighten/antivpn/api/ResultType.java @@ -0,0 +1,18 @@ +package dev.brighten.antivpn.api; + +import lombok.Getter; + +public enum ResultType { + ALLOWED(false), + WHITELISTED(false), + DENIED_COUNTRY(true), + DENIED_PROXY(true), + UNKNOWN(false); + + @Getter + private final boolean shouldBlock; + + ResultType(boolean shouldBlock) { + this.shouldBlock = shouldBlock; + } +} diff --git a/Common/src/main/java/dev/brighten/antivpn/api/VPNExecutor.java b/Common/src/main/java/dev/brighten/antivpn/api/VPNExecutor.java index 838e6d8..6635f94 100644 --- a/Common/src/main/java/dev/brighten/antivpn/api/VPNExecutor.java +++ b/Common/src/main/java/dev/brighten/antivpn/api/VPNExecutor.java @@ -1,6 +1,8 @@ package dev.brighten.antivpn.api; import dev.brighten.antivpn.AntiVPN; +import dev.brighten.antivpn.utils.StringUtil; +import dev.brighten.antivpn.utils.Tuple; import dev.brighten.antivpn.utils.json.JSONException; import dev.brighten.antivpn.web.FunkemunkyAPI; import dev.brighten.antivpn.web.objects.VPNResponse; @@ -11,6 +13,7 @@ import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; import java.util.logging.Level; public abstract class VPNExecutor { @@ -20,6 +23,10 @@ public abstract class VPNExecutor { private final Set whitelisted = Collections.synchronizedSet(new HashSet<>()); @Getter private final Set whitelistedIps = Collections.synchronizedSet(new HashSet<>()); + + @Getter + private final List> toKick = Collections.synchronizedList(new LinkedList<>()); + public abstract void registerListeners(); public abstract void log(Level level, String log, Object... objects); @@ -28,10 +35,73 @@ public abstract class VPNExecutor { public abstract void logException(String message, Throwable ex); + public abstract void runCommand(String command); + public void logException(Throwable ex) { logException("An exception occurred: " + ex.getMessage(), ex); } + public void startKickChecks() { + threadExecutor.scheduleAtFixedRate(() -> { + synchronized (toKick) { + if(toKick.isEmpty()) return; + + Iterator> i = toKick.iterator(); + + while(i.hasNext()) { + var toCheck = i.next(); + + Optional player = AntiVPN.getInstance().getPlayerExecutor().getPlayer(toCheck.second()); + + if(player.isEmpty()) { + continue; + } + + handleKickingOfPlayer(toCheck.first(), player.get()); + + i.remove(); + } + } + }, 8, 2, TimeUnit.SECONDS); + } + + public void handleKickingOfPlayer(CheckResult result, APIPlayer player) { + if (AntiVPN.getInstance().getVpnConfig().alertToStaff()) AntiVPN.getInstance().getPlayerExecutor() + .getOnlinePlayers() + .stream() + .filter(APIPlayer::isAlertsEnabled) + .forEach(pl -> + pl.sendMessage(StringUtil.translateAlternateColorCodes('&', + StringUtil.varReplace(dev.brighten.antivpn.AntiVPN.getInstance().getVpnConfig() + .alertMessage(), player, result.response())))); + + if(AntiVPN.getInstance().getVpnConfig().kickPlayersOnDetect()) { + switch (result.resultType()) { + case DENIED_PROXY -> player.kickPlayer(StringUtil.varReplace(AntiVPN.getInstance().getVpnConfig() + .getKickString(), player, result.response())); + case DENIED_COUNTRY -> player.kickPlayer(StringUtil.varReplace(AntiVPN.getInstance().getVpnConfig() + .countryVanillaKickReason(), player, result.response())); + } + } + + if(!AntiVPN.getInstance().getVpnConfig().runCommands()) return; + + switch (result.resultType()) { + case DENIED_PROXY -> { + for (String command : AntiVPN.getInstance().getVpnConfig().commands()) { + runCommand(StringUtil.translateAlternateColorCodes('&', + StringUtil.varReplace(command, player, result.response()))); + } + } + case DENIED_COUNTRY -> { + for (String command : AntiVPN.getInstance().getVpnConfig().countryKickCommands()) { + runCommand(StringUtil.translateAlternateColorCodes('&', + StringUtil.varReplace(command, player, result.response()))); + } + } + } + } + public boolean isWhitelisted(UUID uuid) { if(AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled()) { return AntiVPN.getInstance().getDatabase().isWhitelisted(uuid); diff --git a/Common/src/main/java/dev/brighten/antivpn/database/sql/utils/MySQL.java b/Common/src/main/java/dev/brighten/antivpn/database/sql/utils/MySQL.java index 8355e2a..e8a1530 100644 --- a/Common/src/main/java/dev/brighten/antivpn/database/sql/utils/MySQL.java +++ b/Common/src/main/java/dev/brighten/antivpn/database/sql/utils/MySQL.java @@ -75,6 +75,9 @@ public class MySQL { } else { AntiVPN.getInstance().getExecutor().logException("Failed to load H2 database: " + ex.getCause().toString(), ex); } + } catch (Exception e) { + AntiVPN.getInstance().getExecutor().logException("Failed to load H2 database: " + e.getMessage(), e); + AntiVPN.getInstance().getExecutor().log(Level.INFO, "TIP: Try deleting the plugin folder and restarting your server!"); } } diff --git a/Common/src/main/java/dev/brighten/antivpn/utils/MiscUtils.java b/Common/src/main/java/dev/brighten/antivpn/utils/MiscUtils.java index 41c1856..50d7dea 100644 --- a/Common/src/main/java/dev/brighten/antivpn/utils/MiscUtils.java +++ b/Common/src/main/java/dev/brighten/antivpn/utils/MiscUtils.java @@ -1,6 +1,12 @@ package dev.brighten.antivpn.utils; +import dev.brighten.antivpn.AntiVPN; +import dev.brighten.antivpn.utils.json.JSONException; +import dev.brighten.antivpn.utils.json.JSONObject; +import dev.brighten.antivpn.utils.json.JsonReader; + import java.io.*; +import java.util.UUID; import java.util.concurrent.ThreadFactory; import java.util.regex.Pattern; @@ -12,7 +18,7 @@ public class MiscUtils { try { for (Closeable closeable : closeables) if (closeable != null) closeable.close(); } catch (Exception e) { - e.printStackTrace(); + AntiVPN.getInstance().getExecutor().logException(e); } } @@ -20,7 +26,7 @@ public class MiscUtils { try { for (AutoCloseable closeable : closeables) if (closeable != null) closeable.close(); } catch (Exception e) { - e.printStackTrace(); + AntiVPN.getInstance().getExecutor().logException(e); } } @@ -38,7 +44,7 @@ public class MiscUtils { out.close(); in.close(); } catch (Exception e) { - e.printStackTrace(); + AntiVPN.getInstance().getExecutor().logException(e); } } @@ -50,6 +56,33 @@ public class MiscUtils { }; } + public static UUID formatFromMojangUUID(String mojangUUID) { + StringBuilder uuid = new StringBuilder(); + for(int i = 0; i <= 31; i++) { + uuid.append(mojangUUID.charAt(i)); + if(i == 7 || i == 11 || i == 15 || i == 19) { + uuid.append("-"); + } + } + + return UUID.fromString(uuid.toString()); + } + + public static UUID lookupUUID(String playername) { + try { + JSONObject object = JsonReader + .readJsonFromUrl("https://funkemunky.cc/mojang/uuid?name=" + playername); + + if(object.has("id")) { + return formatFromMojangUUID(object.getString("uuid")); + } + } catch (IOException | JSONException e) { + AntiVPN.getInstance().getExecutor().logException("Error while looking up UUID for " + playername, e); + } + + return null; + } + public static boolean isIpv4(String ip) { return ipv4.matcher(ip).matches(); diff --git a/Common/src/main/java/dev/brighten/antivpn/utils/StringUtil.java b/Common/src/main/java/dev/brighten/antivpn/utils/StringUtil.java index 53ba8af..5081aa3 100644 --- a/Common/src/main/java/dev/brighten/antivpn/utils/StringUtil.java +++ b/Common/src/main/java/dev/brighten/antivpn/utils/StringUtil.java @@ -1,5 +1,8 @@ package dev.brighten.antivpn.utils; +import dev.brighten.antivpn.api.APIPlayer; +import dev.brighten.antivpn.web.objects.VPNResponse; + public class StringUtil { public static String line(String color) { return color + "&m-----------------------------------------------------"; @@ -12,4 +15,24 @@ public class StringUtil { public static String lineNoStrike(String color) { return color + "-----------------------------------------------------"; } + + public static String varReplace(String input, APIPlayer player, VPNResponse result) { + return input.replace("%player%", player.getName()) + .replace("%reason%", result.getMethod()) + .replace("%country%", result.getCountryName()) + .replace("%city%", result.getCity()); + } + + public static String translateAlternateColorCodes(char altColorChar, String textToTranslate) { + char[] b = textToTranslate.toCharArray(); + + for(int i = 0; i < b.length - 1; ++i) { + if (b[i] == altColorChar && "0123456789AaBbCcDdEeFfKkLlMmNnOoRr".indexOf(b[i + 1]) > -1) { + b[i] = 167; + b[i + 1] = Character.toLowerCase(b[i + 1]); + } + } + + return new String(b); + } } diff --git a/Common/src/main/java/dev/brighten/antivpn/utils/Tuple.java b/Common/src/main/java/dev/brighten/antivpn/utils/Tuple.java new file mode 100644 index 0000000..0e54854 --- /dev/null +++ b/Common/src/main/java/dev/brighten/antivpn/utils/Tuple.java @@ -0,0 +1,5 @@ +package dev.brighten.antivpn.utils; + +public record Tuple(F first, S second) { + +} diff --git a/Sponge/pom.xml b/Sponge/pom.xml index de32988..98b6cc5 100644 --- a/Sponge/pom.xml +++ b/Sponge/pom.xml @@ -31,6 +31,12 @@ 1.9.4-DEV provided + + org.slf4j + slf4j-api + 2.0.17 + compile + diff --git a/Sponge/src/main/java/dev/brighten/antivpn/sponge/SpongeListener.java b/Sponge/src/main/java/dev/brighten/antivpn/sponge/SpongeListener.java new file mode 100644 index 0000000..4519f61 --- /dev/null +++ b/Sponge/src/main/java/dev/brighten/antivpn/sponge/SpongeListener.java @@ -0,0 +1,106 @@ +package dev.brighten.antivpn.sponge; + +import dev.brighten.antivpn.AntiVPN; +import dev.brighten.antivpn.api.*; +import dev.brighten.antivpn.sponge.util.StringUtil; +import dev.brighten.antivpn.utils.Tuple; +import net.kyori.adventure.text.Component; +import org.spongepowered.api.Sponge; +import org.spongepowered.api.command.exception.CommandException; +import org.spongepowered.api.event.Listener; +import org.spongepowered.api.event.Order; +import org.spongepowered.api.event.network.ServerSideConnectionEvent; + +import java.util.concurrent.atomic.AtomicReference; +import java.util.logging.Level; + +public class SpongeListener extends VPNExecutor { + + @Listener(order = Order.EARLY) + public void onJoin(ServerSideConnectionEvent.Auth event) { + AtomicReference player = new AtomicReference<>(AntiVPN.getInstance().getPlayerExecutor() + .getPlayer(event.connection().profile().uuid()) + .orElse(new OfflinePlayer( + event.connection().profile().uuid(), + event.connection().profile().name().orElse("Unknown"), + event.connection().address().getAddress() + ))); + + CheckResult instantResult = player.get().checkPlayer(result -> { + if(result.resultType().isShouldBlock()) { + AntiVPN.getInstance().getExecutor().getToKick().add(new Tuple<>(result, player.get().getUuid())); + } + }); + + if(!instantResult.resultType().isShouldBlock()) { + return; + } + + AntiVPN.getInstance().getExecutor().getToKick().add(new Tuple<>(instantResult, player.get().getUuid())); + + if(!AntiVPN.getInstance().getVpnConfig().kickPlayersOnDetect()) { + return; + } + + event.setCancelled(true); + switch (instantResult.resultType()) { + case DENIED_PROXY -> { + AntiVPN.getInstance().getExecutor().log(Level.INFO, player.get().getName() + + " joined on a VPN/Proxy (" + instantResult.response().getMethod() + ")"); + event.setMessage(Component.text(StringUtil + .translateColorCodes('&', AntiVPN.getInstance().getVpnConfig() + .getKickString() + .replace("%player%", player.get().getName()) + .replace("%country%", instantResult.response().getCountryName()) + .replace("%code%", instantResult.response().getCountryCode())))); + } + case DENIED_COUNTRY -> + event.setMessage(Component.text(StringUtil + .translateColorCodes('&', AntiVPN.getInstance().getVpnConfig() + .countryVanillaKickReason() + .replace("%player%", player.get().getName()) + .replace("%country%", instantResult.response().getCountryName()) + .replace("%code%", instantResult.response().getCountryCode())))); + } + } + + @Override + public void registerListeners() { + Sponge.eventManager().registerListeners(SpongePlugin.INSTANCE.getPlugin(), this); + } + + @Override + public void log(Level level, String log, Object... objects) { + if (level.equals(Level.SEVERE)) { + SpongePlugin.INSTANCE.getLogger().error(String.format(log, objects)); + } else if (level.equals(Level.WARNING)) { + SpongePlugin.INSTANCE.getLogger().warn(String.format(log, objects)); + } else { + SpongePlugin.INSTANCE.getLogger().info(String.format(log, objects)); + } + } + + @Override + public void log(String log, Object... objects) { + log(Level.INFO, String.format(log, objects)); + } + + @Override + public void logException(String message, Throwable ex) { + SpongePlugin.INSTANCE.getLogger().error(message, ex); + } + + @Override + public void runCommand(String command) { + try { + Sponge.server().commandManager().process(Sponge.systemSubject(), command); + } catch (CommandException e) { + logException(e); + } + } + + @Override + public void disablePlugin() { + Sponge.eventManager().unregisterListeners(this); + } +} diff --git a/Sponge/src/main/java/dev/brighten/antivpn/sponge/SpongePlayer.java b/Sponge/src/main/java/dev/brighten/antivpn/sponge/SpongePlayer.java index 9ce17f9..2052eea 100644 --- a/Sponge/src/main/java/dev/brighten/antivpn/sponge/SpongePlayer.java +++ b/Sponge/src/main/java/dev/brighten/antivpn/sponge/SpongePlayer.java @@ -16,7 +16,7 @@ public class SpongePlayer extends APIPlayer { @Override public void sendMessage(String message) { - //player.sendMessage(StringUtil.translateColorCodes('&', message)); + player.sendMessage(Component.text(StringUtil.translateColorCodes('&', message))); } @Override diff --git a/Sponge/src/main/java/dev/brighten/antivpn/sponge/SpongePlayerExecutor.java b/Sponge/src/main/java/dev/brighten/antivpn/sponge/SpongePlayerExecutor.java index 414d7b3..be373de 100644 --- a/Sponge/src/main/java/dev/brighten/antivpn/sponge/SpongePlayerExecutor.java +++ b/Sponge/src/main/java/dev/brighten/antivpn/sponge/SpongePlayerExecutor.java @@ -1,31 +1,68 @@ package dev.brighten.antivpn.sponge; +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; import dev.brighten.antivpn.api.APIPlayer; import dev.brighten.antivpn.api.PlayerExecutor; +import org.spongepowered.api.Sponge; +import org.spongepowered.api.entity.living.player.server.ServerPlayer; import java.util.List; import java.util.Optional; import java.util.UUID; +import java.util.concurrent.TimeUnit; public class SpongePlayerExecutor implements PlayerExecutor { + + private final Cache playerCache = Caffeine.newBuilder().maximumSize(10000) + .expireAfterAccess(30, TimeUnit.MINUTES) + .build(); + @Override public Optional getPlayer(String name) { + Optional serverPlayer = Sponge.server().player(name); - return Optional.empty(); + return serverPlayer.map(SpongePlayer::new); } @Override public Optional getPlayer(UUID uuid) { - return Optional.empty(); + SpongePlayer cachedPlayer = playerCache.getIfPresent(uuid); + + if(cachedPlayer != null) { + return Optional.of(cachedPlayer); + } + + Optional serverPlayer = Sponge.server().player(uuid); + + Optional player = serverPlayer.map(SpongePlayer::new); + + player.ifPresent(value -> playerCache.put(uuid, (SpongePlayer) value)); + + return player; } @Override public void unloadPlayer(UUID uuid) { - + playerCache.invalidate(uuid); } @Override public List getOnlinePlayers() { - return null; + return Sponge.server().onlinePlayers() + .stream() + .map(pl -> { + SpongePlayer cachedPlayer = playerCache.getIfPresent(pl.uniqueId()); + + if(cachedPlayer != null) { + return cachedPlayer; + } + + SpongePlayer player = new SpongePlayer(pl); + playerCache.put(pl.uniqueId(), player); + + return (APIPlayer) player; + }) + .toList(); } } diff --git a/Sponge/src/main/java/dev/brighten/antivpn/sponge/SpongePlugin.java b/Sponge/src/main/java/dev/brighten/antivpn/sponge/SpongePlugin.java index 39d9c50..3b68391 100644 --- a/Sponge/src/main/java/dev/brighten/antivpn/sponge/SpongePlugin.java +++ b/Sponge/src/main/java/dev/brighten/antivpn/sponge/SpongePlugin.java @@ -1,28 +1,52 @@ package dev.brighten.antivpn.sponge; import com.google.inject.Inject; +import dev.brighten.antivpn.AntiVPN; +import lombok.Getter; import org.spongepowered.api.Server; +import org.spongepowered.api.config.ConfigDir; import org.spongepowered.api.event.Listener; import org.spongepowered.api.event.lifecycle.StartedEngineEvent; +import org.spongepowered.api.event.lifecycle.StoppingEngineEvent; +import org.spongepowered.plugin.PluginContainer; import org.spongepowered.plugin.builtin.jvm.Plugin; -import java.util.logging.Logger; +import java.nio.file.Path; +import org.slf4j.Logger; @Plugin("kaurivpn") +@Getter public class SpongePlugin { public static SpongePlugin INSTANCE; + //Plugin init + @Inject + private PluginContainer plugin; @Inject private Logger logger; + @Inject + @ConfigDir(sharedRoot = false) + private Path configDir; + + @Listener public void onServerStart(final StartedEngineEvent event) { INSTANCE = this; logger.info("Starting AntiVPN services..."); //Start AntiVPN + + SpongeListener spongeListener = new SpongeListener(); + + AntiVPN.start(spongeListener, new SpongePlayerExecutor(), configDir.toFile()); + } + + @Listener + public void onServer(final StoppingEngineEvent event) { + AntiVPN.getInstance().getExecutor().disablePlugin(); } } diff --git a/Velocity/src/main/java/dev/brighten/antivpn/velocity/VelocityListener.java b/Velocity/src/main/java/dev/brighten/antivpn/velocity/VelocityListener.java index 8c6a437..8f79deb 100644 --- a/Velocity/src/main/java/dev/brighten/antivpn/velocity/VelocityListener.java +++ b/Velocity/src/main/java/dev/brighten/antivpn/velocity/VelocityListener.java @@ -5,8 +5,11 @@ import com.velocitypowered.api.event.connection.DisconnectEvent; import com.velocitypowered.api.event.connection.LoginEvent; import dev.brighten.antivpn.AntiVPN; import dev.brighten.antivpn.api.APIPlayer; +import dev.brighten.antivpn.api.CheckResult; +import dev.brighten.antivpn.api.OfflinePlayer; import dev.brighten.antivpn.api.VPNExecutor; -import dev.brighten.antivpn.velocity.util.StringUtils; +import dev.brighten.antivpn.utils.StringUtil; +import dev.brighten.antivpn.web.objects.VPNResponse; import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; import java.util.concurrent.TimeUnit; @@ -20,136 +23,146 @@ public class VelocityListener extends VPNExecutor { .register(VelocityPlugin.INSTANCE, this); VelocityPlugin.INSTANCE.getServer().getEventManager().register(VelocityPlugin.INSTANCE, DisconnectEvent.class, - event -> AntiVPN.getInstance().getPlayerExecutor().unloadPlayer(event.getPlayer().getUniqueId())); + event -> AntiVPN.getInstance() + .getPlayerExecutor() + .unloadPlayer(event.getPlayer().getUniqueId())); VelocityPlugin.INSTANCE.getServer().getEventManager().register(VelocityPlugin.INSTANCE, LoginEvent.class, event -> { - if (event.getResult().isAllowed()) { - if (event.getPlayer().hasPermission("antivpn.bypass") //Has bypass permission - //Is exempt - || AntiVPN.getInstance().getExecutor().isWhitelisted(event.getPlayer().getUniqueId()) - //Or has a name that starts with a certain prefix. This is for Bedrock exempting. - || AntiVPN.getInstance().getExecutor().isWhitelisted(event.getPlayer().getRemoteAddress() - .getAddress().getHostAddress()) - || AntiVPN.getInstance().getVpnConfig().getPrefixWhitelists().stream() - .anyMatch(prefix -> event.getPlayer().getUsername().startsWith(prefix))) return; - checkIp(event.getPlayer().getRemoteAddress().getAddress().getHostAddress()) - .thenAccept(result -> { - if(!result.isSuccess()) { - VelocityPlugin.INSTANCE.getLogger() - .log(Level.WARNING, - "The API query was not a success! " + - "You may need to upgrade your license on " + - "https://funkemunky.cc/shop"); - } - // If the countryList() size is zero, no need to check. - // Running country check first - if (!AntiVPN.getInstance().getVpnConfig().countryList().isEmpty() - && !(AntiVPN.getInstance().getExecutor() - .isWhitelisted(event.getPlayer().getUniqueId()) //Is exempt - //Or has a name that starts with a certain prefix. This is for Bedrock exempting. - || AntiVPN.getInstance().getExecutor().isWhitelisted(event.getPlayer() - .getRemoteAddress().getAddress().getHostAddress())) - // This bit of code will decide whether or not to kick the player - // If it contains the code and it is set to whitelist, it will not kick - // as they are equal and vise versa. However, if the contains does not match - // the state, it will kick. - && AntiVPN.getInstance().getVpnConfig().countryList() - .contains(result.getCountryCode()) - != AntiVPN.getInstance().getVpnConfig().whitelistCountries()) { - //Using our built in kicking system if no commands are configured - if (AntiVPN.getInstance().getVpnConfig().countryKickCommands().isEmpty()) { - final String kickReason = AntiVPN.getInstance().getVpnConfig() - .countryVanillaKickReason(); - // Kicking our player - event.setResult(ResultedEvent.ComponentResult.denied(LegacyComponentSerializer.builder() - .character('&') - .build().deserialize(kickReason - .replace("%player%", event.getPlayer().getUsername()) - .replace("%country%", result.getCountryName()) - .replace("%code%", result.getCountryCode())))); - VelocityPlugin.INSTANCE.getServer().getScheduler() - .buildTask(VelocityPlugin.INSTANCE, () -> - event.getPlayer().disconnect(LegacyComponentSerializer.builder() - .character('&') - .build().deserialize(kickReason - .replace("%player%", event.getPlayer().getUsername()) - .replace("%country%", result.getCountryName()) - .replace("%code%", result.getCountryCode())))); - } else { - for (String cmd : AntiVPN.getInstance().getVpnConfig().countryKickCommands()) { - final String formattedCommand = StringUtils - .translateAlternateColorCodes('&', - cmd.replace("%player%", - event.getPlayer().getUsername()) - .replace("%country%", result.getCountryName()) - .replace("%code%", result.getCountryCode())); - // Running the command from console - VelocityPlugin.INSTANCE.getServer().getCommandManager() - .executeAsync(VelocityPlugin.INSTANCE.getServer() - .getConsoleCommandSource(), - StringUtils.translateAlternateColorCodes('&', - formattedCommand)); - } - } - } else if (result.isProxy()) { - if (AntiVPN.getInstance().getVpnConfig().kickPlayersOnDetect()) { - // Delay code execution - event.setResult(ResultedEvent.ComponentResult.denied(LegacyComponentSerializer.builder() - .character('&') - .build().deserialize(AntiVPN.getInstance().getVpnConfig() - .getKickString() - .replace("%player%", event.getPlayer().getUsername()) - .replace("%country%", result.getCountryName()) - .replace("%code%", result.getCountryCode())))); + APIPlayer player = AntiVPN.getInstance().getPlayerExecutor().getPlayer(event.getPlayer().getUniqueId()) + .orElse(new OfflinePlayer( + event.getPlayer().getUniqueId(), + event.getPlayer().getUsername(), + event.getPlayer().getRemoteAddress().getAddress() + )); - VelocityPlugin.INSTANCE.getServer().getScheduler() - .buildTask(VelocityPlugin.INSTANCE, () -> - event.getPlayer().disconnect(LegacyComponentSerializer.builder() - .character('&') - .build().deserialize(AntiVPN.getInstance().getVpnConfig() - .getKickString() - .replace("%player%", event.getPlayer().getUsername()) - .replace("%country%", result.getCountryName()) - .replace("%code%", result.getCountryCode())))) - .delay(1, TimeUnit.SECONDS).schedule(); - } - VelocityPlugin.INSTANCE.getLogger().info(event.getPlayer().getUsername() - + " joined on a VPN/Proxy (" + result.getMethod() + ")"); - //Ensuring the user wishes to alert to staff - if (AntiVPN.getInstance().getVpnConfig().alertToStaff()) - AntiVPN.getInstance().getPlayerExecutor().getOnlinePlayers().stream() - .filter(APIPlayer::isAlertsEnabled) - .forEach(pl -> - pl.sendMessage(AntiVPN.getInstance().getVpnConfig() - .alertMessage() - .replace("%player%", - event.getPlayer().getUsername()) - .replace("%reason%", - result.getMethod()) - .replace("%country%", - result.getCountryName()) - .replace("%city%", - result.getCity()))); + CheckResult instantResult = player.checkPlayer(result -> { + if(!result.resultType().isShouldBlock()) return; + + handleDeniedTasks(event, result); + }); + + if(!instantResult.resultType().isShouldBlock()) return; + + switch (instantResult.resultType()) { + case DENIED_COUNTRY -> event.setResult(ResultedEvent.ComponentResult.denied( + LegacyComponentSerializer.builder() + .character('&') + .build().deserialize(AntiVPN.getInstance().getVpnConfig() + .countryVanillaKickReason() + .replace("%player%", event.getPlayer().getUsername()) + .replace("%country%", instantResult.response().getCountryName()) + .replace("%code%", instantResult.response().getCountryCode())))); + case DENIED_PROXY -> { + VelocityPlugin.INSTANCE.getLogger().info(event.getPlayer().getUsername() + + " joined on a VPN/Proxy (" + instantResult.response().getMethod() + ")"); + event.setResult(ResultedEvent.ComponentResult.denied(LegacyComponentSerializer.builder() + .character('&') + .build().deserialize(AntiVPN.getInstance().getVpnConfig() + .getKickString() + .replace("%player%", event.getPlayer().getUsername()) + .replace("%country%", instantResult.response().getCountryName()) + .replace("%code%", instantResult.response().getCountryCode())))); + } + } + + handleDeniedTasks(event, instantResult, true); + }); + } + + private void handleDeniedTasks(LoginEvent event, CheckResult result) { + handleDeniedTasks(event, result, false); + } + + private void handleDeniedTasks(LoginEvent event, CheckResult checkResult, boolean deniedOnLogin) { + VPNResponse result = checkResult.response(); + //Ensuring the user wishes to alert to staff + if (AntiVPN.getInstance().getVpnConfig().alertToStaff()) + AntiVPN.getInstance().getPlayerExecutor().getOnlinePlayers().stream() + .filter(APIPlayer::isAlertsEnabled) + .forEach(pl -> + pl.sendMessage(dev.brighten.antivpn.AntiVPN.getInstance().getVpnConfig() + .alertMessage() + .replace("%player%", + event.getPlayer().getUsername()) + .replace("%reason%", + result.getMethod()) + .replace("%country%", + result.getCountryName()) + .replace("%city%", + result.getCity()))); + + if(deniedOnLogin) return; + + //In case the user wants to run their own commands instead of using the + // built in kicking + + if(AntiVPN.getInstance().getVpnConfig().kickPlayersOnDetect()) { + switch (checkResult.resultType()) { + case DENIED_PROXY -> VelocityPlugin.INSTANCE.getServer().getScheduler() + .buildTask(VelocityPlugin.INSTANCE, () -> + event.getPlayer().disconnect(LegacyComponentSerializer.builder() + .character('&') + .build().deserialize(AntiVPN.getInstance().getVpnConfig() + .getKickString() + .replace("%player%", event.getPlayer().getUsername()) + .replace("%country%", result.getCountryName()) + .replace("%code%", result.getCountryCode())))) + .delay(1, TimeUnit.SECONDS).schedule(); + case DENIED_COUNTRY -> VelocityPlugin.INSTANCE.getServer().getScheduler() + .buildTask(VelocityPlugin.INSTANCE, () -> + event.getPlayer().disconnect(LegacyComponentSerializer.builder() + .character('&') + .build().deserialize(AntiVPN.getInstance().getVpnConfig() + .countryVanillaKickReason() + .replace("%player%", event.getPlayer().getUsername()) + .replace("%country%", result.getCountryName()) + .replace("%code%", result.getCountryCode())))) + .delay(1, TimeUnit.SECONDS).schedule(); + } + } + + if(!AntiVPN.getInstance().getVpnConfig().runCommands()) return; + + switch (checkResult.resultType()) { + case DENIED_PROXY -> { + for (String command : AntiVPN.getInstance().getVpnConfig().commands()) { + VelocityPlugin.INSTANCE.getServer().getCommandManager() + .executeAsync(VelocityPlugin.INSTANCE.getServer() + .getConsoleCommandSource(), + StringUtil.translateAlternateColorCodes('&', + StringUtil.varReplace( + command, + AntiVPN.getInstance().getPlayerExecutor() + .getPlayer(event.getPlayer().getUniqueId()) + .orElse(new OfflinePlayer( + event.getPlayer().getUniqueId(), + event.getPlayer().getUsername(), + event.getPlayer().getRemoteAddress().getAddress()) + ), + result))); + } + } + case DENIED_COUNTRY -> { + for (String cmd : AntiVPN.getInstance().getVpnConfig().countryKickCommands()) { + final String formattedCommand = StringUtil + .translateAlternateColorCodes('&', + StringUtil.varReplace( + cmd, + AntiVPN.getInstance().getPlayerExecutor() + .getPlayer(event.getPlayer().getUniqueId()) + .orElse(new OfflinePlayer( + event.getPlayer().getUniqueId(), + event.getPlayer().getUsername(), + event.getPlayer().getRemoteAddress().getAddress()) + ), + result)); + // Running the command from console + runCommand(formattedCommand); + } + } + } - //In case the user wants to run their own commands instead of using the - // built in kicking - if (AntiVPN.getInstance().getVpnConfig().runCommands()) { - for (String command : AntiVPN.getInstance().getVpnConfig().commands()) { - VelocityPlugin.INSTANCE.getServer().getCommandManager() - .executeAsync(VelocityPlugin.INSTANCE.getServer() - .getConsoleCommandSource(), - StringUtils.translateAlternateColorCodes('&', - command.replace("%player%", - event.getPlayer().getUsername()))); - } - } - AntiVPN.getInstance().detections++; - } - AntiVPN.getInstance().checked++; - }); - } - }); } @Override @@ -167,6 +180,15 @@ public class VelocityListener extends VPNExecutor { VelocityPlugin.INSTANCE.getLogger().log(Level.SEVERE, message, ex); } + @Override + public void runCommand(String command) { + VelocityPlugin.INSTANCE.getServer().getCommandManager() + .executeAsync(VelocityPlugin.INSTANCE.getServer() + .getConsoleCommandSource(), + StringUtil.translateAlternateColorCodes('&', + command)); + } + @Override public void disablePlugin() { VelocityPlugin.INSTANCE.getServer().getEventManager().unregisterListener(VelocityPlugin.INSTANCE, this); diff --git a/Velocity/src/main/java/dev/brighten/antivpn/velocity/util/StringUtils.java b/Velocity/src/main/java/dev/brighten/antivpn/velocity/util/StringUtils.java deleted file mode 100644 index a4f02fe..0000000 --- a/Velocity/src/main/java/dev/brighten/antivpn/velocity/util/StringUtils.java +++ /dev/null @@ -1,17 +0,0 @@ -package dev.brighten.antivpn.velocity.util; - -public class StringUtils { - - public static String translateAlternateColorCodes(char altColorChar, String textToTranslate) { - char[] b = textToTranslate.toCharArray(); - - for(int i = 0; i < b.length - 1; ++i) { - if (b[i] == altColorChar && "0123456789AaBbCcDdEeFfKkLlMmNnOoRr".indexOf(b[i + 1]) > -1) { - b[i] = 167; - b[i + 1] = Character.toLowerCase(b[i + 1]); - } - } - - return new String(b); - } -} From 7247341693a764435c47bbd55710dc81c45ef79a Mon Sep 17 00:00:00 2001 From: Dawson Date: Tue, 27 May 2025 19:39:27 -0400 Subject: [PATCH 13/20] Got initial sponge powered loading working, Im not sure the consequences of it though --- .../antivpn/bungee/BungeeListener.java | 7 +-- Common/pom.xml | 6 ++- .../java/dev/brighten/antivpn/AntiVPN.java | 1 + .../dev/brighten/antivpn/utils/MiscUtils.java | 4 +- Sponge/pom.xml | 49 +++++++++++++++---- .../antivpn/sponge/SpongeListener.java | 16 +++--- .../brighten/antivpn/sponge/SpongePlugin.java | 41 +++++++++------- .../resources/META-INF/sponge_plugins.json | 29 +++++++++++ Universal/pom.xml | 9 ++-- 9 files changed, 115 insertions(+), 47 deletions(-) create mode 100644 Sponge/src/main/resources/META-INF/sponge_plugins.json diff --git a/Bungee/src/main/java/dev/brighten/antivpn/bungee/BungeeListener.java b/Bungee/src/main/java/dev/brighten/antivpn/bungee/BungeeListener.java index 7d94b55..ae5c12e 100644 --- a/Bungee/src/main/java/dev/brighten/antivpn/bungee/BungeeListener.java +++ b/Bungee/src/main/java/dev/brighten/antivpn/bungee/BungeeListener.java @@ -1,10 +1,7 @@ package dev.brighten.antivpn.bungee; import dev.brighten.antivpn.AntiVPN; -import dev.brighten.antivpn.api.APIPlayer; -import dev.brighten.antivpn.api.CheckResult; -import dev.brighten.antivpn.api.OfflinePlayer; -import dev.brighten.antivpn.api.VPNExecutor; +import dev.brighten.antivpn.api.*; import dev.brighten.antivpn.utils.MiscUtils; import dev.brighten.antivpn.utils.StringUtil; import dev.brighten.antivpn.utils.Tuple; @@ -79,7 +76,7 @@ public class BungeeListener extends VPNExecutor implements Listener { CheckResult instantResult = player.checkPlayer(result -> { if (!result.resultType().isShouldBlock()) return; AntiVPN.getInstance().getExecutor().getToKick() - .add(new Tuple<>(result, event.getConnection().getUniqueId())); + .add(new Tuple<>(result, player.getUuid())); }); if (!instantResult.resultType().isShouldBlock()) { diff --git a/Common/pom.xml b/Common/pom.xml index 0854ccd..bed828a 100644 --- a/Common/pom.xml +++ b/Common/pom.xml @@ -57,6 +57,10 @@ dev.brighten.antivpn.depends.MavenLibraries + + com.github.benmanes.caffeine + dev.brighten.antivpn.com.github.benmanes.caffeine + org.h2 dev.brighten.antivpn.shaded.org.h2 @@ -159,7 +163,7 @@ com.github.ben-manes.caffeine caffeine 3.1.8 - compile + provided diff --git a/Common/src/main/java/dev/brighten/antivpn/AntiVPN.java b/Common/src/main/java/dev/brighten/antivpn/AntiVPN.java index 3eb00b2..6382ccb 100644 --- a/Common/src/main/java/dev/brighten/antivpn/AntiVPN.java +++ b/Common/src/main/java/dev/brighten/antivpn/AntiVPN.java @@ -47,6 +47,7 @@ import java.util.List; @Relocate(from = "com.my\\" + "sql.jdbc", to = "dev.brighten.antivpn.shaded.com.mysql.jdbc") } ) +@MavenLibrary(groupId = "com.github.ben-manes.caffeine", artifactId = "caffeine", version = "3.1.8") public class AntiVPN { private static AntiVPN INSTANCE; diff --git a/Common/src/main/java/dev/brighten/antivpn/utils/MiscUtils.java b/Common/src/main/java/dev/brighten/antivpn/utils/MiscUtils.java index 50d7dea..77cde0b 100644 --- a/Common/src/main/java/dev/brighten/antivpn/utils/MiscUtils.java +++ b/Common/src/main/java/dev/brighten/antivpn/utils/MiscUtils.java @@ -73,8 +73,8 @@ public class MiscUtils { JSONObject object = JsonReader .readJsonFromUrl("https://funkemunky.cc/mojang/uuid?name=" + playername); - if(object.has("id")) { - return formatFromMojangUUID(object.getString("uuid")); + if(object.has("uuid")) { + return UUID.fromString(object.getString("uuid")); } } catch (IOException | JSONException e) { AntiVPN.getInstance().getExecutor().logException("Error while looking up UUID for " + playername, e); diff --git a/Sponge/pom.xml b/Sponge/pom.xml index 98b6cc5..7af842c 100644 --- a/Sponge/pom.xml +++ b/Sponge/pom.xml @@ -13,8 +13,8 @@ - sponge - https://repo.spongepowered.org/repository/maven-public/ + spongepowered-repo + https://repo.spongepowered.org/maven/ @@ -22,7 +22,7 @@ org.spongepowered spongeapi - 8.1.0 + 11.0.0 provided @@ -31,12 +31,6 @@ 1.9.4-DEV provided - - org.slf4j - slf4j-api - 2.0.17 - compile - @@ -44,4 +38,41 @@ 17 + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.13.0 + + 17 + 17 + -XDignore.symbol.file + + + + org.apache.maven.plugins + maven-shade-plugin + 3.6.0 + + + package + + shade + + + + + + + + + + + src/main/resources + true + + + + \ No newline at end of file diff --git a/Sponge/src/main/java/dev/brighten/antivpn/sponge/SpongeListener.java b/Sponge/src/main/java/dev/brighten/antivpn/sponge/SpongeListener.java index 4519f61..c996343 100644 --- a/Sponge/src/main/java/dev/brighten/antivpn/sponge/SpongeListener.java +++ b/Sponge/src/main/java/dev/brighten/antivpn/sponge/SpongeListener.java @@ -19,10 +19,10 @@ public class SpongeListener extends VPNExecutor { @Listener(order = Order.EARLY) public void onJoin(ServerSideConnectionEvent.Auth event) { AtomicReference player = new AtomicReference<>(AntiVPN.getInstance().getPlayerExecutor() - .getPlayer(event.connection().profile().uuid()) + .getPlayer(event.profile().uuid()) .orElse(new OfflinePlayer( - event.connection().profile().uuid(), - event.connection().profile().name().orElse("Unknown"), + event.profile().uuid(), + event.profile().name().orElse("Unknown"), event.connection().address().getAddress() ))); @@ -66,17 +66,17 @@ public class SpongeListener extends VPNExecutor { @Override public void registerListeners() { - Sponge.eventManager().registerListeners(SpongePlugin.INSTANCE.getPlugin(), this); + Sponge.eventManager().registerListeners(SpongePlugin.getInstance().getContainer(), this); } @Override public void log(Level level, String log, Object... objects) { if (level.equals(Level.SEVERE)) { - SpongePlugin.INSTANCE.getLogger().error(String.format(log, objects)); + SpongePlugin.getInstance().getLogger().error(String.format(log, objects)); } else if (level.equals(Level.WARNING)) { - SpongePlugin.INSTANCE.getLogger().warn(String.format(log, objects)); + SpongePlugin.getInstance().getLogger().warn(String.format(log, objects)); } else { - SpongePlugin.INSTANCE.getLogger().info(String.format(log, objects)); + SpongePlugin.getInstance().getLogger().info(String.format(log, objects)); } } @@ -87,7 +87,7 @@ public class SpongeListener extends VPNExecutor { @Override public void logException(String message, Throwable ex) { - SpongePlugin.INSTANCE.getLogger().error(message, ex); + SpongePlugin.getInstance().getLogger().error(message, ex); } @Override diff --git a/Sponge/src/main/java/dev/brighten/antivpn/sponge/SpongePlugin.java b/Sponge/src/main/java/dev/brighten/antivpn/sponge/SpongePlugin.java index 3b68391..ae9cbb3 100644 --- a/Sponge/src/main/java/dev/brighten/antivpn/sponge/SpongePlugin.java +++ b/Sponge/src/main/java/dev/brighten/antivpn/sponge/SpongePlugin.java @@ -4,49 +4,52 @@ import com.google.inject.Inject; import dev.brighten.antivpn.AntiVPN; import lombok.Getter; import org.spongepowered.api.Server; -import org.spongepowered.api.config.ConfigDir; +import org.apache.logging.log4j.Logger; +import org.spongepowered.api.Sponge; +import org.spongepowered.api.config.ConfigManager; import org.spongepowered.api.event.Listener; -import org.spongepowered.api.event.lifecycle.StartedEngineEvent; -import org.spongepowered.api.event.lifecycle.StoppingEngineEvent; +import org.spongepowered.api.event.lifecycle.*; import org.spongepowered.plugin.PluginContainer; import org.spongepowered.plugin.builtin.jvm.Plugin; import java.nio.file.Path; -import org.slf4j.Logger; @Plugin("kaurivpn") @Getter public class SpongePlugin { - public static SpongePlugin INSTANCE; - //Plugin init - @Inject - private PluginContainer plugin; + private final PluginContainer container; + private final Logger logger; @Inject - private Logger logger; - - @Inject - @ConfigDir(sharedRoot = false) - private Path configDir; + SpongePlugin(final PluginContainer container, final Logger logger) { + this.container = container; + this.logger = logger; + } @Listener - public void onServerStart(final StartedEngineEvent event) { - INSTANCE = this; - - logger.info("Starting AntiVPN services..."); + public void onServerStart(final ConstructPluginEvent event) { //Start AntiVPN + ConfigManager configManager = Sponge.game().configManager(); SpongeListener spongeListener = new SpongeListener(); - AntiVPN.start(spongeListener, new SpongePlayerExecutor(), configDir.toFile()); + var path = configManager.sharedConfig(container).directory(); + + logger.info("Fucking path: " + path); + + AntiVPN.start(spongeListener, new SpongePlayerExecutor(), path.toFile()); } @Listener - public void onServer(final StoppingEngineEvent event) { + public void onServer(final StoppingEngineEvent event) { AntiVPN.getInstance().getExecutor().disablePlugin(); } + public static SpongePlugin getInstance() { + return (SpongePlugin) Sponge.pluginManager().plugin("kaurivpn").get(); + } + } diff --git a/Sponge/src/main/resources/META-INF/sponge_plugins.json b/Sponge/src/main/resources/META-INF/sponge_plugins.json new file mode 100644 index 0000000..be21d3b --- /dev/null +++ b/Sponge/src/main/resources/META-INF/sponge_plugins.json @@ -0,0 +1,29 @@ +{ + "loader": { + "name": "java_plain", + "version": "1.0" + }, + "license": "All-Rights-Reserved", + "plugins": [ + { + "id": "kaurivpn", + "name": "Kauri VPN", + "version": "${version}", + "entrypoint": "dev.brighten.antivpn.sponge.SpongePlugin", + "description": "A simple and fast antivpn plugin.", + "branding": {}, + "links": { + }, + "contributors": [ + ], + "dependencies": [ + { + "id": "spongeapi", + "version": "11.0.0", + "load-order": "after", + "optional": false + } + ] + } + ] +} diff --git a/Universal/pom.xml b/Universal/pom.xml index fe9424e..0706963 100644 --- a/Universal/pom.xml +++ b/Universal/pom.xml @@ -42,12 +42,11 @@ ${project.version} compile - - + @@ -77,6 +76,10 @@ org.bstats dev.brighten.antivpn.shaded.org.bstats + + org.objectweb + dev.brighten.antivpn.shaded.org.objectweb + From 68a2acce008e83b5b0b39a3d2a2645edf1ba9cca Mon Sep 17 00:00:00 2001 From: Dawson Date: Wed, 28 May 2025 15:42:43 -0400 Subject: [PATCH 14/20] Got Sponge support working, need to ensure other plugins are still working tho --- .../dev/brighten/antivpn/api/APIPlayer.java | 5 +- .../antivpn/depends/LibraryLoader.java | 10 +- Sponge/pom.xml | 97 ++++++++++++++- .../antivpn/sponge/SpongeListener.java | 2 +- .../antivpn/sponge/SpongePlayerExecutor.java | 2 + .../brighten/antivpn/sponge/SpongePlugin.java | 37 +++--- .../antivpn/sponge/command/SpongeCommand.java | 111 ++++++++++++++++++ .../sponge/command/SpongeCommandExecutor.java | 42 +++++++ Universal/pom.xml | 5 - 9 files changed, 285 insertions(+), 26 deletions(-) create mode 100644 Sponge/src/main/java/dev/brighten/antivpn/sponge/command/SpongeCommand.java create mode 100644 Sponge/src/main/java/dev/brighten/antivpn/sponge/command/SpongeCommandExecutor.java diff --git a/Common/src/main/java/dev/brighten/antivpn/api/APIPlayer.java b/Common/src/main/java/dev/brighten/antivpn/api/APIPlayer.java index 1660a73..086755a 100644 --- a/Common/src/main/java/dev/brighten/antivpn/api/APIPlayer.java +++ b/Common/src/main/java/dev/brighten/antivpn/api/APIPlayer.java @@ -54,7 +54,10 @@ public abstract class APIPlayer { CheckResult cachedResult = checkResultCache.getIfPresent(ip.getHostAddress()); if(cachedResult != null) { - return cachedResult; + if(cachedResult.response().getIp().equals(ip.getHostAddress())) { + AntiVPN.getInstance().getExecutor().log(Level.FINE, "Cached result for " + ip.getHostAddress() + " is " + cachedResult.resultType()); + return cachedResult; + } } AntiVPN.getInstance().getExecutor().checkIp(ip.getHostAddress()) diff --git a/Common/src/main/java/dev/brighten/antivpn/depends/LibraryLoader.java b/Common/src/main/java/dev/brighten/antivpn/depends/LibraryLoader.java index 2ef3bca..8f16b26 100644 --- a/Common/src/main/java/dev/brighten/antivpn/depends/LibraryLoader.java +++ b/Common/src/main/java/dev/brighten/antivpn/depends/LibraryLoader.java @@ -55,14 +55,20 @@ import java.util.jar.JarOutputStream; public final class LibraryLoader { @SuppressWarnings("Guava") - private static final Supplier URL_INJECTOR = Suppliers.memoize(() -> - URLClassLoaderAccess.create((URLClassLoader) AntiVPN.getInstance().getClass().getClassLoader())); + private static final Supplier URL_INJECTOR = AntiVPN.getInstance().getClass().getClassLoader() instanceof URLClassLoader ? + Suppliers.memoize(() -> + URLClassLoaderAccess.create((URLClassLoader) AntiVPN.getInstance().getClass().getClassLoader())) + : null; public static void loadAll(Object object) { + if(URL_INJECTOR == null) + return; loadAll(object.getClass()); } public static void loadAll(Class clazz) { + if(URL_INJECTOR == null) + return; MavenLibrary[] libs = clazz.getDeclaredAnnotationsByType(MavenLibrary.class); for (MavenLibrary lib : libs) { diff --git a/Sponge/pom.xml b/Sponge/pom.xml index 7af842c..bb8f79a 100644 --- a/Sponge/pom.xml +++ b/Sponge/pom.xml @@ -29,7 +29,32 @@ dev.brighten.antivpn Common 1.9.4-DEV - provided + compile + + + com.github.ben-manes.caffeine + caffeine + 3.1.8 + compile + + + org.mongodb + mongo-java-driver + 3.12.14 + compile + + + com.mysql + mysql-connector-j + 9.1.0 + jar + compile + + + com.h2database + h2 + 2.2.220 + compile @@ -61,7 +86,77 @@ shade + + + *:* + + com/google/** + org/objectweb/** + org/checkerframework/** + + + + + + org.yaml.snakeyaml + dev.brighten.antivpn.shaded.org.yaml.snakeyaml + + + dev.brighten.antivpn.depends.Relocate + dev.brighten.antivpn.depends.MavenLibraries + + + + com.github.benmanes.caffeine + dev.brighten.antivpn.com.github.benmanes.caffeine + + + org.h2 + dev.brighten.antivpn.shaded.org.h2 + + + dev.brighten.antivpn.depends.Relocate + dev.brighten.antivpn.depends.MavenLibraries + + + + org.bson + dev.brighten.antivpn.shaded.org.bson + + + dev.brighten.antivpn.depends.Relocate + dev.brighten.antivpn.depends.MavenLibraries + + + + com.mongodb + dev.brighten.antivpn.shaded.com.mongodb + + + dev.brighten.antivpn.depends.Relocate + dev.brighten.antivpn.depends.MavenLibraries + + + + com.mysql.cj + dev.brighten.antivpn.shaded.com.mysql.cj + + + dev.brighten.antivpn.depends.Relocate + dev.brighten.antivpn.depends.MavenLibraries + + + + com.mysql.jdbc + dev.brighten.antivpn.shaded.com.mysql.jdbc + + + dev.brighten.antivpn.depends.Relocate + dev.brighten.antivpn.depends.MavenLibraries + + + diff --git a/Sponge/src/main/java/dev/brighten/antivpn/sponge/SpongeListener.java b/Sponge/src/main/java/dev/brighten/antivpn/sponge/SpongeListener.java index c996343..d059c0b 100644 --- a/Sponge/src/main/java/dev/brighten/antivpn/sponge/SpongeListener.java +++ b/Sponge/src/main/java/dev/brighten/antivpn/sponge/SpongeListener.java @@ -101,6 +101,6 @@ public class SpongeListener extends VPNExecutor { @Override public void disablePlugin() { - Sponge.eventManager().unregisterListeners(this); + AntiVPN.getInstance().getExecutor().log(Level.INFO, "Disabling listeners for plugin..."); } } diff --git a/Sponge/src/main/java/dev/brighten/antivpn/sponge/SpongePlayerExecutor.java b/Sponge/src/main/java/dev/brighten/antivpn/sponge/SpongePlayerExecutor.java index be373de..3904633 100644 --- a/Sponge/src/main/java/dev/brighten/antivpn/sponge/SpongePlayerExecutor.java +++ b/Sponge/src/main/java/dev/brighten/antivpn/sponge/SpongePlayerExecutor.java @@ -7,6 +7,7 @@ import dev.brighten.antivpn.api.PlayerExecutor; import org.spongepowered.api.Sponge; import org.spongepowered.api.entity.living.player.server.ServerPlayer; +import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.UUID; @@ -49,6 +50,7 @@ public class SpongePlayerExecutor implements PlayerExecutor { @Override public List getOnlinePlayers() { + if(!Sponge.game().isServerAvailable()) return Collections.emptyList(); return Sponge.server().onlinePlayers() .stream() .map(pl -> { diff --git a/Sponge/src/main/java/dev/brighten/antivpn/sponge/SpongePlugin.java b/Sponge/src/main/java/dev/brighten/antivpn/sponge/SpongePlugin.java index ae9cbb3..1aa11f2 100644 --- a/Sponge/src/main/java/dev/brighten/antivpn/sponge/SpongePlugin.java +++ b/Sponge/src/main/java/dev/brighten/antivpn/sponge/SpongePlugin.java @@ -2,38 +2,35 @@ package dev.brighten.antivpn.sponge; import com.google.inject.Inject; import dev.brighten.antivpn.AntiVPN; +import dev.brighten.antivpn.sponge.command.SpongeCommand; import lombok.Getter; import org.spongepowered.api.Server; import org.apache.logging.log4j.Logger; import org.spongepowered.api.Sponge; +import org.spongepowered.api.command.Command; import org.spongepowered.api.config.ConfigManager; import org.spongepowered.api.event.Listener; import org.spongepowered.api.event.lifecycle.*; import org.spongepowered.plugin.PluginContainer; import org.spongepowered.plugin.builtin.jvm.Plugin; -import java.nio.file.Path; - @Plugin("kaurivpn") @Getter public class SpongePlugin { //Plugin init - private final PluginContainer container; - private final Logger logger; - @Inject - SpongePlugin(final PluginContainer container, final Logger logger) { - this.container = container; - this.logger = logger; - } - + private PluginContainer container; + @Inject + private Logger logger; + @Getter + private static SpongePlugin instance; @Listener - public void onServerStart(final ConstructPluginEvent event) { - //Start AntiVPN + public void onConstruct(final ConstructPluginEvent event) { + instance = this; - ConfigManager configManager = Sponge.game().configManager(); + ConfigManager configManager = Sponge.configManager(); SpongeListener spongeListener = new SpongeListener(); var path = configManager.sharedConfig(container).directory(); @@ -48,8 +45,16 @@ public class SpongePlugin { AntiVPN.getInstance().getExecutor().disablePlugin(); } - public static SpongePlugin getInstance() { - return (SpongePlugin) Sponge.pluginManager().plugin("kaurivpn").get(); + @Listener + public void onRegisterRawCommands(final RegisterCommandEvent event){ + if(AntiVPN.getInstance() == null) { + for(int i = 0 ; i < 5 ; i++) System.out.println("FUCKING NULL"); + return; + } + AntiVPN.getInstance().getExecutor().log("Registering commands..."); + for (dev.brighten.antivpn.command.Command command : AntiVPN.getInstance().getCommands()) { + AntiVPN.getInstance().getExecutor().log("Registering command %s...", command.name()); + event.register(this.container, new SpongeCommand(command), command.name(), command.aliases()); + } } - } diff --git a/Sponge/src/main/java/dev/brighten/antivpn/sponge/command/SpongeCommand.java b/Sponge/src/main/java/dev/brighten/antivpn/sponge/command/SpongeCommand.java new file mode 100644 index 0000000..3bccdc2 --- /dev/null +++ b/Sponge/src/main/java/dev/brighten/antivpn/sponge/command/SpongeCommand.java @@ -0,0 +1,111 @@ +package dev.brighten.antivpn.sponge.command; + +import dev.brighten.antivpn.AntiVPN; +import dev.brighten.antivpn.command.Command; +import dev.brighten.antivpn.command.CommandExecutor; +import dev.brighten.antivpn.utils.StringUtil; +import lombok.val; +import net.kyori.adventure.text.Component; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.spongepowered.api.command.CommandCause; +import org.spongepowered.api.command.CommandCompletion; +import org.spongepowered.api.command.CommandResult; +import org.spongepowered.api.command.parameter.ArgumentReader; + +import java.util.*; +import java.util.stream.IntStream; + +public class SpongeCommand implements org.spongepowered.api.command.Command.Raw { + + private final Command command; + + public SpongeCommand(Command command) { + this.command = command; + } + + @Override + public CommandResult process(CommandCause sender, ArgumentReader.Mutable arguments) { + + String[] args = arguments.input().split(" "); + + CommandExecutor commandExecutor = new SpongeCommandExecutor(sender); + + val children = command.children(); + + if(children.length > 0 && args.length > 0) { + for (dev.brighten.antivpn.command.Command child : children) { + if(child.name().equalsIgnoreCase(args[0]) || Arrays.stream(child.aliases()) + .anyMatch(alias -> alias.equalsIgnoreCase(args[0]))) { + if(!sender.hasPermission("antivpn.command.*") + && !sender.hasPermission(child.permission())) { + return CommandResult.error(Component.text(StringUtil.translateAlternateColorCodes('&', + AntiVPN.getInstance().getMessageHandler().getString("no-permission").getMessage()))); + } + + commandExecutor.sendMessage(StringUtil + .translateAlternateColorCodes('&', + child.execute(commandExecutor, IntStream + .range(0, args.length - 1) + .mapToObj(i -> args[i + 1]).toArray(String[]::new)))); + return CommandResult.success(); + } + } + } + + commandExecutor.sendMessage(StringUtil + .translateAlternateColorCodes('&', + command.execute(new SpongeCommandExecutor(sender), args))); + + command.execute(new SpongeCommandExecutor(sender), args); + return CommandResult.success(); + } + + @Override + public List complete(CommandCause sender, ArgumentReader.Mutable arguments) { + val children = command.children(); + String[] args = arguments.input().split(" "); + if(children.length > 0 && args.length > 0) { + for (dev.brighten.antivpn.command.Command child : children) { + if(child.name().equalsIgnoreCase(args[0]) || Arrays.stream(child.aliases()) + .anyMatch(alias2 -> alias2.equalsIgnoreCase(args[0]))) { + return child.tabComplete(new SpongeCommandExecutor(sender), "alias", IntStream + .range(0, args.length - 1) + .mapToObj(i -> args[i + 1]).toArray(String[]::new)) + .stream() + .map(CommandCompletion::of) + .toList(); + } + } + } + return command.tabComplete(new SpongeCommandExecutor(sender), "alias", args) + .stream() + .map(CommandCompletion::of) + .toList(); + } + + @Override + public boolean canExecute(CommandCause cause) { + return cause.hasPermission(command.permission()); + } + + @Override + public Optional shortDescription(CommandCause cause) { + return command.description() != null ? Optional.of(Component.text(command.description())) : Optional.empty(); + } + + @Override + public Optional extendedDescription(CommandCause cause) { + return Optional.empty(); + } + + @Override + public Optional help(@NonNull CommandCause cause) { + return Optional.of(Component.text(StringUtil.translateAlternateColorCodes('&', + command.execute(new SpongeCommandExecutor(cause), new String[0])))); + } + + @Override + public Component usage(CommandCause cause) { + return command.usage() != null ? Component.text(command.usage()) : Component.empty(); + } +} diff --git a/Sponge/src/main/java/dev/brighten/antivpn/sponge/command/SpongeCommandExecutor.java b/Sponge/src/main/java/dev/brighten/antivpn/sponge/command/SpongeCommandExecutor.java new file mode 100644 index 0000000..de96dd5 --- /dev/null +++ b/Sponge/src/main/java/dev/brighten/antivpn/sponge/command/SpongeCommandExecutor.java @@ -0,0 +1,42 @@ +package dev.brighten.antivpn.sponge.command; + +import dev.brighten.antivpn.AntiVPN; +import dev.brighten.antivpn.api.APIPlayer; +import dev.brighten.antivpn.command.CommandExecutor; +import dev.brighten.antivpn.sponge.util.StringUtil; +import lombok.RequiredArgsConstructor; +import net.kyori.adventure.text.Component; +import org.spongepowered.api.command.CommandCause; +import org.spongepowered.api.entity.living.player.server.ServerPlayer; + +import java.util.Optional; + +@RequiredArgsConstructor +public class SpongeCommandExecutor implements CommandExecutor { + + private final CommandCause cause; + + @Override + public void sendMessage(String message, Object... objects) { + cause.sendMessage(Component.text(StringUtil.translateColorCodes('&', + String.format(message, objects)))); + } + + @Override + public boolean hasPermission(String permission) { + return cause.hasPermission(permission); + } + + @Override + public Optional getPlayer() { + if(cause.subject() instanceof ServerPlayer serverPlayer) { + return AntiVPN.getInstance().getPlayerExecutor().getPlayer(serverPlayer.uniqueId()); + } + return Optional.empty(); + } + + @Override + public boolean isPlayer() { + return cause.subject() instanceof ServerPlayer; + } +} diff --git a/Universal/pom.xml b/Universal/pom.xml index 0706963..b036049 100644 --- a/Universal/pom.xml +++ b/Universal/pom.xml @@ -42,11 +42,6 @@ ${project.version} compile - - dev.brighten.antivpn - Sponge - ${project.version} - From f271275bfae70935aece69dfb25b3139ce53f46c Mon Sep 17 00:00:00 2001 From: Dawson Date: Wed, 28 May 2025 16:33:29 -0400 Subject: [PATCH 15/20] Sponge is loading, other plugins load, fixed some bugs that were introduced, added sponge plugin uploader in maven workflow --- .github/workflows/maven.yml | 7 ++++++- .../dev/brighten/antivpn/bukkit/BukkitListener.java | 4 +++- Common/pom.xml | 2 +- Common/src/main/java/dev/brighten/antivpn/AntiVPN.java | 5 ++++- .../main/java/dev/brighten/antivpn/api/APIPlayer.java | 2 ++ Sponge/pom.xml | 2 +- .../dev/brighten/antivpn/sponge/SpongeListener.java | 10 +++++++++- 7 files changed, 26 insertions(+), 6 deletions(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index f4dd1cb..ed7bcd3 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -29,5 +29,10 @@ jobs: - name: Upload AntiVPN uses: actions/upload-artifact@v4 with: - name: AntiVPN + name: AntiVPN-Universal path: Universal/target/AntiVPN-*.jar + - name: Upload Sponge plugin + uses: actions/upload-artifact@v4 + with: + name: AntiVPN-Sponge + path: Sponge/target/Sponge-*.jar diff --git a/Bukkit/src/main/java/dev/brighten/antivpn/bukkit/BukkitListener.java b/Bukkit/src/main/java/dev/brighten/antivpn/bukkit/BukkitListener.java index fe37786..9c51ef6 100644 --- a/Bukkit/src/main/java/dev/brighten/antivpn/bukkit/BukkitListener.java +++ b/Bukkit/src/main/java/dev/brighten/antivpn/bukkit/BukkitListener.java @@ -62,7 +62,7 @@ public class BukkitListener extends VPNExecutor implements Listener { .orElse(new OfflinePlayer( event.getPlayer().getUniqueId(), event.getPlayer().getName(), - event.getRealAddress() + event.getAddress() )); CheckResult instantResult = player.checkPlayer(result -> { @@ -81,6 +81,8 @@ public class BukkitListener extends VPNExecutor implements Listener { return; } + AntiVPN.getInstance().getExecutor().log(Level.INFO, "%s was kicked from pre-login cache with IP %s", event.getPlayer().getName(), instantResult.response().getIp()); + event.setResult(PlayerLoginEvent.Result.KICK_BANNED); switch (instantResult.resultType()) { case DENIED_COUNTRY -> event.setKickMessage(StringUtil.translateAlternateColorCodes('&', diff --git a/Common/pom.xml b/Common/pom.xml index bed828a..dec5693 100644 --- a/Common/pom.xml +++ b/Common/pom.xml @@ -59,7 +59,7 @@ com.github.benmanes.caffeine - dev.brighten.antivpn.com.github.benmanes.caffeine + dev.brighten.antivpn.shaded.com.github.benmanes.caffeine org.h2 diff --git a/Common/src/main/java/dev/brighten/antivpn/AntiVPN.java b/Common/src/main/java/dev/brighten/antivpn/AntiVPN.java index 6382ccb..e89821a 100644 --- a/Common/src/main/java/dev/brighten/antivpn/AntiVPN.java +++ b/Common/src/main/java/dev/brighten/antivpn/AntiVPN.java @@ -47,7 +47,10 @@ import java.util.List; @Relocate(from = "com.my\\" + "sql.jdbc", to = "dev.brighten.antivpn.shaded.com.mysql.jdbc") } ) -@MavenLibrary(groupId = "com.github.ben-manes.caffeine", artifactId = "caffeine", version = "3.1.8") +@MavenLibrary(groupId = "com.\\github\\.ben-manes\\.caffeine", artifactId = "caffeine", version = "3.1.8", + relocations = { + @Relocate(from = "com\\.github\\.benmanes\\.caffeine", to = "dev.brighten.antivpn.shaded.com.github.benmanes.caffeine"), + }) public class AntiVPN { private static AntiVPN INSTANCE; diff --git a/Common/src/main/java/dev/brighten/antivpn/api/APIPlayer.java b/Common/src/main/java/dev/brighten/antivpn/api/APIPlayer.java index 086755a..1c0f2bc 100644 --- a/Common/src/main/java/dev/brighten/antivpn/api/APIPlayer.java +++ b/Common/src/main/java/dev/brighten/antivpn/api/APIPlayer.java @@ -90,6 +90,8 @@ public abstract class APIPlayer { checkResult = new CheckResult(result, ResultType.ALLOWED); } + AntiVPN.getInstance().getExecutor().log(Level.FINE, "Result for " + ip.getHostAddress() + " is " + checkResult.resultType()); + checkResultCache.put(ip.getHostAddress(), checkResult); onKick.accept(checkResult); AntiVPN.getInstance().checked++; diff --git a/Sponge/pom.xml b/Sponge/pom.xml index bb8f79a..075ed54 100644 --- a/Sponge/pom.xml +++ b/Sponge/pom.xml @@ -109,7 +109,7 @@ com.github.benmanes.caffeine - dev.brighten.antivpn.com.github.benmanes.caffeine + dev.brighten.antivpn.shaded.com.github.benmanes.caffeine org.h2 diff --git a/Sponge/src/main/java/dev/brighten/antivpn/sponge/SpongeListener.java b/Sponge/src/main/java/dev/brighten/antivpn/sponge/SpongeListener.java index d059c0b..47b6664 100644 --- a/Sponge/src/main/java/dev/brighten/antivpn/sponge/SpongeListener.java +++ b/Sponge/src/main/java/dev/brighten/antivpn/sponge/SpongeListener.java @@ -17,7 +17,7 @@ import java.util.logging.Level; public class SpongeListener extends VPNExecutor { @Listener(order = Order.EARLY) - public void onJoin(ServerSideConnectionEvent.Auth event) { + public void onJoin(ServerSideConnectionEvent.Login event) { AtomicReference player = new AtomicReference<>(AntiVPN.getInstance().getPlayerExecutor() .getPlayer(event.profile().uuid()) .orElse(new OfflinePlayer( @@ -42,6 +42,8 @@ public class SpongeListener extends VPNExecutor { return; } + AntiVPN.getInstance().getExecutor().log(Level.INFO, "%s was kicked from cache with IP %s", player.get().getName(), instantResult.response().getIp()); + event.setCancelled(true); switch (instantResult.resultType()) { case DENIED_PROXY -> { @@ -64,6 +66,12 @@ public class SpongeListener extends VPNExecutor { } } + @Listener + public void onPlayerDisconnect(ServerSideConnectionEvent.Disconnect event) { + event.profile().ifPresent(profile -> + AntiVPN.getInstance().getPlayerExecutor().unloadPlayer(profile.uuid())); + } + @Override public void registerListeners() { Sponge.eventManager().registerListeners(SpongePlugin.getInstance().getContainer(), this); From 866217ff08873aadf8d3f28e195fb3b6feca479d Mon Sep 17 00:00:00 2001 From: Dawson Date: Wed, 28 May 2025 16:36:28 -0400 Subject: [PATCH 16/20] Fixing repositories --- Sponge/pom.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Sponge/pom.xml b/Sponge/pom.xml index 075ed54..468cf97 100644 --- a/Sponge/pom.xml +++ b/Sponge/pom.xml @@ -16,6 +16,10 @@ spongepowered-repo https://repo.spongepowered.org/maven/ + + funkemunky-releases + https://nexus.funkemunky.cc/content/repositories/releases/ + From 4f43028ec0076f5fdc1869136f31a2c58ddb88e9 Mon Sep 17 00:00:00 2001 From: Dawson Date: Wed, 28 May 2025 16:38:48 -0400 Subject: [PATCH 17/20] Attempting to fix cache dependency path --- .github/workflows/maven.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index ed7bcd3..c3638cb 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -17,7 +17,8 @@ jobs: with: java-version: '17' distribution: 'zulu' - cache: maven + cache: 'maven' + cache-dependency-path: '**/pom.xml' # Add this line - name: Set up Maven uses: stCarolas/setup-maven@v5 with: From 103fdf74da59439d2320347c3c2bcde7b3d506f8 Mon Sep 17 00:00:00 2001 From: Dawson Date: Wed, 28 May 2025 16:40:28 -0400 Subject: [PATCH 18/20] Using JDK 21 instead --- .github/workflows/maven.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index c3638cb..ad0d183 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -12,10 +12,10 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Set up JDK 17 + - name: Set up JDK 21 uses: actions/setup-java@v4 with: - java-version: '17' + java-version: '21' distribution: 'zulu' cache: 'maven' cache-dependency-path: '**/pom.xml' # Add this line From f28badf9499dfa49a4be003d27538d0a582bc069 Mon Sep 17 00:00:00 2001 From: Dawson Date: Wed, 28 May 2025 16:44:00 -0400 Subject: [PATCH 19/20] Adding maven cache instead of using the setup-java job --- .github/workflows/maven.yml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index ad0d183..073bbfa 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -11,14 +11,19 @@ jobs: runs-on: ubuntu-latest steps: + - name: Cache local Maven repository + uses: actions/cache@v3 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- - uses: actions/checkout@v4 - name: Set up JDK 21 uses: actions/setup-java@v4 with: java-version: '21' distribution: 'zulu' - cache: 'maven' - cache-dependency-path: '**/pom.xml' # Add this line - name: Set up Maven uses: stCarolas/setup-maven@v5 with: From da5511fc3383d1b40d8efe99762508a725d1eeb7 Mon Sep 17 00:00:00 2001 From: Dawson Date: Wed, 28 May 2025 16:47:06 -0400 Subject: [PATCH 20/20] Sponge compile target to Java 21 --- Sponge/pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Sponge/pom.xml b/Sponge/pom.xml index 468cf97..94e28a5 100644 --- a/Sponge/pom.xml +++ b/Sponge/pom.xml @@ -63,8 +63,8 @@ - 17 - 17 + 21 + 21 @@ -74,8 +74,8 @@ maven-compiler-plugin 3.13.0 - 17 - 17 + 21 + 21 -XDignore.symbol.file