Refactor of checkPlayer logic to fix potential concurrency issues. This was a poor design. Also implemented gradle tooling for debugging velocity plugins

This commit is contained in:
2026-04-30 20:54:37 -04:00
parent 8637cd5e8b
commit bc6828f8af
10 changed files with 143 additions and 82 deletions
+1
View File
@@ -296,3 +296,4 @@ $RECYCLE.BIN/
# End of https://www.toptal.com/developers/gitignore/api/windows,macos,linux,maven,java,intellij,eclipse,netbeans
/.gradle/
.grade/**
/run/
@@ -18,6 +18,7 @@ package dev.brighten.antivpn.bukkit;
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.utils.StringUtil;
@@ -82,7 +83,8 @@ public class BukkitListener extends VPNExecutor implements Listener {
event.getAddress()
));
player.checkPlayer(result -> {
CheckResult result = player.checkPlayer();
if(!result.resultType().isShouldBlock()) return;
if(!AntiVPN.getInstance().getVpnConfig().isKickPlayers()) {
@@ -104,7 +106,6 @@ public class BukkitListener extends VPNExecutor implements Listener {
);
default -> "You were kicked by KauriVPN for an unknown reason!";
});
});
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
@@ -77,7 +77,6 @@ public class BungeeListener extends VPNExecutor implements Listener {
@EventHandler(priority = EventPriority.HIGH)
public void onListener(final PreLoginEvent event) {
APIPlayer player = AntiVPN.getInstance().getPlayerExecutor().getPlayer(event.getConnection().getUniqueId())
.orElseGet(() -> {
UUID uuid = MiscUtils.lookupUUID(event.getConnection().getName());
@@ -88,7 +87,8 @@ public class BungeeListener extends VPNExecutor implements Listener {
((InetSocketAddress) event.getConnection().getSocketAddress()).getAddress());
});
player.checkPlayer(result -> {
CheckResult result = player.checkPlayer();
if (!result.resultType().isShouldBlock()) return;
if(!AntiVPN.getInstance().getVpnConfig().isKickPlayers()) {
@@ -103,7 +103,6 @@ public class BungeeListener extends VPNExecutor implements Listener {
.getCountryVanillaKickReason(), player, result.response());
default -> "You were kicked by KauriVPN for an unknown reason!";
}, player, result.response())));
});
}
@EventHandler(priority = EventPriority.HIGH)
@@ -26,7 +26,6 @@ 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
@@ -74,7 +73,12 @@ public abstract class APIPlayer {
);
}
public void checkPlayer(Consumer<CheckResult> onResult) {
/***
* The result is only returned if it is cached. Otherwise, there is an asynchronous call to the API
* and will handle kicking the player if necessary.
* @return CheckResult - The cached result of the check if it exists.
*/
public CheckResult checkPlayer() {
if (hasPermission("antivpn.bypass") //Has bypass permission
//Is exempt
|| (uuid != null && AntiVPN.getInstance().getExecutor().isWhitelisted(uuid))
@@ -82,8 +86,7 @@ public abstract class APIPlayer {
|| AntiVPN.getInstance().getExecutor().isWhitelisted(ip.getHostAddress() + "/32")
|| AntiVPN.getInstance().getVpnConfig().getPrefixWhitelists().stream()
.anyMatch(name::startsWith)) {
onResult.accept(new CheckResult(null, ResultType.WHITELISTED, false));
return;
return new CheckResult(null, ResultType.WHITELISTED, false);
}
CheckResult cachedResult = checkResultCache.getIfPresent(ip.getHostAddress());
@@ -94,8 +97,7 @@ public abstract class APIPlayer {
if(cachedResult.resultType().isShouldBlock()) {
AntiVPN.getInstance().getExecutor().handleKickingOfPlayer(cachedResult, this);
}
onResult.accept(cachedResult);
return;
return cachedResult;
}
}
@@ -105,7 +107,6 @@ public abstract class APIPlayer {
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");
onResult.accept(new CheckResult(null, ResultType.API_FAILURE, false));
return;
}
// If the countryList() size is zero, no need to check.
@@ -137,9 +138,8 @@ public abstract class APIPlayer {
if(checkResult.resultType().isShouldBlock()) {
AntiVPN.getInstance().getExecutor().handleKickingOfPlayer(checkResult, this);
}
onResult.accept(checkResult);
AntiVPN.getInstance().checked++;
});
onResult.accept(new CheckResult(null, ResultType.UNKNOWN, false));
return new CheckResult(null, ResultType.UNKNOWN, false);
}
}
@@ -16,6 +16,7 @@
package dev.brighten.antivpn.utils;
import java.io.Serial;
import java.io.Serializable;
import static dev.brighten.antivpn.utils.NullnessCasts.uncheckedCastNullableTToT;
@@ -36,7 +37,7 @@ public final class Suppliers {
/**
* Returns a supplier which caches the instance retrieved during the first call to {@code get()}
* and returns that value on subsequent calls to {@code get()}. See: <a
* and returns that value on later calls to {@code get()}. See: <a
* href="http://en.wikipedia.org/wiki/Memoization">memoization</a>
*
* <p>The returned supplier is thread-safe. The delegate's {@code get()} method will be invoked at
@@ -44,7 +45,7 @@ public final class Suppliers {
* form does not contain the cached value, which will be recalculated when {@code get()} is called
* on the reserialized instance.
*
* <p>When the underlying delegate throws an exception then this memoizing supplier will keep
* <p>When the underlying delegate throws an exception then this memorizing supplier will keep
* delegating calls until it returns valid data.
*
* <p>If {@code delegate} is an instance created by an earlier call to {@code memoize}, it is
@@ -95,6 +96,7 @@ public final class Suppliers {
+ ")";
}
@Serial
private static final long serialVersionUID = 0;
}
+15
View File
@@ -8,3 +8,18 @@ Just a simple plugin using an incredibly fast and accurate API.
## SpigotMC Page
Rate and support the project on SpigotMC: https://www.spigotmc.org/resources/kaurivpn-anti-proxy-tor-and-vpn-free-api.93355/
## Velocity Debugging
Run a generated local Velocity proxy with the AntiVPN Velocity loader installed:
```bash
./gradlew runVelocity
```
Run the proxy suspended for IDE debugger attach on port `5005`:
```bash
./gradlew debugVelocity
```
In IntelliJ IDEA, use the Gradle `debugVelocity` task and attach a remote JVM debugger to `localhost:5005`.
@@ -41,7 +41,8 @@ public class SpongeListener extends VPNExecutor {
event.connection().address().getAddress()
)));
player.get().checkPlayer(result -> {
CheckResult result = player.get().checkPlayer();
if(!result.resultType().isShouldBlock()) return;
if(!AntiVPN.getInstance().getVpnConfig().isKickPlayers()) {
@@ -56,7 +57,6 @@ public class SpongeListener extends VPNExecutor {
.getCountryVanillaKickReason(), player.get(), result.response());
default -> "You were kicked by KauriVPN for an unknown reason!";
}));
});
}
@Listener
+29
View File
@@ -1,5 +1,6 @@
plugins {
id 'com.gradleup.shadow'
id 'xyz.jpenilla.run-velocity'
}
evaluationDependsOn(':Velocity:VelocityPlugin')
@@ -25,16 +26,44 @@ shadowJar {
rename { 'antivpn-velocity.jarinjar' }
}
// Include the shared shaded source jar required by JarInJarClassLoader.
from(project(':Common:Source').tasks.named('shadowJar')) {
rename { 'antivpn-source.jarinjar' }
}
relocate 'org.bstats', 'dev.brighten.antivpn.velocity.org.bstats'
relocate 'org.yaml.snakeyaml', 'dev.brighten.antivpn.shaded.org.yaml.snakeyaml'
}
tasks.named('shadowJar') {
dependsOn(':Common:Source:shadowJar')
dependsOn(':Velocity:VelocityPlugin:shadowJar')
}
tasks.build.dependsOn shadowJar
tasks.named('runVelocity') {
velocityVersion('3.4.0-SNAPSHOT')
runDirectory.set(rootProject.layout.projectDirectory.dir('run/velocity'))
pluginJars.from(tasks.named('shadowJar'))
dependsOn(tasks.named('shadowJar'))
}
tasks.register('debugVelocity', xyz.jpenilla.runvelocity.task.RunVelocity) {
group = 'Run Velocity'
description = 'Run a local Velocity proxy for plugin debugging on port 5005.'
velocityVersion('3.4.0-SNAPSHOT')
runDirectory.set(rootProject.layout.projectDirectory.dir('run/velocity'))
pluginJars.from(tasks.named('shadowJar'))
dependsOn(tasks.named('shadowJar'))
debugOptions {
enabled = true
port = 5005
server = true
suspend = true
}
}
jar {
archiveClassifier.set('raw')
}
@@ -21,6 +21,7 @@ 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.utils.StringUtil;
@@ -52,7 +53,8 @@ public class VelocityListener extends VPNExecutor {
event.getPlayer().getRemoteAddress().getAddress()
));
player.checkPlayer(result -> {
CheckResult result = player.checkPlayer();
if(!result.resultType().isShouldBlock()) return;
if(!AntiVPN.getInstance().getVpnConfig().isKickPlayers()) {
@@ -80,7 +82,6 @@ public class VelocityListener extends VPNExecutor {
.replace("%code%", result.response().getCountryCode()))));
}
}
});
}
@Override
+13
View File
@@ -1,6 +1,7 @@
plugins {
id 'java'
id 'com.gradleup.shadow' version '9.4.1'
id 'xyz.jpenilla.run-velocity' version '3.0.2' apply false
}
def aggregateTestProjects = [
@@ -101,3 +102,15 @@ tasks.named('shadowJar') {
}
tasks.build.dependsOn shadowJar
tasks.register('runVelocity') {
group = 'run'
description = 'Starts a local Velocity proxy with the AntiVPN Velocity loader installed.'
dependsOn(':Velocity:VelocityLoader:runVelocity')
}
tasks.register('debugVelocity') {
group = 'run'
description = 'Starts a local Velocity proxy suspended for debugger attach on port 5005.'
dependsOn(':Velocity:VelocityLoader:debugVelocity')
}