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 bb369a2..bd8446c 100644 --- a/Bukkit/src/main/java/dev/brighten/antivpn/bukkit/BukkitListener.java +++ b/Bukkit/src/main/java/dev/brighten/antivpn/bukkit/BukkitListener.java @@ -5,8 +5,9 @@ import dev.brighten.antivpn.api.APIPlayer; import dev.brighten.antivpn.api.VPNExecutor; import dev.brighten.antivpn.message.VpnString; import lombok.val; +import net.md_5.bungee.api.ChatColor; +import net.md_5.bungee.api.chat.TextComponent; import org.bukkit.Bukkit; -import org.bukkit.ChatColor; import org.bukkit.entity.Player; import org.bukkit.event.Event; import org.bukkit.event.EventHandler; @@ -71,49 +72,88 @@ public class BukkitListener extends VPNExecutor implements Listener { @EventHandler public void onListener(final PlayerLoginEvent event) { //If they're exempt, don't check. - if(AntiVPN.getInstance().getExecutor().isWhitelisted(event.getPlayer().getUniqueId()) - || AntiVPN.getInstance().getExecutor().isWhitelisted(event.getAddress().getHostAddress())) return; - checkIp(event.getAddress().getHostAddress(), AntiVPN.getInstance().getVpnConfig().cachedResults(), result -> { - if(result.isSuccess() && result.isProxy()) { - new BukkitRunnable() { - public void run() { - Player player = event.getPlayer(); + 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().getVpnConfig().getPrefixWhitelists().stream() + .anyMatch(prefix -> event.getPlayer().getName().startsWith(prefix))) return; - if(!player.hasPermission("antivpn.bypass") //Has bypass permission - //Or has a name that starts with a certain prefix. This is for Bedrock exempting. - && AntiVPN.getInstance().getVpnConfig().getPrefixWhitelists().stream() - .noneMatch(prefix -> player.getName().startsWith(prefix))) { - if (AntiVPN.getInstance().getVpnConfig().kickPlayersOnDetect()) - player.kickPlayer(ChatColor.translateAlternateColorCodes('&', - AntiVPN.getInstance().getVpnConfig().getKickString())); + final Player player = event.getPlayer(); + checkIp(event.getAddress().getHostAddress(), + AntiVPN.getInstance().getVpnConfig().cachedResults(), result -> { + if(result.isSuccess()) { + //We need to run on main thread or kicking and running commands will cause errors + new BukkitRunnable() { + public void run() { + // If the countryList() size is zero, no need to check. + // Running country check first + if(AntiVPN.getInstance().getVpnConfig().countryList().size() > 0 + // 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().size() == 0) { + final String kickReason = AntiVPN.getInstance().getVpnConfig() + .countryVanillaKickReason(); + // Kicking our player + event.getPlayer().kickPlayer(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())); - //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()))); + // Runs our command from console + Bukkit.dispatchCommand(Bukkit.getConsoleSender(), formattedCommand); + } + } + } else if(result.isProxy()) { + if(AntiVPN.getInstance().getVpnConfig().kickPlayersOnDetect()) + player.kickPlayer(org.bukkit.ChatColor.translateAlternateColorCodes('&', + AntiVPN.getInstance().getVpnConfig().getKickString())); + Bukkit.getLogger().info(event.getPlayer().getName() + + " joined on a VPN/Proxy (" + result.getMethod() + ")"); - //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()) { - Bukkit.dispatchCommand(Bukkit.getConsoleSender(), - ChatColor.translateAlternateColorCodes('&', - command.replace("%player%", event.getPlayer().getName()))); + //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()) { + for (String command : AntiVPN.getInstance().getVpnConfig().commands()) { + Bukkit.dispatchCommand(Bukkit.getConsoleSender(), + ChatColor.translateAlternateColorCodes('&', + command.replace("%player%", + event.getPlayer().getName()))); + } + } + AntiVPN.getInstance().detections++; } - } - Bukkit.getLogger().info(player.getPlayer().getName() - + " joined on a VPN/Proxy (" + result.getMethod() + ")"); + } + }.runTask(BukkitPlugin.pluginInstance); + } else { + Bukkit.getLogger() + .log(Level.WARNING, + "The API query was not a success! " + + "You may need to upgrade your license on https://funkemunky.cc/shop"); } - }.runTask(BukkitPlugin.pluginInstance); - } else if(!result.isSuccess()) { - Bukkit.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++; + }); } } 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 66b7fa3..43895d3 100644 --- a/Bungee/src/main/java/dev/brighten/antivpn/bungee/BungeeListener.java +++ b/Bungee/src/main/java/dev/brighten/antivpn/bungee/BungeeListener.java @@ -57,34 +57,68 @@ public class BungeeListener extends VPNExecutor implements Listener { checkIp(event.getPlayer().getAddress().getAddress().getHostAddress(), AntiVPN.getInstance().getVpnConfig().cachedResults(), result -> { - if(result.isSuccess() && 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(result.isSuccess()) { + // If the countryList() size is zero, no need to check. + // Running country check first + if(AntiVPN.getInstance().getVpnConfig().countryList().size() > 0 + // 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().size() == 0) { + final String kickReason = AntiVPN.getInstance().getVpnConfig() + .countryVanillaKickReason(); + // Kicking our player + event.getPlayer().disconnect(TextComponent.fromLegacyText(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())); - 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()))); + // Runs our command from console + BungeeCord.getInstance().getPluginManager().dispatchCommand( + BungeeCord.getInstance().getConsole(), formattedCommand); + } } + } 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++; } - AntiVPN.getInstance().detections++; - } else if(!result.isSuccess()) { + + } else { BungeeCord.getInstance().getLogger() .log(Level.WARNING, "The API query was not a success! " + diff --git a/Common/src/main/java/dev/brighten/antivpn/api/VPNConfig.java b/Common/src/main/java/dev/brighten/antivpn/api/VPNConfig.java index b6d700a..d6df035 100644 --- a/Common/src/main/java/dev/brighten/antivpn/api/VPNConfig.java +++ b/Common/src/main/java/dev/brighten/antivpn/api/VPNConfig.java @@ -4,6 +4,7 @@ import dev.brighten.antivpn.AntiVPN; import dev.brighten.antivpn.utils.ConfigDefault; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -21,9 +22,10 @@ public class VPNConfig { "database.username", AntiVPN.getInstance()), defaultPassword = new ConfigDefault<>("password", "database.password", AntiVPN.getInstance()), - - - defaultIp = new ConfigDefault<>("localhost", "database.ip", AntiVPN.getInstance()), + defaultCountryKickReason = new ConfigDefault<>( + "&cSorry, but our server does not allow connections from\n&f%country%", + "countries.vanillaKickReason", AntiVPN.getInstance()), + defaultIp = new ConfigDefault<>("localhost", "database.ip", AntiVPN.getInstance()), defaultAlertMsg = new ConfigDefault<>("&8[&6KauriVPN&8] &e%player% &7has joined on a VPN/proxy" + " &8(&f%reason%&8) &7in location &8(&f%city%&7, &f%country%&8)", "alerts.message", AntiVPN.getInstance()); @@ -37,6 +39,8 @@ public class VPNConfig { = new ConfigDefault<>(true, "kickPlayers", AntiVPN.getInstance()), defaultAlertToStaff = new ConfigDefault<>(true, "alerts.enabled", AntiVPN.getInstance()), + defaultWhitelistCountries = new ConfigDefault<>(true, "countries.whitelist", + AntiVPN.getInstance()), defaultMetrics = new ConfigDefault<>(true, "bstats", AntiVPN.getInstance()); private final ConfigDefault defaultPort = new ConfigDefault<>(-1, "database.port", AntiVPN.getInstance()); @@ -44,95 +48,192 @@ public class VPNConfig { "prefixWhitelists", AntiVPN.getInstance()), defaultCommands = new ConfigDefault<>( Collections.singletonList("kick %player% VPNs are not allowed on our server!"), "commands.execute", AntiVPN.getInstance()), - defBlockedCountries = new ConfigDefault<>(new ArrayList<>(), "blockedCountries", - AntiVPN.getInstance()), - defAllowedCountries = new ConfigDefault<>(new ArrayList<>(), "allowedCountries", + defCountryKickCommands = new ConfigDefault<>(Collections.emptyList(), + "countries.commands", AntiVPN.getInstance()), + defCountrylist = new ConfigDefault<>(new ArrayList<>(), "countries.list", AntiVPN.getInstance()); - private String license, kickMessage, databaseType, databaseName, mongoURL, username, password, ip, alertMsg; - private List prefixWhitelists, commands, allowedCountries, blockedCountries; + private String license, kickMessage, databaseType, databaseName, mongoURL, username, password, ip, alertMsg, + countryVanillaKickReason; + private List prefixWhitelists, commands, countryList, countryKickCommands; private int port; - private boolean cacheResults, databaseEnabled, useCredentials, commandsEnabled, kickPlayers, alertToStaff, metrics; + private boolean cacheResults, databaseEnabled, useCredentials, commandsEnabled, kickPlayers, alertToStaff, + metrics, whitelistCountries; + /** + * License from https://funkemunky.cc/shop to be used for more queries. + * @return String + */ public String getLicense() { return license; } + /** + * If true, results will be cached to reduce queries to https://funkemunky.cc + * @return boolean + */ public boolean cachedResults() { return cacheResults; } + /** + * Will be used for vanilla kick message when {@link VPNConfig#runCommands()} is true. + * @return String + */ public String getKickString() { return kickMessage; } - + + /** + * Message to send staff on proxy detection. + * @return String + */ public String alertMessage() { return alertMsg; } - + + /** + * If true, staff will be alerted on proxy detection. + * @return boolean + */ public boolean alertToStaff() { return alertToStaff; } + /** + * If true, will run {@link VPNConfig#commands()} on detect. If not, it will use vanilla kicking methods. + * @return boolean + */ public boolean runCommands() { return commandsEnabled; } - + + /** + * Commands to run on proxy detection. + * @return List + */ public List commands() { return commands; } + /** + * If false, no commands nor kick will be run on proxy detection. + * @return boolean + */ public boolean kickPlayersOnDetect() { return kickPlayers; } - + + /** + * Returns Strings of which are checked against the beginning of player names. Used to + * allow Geyser-connected players to join. + * @return List + */ public List getPrefixWhitelists() { return prefixWhitelists; } - + + /** + * Returns true if we want to use a database + * @return boolean + */ public boolean isDatabaseEnabled() { return databaseEnabled; } + /** + * Whether or not the database we want to connect to requires credentials. + * @return boolean + */ public boolean useDatabaseCreds() { return useCredentials; } + /** + * Only for Mongo only. URL used for connecting to database. Overrides other fields + * @return String + */ public String mongoDatabaseURL() { return mongoURL; } - + + /** + * Database type. Either MySQL and Mongo. + * @return String + */ public String getDatabaseType() { return databaseType; } - + + /** + * Database name + * @return String + */ public String getDatabaseName() { return databaseName; } + /** + * Database username + * @return String + */ public String getUsername() { return username; } - + + /** + * Database Password + * @return String + */ public String getPassword() { return password; } + /** + * Database IP + * @return String + */ public String getIp() { return ip; } - - public List allowedCountries() { - return allowedCountries; + /** + * Returns the list of ISO country codes we need to check. + * @return List + */ + public List countryList() { + return countryList; } - - public List blockedCountries() { - return blockedCountries; + /** + * If true, we only allow the {@link VPNConfig#countryKickCommands()}. If false, we blacklist them. + * @return boolean + */ + public boolean whitelistCountries() { + return whitelistCountries; } - + /** + * Returns our configured commands to run on player country detection. + * @return List + */ + public List countryKickCommands() { + return countryKickCommands; + } + + /** + * Returns the vanilla kick reason for bad country locations + * @return String + */ + public String countryVanillaKickReason() { + return countryVanillaKickReason; + } + + /** + * Gets the port based on configuration. If {@link VPNConfig#port} is -1, will get default port + * based on {@link VPNConfig#getDatabaseType()} lowerCase(). + * @return int + */ public int getPort() { if(port == -1) { switch (getDatabaseType().toLowerCase()) { @@ -149,11 +250,18 @@ public class VPNConfig { return port; } - + + /** + * If true, https://bstats.org metrics will be collected to improve KauriVPN. + * @return boolean + */ public boolean metrics() { return metrics; } + /** + * Grabs all information from the config.yml + */ public void update() { license = licenseDefault.get(); kickMessage = kickStringDefault.get(); @@ -174,8 +282,10 @@ public class VPNConfig { alertToStaff = defaultAlertToStaff.get(); alertMsg = defaultAlertMsg.get(); metrics = defaultMetrics.get(); - blockedCountries = defBlockedCountries.get(); - allowedCountries = defAllowedCountries.get(); + countryList = defCountrylist.get(); + whitelistCountries = defaultWhitelistCountries.get(); + countryKickCommands = defCountryKickCommands.get(); + countryVanillaKickReason = defaultCountryKickReason.get(); } } diff --git a/Common/src/main/resources/config.yml b/Common/src/main/resources/config.yml index e02ea71..76c8abe 100644 --- a/Common/src/main/resources/config.yml +++ b/Common/src/main/resources/config.yml @@ -48,6 +48,20 @@ alerts: # %city% (City name). message: '&8[&6KauriVPN&8] &e%player% &7has joined on a VPN/proxy &8(&f%reason%&8) &7in location &8(&f%city%&7, &f%country%&8)' +# Configuration for country gatekeepings +countries: + # You must use ISO codes for country configuration: https://www.iban.com/country-code + # Leave empty to disable this configuration + list: [] + # Set whitelist to true to only allow listed country codes, and false to deny listed country codes. + whitelist: true + # The commands to be run if the player is not allowed on the server with the above configured conditions + # Placeholders: %country% (Country name), %player% (Player name), %code% (Country ISO Code) + # Keep this empty with "[]" if you want to use the built in kicking system. + commands: [] + # The kick message that will be used if commands are configured to use the built-in kicking sytem. + # PlaceHolders: %country% (Country name), %player% (Player name), %code% (Country ISO Code) + vanillaKickReason: "&cSorry, but our server does not allow connections from\n&f%country%" # This will disable any information being sent to https://bstats.org. We recommend you keep this enabled as it helps # us understand our users and put effort where it is needed. All information sent goes under their privacy as seen # here: https://bstats.org/privacy-policy 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 a14bd27..3daf9bc 100644 --- a/Velocity/src/main/java/dev/brighten/antivpn/velocity/VelocityListener.java +++ b/Velocity/src/main/java/dev/brighten/antivpn/velocity/VelocityListener.java @@ -4,6 +4,7 @@ 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.VPNConfig; import dev.brighten.antivpn.api.VPNExecutor; import dev.brighten.antivpn.velocity.util.StringUtils; import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; @@ -24,48 +25,96 @@ public class VelocityListener extends VPNExecutor { event -> { if(event.getResult().isAllowed()) { if(event.getPlayer().hasPermission("antivpn.bypass") //Has bypass permission - || AntiVPN.getInstance().getExecutor().isWhitelisted(event.getPlayer().getUniqueId()) //Is exempt + //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()) + .getAddress().getHostAddress()) || AntiVPN.getInstance().getVpnConfig().getPrefixWhitelists().stream() .anyMatch(prefix -> event.getPlayer().getUsername().startsWith(prefix))) return; checkIp(event.getPlayer().getRemoteAddress().getAddress().getHostAddress(), AntiVPN.getInstance().getVpnConfig().cachedResults(), result -> { - if(result.isSuccess() && result.isProxy()) { - if(AntiVPN.getInstance().getVpnConfig().kickPlayersOnDetect()) - event.getPlayer().disconnect(LegacyComponentSerializer.builder().character('&') - .build().deserialize(AntiVPN.getInstance().getVpnConfig().getKickString())); - VelocityPlugin.INSTANCE.getLogger().info(event.getPlayer().getUsername() - + " 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().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()))); + if(result.isSuccess()) { + // If the countryList() size is zero, no need to check. + // Running country check first + if(AntiVPN.getInstance().getVpnConfig().countryList().size() > 0 + // 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().size() == 0) { + final String kickReason = AntiVPN.getInstance().getVpnConfig() + .countryVanillaKickReason(); + // Kicking our player + 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()) + event.getPlayer().disconnect(LegacyComponentSerializer.builder().character('&') + .build().deserialize(AntiVPN.getInstance().getVpnConfig() + .getKickString())); + 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().detections++; - } else if(!result.isSuccess()) { + } 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"); + "You may need to upgrade your license on " + + "https://funkemunky.cc/shop"); } AntiVPN.getInstance().checked++; });