mirror of
https://github.com/funkemunky/AntiVPN.git
synced 2026-05-31 17:31:55 +00:00
Compare commits
209 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
e1e7b375c8
|
|||
|
ef15d4750f
|
|||
|
d8b48e8c9b
|
|||
|
7b3174eaae
|
|||
| da5511fc33 | |||
| f28badf949 | |||
| 00124cddf2 | |||
| 103fdf74da | |||
| 4f43028ec0 | |||
| 866217ff08 | |||
| f271275bfa | |||
| 68a2acce00 | |||
| 7247341693 | |||
| 3f6bb4a0e6 | |||
| de31d837b9 | |||
| 6967246edb | |||
| 52efc7de3f | |||
| ea33a34b3d | |||
| 3a0419cbac | |||
| 24257e4f42 | |||
| 2b7f043eb9 | |||
| 2dbe465b9e | |||
| ac628811cc | |||
| 3aae8d8f49 | |||
| f0c37c6ff0 | |||
| 311f1e198b | |||
| 9cbeed1df3 | |||
| 6453898ca4 | |||
| 6243727ebf | |||
| 353b7dad78 | |||
| 0291aca052 | |||
| ae5893be89 | |||
| f9ed53bfec | |||
| cb32dfc370 | |||
| 4c7ff3d061 | |||
| aec0bb2738 | |||
| 3f5ab39877 | |||
| f2e59c0075 | |||
| a01b595953 | |||
| b2fcc4ff26 | |||
| 5363b7c469 | |||
| df48e3dfd4 | |||
| 0686c5fd3e | |||
| 5b6d214e6f | |||
| 2bdd7d2c34 | |||
| 31a9412c0a | |||
| 7f96c49ce8 | |||
| 7b3f9fc6ae | |||
| edd08b27ce | |||
| 158045217e | |||
| 63bdb0a4da | |||
| b9e23ba34e | |||
| 9f6b0f8b27 | |||
| fec1dcdef1 | |||
| c062e3d910 | |||
| a79fb1fe9a | |||
| d224efce3c | |||
| a2554c2bba | |||
| 70bfb4e83d | |||
| bc666447c5 | |||
| 26cfc3e3f8 | |||
| 7974b24271 | |||
| 87cdd57383 | |||
| 6fe928ca14 | |||
| dae9111a34 | |||
| f4d6fc2b4b | |||
| d461b5945b | |||
| c6303ec1b2 | |||
| ca8fb24134 | |||
| 5e37d2c371 | |||
| 48c6dd63ee | |||
| 50e7059597 | |||
| 464b02f416 | |||
| db1cdad4e1 | |||
| 9f66570088 | |||
| 5f0b2796b3 | |||
| be5eb4e953 | |||
| dde81b0495 | |||
| cbc00b79e2 | |||
| 3b2a463e58 | |||
| 96e48594d8 | |||
| c1ab71c7ed | |||
| 4bda24f10c | |||
| 259cff4402 | |||
| c54e90dca1 | |||
| 4f1e3848de | |||
| 1606ad192e | |||
| 40308869c0 | |||
| 6959f35d0c | |||
| 21b6924cce | |||
| 9c843cd061 | |||
| 91a09f6940 | |||
| 36b44200c4 | |||
| 903dd8e73e | |||
| e6bc601372 | |||
| 9dbf0e8635 | |||
| dd1b6afbb7 | |||
| 14e266b978 | |||
| cc289f41ff | |||
| bf5b81b750 | |||
| a480f13302 | |||
| 4a95b51350 | |||
| 9dc312186b | |||
| 3f9a2100a9 | |||
| 4c82755935 | |||
| 0048cf6b8c | |||
| 795c869fc0 | |||
| 95a00a4d0a | |||
| a6f26d4ba7 | |||
| 7a0786e29f | |||
| df4a14086b | |||
| f55fa88c2b | |||
| 4f79522010 | |||
| 7654cca651 | |||
| e01cbf95f2 | |||
| db49d400a0 | |||
| b39cc3e19c | |||
| ff25c75055 | |||
| 733e797a17 | |||
| 0c903794e5 | |||
| bddf26359d | |||
| 4424b2b9a5 | |||
| 60043dd07a | |||
| 314e554ce0 | |||
| 110e696995 | |||
| d12f1c983c | |||
| cf9de8115e | |||
| 338f64962e | |||
| 71604d5b45 | |||
| 5a69e49fb9 | |||
| fe6d5a3635 | |||
| b5caf9604d | |||
| 315d4eaa3f | |||
| f98ab77944 | |||
| 0fccd9e296 | |||
| 0db8b93a7c | |||
| ea979cd729 | |||
| ba72ad2a44 | |||
| 8edef241e4 | |||
| 2fbbe5b3c8 | |||
| 325e19dca5 | |||
| 8ad6c3aaa2 | |||
| f8765ff95f | |||
| 619b61fe55 | |||
| a6aac8fce7 | |||
| 2afb31b073 | |||
| 66d193148e | |||
| 58b48dceb4 | |||
| e03deb6ba6 | |||
| 6142ef603d | |||
| 2d82e0c433 | |||
| 3b629f4796 | |||
| 23481bd786 | |||
| 46156c4286 | |||
| 898e32972b | |||
| cd502b6f34 | |||
| 7ee04b74ea | |||
| 206d375bbd | |||
| cba3c2f2d9 | |||
| 5ec4cb98e3 | |||
| e44bd5843d | |||
| a9d356a04a | |||
| 5ba19b42f9 | |||
| 2082ad6d8e | |||
| 256f500dff | |||
| 9f05467553 | |||
| b23fed5392 | |||
| 28094f7d46 | |||
| 723aa6a127 | |||
| 7a229b23ff | |||
| 795f0333c7 | |||
| b573fca58b | |||
| c1ef2eef56 | |||
| a3cb7e8e8a | |||
| 2bf16ad6b7 | |||
| e37b4c3e6f | |||
| 334c894fe2 | |||
| 3b15a4c919 | |||
| b1cf629945 | |||
| 5907a9496b | |||
| 7cb4bae972 | |||
| f32beb4dc9 | |||
| feb2049d99 | |||
| c9655782ee | |||
| 729381a4e5 | |||
| 9786a93ca8 | |||
| fbba41fd71 | |||
| 2cd7951bca | |||
| 1ce3f28398 | |||
| e5107bd2c3 | |||
| ccdc260b68 | |||
| 7533b32039 | |||
| 73bddca5c3 | |||
| 68b6335ad5 | |||
| 665b313828 | |||
| 88dfcc3349 | |||
| d04c33c676 | |||
| 1f6043c20d | |||
| a60c1b2360 | |||
| 5fa7387927 | |||
| a2dc04dc51 | |||
| c21098a511 | |||
| c4336b2760 | |||
| 0a7c2c0207 | |||
| 09482b970b | |||
| 11c0c177fe | |||
| 1c636c3b6f | |||
| 15a5d3ba4f | |||
| 123221cd58 |
@@ -0,0 +1,34 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a bug report that will allow us to fix any unexpected behavior
|
||||
title: "[BUG]"
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**What instance are you running KauriVPN on?**
|
||||
*Put an 'x' in the brackets to check it*
|
||||
- [ ] Velocity
|
||||
- [ ] Bukkit/Spigot
|
||||
- [ ] Bungeecord
|
||||
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
@@ -0,0 +1,20 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea you would like added
|
||||
title: "[FEATURE] "
|
||||
labels: enhancement
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
@@ -0,0 +1,93 @@
|
||||
name: create-release.yml
|
||||
on:
|
||||
workflow_dispatch:
|
||||
jobs:
|
||||
build:
|
||||
name: Build and Test
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Cache local Maven repository
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.m2/repository
|
||||
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-maven-
|
||||
- uses: actions/checkout@v5
|
||||
- name: Set up JDK 21
|
||||
uses: actions/setup-java@v5
|
||||
with:
|
||||
java-version: '21'
|
||||
distribution: 'zulu'
|
||||
- name: Set up Maven
|
||||
uses: stCarolas/setup-maven@v5
|
||||
with:
|
||||
maven-version: 3.9.6
|
||||
- name: Compile
|
||||
run: mvn -B package --file pom.xml
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Upload AntiVPN
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: AntiVPN-Universal
|
||||
path: Universal/target/AntiVPN-*.jar
|
||||
- name: Upload Sponge plugin
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: AntiVPN-Sponge
|
||||
path: Sponge/target/Sponge-*.jar
|
||||
release:
|
||||
name: Create Release
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- name: Download AntiVPN
|
||||
uses: actions/download-artifact@v5
|
||||
with:
|
||||
name: AntiVPN-Universal
|
||||
- name: Download Sponge plugin
|
||||
uses: actions/download-artifact@v5
|
||||
with:
|
||||
name: AntiVPN-Sponge
|
||||
- name: Get Version Number from Pom
|
||||
id: get_version
|
||||
run: echo "VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)" >> $GITHUB_ENV
|
||||
- name: Extract latest CHANGELOG entry
|
||||
id: changelog
|
||||
run: |
|
||||
CHANGELOG_CONTENT=$(awk 'BEGIN {print_section=0;} /^## \[/ {if (print_section == 0) {print_section=1;} else {exit;}} print_section {print;}' CHANGELOG.md)
|
||||
CHANGELOG_ESCAPED=$(echo "$CHANGELOG_CONTENT" | sed ':a;N;$!ba;s/\n/%0A/g')
|
||||
echo "Extracted latest release notes from CHANGELOG.md:"
|
||||
echo -e "$CHANGELOG_CONTENT"
|
||||
echo "::set-output name=content::$CHANGELOG_ESCAPED"
|
||||
- name: Create Release
|
||||
uses: actions/create-release@v1
|
||||
id: create_release
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: v${{ github.run_number }}
|
||||
release_name: Release v${{ github.run_number }}
|
||||
draft: false
|
||||
prerelease: false
|
||||
body: ${{ steps.changelog.outputs.content }}
|
||||
- uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./AntiVPN-*.jar
|
||||
asset_name: AntiVPN-Universal-v${{ env.VERSION }}.jar
|
||||
asset_content_type: application/java-archive
|
||||
- uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./Sponge-*.jar
|
||||
asset_name: AntiVPN-Sponge-v${{ env.VERSION }}.jar
|
||||
asset_content_type: application/java-archive
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
pull_request:
|
||||
branches: [master]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build and Test
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Cache local Maven repository
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.m2/repository
|
||||
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-maven-
|
||||
- uses: actions/checkout@v5
|
||||
- name: Set up JDK 21
|
||||
uses: actions/setup-java@v5
|
||||
with:
|
||||
java-version: '21'
|
||||
distribution: 'zulu'
|
||||
- name: Set up Maven
|
||||
uses: stCarolas/setup-maven@v5
|
||||
with:
|
||||
maven-version: 3.9.6
|
||||
- name: Compile
|
||||
run: mvn -B package --file pom.xml
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Upload AntiVPN
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: AntiVPN-Universal
|
||||
path: Universal/target/AntiVPN-*.jar
|
||||
- name: Upload Sponge plugin
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: AntiVPN-Sponge
|
||||
path: Sponge/target/Sponge-*.jar
|
||||
@@ -13,6 +13,9 @@ local.properties
|
||||
.settings/
|
||||
.loadpath
|
||||
.recommenders
|
||||
*.iml
|
||||
|
||||
.idea/
|
||||
|
||||
# External tool builders
|
||||
.externalToolBuilders/
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<parent>
|
||||
<artifactId>AntiVPN</artifactId>
|
||||
<groupId>dev.brighten.antivpn</groupId>
|
||||
<version>1.1.1</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>Assembly</artifactId>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.1.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<minimizeJar>false</minimizeJar>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>cc.funkemunky.utils</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<properties>
|
||||
<maven.compiler.source>8</maven.compiler.source>
|
||||
<maven.compiler.target>8</maven.compiler.target>
|
||||
</properties>
|
||||
</project>
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>AntiVPN</artifactId>
|
||||
<groupId>dev.brighten.antivpn</groupId>
|
||||
<version>1.1.1</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>Assembly</artifactId>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>8</maven.compiler.source>
|
||||
<maven.compiler.target>8</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.1.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<minimizeJar>false</minimizeJar>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>dev.brighten.antivpn</groupId>
|
||||
<artifactId>Bungee</artifactId>
|
||||
<version>${version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>dev.brighten.antivpn</groupId>
|
||||
<artifactId>Common</artifactId>
|
||||
<version>${version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>dev.brighten.antivpn</groupId>
|
||||
<artifactId>Bukkit</artifactId>
|
||||
<version>${version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -1,29 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<parent>
|
||||
<artifactId>AntiVPN</artifactId>
|
||||
<groupId>dev.brighten.antivpn</groupId>
|
||||
<version>1.1.1</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>Bukkit</artifactId>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.github.spigot</groupId>
|
||||
<artifactId>1.13.2</artifactId>
|
||||
<version>1.13.2</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cc.funkemunky.utils</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<properties>
|
||||
<maven.compiler.source>8</maven.compiler.source>
|
||||
<maven.compiler.target>8</maven.compiler.target>
|
||||
</properties>
|
||||
</project>
|
||||
|
||||
+50
-10
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>AntiVPN</artifactId>
|
||||
<groupId>dev.brighten.antivpn</groupId>
|
||||
<version>1.1.1</version>
|
||||
<version>1.9.4</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@@ -16,13 +16,40 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.7.0</version>
|
||||
<version>3.13.0</version>
|
||||
<configuration>
|
||||
<source>8</source>
|
||||
<target>8</target>
|
||||
<source>17</source>
|
||||
<target>17</target>
|
||||
<compilerArgument>-XDignore.symbol.file</compilerArgument>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.6.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<minimizeJar>true</minimizeJar>
|
||||
<relocations>
|
||||
<relocation>
|
||||
<pattern>org.bstats</pattern>
|
||||
<!-- Replace this with your package! -->
|
||||
<shadedPattern>dev.brighten.antivpn.bukkit.org.bstats</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>org.yaml.snakeyaml</pattern>
|
||||
<shadedPattern>dev.brighten.antivpn.shaded.org.yaml.snakeyaml</shadedPattern>
|
||||
</relocation>
|
||||
</relocations>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
<resources>
|
||||
<resource>
|
||||
@@ -33,23 +60,36 @@
|
||||
</build>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>8</maven.compiler.source>
|
||||
<maven.compiler.target>8</maven.compiler.target>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spigot-repo</id>
|
||||
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.github.spigot</groupId>
|
||||
<artifactId>1.13.2</artifactId>
|
||||
<version>1.13.2</version>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
<artifactId>spigot-api</artifactId>
|
||||
<version>1.20.2-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>dev.brighten.antivpn</groupId>
|
||||
<artifactId>Common</artifactId>
|
||||
<version>1.1.1</version>
|
||||
<version>1.9.4</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bstats</groupId>
|
||||
<artifactId>bstats-bukkit</artifactId>
|
||||
<version>2.2.1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -1,9 +1,14 @@
|
||||
package dev.brighten.antivpn.bukkit;
|
||||
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
import dev.brighten.antivpn.api.APIPlayer;
|
||||
import dev.brighten.antivpn.command.CommandExecutor;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class BukkitCommandExecutor implements CommandExecutor {
|
||||
@@ -11,12 +16,25 @@ public class BukkitCommandExecutor implements CommandExecutor {
|
||||
private final CommandSender sender;
|
||||
|
||||
@Override
|
||||
public void sendMessage(String message) {
|
||||
sender.sendMessage(ChatColor.translateAlternateColorCodes('&', message));
|
||||
public void sendMessage(String message, Object... objects) {
|
||||
sender.sendMessage(ChatColor.translateAlternateColorCodes('&',
|
||||
String.format(message, objects)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(String permission) {
|
||||
return sender.hasPermission(permission);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<APIPlayer> getPlayer() {
|
||||
if(!isPlayer()) return Optional.empty();
|
||||
|
||||
return AntiVPN.getInstance().getPlayerExecutor().getPlayer(((Player)sender).getUniqueId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPlayer() {
|
||||
return sender instanceof Player;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
package dev.brighten.antivpn.bukkit;
|
||||
|
||||
import dev.brighten.antivpn.api.VPNConfig;
|
||||
import dev.brighten.antivpn.bukkit.util.ConfigDefault;
|
||||
|
||||
public class BukkitConfig implements VPNConfig {
|
||||
private final ConfigDefault<String> licenseDefault = new ConfigDefault<>("",
|
||||
"license", BukkitPlugin.pluginInstance), kickStringDefault =
|
||||
new ConfigDefault<>("Proxies are not allowed on our server",
|
||||
"kickMessage", BukkitPlugin.pluginInstance);
|
||||
private final ConfigDefault<Boolean> cacheResultsDefault = new ConfigDefault<>(true,
|
||||
"cachedResults", BukkitPlugin.pluginInstance);
|
||||
|
||||
private String license, kickMessage;
|
||||
private boolean cacheResults;
|
||||
|
||||
@Override
|
||||
public String getLicense() {
|
||||
return license;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cachedResults() {
|
||||
return cacheResults;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKickString() {
|
||||
return kickMessage;
|
||||
}
|
||||
|
||||
public void update() {
|
||||
license = licenseDefault.get();
|
||||
kickMessage = kickStringDefault.get();
|
||||
cacheResults = cacheResultsDefault.get();
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,28 @@
|
||||
package dev.brighten.antivpn.bukkit;
|
||||
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
import dev.brighten.antivpn.api.APIPlayer;
|
||||
import dev.brighten.antivpn.api.CheckResult;
|
||||
import dev.brighten.antivpn.api.OfflinePlayer;
|
||||
import dev.brighten.antivpn.api.VPNExecutor;
|
||||
import dev.brighten.antivpn.message.VpnString;
|
||||
import dev.brighten.antivpn.utils.StringUtil;
|
||||
import dev.brighten.antivpn.utils.Tuple;
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.event.Event;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.AsyncPlayerPreLoginEvent;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
import org.bukkit.scheduler.BukkitTask;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.bukkit.event.player.PlayerLoginEvent;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.logging.Level;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public class BukkitListener extends VPNExecutor implements Listener {
|
||||
|
||||
private BukkitTask cacheResetTask;
|
||||
@Override
|
||||
public void registerListeners() {
|
||||
BukkitPlugin.pluginInstance.getServer().getPluginManager()
|
||||
@@ -25,45 +30,104 @@ public class BukkitListener extends VPNExecutor implements Listener {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runCacheReset() {
|
||||
cacheResetTask = new BukkitRunnable() {
|
||||
public void run() {
|
||||
resetCache();
|
||||
}
|
||||
}.runTaskTimerAsynchronously(BukkitPlugin.pluginInstance, 24000, 24000); //Reset cache every 20 minutes
|
||||
|
||||
HandlerList.unregisterAll(this);
|
||||
threadExecutor.shutdown();
|
||||
public void log(Level level, String log, Object... objects) {
|
||||
Bukkit.getLogger().log(level, String.format(log, objects));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
if(cacheResetTask != null && !cacheResetTask.isCancelled()) cacheResetTask.cancel();
|
||||
public void log(String log, Object... objects) {
|
||||
log(Level.INFO, String.format(log, objects));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logException(String message, Throwable ex) {
|
||||
Bukkit.getLogger().log(Level.SEVERE, message, ex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runCommand(String command) {
|
||||
Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(),
|
||||
ChatColor.translateAlternateColorCodes('&', command));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disablePlugin() {
|
||||
HandlerList.unregisterAll(this);
|
||||
BukkitPlugin.pluginInstance.getServer().getPluginManager().disablePlugin(BukkitPlugin.pluginInstance);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGH)
|
||||
public void onLogin(final PlayerLoginEvent event) {
|
||||
APIPlayer player = AntiVPN.getInstance().getPlayerExecutor().getPlayer(event.getPlayer().getUniqueId())
|
||||
.orElse(new OfflinePlayer(
|
||||
event.getPlayer().getUniqueId(),
|
||||
event.getPlayer().getName(),
|
||||
event.getAddress()
|
||||
));
|
||||
|
||||
CheckResult instantResult = player.checkPlayer(result -> {
|
||||
if(!result.resultType().isShouldBlock()) return;
|
||||
|
||||
AntiVPN.getInstance().getExecutor().log(Level.INFO, "Adding %s to kick", event.getPlayer().getName());
|
||||
AntiVPN.getInstance().getExecutor().getToKick().add(new Tuple<>(result, event.getPlayer().getUniqueId()));
|
||||
});
|
||||
|
||||
if(!instantResult.resultType().isShouldBlock()) return;
|
||||
|
||||
AntiVPN.getInstance().getExecutor().getToKick()
|
||||
.add(new Tuple<>(instantResult, event.getPlayer().getUniqueId()));
|
||||
|
||||
if(!AntiVPN.getInstance().getVpnConfig().kickPlayersOnDetect()) {
|
||||
return;
|
||||
}
|
||||
|
||||
AntiVPN.getInstance().getExecutor().log(Level.INFO, "%s was kicked from pre-login cache with IP %s", event.getPlayer().getName(), instantResult.response().getIp());
|
||||
|
||||
event.setResult(PlayerLoginEvent.Result.KICK_BANNED);
|
||||
switch (instantResult.resultType()) {
|
||||
case DENIED_COUNTRY -> event.setKickMessage(StringUtil.translateAlternateColorCodes('&',
|
||||
StringUtil.varReplace(
|
||||
AntiVPN.getInstance().getVpnConfig().countryVanillaKickReason(),
|
||||
player,
|
||||
instantResult.response()
|
||||
)));
|
||||
case DENIED_PROXY -> {
|
||||
if(AntiVPN.getInstance().getVpnConfig().alertToStaff()) {
|
||||
AntiVPN.getInstance().getPlayerExecutor().getOnlinePlayers().stream()
|
||||
.filter(APIPlayer::isAlertsEnabled)
|
||||
.forEach(pl ->
|
||||
pl.sendMessage(StringUtil.varReplace(
|
||||
ChatColor.translateAlternateColorCodes(
|
||||
'&',
|
||||
AntiVPN.getInstance().getVpnConfig().alertMessage()),
|
||||
player,
|
||||
instantResult.response())));
|
||||
}
|
||||
event.setKickMessage(StringUtil.translateAlternateColorCodes('&',
|
||||
StringUtil.varReplace(
|
||||
AntiVPN.getInstance().getVpnConfig().getKickString(),
|
||||
player,
|
||||
instantResult.response()
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
public void onJoin(final PlayerJoinEvent event) {
|
||||
AntiVPN.getInstance().getPlayerExecutor().getPlayer(event.getPlayer().getUniqueId())
|
||||
.ifPresent(player -> AntiVPN.getInstance().getDatabase().alertsState(player.getUuid(), enabled -> {
|
||||
if(enabled) {
|
||||
player.setAlertsEnabled(true);
|
||||
player.sendMessage(AntiVPN.getInstance().getMessageHandler()
|
||||
.getString("command-alerts-toggled")
|
||||
.getFormattedMessage(new VpnString.Var<>("state", true)));
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onListener(final AsyncPlayerPreLoginEvent event) {
|
||||
checkIp(event.getAddress().getHostAddress(), AntiVPN.getInstance().getConfig().cachedResults(), result -> {
|
||||
if(result.isSuccess() && result.isProxy()) {
|
||||
event.setLoginResult(AsyncPlayerPreLoginEvent.Result.KICK_OTHER);
|
||||
event.setKickMessage(ChatColor.translateAlternateColorCodes('&',
|
||||
AntiVPN.getInstance().getConfig().getKickString()));
|
||||
Optional.ofNullable(Bukkit.getPlayer(event.getUniqueId())).ifPresent(player -> {
|
||||
new BukkitRunnable() {
|
||||
public void run() {
|
||||
if(!player.hasPermission("antivpn.bypass"))
|
||||
player.kickPlayer(ChatColor.translateAlternateColorCodes('&',
|
||||
AntiVPN.getInstance().getConfig().getKickString()));
|
||||
System.out.println(player.getPlayer().getName()
|
||||
+ " joined on a VPN/Proxy (" + result.getMethod() + ")");
|
||||
}
|
||||
}.runTask(BukkitPlugin.pluginInstance);
|
||||
});
|
||||
} else if(!result.isSuccess()) {
|
||||
Bukkit.getLogger().log(Level.WARNING,
|
||||
"The API query was not a success! " +
|
||||
"You may need to upgrade your license on https://funkemunky.cc/shop");
|
||||
}
|
||||
});
|
||||
public void onQuit(PlayerQuitEvent event) {
|
||||
AntiVPN.getInstance().getPlayerExecutor().unloadPlayer(event.getPlayer().getUniqueId());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,12 +5,13 @@ import dev.brighten.antivpn.api.PlayerExecutor;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class BukkitPlayerExecutor implements PlayerExecutor {
|
||||
|
||||
private final Map<UUID, BukkitPlayer> cachedPlayers = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public Optional<APIPlayer> getPlayer(String name) {
|
||||
final Player player = Bukkit.getPlayer(name);
|
||||
@@ -19,7 +20,7 @@ public class BukkitPlayerExecutor implements PlayerExecutor {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
return Optional.of(new BukkitPlayer(player));
|
||||
return Optional.of(cachedPlayers.computeIfAbsent(player.getUniqueId(), k -> new BukkitPlayer(player)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -30,11 +31,20 @@ public class BukkitPlayerExecutor implements PlayerExecutor {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
return Optional.of(new BukkitPlayer(player));
|
||||
return Optional.of(cachedPlayers.computeIfAbsent(player.getUniqueId(), k -> new BukkitPlayer(player)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<APIPlayer> getOnlinePlayers() {
|
||||
return Bukkit.getOnlinePlayers().stream().map(BukkitPlayer::new).collect(Collectors.toList());
|
||||
public void unloadPlayer(UUID uuid) {
|
||||
cachedPlayers.remove(uuid);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public List<APIPlayer> getOnlinePlayers() {
|
||||
return Bukkit.getOnlinePlayers().stream()
|
||||
.map(pl -> cachedPlayers.computeIfAbsent(pl.getUniqueId(), k -> new BukkitPlayer(pl)))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,133 +1,131 @@
|
||||
package dev.brighten.antivpn.bukkit;
|
||||
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
import dev.brighten.antivpn.bukkit.command.BukkitCommand;
|
||||
import dev.brighten.antivpn.command.Command;
|
||||
import lombok.val;
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
import dev.brighten.antivpn.database.VPNDatabase;
|
||||
import dev.brighten.antivpn.database.local.H2VPN;
|
||||
import dev.brighten.antivpn.database.mongo.MongoVPN;
|
||||
import dev.brighten.antivpn.database.sql.MySqlVPN;
|
||||
import lombok.Getter;
|
||||
import org.bstats.bukkit.Metrics;
|
||||
import org.bstats.charts.SimplePie;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.SimpleCommandMap;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.plugin.SimplePluginManager;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
public class BukkitPlugin extends JavaPlugin {
|
||||
|
||||
public static BukkitPlugin pluginInstance;
|
||||
private SimpleCommandMap commandMap;
|
||||
private List<org.bukkit.command.Command> registeredCommands = new ArrayList<>();
|
||||
private final List<org.bukkit.command.Command> registeredCommands = new ArrayList<>();
|
||||
|
||||
@Getter
|
||||
private PlayerCommandRunner playerCommandRunner;
|
||||
|
||||
public void onEnable() {
|
||||
pluginInstance = this;
|
||||
|
||||
//Loading config
|
||||
System.out.println("Loading config...");
|
||||
saveDefaultConfig();
|
||||
Bukkit.getLogger().info("Starting AntiVPN services...");
|
||||
AntiVPN.start(new BukkitListener(), new BukkitPlayerExecutor(), getDataFolder());
|
||||
|
||||
System.out.println("Starting AntiVPN services...");
|
||||
AntiVPN.start(new BukkitConfig(), new BukkitListener(), new BukkitPlayerExecutor());
|
||||
playerCommandRunner = new PlayerCommandRunner();
|
||||
playerCommandRunner.start();
|
||||
|
||||
System.out.println("Setting up and registering commands...");
|
||||
if (pluginInstance.getServer().getPluginManager() instanceof SimplePluginManager) {
|
||||
SimplePluginManager manager = (SimplePluginManager) pluginInstance.getServer().getPluginManager();
|
||||
// Loading our bStats metrics to be pushed to https://bstats.org
|
||||
if(AntiVPN.getInstance().getVpnConfig().metrics()) {
|
||||
Bukkit.getLogger().info("Starting bStats metrics...");
|
||||
Metrics metrics = new Metrics(this, 12615);
|
||||
metrics.addCustomChart(new SimplePie("database_used", this::getDatabaseType));
|
||||
new BukkitRunnable() {
|
||||
public void run() {
|
||||
AntiVPN.getInstance().checked = AntiVPN.getInstance().detections = 0;
|
||||
}
|
||||
}.runTaskTimerAsynchronously(this, 12000, 12000);
|
||||
}
|
||||
|
||||
Bukkit.getLogger().info("Setting up and registering commands...");
|
||||
// We need access to the commandMap to register our commands without using the "proper" method
|
||||
if (pluginInstance.getServer().getPluginManager() instanceof SimplePluginManager manager) {
|
||||
try {
|
||||
Field field = SimplePluginManager.class.getDeclaredField("commandMap");
|
||||
field.setAccessible(true);
|
||||
commandMap = (SimpleCommandMap) field.get(manager);
|
||||
} catch (IllegalArgumentException | SecurityException | NoSuchFieldException | IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
AntiVPN.getInstance().getExecutor().logException(e);
|
||||
}
|
||||
}
|
||||
|
||||
// Registering commands
|
||||
for (Command command : AntiVPN.getInstance().getCommands()) {
|
||||
val newCommand = new org.bukkit.command.Command(command.name(), command.description(), command.usage(),
|
||||
Arrays.asList(command.aliases())) {
|
||||
@Override
|
||||
public List<String> tabComplete(CommandSender sender, String alias, String[] args)
|
||||
throws IllegalArgumentException {
|
||||
val children = command.children();
|
||||
|
||||
if(children.length > 0 && args.length > 0) {
|
||||
for (Command child : children) {
|
||||
if(child.name().equalsIgnoreCase(args[0])) {
|
||||
return child.tabComplete(new BukkitCommandExecutor(sender), alias, IntStream
|
||||
.range(0, args.length - 1)
|
||||
.mapToObj(i -> args[i + 1]).toArray(String[]::new));
|
||||
}
|
||||
}
|
||||
}
|
||||
return command.tabComplete(new BukkitCommandExecutor(sender), alias, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(CommandSender sender, String s, String[] args) {
|
||||
if(!sender.hasPermission("antivpn.command.*")
|
||||
&& !sender.hasPermission(command.permission())) {
|
||||
sender.sendMessage(ChatColor.RED + "No permission.");
|
||||
return true;
|
||||
}
|
||||
|
||||
val children = command.children();
|
||||
|
||||
if(children.length > 0 && args.length > 0) {
|
||||
for (Command child : children) {
|
||||
if(child.name().equalsIgnoreCase(args[0])) {
|
||||
if(!sender.hasPermission("antivpn.command.*")
|
||||
&& !sender.hasPermission(child.permission())) {
|
||||
sender.sendMessage(ChatColor.RED + "No permission.");
|
||||
return true;
|
||||
}
|
||||
|
||||
sender.sendMessage(ChatColor.translateAlternateColorCodes('&',
|
||||
child.execute(new BukkitCommandExecutor(sender), IntStream
|
||||
.range(0, args.length - 1)
|
||||
.mapToObj(i -> args[i + 1]).toArray(String[]::new))));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sender.sendMessage(ChatColor.translateAlternateColorCodes('&',
|
||||
command.execute(new BukkitCommandExecutor(sender), args)));
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
// Wraps our general command API to Bukkit specific calls
|
||||
BukkitCommand newCommand = new BukkitCommand(command);
|
||||
|
||||
// Adding to our own list for later referencing
|
||||
registeredCommands.add(newCommand);
|
||||
|
||||
// This tells Bukkit to register our command for use.
|
||||
commandMap.register(pluginInstance.getName(), newCommand);
|
||||
}
|
||||
|
||||
//TODO Finish system before implementing on startup
|
||||
/*Bukkit.getLogger().info("Getting strings...");
|
||||
AntiVPN.getInstance().getMessageHandler().initStrings(vpnString -> new ConfigDefault<>
|
||||
(vpnString.getDefaultMessage(), "messages." + vpnString.getKey(), BukkitPlugin.pluginInstance)
|
||||
.get());
|
||||
AntiVPN.getInstance().getMessageHandler().reloadStrings();*/
|
||||
|
||||
reloadConfig();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void onDisable() {
|
||||
System.out.println("Stopping plugin services...");
|
||||
Bukkit.getLogger().info("Stopping plugin services...");
|
||||
AntiVPN.getInstance().stop();
|
||||
playerCommandRunner.stop();
|
||||
|
||||
System.out.println("Unregistering commands...");
|
||||
Bukkit.getLogger().info("Unregistering commands...");
|
||||
try {
|
||||
Field field = SimpleCommandMap.class.getDeclaredField("knownCommands");
|
||||
field.setAccessible(true);
|
||||
Map<String, org.bukkit.command.Command> knownCommands =
|
||||
(Map<String, org.bukkit.command.Command>) field.get(commandMap);
|
||||
|
||||
knownCommands.values().removeAll(registeredCommands);
|
||||
registeredCommands.clear();
|
||||
if(field.get(commandMap) instanceof Map<?, ?> knownCommands) {
|
||||
Map<String, org.bukkit.command.Command> casted = (Map<String, org.bukkit.command.Command>) knownCommands;
|
||||
casted.values().removeAll(registeredCommands);
|
||||
registeredCommands.clear();
|
||||
}
|
||||
|
||||
} catch (IllegalAccessException | NoSuchFieldException e) {
|
||||
e.printStackTrace();
|
||||
AntiVPN.getInstance().getExecutor().logException(e);
|
||||
}
|
||||
|
||||
System.out.println("Unregistering listeners...");
|
||||
Bukkit.getLogger().info("Unregistering listeners...");
|
||||
HandlerList.unregisterAll(this);
|
||||
|
||||
System.out.println("Cancelling any running tasks...");
|
||||
Bukkit.getLogger().info("Cancelling any running tasks...");
|
||||
Bukkit.getScheduler().cancelTasks(this);
|
||||
}
|
||||
|
||||
private String getDatabaseType() {
|
||||
VPNDatabase database = AntiVPN.getInstance().getDatabase();
|
||||
|
||||
if(database instanceof H2VPN) {
|
||||
return "H2";
|
||||
} else if(database instanceof MySqlVPN) {
|
||||
return "MySQL";
|
||||
} else if(database instanceof MongoVPN) {
|
||||
return "MongoDB";
|
||||
} else {
|
||||
return "No-Database";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
package dev.brighten.antivpn.bukkit;
|
||||
|
||||
import dev.brighten.antivpn.utils.MiscUtils;
|
||||
import lombok.Data;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
|
||||
import java.util.Queue;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class PlayerCommandRunner {
|
||||
private final ScheduledExecutorService executorService;
|
||||
private final Queue<PlayerAction> playerActions = new ArrayBlockingQueue<>(10000);
|
||||
|
||||
public PlayerCommandRunner() {
|
||||
executorService = Executors.newSingleThreadScheduledExecutor(
|
||||
MiscUtils.createThreadFactory("AntiVPN:PlayerCommandRunner")
|
||||
);
|
||||
}
|
||||
|
||||
void start() {
|
||||
executorService.scheduleAtFixedRate(() -> {
|
||||
long currentTime = System.currentTimeMillis();
|
||||
while(!playerActions.isEmpty()) {
|
||||
PlayerAction action = playerActions.peek();
|
||||
|
||||
if(action == null) continue;
|
||||
|
||||
if(currentTime - action.start > 2000L || Bukkit.getPlayer(action.getUuid()) != null) {
|
||||
new BukkitRunnable() {
|
||||
public void run() {
|
||||
action.getAction().run();
|
||||
}
|
||||
}.runTask(BukkitPlugin.pluginInstance);
|
||||
|
||||
playerActions.poll();
|
||||
}
|
||||
}
|
||||
}, 1000, 100, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
void stop() {
|
||||
executorService.shutdown();
|
||||
playerActions.clear();
|
||||
}
|
||||
|
||||
void addAction(UUID uuid, Runnable action) {
|
||||
playerActions.add(new PlayerAction(uuid, System.currentTimeMillis(), action));
|
||||
}
|
||||
|
||||
@Data
|
||||
static class PlayerAction {
|
||||
private final UUID uuid;
|
||||
private final long start;
|
||||
private final Runnable action;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package dev.brighten.antivpn.bukkit.command;
|
||||
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
import dev.brighten.antivpn.bukkit.BukkitCommandExecutor;
|
||||
import dev.brighten.antivpn.command.Command;
|
||||
import lombok.val;
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
public class BukkitCommand extends org.bukkit.command.Command {
|
||||
|
||||
private final Command command;
|
||||
public BukkitCommand(Command command) {
|
||||
super(command.name(), command.description(), command.usage(), Arrays.asList(command.aliases()));
|
||||
|
||||
this.command = command;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> tabComplete(CommandSender sender, String alias, String[] args)
|
||||
throws IllegalArgumentException {
|
||||
val children = command.children();
|
||||
|
||||
if(children.length > 0 && args.length > 0) {
|
||||
for (Command child : children) {
|
||||
if(child.name().equalsIgnoreCase(args[0]) || Arrays.stream(child.aliases())
|
||||
.anyMatch(alias2 -> alias2.equalsIgnoreCase(args[0]))) {
|
||||
return child.tabComplete(new BukkitCommandExecutor(sender), alias, IntStream
|
||||
.range(0, args.length - 1)
|
||||
.mapToObj(i -> args[i + 1]).toArray(String[]::new));
|
||||
}
|
||||
}
|
||||
}
|
||||
return command.tabComplete(new BukkitCommandExecutor(sender), alias, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(CommandSender sender, String s, String[] args) {
|
||||
if(!sender.hasPermission("antivpn.command.*")
|
||||
&& !sender.hasPermission(command.permission())) {
|
||||
sender.sendMessage(ChatColor.translateAlternateColorCodes('&',
|
||||
AntiVPN.getInstance().getMessageHandler().getString("no-permission").getMessage()));
|
||||
return true;
|
||||
}
|
||||
|
||||
val children = command.children();
|
||||
|
||||
if(children.length > 0 && args.length > 0) {
|
||||
for (Command child : children) {
|
||||
if(child.name().equalsIgnoreCase(args[0]) || Arrays.stream(child.aliases())
|
||||
.anyMatch(alias -> alias.equalsIgnoreCase(args[0]))) {
|
||||
if(!sender.hasPermission("antivpn.command.*")
|
||||
&& !sender.hasPermission(child.permission())) {
|
||||
sender.sendMessage(ChatColor.translateAlternateColorCodes('&',
|
||||
AntiVPN.getInstance().getMessageHandler().getString("no-permission").getMessage()));
|
||||
return true;
|
||||
}
|
||||
|
||||
sender.sendMessage(ChatColor.translateAlternateColorCodes('&',
|
||||
child.execute(new BukkitCommandExecutor(sender), IntStream
|
||||
.range(0, args.length - 1)
|
||||
.mapToObj(i -> args[i + 1]).toArray(String[]::new))));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sender.sendMessage(ChatColor.translateAlternateColorCodes('&',
|
||||
command.execute(new BukkitCommandExecutor(sender), args)));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
name: AntiVPN
|
||||
name: KauriVPN
|
||||
main: dev.brighten.antivpn.bukkit.BukkitPlugin
|
||||
version: ${project.version}
|
||||
author: funkemunky
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<parent>
|
||||
<artifactId>AntiVPN</artifactId>
|
||||
<groupId>dev.brighten.antivpn</groupId>
|
||||
<version>1.1.1</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>Bungee</artifactId>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.github.bungee</groupId>
|
||||
<artifactId>BungeeCord-1.8</artifactId>
|
||||
<version>1.8</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cc.funkemunky.utils</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<properties>
|
||||
<maven.compiler.source>8</maven.compiler.source>
|
||||
<maven.compiler.target>8</maven.compiler.target>
|
||||
</properties>
|
||||
</project>
|
||||
|
||||
+43
-10
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>AntiVPN</artifactId>
|
||||
<groupId>dev.brighten.antivpn</groupId>
|
||||
<version>1.1.1</version>
|
||||
<version>1.9.4</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
@@ -16,13 +16,39 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.7.0</version>
|
||||
<version>3.13.0</version>
|
||||
<configuration>
|
||||
<source>8</source>
|
||||
<target>8</target>
|
||||
<source>17</source>
|
||||
<target>17</target>
|
||||
<compilerArgument>-XDignore.symbol.file</compilerArgument>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.6.0</version>
|
||||
<configuration>
|
||||
<relocations>
|
||||
<relocation>
|
||||
<pattern>org.bstats</pattern>
|
||||
<!-- Replace this with your package! -->
|
||||
<shadedPattern>dev.brighten.antivpn.bungee.org.bstats</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>org.yaml.snakeyaml</pattern>
|
||||
<shadedPattern>dev.brighten.antivpn.shaded.org.yaml.snakeyaml</shadedPattern>
|
||||
</relocation>
|
||||
</relocations>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
<resources>
|
||||
<resource>
|
||||
@@ -33,23 +59,30 @@
|
||||
</build>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>8</maven.compiler.source>
|
||||
<maven.compiler.target>8</maven.compiler.target>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>dev.brighten.antivpn</groupId>
|
||||
<artifactId>Common</artifactId>
|
||||
<version>1.1.1</version>
|
||||
<version>1.9.4</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.github.bungee</groupId>
|
||||
<artifactId>BungeeCord-1.8</artifactId>
|
||||
<version>1.8</version>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-api</artifactId>
|
||||
<version>1.21-R0.2</version>
|
||||
<type>jar</type>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bstats</groupId>
|
||||
<artifactId>bstats-bungeecord</artifactId>
|
||||
<version>2.2.1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -1,24 +0,0 @@
|
||||
package dev.brighten.antivpn.bungee;
|
||||
|
||||
import dev.brighten.antivpn.command.CommandExecutor;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
import net.md_5.bungee.api.CommandSender;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class BungeeCommandExecutor implements CommandExecutor {
|
||||
|
||||
private final CommandSender executor;
|
||||
|
||||
@Override
|
||||
public void sendMessage(String message) {
|
||||
executor.sendMessage(TextComponent.fromLegacyText(ChatColor
|
||||
.translateAlternateColorCodes('&', message)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(String permission) {
|
||||
return executor.hasPermission(permission);
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
package dev.brighten.antivpn.bungee;
|
||||
|
||||
import dev.brighten.antivpn.api.VPNConfig;
|
||||
import dev.brighten.antivpn.bungee.util.ConfigDefault;
|
||||
|
||||
public class BungeeConfig implements VPNConfig { ;
|
||||
private final ConfigDefault<String> licenseDefault = new ConfigDefault<>("",
|
||||
"license", BungeePlugin.pluginInstance), kickStringDefault =
|
||||
new ConfigDefault<>("Proxies are not allowed on our server",
|
||||
"kickMessage", BungeePlugin.pluginInstance);
|
||||
private final ConfigDefault<Boolean> cacheResultsDefault = new ConfigDefault<>(true,
|
||||
"cachedResults", BungeePlugin.pluginInstance);
|
||||
|
||||
private String license, kickMessage;
|
||||
private boolean cacheResults;
|
||||
|
||||
@Override
|
||||
public String getLicense() {
|
||||
return license;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cachedResults() {
|
||||
return cacheResults;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKickString() {
|
||||
return kickMessage;
|
||||
}
|
||||
|
||||
public void update() {
|
||||
license = licenseDefault.get();
|
||||
kickMessage = kickStringDefault.get();
|
||||
cacheResults = cacheResultsDefault.get();
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,21 @@
|
||||
package dev.brighten.antivpn.bungee;
|
||||
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
import dev.brighten.antivpn.api.VPNExecutor;
|
||||
import net.md_5.bungee.BungeeCord;
|
||||
import dev.brighten.antivpn.api.*;
|
||||
import dev.brighten.antivpn.utils.MiscUtils;
|
||||
import dev.brighten.antivpn.utils.StringUtil;
|
||||
import dev.brighten.antivpn.utils.Tuple;
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import net.md_5.bungee.api.event.PostLoginEvent;
|
||||
import net.md_5.bungee.api.event.TabCompleteEvent;
|
||||
import net.md_5.bungee.api.event.PlayerDisconnectEvent;
|
||||
import net.md_5.bungee.api.event.PreLoginEvent;
|
||||
import net.md_5.bungee.api.plugin.Listener;
|
||||
import net.md_5.bungee.api.scheduler.ScheduledTask;
|
||||
import net.md_5.bungee.event.EventHandler;
|
||||
import net.md_5.bungee.event.EventPriority;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class BungeeListener extends VPNExecutor implements Listener {
|
||||
@@ -25,38 +29,91 @@ public class BungeeListener extends VPNExecutor implements Listener {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runCacheReset() {
|
||||
cacheResetTask = BungeePlugin.pluginInstance.getProxy().getScheduler().schedule(BungeePlugin.pluginInstance,
|
||||
this::resetCache, 20, 20, TimeUnit.MINUTES);
|
||||
public void log(Level level, String log, Object... objects) {
|
||||
BungeePlugin.pluginInstance.getProxy().getLogger().log(Level.INFO, String.format(log, objects));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
if(cacheResetTask != null) {
|
||||
public void log(String log, Object... objects) {
|
||||
log(Level.INFO, String.format(log, objects));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logException(String message, Throwable ex) {
|
||||
BungeePlugin.pluginInstance.getProxy().getLogger().log(Level.SEVERE, message, ex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runCommand(String command) {
|
||||
BungeePlugin.pluginInstance.getProxy().getPluginManager()
|
||||
.dispatchCommand(BungeePlugin.pluginInstance.getProxy().getConsole(), command);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disablePlugin() {
|
||||
BungeePlugin.pluginInstance.getProxy().getPluginManager().unregisterListeners(BungeePlugin.pluginInstance);
|
||||
if (cacheResetTask != null) {
|
||||
cacheResetTask.cancel();
|
||||
cacheResetTask = null;
|
||||
}
|
||||
threadExecutor.shutdown();
|
||||
BungeePlugin.pluginInstance.getProxy().getPluginManager().unregisterListener(this);
|
||||
BungeePlugin.pluginInstance.getProxy().getPluginManager().unregisterCommands(BungeePlugin.pluginInstance);
|
||||
BungeePlugin.pluginInstance.onDisable();
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGH)
|
||||
public void onListener(final PreLoginEvent event) {
|
||||
|
||||
APIPlayer player = AntiVPN.getInstance().getPlayerExecutor().getPlayer(event.getConnection().getUniqueId())
|
||||
.orElseGet(() -> {
|
||||
UUID uuid = MiscUtils.lookupUUID(event.getConnection().getName());
|
||||
AntiVPN.getInstance().getExecutor().log(Level.INFO, "Getting offline player for %s with name %s",
|
||||
event.getConnection().getUniqueId(), uuid);
|
||||
|
||||
return new OfflinePlayer(uuid, event.getConnection().getName(),
|
||||
((InetSocketAddress) event.getConnection().getSocketAddress()).getAddress());
|
||||
});
|
||||
|
||||
CheckResult instantResult = player.checkPlayer(result -> {
|
||||
if (!result.resultType().isShouldBlock()) return;
|
||||
AntiVPN.getInstance().getExecutor().getToKick()
|
||||
.add(new Tuple<>(result, player.getUuid()));
|
||||
});
|
||||
|
||||
if (!instantResult.resultType().isShouldBlock()) {
|
||||
return;
|
||||
}
|
||||
|
||||
AntiVPN.getInstance().getExecutor().getToKick()
|
||||
.add(new Tuple<>(instantResult, player.getUuid()));
|
||||
|
||||
if (!AntiVPN.getInstance().getVpnConfig().kickPlayersOnDetect()) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.setCancelled(true);
|
||||
AntiVPN.getInstance().getExecutor().log(Level.INFO,
|
||||
"%s was kicked from pre-login proxy cache.",
|
||||
event.getConnection().getName());
|
||||
|
||||
|
||||
switch (instantResult.resultType()) {
|
||||
case DENIED_PROXY -> event.setReason(TextComponent.fromLegacy(ChatColor
|
||||
.translateAlternateColorCodes('&',
|
||||
StringUtil.varReplace(
|
||||
AntiVPN.getInstance().getVpnConfig().getKickString(),
|
||||
player,
|
||||
instantResult.response()))));
|
||||
case DENIED_COUNTRY -> event.setReason(TextComponent.fromLegacy(ChatColor
|
||||
.translateAlternateColorCodes('&',
|
||||
StringUtil.varReplace(
|
||||
AntiVPN.getInstance().getVpnConfig().countryVanillaKickReason(),
|
||||
player,
|
||||
instantResult.response()))));
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onListener(final PostLoginEvent event) {
|
||||
if(event.getPlayer().hasPermission("antivpn.bypass")) return;
|
||||
checkIp(event.getPlayer().getAddress().getAddress().getHostAddress(),
|
||||
AntiVPN.getInstance().getConfig().cachedResults(), result -> {
|
||||
if(result.isSuccess() && result.isProxy()) {
|
||||
event.getPlayer().disconnect(TextComponent.fromLegacyText(ChatColor
|
||||
.translateAlternateColorCodes('&',
|
||||
AntiVPN.getInstance().getConfig().getKickString())));
|
||||
System.out.println(event.getPlayer().getName()
|
||||
+ " joined on a VPN/Proxy (" + result.getMethod() + ")");
|
||||
} else if(!result.isSuccess()) {
|
||||
BungeeCord.getInstance().getLogger()
|
||||
.log(Level.WARNING,
|
||||
"The API query was not a success! " +
|
||||
"You may need to upgrade your license on https://funkemunky.cc/shop");
|
||||
}
|
||||
});
|
||||
public void onLeave(PlayerDisconnectEvent event) {
|
||||
AntiVPN.getInstance().getPlayerExecutor().unloadPlayer(event.getPlayer().getUniqueId());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,36 +2,42 @@ package dev.brighten.antivpn.bungee;
|
||||
|
||||
import dev.brighten.antivpn.api.APIPlayer;
|
||||
import dev.brighten.antivpn.api.PlayerExecutor;
|
||||
import net.md_5.bungee.BungeeCord;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class BungeePlayerExecutor implements PlayerExecutor {
|
||||
|
||||
private final Map<UUID, BungeePlayer> cachedPlayers = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public Optional<APIPlayer> getPlayer(String name) {
|
||||
ProxiedPlayer player = BungeeCord.getInstance().getPlayer(name);
|
||||
ProxiedPlayer player = BungeePlugin.pluginInstance.getProxy().getPlayer(name);
|
||||
|
||||
if(player == null) return Optional.empty();
|
||||
|
||||
return Optional.of(new BungeePlayer(player));
|
||||
return Optional.of(cachedPlayers.computeIfAbsent(player.getUniqueId(), key -> new BungeePlayer(player)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<APIPlayer> getPlayer(UUID uuid) {
|
||||
ProxiedPlayer player = BungeeCord.getInstance().getPlayer(uuid);
|
||||
ProxiedPlayer player = BungeePlugin.pluginInstance.getProxy().getPlayer(uuid);
|
||||
|
||||
if(player == null) return Optional.empty();
|
||||
|
||||
return Optional.of(new BungeePlayer(player));
|
||||
return Optional.of(cachedPlayers.computeIfAbsent(uuid, key -> new BungeePlayer(player)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unloadPlayer(UUID uuid) {
|
||||
this.cachedPlayers.remove(uuid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<APIPlayer> getOnlinePlayers() {
|
||||
return BungeeCord.getInstance().getPlayers().stream().map(BungeePlayer::new).collect(Collectors.toList());
|
||||
return BungeePlugin.pluginInstance.getProxy().getPlayers().stream()
|
||||
.map(pl -> cachedPlayers.computeIfAbsent(pl.getUniqueId(), key -> new BungeePlayer(pl)))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,82 +1,45 @@
|
||||
package dev.brighten.antivpn.bungee;
|
||||
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
import dev.brighten.antivpn.bungee.util.Config;
|
||||
import dev.brighten.antivpn.bungee.command.BungeeCommand;
|
||||
import dev.brighten.antivpn.command.Command;
|
||||
import lombok.Getter;
|
||||
import lombok.val;
|
||||
import net.md_5.bungee.BungeeCord;
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
import net.md_5.bungee.api.CommandSender;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
import net.md_5.bungee.api.chat.ComponentBuilder;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import dev.brighten.antivpn.database.VPNDatabase;
|
||||
import dev.brighten.antivpn.database.local.H2VPN;
|
||||
import dev.brighten.antivpn.database.mongo.MongoVPN;
|
||||
import dev.brighten.antivpn.database.sql.MySqlVPN;
|
||||
import net.md_5.bungee.api.plugin.Plugin;
|
||||
import org.bstats.bungeecord.Metrics;
|
||||
import org.bstats.charts.SimplePie;
|
||||
|
||||
import java.util.stream.IntStream;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class BungeePlugin extends Plugin {
|
||||
|
||||
public static BungeePlugin pluginInstance;
|
||||
|
||||
@Getter
|
||||
private Config config;
|
||||
|
||||
private static BaseComponent[] noPermission = new ComponentBuilder("No permission").color(ChatColor.RED)
|
||||
.create();
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
pluginInstance = this;
|
||||
|
||||
//Setting up config
|
||||
config = new Config();
|
||||
getProxy().getLogger().info("Loading config...");
|
||||
|
||||
|
||||
//Loading plugin
|
||||
AntiVPN.start(new BungeeConfig(), new BungeeListener(), new BungeePlayerExecutor());
|
||||
getProxy().getLogger().info("Starting AntiVPN services...");
|
||||
AntiVPN.start(new BungeeListener(), new BungeePlayerExecutor(), getDataFolder());
|
||||
|
||||
if(AntiVPN.getInstance().getVpnConfig().metrics()) {
|
||||
getProxy().getLogger().info("Starting bStats metrics...");
|
||||
Metrics metrics = new Metrics(this, 12616);
|
||||
metrics.addCustomChart(new SimplePie("database_used", this::getDatabaseType));
|
||||
getProxy().getScheduler().schedule(this,
|
||||
() -> AntiVPN.getInstance().checked = AntiVPN.getInstance().detections = 0,
|
||||
10, 10, TimeUnit.MINUTES);
|
||||
}
|
||||
|
||||
//TODO Add command functionality for BungeeCord
|
||||
for (Command command : AntiVPN.getInstance().getCommands()) {
|
||||
BungeeCord.getInstance().getPluginManager().registerCommand(pluginInstance, new net.md_5.bungee.api.plugin
|
||||
.Command(command.name(), command.permission(), command.aliases()) {
|
||||
|
||||
@Override
|
||||
public void execute(CommandSender sender, String[] args) {
|
||||
if(!sender.hasPermission("antivpn.command.*")
|
||||
&& !sender.hasPermission(command.permission())) {
|
||||
sender.sendMessage(noPermission);
|
||||
return;
|
||||
}
|
||||
|
||||
val children = command.children();
|
||||
|
||||
if(children.length > 0 && args.length > 0) {
|
||||
for (Command child : children) {
|
||||
if(child.name().equalsIgnoreCase(args[0])) {
|
||||
if(!sender.hasPermission("antivpn.command.*")
|
||||
&& !sender.hasPermission(child.permission())) {
|
||||
sender.sendMessage(noPermission);
|
||||
return;
|
||||
}
|
||||
|
||||
sender.sendMessage(TextComponent
|
||||
.fromLegacyText(ChatColor
|
||||
.translateAlternateColorCodes('&',
|
||||
child.execute(new BungeeCommandExecutor(sender), IntStream
|
||||
.range(0, args.length - 1)
|
||||
.mapToObj(i -> args[i + 1]).toArray(String[]::new)))));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sender.sendMessage(TextComponent
|
||||
.fromLegacyText(ChatColor
|
||||
.translateAlternateColorCodes('&',
|
||||
command.execute(new BungeeCommandExecutor(sender), args))));
|
||||
}
|
||||
});
|
||||
getProxy().getPluginManager().registerCommand(pluginInstance, new BungeeCommand(command));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,4 +47,18 @@ public class BungeePlugin extends Plugin {
|
||||
public void onDisable() {
|
||||
AntiVPN.getInstance().stop();
|
||||
}
|
||||
|
||||
private String getDatabaseType() {
|
||||
VPNDatabase database = AntiVPN.getInstance().getDatabase();
|
||||
|
||||
if(database instanceof H2VPN) {
|
||||
return "H2";
|
||||
} else if(database instanceof MySqlVPN) {
|
||||
return "MySQL";
|
||||
} else if(database instanceof MongoVPN) {
|
||||
return "MongoDB";
|
||||
} else {
|
||||
return "No-Database";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
package dev.brighten.antivpn.bungee.command;
|
||||
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
import lombok.val;
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
import net.md_5.bungee.api.CommandSender;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import net.md_5.bungee.api.plugin.Command;
|
||||
import net.md_5.bungee.api.plugin.TabExecutor;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
public class BungeeCommand extends Command implements TabExecutor {
|
||||
|
||||
private final dev.brighten.antivpn.command.Command command;
|
||||
public BungeeCommand(dev.brighten.antivpn.command.Command command) {
|
||||
super(command.name(), command.permission(), command.aliases());
|
||||
|
||||
this.command = command;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(CommandSender sender, String[] args) {
|
||||
if(!sender.hasPermission("antivpn.command.*")
|
||||
&& !sender.hasPermission(command.permission())) {
|
||||
sender.sendMessage(TextComponent.fromLegacyText(ChatColor.translateAlternateColorCodes('&',
|
||||
AntiVPN.getInstance().getMessageHandler().getString("no-permission").getMessage())));
|
||||
return;
|
||||
}
|
||||
|
||||
val children = command.children();
|
||||
|
||||
if(children.length > 0 && args.length > 0) {
|
||||
for (dev.brighten.antivpn.command.Command child : children) {
|
||||
if(child.name().equalsIgnoreCase(args[0]) || Arrays.stream(child.aliases())
|
||||
.anyMatch(alias -> alias.equalsIgnoreCase(args[0]))) {
|
||||
if(!sender.hasPermission("antivpn.command.*")
|
||||
&& !sender.hasPermission(child.permission())) {
|
||||
sender.sendMessage(TextComponent.fromLegacyText(ChatColor.translateAlternateColorCodes('&',
|
||||
AntiVPN.getInstance().getMessageHandler().getString("no-permission").getMessage())));
|
||||
return;
|
||||
}
|
||||
|
||||
sender.sendMessage(TextComponent
|
||||
.fromLegacyText(ChatColor
|
||||
.translateAlternateColorCodes('&',
|
||||
child.execute(new BungeeCommandExecutor(sender), IntStream
|
||||
.range(0, args.length - 1)
|
||||
.mapToObj(i -> args[i + 1]).toArray(String[]::new)))));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sender.sendMessage(TextComponent
|
||||
.fromLegacyText(ChatColor
|
||||
.translateAlternateColorCodes('&',
|
||||
command.execute(new BungeeCommandExecutor(sender), args))));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<String> onTabComplete(CommandSender sender, String[] args) {
|
||||
val children = command.children();
|
||||
|
||||
if(children.length > 0 && args.length > 0) {
|
||||
for (dev.brighten.antivpn.command.Command child : children) {
|
||||
if(child.name().equalsIgnoreCase(args[0]) || Arrays.stream(child.aliases())
|
||||
.anyMatch(alias2 -> alias2.equalsIgnoreCase(args[0]))) {
|
||||
return child.tabComplete(new BungeeCommandExecutor(sender), "alias", IntStream
|
||||
.range(0, args.length - 1)
|
||||
.mapToObj(i -> args[i + 1]).toArray(String[]::new));
|
||||
}
|
||||
}
|
||||
}
|
||||
return command.tabComplete(new BungeeCommandExecutor(sender), "alias", args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package dev.brighten.antivpn.bungee.command;
|
||||
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
import dev.brighten.antivpn.api.APIPlayer;
|
||||
import dev.brighten.antivpn.command.CommandExecutor;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
import net.md_5.bungee.api.CommandSender;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class BungeeCommandExecutor implements CommandExecutor {
|
||||
|
||||
private final CommandSender sender;
|
||||
|
||||
@Override
|
||||
public void sendMessage(String message, Object... objects) {
|
||||
sender.sendMessage(TextComponent.fromLegacyText(ChatColor
|
||||
.translateAlternateColorCodes('&', String.format(message, objects))));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(String permission) {
|
||||
return sender.hasPermission(permission);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<APIPlayer> getPlayer() {
|
||||
if(!isPlayer()) return Optional.empty();
|
||||
|
||||
return AntiVPN.getInstance().getPlayerExecutor().getPlayer(((ProxiedPlayer) sender).getUniqueId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPlayer() {
|
||||
return sender instanceof ProxiedPlayer;
|
||||
}
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
package dev.brighten.antivpn.bungee.util;
|
||||
|
||||
import com.google.common.io.ByteStreams;
|
||||
import dev.brighten.antivpn.bungee.BungeePlugin;
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
import net.md_5.bungee.config.Configuration;
|
||||
import net.md_5.bungee.config.ConfigurationProvider;
|
||||
import net.md_5.bungee.config.YamlConfiguration;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Author: nitramleo (Martin)
|
||||
* Date created: 10-Aug-18
|
||||
*/
|
||||
public class Config {
|
||||
|
||||
private File file;
|
||||
private Configuration configuration;
|
||||
|
||||
public Config() {
|
||||
this.file = new File(BungeePlugin.pluginInstance.getDataFolder(), "config.yml");
|
||||
try {
|
||||
if (!this.file.exists()) {
|
||||
if (!BungeePlugin.pluginInstance.getDataFolder().exists()) {
|
||||
BungeePlugin.pluginInstance.getDataFolder().mkdir();
|
||||
}
|
||||
this.file.createNewFile();
|
||||
try (final InputStream is = BungeePlugin.pluginInstance.getResourceAsStream("config.yml");
|
||||
final OutputStream os = new FileOutputStream(this.file)) {
|
||||
ByteStreams.copy(is, os);
|
||||
}
|
||||
}
|
||||
this.configuration = ConfigurationProvider.getProvider(YamlConfiguration.class).load(this.file);
|
||||
}
|
||||
catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void load() {
|
||||
this.file = new File(BungeePlugin.pluginInstance.getDataFolder(), "config.yml");
|
||||
try {
|
||||
this.configuration = ConfigurationProvider.getProvider(YamlConfiguration.class).load(this.file);
|
||||
}
|
||||
catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void save() {
|
||||
try {
|
||||
ConfigurationProvider.getProvider( YamlConfiguration.class).save(this.configuration, this.file);
|
||||
}
|
||||
catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public Configuration getConfiguration() {
|
||||
return this.configuration;
|
||||
}
|
||||
|
||||
public File getFile() {
|
||||
return this.file;
|
||||
}
|
||||
|
||||
public double getDouble(final String path) {
|
||||
if (this.configuration.get(path) != null) {
|
||||
return this.configuration.getDouble(path);
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
public int getInt(final String path) {
|
||||
if (this.configuration.get(path) != null) {
|
||||
return this.configuration.getInt(path);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public Object get(final String path) {
|
||||
return this.configuration.get(path);
|
||||
}
|
||||
|
||||
public void set(final String path, final Object object) {
|
||||
configuration.set(path, object);
|
||||
}
|
||||
|
||||
public boolean getBoolean(final String path) {
|
||||
return this.configuration.get(path) != null && this.configuration.getBoolean(path);
|
||||
}
|
||||
|
||||
public String getString(final String path) {
|
||||
if (this.configuration.get(path) != null) {
|
||||
return ChatColor.translateAlternateColorCodes('&', this.configuration.getString(path));
|
||||
}
|
||||
return "String at path: " + path + " not found!";
|
||||
}
|
||||
|
||||
public List<String> getStringList(final String path) {
|
||||
if (this.configuration.get(path) != null) {
|
||||
final ArrayList<String> strings = new ArrayList<String>();
|
||||
for (final String string : this.configuration.getStringList(path)) {
|
||||
strings.add(ChatColor.translateAlternateColorCodes('&', string));
|
||||
}
|
||||
return strings;
|
||||
}
|
||||
return Arrays.asList("String List at path: " + path + " not found!");
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
package dev.brighten.antivpn.bungee.util;
|
||||
|
||||
import dev.brighten.antivpn.bungee.BungeePlugin;
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
@AllArgsConstructor
|
||||
public class ConfigDefault<A> {
|
||||
|
||||
private final A defaultValue;
|
||||
private final String path;
|
||||
private final BungeePlugin plugin;
|
||||
|
||||
public A get() {
|
||||
if(plugin.getConfig().get(path) != null)
|
||||
return (A) plugin.getConfig().get(path);
|
||||
else {
|
||||
plugin.getConfig().set(path, defaultValue);
|
||||
plugin.getConfig().save();
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
public A set(A value) {
|
||||
plugin.getConfig().set(path, value);
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
name: AntiVPN
|
||||
name: KauriVPN
|
||||
main: dev.brighten.antivpn.bungee.BungeePlugin
|
||||
description: A simple and fast antivpn plugin.
|
||||
version: ${project.version}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
# Changelog
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [1.9.4] - 2025-09-30
|
||||
|
||||
### Added
|
||||
- New dependency management system with automatic library loading and relocation
|
||||
- Caffeine cache implementation to replace Guava
|
||||
- Sponge platform support with full event handling and command system
|
||||
- UUID lookup functionality for player validation
|
||||
- Enhanced kick checking system with scheduled task execution
|
||||
- Support for Java 17 and Java 21 runtime environments
|
||||
- New database metrics tracking for bStats
|
||||
|
||||
### Changed
|
||||
- **BREAKING**: Minimum Java version upgraded from 8 to 17
|
||||
- Replaced Guava cache with Caffeine cache for better performance
|
||||
- Modernized player checking system with asynchronous processing
|
||||
- Improved database connection handling with proper resource management
|
||||
- Enhanced VPN/Proxy detection with new `CheckResult` and `ResultType` system
|
||||
- Updated Maven dependencies and build process
|
||||
- Reorganized project structure (Assembly → Universal module)
|
||||
- Improved error handling and exception logging throughout codebase
|
||||
|
||||
### Fixed
|
||||
- H2 database compatibility issues with automatic backup and recovery
|
||||
- Memory leaks in database result set handling with try-with-resources
|
||||
- Thread safety issues in player cache management
|
||||
- Command registration and unregistration during plugin lifecycle
|
||||
- Proper cleanup of database drivers on shutdown
|
||||
- Resource management in SQL connections and prepared statements
|
||||
|
||||
### Removed
|
||||
- Guava dependency (replaced with Caffeine and built-in utilities)
|
||||
- Legacy cached response handling system
|
||||
- Old table format compatibility code
|
||||
+161
-3
@@ -5,15 +5,173 @@
|
||||
<parent>
|
||||
<artifactId>AntiVPN</artifactId>
|
||||
<groupId>dev.brighten.antivpn</groupId>
|
||||
<version>1.1.1</version>
|
||||
<version>1.9.4</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>Common</artifactId>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>8</maven.compiler.source>
|
||||
<maven.compiler.target>8</maven.compiler.target>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.13.0</version>
|
||||
<configuration>
|
||||
<source>17</source>
|
||||
<target>17</target>
|
||||
<compilerArgument>-XDignore.symbol.file</compilerArgument>
|
||||
<annotationProcessorPaths>
|
||||
<path>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.30</version>
|
||||
</path>
|
||||
</annotationProcessorPaths>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.6.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<minimizeJar>true</minimizeJar>
|
||||
<relocations>
|
||||
<relocation>
|
||||
<pattern>org.yaml.snakeyaml</pattern>
|
||||
<shadedPattern>dev.brighten.antivpn.shaded.org.yaml.snakeyaml</shadedPattern>
|
||||
<excludes>
|
||||
<!-- Exclude annotation values from relocation -->
|
||||
<exclude>dev.brighten.antivpn.depends.Relocate</exclude>
|
||||
<exclude>dev.brighten.antivpn.depends.MavenLibraries</exclude>
|
||||
</excludes>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>com.github.benmanes.caffeine</pattern>
|
||||
<shadedPattern>dev.brighten.antivpn.shaded.com.github.benmanes.caffeine</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>org.h2</pattern>
|
||||
<shadedPattern>dev.brighten.antivpn.shaded.org.h2</shadedPattern>
|
||||
<excludes>
|
||||
<!-- Exclude annotation values from relocation -->
|
||||
<exclude>dev.brighten.antivpn.depends.Relocate</exclude>
|
||||
<exclude>dev.brighten.antivpn.depends.MavenLibraries</exclude>
|
||||
</excludes>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>org.bson</pattern>
|
||||
<shadedPattern>dev.brighten.antivpn.shaded.org.bson</shadedPattern>
|
||||
<excludes>
|
||||
<!-- Exclude annotation values from relocation -->
|
||||
<exclude>dev.brighten.antivpn.depends.Relocate</exclude>
|
||||
<exclude>dev.brighten.antivpn.depends.MavenLibraries</exclude>
|
||||
</excludes>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>com.mongodb</pattern>
|
||||
<shadedPattern>dev.brighten.antivpn.shaded.com.mongodb</shadedPattern>
|
||||
<excludes>
|
||||
<!-- Exclude annotation values from relocation -->
|
||||
<exclude>dev.brighten.antivpn.depends.Relocate</exclude>
|
||||
<exclude>dev.brighten.antivpn.depends.MavenLibraries</exclude>
|
||||
</excludes>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>com.mysql.cj</pattern>
|
||||
<shadedPattern>dev.brighten.antivpn.shaded.com.mysql.cj</shadedPattern>
|
||||
<excludes>
|
||||
<!-- Exclude annotation values from relocation -->
|
||||
<exclude>dev.brighten.antivpn.depends.Relocate</exclude>
|
||||
<exclude>dev.brighten.antivpn.depends.MavenLibraries</exclude>
|
||||
</excludes>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>com.mysql.jdbc</pattern>
|
||||
<shadedPattern>dev.brighten.antivpn.shaded.com.mysql.jdbc</shadedPattern>
|
||||
<excludes>
|
||||
<!-- Exclude annotation values from relocation -->
|
||||
<exclude>dev.brighten.antivpn.depends.Relocate</exclude>
|
||||
<exclude>dev.brighten.antivpn.depends.MavenLibraries</exclude>
|
||||
</excludes>
|
||||
</relocation>
|
||||
</relocations>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<filtering>true</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
</build>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>funkemunky-releases</id>
|
||||
<url>https://nexus.funkemunky.cc/content/repositories/releases/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.mysql</groupId>
|
||||
<artifactId>mysql-connector-j</artifactId>
|
||||
<version>9.1.0</version>
|
||||
<type>jar</type>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<version>2.2.220</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.ow2.asm</groupId>
|
||||
<artifactId>asm</artifactId>
|
||||
<version>9.8</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.ow2.asm</groupId>
|
||||
<artifactId>asm-commons</artifactId>
|
||||
<version>9.8</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.yaml</groupId>
|
||||
<artifactId>snakeyaml</artifactId>
|
||||
<version>2.2</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.ben-manes.caffeine</groupId>
|
||||
<artifactId>caffeine</artifactId>
|
||||
<version>3.1.8</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.mongodb</groupId>
|
||||
<artifactId>mongo-java-driver</artifactId>
|
||||
<version>3.12.14</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -5,47 +5,227 @@ import dev.brighten.antivpn.api.VPNConfig;
|
||||
import dev.brighten.antivpn.api.VPNExecutor;
|
||||
import dev.brighten.antivpn.command.Command;
|
||||
import dev.brighten.antivpn.command.impl.AntiVPNCommand;
|
||||
import dev.brighten.antivpn.command.impl.LookupCommand;
|
||||
import dev.brighten.antivpn.utils.VPNResponse;
|
||||
import dev.brighten.antivpn.utils.json.JSONException;
|
||||
import dev.brighten.antivpn.utils.json.JSONObject;
|
||||
import dev.brighten.antivpn.utils.json.JsonReader;
|
||||
import dev.brighten.antivpn.database.VPNDatabase;
|
||||
import dev.brighten.antivpn.database.local.H2VPN;
|
||||
import dev.brighten.antivpn.database.mongo.MongoVPN;
|
||||
import dev.brighten.antivpn.database.sql.MySqlVPN;
|
||||
import dev.brighten.antivpn.depends.LibraryLoader;
|
||||
import dev.brighten.antivpn.depends.MavenLibrary;
|
||||
import dev.brighten.antivpn.depends.Relocate;
|
||||
import dev.brighten.antivpn.message.MessageHandler;
|
||||
import dev.brighten.antivpn.utils.ConfigDefault;
|
||||
import dev.brighten.antivpn.utils.MiscUtils;
|
||||
import dev.brighten.antivpn.utils.config.Configuration;
|
||||
import dev.brighten.antivpn.utils.config.ConfigurationProvider;
|
||||
import dev.brighten.antivpn.utils.config.YamlConfiguration;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
@Setter(AccessLevel.PRIVATE)
|
||||
@MavenLibrary(groupId = "com.h2database", artifactId ="h2", version = "2.2.220", relocations = {
|
||||
@Relocate(from ="org" + ".\\h2", to ="dev.brighten.antivpn.shaded.org.h2")})
|
||||
@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(
|
||||
groupId = "com.mysql",
|
||||
artifactId = "mysql-connector-j",
|
||||
version = "9.1.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 {
|
||||
|
||||
private static AntiVPN INSTANCE;
|
||||
private VPNConfig config;
|
||||
private VPNConfig vpnConfig;
|
||||
private VPNExecutor executor;
|
||||
private PlayerExecutor playerExecutor;
|
||||
private VPNDatabase database;
|
||||
private MessageHandler messageHandler;
|
||||
private Configuration config;
|
||||
private List<Command> commands = new ArrayList<>();
|
||||
public int detections, checked;
|
||||
private File pluginFolder;
|
||||
|
||||
public static void start(VPNConfig config, VPNExecutor executor, PlayerExecutor playerExecutor) {
|
||||
public static void start(VPNExecutor executor, PlayerExecutor playerExecutor, File pluginFolder) {
|
||||
//Initializing
|
||||
|
||||
INSTANCE = new AntiVPN();
|
||||
|
||||
INSTANCE.config = config;
|
||||
INSTANCE.pluginFolder = pluginFolder;
|
||||
INSTANCE.executor = executor;
|
||||
INSTANCE.playerExecutor = playerExecutor;
|
||||
|
||||
LibraryLoader.loadAll(INSTANCE);
|
||||
|
||||
try {
|
||||
File configFile = new File(pluginFolder, "config.yml");
|
||||
if(!configFile.exists()){
|
||||
if(configFile.getParentFile().mkdirs()) {
|
||||
AntiVPN.getInstance().getExecutor().log("Created plugin folder!");
|
||||
}
|
||||
MiscUtils.copy(INSTANCE.getResource( "config.yml"), configFile);
|
||||
}
|
||||
INSTANCE.config = ConfigurationProvider.getProvider(YamlConfiguration.class)
|
||||
.load(configFile);
|
||||
} catch (IOException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException("Could not load config.yml, plugin disabling...", e);
|
||||
executor.disablePlugin();
|
||||
return;
|
||||
}
|
||||
|
||||
INSTANCE.vpnConfig = new VPNConfig();
|
||||
|
||||
INSTANCE.executor.registerListeners();
|
||||
INSTANCE.config.update();
|
||||
INSTANCE.vpnConfig.update();
|
||||
|
||||
INSTANCE.messageHandler = new MessageHandler();
|
||||
|
||||
try {
|
||||
switch(INSTANCE.vpnConfig.getDatabaseType().toLowerCase()) {
|
||||
case "h2":
|
||||
case "local":
|
||||
case "flatfile": {
|
||||
AntiVPN.getInstance().getExecutor().log("Using databaseType H2...");
|
||||
INSTANCE.database = new H2VPN();
|
||||
INSTANCE.database.init();
|
||||
break;
|
||||
}
|
||||
case "mysql":
|
||||
case "sql":{
|
||||
AntiVPN.getInstance().getExecutor().log("Using databaseType MySQL...");
|
||||
INSTANCE.database = new MySqlVPN();
|
||||
INSTANCE.database.init();
|
||||
break;
|
||||
}
|
||||
case "mongo":
|
||||
case "mongodb":
|
||||
case "mongod": {
|
||||
INSTANCE.database = new MongoVPN();
|
||||
INSTANCE.database.init();
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
AntiVPN.getInstance().getExecutor().log("Could not find database type \"" + INSTANCE.vpnConfig.getDatabaseType() + "\". " +
|
||||
"Options: [MySQL]");
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
AntiVPN.getInstance().getExecutor().logException("Could not initialize database, plugin disabling...", e);
|
||||
executor.disablePlugin();
|
||||
return;
|
||||
}
|
||||
|
||||
//Registering commands
|
||||
INSTANCE.registerCommands();
|
||||
|
||||
//Turning on alerts of players who are already online.
|
||||
playerExecutor.getOnlinePlayers().forEach(player -> {
|
||||
//We want to make sure they even have permission to see alerts before we make a bunch
|
||||
//of unnecessary database queries.
|
||||
if(player.hasPermission("antivpn.command.alerts")) {
|
||||
//Running database check for enabled alerts.
|
||||
INSTANCE.database.alertsState(player.getUuid(), player::setAlertsEnabled);
|
||||
}
|
||||
});
|
||||
|
||||
AntiVPN.getInstance().getMessageHandler().initStrings(vpnString -> new ConfigDefault<>
|
||||
(vpnString.getDefaultMessage(), "messages." + vpnString.getKey(), AntiVPN.getInstance())
|
||||
.get());
|
||||
AntiVPN.getInstance().getMessageHandler().reloadStrings();
|
||||
|
||||
// Starting kick checks
|
||||
AntiVPN.getInstance().getExecutor().startKickChecks();
|
||||
}
|
||||
|
||||
public InputStream getResource(String filename) {
|
||||
if (filename == null) {
|
||||
throw new IllegalArgumentException("Filename cannot be null");
|
||||
} else {
|
||||
try {
|
||||
URL url = executor.getClass().getClassLoader().getResource(filename);
|
||||
if (url == null) {
|
||||
return null;
|
||||
} else {
|
||||
URLConnection connection = url.openConnection();
|
||||
connection.setUseCaches(false);
|
||||
return connection.getInputStream();
|
||||
}
|
||||
} catch (IOException var4) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
executor.shutdown();
|
||||
if (database instanceof H2VPN) {
|
||||
database.shutdown();
|
||||
|
||||
// Try to deregister driver
|
||||
try {
|
||||
java.sql.Driver driver = java.sql.DriverManager.getDriver("jdbc:h2:");
|
||||
if (driver != null) {
|
||||
java.sql.DriverManager.deregisterDriver(driver);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Log but don't throw
|
||||
executor.log("Failed to deregister H2 driver: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
VPNExecutor.threadExecutor.shutdown();
|
||||
if(database != null) database.shutdown();
|
||||
}
|
||||
|
||||
public void reloadDatabase() {
|
||||
database.shutdown();
|
||||
|
||||
switch(AntiVPN.getInstance().getVpnConfig().getDatabaseType().toLowerCase()) {
|
||||
case "h2":
|
||||
case "local":
|
||||
case "flatfile": {
|
||||
AntiVPN.getInstance().getExecutor().log("Using databaseType H2...");
|
||||
INSTANCE.database = new H2VPN();
|
||||
INSTANCE.database.init();
|
||||
break;
|
||||
}
|
||||
case "mysql":
|
||||
case "sql":{
|
||||
AntiVPN.getInstance().getExecutor().log("Using databaseType MySQL...");
|
||||
INSTANCE.database = new MySqlVPN();
|
||||
INSTANCE.database.init();
|
||||
break;
|
||||
}
|
||||
case "mongo":
|
||||
case "mongodb":
|
||||
case "mongod": {
|
||||
INSTANCE.database = new MongoVPN();
|
||||
INSTANCE.database.init();
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
AntiVPN.getInstance().getExecutor().log("Could not find database type \"" + INSTANCE.vpnConfig.getDatabaseType() + "\". " +
|
||||
"Options: [MySQL]");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static AntiVPN getInstance() {
|
||||
@@ -54,17 +234,26 @@ public class AntiVPN {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
public static VPNResponse getVPNResponse(String ip, String license, boolean cachedResults /* faster if set to true*/)
|
||||
throws JSONException, IOException {
|
||||
JSONObject result = JsonReader.readJsonFromUrl(String
|
||||
.format("https://funkemunky.cc/vpn?ip=%s&license=%s&cache=%s",
|
||||
ip, license.length() == 0 ? "none" : license, cachedResults));
|
||||
public void saveConfig() {
|
||||
try {
|
||||
ConfigurationProvider.getProvider(YamlConfiguration.class)
|
||||
.save(getConfig(), new File(pluginFolder.getPath() + File.separator + "config.yml"));
|
||||
} catch (IOException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException(e);
|
||||
}
|
||||
}
|
||||
|
||||
return VPNResponse.fromJson(result);
|
||||
public void reloadConfig() {
|
||||
try {
|
||||
|
||||
config = ConfigurationProvider.getProvider(YamlConfiguration.class)
|
||||
.load(new File(pluginFolder.getPath() + File.separator + "config.yml"));
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void registerCommands() {
|
||||
commands.add(new AntiVPNCommand());
|
||||
commands.add(new LookupCommand());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,101 @@
|
||||
package dev.brighten.antivpn.api;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.Cache;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.logging.Level;
|
||||
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public abstract class APIPlayer {
|
||||
private final UUID uuid;
|
||||
private final String name;
|
||||
private final InetAddress ip;
|
||||
@Setter
|
||||
private boolean alertsEnabled;
|
||||
|
||||
private static final Cache<String, CheckResult> checkResultCache = Caffeine.newBuilder()
|
||||
.expireAfterWrite(5, TimeUnit.MINUTES)
|
||||
.maximumSize(2000)
|
||||
.build();
|
||||
|
||||
public APIPlayer(UUID uuid, String name, InetAddress ip) {
|
||||
this.uuid = uuid;
|
||||
this.name = name;
|
||||
this.ip = ip;
|
||||
}
|
||||
|
||||
public abstract void sendMessage(String message);
|
||||
|
||||
public abstract void kickPlayer(String reason);
|
||||
|
||||
public abstract boolean hasPermission(String permission);
|
||||
|
||||
public void updateAlertsState() {
|
||||
//Updating into database so its synced across servers and saved on logout.
|
||||
AntiVPN.getInstance().getDatabase().updateAlertsState(uuid, alertsEnabled);
|
||||
}
|
||||
|
||||
public CheckResult checkPlayer(Consumer<CheckResult> onKick) {
|
||||
if (hasPermission("antivpn.bypass") //Has bypass permission
|
||||
//Is exempt
|
||||
|| (uuid != null && AntiVPN.getInstance().getExecutor().isWhitelisted(uuid))
|
||||
//Or has a name that starts with a certain prefix. This is for Bedrock exempting.
|
||||
|| AntiVPN.getInstance().getExecutor().isWhitelisted(ip.getHostAddress())
|
||||
|| AntiVPN.getInstance().getVpnConfig().getPrefixWhitelists().stream()
|
||||
.anyMatch(name::startsWith)) return new CheckResult(null, ResultType.WHITELISTED);
|
||||
|
||||
CheckResult cachedResult = checkResultCache.getIfPresent(ip.getHostAddress());
|
||||
|
||||
if(cachedResult != null) {
|
||||
if(cachedResult.response().getIp().equals(ip.getHostAddress())) {
|
||||
AntiVPN.getInstance().getExecutor().log(Level.FINE, "Cached result for " + ip.getHostAddress() + " is " + cachedResult.resultType());
|
||||
return cachedResult;
|
||||
}
|
||||
}
|
||||
|
||||
AntiVPN.getInstance().getExecutor().checkIp(ip.getHostAddress())
|
||||
.thenAccept(result -> {
|
||||
if(!result.isSuccess()) {
|
||||
AntiVPN.getInstance().getExecutor().log(Level.WARNING, "The API query was not a success! " +
|
||||
"You may need to upgrade your license on " +
|
||||
"https://funkemunky.cc/shop");
|
||||
}
|
||||
// If the countryList() size is zero, no need to check.
|
||||
// Running country check first
|
||||
CheckResult checkResult;
|
||||
if (!AntiVPN.getInstance().getVpnConfig().countryList().isEmpty()
|
||||
&& !((uuid != null && AntiVPN.getInstance().getExecutor()
|
||||
.isWhitelisted(uuid))
|
||||
//Or has a name that starts with a certain prefix. This is for Bedrock exempting.
|
||||
|| AntiVPN.getInstance().getExecutor().isWhitelisted(ip.getHostAddress()))
|
||||
// This bit of code will decide whether or not to kick the player
|
||||
// If it contains the code and it is set to whitelist, it will not kick
|
||||
// as they are equal and vise versa. However, if the contains does not match
|
||||
// the state, it will kick.
|
||||
&& AntiVPN.getInstance().getVpnConfig().countryList()
|
||||
.contains(result.getCountryCode())
|
||||
!= AntiVPN.getInstance().getVpnConfig().whitelistCountries()) {
|
||||
//Using our built in kicking system if no commands are configured
|
||||
checkResult = new CheckResult(result, ResultType.DENIED_COUNTRY);
|
||||
} else if (result.isProxy()) {
|
||||
checkResult = new CheckResult(result, ResultType.DENIED_PROXY);
|
||||
} else {
|
||||
checkResult = new CheckResult(result, ResultType.ALLOWED);
|
||||
}
|
||||
|
||||
AntiVPN.getInstance().getExecutor().log(Level.FINE, "Result for " + ip.getHostAddress() + " is " + checkResult.resultType());
|
||||
|
||||
checkResultCache.put(ip.getHostAddress(), checkResult);
|
||||
onKick.accept(checkResult);
|
||||
AntiVPN.getInstance().checked++;
|
||||
});
|
||||
return new CheckResult(null, ResultType.UNKNOWN);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
package dev.brighten.antivpn.api;
|
||||
|
||||
import dev.brighten.antivpn.web.objects.VPNResponse;
|
||||
|
||||
public record CheckResult(VPNResponse response, ResultType resultType) {
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package dev.brighten.antivpn.api;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.util.UUID;
|
||||
|
||||
public class OfflinePlayer extends APIPlayer {
|
||||
|
||||
public OfflinePlayer(UUID uuid, String name, InetAddress ip) {
|
||||
super(uuid, name, ip);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(String message) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void kickPlayer(String reason) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(String permission) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -10,5 +10,7 @@ public interface PlayerExecutor {
|
||||
|
||||
Optional<APIPlayer> getPlayer(UUID uuid);
|
||||
|
||||
void unloadPlayer(UUID uuid);
|
||||
|
||||
List<APIPlayer> getOnlinePlayers();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package dev.brighten.antivpn.api;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
public enum ResultType {
|
||||
ALLOWED(false),
|
||||
WHITELISTED(false),
|
||||
DENIED_COUNTRY(true),
|
||||
DENIED_PROXY(true),
|
||||
UNKNOWN(false);
|
||||
|
||||
@Getter
|
||||
private final boolean shouldBlock;
|
||||
|
||||
ResultType(boolean shouldBlock) {
|
||||
this.shouldBlock = shouldBlock;
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,290 @@
|
||||
package dev.brighten.antivpn.api;
|
||||
|
||||
public interface VPNConfig {
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
import dev.brighten.antivpn.utils.ConfigDefault;
|
||||
|
||||
String getLicense();
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
boolean cachedResults();
|
||||
public class VPNConfig {
|
||||
private final ConfigDefault<String> licenseDefault = new ConfigDefault<>("",
|
||||
"license", AntiVPN.getInstance()), kickStringDefault =
|
||||
new ConfigDefault<>("Proxies are not allowed on our server",
|
||||
"kickMessage", AntiVPN.getInstance()),
|
||||
defaultDatabaseType = new ConfigDefault<>("H2",
|
||||
"database.type", AntiVPN.getInstance()),
|
||||
defaultDatabaseName = new ConfigDefault<>("kaurivpn",
|
||||
"database.database", AntiVPN.getInstance()),
|
||||
defaultMongoURL = new ConfigDefault<>("", "database.mongoURL", AntiVPN.getInstance()),
|
||||
defaultUsername = new ConfigDefault<>("root",
|
||||
"database.username", AntiVPN.getInstance()),
|
||||
defaultPassword = new ConfigDefault<>("password",
|
||||
"database.password", AntiVPN.getInstance()),
|
||||
defaultCountryKickReason = new ConfigDefault<>(
|
||||
"&cSorry, but our server does not allow connections from\n&f%country%",
|
||||
"countries.vanillaKickReason", AntiVPN.getInstance()),
|
||||
defaultIp = new ConfigDefault<>("localhost", "database.ip", AntiVPN.getInstance()),
|
||||
defaultAlertMsg = new ConfigDefault<>("&8[&6KauriVPN&8] &e%player% &7has joined on a VPN/proxy" +
|
||||
" &8(&f%reason%&8) &7in location &8(&f%city%&7, &f%country%&8)", "alerts.message",
|
||||
AntiVPN.getInstance());
|
||||
private final ConfigDefault<Boolean> cacheResultsDefault = new ConfigDefault<>(true,
|
||||
"cachedResults", AntiVPN.getInstance()),
|
||||
defaultUseCredentials = new ConfigDefault<>(true,
|
||||
"database.useCredentials", AntiVPN.getInstance()),
|
||||
defaultDatabaseEnabled = new ConfigDefault<>(false, "database.enabled",
|
||||
AntiVPN.getInstance()), defaultCommandsEnable = new ConfigDefault<>(false,
|
||||
"commands.enabled", AntiVPN.getInstance()), defaultKickPlayers
|
||||
= new ConfigDefault<>(true, "kickPlayers", AntiVPN.getInstance()),
|
||||
defaultAlertToStaff = new ConfigDefault<>(true, "alerts.enabled",
|
||||
AntiVPN.getInstance()),
|
||||
defaultWhitelistCountries = new ConfigDefault<>(true, "countries.whitelist",
|
||||
AntiVPN.getInstance()),
|
||||
defaultMetrics = new ConfigDefault<>(true, "bstats", AntiVPN.getInstance());
|
||||
private final ConfigDefault<Integer>
|
||||
defaultPort = new ConfigDefault<>(-1, "database.port", AntiVPN.getInstance());
|
||||
private final ConfigDefault<List<String>> prefixWhitelistsDefault = new ConfigDefault<>(new ArrayList<>(),
|
||||
"prefixWhitelists", AntiVPN.getInstance()), defaultCommands = new ConfigDefault<>(
|
||||
Collections.singletonList("kick %player% VPNs are not allowed on our server!"), "commands.execute",
|
||||
AntiVPN.getInstance()),
|
||||
defCountryKickCommands = new ConfigDefault<>(Collections.emptyList(),
|
||||
"countries.commands", AntiVPN.getInstance()),
|
||||
defCountrylist = new ConfigDefault<>(new ArrayList<>(), "countries.list",
|
||||
AntiVPN.getInstance());
|
||||
|
||||
String getKickString();
|
||||
private String license, kickMessage, databaseType, databaseName, mongoURL, username, password, ip, alertMsg,
|
||||
countryVanillaKickReason;
|
||||
private List<String> prefixWhitelists, commands, countryList, countryKickCommands;
|
||||
private int port;
|
||||
private boolean cacheResults, databaseEnabled, useCredentials, commandsEnabled, kickPlayers, alertToStaff,
|
||||
metrics, whitelistCountries;
|
||||
|
||||
void update();
|
||||
/**
|
||||
* License from https://funkemunky.cc/shop to be used for more queries.
|
||||
* @return String
|
||||
*/
|
||||
public String getLicense() {
|
||||
return license;
|
||||
}
|
||||
|
||||
/**
|
||||
* If true, results will be cached to reduce queries to https://funkemunky.cc
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean cachedResults() {
|
||||
return cacheResults;
|
||||
}
|
||||
|
||||
/**
|
||||
* Will be used for vanilla kick message when {@link VPNConfig#runCommands()} is true.
|
||||
* @return String
|
||||
*/
|
||||
public String getKickString() {
|
||||
return kickMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Message to send staff on proxy detection.
|
||||
* @return String
|
||||
*/
|
||||
public String alertMessage() {
|
||||
return alertMsg;
|
||||
}
|
||||
|
||||
/**
|
||||
* If true, staff will be alerted on proxy detection.
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean alertToStaff() {
|
||||
return alertToStaff;
|
||||
}
|
||||
|
||||
/**
|
||||
* If true, will run {@link VPNConfig#commands()} on detect. If not, it will use vanilla kicking methods.
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean runCommands() {
|
||||
return commandsEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Commands to run on proxy detection.
|
||||
* @return List
|
||||
*/
|
||||
public List<String> commands() {
|
||||
return commands;
|
||||
}
|
||||
|
||||
/**
|
||||
* If false, no commands nor kick will be run on proxy detection.
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean kickPlayersOnDetect() {
|
||||
return kickPlayers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Strings of which are checked against the beginning of player names. Used to
|
||||
* allow Geyser-connected players to join.
|
||||
* @return List
|
||||
*/
|
||||
public List<String> getPrefixWhitelists() {
|
||||
return prefixWhitelists;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if we want to use a database
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean isDatabaseEnabled() {
|
||||
return databaseEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not the database we want to connect to requires credentials.
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean useDatabaseCreds() {
|
||||
return useCredentials;
|
||||
}
|
||||
|
||||
/**
|
||||
* Only for Mongo only. URL used for connecting to database. Overrides other fields
|
||||
* @return String
|
||||
*/
|
||||
public String mongoDatabaseURL() {
|
||||
return mongoURL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Database type. Either MySQL and Mongo.
|
||||
* @return String
|
||||
*/
|
||||
public String getDatabaseType() {
|
||||
return databaseType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Database name
|
||||
* @return String
|
||||
*/
|
||||
public String getDatabaseName() {
|
||||
return databaseName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Database username
|
||||
* @return String
|
||||
*/
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
/**
|
||||
* Database Password
|
||||
* @return String
|
||||
*/
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
/**
|
||||
* Database IP
|
||||
* @return String
|
||||
*/
|
||||
public String getIp() {
|
||||
return ip;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of ISO country codes we need to check.
|
||||
* @return List
|
||||
*/
|
||||
public List<String> countryList() {
|
||||
return countryList;
|
||||
}
|
||||
|
||||
/**
|
||||
* If true, we only allow the {@link VPNConfig#countryKickCommands()}. If false, we blacklist them.
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean whitelistCountries() {
|
||||
return whitelistCountries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns our configured commands to run on player country detection.
|
||||
* @return List
|
||||
*/
|
||||
public List<String> countryKickCommands() {
|
||||
return countryKickCommands;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the vanilla kick reason for bad country locations
|
||||
* @return String
|
||||
*/
|
||||
public String countryVanillaKickReason() {
|
||||
return countryVanillaKickReason;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the port based on configuration. If {@link VPNConfig#port} is -1, will get default port
|
||||
* based on {@link VPNConfig#getDatabaseType()} lowerCase().
|
||||
* @return int
|
||||
*/
|
||||
public int getPort() {
|
||||
if(port == -1) {
|
||||
switch (getDatabaseType().toLowerCase()) {
|
||||
case "mongodb":
|
||||
case "mongo":
|
||||
case "mongod":
|
||||
return 27017;
|
||||
case "sql":
|
||||
case "mysql":
|
||||
return 3306;
|
||||
}
|
||||
}
|
||||
|
||||
return port;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* If true, https://bstats.org metrics will be collected to improve KauriVPN.
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean metrics() {
|
||||
return metrics;
|
||||
}
|
||||
|
||||
/**
|
||||
* Grabs all information from the config.yml
|
||||
*/
|
||||
public void update() {
|
||||
license = licenseDefault.get();
|
||||
kickMessage = kickStringDefault.get();
|
||||
cacheResults = cacheResultsDefault.get();
|
||||
prefixWhitelists = prefixWhitelistsDefault.get();
|
||||
databaseEnabled = defaultDatabaseEnabled.get();
|
||||
useCredentials = defaultUseCredentials.get();
|
||||
databaseType = defaultDatabaseType.get();
|
||||
databaseName = defaultDatabaseName.get();
|
||||
mongoURL = defaultMongoURL.get();
|
||||
username = defaultUsername.get();
|
||||
password = defaultPassword.get();
|
||||
ip = defaultIp.get();
|
||||
port = defaultPort.get();
|
||||
commandsEnabled = defaultCommandsEnable.get();
|
||||
commands = defaultCommands.get();
|
||||
kickPlayers = defaultKickPlayers.get();
|
||||
alertToStaff = defaultAlertToStaff.get();
|
||||
alertMsg = defaultAlertMsg.get();
|
||||
metrics = defaultMetrics.get();
|
||||
countryList = defCountrylist.get();
|
||||
whitelistCountries = defaultWhitelistCountries.get();
|
||||
countryKickCommands = defCountryKickCommands.get();
|
||||
countryVanillaKickReason = defaultCountryKickReason.get();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,56 +1,147 @@
|
||||
package dev.brighten.antivpn.api;
|
||||
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
import dev.brighten.antivpn.utils.VPNResponse;
|
||||
import dev.brighten.antivpn.utils.StringUtil;
|
||||
import dev.brighten.antivpn.utils.Tuple;
|
||||
import dev.brighten.antivpn.utils.json.JSONException;
|
||||
import dev.brighten.antivpn.web.FunkemunkyAPI;
|
||||
import dev.brighten.antivpn.web.objects.VPNResponse;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public abstract class VPNExecutor {
|
||||
public static ExecutorService threadExecutor = Executors.newSingleThreadExecutor();
|
||||
public static ScheduledExecutorService threadExecutor = Executors.newScheduledThreadPool(2);
|
||||
|
||||
private static final Map<String, VPNResponse> responseCache = new HashMap<>();
|
||||
@Getter
|
||||
private final Set<UUID> whitelisted = Collections.synchronizedSet(new HashSet<>());
|
||||
@Getter
|
||||
private final Set<String> whitelistedIps = Collections.synchronizedSet(new HashSet<>());
|
||||
|
||||
@Getter
|
||||
private final List<Tuple<CheckResult, UUID>> toKick = Collections.synchronizedList(new LinkedList<>());
|
||||
|
||||
public abstract void registerListeners();
|
||||
|
||||
public abstract void runCacheReset();
|
||||
public abstract void log(Level level, String log, Object... objects);
|
||||
|
||||
public void resetCache() {
|
||||
responseCache.clear();
|
||||
public abstract void log(String log, Object... objects);
|
||||
|
||||
public abstract void logException(String message, Throwable ex);
|
||||
|
||||
public abstract void runCommand(String command);
|
||||
|
||||
public void logException(Throwable ex) {
|
||||
logException("An exception occurred: " + ex.getMessage(), ex);
|
||||
}
|
||||
|
||||
public abstract void shutdown();
|
||||
public void startKickChecks() {
|
||||
threadExecutor.scheduleAtFixedRate(() -> {
|
||||
synchronized (toKick) {
|
||||
if(toKick.isEmpty()) return;
|
||||
|
||||
public void checkIp(String ip, boolean cachedResults, Consumer<VPNResponse> result) {
|
||||
threadExecutor.execute(() -> result.accept(responseCache.compute(ip, (key, val) -> {
|
||||
if(val == null) {
|
||||
try {
|
||||
return AntiVPN.getVPNResponse(ip, AntiVPN.getInstance().getConfig().getLicense(), cachedResults);
|
||||
} catch (JSONException | IOException e) {
|
||||
e.printStackTrace();
|
||||
Iterator<Tuple<CheckResult, UUID>> i = toKick.iterator();
|
||||
|
||||
while(i.hasNext()) {
|
||||
var toCheck = i.next();
|
||||
|
||||
Optional<APIPlayer> player = AntiVPN.getInstance().getPlayerExecutor().getPlayer(toCheck.second());
|
||||
|
||||
if(player.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
handleKickingOfPlayer(toCheck.first(), player.get());
|
||||
|
||||
i.remove();
|
||||
}
|
||||
}
|
||||
|
||||
return val;
|
||||
})));
|
||||
}, 8, 2, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
public VPNResponse checkIp(String ip, boolean cachedResults) {
|
||||
return responseCache.compute(ip, (key, val) -> {
|
||||
if(val == null) {
|
||||
try {
|
||||
return AntiVPN.getVPNResponse(ip, AntiVPN.getInstance().getConfig().getLicense(), cachedResults);
|
||||
} catch (JSONException | IOException e) {
|
||||
e.printStackTrace();
|
||||
public void handleKickingOfPlayer(CheckResult result, APIPlayer player) {
|
||||
if (AntiVPN.getInstance().getVpnConfig().alertToStaff()) AntiVPN.getInstance().getPlayerExecutor()
|
||||
.getOnlinePlayers()
|
||||
.stream()
|
||||
.filter(APIPlayer::isAlertsEnabled)
|
||||
.forEach(pl ->
|
||||
pl.sendMessage(StringUtil.translateAlternateColorCodes('&',
|
||||
StringUtil.varReplace(dev.brighten.antivpn.AntiVPN.getInstance().getVpnConfig()
|
||||
.alertMessage(), player, result.response()))));
|
||||
|
||||
if(AntiVPN.getInstance().getVpnConfig().kickPlayersOnDetect()) {
|
||||
switch (result.resultType()) {
|
||||
case DENIED_PROXY -> player.kickPlayer(StringUtil.varReplace(AntiVPN.getInstance().getVpnConfig()
|
||||
.getKickString(), player, result.response()));
|
||||
case DENIED_COUNTRY -> player.kickPlayer(StringUtil.varReplace(AntiVPN.getInstance().getVpnConfig()
|
||||
.countryVanillaKickReason(), player, result.response()));
|
||||
}
|
||||
}
|
||||
|
||||
if(!AntiVPN.getInstance().getVpnConfig().runCommands()) return;
|
||||
|
||||
switch (result.resultType()) {
|
||||
case DENIED_PROXY -> {
|
||||
for (String command : AntiVPN.getInstance().getVpnConfig().commands()) {
|
||||
runCommand(StringUtil.translateAlternateColorCodes('&',
|
||||
StringUtil.varReplace(command, player, result.response())));
|
||||
}
|
||||
}
|
||||
|
||||
return val;
|
||||
});
|
||||
case DENIED_COUNTRY -> {
|
||||
for (String command : AntiVPN.getInstance().getVpnConfig().countryKickCommands()) {
|
||||
runCommand(StringUtil.translateAlternateColorCodes('&',
|
||||
StringUtil.varReplace(command, player, result.response())));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isWhitelisted(UUID uuid) {
|
||||
if(AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled()) {
|
||||
return AntiVPN.getInstance().getDatabase().isWhitelisted(uuid);
|
||||
}
|
||||
return whitelisted.contains(uuid);
|
||||
}
|
||||
|
||||
public boolean isWhitelisted(String ip) {
|
||||
if(AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled()) {
|
||||
return AntiVPN.getInstance().getDatabase().isWhitelisted(ip);
|
||||
}
|
||||
return whitelistedIps.contains(ip);
|
||||
}
|
||||
|
||||
public CompletableFuture<VPNResponse> checkIp(String ip) {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
Optional<VPNResponse> cachedRes = AntiVPN.getInstance().getDatabase().getStoredResponse(ip);
|
||||
|
||||
if(cachedRes.isPresent()) {
|
||||
return cachedRes.get();
|
||||
}
|
||||
else {
|
||||
try {
|
||||
VPNResponse response = FunkemunkyAPI
|
||||
.getVPNResponse(ip, AntiVPN.getInstance().getVpnConfig().getLicense(), true);
|
||||
|
||||
if (response.isSuccess()) {
|
||||
AntiVPN.getInstance().getDatabase().cacheResponse(response);
|
||||
} else {
|
||||
log("Query to VPN API failed! Reason: " + response.getFailureReason());
|
||||
}
|
||||
|
||||
return response;
|
||||
} catch (JSONException | IOException e) {
|
||||
log("Query to VPN API failed! Reason: " + e.getMessage());
|
||||
return VPNResponse.FAILED_RESPONSE;
|
||||
}
|
||||
}
|
||||
}, threadExecutor);
|
||||
}
|
||||
|
||||
public abstract void disablePlugin();
|
||||
}
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
package dev.brighten.antivpn.command;
|
||||
|
||||
import dev.brighten.antivpn.api.APIPlayer;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public interface CommandExecutor {
|
||||
|
||||
void sendMessage(String message);
|
||||
void sendMessage(String message, Object... objects);
|
||||
boolean hasPermission(String permission);
|
||||
Optional<APIPlayer> getPlayer();
|
||||
boolean isPlayer();
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
package dev.brighten.antivpn.command.impl;
|
||||
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
import dev.brighten.antivpn.api.APIPlayer;
|
||||
import dev.brighten.antivpn.command.Command;
|
||||
import dev.brighten.antivpn.command.CommandExecutor;
|
||||
import dev.brighten.antivpn.message.VpnString;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public class AlertsCommand extends Command {
|
||||
@Override
|
||||
public String permission() {
|
||||
return "antivpn.command.alerts";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "alerts";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] aliases() {
|
||||
return new String[] {"valerts", "vpnalerts"};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return "toggle VPN use alerts";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String usage() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String parent() {
|
||||
return "antivpn";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Command[] children() {
|
||||
return new Command[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public String execute(CommandExecutor executor, String[] args) {
|
||||
Optional<APIPlayer> pgetter = executor.getPlayer();
|
||||
if(!pgetter.isPresent()) return AntiVPN.getInstance().getMessageHandler()
|
||||
.getString("command-misc-playerRequired").getMessage();
|
||||
|
||||
APIPlayer player = pgetter.get();
|
||||
|
||||
player.setAlertsEnabled(!player.isAlertsEnabled());
|
||||
player.updateAlertsState();
|
||||
|
||||
return AntiVPN.getInstance().getMessageHandler().getString("command-alerts-toggled")
|
||||
.getFormattedMessage(new VpnString.Var<>("state", player.isAlertsEnabled()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> tabComplete(CommandExecutor executor, String alias, String[] args) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
package dev.brighten.antivpn.command.impl;
|
||||
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
import dev.brighten.antivpn.api.APIPlayer;
|
||||
import dev.brighten.antivpn.command.Command;
|
||||
import dev.brighten.antivpn.command.CommandExecutor;
|
||||
import dev.brighten.antivpn.utils.MiscUtils;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class AllowlistCommand extends Command {
|
||||
|
||||
private static final String[] secondArgs = new String[] {"add", "remove"};
|
||||
|
||||
@Override
|
||||
public String permission() {
|
||||
return "antivpn.command.allowlist";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "allowlist";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] aliases() {
|
||||
return new String[] {"whitelist"};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return "Add/remove players to/from exemption list.";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String usage() {
|
||||
return "<add/remove> <player/uuid/ip>";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String parent() {
|
||||
return "antivpn";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Command[] children() {
|
||||
return new Command[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public String execute(CommandExecutor executor, String[] args) {
|
||||
if(args.length == 0 || Arrays.stream(secondArgs).noneMatch(arg -> arg.equalsIgnoreCase(args[0]))) {
|
||||
return "&cUsage: /antivpn allowlist " + usage();
|
||||
}
|
||||
|
||||
if(args.length == 1)
|
||||
return "&cYou have to provide a player to allow or deny exemption.";
|
||||
|
||||
boolean databaseEnabled = AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled();
|
||||
|
||||
if(!databaseEnabled) executor.sendMessage("&cThe database is currently not setup, " +
|
||||
"so any changes here will disappear after a restart.");
|
||||
|
||||
if(MiscUtils.isIpv4(args[1])) {
|
||||
if(!databaseEnabled) {
|
||||
switch(args[0].toLowerCase()) {
|
||||
case "add":
|
||||
case "insert": {
|
||||
AntiVPN.getInstance().getExecutor().getWhitelistedIps().add(args[1]);
|
||||
AntiVPN.getInstance().getDatabase().setWhitelisted(args[1], true);
|
||||
return String.format("&aAdded &6%s &ato the exemption allowlist.", args[1]);
|
||||
}
|
||||
case "remove":
|
||||
case "delete": {
|
||||
AntiVPN.getInstance().getExecutor().getWhitelistedIps().remove(args[1]);
|
||||
AntiVPN.getInstance().getDatabase().setWhitelisted(args[1], false);
|
||||
return String.format("&cRemoved &6%s &cfrom the exemption allowlist.", args[1]);
|
||||
}
|
||||
default: {
|
||||
return "&c\"" + args[0] + "\" is not a valid argument";
|
||||
}
|
||||
}
|
||||
} else {
|
||||
switch(args[0].toLowerCase()) {
|
||||
case "add":
|
||||
case "insert": {
|
||||
AntiVPN.getInstance().getDatabase().setWhitelisted(args[1], true);
|
||||
return String.format("&aAdded &6%s &a to the exemption allowlist.", args[1]);
|
||||
}
|
||||
case "remove":
|
||||
case "delete": {
|
||||
AntiVPN.getInstance().getDatabase().setWhitelisted(args[1], false);
|
||||
return String.format("&cRemoved &6%s &c from the exemption allowlist.", args[1]);
|
||||
}
|
||||
default: {
|
||||
return "&c\"" + args[0] + "\" is not a valid argument";
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
UUID uuid = null;
|
||||
try {
|
||||
uuid = UUID.fromString(args[1]);
|
||||
} catch(IllegalArgumentException e) {
|
||||
Optional<APIPlayer> player = AntiVPN.getInstance().getPlayerExecutor().getPlayer(args[1]);
|
||||
|
||||
if(!player.isPresent()) {
|
||||
return "&cThe player \"" + args[1] + "\" is not online, so please provide a UUID.";
|
||||
}
|
||||
|
||||
uuid = player.get().getUuid();
|
||||
}
|
||||
|
||||
if(!databaseEnabled) {
|
||||
switch(args[0].toLowerCase()) {
|
||||
case "add": {
|
||||
AntiVPN.getInstance().getExecutor().getWhitelisted().add(uuid);
|
||||
return String.format("&aAdded &6%s &auuid to the exemption allowlist.", uuid.toString());
|
||||
}
|
||||
case "remove":
|
||||
case "delete": {
|
||||
AntiVPN.getInstance().getExecutor().getWhitelisted().remove(uuid);
|
||||
return String.format("&cRemoved &6%s &cuuid from the exemption allowlist.", uuid.toString());
|
||||
}
|
||||
default: {
|
||||
return "&c\"" + args[0] + "\" is not a valid argument";
|
||||
}
|
||||
}
|
||||
} else {
|
||||
switch(args[0].toLowerCase()) {
|
||||
case "add": {
|
||||
AntiVPN.getInstance().getDatabase().setWhitelisted(uuid, true);
|
||||
return String.format("&aAdded &6%s &auuid to the exemption allowlist.", uuid.toString());
|
||||
}
|
||||
case "remove":
|
||||
case "delete": {
|
||||
AntiVPN.getInstance().getDatabase().setWhitelisted(uuid, false);
|
||||
return String.format("&cRemoved &6%s &cuuid from the exemption allowlist.", uuid.toString());
|
||||
}
|
||||
default: {
|
||||
return "&c\"" + args[0] + "\" is not a valid argument";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> tabComplete(CommandExecutor executor, String alias, String[] args) {
|
||||
switch(args.length) {
|
||||
case 1: {
|
||||
return Arrays.stream(secondArgs)
|
||||
.filter(narg -> narg.toLowerCase().startsWith(args[0].toLowerCase()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
case 2: {
|
||||
return AntiVPN.getInstance().getPlayerExecutor().getOnlinePlayers().stream()
|
||||
.map(APIPlayer::getName)
|
||||
.filter(name -> name.toLowerCase().startsWith(args[1].toLowerCase()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
@@ -4,9 +4,11 @@ import dev.brighten.antivpn.AntiVPN;
|
||||
import dev.brighten.antivpn.command.Command;
|
||||
import dev.brighten.antivpn.command.CommandExecutor;
|
||||
import dev.brighten.antivpn.utils.StringUtil;
|
||||
import jdk.nashorn.internal.lookup.Lookup;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class AntiVPNCommand extends Command {
|
||||
@@ -42,7 +44,8 @@ public class AntiVPNCommand extends Command {
|
||||
|
||||
@Override
|
||||
public Command[] children() {
|
||||
return new Command[] {new LookupCommand()};
|
||||
return new Command[] {new LookupCommand(), new AllowlistCommand(), new AlertsCommand(),
|
||||
new ClearCacheCommand(), new PlanCommand(), new ReloadCommand()};
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -52,11 +55,17 @@ public class AntiVPNCommand extends Command {
|
||||
messages.add(StringUtil.line("&8"));
|
||||
messages.add("&6&lAntiVPN Help Page");
|
||||
messages.add("");
|
||||
for (Command child : AntiVPN.getInstance().getCommands()) {
|
||||
for (Command cmd : AntiVPN.getInstance().getCommands()) {
|
||||
messages.add(String.format("&8/&f%s &8- &7&o%s", "&7" + cmd.parent()
|
||||
+ (cmd.parent().length() > 0 ? " " : "") + "&f" + cmd.name() + " &7"
|
||||
+ cmd.usage(), cmd.description()));
|
||||
}
|
||||
for (Command child : children()) {
|
||||
messages.add(String.format("&8/&f%s &8- &7&o%s", "&7" + child.parent()
|
||||
+ (child.parent().length() > 0 ? " " : "") + "&f" + child.name() + " &7"
|
||||
+ child.usage(), description()));
|
||||
+ child.usage(), child.description()));
|
||||
}
|
||||
|
||||
messages.add(StringUtil.line("&8"));
|
||||
|
||||
return String.join("\n", messages);
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
package dev.brighten.antivpn.command.impl;
|
||||
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
import dev.brighten.antivpn.command.Command;
|
||||
import dev.brighten.antivpn.command.CommandExecutor;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class ClearCacheCommand extends Command {
|
||||
@Override
|
||||
public String permission() {
|
||||
return "antivpn.command.clearcache";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "clearcache";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] aliases() {
|
||||
return new String[] {"clear", "cc"};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return "Clear the API response cache if you're having problems.";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String usage() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String parent() {
|
||||
return "antivpn";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Command[] children() {
|
||||
return new Command[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public String execute(CommandExecutor executor, String[] args) {
|
||||
AntiVPN.getInstance().getDatabase().clearResponses();
|
||||
return "&aCleared all cached API response information!";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> tabComplete(CommandExecutor executor, String alias, String[] args) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,6 @@ import dev.brighten.antivpn.utils.StringUtil;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -56,28 +55,31 @@ public class LookupCommand extends Command {
|
||||
|
||||
Optional<APIPlayer> player = AntiVPN.getInstance().getPlayerExecutor().getPlayer(args[0]);
|
||||
|
||||
if(!player.isPresent()) {
|
||||
if(player.isEmpty()) {
|
||||
return String.format("&cNo player found with the name \"%s\"", args[0]);
|
||||
}
|
||||
|
||||
AntiVPN.getInstance().getExecutor().checkIp(player.get().getIp().getHostAddress(),
|
||||
false, result -> {
|
||||
if(!result.isSuccess()) {
|
||||
executor.sendMessage("&cThere was an error trying to find the information of this player.");
|
||||
} else {
|
||||
executor.sendMessage(StringUtil.line("&8"));
|
||||
executor.sendMessage("&6&l" + player.get().getName() + "&7&l's Connection Information");
|
||||
executor.sendMessage("");
|
||||
executor.sendMessage(String.format("&e%s&8: &f%s", "Proxy", result.isProxy()
|
||||
? "&a" + result.getMethod() : "&cNo"));
|
||||
executor.sendMessage(String.format("&e%s&8: &f%s", "ISP", result.getIsp()));
|
||||
executor.sendMessage(String.format("&e%s&8: &f%s", "Country", result.getCountryName()));
|
||||
executor.sendMessage(String.format("&e%s&8: &f%s", "City", result.getCity()));
|
||||
executor.sendMessage(String.format("&e%s&8: &f%s", "Coordinates", result.getLatitude()
|
||||
+ "&7/&f" + result.getLongitude()));
|
||||
executor.sendMessage(StringUtil.line("&8"));
|
||||
}
|
||||
});
|
||||
AntiVPN.getInstance().getExecutor()
|
||||
.checkIp(player.get().getIp().getHostAddress())
|
||||
.thenAccept(result -> {
|
||||
if(!result.isSuccess()) {
|
||||
executor.sendMessage("&cThere was an error trying to find the " +
|
||||
"information of this player.");
|
||||
return;
|
||||
}
|
||||
|
||||
executor.sendMessage(StringUtil.line("&8"));
|
||||
executor.sendMessage("&6&l" + player.get().getName() + "&7&l's Connection Information");
|
||||
executor.sendMessage("");
|
||||
executor.sendMessage("&e%s&8: &f%s", "Proxy", result.isProxy()
|
||||
? "&a" + result.getMethod() : "&cNo");
|
||||
executor.sendMessage("&e%s&8: &f%s", "ISP", result.getIsp());
|
||||
executor.sendMessage("&e%s&8: &f%s", "Country", result.getCountryName());
|
||||
executor.sendMessage("&e%s&8: &f%s", "City", result.getCity());
|
||||
executor.sendMessage("&e%s&8: &f%s", "Coordinates", result.getLatitude()
|
||||
+ "&7/&f" + result.getLongitude());
|
||||
executor.sendMessage(StringUtil.line("&8"));
|
||||
});
|
||||
|
||||
|
||||
return "&7Looking up the IP information for player " + player.get().getName() + "...";
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
package dev.brighten.antivpn.command.impl;
|
||||
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
import dev.brighten.antivpn.api.VPNExecutor;
|
||||
import dev.brighten.antivpn.command.Command;
|
||||
import dev.brighten.antivpn.command.CommandExecutor;
|
||||
import dev.brighten.antivpn.utils.StringUtil;
|
||||
import dev.brighten.antivpn.utils.json.JSONException;
|
||||
import dev.brighten.antivpn.web.FunkemunkyAPI;
|
||||
import dev.brighten.antivpn.web.objects.QueryResponse;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class PlanCommand extends Command {
|
||||
@Override
|
||||
public String permission() {
|
||||
return "antivpn.command.plan";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "plan";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] aliases() {
|
||||
return new String[] {"queries", "query"};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return "Info related to KauriVPN Plan";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String usage() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String parent() {
|
||||
return "antivpn";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Command[] children() {
|
||||
return new Command[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public String execute(CommandExecutor executor, String[] args) {
|
||||
VPNExecutor.threadExecutor.execute(() -> {
|
||||
QueryResponse result;
|
||||
try {
|
||||
if(AntiVPN.getInstance().getVpnConfig().getLicense().isEmpty()) {
|
||||
result = FunkemunkyAPI.getQueryResponse();
|
||||
} else {
|
||||
result = FunkemunkyAPI.getQueryResponse(AntiVPN.getInstance().getVpnConfig().getLicense());
|
||||
|
||||
if(!result.isValidPlan()) {
|
||||
executor.sendMessage("&cThe license &f%s &cis not a valid license, " +
|
||||
"checking your Free plan information...",
|
||||
AntiVPN.getInstance().getVpnConfig().getLicense());
|
||||
|
||||
result = FunkemunkyAPI.getQueryResponse();
|
||||
}
|
||||
}
|
||||
|
||||
String plan = result.getPlanType();
|
||||
if(plan.equals("IP")) plan+= " (Free)";
|
||||
|
||||
String queryMax = result.getQueriesMax() == Long.MAX_VALUE
|
||||
? "Unlimited" : String.valueOf(result.getQueriesMax());
|
||||
|
||||
executor.sendMessage(StringUtil.line("&8"));
|
||||
executor.sendMessage("&6&lKauriVPN Plan Information");
|
||||
executor.sendMessage("");
|
||||
executor.sendMessage("&e%s&8: &f%s", "Plan", plan);
|
||||
executor.sendMessage("&e%s&8: &f%s&7/&f%s", "Queries Used",
|
||||
result.getQueries(), queryMax);
|
||||
executor.sendMessage(StringUtil.line("&8"));
|
||||
} catch(JSONException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException(e);
|
||||
executor.sendMessage("&cThere was a JSONException thrown while looking up your query " +
|
||||
"information. Check console for more details.");
|
||||
} catch (IOException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException(e);
|
||||
executor.sendMessage("&cThere was a IOException thrown while looking up your query " +
|
||||
"information. Check console for more details.");
|
||||
}
|
||||
});
|
||||
return "&7Looking up your query information...";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> tabComplete(CommandExecutor executor, String alias, String[] args) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package dev.brighten.antivpn.command.impl;
|
||||
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
import dev.brighten.antivpn.command.Command;
|
||||
import dev.brighten.antivpn.command.CommandExecutor;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class ReloadCommand extends Command {
|
||||
@Override
|
||||
public String permission() {
|
||||
return "antivpn.command.reload";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "reload";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] aliases() {
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return "Reload the plugin";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String usage() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String parent() {
|
||||
return "antivpn";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Command[] children() {
|
||||
return new Command[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public String execute(CommandExecutor executor, String[] args) {
|
||||
// Loading changes from the config.yml
|
||||
AntiVPN.getInstance().reloadConfig();
|
||||
|
||||
// Updating the cache of these values in VPNConfig
|
||||
AntiVPN.getInstance().getVpnConfig().update();
|
||||
|
||||
AntiVPN.getInstance().getMessageHandler().reloadStrings();
|
||||
|
||||
AntiVPN.getInstance().reloadDatabase();
|
||||
|
||||
return AntiVPN.getInstance().getMessageHandler().getString("command-reload-complete").getMessage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> tabComplete(CommandExecutor executor, String alias, String[] args) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package dev.brighten.antivpn.database;
|
||||
|
||||
import dev.brighten.antivpn.web.objects.VPNResponse;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public interface VPNDatabase {
|
||||
Optional<VPNResponse> getStoredResponse(String ip);
|
||||
|
||||
void cacheResponse(VPNResponse toCache);
|
||||
|
||||
void deleteResponse(String ip);
|
||||
|
||||
boolean isWhitelisted(UUID uuid);
|
||||
|
||||
boolean isWhitelisted(String ip);
|
||||
|
||||
void setWhitelisted(UUID uuid, boolean whitelisted);
|
||||
|
||||
void setWhitelisted(String ip, boolean whitelisted);
|
||||
|
||||
List<UUID> getAllWhitelisted();
|
||||
|
||||
List<String> getAllWhitelistedIps();
|
||||
|
||||
void getStoredResponseAsync(String ip, Consumer<Optional<VPNResponse>> result);
|
||||
|
||||
void isWhitelistedAsync(UUID uuid, Consumer<Boolean> result);
|
||||
|
||||
void isWhitelistedAsync(String ip, Consumer<Boolean> result);
|
||||
|
||||
void alertsState(UUID uuid, Consumer<Boolean> result);
|
||||
|
||||
void updateAlertsState(UUID uuid, boolean state);
|
||||
|
||||
void clearResponses();
|
||||
|
||||
void init();
|
||||
|
||||
void shutdown();
|
||||
}
|
||||
@@ -0,0 +1,283 @@
|
||||
package dev.brighten.antivpn.database.local;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.Cache;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
import dev.brighten.antivpn.api.VPNExecutor;
|
||||
import dev.brighten.antivpn.database.VPNDatabase;
|
||||
import dev.brighten.antivpn.database.sql.utils.MySQL;
|
||||
import dev.brighten.antivpn.database.sql.utils.Query;
|
||||
import dev.brighten.antivpn.web.objects.VPNResponse;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class H2VPN implements VPNDatabase {
|
||||
|
||||
private final Cache<String, VPNResponse> cachedResponses = Caffeine.newBuilder()
|
||||
.expireAfterWrite(20, TimeUnit.MINUTES)
|
||||
.maximumSize(4000)
|
||||
.build();
|
||||
|
||||
|
||||
public H2VPN() {
|
||||
VPNExecutor.threadExecutor.scheduleAtFixedRate(() -> {
|
||||
if(!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed()) return;
|
||||
|
||||
//Refreshing whitelisted players
|
||||
AntiVPN.getInstance().getExecutor().getWhitelisted().clear();
|
||||
AntiVPN.getInstance().getExecutor().getWhitelisted()
|
||||
.addAll(AntiVPN.getInstance().getDatabase().getAllWhitelisted());
|
||||
|
||||
//Refreshing whitlisted IPs
|
||||
AntiVPN.getInstance().getExecutor().getWhitelistedIps().clear();
|
||||
AntiVPN.getInstance().getExecutor().getWhitelistedIps()
|
||||
.addAll(AntiVPN.getInstance().getDatabase().getAllWhitelistedIps());
|
||||
}, 2, 30, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<VPNResponse> getStoredResponse(String ip) {
|
||||
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled()|| MySQL.isClosed())
|
||||
return Optional.empty();
|
||||
|
||||
VPNResponse response = cachedResponses.get(ip, ip2 -> {
|
||||
try(ResultSet rs = Query.prepare("select * from `responses` where `ip` = ? limit 1").append(ip).
|
||||
executeQuery()) {
|
||||
if (rs != null && rs.next()) {
|
||||
return new VPNResponse(rs.getString("asn"), rs.getString("ip"),
|
||||
rs.getString("countryName"), rs.getString("countryCode"),
|
||||
rs.getString("city"), rs.getString("timeZone"),
|
||||
rs.getString("method"), rs.getString("isp"), "N/A",
|
||||
rs.getBoolean("proxy"), rs.getBoolean("cached"), true,
|
||||
rs.getDouble("latitude"), rs.getDouble("longitude"),
|
||||
rs.getTimestamp("inserted").getTime(), -1);
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException("There was a problem getting a response for "
|
||||
+ ip, e);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
return Optional.ofNullable(response);
|
||||
}
|
||||
|
||||
/*
|
||||
* Query.
|
||||
* prepare("create table if not exists `responses` (`ip` varchar(45) not null, "
|
||||
* +
|
||||
* "`countryName` varchar(64), `countryCode` varchar(10), `city` varchar(64), `timeZone` varchar(64), "
|
||||
* +
|
||||
* "`method` varchar(32), `isp` varchar(32), `proxy` boolean, `cached` boolean "
|
||||
* + "`latitude` double, `longitude` double)");
|
||||
*/
|
||||
@Override
|
||||
public void cacheResponse(VPNResponse toCache) {
|
||||
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed())
|
||||
return;
|
||||
|
||||
cachedResponses.put(toCache.getIp(), toCache);
|
||||
|
||||
Query.prepare("insert into `responses` (`ip`,`asn`,`countryName`,`countryCode`,`city`,`timeZone`,"
|
||||
+ "`method`,`isp`,`proxy`,`cached`,`inserted`,`latitude`,`longitude`) values (?,?,?,?,?,?,?,?,?,?,?,?,?)")
|
||||
.append(toCache.getIp()).append(toCache.getAsn()).append(toCache.getCountryName())
|
||||
.append(toCache.getCountryCode()).append(toCache.getCity()).append(toCache.getTimeZone())
|
||||
.append(toCache.getMethod()).append(toCache.getIsp()).append(toCache.isProxy())
|
||||
.append(toCache.isCached()).append(new Timestamp(System.currentTimeMillis()))
|
||||
.append(toCache.getLatitude()).append(toCache.getLongitude()).execute();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteResponse(String ip) {
|
||||
if(!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed())
|
||||
return;
|
||||
|
||||
Query.prepare("delete from `responses` where `ip` = ?").append(ip).execute();
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public boolean isWhitelisted(UUID uuid) {
|
||||
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed())
|
||||
return false;
|
||||
ResultSet set = Query.prepare("select uuid from `whitelisted` where `uuid` = ? limit 1")
|
||||
.append(uuid.toString()).executeQuery();
|
||||
|
||||
return set != null && set.next() && set.getString("uuid") != null;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public boolean isWhitelisted(String ip) {
|
||||
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed())
|
||||
return false;
|
||||
ResultSet set = Query.prepare("select `ip` from `whitelisted-ips` where `ip` = ? limit 1")
|
||||
.append(ip).executeQuery();
|
||||
|
||||
|
||||
return set != null && set.next() && set.getString("ip") != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWhitelisted(UUID uuid, boolean whitelisted) {
|
||||
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed())
|
||||
return;
|
||||
|
||||
if (whitelisted) {
|
||||
if (!isWhitelisted(uuid)) {
|
||||
Query.prepare("insert into `whitelisted` (`uuid`) values (?)").append(uuid.toString()).execute();
|
||||
}
|
||||
AntiVPN.getInstance().getExecutor().getWhitelisted().add(uuid);
|
||||
} else {
|
||||
Query.prepare("delete from `whitelisted` where `uuid` = ?").append(uuid.toString()).execute();
|
||||
AntiVPN.getInstance().getExecutor().getWhitelisted().remove(uuid);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWhitelisted(String ip, boolean whitelisted) {
|
||||
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed())
|
||||
return;
|
||||
|
||||
if(whitelisted) {
|
||||
if(!isWhitelisted(ip)) {
|
||||
Query.prepare("insert into `whitelisted-ips` (`ip`) values (?)").append(ip).execute();
|
||||
}
|
||||
AntiVPN.getInstance().getExecutor().getWhitelistedIps().add(ip);
|
||||
} else {
|
||||
Query.prepare("delete from `whitelisted-ips` where `ip` = ?").append(ip).execute();
|
||||
AntiVPN.getInstance().getExecutor().getWhitelistedIps().remove(ip);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UUID> getAllWhitelisted() {
|
||||
List<UUID> uuids = new ArrayList<>();
|
||||
|
||||
if(!MySQL.isClosed()) Query.prepare("select uuid from `whitelisted`")
|
||||
.execute(set -> uuids.add(UUID.fromString(set.getString("uuid"))));
|
||||
|
||||
return uuids;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getAllWhitelistedIps() {
|
||||
List<String> ips = new ArrayList<>();
|
||||
|
||||
if(!MySQL.isClosed()) Query.prepare("select `ip` from `whitelisted-ips`")
|
||||
.execute(set -> ips.add(set.getString("ip")));
|
||||
|
||||
return ips;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getStoredResponseAsync(String ip, Consumer<Optional<VPNResponse>> result) {
|
||||
if(MySQL.isClosed()) return;
|
||||
|
||||
VPNExecutor.threadExecutor.execute(() -> result.accept(getStoredResponse(ip)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void isWhitelistedAsync(UUID uuid, Consumer<Boolean> result) {
|
||||
if(MySQL.isClosed()) return;
|
||||
|
||||
VPNExecutor.threadExecutor.execute(() -> result.accept(isWhitelisted(uuid)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void isWhitelistedAsync(String ip, Consumer<Boolean> result) {
|
||||
if(MySQL.isClosed()) return;
|
||||
|
||||
VPNExecutor.threadExecutor.execute(() -> result.accept(isWhitelisted(ip)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void alertsState(UUID uuid, Consumer<Boolean> result) {
|
||||
if(MySQL.isClosed()) return;
|
||||
|
||||
VPNExecutor.threadExecutor.execute(() -> {
|
||||
|
||||
try(ResultSet set = Query.prepare("select * from `alerts` where `uuid` = ? limit 1")
|
||||
.append(uuid.toString()).executeQuery()) {
|
||||
result.accept(set != null && set.next() && set.getString("uuid") != null);
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException("There was a problem getting alerts state for " + uuid, e);
|
||||
result.accept(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateAlertsState(UUID uuid, boolean enabled) {
|
||||
if(MySQL.isClosed()) return;
|
||||
|
||||
if(enabled) {
|
||||
//We want to make sure there isn't already a uuid inserted to prevent double insertions
|
||||
alertsState(uuid, alreadyEnabled -> { //No need to make another thread execute, already async
|
||||
if(!alreadyEnabled) {
|
||||
Query.prepare("insert into `alerts` (`uuid`) values (?)").append(uuid.toString())
|
||||
.execute();
|
||||
} //No need to insert again of already enabled
|
||||
});
|
||||
//Removing any uuid from the alerts table will disable alerts globally.
|
||||
} else VPNExecutor.threadExecutor.execute(() ->
|
||||
Query.prepare("delete from `alerts` where `uuid` = ?")
|
||||
.append(uuid.toString())
|
||||
.execute());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearResponses() {
|
||||
if(MySQL.isClosed()) return;
|
||||
|
||||
VPNExecutor.threadExecutor.execute(() -> Query.prepare("delete from `responses`").execute());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled())
|
||||
return;
|
||||
AntiVPN.getInstance().getExecutor().log("Initializing H2...");
|
||||
MySQL.initH2();
|
||||
|
||||
AntiVPN.getInstance().getExecutor().log("Creating tables...");
|
||||
|
||||
//Running check for old table types to update
|
||||
|
||||
Query.prepare("create table if not exists `whitelisted` (`uuid` varchar(36) not null)").execute();
|
||||
Query.prepare("create table if not exists `whitelisted-ips` (`ip` varchar(45) not null)").execute();
|
||||
Query.prepare("create table if not exists `responses` (`ip` varchar(45) not null, `asn` varchar(12),"
|
||||
+ "`countryName` text, `countryCode` varchar(10), `city` text, `timeZone` varchar(64), "
|
||||
+ "`method` varchar(32), `isp` text, `proxy` boolean, `cached` boolean, `inserted` timestamp,"
|
||||
+ "`latitude` double, `longitude` double)").execute();
|
||||
Query.prepare("create table if not exists `alerts` (`uuid` varchar(36) not null)").execute();
|
||||
|
||||
AntiVPN.getInstance().getExecutor().log("Creating indexes...");
|
||||
try {
|
||||
Query.prepare("create index if not exists `uuid_1` on `whitelisted` (`uuid`)").execute();
|
||||
Query.prepare("create index if not exists `ip_1` on `responses` (`ip`)").execute();
|
||||
Query.prepare("create index if not exists `proxy_1` on `responses` (`proxy`)").execute();
|
||||
Query.prepare("create index if not exists `inserted_1` on `responses` (`inserted`)").execute();
|
||||
Query.prepare("create index if not exists `ip_1` on `whitelisted-ips` (`ip`)").execute();
|
||||
} catch (Exception e) {
|
||||
System.err.println("MySQL Excepton created" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled())
|
||||
return;
|
||||
|
||||
MySQL.shutdown();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,260 @@
|
||||
package dev.brighten.antivpn.database.mongo;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.Cache;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import com.mongodb.*;
|
||||
import com.mongodb.client.MongoClient;
|
||||
import com.mongodb.client.MongoClients;
|
||||
import com.mongodb.client.MongoCollection;
|
||||
import com.mongodb.client.MongoDatabase;
|
||||
import com.mongodb.client.model.Filters;
|
||||
import com.mongodb.client.model.Indexes;
|
||||
import com.mongodb.client.model.UpdateOptions;
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
import dev.brighten.antivpn.api.VPNExecutor;
|
||||
import dev.brighten.antivpn.database.VPNDatabase;
|
||||
import dev.brighten.antivpn.web.objects.VPNResponse;
|
||||
import org.bson.Document;
|
||||
import org.bson.conversions.Bson;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class MongoVPN implements VPNDatabase {
|
||||
|
||||
private MongoCollection<Document> settingsDocument, cacheDocument;
|
||||
private MongoClient client;
|
||||
|
||||
private final Cache<String, VPNResponse> cachedResponses = Caffeine.newBuilder()
|
||||
.expireAfterWrite(20, TimeUnit.MINUTES)
|
||||
.maximumSize(4000)
|
||||
.build();
|
||||
|
||||
public MongoVPN() {
|
||||
VPNExecutor.threadExecutor.scheduleAtFixedRate(() -> {
|
||||
if(!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled()) return;
|
||||
|
||||
//Refreshing whitelisted players
|
||||
AntiVPN.getInstance().getExecutor().getWhitelisted().clear();
|
||||
AntiVPN.getInstance().getExecutor().getWhitelisted()
|
||||
.addAll(AntiVPN.getInstance().getDatabase().getAllWhitelisted());
|
||||
|
||||
//Refreshing whitlisted IPs
|
||||
AntiVPN.getInstance().getExecutor().getWhitelistedIps().clear();
|
||||
AntiVPN.getInstance().getExecutor().getWhitelistedIps()
|
||||
.addAll(AntiVPN.getInstance().getDatabase().getAllWhitelistedIps());
|
||||
}, 2, 30, TimeUnit.SECONDS);
|
||||
}
|
||||
@Override
|
||||
public Optional<VPNResponse> getStoredResponse(String ip) {
|
||||
VPNResponse response = cachedResponses.get(ip, ip2 -> {
|
||||
Document rdoc = cacheDocument.find(Filters.eq("ip", ip)).first();
|
||||
|
||||
if(rdoc != null) {
|
||||
long lastUpdate = rdoc.get("lastAccess", 0L);
|
||||
|
||||
if(System.currentTimeMillis() - lastUpdate > TimeUnit.HOURS.toMillis(1)) {
|
||||
VPNExecutor.threadExecutor.execute(() -> deleteResponse(ip));
|
||||
return null;
|
||||
}
|
||||
|
||||
return VPNResponse.builder().asn(rdoc.getString("asn")).ip(ip)
|
||||
.countryName(rdoc.getString("countryName"))
|
||||
.countryCode(rdoc.getString("countryCode"))
|
||||
.city(rdoc.getString("city"))
|
||||
.isp(rdoc.getString("isp"))
|
||||
.method(rdoc.getString("method"))
|
||||
.timeZone(rdoc.getString("timeZone"))
|
||||
.proxy(rdoc.getBoolean("proxy"))
|
||||
.cached(rdoc.getBoolean("cached"))
|
||||
.success(true)
|
||||
.latitude(rdoc.getDouble("latitude"))
|
||||
.longitude(rdoc.getDouble("longitude"))
|
||||
.lastAccess(rdoc.get("lastAccess", 0L))
|
||||
.build();
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
|
||||
return Optional.ofNullable(response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cacheResponse(VPNResponse toCache) {
|
||||
Document rdoc = new Document("ip", toCache.getIp());
|
||||
|
||||
rdoc.put("asn", toCache.getAsn());
|
||||
rdoc.put("countryName", toCache.getCountryName());
|
||||
rdoc.put("countryCode", toCache.getCountryCode());
|
||||
rdoc.put("city", toCache.getCity());
|
||||
rdoc.put("isp", toCache.getIsp());
|
||||
rdoc.put("method", toCache.getMethod());
|
||||
rdoc.put("timeZone", toCache.getTimeZone());
|
||||
rdoc.put("proxy", toCache.isProxy());
|
||||
rdoc.put("cached", toCache.isCached());
|
||||
rdoc.put("success", toCache.isSuccess());
|
||||
rdoc.put("latitude", toCache.getLatitude());
|
||||
rdoc.put("longitude", toCache.getLongitude());
|
||||
rdoc.put("lastAccess", System.currentTimeMillis());
|
||||
|
||||
cachedResponses.put(toCache.getIp(), toCache);
|
||||
|
||||
VPNExecutor.threadExecutor.execute(() -> {
|
||||
Bson update = new Document("$set", rdoc);
|
||||
cacheDocument.updateOne(Filters.eq("ip", toCache.getIp()), update,
|
||||
new UpdateOptions().upsert(true));
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteResponse(String ip) {
|
||||
cacheDocument.deleteMany(Filters.eq("ip", ip));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWhitelisted(UUID uuid) {
|
||||
return settingsDocument
|
||||
.find(Filters.and(Filters.eq("setting", "whitelist"),
|
||||
Filters.eq("uuid", uuid.toString()))).first() != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWhitelisted(String ip) {
|
||||
return settingsDocument
|
||||
.find(Filters.and(Filters.eq("setting", "whitelist"),
|
||||
Filters.eq("ip", ip))).first() != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWhitelisted(UUID uuid, boolean whitelisted) {
|
||||
if(whitelisted) {
|
||||
Document wdoc = new Document("setting", "whitelist");
|
||||
wdoc.put("uuid", uuid.toString());
|
||||
AntiVPN.getInstance().getExecutor().getWhitelisted().add(uuid);
|
||||
VPNExecutor.threadExecutor.execute(() -> settingsDocument.insertOne(wdoc));
|
||||
} else {
|
||||
AntiVPN.getInstance().getExecutor().getWhitelisted().remove(uuid);
|
||||
VPNExecutor.threadExecutor.execute(() -> settingsDocument.deleteMany(Filters
|
||||
.and(
|
||||
Filters.eq("setting", "whitelist"),
|
||||
Filters.eq("uuid", uuid.toString()))));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWhitelisted(String ip, boolean whitelisted) {
|
||||
if(whitelisted) {
|
||||
Document wdoc = new Document("setting", "whitelist").append("ip", ip);
|
||||
|
||||
AntiVPN.getInstance().getExecutor().getWhitelistedIps().add(ip);
|
||||
VPNExecutor.threadExecutor.execute(() -> settingsDocument.insertOne(wdoc));
|
||||
} else {
|
||||
AntiVPN.getInstance().getExecutor().getWhitelistedIps().remove(ip);
|
||||
VPNExecutor.threadExecutor.execute(() -> settingsDocument.deleteMany(Filters
|
||||
.and(
|
||||
Filters.eq("setting", "whitelist"),
|
||||
Filters.eq("ip", ip))));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UUID> getAllWhitelisted() {
|
||||
List<UUID> uuids = new ArrayList<>();
|
||||
settingsDocument.find(Filters.and(Filters.eq("setting", "whitelist"),
|
||||
Filters.exists("uuid")))
|
||||
.forEach((Consumer<? super Document>) doc -> uuids.add(UUID.fromString(doc.getString("uuid"))));
|
||||
return uuids;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getAllWhitelistedIps() {
|
||||
List<String> ips = new ArrayList<>();
|
||||
settingsDocument.find(Filters.and(Filters.eq("setting", "whitelist"),
|
||||
Filters.exists("ip")))
|
||||
.forEach((Consumer<? super Document>) doc -> ips.add(doc.getString("ip")));
|
||||
return ips;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getStoredResponseAsync(String ip, Consumer<Optional<VPNResponse>> result) {
|
||||
VPNExecutor.threadExecutor.execute(() -> result.accept(getStoredResponse(ip)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void isWhitelistedAsync(UUID uuid, Consumer<Boolean> result) {
|
||||
VPNExecutor.threadExecutor.execute(() -> result.accept(isWhitelisted(uuid)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void isWhitelistedAsync(String ip, Consumer<Boolean> result) {
|
||||
VPNExecutor.threadExecutor.execute(() -> result.accept(isWhitelisted(ip)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void alertsState(UUID uuid, Consumer<Boolean> result) {
|
||||
VPNExecutor.threadExecutor.execute(() -> result.accept(settingsDocument
|
||||
.find(Filters.and(Filters.eq("setting", "alerts"),
|
||||
Filters.eq("uuid", uuid.toString()))).first() != null));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateAlertsState(UUID uuid, boolean state) {
|
||||
VPNExecutor.threadExecutor.execute(() -> {
|
||||
settingsDocument.deleteMany(Filters.and(Filters.eq("setting", "alerts"),
|
||||
Filters.eq("uuid", uuid.toString())));
|
||||
if(state) {
|
||||
Document adoc = new Document("setting", "alerts");
|
||||
|
||||
adoc.put("uuid", uuid.toString());
|
||||
settingsDocument.insertOne(adoc);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearResponses() {
|
||||
cacheDocument.deleteMany(Filters.exists("ip"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
if(!AntiVPN.getInstance().getVpnConfig().mongoDatabaseURL().isEmpty()) { //URL
|
||||
ConnectionString cs = new ConnectionString(AntiVPN.getInstance().getVpnConfig().mongoDatabaseURL());
|
||||
MongoClientSettings settings = MongoClientSettings.builder().applyConnectionString(cs).build();
|
||||
client = MongoClients.create(settings);
|
||||
} else {
|
||||
MongoClientSettings.Builder settingsBld = MongoClientSettings.builder().readPreference(ReadPreference.nearest())
|
||||
.applyToClusterSettings(builder -> builder.
|
||||
hosts(Collections.singletonList(
|
||||
new ServerAddress(
|
||||
AntiVPN.getInstance().getVpnConfig().getIp(),
|
||||
AntiVPN.getInstance().getVpnConfig().getPort())
|
||||
)));
|
||||
if(AntiVPN.getInstance().getVpnConfig().useDatabaseCreds()) {
|
||||
settingsBld.credential(MongoCredential
|
||||
.createCredential(AntiVPN.getInstance().getVpnConfig().getUsername(),
|
||||
AntiVPN.getInstance().getVpnConfig().getDatabaseName(),
|
||||
AntiVPN.getInstance().getVpnConfig().getPassword().toCharArray()));
|
||||
}
|
||||
|
||||
client = MongoClients.create(settingsBld.build());
|
||||
}
|
||||
MongoDatabase antivpnDatabase = client.getDatabase(AntiVPN.getInstance().getVpnConfig().getDatabaseName());
|
||||
|
||||
settingsDocument = antivpnDatabase.getCollection("settings");
|
||||
if(settingsDocument.listIndexes().first() == null) {
|
||||
AntiVPN.getInstance().getExecutor().log("Created index for settings collection!");
|
||||
settingsDocument.createIndex(Indexes.ascending("ip"));
|
||||
}
|
||||
cacheDocument = antivpnDatabase.getCollection("cache");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
settingsDocument = null;
|
||||
cacheDocument = null;
|
||||
client.close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,362 @@
|
||||
package dev.brighten.antivpn.database.sql;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.Cache;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
import dev.brighten.antivpn.api.VPNExecutor;
|
||||
import dev.brighten.antivpn.database.VPNDatabase;
|
||||
import dev.brighten.antivpn.database.sql.utils.MySQL;
|
||||
import dev.brighten.antivpn.database.sql.utils.Query;
|
||||
import dev.brighten.antivpn.web.objects.VPNResponse;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class MySqlVPN implements VPNDatabase {
|
||||
|
||||
private final Cache<String, VPNResponse> cachedResponses = Caffeine.newBuilder()
|
||||
.expireAfterWrite(20, TimeUnit.MINUTES)
|
||||
.maximumSize(4000)
|
||||
.build();
|
||||
|
||||
|
||||
public MySqlVPN() {
|
||||
VPNExecutor.threadExecutor.scheduleAtFixedRate(() -> {
|
||||
if(!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed()) return;
|
||||
|
||||
//Refreshing whitelisted players
|
||||
AntiVPN.getInstance().getExecutor().getWhitelisted().clear();
|
||||
AntiVPN.getInstance().getExecutor().getWhitelisted()
|
||||
.addAll(AntiVPN.getInstance().getDatabase().getAllWhitelisted());
|
||||
|
||||
//Refreshing whitlisted IPs
|
||||
AntiVPN.getInstance().getExecutor().getWhitelistedIps().clear();
|
||||
AntiVPN.getInstance().getExecutor().getWhitelistedIps()
|
||||
.addAll(AntiVPN.getInstance().getDatabase().getAllWhitelistedIps());
|
||||
}, 2, 30, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<VPNResponse> getStoredResponse(String ip) {
|
||||
if (isDisabled())
|
||||
return Optional.empty();
|
||||
|
||||
VPNResponse response = cachedResponses.get(ip, ip2 -> {
|
||||
try(ResultSet rs = Query.prepare("select * from `responses` where `ip` = ? limit 1").append(ip)
|
||||
.executeQuery()) {
|
||||
if (rs != null && rs.next()) {
|
||||
VPNResponse responseFromDoc = new VPNResponse(rs.getString("asn"),
|
||||
rs.getString("ip"),
|
||||
rs.getString("countryName"), rs.getString("countryCode"),
|
||||
rs.getString("city"), rs.getString("timeZone"),
|
||||
rs.getString("method"), rs.getString("isp"), "N/A",
|
||||
rs.getBoolean("proxy"), rs.getBoolean("cached"), true,
|
||||
rs.getDouble("latitude"), rs.getDouble("longitude"),
|
||||
rs.getTimestamp("inserted").getTime(), -1);
|
||||
|
||||
if(System.currentTimeMillis() - responseFromDoc.getLastAccess() > TimeUnit.HOURS.toMillis(1)) {
|
||||
VPNExecutor.threadExecutor.execute(() -> deleteResponse(ip));
|
||||
return null;
|
||||
}
|
||||
|
||||
return responseFromDoc;
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance().getExecutor()
|
||||
.logException("Failed to get response from cache due to SQL error for: " + ip, e);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
return Optional.ofNullable(response);
|
||||
}
|
||||
|
||||
/*
|
||||
* Query.
|
||||
* prepare("create table if not exists `responses` (`ip` varchar(45) not null, "
|
||||
* +
|
||||
* "`countryName` varchar(64), `countryCode` varchar(10), `city` varchar(64), `timeZone` varchar(64), "
|
||||
* +
|
||||
* "`method` varchar(32), `isp` varchar(32), `proxy` boolean, `cached` boolean "
|
||||
* + "`latitude` double, `longitude` double)");
|
||||
*/
|
||||
@Override
|
||||
public void cacheResponse(VPNResponse toCache) {
|
||||
if (isDisabled())
|
||||
return;
|
||||
|
||||
cachedResponses.put(toCache.getIp(), toCache);
|
||||
|
||||
Query.prepare("insert into `responses` (`ip`,`asn`,`countryName`,`countryCode`,`city`,`timeZone`,"
|
||||
+ "`method`,`isp`,`proxy`,`cached`,`inserted`,`latitude`,`longitude`) values (?,?,?,?,?,?,?,?,?,?,?,?,?)")
|
||||
.append(toCache.getIp()).append(toCache.getAsn()).append(toCache.getCountryName())
|
||||
.append(toCache.getCountryCode()).append(toCache.getCity()).append(toCache.getTimeZone())
|
||||
.append(toCache.getMethod()).append(toCache.getIsp()).append(toCache.isProxy())
|
||||
.append(toCache.isCached()).append(new Timestamp(System.currentTimeMillis()))
|
||||
.append(toCache.getLatitude()).append(toCache.getLongitude()).execute();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteResponse(String ip) {
|
||||
if(!isDisabled())
|
||||
return;
|
||||
|
||||
Query.prepare("delete from `responses` where `ip` = ?").append(ip).execute();
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public boolean isWhitelisted(UUID uuid) {
|
||||
if (isDisabled())
|
||||
return false;
|
||||
try(ResultSet set = Query.prepare("select uuid from `whitelisted` where `uuid` = ? limit 1")
|
||||
.append(uuid.toString()).executeQuery()) {
|
||||
return set != null && set.next() && set.getString("uuid") != null;
|
||||
}
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public boolean isWhitelisted(String ip) {
|
||||
if (isDisabled())
|
||||
return false;
|
||||
try(ResultSet set = Query.prepare("select `ip` from `whitelisted-ips` where `ip` = ? limit 1")
|
||||
.append(ip).executeQuery()) {
|
||||
return set != null && set.next() && set.getString("ip") != null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWhitelisted(UUID uuid, boolean whitelisted) {
|
||||
if (isDisabled())
|
||||
return;
|
||||
|
||||
if (whitelisted) {
|
||||
if (!isWhitelisted(uuid)) {
|
||||
Query.prepare("insert into `whitelisted` (`uuid`) values (?)").append(uuid.toString()).execute();
|
||||
}
|
||||
AntiVPN.getInstance().getExecutor().getWhitelisted().add(uuid);
|
||||
} else {
|
||||
Query.prepare("delete from `whitelisted` where `uuid` = ?").append(uuid.toString()).execute();
|
||||
AntiVPN.getInstance().getExecutor().getWhitelisted().remove(uuid);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWhitelisted(String ip, boolean whitelisted) {
|
||||
if (isDisabled())
|
||||
return;
|
||||
|
||||
if(whitelisted) {
|
||||
if(!isWhitelisted(ip)) {
|
||||
Query.prepare("insert into `whitelisted-ips` (`ip`) values (?)").append(ip).execute();
|
||||
}
|
||||
AntiVPN.getInstance().getExecutor().getWhitelistedIps().add(ip);
|
||||
} else {
|
||||
Query.prepare("delete from `whitelisted-ips` where `ip` = ?").append(ip).execute();
|
||||
AntiVPN.getInstance().getExecutor().getWhitelistedIps().remove(ip);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<UUID> getAllWhitelisted() {
|
||||
List<UUID> uuids = new ArrayList<>();
|
||||
|
||||
if(!MySQL.isClosed()) Query.prepare("select uuid from `whitelisted`")
|
||||
.execute(set -> uuids.add(UUID.fromString(set.getString("uuid"))));
|
||||
|
||||
return uuids;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getAllWhitelistedIps() {
|
||||
List<String> ips = new ArrayList<>();
|
||||
|
||||
if(!MySQL.isClosed()) Query.prepare("select `ip` from `whitelisted-ips`")
|
||||
.execute(set -> ips.add(set.getString("ip")));
|
||||
|
||||
return ips;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getStoredResponseAsync(String ip, Consumer<Optional<VPNResponse>> result) {
|
||||
if(MySQL.isClosed()) return;
|
||||
|
||||
VPNExecutor.threadExecutor.execute(() -> result.accept(getStoredResponse(ip)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void isWhitelistedAsync(UUID uuid, Consumer<Boolean> result) {
|
||||
if(MySQL.isClosed()) return;
|
||||
|
||||
VPNExecutor.threadExecutor.execute(() -> result.accept(isWhitelisted(uuid)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void isWhitelistedAsync(String ip, Consumer<Boolean> result) {
|
||||
if(MySQL.isClosed()) return;
|
||||
|
||||
VPNExecutor.threadExecutor.execute(() -> result.accept(isWhitelisted(ip)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void alertsState(UUID uuid, Consumer<Boolean> result) {
|
||||
if(MySQL.isClosed()) return;
|
||||
|
||||
VPNExecutor.threadExecutor.execute(() -> {
|
||||
|
||||
try(ResultSet set = Query.prepare("select * from `alerts` where `uuid` = ? limit 1")
|
||||
.append(uuid.toString()).executeQuery()) {
|
||||
result.accept(set != null && set.next() && set.getString("uuid") != null);
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance().getExecutor()
|
||||
.logException("Failed to get alerts state from database for: " + uuid, e);
|
||||
result.accept(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateAlertsState(UUID uuid, boolean enabled) {
|
||||
if(MySQL.isClosed()) return;
|
||||
|
||||
if(enabled) {
|
||||
//We want to make sure there isn't already a uuid inserted to prevent double insertions
|
||||
alertsState(uuid, alreadyEnabled -> { //No need to make another thread execute, already async
|
||||
if(!alreadyEnabled) {
|
||||
Query.prepare("insert into `alerts` (`uuid`) values (?)").append(uuid.toString())
|
||||
.execute();
|
||||
} //No need to insert again of already enabled
|
||||
});
|
||||
//Removing any uuid from the alerts table will disable alerts globally.
|
||||
} else VPNExecutor.threadExecutor.execute(() ->
|
||||
Query.prepare("delete from `alerts` where `uuid` = ?")
|
||||
.append(uuid.toString())
|
||||
.execute());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearResponses() {
|
||||
if(MySQL.isClosed()) return;
|
||||
|
||||
VPNExecutor.threadExecutor.execute(() -> Query.prepare("delete from `responses`").execute());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled())
|
||||
return;
|
||||
AntiVPN.getInstance().getExecutor().log("Initializing MySQL...");
|
||||
MySQL.init();
|
||||
|
||||
AntiVPN.getInstance().getExecutor().log("Creating tables...");
|
||||
|
||||
//Running check for old table types to update
|
||||
Query.prepare("select `DATA_TYPE` from INFORMATION_SCHEMA.COLUMNS " +
|
||||
"WHERE table_name = 'responses' AND COLUMN_NAME = 'isp';").execute(set -> {
|
||||
if(set.getObject("DATA_TYPE").toString().contains("varchar")) {
|
||||
AntiVPN.getInstance().getExecutor().log("Using old database format for storing responses! " +
|
||||
"Dropping table and creating a new one...");
|
||||
if(Query.prepare("drop table `responses`").execute() > 0) {
|
||||
AntiVPN.getInstance().getExecutor().log("Successfully dropped table!");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Query.prepare("create table if not exists `whitelisted` (`uuid` varchar(36) not null)").execute();
|
||||
Query.prepare("create table if not exists `whitelisted-ips` (`ip` varchar(45) not null)").execute();
|
||||
Query.prepare("create table if not exists `responses` (`ip` varchar(45) not null, `asn` varchar(12),"
|
||||
+ "`countryName` text, `countryCode` varchar(10), `city` text, `timeZone` varchar(64), "
|
||||
+ "`method` varchar(32), `isp` text, `proxy` boolean, `cached` boolean, `inserted` timestamp,"
|
||||
+ "`latitude` double, `longitude` double)").execute();
|
||||
Query.prepare("create table if not exists `alerts` (`uuid` varchar(36) not null)").execute();
|
||||
|
||||
AntiVPN.getInstance().getExecutor().log("Creating indexes...");
|
||||
try {
|
||||
// Ref:
|
||||
// https://dba.stackexchange.com/questions/24531/mysql-create-index-if-not-exists
|
||||
|
||||
String query = "SELECT COUNT(1) IndexExists FROM INFORMATION_SCHEMA.STATISTICS WHERE table_schema=DATABASE()" +
|
||||
" AND table_name='whitelisted' AND index_name='uuid_1';";
|
||||
int id = 0;
|
||||
try(ResultSet rs = Query.prepare(query).executeQuery()) {
|
||||
while (rs.next()) {
|
||||
id = rs.getInt("IndexExists");
|
||||
}
|
||||
if (id == 0) {
|
||||
Query.prepare("create index `uuid_1` on `whitelisted` (`uuid`)").execute();
|
||||
}
|
||||
}
|
||||
id = 0;
|
||||
|
||||
// Responses index
|
||||
query = "SELECT COUNT(1) IndexExists FROM INFORMATION_SCHEMA.STATISTICS WHERE table_schema=DATABASE() " +
|
||||
"AND table_name='responses' AND index_name='ip_1';";
|
||||
try(ResultSet rs = Query.prepare(query).executeQuery()) {
|
||||
while (rs.next()) {
|
||||
id = rs.getInt("IndexExists");
|
||||
}
|
||||
if (id == 0) {
|
||||
Query.prepare("create index `ip_1` on `responses` (`ip`)").execute();
|
||||
}
|
||||
id = 0;
|
||||
}
|
||||
|
||||
query = "SELECT COUNT(1) IndexExists FROM INFORMATION_SCHEMA.STATISTICS WHERE table_schema=DATABASE() " +
|
||||
"AND table_name='responses' AND index_name='proxy_1';";
|
||||
try(ResultSet rs = Query.prepare(query).executeQuery()) {
|
||||
while (rs.next()) {
|
||||
id = rs.getInt("IndexExists");
|
||||
}
|
||||
if (id == 0) {
|
||||
Query.prepare("create index `proxy_1` on `responses` (`proxy`)").execute();
|
||||
}
|
||||
id = 0;
|
||||
}
|
||||
query = "SELECT COUNT(1) IndexExists FROM INFORMATION_SCHEMA.STATISTICS WHERE table_schema=DATABASE()" +
|
||||
" AND table_name='responses' AND index_name='inserted_1';";
|
||||
try(ResultSet rs = Query.prepare(query).executeQuery()) {
|
||||
while (rs.next()) {
|
||||
id = rs.getInt("IndexExists");
|
||||
}
|
||||
if (id == 0) {
|
||||
Query.prepare("create index `inserted_1` on `responses` (`inserted`)").execute();
|
||||
}
|
||||
id = 0;
|
||||
}
|
||||
|
||||
//Whitelisted IPs index
|
||||
query = "SELECT COUNT(1) IndexExists FROM INFORMATION_SCHEMA.STATISTICS WHERE table_schema=DATABASE()" +
|
||||
" AND table_name='whitelisted-ips' AND index_name='ip_1';";
|
||||
try(ResultSet rs = Query.prepare(query).executeQuery()) {
|
||||
while (rs.next()) {
|
||||
id = rs.getInt("IndexExists");
|
||||
}
|
||||
if (id == 0) {
|
||||
Query.prepare("create index `ip_1` on `whitelisted-ips` (`ip`)").execute();
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.err.println("MySQL Excepton created" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isDisabled() {
|
||||
return !AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled()|| MySQL.isClosed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled())
|
||||
return;
|
||||
MySQL.shutdown();
|
||||
}
|
||||
}
|
||||
+138
@@ -0,0 +1,138 @@
|
||||
package dev.brighten.antivpn.database.sql.utils;
|
||||
|
||||
import dev.brighten.antivpn.utils.MiscUtils;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
import java.sql.*;
|
||||
import java.util.UUID;
|
||||
|
||||
public class ExecutableStatement {
|
||||
private PreparedStatement statement;
|
||||
private int pos = 1;
|
||||
|
||||
public ExecutableStatement(PreparedStatement statement) {
|
||||
this.statement = statement;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public Integer execute() {
|
||||
try {
|
||||
return statement.executeUpdate();
|
||||
} finally {
|
||||
MiscUtils.close(statement);
|
||||
}
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public void execute(ResultSetIterator iterator) {
|
||||
ResultSet rs = null;
|
||||
try {
|
||||
rs = statement.executeQuery();
|
||||
while (rs.next()) iterator.next(rs);
|
||||
} finally {
|
||||
MiscUtils.close(statement, rs);
|
||||
}
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public void executeSingle(ResultSetIterator iterator) {
|
||||
ResultSet rs = null;
|
||||
try {
|
||||
rs = statement.executeQuery();
|
||||
if (rs.next()) iterator.next(rs);
|
||||
else iterator.next(null);
|
||||
} finally {
|
||||
MiscUtils.close(statement, rs);
|
||||
}
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public ResultSet executeQuery() {
|
||||
return statement.executeQuery();
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public ExecutableStatement append(Object obj) {
|
||||
statement.setObject(pos++, obj);
|
||||
return this;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public ExecutableStatement append(String obj) {
|
||||
statement.setString(pos++, obj);
|
||||
return this;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public ExecutableStatement append(UUID uuid) {
|
||||
if (uuid != null) statement.setString(pos++, uuid.toString().replace("-", ""));
|
||||
else statement.setString(pos++, null);
|
||||
return this;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public ExecutableStatement append(Array obj) {
|
||||
statement.setArray(pos++, obj);
|
||||
return this;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public ExecutableStatement append(Integer obj) {
|
||||
statement.setInt(pos++, obj);
|
||||
return this;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public ExecutableStatement append(Short obj) {
|
||||
statement.setShort(pos++, obj);
|
||||
return this;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public ExecutableStatement append(Long obj) {
|
||||
statement.setLong(pos++, obj);
|
||||
return this;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public ExecutableStatement append(Float obj) {
|
||||
statement.setFloat(pos++, obj);
|
||||
return this;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public ExecutableStatement append(Double obj) {
|
||||
statement.setDouble(pos++, obj);
|
||||
return this;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public ExecutableStatement append(Date obj) {
|
||||
statement.setDate(pos++, obj);
|
||||
return this;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public ExecutableStatement append(Timestamp obj) {
|
||||
statement.setTimestamp(pos++, obj);
|
||||
return this;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public ExecutableStatement append(Time obj) {
|
||||
statement.setTime(pos++, obj);
|
||||
return this;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public ExecutableStatement append(Blob obj) {
|
||||
statement.setBlob(pos++, obj);
|
||||
return this;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public ExecutableStatement append(byte[] obj) {
|
||||
statement.setBytes(pos++, obj);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
package dev.brighten.antivpn.database.sql.utils;
|
||||
|
||||
import com.mysql.cj.jdbc.Driver;
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
import org.h2.jdbc.JdbcSQLFeatureNotSupportedException;
|
||||
import org.h2.jdbc.JdbcSQLNonTransientConnectionException;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Properties;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class MySQL {
|
||||
private static Connection conn;
|
||||
|
||||
public static void init() {
|
||||
try {
|
||||
if (conn == null || conn.isClosed()) {
|
||||
DriverManager.registerDriver(new Driver());
|
||||
conn = DriverManager.getConnection("jdbc:mysql://" + AntiVPN.getInstance().getVpnConfig().getIp()
|
||||
+ ":" + AntiVPN.getInstance().getVpnConfig().getPort()
|
||||
+ "/?useSSL=true&autoReconnect=true",
|
||||
AntiVPN.getInstance().getVpnConfig().getUsername(),
|
||||
AntiVPN.getInstance().getVpnConfig().getPassword());
|
||||
conn.setAutoCommit(true);
|
||||
Query.use(conn);
|
||||
Query.prepare("CREATE DATABASE IF NOT EXISTS `"
|
||||
+ AntiVPN.getInstance().getVpnConfig().getDatabaseName() + "`").execute();
|
||||
Query.prepare("USE `" + AntiVPN.getInstance().getVpnConfig().getDatabaseName() + "`").execute();
|
||||
AntiVPN.getInstance().getExecutor().log("Connection to MySQL has been established.");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
AntiVPN.getInstance().getExecutor().logException("Failed to load mysql: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean didRetry = false;
|
||||
|
||||
public static void initH2() {
|
||||
if(didRetry) {
|
||||
AntiVPN.getInstance().getExecutor().log(Level.WARNING,
|
||||
"Already attempted to retry H2 connection, skipping.");
|
||||
return;
|
||||
}
|
||||
File dataFolder = new File(AntiVPN.getInstance().getPluginFolder(), "databases");
|
||||
if (!dataFolder.exists() && dataFolder.mkdirs()) {
|
||||
AntiVPN.getInstance().getExecutor().log("Created database directory");
|
||||
}
|
||||
|
||||
File dbFile = new File(dataFolder, "database.mv.db");
|
||||
|
||||
File databaseFile = new File(dataFolder, "database");
|
||||
try {
|
||||
conn = new NonClosableConnection(new org.h2.jdbc.JdbcConnection("jdbc:h2:file:" +
|
||||
databaseFile.getAbsolutePath(),
|
||||
new Properties(), AntiVPN.getInstance().getVpnConfig().getUsername(),
|
||||
AntiVPN.getInstance().getVpnConfig().getPassword(), false));
|
||||
conn.setAutoCommit(true);
|
||||
Query.use(conn);
|
||||
AntiVPN.getInstance().getExecutor().log("Connection to H2 has been established.");
|
||||
} catch (SQLException ex) {
|
||||
AntiVPN.getInstance().getExecutor().logException("H2 exception on initialize", ex);
|
||||
if(ex instanceof JdbcSQLFeatureNotSupportedException
|
||||
|| ex instanceof JdbcSQLNonTransientConnectionException) {
|
||||
AntiVPN.getInstance().getExecutor()
|
||||
.log("H2 database file is incompatible with this version of AntiVPN. " +
|
||||
"Backing up old database file...");
|
||||
backupOldDB(dbFile, dataFolder);
|
||||
initH2();
|
||||
didRetry = true;
|
||||
} else {
|
||||
AntiVPN.getInstance().getExecutor().logException("Failed to load H2 database: " + ex.getCause().toString(), ex);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
AntiVPN.getInstance().getExecutor().logException("Failed to load H2 database: " + e.getMessage(), e);
|
||||
AntiVPN.getInstance().getExecutor().log(Level.INFO, "TIP: Try deleting the plugin folder and restarting your server!");
|
||||
}
|
||||
}
|
||||
|
||||
private static void backupOldDB(File dbFile, File dataFolder) {
|
||||
if (dbFile.exists()) {
|
||||
try {
|
||||
// Optional: Make backup first
|
||||
File backupDir = new File(dataFolder, "backups");
|
||||
if(backupDir.mkdirs()) {
|
||||
AntiVPN.getInstance().getExecutor().log("Created backup directory");
|
||||
} else {
|
||||
AntiVPN.getInstance().getExecutor().log("Backup directory already exists");
|
||||
}
|
||||
File backupFile = new File(backupDir, "database.mv.db.backup_" + System.currentTimeMillis());
|
||||
Files.copy(dbFile.toPath(), backupFile.toPath());
|
||||
|
||||
// Actually delete the file
|
||||
if (!dbFile.delete()) {
|
||||
// If normal delete fails, try force delete on JVM exit
|
||||
dbFile.deleteOnExit();
|
||||
AntiVPN.getInstance().getExecutor().log("Could not delete database file - will try again on shutdown");
|
||||
} else {
|
||||
AntiVPN.getInstance().getExecutor().log("Successfully deleted incompatible database file");
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
AntiVPN.getInstance().getExecutor().logException("Failed to handle database file", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void use() {
|
||||
try {
|
||||
init();
|
||||
} catch (Exception e) {
|
||||
AntiVPN.getInstance().getExecutor().logException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void shutdown() {
|
||||
try {
|
||||
if(conn != null && !conn.isClosed()) {
|
||||
if(conn instanceof NonClosableConnection) {
|
||||
((NonClosableConnection)conn).shutdown();
|
||||
} else conn.close();
|
||||
conn = null;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
AntiVPN.getInstance().getExecutor().logException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isClosed() {
|
||||
if(conn == null)
|
||||
return true;
|
||||
|
||||
try {
|
||||
return conn.isClosed();
|
||||
} catch (SQLException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException(e);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* This file is part of LuckPerms, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.database.sql.utils;
|
||||
|
||||
import java.sql.*;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
* A wrapper around a {@link Connection} which blocks usage of the default {@link #close()} method.
|
||||
*/
|
||||
public class NonClosableConnection implements Connection {
|
||||
private final Connection delegate;
|
||||
|
||||
public NonClosableConnection(Connection delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Actually {@link #close() closes} the underlying connection.
|
||||
*/
|
||||
public final void shutdown() throws SQLException {
|
||||
this.delegate.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void close() throws SQLException {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isWrapperFor(Class<?> iface) throws SQLException {
|
||||
return iface.isInstance(this.delegate) || this.delegate.isWrapperFor(iface);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public final <T> T unwrap(Class<T> iface) throws SQLException {
|
||||
if (iface.isInstance(this.delegate)) {
|
||||
return (T) this.delegate;
|
||||
}
|
||||
return this.delegate.unwrap(iface);
|
||||
}
|
||||
|
||||
// Forward to the delegate connection
|
||||
@Override public Statement createStatement() throws SQLException { return this.delegate.createStatement(); }
|
||||
@Override public PreparedStatement prepareStatement(String sql) throws SQLException { return this.delegate.prepareStatement(sql); }
|
||||
@Override public CallableStatement prepareCall(String sql) throws SQLException { return this.delegate.prepareCall(sql); }
|
||||
@Override public String nativeSQL(String sql) throws SQLException { return this.delegate.nativeSQL(sql); }
|
||||
@Override public void setAutoCommit(boolean autoCommit) throws SQLException { this.delegate.setAutoCommit(autoCommit); }
|
||||
@Override public boolean getAutoCommit() throws SQLException { return this.delegate.getAutoCommit(); }
|
||||
@Override public void commit() throws SQLException { this.delegate.commit(); }
|
||||
@Override public void rollback() throws SQLException { this.delegate.rollback(); }
|
||||
@Override public boolean isClosed() throws SQLException { return this.delegate.isClosed(); }
|
||||
@Override public DatabaseMetaData getMetaData() throws SQLException { return this.delegate.getMetaData(); }
|
||||
@Override public void setReadOnly(boolean readOnly) throws SQLException { this.delegate.setReadOnly(readOnly); }
|
||||
@Override public boolean isReadOnly() throws SQLException { return this.delegate.isReadOnly(); }
|
||||
@Override public void setCatalog(String catalog) throws SQLException { this.delegate.setCatalog(catalog); }
|
||||
@Override public String getCatalog() throws SQLException { return this.delegate.getCatalog(); }
|
||||
@Override public void setTransactionIsolation(int level) throws SQLException { this.delegate.setTransactionIsolation(level); }
|
||||
@Override public int getTransactionIsolation() throws SQLException { return this.delegate.getTransactionIsolation(); }
|
||||
@Override public SQLWarning getWarnings() throws SQLException { return this.delegate.getWarnings(); }
|
||||
@Override public void clearWarnings() throws SQLException { this.delegate.clearWarnings(); }
|
||||
@Override public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException { return this.delegate.createStatement(resultSetType, resultSetConcurrency); }
|
||||
@Override public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { return this.delegate.prepareStatement(sql, resultSetType, resultSetConcurrency); }
|
||||
@Override public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { return this.delegate.prepareCall(sql, resultSetType, resultSetConcurrency); }
|
||||
@Override public Map<String, Class<?>> getTypeMap() throws SQLException { return this.delegate.getTypeMap(); }
|
||||
@Override public void setTypeMap(Map<String, Class<?>> map) throws SQLException { this.delegate.setTypeMap(map); }
|
||||
@Override public void setHoldability(int holdability) throws SQLException { this.delegate.setHoldability(holdability); }
|
||||
@Override public int getHoldability() throws SQLException { return this.delegate.getHoldability(); }
|
||||
@Override public Savepoint setSavepoint() throws SQLException { return this.delegate.setSavepoint(); }
|
||||
@Override public Savepoint setSavepoint(String name) throws SQLException { return this.delegate.setSavepoint(name); }
|
||||
@Override public void rollback(Savepoint savepoint) throws SQLException { this.delegate.rollback(savepoint); }
|
||||
@Override public void releaseSavepoint(Savepoint savepoint) throws SQLException { this.delegate.releaseSavepoint(savepoint); }
|
||||
@Override public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { return this.delegate.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability); }
|
||||
@Override public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { return this.delegate.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability); }
|
||||
@Override public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { return this.delegate.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability); }
|
||||
@Override public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException { return this.delegate.prepareStatement(sql, autoGeneratedKeys); }
|
||||
@Override public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException { return this.delegate.prepareStatement(sql, columnIndexes); }
|
||||
@Override public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException { return this.delegate.prepareStatement(sql, columnNames); }
|
||||
@Override public Clob createClob() throws SQLException { return this.delegate.createClob(); }
|
||||
@Override public Blob createBlob() throws SQLException { return this.delegate.createBlob(); }
|
||||
@Override public NClob createNClob() throws SQLException { return this.delegate.createNClob(); }
|
||||
@Override public SQLXML createSQLXML() throws SQLException { return this.delegate.createSQLXML(); }
|
||||
@Override public boolean isValid(int timeout) throws SQLException { return this.delegate.isValid(timeout); }
|
||||
@Override public void setClientInfo(String name, String value) throws SQLClientInfoException { this.delegate.setClientInfo(name, value); }
|
||||
@Override public void setClientInfo(Properties properties) throws SQLClientInfoException { this.delegate.setClientInfo(properties); }
|
||||
@Override public String getClientInfo(String name) throws SQLException { return this.delegate.getClientInfo(name); }
|
||||
@Override public Properties getClientInfo() throws SQLException { return this.delegate.getClientInfo(); }
|
||||
@Override public Array createArrayOf(String typeName, Object[] elements) throws SQLException { return this.delegate.createArrayOf(typeName, elements); }
|
||||
@Override public Struct createStruct(String typeName, Object[] attributes) throws SQLException { return this.delegate.createStruct(typeName, attributes); }
|
||||
@Override public void setSchema(String schema) throws SQLException { this.delegate.setSchema(schema); }
|
||||
@Override public String getSchema() throws SQLException { return this.delegate.getSchema(); }
|
||||
@Override public void abort(Executor executor) throws SQLException { this.delegate.abort(executor); }
|
||||
@Override public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException { this.delegate.setNetworkTimeout(executor, milliseconds); }
|
||||
@Override public int getNetworkTimeout() throws SQLException { return this.delegate.getNetworkTimeout(); }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package dev.brighten.antivpn.database.sql.utils;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
|
||||
public class Query {
|
||||
private static Connection conn;
|
||||
|
||||
public static void use(Connection conn) {
|
||||
Query.conn = conn;
|
||||
}
|
||||
|
||||
public static ExecutableStatement prepare(String query) {
|
||||
try {
|
||||
return new ExecutableStatement(conn.prepareStatement(query));
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package dev.brighten.antivpn.database.sql.utils;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
|
||||
public interface ResultSetIterator {
|
||||
void next(ResultSet rs) throws Exception;
|
||||
}
|
||||
@@ -0,0 +1,371 @@
|
||||
/*
|
||||
* This file is part of helper, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.depends;
|
||||
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
import dev.brighten.antivpn.utils.NonnullByDefault;
|
||||
import dev.brighten.antivpn.utils.Supplier;
|
||||
import dev.brighten.antivpn.utils.Suppliers;
|
||||
import lombok.Getter;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.commons.ClassRemapper;
|
||||
import org.objectweb.asm.commons.Remapper;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.nio.file.Files;
|
||||
import java.util.*;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.jar.JarOutputStream;
|
||||
|
||||
/**
|
||||
* Resolves {@link MavenLibrary} annotations for a class, and loads the dependency
|
||||
* into the classloader.
|
||||
*/
|
||||
@SuppressWarnings("CallToPrintStackTrace")
|
||||
@NonnullByDefault
|
||||
public final class LibraryLoader {
|
||||
|
||||
@SuppressWarnings("Guava")
|
||||
private static final Supplier<URLClassLoaderAccess> URL_INJECTOR = AntiVPN.getInstance().getClass().getClassLoader() instanceof URLClassLoader ?
|
||||
Suppliers.memoize(() ->
|
||||
URLClassLoaderAccess.create((URLClassLoader) AntiVPN.getInstance().getClass().getClassLoader()))
|
||||
: null;
|
||||
|
||||
public static void loadAll(Object object) {
|
||||
if(URL_INJECTOR == null)
|
||||
return;
|
||||
loadAll(object.getClass());
|
||||
}
|
||||
|
||||
public static void loadAll(Class<?> clazz) {
|
||||
if(URL_INJECTOR == null)
|
||||
return;
|
||||
MavenLibrary[] libs = clazz.getDeclaredAnnotationsByType(MavenLibrary.class);
|
||||
|
||||
for (MavenLibrary lib : libs) {
|
||||
// Create relocations map if any are defined
|
||||
Map<String, String> relocations = new HashMap<>();
|
||||
for (Relocate relocate : lib.relocations()) {
|
||||
relocations.put(relocate.from().replace("\\", ""), relocate.to());
|
||||
}
|
||||
|
||||
load(lib.groupId().replace("\\", ""), lib.artifactId(), lib.version(), lib.repo().url(), relocations);
|
||||
}
|
||||
}
|
||||
|
||||
public static void load(String groupId, String artifactId, String version, String repoUrl,
|
||||
Map<String, String> relocations) {
|
||||
load(new Dependency(groupId, artifactId, version, repoUrl), relocations);
|
||||
}
|
||||
|
||||
public static void load(Dependency d, Map<String, String> relocations) {
|
||||
System.out.printf("Loading dependency %s:%s:%s from %s%n",
|
||||
d.getGroupId(), d.getArtifactId(), d.getVersion(), d.getRepoUrl());
|
||||
String name = d.getArtifactId() + "-" + d.getVersion();
|
||||
|
||||
// If we have relocations, add a suffix to identify the relocated version
|
||||
String fileName = name + ".jar";
|
||||
if (!relocations.isEmpty()) {
|
||||
fileName = name + "-relocated.jar";
|
||||
}
|
||||
|
||||
File saveLocation = new File(getLibFolder(), fileName);
|
||||
File originalJar = new File(getLibFolder(), name + ".jar");
|
||||
|
||||
// Download the original jar if it doesn't exist
|
||||
if (!originalJar.exists()) {
|
||||
try {
|
||||
System.out.println("Dependency '" + name +
|
||||
"' is not already in the libraries folder. Attempting to download...");
|
||||
URL url = d.getUrl();
|
||||
|
||||
try (InputStream is = url.openStream()) {
|
||||
Files.copy(is, originalJar.toPath());
|
||||
}
|
||||
System.out.println("Dependency '" + name + "' successfully downloaded.");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException("Unable to download dependency: " + d, e);
|
||||
}
|
||||
}
|
||||
|
||||
// If we have relocations, create a relocated jar
|
||||
if (!relocations.isEmpty() && !saveLocation.exists()) {
|
||||
try {
|
||||
System.out.println("Relocating packages for " + name + "...");
|
||||
relocateJar(originalJar, saveLocation, relocations);
|
||||
System.out.println("Successfully relocated packages for " + name);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException("Failed to relocate packages for dependency: " + d, e);
|
||||
}
|
||||
}
|
||||
|
||||
// Load the appropriate jar (original or relocated)
|
||||
File jarToLoad = relocations.isEmpty() ? originalJar : saveLocation;
|
||||
|
||||
if (!jarToLoad.exists()) {
|
||||
throw new RuntimeException("Unable to find dependency jar: " + jarToLoad.getAbsolutePath());
|
||||
}
|
||||
|
||||
try {
|
||||
URL_INJECTOR.get().addURL(jarToLoad.toURI().toURL());
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Unable to load dependency: " + jarToLoad, e);
|
||||
}
|
||||
|
||||
System.out.println("Loaded dependency '" + name + "' successfully.");
|
||||
}
|
||||
|
||||
private static void relocateJar(File sourceJar, File targetJar, Map<String, String> relocations)
|
||||
throws IOException {
|
||||
// Track service files to avoid duplicates
|
||||
Map<String, StringBuilder> serviceFiles = new HashMap<>();
|
||||
|
||||
try (JarFile jar = new JarFile(sourceJar);
|
||||
JarOutputStream jos = new JarOutputStream(Files.newOutputStream(targetJar.toPath()))) {
|
||||
|
||||
Enumeration<JarEntry> entries = jar.entries();
|
||||
|
||||
while (entries.hasMoreElements()) {
|
||||
JarEntry entry = entries.nextElement();
|
||||
String name = entry.getName();
|
||||
|
||||
// Skip directories
|
||||
if (entry.isDirectory()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try (InputStream is = jar.getInputStream(entry)) {
|
||||
if (name.startsWith("META-INF/services/")) {
|
||||
// Process service files but don't write yet
|
||||
processServiceFile(name, is, serviceFiles, relocations);
|
||||
} else if (name.endsWith(".class")) {
|
||||
// Relocate class file path as well as content
|
||||
String relocatedPath = relocateClassPath(name, relocations);
|
||||
|
||||
JarEntry newEntry = new JarEntry(relocatedPath);
|
||||
jos.putNextEntry(newEntry);
|
||||
|
||||
byte[] classBytes = readAllBytes(is);
|
||||
byte[] relocatedBytes = relocateClass(classBytes, relocations);
|
||||
jos.write(relocatedBytes);
|
||||
jos.closeEntry();
|
||||
} else {
|
||||
// Copy other files as-is
|
||||
JarEntry newEntry = new JarEntry(name);
|
||||
jos.putNextEntry(newEntry);
|
||||
copyStream(is, jos);
|
||||
jos.closeEntry();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now write all service files after processing
|
||||
for (Map.Entry<String, StringBuilder> entry : serviceFiles.entrySet()) {
|
||||
try {
|
||||
JarEntry serviceEntry = new JarEntry(entry.getKey());
|
||||
jos.putNextEntry(serviceEntry);
|
||||
jos.write(entry.getValue().toString().getBytes());
|
||||
jos.closeEntry();
|
||||
} catch (Exception e) {
|
||||
// Log but continue with other service files
|
||||
System.out.println("Warning: Could not write service file " +
|
||||
entry.getKey() + ": " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void processServiceFile(String name, InputStream is,
|
||||
Map<String, StringBuilder> serviceFiles,
|
||||
Map<String, String> relocations) throws IOException {
|
||||
// Read service file content
|
||||
String content = new String(readAllBytes(is));
|
||||
StringBuilder contentBuilder = serviceFiles.computeIfAbsent(name, k -> new StringBuilder());
|
||||
|
||||
// Process and relocate service implementations
|
||||
for (String line : content.split("\n")) {
|
||||
String trimmed = line.trim();
|
||||
if (!trimmed.isEmpty() && !trimmed.startsWith("#")) {
|
||||
for (Map.Entry<String, String> relocation : relocations.entrySet()) {
|
||||
if (trimmed.startsWith(relocation.getKey())) {
|
||||
trimmed = relocation.getValue() +
|
||||
trimmed.substring(relocation.getKey().length());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
contentBuilder.append(trimmed).append("\n");
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] relocateClass(byte[] classBytes, Map<String, String> relocations) {
|
||||
try {
|
||||
// Convert to slash notation for ASM
|
||||
Remapper prefixRemapper = getPrefixRemapper(relocations);
|
||||
|
||||
// Create custom ClassWriter to handle missing classes
|
||||
ClassReader reader = new ClassReader(classBytes);
|
||||
ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS) {
|
||||
@Override
|
||||
protected String getCommonSuperClass(String type1, String type2) {
|
||||
try {
|
||||
return super.getCommonSuperClass(type1, type2);
|
||||
} catch (RuntimeException e) {
|
||||
// Fall back to Object when classes can't be loaded
|
||||
return "java/lang/Object";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ClassVisitor visitor = new ClassRemapper(writer, prefixRemapper);
|
||||
|
||||
// Process class with remapper
|
||||
reader.accept(visitor, ClassReader.EXPAND_FRAMES);
|
||||
|
||||
return writer.toByteArray();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return classBytes;
|
||||
}
|
||||
}
|
||||
|
||||
private static Remapper getPrefixRemapper(Map<String, String> relocations) {
|
||||
Map<String, String> slashMappings = new HashMap<>();
|
||||
for (Map.Entry<String, String> entry : relocations.entrySet()) {
|
||||
String fromSlash = entry.getKey().replace('.', '/');
|
||||
String toSlash = entry.getValue().replace('.', '/');
|
||||
slashMappings.put(fromSlash, toSlash);
|
||||
}
|
||||
|
||||
// Create customized remapper for package prefixes
|
||||
return new Remapper() {
|
||||
@Override
|
||||
public String map(String typeName) {
|
||||
if (typeName == null) return null;
|
||||
|
||||
for (Map.Entry<String, String> entry : slashMappings.entrySet()) {
|
||||
String from = entry.getKey();
|
||||
String to = entry.getValue();
|
||||
|
||||
if (typeName.startsWith(from)) {
|
||||
return to + typeName.substring(from.length());
|
||||
}
|
||||
}
|
||||
return typeName;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static String relocateClassPath(String path, Map<String, String> relocations) {
|
||||
// Convert path to package format (replacing / with .)
|
||||
String packagePath = path.substring(0, path.length() - 6).replace('/', '.');
|
||||
|
||||
// Apply relocations
|
||||
for (Map.Entry<String, String> relocation : relocations.entrySet()) {
|
||||
if (packagePath.startsWith(relocation.getKey())) {
|
||||
packagePath = relocation.getValue() + packagePath.substring(relocation.getKey().length());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Convert back to path format
|
||||
return packagePath.replace('.', '/') + ".class";
|
||||
}
|
||||
|
||||
private static byte[] readAllBytes(InputStream is) throws IOException {
|
||||
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||
int bytesRead;
|
||||
byte[] data = new byte[1024];
|
||||
while ((bytesRead = is.read(data, 0, data.length)) != -1) {
|
||||
buffer.write(data, 0, bytesRead);
|
||||
}
|
||||
return buffer.toByteArray();
|
||||
}
|
||||
|
||||
private static void copyStream(InputStream is, OutputStream os) throws IOException {
|
||||
byte[] buffer = new byte[1024];
|
||||
int bytesRead;
|
||||
while ((bytesRead = is.read(buffer)) != -1) {
|
||||
os.write(buffer, 0, bytesRead);
|
||||
}
|
||||
}
|
||||
|
||||
private static File getLibFolder() {
|
||||
File pluginDataFolder = AntiVPN.getInstance().getPluginFolder();
|
||||
File libs = new File(pluginDataFolder, "libraries");
|
||||
if(libs.mkdirs()) {
|
||||
System.out.println("Created libraries folder!");
|
||||
}
|
||||
return libs;
|
||||
}
|
||||
|
||||
@Getter
|
||||
@NonnullByDefault
|
||||
// Fix the Dependency class to preserve original groupId for downloading
|
||||
public static final class Dependency {
|
||||
private final String groupId;
|
||||
private final String artifactId;
|
||||
private final String version;
|
||||
private final String repoUrl;
|
||||
// Keep the original groupId/artifactId for Maven downloads
|
||||
private final String originalGroupId;
|
||||
private final String originalArtifactId;
|
||||
|
||||
public Dependency(String groupId, String artifactId, String version, String repoUrl) {
|
||||
this.originalGroupId = Objects.requireNonNull(groupId, "groupId");
|
||||
this.originalArtifactId = Objects.requireNonNull(artifactId, "artifactId");
|
||||
this.groupId = this.originalGroupId;
|
||||
this.artifactId = this.originalArtifactId;
|
||||
this.version = Objects.requireNonNull(version, "version");
|
||||
this.repoUrl = Objects.requireNonNull(repoUrl, "repoUrl");
|
||||
}
|
||||
|
||||
public URL getUrl() throws MalformedURLException {
|
||||
String repo = this.repoUrl;
|
||||
if (!repo.endsWith("/")) {
|
||||
repo += "/";
|
||||
}
|
||||
repo += "%s/%s/%s/%s-%s.jar";
|
||||
|
||||
// Always use original groupId for Maven repository URL
|
||||
String url = String.format(repo, this.originalGroupId.replace(".", "/"),
|
||||
this.originalArtifactId, this.version, this.originalArtifactId, this.version);
|
||||
return new URL(url);
|
||||
}
|
||||
|
||||
// Rest of the class unchanged
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* This file is part of helper, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.depends;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* Annotation to indicate the required libraries for a class.
|
||||
*/
|
||||
@Documented
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface MavenLibraries {
|
||||
|
||||
MavenLibrary[] value() default {};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* This file is part of helper, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.depends;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* Annotation to indicate a required library for a class.
|
||||
*/
|
||||
@Documented
|
||||
@Repeatable(MavenLibraries.class)
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface MavenLibrary {
|
||||
|
||||
/**
|
||||
* The group id of the library
|
||||
*
|
||||
* @return the group id of the library
|
||||
*/
|
||||
String groupId();
|
||||
|
||||
/**
|
||||
* The artifact id of the library
|
||||
*
|
||||
* @return the artifact id of the library
|
||||
*/
|
||||
String artifactId();
|
||||
|
||||
/**
|
||||
* The version of the library
|
||||
*
|
||||
* @return the version of the library
|
||||
*/
|
||||
String version();
|
||||
|
||||
/**
|
||||
* The repo where the library can be obtained from
|
||||
*
|
||||
* @return the repo where the library can be obtained from
|
||||
*/
|
||||
Repository repo() default @Repository(url = "https://repo1.maven.org/maven2");
|
||||
|
||||
Relocate[] relocations() default {}; // Add this line
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package dev.brighten.antivpn.depends;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({})
|
||||
public @interface Relocate {
|
||||
String from();
|
||||
String to();
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* This file is part of helper, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.depends;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* Represents a maven repository.
|
||||
*/
|
||||
@Documented
|
||||
@Target(ElementType.LOCAL_VARIABLE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Repository {
|
||||
|
||||
/**
|
||||
* Gets the base url of the repository.
|
||||
*
|
||||
* @return the base url of the repository
|
||||
*/
|
||||
String url();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* This file is part of helper, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.depends;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* Provides access to {@link URLClassLoader}#addURL.
|
||||
*/
|
||||
public abstract class URLClassLoaderAccess {
|
||||
|
||||
/**
|
||||
* Creates a {@link URLClassLoaderAccess} for the given class loader.
|
||||
*
|
||||
* @param classLoader the class loader
|
||||
* @return the access object
|
||||
*/
|
||||
static URLClassLoaderAccess create(URLClassLoader classLoader) {
|
||||
if (Reflection.isSupported()) {
|
||||
return new Reflection(classLoader);
|
||||
} else if (Unsafe.isSupported()) {
|
||||
return new Unsafe(classLoader);
|
||||
} else {
|
||||
return Noop.INSTANCE;
|
||||
}
|
||||
}
|
||||
|
||||
private final URLClassLoader classLoader;
|
||||
|
||||
protected URLClassLoaderAccess(URLClassLoader classLoader) {
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds the given URL to the class loader.
|
||||
*
|
||||
* @param url the URL to add
|
||||
*/
|
||||
public abstract void addURL(URL url);
|
||||
|
||||
/**
|
||||
* Accesses using reflection, not supported on Java 9+.
|
||||
*/
|
||||
private static class Reflection extends URLClassLoaderAccess {
|
||||
private static final Method ADD_URL_METHOD;
|
||||
|
||||
static {
|
||||
Method addUrlMethod;
|
||||
try {
|
||||
addUrlMethod = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
|
||||
addUrlMethod.setAccessible(true);
|
||||
} catch (Exception e) {
|
||||
addUrlMethod = null;
|
||||
}
|
||||
ADD_URL_METHOD = addUrlMethod;
|
||||
}
|
||||
|
||||
private static boolean isSupported() {
|
||||
return ADD_URL_METHOD != null;
|
||||
}
|
||||
|
||||
Reflection(URLClassLoader classLoader) {
|
||||
super(classLoader);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addURL(URL url) {
|
||||
try {
|
||||
ADD_URL_METHOD.invoke(super.classLoader, url);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Accesses using sun.misc.Unsafe, supported on Java 9+.
|
||||
*
|
||||
* @author Vaishnav Anil (<a href="https://github.com/slimjar/slimjar">...</a>)
|
||||
*/
|
||||
private static class Unsafe extends URLClassLoaderAccess {
|
||||
private static final sun.misc.Unsafe UNSAFE;
|
||||
|
||||
static {
|
||||
sun.misc.Unsafe unsafe;
|
||||
try {
|
||||
Field unsafeField = sun.misc.Unsafe.class.getDeclaredField("theUnsafe");
|
||||
unsafeField.setAccessible(true);
|
||||
unsafe = (sun.misc.Unsafe) unsafeField.get(null);
|
||||
} catch (Throwable t) {
|
||||
unsafe = null;
|
||||
}
|
||||
UNSAFE = unsafe;
|
||||
}
|
||||
|
||||
private static boolean isSupported() {
|
||||
return UNSAFE != null;
|
||||
}
|
||||
|
||||
private final Collection<URL> unopenedURLs;
|
||||
private final Collection<URL> pathURLs;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Unsafe(URLClassLoader classLoader) {
|
||||
super(classLoader);
|
||||
|
||||
Collection<URL> unopenedURLs;
|
||||
Collection<URL> pathURLs;
|
||||
try {
|
||||
Object ucp = fetchField(URLClassLoader.class, classLoader, "ucp");
|
||||
unopenedURLs = (Collection<URL>) fetchField(ucp.getClass(), ucp, "unopenedUrls");
|
||||
pathURLs = (Collection<URL>) fetchField(ucp.getClass(), ucp, "path");
|
||||
} catch (Throwable e) {
|
||||
unopenedURLs = null;
|
||||
pathURLs = null;
|
||||
}
|
||||
this.unopenedURLs = unopenedURLs;
|
||||
this.pathURLs = pathURLs;
|
||||
}
|
||||
|
||||
private static Object fetchField(final Class<?> clazz, final Object object, final String name) throws NoSuchFieldException {
|
||||
Field field = clazz.getDeclaredField(name);
|
||||
long offset = UNSAFE.objectFieldOffset(field);
|
||||
return UNSAFE.getObject(object, offset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addURL(URL url) {
|
||||
this.unopenedURLs.add(url);
|
||||
this.pathURLs.add(url);
|
||||
}
|
||||
}
|
||||
|
||||
private static class Noop extends URLClassLoaderAccess {
|
||||
private static final Noop INSTANCE = new Noop();
|
||||
|
||||
private Noop() {
|
||||
super(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addURL(URL url) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package dev.brighten.antivpn.message;
|
||||
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class MessageHandler {
|
||||
private final Map<String, VpnString> messages = new HashMap<>();
|
||||
|
||||
public VpnString getString(String key) {
|
||||
if(!messages.containsKey(key)) {
|
||||
throw new NullPointerException("There is no VpnString with the key \"" + key + "\"");
|
||||
}
|
||||
|
||||
return messages.get(key);
|
||||
}
|
||||
|
||||
public void reloadStrings() {
|
||||
for (VpnString value : messages.values()) {
|
||||
value.updateString();
|
||||
}
|
||||
}
|
||||
|
||||
public void clearStrings() {
|
||||
messages.clear();
|
||||
}
|
||||
|
||||
public void addString(VpnString string, Function<VpnString, String> getter) {
|
||||
string.setConfigStringGetter(getter);
|
||||
getter.apply(string);
|
||||
AntiVPN.getInstance().getExecutor().log("Added string " + string.getKey());
|
||||
messages.put(string.getKey(), string);
|
||||
}
|
||||
|
||||
public void initStrings(Function<VpnString, String> getter) {
|
||||
addString(new VpnString("command-misc-playerRequired",
|
||||
"&cYou must be a player to execute this command!"), getter);
|
||||
addString(new VpnString("command-alerts-toggled",
|
||||
"&7Your player proxy notifications have been set to: &e%state%"), getter);
|
||||
addString(new VpnString("command-reload-complete",
|
||||
"&aSuccessfully reloaded KauriVPN plugin!"), getter);
|
||||
addString(new VpnString("no-permission", "&cNo permission."), getter);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package dev.brighten.antivpn.message;
|
||||
|
||||
import dev.brighten.antivpn.api.APIPlayer;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
@Getter
|
||||
public class VpnString {
|
||||
private final String key;
|
||||
private final String defaultMessage;
|
||||
private String message;
|
||||
@Setter
|
||||
private Function<VpnString, String> configStringGetter;
|
||||
|
||||
public VpnString(String key, String defaultMessage) {
|
||||
this.key = key;
|
||||
this.defaultMessage = defaultMessage;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public void updateString() {
|
||||
if(configStringGetter == null) throw new Exception("The configStringGetter for string " + key + " is null!");
|
||||
|
||||
message = configStringGetter.apply(this);
|
||||
}
|
||||
|
||||
public String getFormattedMessage(Var<String, Object>... replacements) {
|
||||
String formatted = configStringGetter.apply(this);
|
||||
|
||||
for (Var<String, Object> replacement : replacements) {
|
||||
formatted = formatted
|
||||
.replace("%" + replacement.getKey() + "%", replacement.getReplacement().toString());
|
||||
}
|
||||
|
||||
return formatted;
|
||||
}
|
||||
|
||||
public void sendMessage(APIPlayer player, Var<String, Object>... replacements) {
|
||||
String formatted = message;
|
||||
|
||||
for (Var<String, Object> replacement : replacements) {
|
||||
formatted = formatted
|
||||
.replace("%" + replacement.getKey() + "%", replacement.getReplacement().toString());
|
||||
}
|
||||
player.sendMessage(formatted);
|
||||
}
|
||||
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public static class Var<S, O> {
|
||||
private final String key;
|
||||
private final Object replacement;
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -1,14 +1,14 @@
|
||||
package dev.brighten.antivpn.bukkit.util;
|
||||
package dev.brighten.antivpn.utils;
|
||||
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
@AllArgsConstructor
|
||||
public class ConfigDefault<A> {
|
||||
|
||||
private final A defaultValue;
|
||||
private final String path;
|
||||
private final Plugin plugin;
|
||||
private final AntiVPN plugin;
|
||||
|
||||
public A get() {
|
||||
if(plugin.getConfig().get(path) != null)
|
||||
@@ -0,0 +1,19 @@
|
||||
package dev.brighten.antivpn.utils;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class EvictingMap<K, V> extends LinkedHashMap<K, V> {
|
||||
|
||||
@Getter
|
||||
private final int size;
|
||||
|
||||
@Override
|
||||
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
|
||||
return size() >= size;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright (C) 2016 The Guava Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||
* or implied. See the License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.utils;
|
||||
|
||||
/**
|
||||
* Holder for extra methods of {@code Objects} only in web. Intended to be empty for regular
|
||||
* version.
|
||||
*/
|
||||
abstract class ExtraObjectsMethodsForWeb {}
|
||||
@@ -0,0 +1,90 @@
|
||||
package dev.brighten.antivpn.utils;
|
||||
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
import dev.brighten.antivpn.utils.json.JSONException;
|
||||
import dev.brighten.antivpn.utils.json.JSONObject;
|
||||
import dev.brighten.antivpn.utils.json.JsonReader;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
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}");
|
||||
|
||||
public static void close(Closeable... closeables) {
|
||||
try {
|
||||
for (Closeable closeable : closeables) if (closeable != null) closeable.close();
|
||||
} catch (Exception e) {
|
||||
AntiVPN.getInstance().getExecutor().logException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void close(AutoCloseable... closeables) {
|
||||
try {
|
||||
for (AutoCloseable closeable : closeables) if (closeable != null) closeable.close();
|
||||
} catch (Exception e) {
|
||||
AntiVPN.getInstance().getExecutor().logException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void copy(InputStream in, File file) {
|
||||
try {
|
||||
OutputStream out = new FileOutputStream(file);
|
||||
int lenght;
|
||||
byte[] buf = new byte[1024];
|
||||
|
||||
while ((lenght = in.read(buf)) > 0)
|
||||
{
|
||||
out.write(buf, 0, lenght);
|
||||
}
|
||||
|
||||
out.close();
|
||||
in.close();
|
||||
} catch (Exception e) {
|
||||
AntiVPN.getInstance().getExecutor().logException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static ThreadFactory createThreadFactory(String threadName) {
|
||||
return r -> {
|
||||
Thread thread = new Thread(r);
|
||||
thread.setName(threadName);
|
||||
return thread;
|
||||
};
|
||||
}
|
||||
|
||||
public static UUID formatFromMojangUUID(String mojangUUID) {
|
||||
StringBuilder uuid = new StringBuilder();
|
||||
for(int i = 0; i <= 31; i++) {
|
||||
uuid.append(mojangUUID.charAt(i));
|
||||
if(i == 7 || i == 11 || i == 15 || i == 19) {
|
||||
uuid.append("-");
|
||||
}
|
||||
}
|
||||
|
||||
return UUID.fromString(uuid.toString());
|
||||
}
|
||||
|
||||
public static UUID lookupUUID(String playername) {
|
||||
try {
|
||||
JSONObject object = JsonReader
|
||||
.readJsonFromUrl("https://funkemunky.cc/mojang/uuid?name=" + playername);
|
||||
|
||||
if(object.has("uuid")) {
|
||||
return UUID.fromString(object.getString("uuid"));
|
||||
}
|
||||
} catch (IOException | JSONException e) {
|
||||
AntiVPN.getInstance().getExecutor().logException("Error while looking up UUID for " + playername, e);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static boolean isIpv4(String ip)
|
||||
{
|
||||
return ipv4.matcher(ip).matches();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* This file is part of helper, licensed under the MIT License.
|
||||
*
|
||||
* Copyright (c) lucko (Luck) <luck@lucko.me>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.utils;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface NonnullByDefault {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Guava Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||
* or implied. See the License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.utils;
|
||||
|
||||
/** A utility method to perform unchecked casts to suppress errors produced by nullness analyses. */
|
||||
final class NullnessCasts {
|
||||
/**
|
||||
* Accepts a {@code @Nullable T} and returns a plain {@code T}, without performing any check that
|
||||
* that conversion is safe.
|
||||
*
|
||||
* <p>This method is intended to help with usages of type parameters that have {
|
||||
* ParametricNullness parametric nullness}. If a type parameter instead ranges over only non-null
|
||||
* types (or if the type is a non-variable type, like {@code String}), then code should almost
|
||||
* never use this method, preferring instead to call {@code requireNonNull} so as to benefit from
|
||||
* its runtime check.
|
||||
*
|
||||
* <p>An example use case for this method is in implementing an {@code Iterator<T>} whose {@code
|
||||
* next} field is lazily initialized. The type of that field would be {@code @Nullable T}, and the
|
||||
* code would be responsible for populating a "real" {@code T} (which might still be the value
|
||||
* {@code null}!) before returning it to callers. Depending on how the code is structured, a
|
||||
* nullness analysis might not understand that the field has been populated. To avoid that problem
|
||||
* without having to add {@code @SuppressWarnings}, the code can call this method.
|
||||
*
|
||||
* <p>Why <i>not</i> just add {@code SuppressWarnings}? The problem is that this method is
|
||||
* typically useful for {@code return} statements. That leaves the code with two options: Either
|
||||
* add the suppression to the whole method (which turns off checking for a large section of code),
|
||||
* or extract a variable, and put the suppression on that. However, a local variable typically
|
||||
* doesn't work: Because nullness analyses typically infer the nullness of local variables,
|
||||
* there's no way to assign a {@code @Nullable T} to a field {@code T foo;} and instruct the
|
||||
* analysis that that means "plain {@code T}" rather than the inferred type {@code @Nullable T}.
|
||||
* (Even if supported added {@code @NonNull}, that would not help, since the problem case
|
||||
* addressed by this method is the case in which {@code T} has parametric nullness -- and thus its
|
||||
* value may be legitimately {@code null}.)
|
||||
*/
|
||||
@SuppressWarnings("nullness")
|
||||
static <T> T uncheckedCastNullableTToT(T t) {
|
||||
return t;
|
||||
}
|
||||
|
||||
private NullnessCasts() {}
|
||||
}
|
||||
@@ -0,0 +1,244 @@
|
||||
//
|
||||
// Source code recreated from a .class file by IntelliJ IDEA
|
||||
// (powered by FernFlower decompiler)
|
||||
//
|
||||
|
||||
package dev.brighten.antivpn.utils;
|
||||
|
||||
public final class Preconditions {
|
||||
private Preconditions() {
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T reference) {
|
||||
if (reference == null) {
|
||||
throw new NullPointerException();
|
||||
} else {
|
||||
return reference;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T reference, Object errorMessage) {
|
||||
if (reference == null) {
|
||||
throw new NullPointerException(String.valueOf(errorMessage));
|
||||
} else {
|
||||
return reference;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T reference, String errorMessageTemplate, Object... errorMessageArgs) {
|
||||
if (reference == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, errorMessageArgs));
|
||||
} else {
|
||||
return reference;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, char p1) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, int p1) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, long p1) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, Object p1) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, char p1, char p2) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, char p1, int p2) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, char p1, long p2) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, char p1, Object p2) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, int p1, char p2) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, int p1, int p2) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, int p1, long p2) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, int p1, Object p2) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, long p1, char p2) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, long p1, int p2) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, long p1, long p2) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, long p1, Object p2) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, Object p1, char p2) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, Object p1, int p2) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, Object p1, long p2) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, Object p1, Object p2) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, Object p1, Object p2, Object p3) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2, p3));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T checkNotNull(T obj, String errorMessageTemplate, Object p1, Object p2, Object p3, Object p4) {
|
||||
if (obj == null) {
|
||||
throw new NullPointerException(format(errorMessageTemplate, p1, p2, p3, p4));
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
static String format(String template, Object... args) {
|
||||
template = String.valueOf(template);
|
||||
StringBuilder builder = new StringBuilder(template.length() + 16 * args.length);
|
||||
int templateStart = 0;
|
||||
|
||||
int i;
|
||||
int placeholderStart;
|
||||
for(i = 0; i < args.length; templateStart = placeholderStart + 2) {
|
||||
placeholderStart = template.indexOf("%s", templateStart);
|
||||
if (placeholderStart == -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
builder.append(template, templateStart, placeholderStart);
|
||||
builder.append(args[i++]);
|
||||
}
|
||||
|
||||
builder.append(template, templateStart, template.length());
|
||||
if (i < args.length) {
|
||||
builder.append(" [");
|
||||
builder.append(args[i++]);
|
||||
|
||||
while(i < args.length) {
|
||||
builder.append(", ");
|
||||
builder.append(args[i++]);
|
||||
}
|
||||
|
||||
builder.append(']');
|
||||
}
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
package dev.brighten.antivpn.utils;
|
||||
|
||||
import dev.brighten.antivpn.api.APIPlayer;
|
||||
import dev.brighten.antivpn.web.objects.VPNResponse;
|
||||
|
||||
public class StringUtil {
|
||||
public static String line(String color) {
|
||||
return color + "&m-----------------------------------------------------";
|
||||
@@ -12,4 +15,24 @@ public class StringUtil {
|
||||
public static String lineNoStrike(String color) {
|
||||
return color + "-----------------------------------------------------";
|
||||
}
|
||||
|
||||
public static String varReplace(String input, APIPlayer player, VPNResponse result) {
|
||||
return input.replace("%player%", player.getName())
|
||||
.replace("%reason%", result.getMethod())
|
||||
.replace("%country%", result.getCountryName())
|
||||
.replace("%city%", result.getCity());
|
||||
}
|
||||
|
||||
public static String translateAlternateColorCodes(char altColorChar, String textToTranslate) {
|
||||
char[] b = textToTranslate.toCharArray();
|
||||
|
||||
for(int i = 0; i < b.length - 1; ++i) {
|
||||
if (b[i] == altColorChar && "0123456789AaBbCcDdEeFfKkLlMmNnOoRr".indexOf(b[i + 1]) > -1) {
|
||||
b[i] = 167;
|
||||
b[i + 1] = Character.toLowerCase(b[i + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
return new String(b);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
//
|
||||
// Source code recreated from a .class file by IntelliJ IDEA
|
||||
// (powered by FernFlower decompiler)
|
||||
//
|
||||
|
||||
package dev.brighten.antivpn.utils;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface Supplier<T> extends java.util.function.Supplier<T> {
|
||||
T get();
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Guava Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||
* or implied. See the License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
package dev.brighten.antivpn.utils;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import static dev.brighten.antivpn.utils.NullnessCasts.uncheckedCastNullableTToT;
|
||||
import static dev.brighten.antivpn.utils.Preconditions.checkNotNull;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
/**
|
||||
* Useful suppliers.
|
||||
*
|
||||
* <p>All methods return serializable suppliers as long as they're given serializable parameters.
|
||||
*
|
||||
* @author Laurence Gonsalves
|
||||
* @author Harry Heymann
|
||||
* @since 2.0
|
||||
*/
|
||||
public final class Suppliers {
|
||||
private Suppliers() {}
|
||||
|
||||
/**
|
||||
* Returns a supplier which caches the instance retrieved during the first call to {@code get()}
|
||||
* and returns that value on subsequent calls to {@code get()}. See: <a
|
||||
* href="http://en.wikipedia.org/wiki/Memoization">memoization</a>
|
||||
*
|
||||
* <p>The returned supplier is thread-safe. The delegate's {@code get()} method will be invoked at
|
||||
* most once unless the underlying {@code get()} throws an exception. The supplier's serialized
|
||||
* form does not contain the cached value, which will be recalculated when {@code get()} is called
|
||||
* on the reserialized instance.
|
||||
*
|
||||
* <p>When the underlying delegate throws an exception then this memoizing supplier will keep
|
||||
* delegating calls until it returns valid data.
|
||||
*
|
||||
* <p>If {@code delegate} is an instance created by an earlier call to {@code memoize}, it is
|
||||
* returned directly.
|
||||
*/
|
||||
public static <T> Supplier<T> memoize(Supplier<T> delegate) {
|
||||
if (delegate instanceof NonSerializableMemoizingSupplier
|
||||
|| delegate instanceof MemoizingSupplier) {
|
||||
return delegate;
|
||||
}
|
||||
return delegate instanceof Serializable
|
||||
? new MemoizingSupplier<>(delegate)
|
||||
: new NonSerializableMemoizingSupplier<>(delegate);
|
||||
}
|
||||
|
||||
static class MemoizingSupplier<T> implements Supplier<T>, Serializable {
|
||||
final Supplier<T> delegate;
|
||||
transient volatile boolean initialized;
|
||||
// "value" does not need to be volatile; visibility piggy-backs
|
||||
// on volatile read of "initialized".
|
||||
transient T value;
|
||||
|
||||
MemoizingSupplier(Supplier<T> delegate) {
|
||||
this.delegate = checkNotNull(delegate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get() {
|
||||
// A 2-field variant of Double Checked Locking.
|
||||
if (!initialized) {
|
||||
synchronized (this) {
|
||||
if (!initialized) {
|
||||
T t = delegate.get();
|
||||
value = t;
|
||||
initialized = true;
|
||||
return t;
|
||||
}
|
||||
}
|
||||
}
|
||||
// This is safe because we checked `initialized.`
|
||||
return uncheckedCastNullableTToT(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Suppliers.memoize("
|
||||
+ (initialized ? "<supplier that returned " + value + ">" : delegate)
|
||||
+ ")";
|
||||
}
|
||||
|
||||
private static final long serialVersionUID = 0;
|
||||
}
|
||||
|
||||
static class NonSerializableMemoizingSupplier<T> implements Supplier<T> {
|
||||
volatile Supplier<T> delegate;
|
||||
volatile boolean initialized;
|
||||
// "value" does not need to be volatile; visibility piggy-backs
|
||||
// on volatile read of "initialized".
|
||||
T value;
|
||||
|
||||
NonSerializableMemoizingSupplier(Supplier<T> delegate) {
|
||||
this.delegate = checkNotNull(delegate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get() {
|
||||
// A 2-field variant of Double Checked Locking.
|
||||
if (!initialized) {
|
||||
synchronized (this) {
|
||||
if (!initialized) {
|
||||
/*
|
||||
* requireNonNull is safe because we read and write `delegate` under synchronization.
|
||||
*
|
||||
* TODO(cpovirk): To avoid having to check for null, replace `delegate` with a singleton
|
||||
* `Supplier` that always throws an exception.
|
||||
*/
|
||||
T t = requireNonNull(delegate).get();
|
||||
value = t;
|
||||
initialized = true;
|
||||
// Release the delegate to GC.
|
||||
delegate = null;
|
||||
return t;
|
||||
}
|
||||
}
|
||||
}
|
||||
// This is safe because we checked `initialized.`
|
||||
return uncheckedCastNullableTToT(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
Supplier<T> delegate = this.delegate;
|
||||
return "Suppliers.memoize("
|
||||
+ (delegate == null ? "<supplier that returned " + value + ">" : delegate)
|
||||
+ ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package dev.brighten.antivpn.utils;
|
||||
|
||||
public record Tuple<F, S>(F first, S second) {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,507 @@
|
||||
package dev.brighten.antivpn.utils.config;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public final class Configuration
|
||||
{
|
||||
|
||||
private static final char SEPARATOR = '.';
|
||||
final Map<String, Object> self;
|
||||
final Map<String, List<String>> comments;
|
||||
private final Configuration defaults;
|
||||
|
||||
public Configuration()
|
||||
{
|
||||
this( null );
|
||||
}
|
||||
|
||||
public Configuration(Configuration defaults)
|
||||
{
|
||||
this( new LinkedHashMap<String, Object>(), defaults );
|
||||
}
|
||||
|
||||
Configuration(Map<?, ?> map, Configuration defaults)
|
||||
{
|
||||
this.self = new LinkedHashMap<>();
|
||||
this.defaults = defaults;
|
||||
comments = new HashMap<>();
|
||||
|
||||
for ( Map.Entry<?, ?> entry : map.entrySet() )
|
||||
{
|
||||
String key = ( entry.getKey() == null ) ? "null" : entry.getKey().toString();
|
||||
|
||||
if ( entry.getValue() instanceof Map )
|
||||
{
|
||||
this.self.put( key, new Configuration( (Map) entry.getValue(), ( defaults == null ) ? null : defaults.getSection( key ) ) );
|
||||
} else
|
||||
{
|
||||
this.self.put( key, entry.getValue() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void loadFromString(String contents) {
|
||||
|
||||
List<String> list = new ArrayList<>();
|
||||
Collections.addAll(list, contents.split("\n"));
|
||||
|
||||
int currentLayer = 0;
|
||||
String currentPath = "";
|
||||
|
||||
int lineNumber = 0;
|
||||
for(Iterator<String> iterator = list.iterator(); iterator.hasNext(); lineNumber++) {
|
||||
String line = iterator.next();
|
||||
|
||||
String trimmed = line.trim();
|
||||
if(trimmed.startsWith("#") || trimmed.isEmpty()) {
|
||||
addCommentLine(currentPath, line);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!line.isEmpty()) {
|
||||
if(line.contains(":")) {
|
||||
|
||||
int layerFromLine = getLayerFromLine(line, lineNumber);
|
||||
|
||||
if(layerFromLine < currentLayer) {
|
||||
currentPath = regressPathBy(currentLayer - layerFromLine, currentPath);
|
||||
}
|
||||
|
||||
String key = getKeyFromLine(line);
|
||||
|
||||
if(currentLayer == 0) {
|
||||
currentPath = key;
|
||||
}
|
||||
else {
|
||||
currentPath += "." + key;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addCommentLine(String currentPath, String line) {
|
||||
|
||||
List<String> list = comments.get(currentPath);
|
||||
if(list == null) {
|
||||
list = new ArrayList<>();
|
||||
}
|
||||
list.add(line);
|
||||
|
||||
comments.put(currentPath, list);
|
||||
}
|
||||
|
||||
String getKeyFromLine(String line) {
|
||||
String key = null;
|
||||
|
||||
for(int i = 0; i < line.length(); i++) {
|
||||
if(line.charAt(i) == ':') {
|
||||
key = line.substring(0, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return key == null ? null : key.trim();
|
||||
}
|
||||
|
||||
String regressPathBy(int i, String currentPath) {
|
||||
if(i <= 0) {
|
||||
return currentPath;
|
||||
}
|
||||
String[] split = currentPath.split("\\.");
|
||||
|
||||
String rebuild = "";
|
||||
for(int j = 0; j < split.length - i; j++) {
|
||||
rebuild += split[j];
|
||||
if(j <= (split.length - j)) {
|
||||
rebuild += ".";
|
||||
}
|
||||
}
|
||||
|
||||
return rebuild;
|
||||
}
|
||||
|
||||
int getLayerFromLine(String line, int lineNumber) {
|
||||
|
||||
double d = 0;
|
||||
for(int i = 0; i < line.length(); i++) {
|
||||
if(line.charAt(i) == ' ') {
|
||||
d += 0.5;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return (int) d;
|
||||
|
||||
}
|
||||
|
||||
private Configuration getSectionFor(String path)
|
||||
{
|
||||
int index = path.indexOf( SEPARATOR );
|
||||
if ( index == -1 )
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
String root = path.substring( 0, index );
|
||||
Object section = self.get( root );
|
||||
if ( section == null )
|
||||
{
|
||||
section = new Configuration( ( defaults == null ) ? null : defaults.getSection( root ) );
|
||||
self.put( root, section );
|
||||
}
|
||||
|
||||
return (Configuration) section;
|
||||
}
|
||||
|
||||
private String getChild(String path)
|
||||
{
|
||||
int index = path.indexOf( SEPARATOR );
|
||||
return ( index == -1 ) ? path : path.substring( index + 1 );
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T get(String path, T def)
|
||||
{
|
||||
Configuration section = getSectionFor( path );
|
||||
Object val;
|
||||
if ( section == this )
|
||||
{
|
||||
val = self.get( path );
|
||||
} else
|
||||
{
|
||||
val = section.get( getChild( path ), def );
|
||||
}
|
||||
|
||||
if ( val == null && def instanceof Configuration )
|
||||
{
|
||||
self.put( path, def );
|
||||
}
|
||||
|
||||
return ( val != null ) ? (T) val : def;
|
||||
}
|
||||
|
||||
public boolean contains(String path)
|
||||
{
|
||||
return get( path, null ) != null;
|
||||
}
|
||||
|
||||
public Object get(String path)
|
||||
{
|
||||
return get( path, getDefault( path ) );
|
||||
}
|
||||
|
||||
public Object getDefault(String path)
|
||||
{
|
||||
return ( defaults == null ) ? null : defaults.get( path );
|
||||
}
|
||||
|
||||
public void set(String path, Object value)
|
||||
{
|
||||
if ( value instanceof Map )
|
||||
{
|
||||
value = new Configuration( (Map) value, ( defaults == null ) ? null : defaults.getSection( path ) );
|
||||
}
|
||||
|
||||
Configuration section = getSectionFor( path );
|
||||
if ( section == this )
|
||||
{
|
||||
if ( value == null )
|
||||
{
|
||||
self.remove( path );
|
||||
} else
|
||||
{
|
||||
self.put( path, value );
|
||||
}
|
||||
} else
|
||||
{
|
||||
section.set( getChild( path ), value );
|
||||
}
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
public Configuration getSection(String path)
|
||||
{
|
||||
Object def = getDefault( path );
|
||||
return (Configuration) get( path, ( def instanceof Configuration ) ? def : new Configuration( ( defaults == null ) ? null : defaults.getSection( path ) ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets keys, not deep by default.
|
||||
*
|
||||
* @return top level keys for this section
|
||||
*/
|
||||
public Collection<String> getKeys()
|
||||
{
|
||||
return new LinkedHashSet<>( self.keySet() );
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
public byte getByte(String path)
|
||||
{
|
||||
Object def = getDefault( path );
|
||||
return getByte( path, ( def instanceof Number ) ? ( (Number) def ).byteValue() : 0 );
|
||||
}
|
||||
|
||||
public byte getByte(String path, byte def)
|
||||
{
|
||||
Object val = get( path, def );
|
||||
return ( val instanceof Number ) ? ( (Number) val ).byteValue() : def;
|
||||
}
|
||||
|
||||
public List<Byte> getByteList(String path)
|
||||
{
|
||||
List<?> list = getList( path );
|
||||
List<Byte> result = new ArrayList<>();
|
||||
|
||||
for ( Object object : list )
|
||||
{
|
||||
if ( object instanceof Number )
|
||||
{
|
||||
result.add( ( (Number) object ).byteValue() );
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public short getShort(String path)
|
||||
{
|
||||
Object def = getDefault( path );
|
||||
return getShort( path, ( def instanceof Number ) ? ( (Number) def ).shortValue() : 0 );
|
||||
}
|
||||
|
||||
public short getShort(String path, short def)
|
||||
{
|
||||
Object val = get( path, def );
|
||||
return ( val instanceof Number ) ? ( (Number) val ).shortValue() : def;
|
||||
}
|
||||
|
||||
public List<Short> getShortList(String path)
|
||||
{
|
||||
List<?> list = getList( path );
|
||||
List<Short> result = new ArrayList<>();
|
||||
|
||||
for ( Object object : list )
|
||||
{
|
||||
if ( object instanceof Number )
|
||||
{
|
||||
result.add( ( (Number) object ).shortValue() );
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public int getInt(String path)
|
||||
{
|
||||
Object def = getDefault( path );
|
||||
return getInt( path, ( def instanceof Number ) ? ( (Number) def ).intValue() : 0 );
|
||||
}
|
||||
|
||||
public int getInt(String path, int def)
|
||||
{
|
||||
Object val = get( path, def );
|
||||
return ( val instanceof Number ) ? ( (Number) val ).intValue() : def;
|
||||
}
|
||||
|
||||
public List<Integer> getIntList(String path)
|
||||
{
|
||||
List<?> list = getList( path );
|
||||
List<Integer> result = new ArrayList<>();
|
||||
|
||||
for ( Object object : list )
|
||||
{
|
||||
if ( object instanceof Number )
|
||||
{
|
||||
result.add( ( (Number) object ).intValue() );
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public long getLong(String path)
|
||||
{
|
||||
Object def = getDefault( path );
|
||||
return getLong( path, ( def instanceof Number ) ? ( (Number) def ).longValue() : 0 );
|
||||
}
|
||||
|
||||
public long getLong(String path, long def)
|
||||
{
|
||||
Object val = get( path, def );
|
||||
return ( val instanceof Number ) ? ( (Number) val ).longValue() : def;
|
||||
}
|
||||
|
||||
public List<Long> getLongList(String path)
|
||||
{
|
||||
List<?> list = getList( path );
|
||||
List<Long> result = new ArrayList<>();
|
||||
|
||||
for ( Object object : list )
|
||||
{
|
||||
if ( object instanceof Number )
|
||||
{
|
||||
result.add( ( (Number) object ).longValue() );
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public float getFloat(String path)
|
||||
{
|
||||
Object def = getDefault( path );
|
||||
return getFloat( path, ( def instanceof Number ) ? ( (Number) def ).floatValue() : 0 );
|
||||
}
|
||||
|
||||
public float getFloat(String path, float def)
|
||||
{
|
||||
Object val = get( path, def );
|
||||
return ( val instanceof Number ) ? ( (Number) val ).floatValue() : def;
|
||||
}
|
||||
|
||||
public List<Float> getFloatList(String path)
|
||||
{
|
||||
List<?> list = getList( path );
|
||||
List<Float> result = new ArrayList<>();
|
||||
|
||||
for ( Object object : list )
|
||||
{
|
||||
if ( object instanceof Number )
|
||||
{
|
||||
result.add( ( (Number) object ).floatValue() );
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public double getDouble(String path)
|
||||
{
|
||||
Object def = getDefault( path );
|
||||
return getDouble( path, ( def instanceof Number ) ? ( (Number) def ).doubleValue() : 0 );
|
||||
}
|
||||
|
||||
public double getDouble(String path, double def)
|
||||
{
|
||||
Object val = get( path, def );
|
||||
return ( val instanceof Number ) ? ( (Number) val ).doubleValue() : def;
|
||||
}
|
||||
|
||||
public List<Double> getDoubleList(String path)
|
||||
{
|
||||
List<?> list = getList( path );
|
||||
List<Double> result = new ArrayList<>();
|
||||
|
||||
for ( Object object : list )
|
||||
{
|
||||
if ( object instanceof Number )
|
||||
{
|
||||
result.add( ( (Number) object ).doubleValue() );
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean getBoolean(String path)
|
||||
{
|
||||
Object def = getDefault( path );
|
||||
return getBoolean( path, ( def instanceof Boolean ) ? (Boolean) def : false );
|
||||
}
|
||||
|
||||
public boolean getBoolean(String path, boolean def)
|
||||
{
|
||||
Object val = get( path, def );
|
||||
return ( val instanceof Boolean ) ? (Boolean) val : def;
|
||||
}
|
||||
|
||||
public List<Boolean> getBooleanList(String path)
|
||||
{
|
||||
List<?> list = getList( path );
|
||||
List<Boolean> result = new ArrayList<>();
|
||||
|
||||
for ( Object object : list )
|
||||
{
|
||||
if ( object instanceof Boolean )
|
||||
{
|
||||
result.add( (Boolean) object );
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public char getChar(String path)
|
||||
{
|
||||
Object def = getDefault( path );
|
||||
return getChar( path, ( def instanceof Character ) ? (Character) def : '\u0000' );
|
||||
}
|
||||
|
||||
public char getChar(String path, char def)
|
||||
{
|
||||
Object val = get( path, def );
|
||||
return ( val instanceof Character ) ? (Character) val : def;
|
||||
}
|
||||
|
||||
public List<Character> getCharList(String path)
|
||||
{
|
||||
List<?> list = getList( path );
|
||||
List<Character> result = new ArrayList<>();
|
||||
|
||||
for ( Object object : list )
|
||||
{
|
||||
if ( object instanceof Character )
|
||||
{
|
||||
result.add( (Character) object );
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public String getString(String path)
|
||||
{
|
||||
Object def = getDefault( path );
|
||||
return getString( path, ( def instanceof String ) ? (String) def : "" );
|
||||
}
|
||||
|
||||
public String getString(String path, String def)
|
||||
{
|
||||
Object val = get( path, def );
|
||||
return ( val instanceof String ) ? (String) val : def;
|
||||
}
|
||||
|
||||
public List<String> getStringList(String path)
|
||||
{
|
||||
List<?> list = getList( path );
|
||||
List<String> result = new ArrayList<>();
|
||||
|
||||
for ( Object object : list )
|
||||
{
|
||||
if ( object instanceof String )
|
||||
{
|
||||
result.add( (String) object );
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
public List<?> getList(String path)
|
||||
{
|
||||
Object def = getDefault( path );
|
||||
return getList( path, ( def instanceof List<?> ) ? (List<?>) def : Collections.EMPTY_LIST );
|
||||
}
|
||||
|
||||
public List<?> getList(String path, List<?> def)
|
||||
{
|
||||
Object val = get( path, def );
|
||||
return ( val instanceof List<?> ) ? (List<?>) val : def;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package dev.brighten.antivpn.utils.config;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public abstract class ConfigurationProvider
|
||||
{
|
||||
|
||||
public static final Map<Class<? extends ConfigurationProvider>, ConfigurationProvider> providers = new HashMap<>();
|
||||
|
||||
static
|
||||
{
|
||||
try
|
||||
{
|
||||
providers.put( YamlConfiguration.class, new YamlConfiguration() );
|
||||
} catch ( NoClassDefFoundError ex )
|
||||
{
|
||||
ex.printStackTrace();
|
||||
// Ignore, no SnakeYAML
|
||||
}
|
||||
}
|
||||
|
||||
public static ConfigurationProvider getProvider(Class<? extends ConfigurationProvider> provider)
|
||||
{
|
||||
return providers.get( provider );
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
public abstract void save(Configuration config, File file) throws IOException;
|
||||
|
||||
public abstract void save(Configuration config, Writer writer);
|
||||
|
||||
public abstract Configuration load(File file) throws IOException;
|
||||
|
||||
public abstract Configuration load(File file, Configuration defaults) throws IOException;
|
||||
|
||||
public abstract Configuration load(Reader reader);
|
||||
|
||||
public abstract Configuration load(Reader reader, Configuration defaults);
|
||||
|
||||
public abstract Configuration load(InputStream is);
|
||||
|
||||
public abstract Configuration load(InputStream is, Configuration defaults);
|
||||
|
||||
public abstract Configuration load(String string);
|
||||
|
||||
public abstract Configuration load(String string, Configuration defaults);
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
package dev.brighten.antivpn.utils.config;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
import org.yaml.snakeyaml.DumperOptions;
|
||||
import org.yaml.snakeyaml.LoaderOptions;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
import org.yaml.snakeyaml.constructor.Constructor;
|
||||
import org.yaml.snakeyaml.representer.Representer;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
|
||||
@NoArgsConstructor(access = AccessLevel.PACKAGE)
|
||||
public class YamlConfiguration extends ConfigurationProvider
|
||||
{
|
||||
|
||||
private final ThreadLocal<Yaml> yaml = new ThreadLocal<Yaml>()
|
||||
{
|
||||
@Override
|
||||
protected Yaml initialValue()
|
||||
{
|
||||
DumperOptions options = new DumperOptions();
|
||||
options.setDefaultFlowStyle( DumperOptions.FlowStyle.BLOCK );
|
||||
Representer representer = new Representer(options)
|
||||
{
|
||||
{
|
||||
representers.put( Configuration.class, data -> represent( ( (Configuration) data ).self ));
|
||||
}
|
||||
};
|
||||
|
||||
representer.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
|
||||
return new Yaml( new Constructor(new LoaderOptions()), representer, options );
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void save(Configuration config, File file) throws IOException
|
||||
{
|
||||
try ( Writer writer = new OutputStreamWriter( new FileOutputStream( file ), StandardCharsets.UTF_8 ) )
|
||||
{
|
||||
save( config, writer );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(Configuration config, Writer writer)
|
||||
{
|
||||
String contents = this.yaml.get().dump(config.self);
|
||||
if (contents.equals("{}\n")) {
|
||||
contents = "";
|
||||
}
|
||||
|
||||
List<String> list = new ArrayList<>();
|
||||
Collections.addAll(list, contents.split("\n"));
|
||||
|
||||
int currentLayer = 0;
|
||||
StringBuilder currentPath = new StringBuilder();
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
int lineNumber = 0;
|
||||
for(Iterator<String> iterator = list.iterator(); iterator.hasNext(); lineNumber++) {
|
||||
String line = iterator.next();
|
||||
sb.append(line);
|
||||
sb.append('\n');
|
||||
|
||||
if (!line.isEmpty()) {
|
||||
if (line.contains(":")) {
|
||||
|
||||
int layerFromLine = config.getLayerFromLine(line, lineNumber);
|
||||
|
||||
if (layerFromLine < currentLayer) {
|
||||
currentPath = new StringBuilder(config.regressPathBy(currentLayer - layerFromLine, currentPath.toString()));
|
||||
}
|
||||
|
||||
String key = config.getKeyFromLine(line);
|
||||
|
||||
if (currentLayer == 0) {
|
||||
currentPath = new StringBuilder(key);
|
||||
} else {
|
||||
currentPath.append("." + key);
|
||||
}
|
||||
|
||||
String path = currentPath.toString();
|
||||
if (config.comments.containsKey(path)) {
|
||||
config.comments.get(path).forEach(string -> {
|
||||
sb.append(string);
|
||||
sb.append('\n');
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
writer.write(sb.toString());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public Configuration load(File file) throws IOException
|
||||
{
|
||||
return load( file, null );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Configuration load(File file, Configuration defaults) throws IOException
|
||||
{
|
||||
try ( FileInputStream is = new FileInputStream( file ) )
|
||||
{
|
||||
return load( is, defaults );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Configuration load(Reader reader)
|
||||
{
|
||||
return load( reader, null );
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public Configuration load(Reader reader, Configuration defaults)
|
||||
{
|
||||
BufferedReader input = reader instanceof BufferedReader ? (BufferedReader)reader : new BufferedReader(reader);
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
String line;
|
||||
try {
|
||||
while((line = input.readLine()) != null) {
|
||||
builder.append(line);
|
||||
builder.append('\n');
|
||||
}
|
||||
} finally {
|
||||
input.close();
|
||||
}
|
||||
|
||||
|
||||
return load(builder.toString(), defaults);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Configuration load(InputStream is)
|
||||
{
|
||||
return this.load(new InputStreamReader(is, Charset.defaultCharset()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Configuration load(InputStream is, Configuration defaults)
|
||||
{
|
||||
return this.load(new InputStreamReader(is, Charset.defaultCharset()), defaults);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Configuration load(String string)
|
||||
{
|
||||
return load( string, null );
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Configuration load(String contents, Configuration defaults)
|
||||
{
|
||||
Map<String, Object> map;
|
||||
LoaderOptions loaderOptions = new LoaderOptions();
|
||||
loaderOptions.setMaxAliasesForCollections(2147483647);
|
||||
map = this.yaml.get().loadAs(contents, LinkedHashMap.class);
|
||||
|
||||
Configuration config = new Configuration( map, defaults );
|
||||
config.loadFromString(contents);
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package dev.brighten.antivpn.web;
|
||||
|
||||
import dev.brighten.antivpn.utils.json.JSONException;
|
||||
import dev.brighten.antivpn.utils.json.JSONObject;
|
||||
import dev.brighten.antivpn.utils.json.JsonReader;
|
||||
import dev.brighten.antivpn.web.objects.QueryResponse;
|
||||
import dev.brighten.antivpn.web.objects.VPNResponse;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class FunkemunkyAPI {
|
||||
|
||||
/**
|
||||
*
|
||||
* Queries <a href="https://funkemunky.cc/vpn">...</a> API and returns information on the IP
|
||||
*
|
||||
* @param ip String
|
||||
* @param license String
|
||||
* @param cachedResults boolean
|
||||
* @return VPNResponse
|
||||
* @throws JSONException Throws when JSON response is not formatted properly.
|
||||
* @throws IOException Throws when there is an error connecting to and processing information from API.
|
||||
*/
|
||||
public static VPNResponse getVPNResponse(String ip, String license, boolean cachedResults /* faster if set to true*/)
|
||||
throws JSONException, IOException {
|
||||
JSONObject result = JsonReader.readJsonFromUrl(String
|
||||
.format("https://funkemunky.cc/vpn?ip=%s&license=%s&cache=%s",
|
||||
ip, license.isEmpty() ? "none" : license, cachedResults));
|
||||
|
||||
return VPNResponse.fromJson(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Feeds into {@link FunkemunkyAPI#getQueryResponse(String)} using "none" as argument
|
||||
* to grab query information based on the connecting IP address.
|
||||
*
|
||||
* @return QueryResponse
|
||||
* @throws JSONException Throws when JSON response is not formatted properly.
|
||||
* @throws IOException Throws when there is an error connecting to and processing information from API.
|
||||
*/
|
||||
public static QueryResponse getQueryResponse() throws JSONException, IOException {
|
||||
return getQueryResponse("none");
|
||||
}
|
||||
|
||||
/**
|
||||
* Queries <a href="https://funkemunky.cc/vpn/queryCheck">...</a> and returns information based on the
|
||||
* provided licence input.
|
||||
*
|
||||
* @param license String
|
||||
* @return QueryResponse
|
||||
* @throws JSONException Throws when JSON response is not formatted properly.
|
||||
* @throws IOException Throws when there is an error connecting to and processing information from API.
|
||||
*/
|
||||
public static QueryResponse getQueryResponse(String license) throws JSONException, IOException {
|
||||
JSONObject result = JsonReader.readJsonFromUrl("https://funkemunky.cc/vpn/queryCheck?license=" + license);
|
||||
|
||||
return QueryResponse.fromJson(result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package dev.brighten.antivpn.web.objects;
|
||||
|
||||
import dev.brighten.antivpn.utils.json.JSONException;
|
||||
import dev.brighten.antivpn.utils.json.JSONObject;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
|
||||
/**
|
||||
* Used to format the JSON response from <a href="https://funkemunky.cc/vpn/queryCheck">...</a> into an object for project use.
|
||||
*/
|
||||
@Data
|
||||
@Builder(toBuilder = true)
|
||||
public class QueryResponse {
|
||||
|
||||
private boolean validPlan;
|
||||
private String planType;
|
||||
private long queries;
|
||||
private long queriesMax;
|
||||
|
||||
/**
|
||||
* Formats response from <a href="https://funkemunky.cc/vpn/queryCheck">...</a> into {@link QueryResponse} for project use.
|
||||
*
|
||||
* @param object JSONObject
|
||||
* @return QueryResponse
|
||||
* @throws JSONException Throws when JSON is not formatted properly.
|
||||
*/
|
||||
public static QueryResponse fromJson(JSONObject object) throws JSONException {
|
||||
boolean validPlan = object.getBoolean("validPlan");
|
||||
|
||||
if(!validPlan) { // Nothing else will be returned from API if validPlan is false.
|
||||
return QueryResponse.builder().validPlan(false).build();
|
||||
}
|
||||
|
||||
return QueryResponse.builder().validPlan(object.getBoolean("validPlan"))
|
||||
.planType(object.getString("planType"))
|
||||
.queries(object.getLong("queries"))
|
||||
.queriesMax(object.getLong("queryLimit")).build();
|
||||
}
|
||||
}
|
||||
+30
-22
@@ -1,18 +1,16 @@
|
||||
package dev.brighten.antivpn.utils;
|
||||
package dev.brighten.antivpn.web.objects;
|
||||
|
||||
import dev.brighten.antivpn.utils.json.JSONException;
|
||||
import dev.brighten.antivpn.utils.json.JSONObject;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@RequiredArgsConstructor
|
||||
@Builder
|
||||
public class VPNResponse {
|
||||
private String asn, ip, countryName, countryCode, city, timeZone, method, isp;
|
||||
private String asn, ip, countryName, countryCode, city, timeZone, method, isp, failureReason = "N/A";
|
||||
private boolean proxy, cached;
|
||||
private final boolean success;
|
||||
private double latitude, longitude;
|
||||
@@ -31,38 +29,48 @@ public class VPNResponse {
|
||||
json.put("proxy", proxy);
|
||||
json.put("success", success);
|
||||
json.put("timeZone", timeZone);
|
||||
json.put("success", true);
|
||||
json.put("queriesLeft", queriesLeft);
|
||||
json.put("cached", cached);
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
/**
|
||||
* Feeds into {@link VPNResponse#fromJson(JSONObject)} formatting the JSON {@link String} into
|
||||
* a {@link JSONObject}
|
||||
*
|
||||
* @param json String
|
||||
* @return VPNResponse
|
||||
*/
|
||||
public static VPNResponse fromJson(String json) throws JSONException {
|
||||
JSONObject jsonObject = new JSONObject(json);
|
||||
|
||||
return new VPNResponse(jsonObject.getString("asn"), jsonObject.getString("ip"),
|
||||
jsonObject.getString("countryName"), jsonObject.getString("countryCode"),
|
||||
jsonObject.getString("city"), jsonObject.getString("timeZone"),
|
||||
jsonObject.has("method") ? jsonObject.getString("method") : "N/A",
|
||||
jsonObject.getString("isp"), jsonObject.getBoolean("proxy"),
|
||||
jsonObject.getBoolean("cached"), jsonObject.getBoolean("success"),
|
||||
jsonObject.getDouble("latitude"), jsonObject.getDouble("longitude"),
|
||||
jsonObject.getLong("lastAccess"), jsonObject.getInt("queriesLeft"));
|
||||
return fromJson(new JSONObject(json));
|
||||
}
|
||||
|
||||
public static final VPNResponse FAILED_RESPONSE = VPNResponse.builder()
|
||||
.success(false)
|
||||
.failureReason("Internal plugin API error.")
|
||||
.build();
|
||||
|
||||
/**
|
||||
* Formats response from <a href="https://funkemunky.cc/vpn">...</a> into {@link VPNResponse} for project use.
|
||||
*
|
||||
* @param jsonObject JSONObject
|
||||
* @return VPNResponse
|
||||
* @throws JSONException Throws when JSON is not formatted properly.
|
||||
*/
|
||||
public static VPNResponse fromJson(JSONObject jsonObject) throws JSONException {
|
||||
if(jsonObject.getBoolean("success")) {
|
||||
return new VPNResponse(jsonObject.getString("asn"), jsonObject.getString("ip"),
|
||||
jsonObject.getString("countryName"), jsonObject.getString("countryCode"),
|
||||
jsonObject.getString("city"), jsonObject.getString("timeZone"),
|
||||
jsonObject.has("method") ? jsonObject.getString("method") : "N/A",
|
||||
jsonObject.getString("isp"), jsonObject.getBoolean("proxy"),
|
||||
jsonObject.getString("isp"), "N/A", jsonObject.getBoolean("proxy"),
|
||||
jsonObject.getBoolean("cached"), jsonObject.getBoolean("success"),
|
||||
jsonObject.getDouble("latitude"), jsonObject.getDouble("longitude"),
|
||||
jsonObject.getLong("lastAccess"), jsonObject.getInt("queriesLeft"));
|
||||
} else {
|
||||
return VPNResponse.builder().success(false)
|
||||
.failureReason(jsonObject.getString("failureReason")).build();
|
||||
}
|
||||
|
||||
return new VPNResponse(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
#####################################################
|
||||
# KauriVPN Config #
|
||||
# by Brighten Development #
|
||||
#####################################################
|
||||
# If you find that you run out of your free 20,000 queries, you may purchase a license on https://funkemunky.cc/shop
|
||||
license: ''
|
||||
# Message only sent with commands disabled below. Supports color codes ('&')
|
||||
kickMessage: Proxies are not allowed on our server
|
||||
# Caching results will lower your query usage, but results may be out of date.
|
||||
cachedResults: true
|
||||
# All players with any of the following characters in the beginning of their name will be whitelisted
|
||||
# This is a useful feature for servers that allow players through Geyser and their IPs are not forwarded, causing
|
||||
# players to be removed falsely for use of proxy.
|
||||
prefixWhitelists:
|
||||
- "*"
|
||||
# Configure your database here.
|
||||
database:
|
||||
# Enable to cache queries and save alerts state beyond restarts
|
||||
enabled: true
|
||||
useCredentials: false
|
||||
#Options Mongo, MySQL, or H2
|
||||
type: H2
|
||||
# The database name you would like to use
|
||||
database: kaurivpn
|
||||
# Can be used if you prefer to authenticate via Mongo URL
|
||||
mongoURL: ''
|
||||
# Your database username
|
||||
username: root
|
||||
# Your database password
|
||||
password: password
|
||||
# The IP of your database goes here
|
||||
ip: localhost
|
||||
# -1 will use default port of databases (MySQL:3306, Mongo:27017). Otherwise, enter alternative ports here.
|
||||
port: -1
|
||||
commands:
|
||||
# Enable this to override the default kick function of the plugin with your own commands
|
||||
enabled: false
|
||||
# List of commands to run when a player is detected to be using a proxy. Supports color codes ('&')
|
||||
execute:
|
||||
- kick %player% VPNs are not allowed on our server!
|
||||
# Enable/disable the default kicking feature of KauriVPN.
|
||||
kickPlayers: true
|
||||
# Configure all alerting functionality
|
||||
alerts:
|
||||
# You may set to 'false' to disable all alerts functionality
|
||||
enabled: true
|
||||
# Message to send to users with alerts enabled
|
||||
# Placeholders: %country% (Country name), %player% (Player name), %reason% (Proxy detection method),
|
||||
# %city% (City name).
|
||||
message: '&8[&6KauriVPN&8] &e%player% &7has joined on a VPN/proxy &8(&f%reason%&8)
|
||||
&7in location &8(&f%city%&7, &f%country%&8)'
|
||||
# Configuration for country gatekeepings
|
||||
countries:
|
||||
# You must use ISO codes for country configuration: https://www.iban.com/country-codes
|
||||
# Leave empty to disable this configuration
|
||||
list: []
|
||||
# Set whitelist to true to only allow listed country codes, and false to deny listed country codes.
|
||||
whitelist: true
|
||||
# The commands to be run if the player is not allowed on the server with the above configured conditions
|
||||
# Placeholders: %country% (Country name), %player% (Player name), %code% (Country ISO Code)
|
||||
# Keep this empty with "[]" if you want to use the built in kicking system.
|
||||
commands: []
|
||||
# The kick message that will be used if commands are configured to use the built-in kicking sytem.
|
||||
# PlaceHolders: %country% (Country name), %player% (Player name), %code% (Country ISO Code)
|
||||
vanillaKickReason: |-
|
||||
&cSorry, but our server does not allow connections from
|
||||
&f%country%
|
||||
# This will disable any information being sent to https://bstats.org. We recommend you keep this enabled as it helps
|
||||
# us understand our users and put effort where it is needed. All information sent goes under their privacy as seen
|
||||
# here: https://bstats.org/privacy-policy
|
||||
bstats: true
|
||||
# Here you can configure messages for KauriVPN.
|
||||
messages:
|
||||
command-misc-playerRequired: '&cYou must be a player to execute this command!'
|
||||
command-alerts-toggled: '&7Your player proxy notifications have been set to: &e%state%'
|
||||
command-reload-complete: '&aSuccessfully reloaded KauriVPN plugin!'
|
||||
no-permission: '&cNo permission.'
|
||||
|
||||
+201
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -0,0 +1,10 @@
|
||||
[](https://sourcespy.com/github/funkemunkyantivpn/)
|
||||
|
||||
# AntiVPN
|
||||
An antivpn plugin utilizing the KauriVPN API
|
||||
|
||||
## The Fastest Available
|
||||
Just a simple plugin using an incredibly fast and accurate API.
|
||||
|
||||
## SpigotMC Page
|
||||
Rate and support the project on SpigotMC: https://www.spigotmc.org/resources/kaurivpn-anti-proxy-tor-and-vpn-free-api.93355/
|
||||
+177
@@ -0,0 +1,177 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>AntiVPN</artifactId>
|
||||
<groupId>dev.brighten.antivpn</groupId>
|
||||
<version>1.9.4</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>Sponge</artifactId>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spongepowered-repo</id>
|
||||
<url>https://repo.spongepowered.org/maven/</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>funkemunky-releases</id>
|
||||
<url>https://nexus.funkemunky.cc/content/repositories/releases/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.spongepowered</groupId>
|
||||
<artifactId>spongeapi</artifactId>
|
||||
<version>11.0.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>dev.brighten.antivpn</groupId>
|
||||
<artifactId>Common</artifactId>
|
||||
<version>1.9.4</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.ben-manes.caffeine</groupId>
|
||||
<artifactId>caffeine</artifactId>
|
||||
<version>3.1.8</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mongodb</groupId>
|
||||
<artifactId>mongo-java-driver</artifactId>
|
||||
<version>3.12.14</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.mysql</groupId>
|
||||
<artifactId>mysql-connector-j</artifactId>
|
||||
<version>9.1.0</version>
|
||||
<type>jar</type>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<version>2.2.220</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
<maven.compiler.target>21</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.13.0</version>
|
||||
<configuration>
|
||||
<source>21</source>
|
||||
<target>21</target>
|
||||
<compilerArgument>-XDignore.symbol.file</compilerArgument>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.6.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<filters>
|
||||
<filter>
|
||||
<artifact>*:*</artifact>
|
||||
<excludes>
|
||||
<exclude>com/google/**</exclude>
|
||||
<exclude>org/objectweb/**</exclude>
|
||||
<exclude>org/checkerframework/**</exclude>
|
||||
</excludes>
|
||||
</filter>
|
||||
|
||||
</filters>
|
||||
<relocations>
|
||||
<relocation>
|
||||
<pattern>org.yaml.snakeyaml</pattern>
|
||||
<shadedPattern>dev.brighten.antivpn.shaded.org.yaml.snakeyaml</shadedPattern>
|
||||
<excludes>
|
||||
<!-- Exclude annotation values from relocation -->
|
||||
<exclude>dev.brighten.antivpn.depends.Relocate</exclude>
|
||||
<exclude>dev.brighten.antivpn.depends.MavenLibraries</exclude>
|
||||
</excludes>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>com.github.benmanes.caffeine</pattern>
|
||||
<shadedPattern>dev.brighten.antivpn.shaded.com.github.benmanes.caffeine</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>org.h2</pattern>
|
||||
<shadedPattern>dev.brighten.antivpn.shaded.org.h2</shadedPattern>
|
||||
<excludes>
|
||||
<!-- Exclude annotation values from relocation -->
|
||||
<exclude>dev.brighten.antivpn.depends.Relocate</exclude>
|
||||
<exclude>dev.brighten.antivpn.depends.MavenLibraries</exclude>
|
||||
</excludes>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>org.bson</pattern>
|
||||
<shadedPattern>dev.brighten.antivpn.shaded.org.bson</shadedPattern>
|
||||
<excludes>
|
||||
<!-- Exclude annotation values from relocation -->
|
||||
<exclude>dev.brighten.antivpn.depends.Relocate</exclude>
|
||||
<exclude>dev.brighten.antivpn.depends.MavenLibraries</exclude>
|
||||
</excludes>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>com.mongodb</pattern>
|
||||
<shadedPattern>dev.brighten.antivpn.shaded.com.mongodb</shadedPattern>
|
||||
<excludes>
|
||||
<!-- Exclude annotation values from relocation -->
|
||||
<exclude>dev.brighten.antivpn.depends.Relocate</exclude>
|
||||
<exclude>dev.brighten.antivpn.depends.MavenLibraries</exclude>
|
||||
</excludes>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>com.mysql.cj</pattern>
|
||||
<shadedPattern>dev.brighten.antivpn.shaded.com.mysql.cj</shadedPattern>
|
||||
<excludes>
|
||||
<!-- Exclude annotation values from relocation -->
|
||||
<exclude>dev.brighten.antivpn.depends.Relocate</exclude>
|
||||
<exclude>dev.brighten.antivpn.depends.MavenLibraries</exclude>
|
||||
</excludes>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>com.mysql.jdbc</pattern>
|
||||
<shadedPattern>dev.brighten.antivpn.shaded.com.mysql.jdbc</shadedPattern>
|
||||
<excludes>
|
||||
<!-- Exclude annotation values from relocation -->
|
||||
<exclude>dev.brighten.antivpn.depends.Relocate</exclude>
|
||||
<exclude>dev.brighten.antivpn.depends.MavenLibraries</exclude>
|
||||
</excludes>
|
||||
</relocation>
|
||||
</relocations>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<filtering>true</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,114 @@
|
||||
package dev.brighten.antivpn.sponge;
|
||||
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
import dev.brighten.antivpn.api.*;
|
||||
import dev.brighten.antivpn.sponge.util.StringUtil;
|
||||
import dev.brighten.antivpn.utils.Tuple;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.spongepowered.api.Sponge;
|
||||
import org.spongepowered.api.command.exception.CommandException;
|
||||
import org.spongepowered.api.event.Listener;
|
||||
import org.spongepowered.api.event.Order;
|
||||
import org.spongepowered.api.event.network.ServerSideConnectionEvent;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class SpongeListener extends VPNExecutor {
|
||||
|
||||
@Listener(order = Order.EARLY)
|
||||
public void onJoin(ServerSideConnectionEvent.Login event) {
|
||||
AtomicReference<APIPlayer> player = new AtomicReference<>(AntiVPN.getInstance().getPlayerExecutor()
|
||||
.getPlayer(event.profile().uuid())
|
||||
.orElse(new OfflinePlayer(
|
||||
event.profile().uuid(),
|
||||
event.profile().name().orElse("Unknown"),
|
||||
event.connection().address().getAddress()
|
||||
)));
|
||||
|
||||
CheckResult instantResult = player.get().checkPlayer(result -> {
|
||||
if(result.resultType().isShouldBlock()) {
|
||||
AntiVPN.getInstance().getExecutor().getToKick().add(new Tuple<>(result, player.get().getUuid()));
|
||||
}
|
||||
});
|
||||
|
||||
if(!instantResult.resultType().isShouldBlock()) {
|
||||
return;
|
||||
}
|
||||
|
||||
AntiVPN.getInstance().getExecutor().getToKick().add(new Tuple<>(instantResult, player.get().getUuid()));
|
||||
|
||||
if(!AntiVPN.getInstance().getVpnConfig().kickPlayersOnDetect()) {
|
||||
return;
|
||||
}
|
||||
|
||||
AntiVPN.getInstance().getExecutor().log(Level.INFO, "%s was kicked from cache with IP %s", player.get().getName(), instantResult.response().getIp());
|
||||
|
||||
event.setCancelled(true);
|
||||
switch (instantResult.resultType()) {
|
||||
case DENIED_PROXY -> {
|
||||
AntiVPN.getInstance().getExecutor().log(Level.INFO, player.get().getName()
|
||||
+ " joined on a VPN/Proxy (" + instantResult.response().getMethod() + ")");
|
||||
event.setMessage(Component.text(StringUtil
|
||||
.translateColorCodes('&', AntiVPN.getInstance().getVpnConfig()
|
||||
.getKickString()
|
||||
.replace("%player%", player.get().getName())
|
||||
.replace("%country%", instantResult.response().getCountryName())
|
||||
.replace("%code%", instantResult.response().getCountryCode()))));
|
||||
}
|
||||
case DENIED_COUNTRY ->
|
||||
event.setMessage(Component.text(StringUtil
|
||||
.translateColorCodes('&', AntiVPN.getInstance().getVpnConfig()
|
||||
.countryVanillaKickReason()
|
||||
.replace("%player%", player.get().getName())
|
||||
.replace("%country%", instantResult.response().getCountryName())
|
||||
.replace("%code%", instantResult.response().getCountryCode()))));
|
||||
}
|
||||
}
|
||||
|
||||
@Listener
|
||||
public void onPlayerDisconnect(ServerSideConnectionEvent.Disconnect event) {
|
||||
event.profile().ifPresent(profile ->
|
||||
AntiVPN.getInstance().getPlayerExecutor().unloadPlayer(profile.uuid()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerListeners() {
|
||||
Sponge.eventManager().registerListeners(SpongePlugin.getInstance().getContainer(), this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(Level level, String log, Object... objects) {
|
||||
if (level.equals(Level.SEVERE)) {
|
||||
SpongePlugin.getInstance().getLogger().error(String.format(log, objects));
|
||||
} else if (level.equals(Level.WARNING)) {
|
||||
SpongePlugin.getInstance().getLogger().warn(String.format(log, objects));
|
||||
} else {
|
||||
SpongePlugin.getInstance().getLogger().info(String.format(log, objects));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(String log, Object... objects) {
|
||||
log(Level.INFO, String.format(log, objects));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logException(String message, Throwable ex) {
|
||||
SpongePlugin.getInstance().getLogger().error(message, ex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runCommand(String command) {
|
||||
try {
|
||||
Sponge.server().commandManager().process(Sponge.systemSubject(), command);
|
||||
} catch (CommandException e) {
|
||||
logException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disablePlugin() {
|
||||
AntiVPN.getInstance().getExecutor().log(Level.INFO, "Disabling listeners for plugin...");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package dev.brighten.antivpn.sponge;
|
||||
|
||||
import dev.brighten.antivpn.api.APIPlayer;
|
||||
import dev.brighten.antivpn.sponge.util.StringUtil;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.spongepowered.api.entity.living.player.server.ServerPlayer;
|
||||
|
||||
public class SpongePlayer extends APIPlayer {
|
||||
|
||||
private final ServerPlayer player;
|
||||
|
||||
public SpongePlayer(ServerPlayer player) {
|
||||
super(player.uniqueId(), player.name(), player.connection().address().getAddress());
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(String message) {
|
||||
player.sendMessage(Component.text(StringUtil.translateColorCodes('&', message)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void kickPlayer(String reason) {
|
||||
player.kick(Component.text(StringUtil.translateColorCodes('&', reason)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(String permission) {
|
||||
return player.hasPermission(permission);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package dev.brighten.antivpn.sponge;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.Cache;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import dev.brighten.antivpn.api.APIPlayer;
|
||||
import dev.brighten.antivpn.api.PlayerExecutor;
|
||||
import org.spongepowered.api.Sponge;
|
||||
import org.spongepowered.api.entity.living.player.server.ServerPlayer;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class SpongePlayerExecutor implements PlayerExecutor {
|
||||
|
||||
private final Cache<UUID, SpongePlayer> playerCache = Caffeine.newBuilder().maximumSize(10000)
|
||||
.expireAfterAccess(30, TimeUnit.MINUTES)
|
||||
.build();
|
||||
|
||||
@Override
|
||||
public Optional<APIPlayer> getPlayer(String name) {
|
||||
Optional<ServerPlayer> serverPlayer = Sponge.server().player(name);
|
||||
|
||||
return serverPlayer.map(SpongePlayer::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<APIPlayer> getPlayer(UUID uuid) {
|
||||
SpongePlayer cachedPlayer = playerCache.getIfPresent(uuid);
|
||||
|
||||
if(cachedPlayer != null) {
|
||||
return Optional.of(cachedPlayer);
|
||||
}
|
||||
|
||||
Optional<ServerPlayer> serverPlayer = Sponge.server().player(uuid);
|
||||
|
||||
Optional<APIPlayer> player = serverPlayer.map(SpongePlayer::new);
|
||||
|
||||
player.ifPresent(value -> playerCache.put(uuid, (SpongePlayer) value));
|
||||
|
||||
return player;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unloadPlayer(UUID uuid) {
|
||||
playerCache.invalidate(uuid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<APIPlayer> getOnlinePlayers() {
|
||||
if(!Sponge.game().isServerAvailable()) return Collections.emptyList();
|
||||
return Sponge.server().onlinePlayers()
|
||||
.stream()
|
||||
.map(pl -> {
|
||||
SpongePlayer cachedPlayer = playerCache.getIfPresent(pl.uniqueId());
|
||||
|
||||
if(cachedPlayer != null) {
|
||||
return cachedPlayer;
|
||||
}
|
||||
|
||||
SpongePlayer player = new SpongePlayer(pl);
|
||||
playerCache.put(pl.uniqueId(), player);
|
||||
|
||||
return (APIPlayer) player;
|
||||
})
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package dev.brighten.antivpn.sponge;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
import dev.brighten.antivpn.sponge.command.SpongeCommand;
|
||||
import lombok.Getter;
|
||||
import org.spongepowered.api.Server;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.spongepowered.api.Sponge;
|
||||
import org.spongepowered.api.command.Command;
|
||||
import org.spongepowered.api.config.ConfigManager;
|
||||
import org.spongepowered.api.event.Listener;
|
||||
import org.spongepowered.api.event.lifecycle.*;
|
||||
import org.spongepowered.plugin.PluginContainer;
|
||||
import org.spongepowered.plugin.builtin.jvm.Plugin;
|
||||
|
||||
@Plugin("kaurivpn")
|
||||
@Getter
|
||||
public class SpongePlugin {
|
||||
|
||||
//Plugin init
|
||||
@Inject
|
||||
private PluginContainer container;
|
||||
@Inject
|
||||
private Logger logger;
|
||||
@Getter
|
||||
private static SpongePlugin instance;
|
||||
|
||||
@Listener
|
||||
public void onConstruct(final ConstructPluginEvent event) {
|
||||
instance = this;
|
||||
|
||||
ConfigManager configManager = Sponge.configManager();
|
||||
SpongeListener spongeListener = new SpongeListener();
|
||||
|
||||
var path = configManager.sharedConfig(container).directory();
|
||||
|
||||
logger.info("Fucking path: " + path);
|
||||
|
||||
AntiVPN.start(spongeListener, new SpongePlayerExecutor(), path.toFile());
|
||||
}
|
||||
|
||||
@Listener
|
||||
public void onServer(final StoppingEngineEvent<Server> event) {
|
||||
AntiVPN.getInstance().getExecutor().disablePlugin();
|
||||
}
|
||||
|
||||
@Listener
|
||||
public void onRegisterRawCommands(final RegisterCommandEvent<Command.Raw> event){
|
||||
if(AntiVPN.getInstance() == null) {
|
||||
for(int i = 0 ; i < 5 ; i++) System.out.println("FUCKING NULL");
|
||||
return;
|
||||
}
|
||||
AntiVPN.getInstance().getExecutor().log("Registering commands...");
|
||||
for (dev.brighten.antivpn.command.Command command : AntiVPN.getInstance().getCommands()) {
|
||||
AntiVPN.getInstance().getExecutor().log("Registering command %s...", command.name());
|
||||
event.register(this.container, new SpongeCommand(command), command.name(), command.aliases());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
package dev.brighten.antivpn.sponge.command;
|
||||
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
import dev.brighten.antivpn.command.Command;
|
||||
import dev.brighten.antivpn.command.CommandExecutor;
|
||||
import dev.brighten.antivpn.utils.StringUtil;
|
||||
import lombok.val;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.spongepowered.api.command.CommandCause;
|
||||
import org.spongepowered.api.command.CommandCompletion;
|
||||
import org.spongepowered.api.command.CommandResult;
|
||||
import org.spongepowered.api.command.parameter.ArgumentReader;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
public class SpongeCommand implements org.spongepowered.api.command.Command.Raw {
|
||||
|
||||
private final Command command;
|
||||
|
||||
public SpongeCommand(Command command) {
|
||||
this.command = command;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandResult process(CommandCause sender, ArgumentReader.Mutable arguments) {
|
||||
|
||||
String[] args = arguments.input().split(" ");
|
||||
|
||||
CommandExecutor commandExecutor = new SpongeCommandExecutor(sender);
|
||||
|
||||
val children = command.children();
|
||||
|
||||
if(children.length > 0 && args.length > 0) {
|
||||
for (dev.brighten.antivpn.command.Command child : children) {
|
||||
if(child.name().equalsIgnoreCase(args[0]) || Arrays.stream(child.aliases())
|
||||
.anyMatch(alias -> alias.equalsIgnoreCase(args[0]))) {
|
||||
if(!sender.hasPermission("antivpn.command.*")
|
||||
&& !sender.hasPermission(child.permission())) {
|
||||
return CommandResult.error(Component.text(StringUtil.translateAlternateColorCodes('&',
|
||||
AntiVPN.getInstance().getMessageHandler().getString("no-permission").getMessage())));
|
||||
}
|
||||
|
||||
commandExecutor.sendMessage(StringUtil
|
||||
.translateAlternateColorCodes('&',
|
||||
child.execute(commandExecutor, IntStream
|
||||
.range(0, args.length - 1)
|
||||
.mapToObj(i -> args[i + 1]).toArray(String[]::new))));
|
||||
return CommandResult.success();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
commandExecutor.sendMessage(StringUtil
|
||||
.translateAlternateColorCodes('&',
|
||||
command.execute(new SpongeCommandExecutor(sender), args)));
|
||||
|
||||
command.execute(new SpongeCommandExecutor(sender), args);
|
||||
return CommandResult.success();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<CommandCompletion> complete(CommandCause sender, ArgumentReader.Mutable arguments) {
|
||||
val children = command.children();
|
||||
String[] args = arguments.input().split(" ");
|
||||
if(children.length > 0 && args.length > 0) {
|
||||
for (dev.brighten.antivpn.command.Command child : children) {
|
||||
if(child.name().equalsIgnoreCase(args[0]) || Arrays.stream(child.aliases())
|
||||
.anyMatch(alias2 -> alias2.equalsIgnoreCase(args[0]))) {
|
||||
return child.tabComplete(new SpongeCommandExecutor(sender), "alias", IntStream
|
||||
.range(0, args.length - 1)
|
||||
.mapToObj(i -> args[i + 1]).toArray(String[]::new))
|
||||
.stream()
|
||||
.map(CommandCompletion::of)
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
}
|
||||
return command.tabComplete(new SpongeCommandExecutor(sender), "alias", args)
|
||||
.stream()
|
||||
.map(CommandCompletion::of)
|
||||
.toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canExecute(CommandCause cause) {
|
||||
return cause.hasPermission(command.permission());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Component> shortDescription(CommandCause cause) {
|
||||
return command.description() != null ? Optional.of(Component.text(command.description())) : Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Component> extendedDescription(CommandCause cause) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Component> help(@NonNull CommandCause cause) {
|
||||
return Optional.of(Component.text(StringUtil.translateAlternateColorCodes('&',
|
||||
command.execute(new SpongeCommandExecutor(cause), new String[0]))));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component usage(CommandCause cause) {
|
||||
return command.usage() != null ? Component.text(command.usage()) : Component.empty();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package dev.brighten.antivpn.sponge.command;
|
||||
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
import dev.brighten.antivpn.api.APIPlayer;
|
||||
import dev.brighten.antivpn.command.CommandExecutor;
|
||||
import dev.brighten.antivpn.sponge.util.StringUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.spongepowered.api.command.CommandCause;
|
||||
import org.spongepowered.api.entity.living.player.server.ServerPlayer;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class SpongeCommandExecutor implements CommandExecutor {
|
||||
|
||||
private final CommandCause cause;
|
||||
|
||||
@Override
|
||||
public void sendMessage(String message, Object... objects) {
|
||||
cause.sendMessage(Component.text(StringUtil.translateColorCodes('&',
|
||||
String.format(message, objects))));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(String permission) {
|
||||
return cause.hasPermission(permission);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<APIPlayer> getPlayer() {
|
||||
if(cause.subject() instanceof ServerPlayer serverPlayer) {
|
||||
return AntiVPN.getInstance().getPlayerExecutor().getPlayer(serverPlayer.uniqueId());
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPlayer() {
|
||||
return cause.subject() instanceof ServerPlayer;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package dev.brighten.antivpn.sponge.util;
|
||||
|
||||
public class StringUtil {
|
||||
|
||||
public static String translateColorCodes(char altColorChar, String textToTranslate) {
|
||||
char[] b = textToTranslate.toCharArray();
|
||||
|
||||
for(int i = 0; i < b.length - 1; ++i) {
|
||||
if (b[i] == altColorChar && "0123456789AaBbCcDdEeFfKkLlMmNnOoRr".indexOf(b[i + 1]) > -1) {
|
||||
b[i] = 167;
|
||||
b[i + 1] = Character.toLowerCase(b[i + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
return new String(b);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"loader": {
|
||||
"name": "java_plain",
|
||||
"version": "1.0"
|
||||
},
|
||||
"license": "All-Rights-Reserved",
|
||||
"plugins": [
|
||||
{
|
||||
"id": "kaurivpn",
|
||||
"name": "Kauri VPN",
|
||||
"version": "${version}",
|
||||
"entrypoint": "dev.brighten.antivpn.sponge.SpongePlugin",
|
||||
"description": "A simple and fast antivpn plugin.",
|
||||
"branding": {},
|
||||
"links": {
|
||||
},
|
||||
"contributors": [
|
||||
],
|
||||
"dependencies": [
|
||||
{
|
||||
"id": "spongeapi",
|
||||
"version": "11.0.0",
|
||||
"load-order": "after",
|
||||
"optional": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>dev.brighten.antivpn</groupId>
|
||||
<artifactId>AntiVPN</artifactId>
|
||||
<version>1.9.4</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>Universal</artifactId>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>dev.brighten.antivpn</groupId>
|
||||
<artifactId>Common</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>dev.brighten.antivpn</groupId>
|
||||
<artifactId>Bukkit</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>dev.brighten.antivpn</groupId>
|
||||
<artifactId>Bungee</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>dev.brighten.antivpn</groupId>
|
||||
<artifactId>Velocity</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.6.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<finalName>AntiVPN-${project.version}-universal</finalName>
|
||||
<createDependencyReducedPom>false</createDependencyReducedPom>
|
||||
<transformers>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
|
||||
</transformers>
|
||||
<relocations>
|
||||
<relocation>
|
||||
<pattern>org.yaml.snakeyaml</pattern>
|
||||
<shadedPattern>dev.brighten.antivpn.shaded.org.yaml.snakeyaml</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>org.bstats</pattern>
|
||||
<shadedPattern>dev.brighten.antivpn.shaded.org.bstats</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>org.objectweb</pattern>
|
||||
<shadedPattern>dev.brighten.antivpn.shaded.org.objectweb</shadedPattern>
|
||||
</relocation>
|
||||
<!-- Add other relocations from Common/pom.xml -->
|
||||
</relocations>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,94 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>AntiVPN</artifactId>
|
||||
<groupId>dev.brighten.antivpn</groupId>
|
||||
<version>1.9.4</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>Velocity</artifactId>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>velocity</id>
|
||||
<url>https://nexus.velocitypowered.com/repository/maven-public/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.velocitypowered</groupId>
|
||||
<artifactId>velocity-api</artifactId>
|
||||
<version>3.4.0-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>dev.brighten.antivpn</groupId>
|
||||
<artifactId>Common</artifactId>
|
||||
<version>1.9.4</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bstats</groupId>
|
||||
<artifactId>bstats-velocity</artifactId>
|
||||
<version>2.2.1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.13.0</version>
|
||||
<configuration>
|
||||
<source>17</source>
|
||||
<target>17</target>
|
||||
<compilerArgument>-XDignore.symbol.file</compilerArgument>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.6.0</version>
|
||||
<configuration>
|
||||
<relocations>
|
||||
<relocation>
|
||||
<pattern>org.bstats</pattern>
|
||||
<!-- Replace this with your package! -->
|
||||
<shadedPattern>dev.brighten.antivpn.velocity.org.bstats</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>org.yaml.snakeyaml</pattern>
|
||||
<shadedPattern>dev.brighten.antivpn.shaded.org.yaml.snakeyaml</shadedPattern>
|
||||
</relocation>
|
||||
</relocations>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<filtering>true</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,197 @@
|
||||
package dev.brighten.antivpn.velocity;
|
||||
|
||||
import com.velocitypowered.api.event.ResultedEvent;
|
||||
import com.velocitypowered.api.event.connection.DisconnectEvent;
|
||||
import com.velocitypowered.api.event.connection.LoginEvent;
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
import dev.brighten.antivpn.api.APIPlayer;
|
||||
import dev.brighten.antivpn.api.CheckResult;
|
||||
import dev.brighten.antivpn.api.OfflinePlayer;
|
||||
import dev.brighten.antivpn.api.VPNExecutor;
|
||||
import dev.brighten.antivpn.utils.StringUtil;
|
||||
import dev.brighten.antivpn.web.objects.VPNResponse;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class VelocityListener extends VPNExecutor {
|
||||
|
||||
@Override
|
||||
public void registerListeners() {
|
||||
VelocityPlugin.INSTANCE.getServer().getEventManager()
|
||||
.register(VelocityPlugin.INSTANCE, this);
|
||||
|
||||
VelocityPlugin.INSTANCE.getServer().getEventManager().register(VelocityPlugin.INSTANCE, DisconnectEvent.class,
|
||||
event -> AntiVPN.getInstance()
|
||||
.getPlayerExecutor()
|
||||
.unloadPlayer(event.getPlayer().getUniqueId()));
|
||||
|
||||
VelocityPlugin.INSTANCE.getServer().getEventManager().register(VelocityPlugin.INSTANCE, LoginEvent.class,
|
||||
event -> {
|
||||
APIPlayer player = AntiVPN.getInstance().getPlayerExecutor().getPlayer(event.getPlayer().getUniqueId())
|
||||
.orElse(new OfflinePlayer(
|
||||
event.getPlayer().getUniqueId(),
|
||||
event.getPlayer().getUsername(),
|
||||
event.getPlayer().getRemoteAddress().getAddress()
|
||||
));
|
||||
|
||||
CheckResult instantResult = player.checkPlayer(result -> {
|
||||
if(!result.resultType().isShouldBlock()) return;
|
||||
|
||||
handleDeniedTasks(event, result);
|
||||
});
|
||||
|
||||
if(!instantResult.resultType().isShouldBlock()) return;
|
||||
|
||||
switch (instantResult.resultType()) {
|
||||
case DENIED_COUNTRY -> event.setResult(ResultedEvent.ComponentResult.denied(
|
||||
LegacyComponentSerializer.builder()
|
||||
.character('&')
|
||||
.build().deserialize(AntiVPN.getInstance().getVpnConfig()
|
||||
.countryVanillaKickReason()
|
||||
.replace("%player%", event.getPlayer().getUsername())
|
||||
.replace("%country%", instantResult.response().getCountryName())
|
||||
.replace("%code%", instantResult.response().getCountryCode()))));
|
||||
case DENIED_PROXY -> {
|
||||
VelocityPlugin.INSTANCE.getLogger().info(event.getPlayer().getUsername()
|
||||
+ " joined on a VPN/Proxy (" + instantResult.response().getMethod() + ")");
|
||||
event.setResult(ResultedEvent.ComponentResult.denied(LegacyComponentSerializer.builder()
|
||||
.character('&')
|
||||
.build().deserialize(AntiVPN.getInstance().getVpnConfig()
|
||||
.getKickString()
|
||||
.replace("%player%", event.getPlayer().getUsername())
|
||||
.replace("%country%", instantResult.response().getCountryName())
|
||||
.replace("%code%", instantResult.response().getCountryCode()))));
|
||||
}
|
||||
}
|
||||
|
||||
handleDeniedTasks(event, instantResult, true);
|
||||
});
|
||||
}
|
||||
|
||||
private void handleDeniedTasks(LoginEvent event, CheckResult result) {
|
||||
handleDeniedTasks(event, result, false);
|
||||
}
|
||||
|
||||
private void handleDeniedTasks(LoginEvent event, CheckResult checkResult, boolean deniedOnLogin) {
|
||||
VPNResponse result = checkResult.response();
|
||||
//Ensuring the user wishes to alert to staff
|
||||
if (AntiVPN.getInstance().getVpnConfig().alertToStaff())
|
||||
AntiVPN.getInstance().getPlayerExecutor().getOnlinePlayers().stream()
|
||||
.filter(APIPlayer::isAlertsEnabled)
|
||||
.forEach(pl ->
|
||||
pl.sendMessage(dev.brighten.antivpn.AntiVPN.getInstance().getVpnConfig()
|
||||
.alertMessage()
|
||||
.replace("%player%",
|
||||
event.getPlayer().getUsername())
|
||||
.replace("%reason%",
|
||||
result.getMethod())
|
||||
.replace("%country%",
|
||||
result.getCountryName())
|
||||
.replace("%city%",
|
||||
result.getCity())));
|
||||
|
||||
if(deniedOnLogin) return;
|
||||
|
||||
//In case the user wants to run their own commands instead of using the
|
||||
// built in kicking
|
||||
|
||||
if(AntiVPN.getInstance().getVpnConfig().kickPlayersOnDetect()) {
|
||||
switch (checkResult.resultType()) {
|
||||
case DENIED_PROXY -> VelocityPlugin.INSTANCE.getServer().getScheduler()
|
||||
.buildTask(VelocityPlugin.INSTANCE, () ->
|
||||
event.getPlayer().disconnect(LegacyComponentSerializer.builder()
|
||||
.character('&')
|
||||
.build().deserialize(AntiVPN.getInstance().getVpnConfig()
|
||||
.getKickString()
|
||||
.replace("%player%", event.getPlayer().getUsername())
|
||||
.replace("%country%", result.getCountryName())
|
||||
.replace("%code%", result.getCountryCode()))))
|
||||
.delay(1, TimeUnit.SECONDS).schedule();
|
||||
case DENIED_COUNTRY -> VelocityPlugin.INSTANCE.getServer().getScheduler()
|
||||
.buildTask(VelocityPlugin.INSTANCE, () ->
|
||||
event.getPlayer().disconnect(LegacyComponentSerializer.builder()
|
||||
.character('&')
|
||||
.build().deserialize(AntiVPN.getInstance().getVpnConfig()
|
||||
.countryVanillaKickReason()
|
||||
.replace("%player%", event.getPlayer().getUsername())
|
||||
.replace("%country%", result.getCountryName())
|
||||
.replace("%code%", result.getCountryCode()))))
|
||||
.delay(1, TimeUnit.SECONDS).schedule();
|
||||
}
|
||||
}
|
||||
|
||||
if(!AntiVPN.getInstance().getVpnConfig().runCommands()) return;
|
||||
|
||||
switch (checkResult.resultType()) {
|
||||
case DENIED_PROXY -> {
|
||||
for (String command : AntiVPN.getInstance().getVpnConfig().commands()) {
|
||||
VelocityPlugin.INSTANCE.getServer().getCommandManager()
|
||||
.executeAsync(VelocityPlugin.INSTANCE.getServer()
|
||||
.getConsoleCommandSource(),
|
||||
StringUtil.translateAlternateColorCodes('&',
|
||||
StringUtil.varReplace(
|
||||
command,
|
||||
AntiVPN.getInstance().getPlayerExecutor()
|
||||
.getPlayer(event.getPlayer().getUniqueId())
|
||||
.orElse(new OfflinePlayer(
|
||||
event.getPlayer().getUniqueId(),
|
||||
event.getPlayer().getUsername(),
|
||||
event.getPlayer().getRemoteAddress().getAddress())
|
||||
),
|
||||
result)));
|
||||
}
|
||||
}
|
||||
case DENIED_COUNTRY -> {
|
||||
for (String cmd : AntiVPN.getInstance().getVpnConfig().countryKickCommands()) {
|
||||
final String formattedCommand = StringUtil
|
||||
.translateAlternateColorCodes('&',
|
||||
StringUtil.varReplace(
|
||||
cmd,
|
||||
AntiVPN.getInstance().getPlayerExecutor()
|
||||
.getPlayer(event.getPlayer().getUniqueId())
|
||||
.orElse(new OfflinePlayer(
|
||||
event.getPlayer().getUniqueId(),
|
||||
event.getPlayer().getUsername(),
|
||||
event.getPlayer().getRemoteAddress().getAddress())
|
||||
),
|
||||
result));
|
||||
// Running the command from console
|
||||
runCommand(formattedCommand);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(Level level, String log, Object... objects) {
|
||||
VelocityPlugin.INSTANCE.getLogger().log(level, String.format(log, objects));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(String log, Object... objects) {
|
||||
log(Level.INFO, String.format(log, objects));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logException(String message, Throwable ex) {
|
||||
VelocityPlugin.INSTANCE.getLogger().log(Level.SEVERE, message, ex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runCommand(String command) {
|
||||
VelocityPlugin.INSTANCE.getServer().getCommandManager()
|
||||
.executeAsync(VelocityPlugin.INSTANCE.getServer()
|
||||
.getConsoleCommandSource(),
|
||||
StringUtil.translateAlternateColorCodes('&',
|
||||
command));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disablePlugin() {
|
||||
VelocityPlugin.INSTANCE.getServer().getEventManager().unregisterListener(VelocityPlugin.INSTANCE, this);
|
||||
VelocityPlugin.INSTANCE.getServer().getCommandManager().unregister("antivpn");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package dev.brighten.antivpn.velocity;
|
||||
|
||||
import com.velocitypowered.api.proxy.Player;
|
||||
import dev.brighten.antivpn.api.APIPlayer;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
|
||||
public class VelocityPlayer extends APIPlayer {
|
||||
|
||||
private final Player player;
|
||||
public VelocityPlayer(Player player) {
|
||||
super(player.getUniqueId(), player.getUsername(), player.getRemoteAddress().getAddress());
|
||||
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void sendMessage(String message) {
|
||||
player.sendMessage(LegacyComponentSerializer.builder().character('&').build().deserialize(message));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void kickPlayer(String reason) {
|
||||
player.disconnect(LegacyComponentSerializer.builder().character('&').build().deserialize(reason));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(String permission) {
|
||||
return player.hasPermission(permission);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package dev.brighten.antivpn.velocity;
|
||||
|
||||
import com.velocitypowered.api.proxy.Player;
|
||||
import dev.brighten.antivpn.api.APIPlayer;
|
||||
import dev.brighten.antivpn.api.PlayerExecutor;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class VelocityPlayerExecutor implements PlayerExecutor {
|
||||
|
||||
private final Map<UUID, VelocityPlayer> cachedPlayers = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public Optional<APIPlayer> getPlayer(String name) {
|
||||
Optional<Player> player = VelocityPlugin.INSTANCE.getServer().getPlayer(name);
|
||||
|
||||
return player.map(value -> cachedPlayers.computeIfAbsent(value.getUniqueId(),
|
||||
key -> new VelocityPlayer(value)));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<APIPlayer> getPlayer(UUID uuid) {
|
||||
Optional<Player> player = VelocityPlugin.INSTANCE.getServer().getPlayer(uuid);
|
||||
|
||||
return player.map(value -> cachedPlayers.computeIfAbsent(value.getUniqueId(),
|
||||
key -> new VelocityPlayer(value)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unloadPlayer(UUID uuid) {
|
||||
cachedPlayers.remove(uuid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<APIPlayer> getOnlinePlayers() {
|
||||
return VelocityPlugin.INSTANCE.getServer().getAllPlayers().stream()
|
||||
.map(pl -> cachedPlayers.computeIfAbsent(pl.getUniqueId(), key -> new VelocityPlayer(pl)))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package dev.brighten.antivpn.velocity;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import com.velocitypowered.api.event.Subscribe;
|
||||
import com.velocitypowered.api.event.proxy.ProxyInitializeEvent;
|
||||
import com.velocitypowered.api.event.proxy.ProxyShutdownEvent;
|
||||
import com.velocitypowered.api.plugin.Plugin;
|
||||
import com.velocitypowered.api.plugin.annotation.DataDirectory;
|
||||
import com.velocitypowered.api.proxy.ProxyServer;
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
import dev.brighten.antivpn.command.Command;
|
||||
import dev.brighten.antivpn.database.VPNDatabase;
|
||||
import dev.brighten.antivpn.database.local.H2VPN;
|
||||
import dev.brighten.antivpn.database.mongo.MongoVPN;
|
||||
import dev.brighten.antivpn.database.sql.MySqlVPN;
|
||||
import dev.brighten.antivpn.velocity.command.VelocityCommand;
|
||||
import lombok.Getter;
|
||||
import org.bstats.charts.SimplePie;
|
||||
import org.bstats.velocity.Metrics;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.nio.file.Path;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
@Getter
|
||||
@Plugin(id = "kaurivpn", name = "KauriVPN", version = "1.7.1", authors = {"funkemunky"})
|
||||
public class VelocityPlugin {
|
||||
|
||||
private final ProxyServer server;
|
||||
private final Logger logger;
|
||||
private final Metrics.Factory metricsFactory;
|
||||
private final Path configDir;
|
||||
|
||||
@Nullable
|
||||
private Metrics metrics;
|
||||
|
||||
|
||||
public static VelocityPlugin INSTANCE;
|
||||
|
||||
@Inject
|
||||
public VelocityPlugin(ProxyServer server, Logger logger, @DataDirectory Path path, Metrics.Factory metricsFactory) {
|
||||
this.server = server;
|
||||
this.logger = logger;
|
||||
this.configDir = path;
|
||||
this.metricsFactory = metricsFactory;
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onInit(ProxyInitializeEvent event) {
|
||||
INSTANCE = this;
|
||||
logger.info("Loading config...");
|
||||
|
||||
//Loading plugin
|
||||
logger.info("Starting AntiVPN services...");
|
||||
AntiVPN.start(new VelocityListener(), new VelocityPlayerExecutor(), configDir.toFile());
|
||||
|
||||
if(AntiVPN.getInstance().getVpnConfig().metrics()) {
|
||||
logger.info("Starting metrics...");
|
||||
metrics = metricsFactory.make(this, 12791);
|
||||
|
||||
metrics.addCustomChart(new SimplePie("database_used", this::getDatabaseType));
|
||||
}
|
||||
|
||||
logger.info("Registering commands...");
|
||||
for (Command command : AntiVPN.getInstance().getCommands()) {
|
||||
server.getCommandManager().register(server.getCommandManager().metaBuilder(command.name())
|
||||
.aliases(command.aliases()).build(), new VelocityCommand(command));
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onDisable(ProxyShutdownEvent event) {
|
||||
logger.info("Disabling AntiVPN...");
|
||||
AntiVPN.getInstance().getExecutor().log("Disabling AntiVPN...");
|
||||
|
||||
if (AntiVPN.getInstance().getDatabase() != null) {
|
||||
AntiVPN.getInstance().stop();
|
||||
}
|
||||
|
||||
if (metrics != null) {
|
||||
metrics = null;
|
||||
}
|
||||
|
||||
INSTANCE = null;
|
||||
logger.info("Disabled AntiVPN.");
|
||||
}
|
||||
|
||||
private String getDatabaseType() {
|
||||
VPNDatabase database = AntiVPN.getInstance().getDatabase();
|
||||
|
||||
if(database instanceof H2VPN) {
|
||||
return "H2";
|
||||
} else if(database instanceof MySqlVPN) {
|
||||
return "MySQL";
|
||||
} else if(database instanceof MongoVPN) {
|
||||
return "MongoDB";
|
||||
} else {
|
||||
return "No-Database";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package dev.brighten.antivpn.velocity.command;
|
||||
|
||||
import com.velocitypowered.api.command.CommandSource;
|
||||
import com.velocitypowered.api.command.SimpleCommand;
|
||||
import dev.brighten.antivpn.AntiVPN;
|
||||
import dev.brighten.antivpn.command.Command;
|
||||
import lombok.val;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.TextColor;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
public class VelocityCommand implements SimpleCommand {
|
||||
|
||||
private final Command command;
|
||||
|
||||
public VelocityCommand(Command command) {
|
||||
this.command = command;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Invocation invocation) {
|
||||
CommandSource sender = invocation.source();
|
||||
if(!invocation.source().hasPermission("antivpn.command.*")
|
||||
&& !invocation.source().hasPermission(command.permission())) {
|
||||
invocation.source().sendMessage(LegacyComponentSerializer.builder().character('&')
|
||||
.build().deserialize(AntiVPN.getInstance().getMessageHandler()
|
||||
.getString("no-permission").getMessage()));
|
||||
return;
|
||||
}
|
||||
|
||||
val children = command.children();
|
||||
|
||||
String[] args = invocation.arguments();
|
||||
if(children.length > 0 && args.length > 0) {
|
||||
for (Command child : children) {
|
||||
if(child.name().equalsIgnoreCase(args[0]) || Arrays.stream(child.aliases())
|
||||
.anyMatch(alias -> alias.equalsIgnoreCase(args[0]))) {
|
||||
if(!sender.hasPermission("antivpn.command.*")
|
||||
&& !sender.hasPermission(child.permission())) {
|
||||
invocation.source().sendMessage(LegacyComponentSerializer.builder().character('&')
|
||||
.build().deserialize(AntiVPN.getInstance().getMessageHandler()
|
||||
.getString("no-permission").getMessage()));
|
||||
invocation.source().sendMessage(Component.text("No permission")
|
||||
.toBuilder().color(TextColor.color(255,0,0)).build());
|
||||
return;
|
||||
}
|
||||
sender.sendMessage(LegacyComponentSerializer.builder().character('&').build()
|
||||
.deserialize(child.execute(new VelocityCommandExecutor(sender), IntStream
|
||||
.range(0, args.length - 1)
|
||||
.mapToObj(i -> args[i + 1]).toArray(String[]::new))));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sender.sendMessage(LegacyComponentSerializer.builder().character('&').build()
|
||||
.deserialize(command.execute(new VelocityCommandExecutor(sender), args)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> suggest(Invocation invocation) {
|
||||
final CommandSource sender = invocation.source();
|
||||
final String[] args = invocation.arguments();
|
||||
|
||||
val children = command.children();
|
||||
|
||||
if(children.length > 0 && args.length > 0) {
|
||||
for (dev.brighten.antivpn.command.Command child : children) {
|
||||
if(child.name().equalsIgnoreCase(args[0]) || Arrays.stream(child.aliases())
|
||||
.anyMatch(alias2 -> alias2.equalsIgnoreCase(args[0]))) {
|
||||
return child.tabComplete(new VelocityCommandExecutor(sender), "alias", IntStream
|
||||
.range(0, args.length - 1)
|
||||
.mapToObj(i -> args[i + 1]).toArray(String[]::new));
|
||||
}
|
||||
}
|
||||
}else if (children.length > 0){ // && args.length == 0 is always true here
|
||||
return Arrays.stream(children).map(Command::name).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
return command.tabComplete(new VelocityCommandExecutor(sender), "alias", args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<List<String>> suggestAsync(Invocation invocation) {
|
||||
return CompletableFuture.supplyAsync(() -> this.suggest(invocation));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(Invocation invocation) {
|
||||
return SimpleCommand.super.hasPermission(invocation);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user