mirror of
https://github.com/funkemunky/AntiVPN.git
synced 2026-06-29 06:48:28 +00:00
Refactor, code cleanup, sponge impl.
BungeeCord still has problems, bad API, not my fault technically. still need to fix though
This commit is contained in:
@@ -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<UUID, VPNResponse> 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
|
||||
|
||||
@@ -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<UUID, VPNResponse> 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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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<String, CheckResult> 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<CheckResult> 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
package dev.brighten.antivpn.api;
|
||||
|
||||
import dev.brighten.antivpn.web.objects.VPNResponse;
|
||||
|
||||
public record CheckResult(VPNResponse response, ResultType resultType) {
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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<UUID> whitelisted = Collections.synchronizedSet(new HashSet<>());
|
||||
@Getter
|
||||
private final Set<String> whitelistedIps = Collections.synchronizedSet(new HashSet<>());
|
||||
|
||||
@Getter
|
||||
private final List<Tuple<CheckResult, UUID>> 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<Tuple<CheckResult, UUID>> i = toKick.iterator();
|
||||
|
||||
while(i.hasNext()) {
|
||||
var toCheck = i.next();
|
||||
|
||||
Optional<APIPlayer> 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);
|
||||
|
||||
@@ -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!");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
package dev.brighten.antivpn.utils;
|
||||
|
||||
public record Tuple<F, S>(F first, S second) {
|
||||
|
||||
}
|
||||
@@ -31,6 +31,12 @@
|
||||
<version>1.9.4-DEV</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>2.0.17</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<properties>
|
||||
|
||||
@@ -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<APIPlayer> 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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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<UUID, SpongePlayer> playerCache = Caffeine.newBuilder().maximumSize(10000)
|
||||
.expireAfterAccess(30, TimeUnit.MINUTES)
|
||||
.build();
|
||||
|
||||
@Override
|
||||
public Optional<APIPlayer> getPlayer(String name) {
|
||||
Optional<ServerPlayer> serverPlayer = Sponge.server().player(name);
|
||||
|
||||
return Optional.empty();
|
||||
return serverPlayer.map(SpongePlayer::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<APIPlayer> getPlayer(UUID uuid) {
|
||||
return Optional.empty();
|
||||
SpongePlayer cachedPlayer = playerCache.getIfPresent(uuid);
|
||||
|
||||
if(cachedPlayer != null) {
|
||||
return Optional.of(cachedPlayer);
|
||||
}
|
||||
|
||||
Optional<ServerPlayer> serverPlayer = Sponge.server().player(uuid);
|
||||
|
||||
Optional<APIPlayer> 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<APIPlayer> 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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Server> 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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user