Compare commits

..

10 Commits

Author SHA1 Message Date
funkemunky 8d46d25db1 Adds a regression test to ensure this doesnt happen again 2026-04-28 21:28:16 -04:00
funkemunky 89b20df703 Fixing asynchronous run command 2026-04-28 21:23:47 -04:00
funkemunky aa0dc4001a correcting startup bug as a result of a packaging issue 2026-04-28 21:02:43 -04:00
funkemunky 2e079079d7 Updating gradle piplines and files 2026-04-24 20:53:23 -04:00
funkemunky 8185c9aad0 Fixing async kick error, adding condition that allows players to be whitelisted even while they are offline (assuming this is not a cracked server). 2026-04-17 10:51:09 -04:00
funkemunky 5f07b2393a Forcing BukkitPlayer#kickPlayer to always run within a main thread context using BukkitRunnable. Bumping version to 1.10.1 2026-04-17 09:47:46 -04:00
funkemunky 775d29114a Revert "Adding postgres"
This reverts commit ee07deb68f.
2026-04-17 09:43:56 -04:00
funkemunky ee07deb68f Adding postgres 2026-04-10 13:46:57 -04:00
Dawson 1acdfe8c4b Added automated testing (#82)
* Using built in pipeline

* Added testing for databases, and fixed some potential database bugs

* Fixing MySQL issues

* Adding central test suite

* Updating test workflow to build without tests and one with tests
2026-04-08 21:29:37 -04:00
Dawson 57109e4c97 Feature/gradle refactor (#80)
* Gradle project refactor

* Updating workflows for github

* Attempting to fix workflows

* Fixing maven central proxy

* Fixing missing dependency in velocity

* Fixing build

* Getting rid of universal submodule

* Updating workflows to handle new build structure, removing maven workflows

* Fixing permissions issues caused by gradle files being handled on Windows

* Revert "Fixing permissions issues caused by gradle files being handled on Windows"

This reverts commit 3e4d8be955.

* replacing instances of gradlew

* Adding gradlew wrapper

* Attempting to fix gradle issues

* Attempting to fix gradle issues

* Adding back ssl args

* Using built in pipeline
2026-04-08 15:27:06 -04:00
29 changed files with 660 additions and 180 deletions
+5 -3
View File
@@ -5,6 +5,8 @@ jobs:
build-and-release: build-and-release:
name: Build and Release name: Build and Release
runs-on: ubuntu-latest runs-on: ubuntu-latest
env:
JAVA_TOOL_OPTIONS: -Djavax.net.ssl.trustStoreType=JKS -Djavax.net.ssl.trustStore=/etc/ssl/certs/java/cacerts -Djavax.net.ssl.trustStorePassword=changeit
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v5
- name: Set up JDK 21 - name: Set up JDK 21
@@ -15,15 +17,15 @@ jobs:
- name: Set up Gradle - name: Set up Gradle
uses: gradle/actions/setup-gradle@v4 uses: gradle/actions/setup-gradle@v4
with: with:
gradle-version: 'wrapper' gradle-version: '9.4.1'
- name: Build - name: Build
run: ./gradlew build --no-daemon run: gradle build --no-daemon
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Get Version Number from Gradle - name: Get Version Number from Gradle
id: get_version id: get_version
run: | run: |
VERSION=$(./gradlew properties -q | awk -F': ' '/^version:/ {print $2; exit}') VERSION=$(gradle properties -q | awk -F': ' '/^version:/ {print $2; exit}')
echo "VERSION=$VERSION" >> "$GITHUB_ENV" echo "VERSION=$VERSION" >> "$GITHUB_ENV"
- name: Extract latest CHANGELOG entry - name: Extract latest CHANGELOG entry
id: changelog id: changelog
+4 -7
View File
@@ -9,6 +9,8 @@ jobs:
build: build:
name: Build and Test name: Build and Test
runs-on: ubuntu-latest runs-on: ubuntu-latest
env:
JAVA_TOOL_OPTIONS: -Djavax.net.ssl.trustStoreType=JKS -Djavax.net.ssl.trustStore=/etc/ssl/certs/java/cacerts -Djavax.net.ssl.trustStorePassword=changeit
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v5
@@ -20,9 +22,9 @@ jobs:
- name: Set up Gradle - name: Set up Gradle
uses: gradle/actions/setup-gradle@v4 uses: gradle/actions/setup-gradle@v4
with: with:
gradle-version: 'wrapper' gradle-version: '9.4.1'
- name: Build - name: Build
run: ./gradlew build --no-daemon run: gradle build -x test --no-daemon
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload AntiVPN - name: Upload AntiVPN
@@ -30,8 +32,3 @@ jobs:
with: with:
name: AntiVPN-Universal name: AntiVPN-Universal
path: build/libs/AntiVPN-*-universal.jar path: build/libs/AntiVPN-*-universal.jar
- name: Upload Sponge plugin
uses: actions/upload-artifact@v4
with:
name: AntiVPN-Sponge
path: Sponge/SpongeLoader/build/libs/*.jar
+30 -2
View File
@@ -4,6 +4,8 @@ on:
jobs: jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
env:
JAVA_TOOL_OPTIONS: -Djavax.net.ssl.trustStoreType=JKS -Djavax.net.ssl.trustStore=/etc/ssl/certs/java/cacerts -Djavax.net.ssl.trustStorePassword=changeit
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v5
@@ -15,8 +17,34 @@ jobs:
- name: Set up Gradle - name: Set up Gradle
uses: gradle/actions/setup-gradle@v4 uses: gradle/actions/setup-gradle@v4
with: with:
gradle-version: 'wrapper' gradle-version: '9.4.1'
- name: Build - name: Build
run: ./gradlew build --no-daemon run: gradle build -x test --no-daemon
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload AntiVPN
uses: actions/upload-artifact@v4
with:
name: AntiVPN-Universal
path: build/libs/AntiVPN-*-universal.jar
build-and-test:
runs-on: ubuntu-latest
env:
JAVA_TOOL_OPTIONS: -Djavax.net.ssl.trustStoreType=JKS -Djavax.net.ssl.trustStore=/etc/ssl/certs/java/cacerts -Djavax.net.ssl.trustStorePassword=changeit
steps:
- uses: actions/checkout@v5
- name: Set up JDK 21
uses: actions/setup-java@v5
with:
java-version: '21'
distribution: 'zulu'
- name: Set up Gradle
uses: gradle/actions/setup-gradle@v4
with:
gradle-version: '9.4.1'
- name: Build and Test
run: gradle build --no-daemon
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+1
View File
@@ -295,3 +295,4 @@ $RECYCLE.BIN/
# End of https://www.toptal.com/developers/gitignore/api/windows,macos,linux,maven,java,intellij,eclipse,netbeans # End of https://www.toptal.com/developers/gitignore/api/windows,macos,linux,maven,java,intellij,eclipse,netbeans
/.gradle/ /.gradle/
.grade/**
+1 -1
View File
@@ -1,5 +1,5 @@
plugins { plugins {
id 'com.github.johnrengelman.shadow' id 'com.gradleup.shadow'
} }
evaluationDependsOn(':Bukkit:Plugin') evaluationDependsOn(':Bukkit:Plugin')
+13 -4
View File
@@ -1,18 +1,27 @@
plugins { plugins {
id 'com.github.johnrengelman.shadow' id 'com.gradleup.shadow'
} }
dependencies { dependencies {
compileOnly 'org.spigotmc:spigot-api:1.20.2-R0.1-SNAPSHOT' compileOnly 'org.spigotmc:spigot-api:1.20.2-R0.1-SNAPSHOT'
compileOnly project(':Common:Source') implementation project(':Common:Source')
compileOnly project(':Common:loader-utils') implementation project(':Common:loader-utils')
implementation 'org.bstats:bstats-bukkit:2.2.1' implementation 'org.bstats:bstats-bukkit:2.2.1'
testImplementation 'com.github.seeseemelk:MockBukkit-v1.20:3.84.0'
testImplementation 'com.github.ben-manes.caffeine:caffeine:3.1.8'
testImplementation 'org.mockito:mockito-core:5.11.0'
testImplementation 'org.mockito:mockito-subclass:5.11.0'
testImplementation 'org.mockito:mockito-junit-jupiter:5.11.0'
} }
shadowJar { shadowJar {
archiveClassifier.set('') archiveClassifier.set('')
relocate 'org.bstats', 'dev.brighten.antivpn.bukkit.org.bstats' relocate 'org.bstats', 'dev.brighten.antivpn.bukkit.org.bstats'
relocate 'org.yaml.snakeyaml', 'dev.brighten.antivpn.shaded.org.yaml.snakeyaml' }
test {
useJUnitPlatform()
systemProperty 'mockito.mockmaker', 'subclass'
} }
tasks.build.dependsOn shadowJar tasks.build.dependsOn shadowJar
@@ -30,6 +30,7 @@ import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerLoginEvent; import org.bukkit.event.player.PlayerLoginEvent;
import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.scheduler.BukkitRunnable;
import java.util.logging.Level; import java.util.logging.Level;
@@ -58,8 +59,12 @@ public class BukkitListener extends VPNExecutor implements Listener {
@Override @Override
public void runCommand(String command) { public void runCommand(String command) {
Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(), new BukkitRunnable() {
ChatColor.translateAlternateColorCodes('&', command)); public void run() {
Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(),
ChatColor.translateAlternateColorCodes('&', command));
}
}.runTask(BukkitPlugin.pluginInstance.getPlugin());
} }
@Override @Override
@@ -17,7 +17,6 @@
package dev.brighten.antivpn.bukkit; package dev.brighten.antivpn.bukkit;
import dev.brighten.antivpn.api.APIPlayer; import dev.brighten.antivpn.api.APIPlayer;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor; import org.bukkit.ChatColor;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitRunnable; import org.bukkit.scheduler.BukkitRunnable;
@@ -26,7 +25,7 @@ public class BukkitPlayer extends APIPlayer {
private final Player player; private final Player player;
public BukkitPlayer(Player player) { public BukkitPlayer(Player player) {
super(player.getUniqueId(), player.getName(), player.getAddress().getAddress()); super(player.getUniqueId(), player.getName(), player.getAddress() != null ? player.getAddress().getAddress() : null);
this.player = player; this.player = player;
} }
@@ -38,13 +37,11 @@ public class BukkitPlayer extends APIPlayer {
@Override @Override
public void kickPlayer(String reason) { public void kickPlayer(String reason) {
if(!Bukkit.isPrimaryThread()) { new BukkitRunnable() {
new BukkitRunnable() { public void run() {
public void run() { player.kickPlayer(ChatColor.translateAlternateColorCodes('&', reason));
player.kickPlayer(ChatColor.translateAlternateColorCodes('&', reason)); }
} }.runTask(BukkitPlugin.pluginInstance.getPlugin());
}.runTask(BukkitPlugin.pluginInstance.getPlugin());
} else player.kickPlayer(ChatColor.translateAlternateColorCodes('&', reason));
} }
@Override @Override
@@ -11,6 +11,10 @@ import dev.brighten.antivpn.message.MessageHandler;
import dev.brighten.antivpn.message.VpnString; import dev.brighten.antivpn.message.VpnString;
import dev.brighten.antivpn.web.objects.VPNResponse; import dev.brighten.antivpn.web.objects.VPNResponse;
import org.bukkit.event.player.PlayerLoginEvent; import org.bukkit.event.player.PlayerLoginEvent;
import org.bukkit.command.CommandSender;
import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@@ -20,8 +24,16 @@ import java.net.InetAddress;
import java.util.Optional; import java.util.Optional;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.*;
public class BukkitListenerTest { public class BukkitListenerTest {
@@ -32,7 +44,12 @@ public class BukkitListenerTest {
@BeforeEach @BeforeEach
public void setUp() throws Exception { public void setUp() throws Exception {
server = MockBukkit.mock(); server = MockBukkit.mock(new RecordingServerMock());
JavaPlugin plugin = MockBukkit.loadWith(
TestPlugin.class,
new PluginDescriptionFile("AntiVPNTest", "1.0.0", TestPlugin.class.getName())
);
BukkitPlugin.pluginInstance = new BukkitPlugin(plugin);
AntiVPN antiVPN = mock(AntiVPN.class); AntiVPN antiVPN = mock(AntiVPN.class);
VPNConfig config = mock(VPNConfig.class); VPNConfig config = mock(VPNConfig.class);
@@ -74,6 +91,7 @@ public class BukkitListenerTest {
Field instanceField = AntiVPN.class.getDeclaredField("INSTANCE"); Field instanceField = AntiVPN.class.getDeclaredField("INSTANCE");
instanceField.setAccessible(true); instanceField.setAccessible(true);
instanceField.set(null, null); instanceField.set(null, null);
BukkitPlugin.pluginInstance = null;
MockBukkit.unmock(); MockBukkit.unmock();
} }
@@ -108,4 +126,75 @@ public class BukkitListenerTest {
assertEquals(PlayerLoginEvent.Result.KICK_BANNED, event.getResult()); assertEquals(PlayerLoginEvent.Result.KICK_BANNED, event.getResult());
assertEquals("Blocked!", net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().serialize(event.kickMessage())); assertEquals("Blocked!", net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().serialize(event.kickMessage()));
} }
@Test
public void testLoginPipelineProxyPlayerIsKickedWithoutErrors() throws Exception {
PlayerMock player = server.addPlayer("PipelineProxyPlayer");
InetAddress address = InetAddress.getByName("1.1.1.1");
when(vpnExecutor.checkIp("1.1.1.1")).thenReturn(CompletableFuture.completedFuture(
VPNResponse.builder().success(true).proxy(true).ip("1.1.1.1")
.method("N/A").countryName("N/A").countryCode("N/A").city("N/A").build()
));
PlayerLoginEvent event = new PlayerLoginEvent(player, "localhost", address);
assertDoesNotThrow(() -> listener.onLogin(event));
assertEquals(PlayerLoginEvent.Result.KICK_BANNED, event.getResult());
assertEquals("Blocked!", net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().serialize(event.kickMessage()));
}
@Test
public void testRunCommandDispatchesOnPrimaryThreadWhenCalledAsynchronously() {
RecordingServerMock recordingServer = (RecordingServerMock) server;
ExecutorService executor = Executors.newSingleThreadExecutor();
try {
CompletableFuture<Void> asyncCall = CompletableFuture.runAsync(
() -> listener.runCommand("antivpn-test &aok"),
executor
);
assertDoesNotThrow(() -> asyncCall.get(5, TimeUnit.SECONDS));
assertFalse(recordingServer.commandDispatched(), "Command should be scheduled, not dispatched asynchronously");
server.getScheduler().performOneTick();
assertTrue(recordingServer.commandDispatched(), "Scheduled command should be dispatched on the next server tick");
assertTrue(recordingServer.dispatchedOnPrimaryThread(), "Command dispatch must happen on Bukkit's primary thread");
assertEquals("antivpn-test §aok", recordingServer.dispatchedCommand());
} finally {
executor.shutdownNow();
}
}
public static class TestPlugin extends JavaPlugin {
}
private static class RecordingServerMock extends ServerMock {
private final AtomicBoolean commandDispatched = new AtomicBoolean();
private final AtomicBoolean dispatchedOnPrimaryThread = new AtomicBoolean();
private final AtomicReference<String> dispatchedCommand = new AtomicReference<>();
@Override
public boolean dispatchCommand(@NotNull CommandSender sender, @NotNull String commandLine) {
commandDispatched.set(true);
dispatchedOnPrimaryThread.set(isPrimaryThread());
dispatchedCommand.set(commandLine);
return super.dispatchCommand(sender, commandLine);
}
private boolean commandDispatched() {
return commandDispatched.get();
}
private boolean dispatchedOnPrimaryThread() {
return dispatchedOnPrimaryThread.get();
}
private String dispatchedCommand() {
return dispatchedCommand.get();
}
}
} }
@@ -0,0 +1,56 @@
package dev.brighten.antivpn.bukkit;
import be.seeseemelk.mockbukkit.MockBukkit;
import be.seeseemelk.mockbukkit.ServerMock;
import be.seeseemelk.mockbukkit.entity.PlayerMock;
import org.bukkit.plugin.java.JavaPlugin;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
class BukkitPlayerTest {
private ServerMock server;
@BeforeEach
void setUp() {
server = MockBukkit.mock();
BukkitPlugin pluginBootstrap = mock(BukkitPlugin.class);
JavaPlugin plugin = MockBukkit.createMockPlugin();
when(pluginBootstrap.getPlugin()).thenReturn(plugin);
BukkitPlugin.pluginInstance = pluginBootstrap;
}
@AfterEach
void tearDown() {
BukkitPlugin.pluginInstance = null;
MockBukkit.unmock();
}
@Test
void kickPlayerCalledFromAsyncContext_isScheduledAndExecutedOnMainThread() {
PlayerMock player = server.addPlayer("AsyncKickPlayer");
BukkitPlayer bukkitPlayer = new BukkitPlayer(player);
assertTrue(player.isOnline());
CompletableFuture<Void> asyncKick = CompletableFuture.runAsync(() -> bukkitPlayer.kickPlayer("&cBlocked!"));
assertDoesNotThrow(() -> asyncKick.get(1, TimeUnit.SECONDS));
assertTrue(player.isOnline(), "Kick should be deferred to the server scheduler");
server.getScheduler().performTicks(1);
assertFalse(player.isOnline(), "Player should be kicked when scheduled task runs");
}
}
+1 -1
View File
@@ -1,5 +1,5 @@
plugins { plugins {
id 'com.github.johnrengelman.shadow' id 'com.gradleup.shadow'
} }
evaluationDependsOn(':Bungee:BungeePlugin') evaluationDependsOn(':Bungee:BungeePlugin')
+15 -4
View File
@@ -1,18 +1,29 @@
plugins { plugins {
id 'com.github.johnrengelman.shadow' id 'com.gradleup.shadow'
} }
dependencies { dependencies {
compileOnly 'net.md-5:bungeecord-api:1.21-R0.2' compileOnly 'net.md-5:bungeecord-api:1.21-R0.2'
compileOnly project(':Common:Source') testImplementation 'net.md-5:bungeecord-api:1.21-R0.2'
compileOnly project(':Common:loader-utils') implementation project(':Common:Source')
implementation project(':Common:loader-utils')
implementation 'org.bstats:bstats-bungeecord:2.2.1' implementation 'org.bstats:bstats-bungeecord:2.2.1'
testImplementation 'org.junit.jupiter:junit-jupiter:5.11.4'
testImplementation 'org.mockito:mockito-core:5.11.0'
testImplementation 'org.mockito:mockito-subclass:5.11.0'
testImplementation 'org.mockito:mockito-junit-jupiter:5.11.0'
testImplementation 'com.github.ben-manes.caffeine:caffeine:3.1.8'
}
tasks.compileJava.dependsOn(':Common:Source:jar')
test {
useJUnitPlatform()
systemProperty 'mockito.mockmaker', 'subclass'
} }
shadowJar { shadowJar {
archiveClassifier.set('') archiveClassifier.set('')
relocate 'org.bstats', 'dev.brighten.antivpn.bungee.org.bstats' relocate 'org.bstats', 'dev.brighten.antivpn.bungee.org.bstats'
relocate 'org.yaml.snakeyaml', 'dev.brighten.antivpn.shaded.org.yaml.snakeyaml'
} }
tasks.build.dependsOn shadowJar tasks.build.dependsOn shadowJar
+27 -10
View File
@@ -1,5 +1,5 @@
plugins { plugins {
id 'com.github.johnrengelman.shadow' id 'com.gradleup.shadow'
} }
dependencies { dependencies {
@@ -10,20 +10,25 @@ dependencies {
compileOnly 'com.mysql:mysql-connector-j:9.3.0' compileOnly 'com.mysql:mysql-connector-j:9.3.0'
compileOnly 'com.h2database:h2:2.2.220' compileOnly 'com.h2database:h2:2.2.220'
compileOnly 'com.github.ben-manes.caffeine:caffeine:3.1.8' implementation'com.github.ben-manes.caffeine:caffeine:3.1.8'
compileOnly 'org.mongodb:mongo-java-driver:3.12.14' compileOnly 'org.mongodb:mongo-java-driver:3.12.14'
testImplementation 'org.mockito:mockito-core:5.11.0'
testImplementation "org.junit.jupiter:junit-jupiter:5.8.1"
testImplementation "org.testcontainers:testcontainers:2.0.4"
testImplementation "org.testcontainers:testcontainers-junit-jupiter:2.0.4"
testImplementation 'org.testcontainers:mysql:1.20.4'
testImplementation 'org.testcontainers:mongodb:1.20.4'
testRuntimeOnly 'org.slf4j:slf4j-simple:2.0.16'
testImplementation 'com.mysql:mysql-connector-j:9.3.0'
testImplementation 'com.h2database:h2:2.2.220'
testImplementation 'org.mongodb:mongo-java-driver:3.12.14'
testImplementation 'com.github.ben-manes.caffeine:caffeine:3.1.8'
} }
shadowJar { shadowJar {
archiveClassifier.set('') archiveClassifier.set('')
relocate 'org.yaml.snakeyaml', 'dev.brighten.antivpn.shaded.org.yaml.snakeyaml'
relocate 'com.github.benmanes.caffeine', 'dev.brighten.antivpn.shaded.com.github.benmanes.caffeine'
relocate 'org.h2', 'dev.brighten.antivpn.shaded.org.h2'
relocate 'org.bson', 'dev.brighten.antivpn.shaded.org.bson'
relocate 'com.mongodb', 'dev.brighten.antivpn.shaded.com.mongodb'
relocate 'com.mysql.cj', 'dev.brighten.antivpn.shaded.com.mysql.cj'
relocate 'com.mysql.jdbc', 'dev.brighten.antivpn.shaded.com.mysql.jdbc'
dependencies { dependencies {
exclude 'dev/brighten/antivpn/depends/Relocate*' exclude 'dev/brighten/antivpn/depends/Relocate*'
exclude 'dev/brighten/antivpn/depends/MavenLibraries*' exclude 'dev/brighten/antivpn/depends/MavenLibraries*'
@@ -31,3 +36,15 @@ shadowJar {
} }
tasks.build.dependsOn shadowJar tasks.build.dependsOn shadowJar
components.java.withVariantsFromConfiguration(configurations.shadowRuntimeElements) {
skip()
}
test {
useJUnitPlatform()
}
jar {
archiveClassifier.set('raw')
}
@@ -27,7 +27,6 @@ import dev.brighten.antivpn.database.mongo.MongoVPN;
import dev.brighten.antivpn.database.sql.MySqlVPN; import dev.brighten.antivpn.database.sql.MySqlVPN;
import dev.brighten.antivpn.depends.LibraryLoader; import dev.brighten.antivpn.depends.LibraryLoader;
import dev.brighten.antivpn.depends.MavenLibrary; import dev.brighten.antivpn.depends.MavenLibrary;
import dev.brighten.antivpn.depends.Relocate;
import dev.brighten.antivpn.message.MessageHandler; import dev.brighten.antivpn.message.MessageHandler;
import dev.brighten.antivpn.utils.ConfigDefault; import dev.brighten.antivpn.utils.ConfigDefault;
import dev.brighten.antivpn.utils.MiscUtils; import dev.brighten.antivpn.utils.MiscUtils;
@@ -48,25 +47,13 @@ import java.util.List;
@Getter @Getter
@Setter(AccessLevel.PRIVATE) @Setter(AccessLevel.PRIVATE)
@MavenLibrary(groupId = "com.h2database", artifactId ="h2", version = "2.2.220", relocations = { @MavenLibrary(groupId = "com.h2database", artifactId ="h2", version = "2.2.220")
@Relocate(from ="org" + ".\\h2", to ="dev.brighten.antivpn.shaded.org.h2")}) @MavenLibrary(groupId = "org.mongodb", artifactId = "mongo-java-driver", version = "3.12.14")
@MavenLibrary(groupId = "org.mongodb", artifactId = "mongo-java-driver", version = "3.12.14", relocations = {
@Relocate(from = "com." + "\\mongodb", to = "dev.brighten.antivpn.shaded.com.mongodb"),
@Relocate(from = "org" + "\\.bson", to = "dev.brighten.antivpn.shaded.org.bson")
})
@MavenLibrary( @MavenLibrary(
groupId = "com.mysql", groupId = "com.mysql",
artifactId = "mysql-connector-j", artifactId = "mysql-connector-j",
version = "9.3.0", version = "9.3.0"
relocations = {
@Relocate(from = "com.my\\" + "sql.cj", to = "dev.brighten.antivpn.shaded.com.mysql.cj"),
@Relocate(from = "com.my\\" + "sql.jdbc", to = "dev.brighten.antivpn.shaded.com.mysql.jdbc"),
}
) )
@MavenLibrary(groupId = "com.\\github\\.ben-manes\\.caffeine", artifactId = "caffeine", version = "3.1.8",
relocations = {
@Relocate(from = "com\\.github\\.benmanes\\.caffeine", to = "dev.brighten.antivpn.shaded.com.github.benmanes.caffeine"),
})
public class AntiVPN { public class AntiVPN {
private static AntiVPN INSTANCE; private static AntiVPN INSTANCE;
@@ -80,7 +80,7 @@ public class AllowlistCommand extends Command {
page = Integer.parseInt(args[1]); page = Integer.parseInt(args[1]);
if (page < 1) page = 1; if (page < 1) page = 1;
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
page = 1; return "&cUsage: /antivpn allowlist show [page]";
} }
} }
@@ -242,12 +242,14 @@ public class AllowlistCommand extends Command {
uuid = UUID.fromString(args[1]); uuid = UUID.fromString(args[1]);
} catch(IllegalArgumentException e) { } catch(IllegalArgumentException e) {
Optional<APIPlayer> player = AntiVPN.getInstance().getPlayerExecutor().getPlayer(args[1]); Optional<APIPlayer> player = AntiVPN.getInstance().getPlayerExecutor().getPlayer(args[1]);
if (player.isPresent()) {
if(player.isEmpty()) { uuid = player.get().getUuid();
return "&cThe player \"" + args[1] + "\" is not online, so please provide a UUID."; } else {
uuid = MiscUtils.lookupUUID(args[1]);
if (uuid == null) {
return "&cCould not find a UUID for \"" + args[1] + "\". They might not have provided a valid username.";
}
} }
uuid = player.get().getUuid();
} }
if(!databaseEnabled) { if(!databaseEnabled) {
@@ -23,8 +23,7 @@ import dev.brighten.antivpn.utils.json.JsonReader;
import java.io.*; import java.io.*;
import java.math.BigInteger; import java.math.BigInteger;
import java.net.InetAddress; import java.net.*;
import java.net.UnknownHostException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
@@ -34,6 +33,10 @@ import java.util.regex.Pattern;
public class MiscUtils { public class MiscUtils {
private static final Pattern ipv4 = Pattern.compile("[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}"); private static final Pattern ipv4 = Pattern.compile("[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}");
private static final String DEFAULT_FUNKEMUNKY_UUID_ENDPOINT = "https://funkemunky.cc/mojang/uuid?name=";
private static final String DEFAULT_MOJANG_UUID_ENDPOINT = "https://api.mojang.com/users/profiles/minecraft/";
private static volatile String funkemunkyUuidEndpoint = DEFAULT_FUNKEMUNKY_UUID_ENDPOINT;
private static volatile String mojangUuidEndpoint = DEFAULT_MOJANG_UUID_ENDPOINT;
public static void close(Closeable... closeables) { public static void close(Closeable... closeables) {
try { try {
@@ -94,7 +97,7 @@ public class MiscUtils {
int prefixLen = 32 - blockBits; // use 128 for IPv6 int prefixLen = 32 - blockBits; // use 128 for IPv6
// Build the CIDR string // Build the CIDR string
byte[] addrBytes = toFixedLengthBytes(start, 4); // use 16 for IPv6 byte[] addrBytes = toFixedLengthBytes(start); // use 16 for IPv6
String cidr = InetAddress.getByAddress(addrBytes).getHostAddress() + "/" + prefixLen; String cidr = InetAddress.getByAddress(addrBytes).getHostAddress() + "/" + prefixLen;
cidrs.add(new CIDRUtils(cidr)); cidrs.add(new CIDRUtils(cidr));
@@ -105,24 +108,22 @@ public class MiscUtils {
return cidrs; return cidrs;
} }
private static byte[] toFixedLengthBytes(BigInteger value, int length) { private static byte[] toFixedLengthBytes(BigInteger value) {
byte[] raw = value.toByteArray(); byte[] raw = value.toByteArray();
byte[] result = new byte[length]; byte[] result = new byte[4];
int srcPos = Math.max(0, raw.length - length); int srcPos = Math.max(0, raw.length - 4);
int destPos = Math.max(0, length - raw.length); int destPos = Math.max(0, 4 - raw.length);
System.arraycopy(raw, srcPos, result, destPos, Math.min(raw.length, length)); System.arraycopy(raw, srcPos, result, destPos, Math.min(raw.length, 4));
return result; return result;
} }
public static UUID lookupUUID(String playername) { public static UUID lookupUUID(String playername) {
try { try {
JSONObject object = JsonReader UUID uuid = lookupUuidFromUrl(funkemunkyUuidEndpoint + playername);
.readJsonFromUrl("https://funkemunky.cc/mojang/uuid?name=" + playername); if (uuid != null) {
return uuid;
if(object.has("uuid")) {
return UUID.fromString(object.getString("uuid"));
} }
} catch (IOException | JSONException e) { } catch (IOException | JSONException | URISyntaxException e) {
AntiVPN.getInstance().getExecutor().logException("Error while looking up UUID for " + playername + "! Falling back to Mojang API", e); AntiVPN.getInstance().getExecutor().logException("Error while looking up UUID for " + playername + "! Falling back to Mojang API", e);
return lookupMojangUuid(playername); return lookupMojangUuid(playername);
} }
@@ -130,19 +131,63 @@ public class MiscUtils {
return null; return null;
} }
private static UUID lookupUuidFromUrl(String url) throws IOException, JSONException, URISyntaxException {
HttpURLConnection connection = (HttpURLConnection) new URI(url).toURL().openConnection();
connection.setConnectTimeout(5000);
connection.setReadTimeout(5000);
connection.setInstanceFollowRedirects(true);
int responseCode = connection.getResponseCode();
if (responseCode >= 500) {
throw new IOException("Server returned HTTP " + responseCode + " for " + url);
}
if (responseCode != HttpURLConnection.HTTP_OK) {
return null;
}
try (InputStream inputStream = connection.getInputStream()) {
JSONObject object = new JSONObject(JsonReader.readAll(new InputStreamReader(inputStream, java.nio.charset.StandardCharsets.UTF_8)));
if (object.has("uuid")) {
return parseUuid(object.getString("uuid"));
}
if (object.has("id")) {
return parseUuid(object.getString("id"));
}
}
return null;
}
private static UUID parseUuid(String value) {
if (value.length() == 32) {
value = value.replaceFirst(
"([0-9a-fA-F]{8})([0-9a-fA-F]{4})([0-9a-fA-F]{4})([0-9a-fA-F]{4})([0-9a-fA-F]{12})",
"$1-$2-$3-$4-$5"
);
}
return UUID.fromString(value);
}
private static UUID lookupMojangUuid(String playerName) { private static UUID lookupMojangUuid(String playerName) {
try { try {
JSONObject object = JsonReader.readJsonFromUrl("https://api.mojang.com/users/profiles/minecraft/" + playerName); return lookupUuidFromUrl(mojangUuidEndpoint + playerName);
} catch (IOException | JSONException | URISyntaxException e) {
if(object.has("id")) {
return UUID.fromString(object.getString("id"));
}
} catch (IOException | JSONException e) {
AntiVPN.getInstance().getExecutor().logException("Error while looking up UUID for " + playerName + " from Mojang!:", e); AntiVPN.getInstance().getExecutor().logException("Error while looking up UUID for " + playerName + " from Mojang!:", e);
} }
return null; return null;
} }
static void setLookupEndpointsForTesting(String funkemunkyEndpoint, String mojangEndpoint) {
funkemunkyUuidEndpoint = funkemunkyEndpoint;
mojangUuidEndpoint = mojangEndpoint;
}
static void resetLookupEndpointsForTesting() {
funkemunkyUuidEndpoint = DEFAULT_FUNKEMUNKY_UUID_ENDPOINT;
mojangUuidEndpoint = DEFAULT_MOJANG_UUID_ENDPOINT;
}
public static boolean isIpv4(String ip) public static boolean isIpv4(String ip)
{ {
return ipv4.matcher(ip).matches(); return ipv4.matcher(ip).matches();
@@ -0,0 +1,171 @@
package dev.brighten.antivpn.utils;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpServer;
import dev.brighten.antivpn.AntiVPN;
import dev.brighten.antivpn.api.APIPlayer;
import dev.brighten.antivpn.api.VPNConfig;
import dev.brighten.antivpn.api.VPNExecutor;
import dev.brighten.antivpn.command.CommandExecutor;
import dev.brighten.antivpn.command.impl.AllowlistCommand;
import dev.brighten.antivpn.database.VPNDatabase;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.charset.StandardCharsets;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
class AllowlistCommandTest {
private static final UUID FUNKEMUNKY_UUID = UUID.fromString("123e4567-e89b-12d3-a456-426614174000");
@Mock
private AntiVPN antiVPN;
@Mock
private VPNConfig vpnConfig;
@Mock
private VPNDatabase database;
@Mock
private dev.brighten.antivpn.api.PlayerExecutor playerExecutor;
@Mock
private CommandExecutor commandExecutor;
private TestVPNExecutor vpnExecutor;
private AutoCloseable mocks;
private AllowlistCommand command;
@BeforeEach
void setUp() throws Exception {
mocks = MockitoAnnotations.openMocks(this);
vpnExecutor = new TestVPNExecutor();
command = new AllowlistCommand();
when(antiVPN.getVpnConfig()).thenReturn(vpnConfig);
when(antiVPN.getExecutor()).thenReturn(vpnExecutor);
when(antiVPN.getDatabase()).thenReturn(database);
when(antiVPN.getPlayerExecutor()).thenReturn(playerExecutor);
when(vpnConfig.isDatabaseEnabled()).thenReturn(true);
setAntiVpnInstance(antiVPN);
}
@AfterEach
void tearDown() throws Exception {
MiscUtils.resetLookupEndpointsForTesting();
setAntiVpnInstance(null);
if (vpnExecutor != null) {
vpnExecutor.getThreadExecutor().shutdownNow();
}
if (mocks != null) {
mocks.close();
}
}
@Test
void onlinePlayerIsWhitelistedWithoutLookup() {
APIPlayer onlinePlayer = mock(APIPlayer.class);
when(onlinePlayer.getUuid()).thenReturn(FUNKEMUNKY_UUID);
when(playerExecutor.getPlayer("funkemunky")).thenReturn(Optional.of(onlinePlayer));
String result = command.execute(commandExecutor, new String[] {"add", "funkemunky"});
assertTrue(result.contains(FUNKEMUNKY_UUID.toString()));
verify(playerExecutor).getPlayer("funkemunky");
verify(database).addWhitelist(FUNKEMUNKY_UUID);
verify(database, never()).removeWhitelist(FUNKEMUNKY_UUID);
}
@Test
void offlinePlayerFallsBackToMojangLookupWhenPrimaryEndpointCannotBeReached() throws Exception {
when(playerExecutor.getPlayer("funkemunky")).thenReturn(Optional.empty());
int closedPort;
try (ServerSocket socket = new ServerSocket(0)) {
closedPort = socket.getLocalPort();
}
AtomicInteger mojangHits = new AtomicInteger();
HttpServer mojangServer = HttpServer.create(new InetSocketAddress("127.0.0.1", 0), 0);
mojangServer.createContext("/users/profiles/minecraft/funkemunky", exchange -> {
mojangHits.incrementAndGet();
respond(exchange);
});
mojangServer.start();
try {
MiscUtils.setLookupEndpointsForTesting(
"http://127.0.0.1:" + closedPort + "/mojang/uuid?name=",
"http://127.0.0.1:" + mojangServer.getAddress().getPort() + "/users/profiles/minecraft/"
);
String result = command.execute(commandExecutor, new String[] {"add", "funkemunky"});
assertTrue(result.contains(FUNKEMUNKY_UUID.toString()));
verify(playerExecutor).getPlayer("funkemunky");
assertEquals(1, mojangHits.get(), "Expected Mojang lookup to be used as the fallback");
verify(database).addWhitelist(FUNKEMUNKY_UUID);
} finally {
mojangServer.stop(0);
}
}
private static void respond(HttpExchange exchange) throws IOException {
byte[] bytes = "{\"id\":\"123e4567e89b12d3a456426614174000\"}".getBytes(StandardCharsets.UTF_8);
exchange.getResponseHeaders().add("Content-Type", "application/json");
exchange.sendResponseHeaders(200, bytes.length);
try (exchange; OutputStream outputStream = exchange.getResponseBody()) {
outputStream.write(bytes);
}
}
private static void setAntiVpnInstance(AntiVPN instance) throws Exception {
Field instanceField = AntiVPN.class.getDeclaredField("INSTANCE");
instanceField.setAccessible(true);
instanceField.set(null, instance);
}
protected static final class TestVPNExecutor extends VPNExecutor {
@Override
public void registerListeners() {}
@Override
public void log(Level level, String log, Object... objects) {}
@Override
public void log(String log, Object... objects) {}
@Override
public void logException(String message, Throwable ex) {}
@Override
public void runCommand(String command) {}
@Override
public void disablePlugin() {}
}
}
+1 -1
View File
@@ -1,5 +1,5 @@
plugins { plugins {
id 'com.github.johnrengelman.shadow' id 'com.gradleup.shadow'
} }
evaluationDependsOn(':Sponge:SpongePlugin') evaluationDependsOn(':Sponge:SpongePlugin')
+15 -9
View File
@@ -1,11 +1,25 @@
plugins { plugins {
id 'com.github.johnrengelman.shadow' id 'com.gradleup.shadow'
} }
dependencies { dependencies {
compileOnly 'org.spongepowered:spongeapi:11.0.0' compileOnly 'org.spongepowered:spongeapi:11.0.0'
testImplementation 'org.spongepowered:spongeapi:11.0.0'
compileOnly project(':Common:Source') compileOnly project(':Common:Source')
testImplementation project(':Common:Source')
compileOnly project(':Common:loader-utils') compileOnly project(':Common:loader-utils')
testImplementation project(':Common:loader-utils')
testImplementation 'org.junit.jupiter:junit-jupiter:5.11.4'
testImplementation 'org.mockito:mockito-core:5.11.0'
testImplementation 'org.mockito:mockito-subclass:5.11.0'
testImplementation 'org.mockito:mockito-junit-jupiter:5.11.0'
testImplementation 'com.github.ben-manes.caffeine:caffeine:3.1.8'
}
tasks.compileJava.dependsOn(':Common:Source:jar')
test {
useJUnitPlatform()
systemProperty 'mockito.mockmaker', 'subclass'
} }
shadowJar { shadowJar {
@@ -15,14 +29,6 @@ shadowJar {
exclude 'com/google/**' exclude 'com/google/**'
exclude 'org/objectweb/**' exclude 'org/objectweb/**'
exclude 'org/checkerframework/**' exclude 'org/checkerframework/**'
relocate 'org.yaml.snakeyaml', 'dev.brighten.antivpn.shaded.org.yaml.snakeyaml'
relocate 'com.github.benmanes.caffeine', 'dev.brighten.antivpn.shaded.com.github.benmanes.caffeine'
relocate 'org.h2', 'dev.brighten.antivpn.shaded.org.h2'
relocate 'org.bson', 'dev.brighten.antivpn.shaded.org.bson'
relocate 'com.mongodb', 'dev.brighten.antivpn.shaded.com.mongodb'
relocate 'com.mysql.cj', 'dev.brighten.antivpn.shaded.com.mysql.cj'
relocate 'com.mysql.jdbc', 'dev.brighten.antivpn.shaded.com.mysql.jdbc'
} }
tasks.build.dependsOn shadowJar tasks.build.dependsOn shadowJar
-36
View File
@@ -1,36 +0,0 @@
plugins {
id 'com.github.johnrengelman.shadow'
}
// Aggregate universal jar that embeds platform loaders and the common source jar
evaluationDependsOn(':Common:Source')
dependencies {
implementation project(':Bukkit:Loader')
implementation project(':Velocity:VelocityLoader')
implementation project(':Bungee:BungeeLoader')
}
shadowJar {
archiveClassifier.set('')
archiveBaseName.set("AntiVPN-${project.version}-universal")
// Include the shaded Common:Source jar as a single resource
from(project(':Common:Source').tasks.shadowJar) {
rename { 'antivpn-source.jarinjar' }
}
relocate 'org.yaml.snakeyaml', 'dev.brighten.antivpn.shaded.org.yaml.snakeyaml'
relocate 'org.bstats', 'dev.brighten.antivpn.shaded.org.bstats'
relocate 'org.objectweb', 'dev.brighten.antivpn.shaded.org.objectweb'
}
tasks.named('shadowJar') {
dependsOn(':Common:Source:shadowJar')
dependsOn(':Bukkit:Loader:shadowJar')
dependsOn(':Velocity:VelocityLoader:shadowJar')
dependsOn(':Bungee:BungeeLoader:shadowJar')
}
tasks.build.dependsOn shadowJar
+2 -1
View File
@@ -1,5 +1,5 @@
plugins { plugins {
id 'com.github.johnrengelman.shadow' id 'com.gradleup.shadow'
} }
evaluationDependsOn(':Velocity:VelocityPlugin') evaluationDependsOn(':Velocity:VelocityPlugin')
@@ -8,6 +8,7 @@ dependencies {
compileOnly 'com.velocitypowered:velocity-api:3.4.0-SNAPSHOT' compileOnly 'com.velocitypowered:velocity-api:3.4.0-SNAPSHOT'
compileOnly project(':Velocity:VelocityPlugin') compileOnly project(':Velocity:VelocityPlugin')
implementation project(':Common:loader-utils') implementation project(':Common:loader-utils')
implementation 'org.bstats:bstats-velocity:2.2.1'
} }
shadowJar { shadowJar {
+15 -2
View File
@@ -1,18 +1,31 @@
plugins { plugins {
id 'com.github.johnrengelman.shadow' id 'com.gradleup.shadow'
} }
dependencies { dependencies {
compileOnly 'com.velocitypowered:velocity-api:3.4.0-SNAPSHOT' compileOnly 'com.velocitypowered:velocity-api:3.4.0-SNAPSHOT'
testImplementation 'com.velocitypowered:velocity-api:3.4.0-SNAPSHOT'
compileOnly project(':Common:Source') compileOnly project(':Common:Source')
compileOnly project(':Common:loader-utils') compileOnly project(':Common:loader-utils')
implementation 'org.bstats:bstats-velocity:2.2.1' implementation 'org.bstats:bstats-velocity:2.2.1'
testImplementation 'org.mockito:mockito-core:5.11.0'
testImplementation 'org.mockito:mockito-junit-jupiter:5.11.0'
testImplementation 'net.java.dev.jna:jna:5.14.0'
testImplementation 'com.github.ben-manes.caffeine:caffeine:3.1.8'
testImplementation 'org.junit.jupiter:junit-jupiter:5.11.4'
testImplementation project(':Common:Source')
testImplementation project(':Common:loader-utils')
}
tasks.compileJava.dependsOn(':Common:Source:jar')
test {
useJUnitPlatform()
jvmArgs("-XX:+EnableDynamicAgentLoading")
} }
shadowJar { shadowJar {
archiveClassifier.set('') archiveClassifier.set('')
relocate 'org.bstats', 'dev.brighten.antivpn.velocity.org.bstats' relocate 'org.bstats', 'dev.brighten.antivpn.velocity.org.bstats'
relocate 'org.yaml.snakeyaml', 'dev.brighten.antivpn.shaded.org.yaml.snakeyaml'
} }
tasks.build.dependsOn shadowJar tasks.build.dependsOn shadowJar
+67 -5
View File
@@ -1,19 +1,29 @@
plugins { plugins {
id 'java' id 'java'
id 'com.github.johnrengelman.shadow' version '8.1.1' apply false id 'com.gradleup.shadow' version '9.4.1'
} }
def aggregateTestProjects = [
project(':Common:Source'),
project(':Bukkit:Plugin'),
project(':Bungee:BungeePlugin'),
project(':Sponge:SpongePlugin'),
project(':Velocity:VelocityPlugin')
]
allprojects { allprojects {
group = 'dev.brighten.antivpn' group = 'dev.brighten.antivpn'
version = '1.10.0' version = '1.10.1'
repositories { repositories {
mavenCentral() maven { url 'https://repo.papermc.io/repository/maven-public/' }
maven { url 'https://nexus.funkemunky.cc/repository/papermc-public/' }
maven { url 'https://nexus.funkemunky.cc/repository/maven-public/' }
maven { url 'https://nexus.funkemunky.cc/repository/maven-central/' }
maven { url 'https://nexus.funkemunky.cc/content/repositories/releases/' } maven { url 'https://nexus.funkemunky.cc/content/repositories/releases/' }
maven { url 'https://hub.spigotmc.org/nexus/content/repositories/snapshots/' } maven { url 'https://hub.spigotmc.org/nexus/content/repositories/snapshots/' }
maven { url 'https://oss.sonatype.org/content/repositories/snapshots' } maven { url 'https://oss.sonatype.org/content/repositories/snapshots' }
maven { url 'https://repo.spongepowered.org/repository/maven-public/' } maven { url 'https://repo.spongepowered.org/repository/maven-public/' }
maven { url 'https://repo.velocitypowered.com/releases/' }
maven { url 'https://jitpack.io' } maven { url 'https://jitpack.io' }
} }
@@ -21,7 +31,7 @@ allprojects {
java { java {
toolchain { toolchain {
languageVersion = JavaLanguageVersion.of(17) languageVersion = JavaLanguageVersion.of(21)
} }
} }
@@ -33,5 +43,57 @@ allprojects {
dependencies { dependencies {
compileOnly 'org.projectlombok:lombok:1.18.44' compileOnly 'org.projectlombok:lombok:1.18.44'
annotationProcessor 'org.projectlombok:lombok:1.18.44' annotationProcessor 'org.projectlombok:lombok:1.18.44'
testImplementation 'org.junit.jupiter:junit-jupiter:5.11.4'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
test {
useJUnitPlatform()
systemProperty 'mockito.mockmaker', 'subclass'
} }
} }
dependencies {
testImplementation 'org.junit.platform:junit-platform-suite:1.11.4'
}
sourceSets {
test {
compileClasspath += files(aggregateTestProjects.collect { it.sourceSets.test.output + it.sourceSets.test.compileClasspath })
runtimeClasspath += files(aggregateTestProjects.collect { it.sourceSets.test.output + it.sourceSets.test.runtimeClasspath })
}
}
tasks.named('test') {
dependsOn(aggregateTestProjects.collect { it.tasks.named('testClasses') })
jvmArgs("-XX:+EnableDynamicAgentLoading")
}
evaluationDependsOn(':Common:Source')
evaluationDependsOn(':Bukkit:Loader')
evaluationDependsOn(':Velocity:VelocityLoader')
evaluationDependsOn(':Bungee:BungeeLoader')
dependencies {
implementation project(':Bukkit:Loader')
implementation project(':Velocity:VelocityLoader')
implementation project(':Bungee:BungeeLoader')
}
shadowJar {
archiveFileName.set("AntiVPN-${project.version}-universal.jar")
// Include the shaded Common:Source jar as a single resource
from(project(':Common:Source').tasks.shadowJar) {
rename { 'antivpn-source.jarinjar' }
}
}
tasks.named('shadowJar') {
dependsOn(':Common:Source:shadowJar')
dependsOn(':Bukkit:Loader:shadowJar')
dependsOn(':Velocity:VelocityLoader:shadowJar')
dependsOn(':Bungee:BungeeLoader:shadowJar')
}
tasks.build.dependsOn shadowJar
-2
View File
@@ -1,2 +0,0 @@
systemProp.javax.net.ssl.trustStore=NONE
systemProp.javax.net.ssl.trustStoreType=Windows-ROOT
Binary file not shown.
+2 -1
View File
@@ -1,6 +1,7 @@
#Wed Apr 08 09:36:41 EDT 2026
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.0-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.0-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
Vendored Regular → Executable
+32 -18
View File
@@ -1,7 +1,7 @@
#!/bin/sh #!/bin/sh
# #
# Copyright © 2015-2021 the original authors. # Copyright © 2015 the original authors.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
@@ -15,6 +15,8 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
# #
# SPDX-License-Identifier: Apache-2.0
#
############################################################################## ##############################################################################
# #
@@ -55,7 +57,7 @@
# Darwin, MinGW, and NonStop. # Darwin, MinGW, and NonStop.
# #
# (3) This script is generated from the Groovy template # (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # https://github.com/gradle/gradle/blob/b631911858264c0b6e4d6603d677ff5218766cee/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project. # within the Gradle project.
# #
# You can find Gradle at https://github.com/gradle/gradle/. # You can find Gradle at https://github.com/gradle/gradle/.
@@ -80,13 +82,11 @@ do
esac esac
done done
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit # This is normally unused
# shellcheck disable=SC2034
APP_NAME="Gradle"
APP_BASE_NAME=${0##*/} APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value. # Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum MAX_FD=maximum
@@ -114,7 +114,6 @@ case "$( uname )" in #(
NONSTOP* ) nonstop=true ;; NONSTOP* ) nonstop=true ;;
esac esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM. # Determine the Java command to use to start the JVM.
@@ -133,22 +132,29 @@ location of your Java installation."
fi fi
else else
JAVACMD=java JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. if ! command -v java >/dev/null 2>&1
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the Please set the JAVA_HOME variable in your environment to match the
location of your Java installation." location of your Java installation."
fi
fi fi
# Increase the maximum file descriptors if we can. # Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #( case $MAX_FD in #(
max*) max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) || MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit" warn "Could not query maximum file descriptor limit"
esac esac
case $MAX_FD in #( case $MAX_FD in #(
'' | soft) :;; #( '' | soft) :;; #(
*) *)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" || ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD" warn "Could not set maximum file descriptor limit to $MAX_FD"
esac esac
@@ -165,7 +171,6 @@ fi
# For Cygwin or MSYS, switch paths to Windows format before running java # For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" ) JAVACMD=$( cygpath --unix "$JAVACMD" )
@@ -193,18 +198,27 @@ if "$cygwin" || "$msys" ; then
done done
fi fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
# shell script including quotes and variable substitutions, so put them in DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded. # Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \ set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \ "-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \ -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
org.gradle.wrapper.GradleWrapperMain \
"$@" "$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args. # Use "xargs" to parse quoted args.
# #
# With -n1 it outputs one arg per line, with the quotes and backslashes removed. # With -n1 it outputs one arg per line, with the quotes and backslashes removed.
Vendored
+22 -18
View File
@@ -13,8 +13,10 @@
@rem See the License for the specific language governing permissions and @rem See the License for the specific language governing permissions and
@rem limitations under the License. @rem limitations under the License.
@rem @rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%" == "" @echo off @if "%DEBUG%"=="" @echo off
@rem ########################################################################## @rem ##########################################################################
@rem @rem
@rem Gradle startup script for Windows @rem Gradle startup script for Windows
@@ -25,7 +27,8 @@
if "%OS%"=="Windows_NT" setlocal if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0 set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=. if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0 set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME% set APP_HOME=%DIRNAME%
@@ -40,13 +43,13 @@ if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1 %JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute if %ERRORLEVEL% equ 0 goto execute
echo. echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. echo location of your Java installation. 1>&2
goto fail goto fail
@@ -56,32 +59,33 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute if exist "%JAVA_EXE%" goto execute
echo. echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. echo location of your Java installation. 1>&2
goto fail goto fail
:execute :execute
@rem Setup the command line @rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle @rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
:end :end
@rem End local scope for the variables with windows NT shell @rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd if %ERRORLEVEL% equ 0 goto mainEnd
:fail :fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code! rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 set EXIT_CODE=%ERRORLEVEL%
exit /b 1 if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd :mainEnd
if "%OS%"=="Windows_NT" endlocal if "%OS%"=="Windows_NT" endlocal
+1 -1
View File
@@ -15,4 +15,4 @@ include 'Sponge:SpongeLoader'
include 'Velocity:VelocityPlugin' include 'Velocity:VelocityPlugin'
include 'Velocity:VelocityLoader' include 'Velocity:VelocityLoader'
include 'Universal'