mirror of
https://github.com/funkemunky/AntiVPN.git
synced 2026-05-31 09:31:54 +00:00
Correcting Kick Errors on Bukkit-based platforms (#84)
* Forcing BukkitPlayer#kickPlayer to always run within a main thread context using BukkitRunnable. Bumping version to 1.10.1 * Fixing async kick error, adding condition that allows players to be whitelisted even while they are offline (assuming this is not a cracked server). * Updating gradle piplines and files * correcting startup bug as a result of a packaging issue * Fixing asynchronous run command * Adds a regression test to ensure this doesnt happen again
This commit is contained in:
@@ -24,7 +24,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
gradle-version: '9.4.1'
|
gradle-version: '9.4.1'
|
||||||
- name: Build
|
- name: Build
|
||||||
run: gradle 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
|
||||||
@@ -32,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
|
|
||||||
|
|||||||
@@ -22,6 +22,12 @@ jobs:
|
|||||||
run: gradle build -x test --no-daemon
|
run: gradle build -x test --no-daemon
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
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:
|
build-and-test:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
env:
|
env:
|
||||||
|
|||||||
@@ -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/**
|
||||||
@@ -17,7 +17,6 @@ dependencies {
|
|||||||
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 {
|
test {
|
||||||
|
|||||||
@@ -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,9 +59,13 @@ public class BukkitListener extends VPNExecutor implements Listener {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void runCommand(String command) {
|
public void runCommand(String command) {
|
||||||
|
new BukkitRunnable() {
|
||||||
|
public void run() {
|
||||||
Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(),
|
Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(),
|
||||||
ChatColor.translateAlternateColorCodes('&', command));
|
ChatColor.translateAlternateColorCodes('&', command));
|
||||||
}
|
}
|
||||||
|
}.runTask(BukkitPlugin.pluginInstance.getPlugin());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void disablePlugin() {
|
public void disablePlugin() {
|
||||||
|
|||||||
@@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -24,7 +24,6 @@ test {
|
|||||||
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
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ 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.mockito:mockito-core:5.11.0'
|
||||||
@@ -28,13 +28,6 @@ dependencies {
|
|||||||
|
|
||||||
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*'
|
||||||
|
|||||||
@@ -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()) {
|
|
||||||
return "&cThe player \"" + args[1] + "\" is not online, so please provide a UUID.";
|
|
||||||
}
|
|
||||||
|
|
||||||
uuid = player.get().getUuid();
|
uuid = player.get().getUuid();
|
||||||
|
} 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.";
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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() {}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -29,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
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ test {
|
|||||||
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
|
||||||
|
|||||||
+1
-5
@@ -13,7 +13,7 @@ def aggregateTestProjects = [
|
|||||||
|
|
||||||
allprojects {
|
allprojects {
|
||||||
group = 'dev.brighten.antivpn'
|
group = 'dev.brighten.antivpn'
|
||||||
version = '1.10.0'
|
version = '1.10.1'
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
maven { url 'https://repo.papermc.io/repository/maven-public/' }
|
maven { url 'https://repo.papermc.io/repository/maven-public/' }
|
||||||
@@ -87,10 +87,6 @@ shadowJar {
|
|||||||
from(project(':Common:Source').tasks.shadowJar) {
|
from(project(':Common:Source').tasks.shadowJar) {
|
||||||
rename { 'antivpn-source.jarinjar' }
|
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') {
|
tasks.named('shadowJar') {
|
||||||
|
|||||||
Vendored
BIN
Binary file not shown.
+2
-1
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user