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); - } -}