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:
2025-05-27 13:13:53 -04:00
parent de31d837b9
commit 3f6bb4a0e6
19 changed files with 739 additions and 446 deletions
@@ -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);