Compare commits

..

98 Commits

Author SHA1 Message Date
funkemunky d8b48e8c9b Checking out files 2025-09-30 09:46:08 -04:00
funkemunky 7b3174eaae Updating to 1.9.4 2025-09-30 08:38:23 -04:00
funkemunky da5511fc33 Sponge compile target to Java 21 2025-05-28 16:47:06 -04:00
funkemunky f28badf949 Adding maven cache instead of using the setup-java job 2025-05-28 16:44:34 -04:00
Dawson 00124cddf2 Merge pull request #66 from funkemunky/feature/spongepowered
feature/spongepowered
2025-05-28 16:42:44 -04:00
funkemunky 103fdf74da Using JDK 21 instead 2025-05-28 16:40:28 -04:00
funkemunky 4f43028ec0 Attempting to fix cache dependency path 2025-05-28 16:38:48 -04:00
funkemunky 866217ff08 Fixing repositories 2025-05-28 16:36:28 -04:00
funkemunky f271275bfa Sponge is loading, other plugins load, fixed some bugs that were introduced, added sponge plugin uploader in maven workflow 2025-05-28 16:33:29 -04:00
funkemunky 68a2acce00 Got Sponge support working, need to ensure other plugins are still working tho 2025-05-28 15:42:43 -04:00
funkemunky 7247341693 Got initial sponge powered loading working, Im not sure the consequences of it though 2025-05-27 19:39:27 -04:00
funkemunky 3f6bb4a0e6 Refactor, code cleanup, sponge impl.
BungeeCord still has problems, bad API, not my fault technically. still need to fix though
2025-05-27 13:13:53 -04:00
funkemunky de31d837b9 Velocity dependency fix 2025-05-22 14:29:05 -04:00
funkemunky 6967246edb Fixing loading on velocity and removing sneakythrows from Query class 2025-05-19 13:23:38 -04:00
funkemunky 52efc7de3f Ensuring queries auto-close 2025-05-19 10:31:54 -04:00
Dawson ea33a34b3d Shrinking Jar File Size Again (#64)
* Adding back dynamic library support and adding some metrics, velocity first

* Removing guava, using caffiene instead

* Merge cleanup

* Maybe this will get caches working properly now?

* Refactored to be more clean and reliable

* Fixing bungee compile

---------

Co-authored-by: Dawson <dawson@funkemunky.cc>
2025-05-12 11:20:23 -04:00
funkemunky 3a0419cbac Merge branch 'bugfix/h2-load' 2025-04-22 12:09:04 -04:00
funkemunky 24257e4f42 Merge branch 'bugfix/bukkit-kick'
# Conflicts:
#	Bukkit/src/main/java/dev/brighten/antivpn/bukkit/BukkitListener.java
2025-04-22 12:08:53 -04:00
Dawson 2b7f043eb9 Improving shutdown and error handling (#63)
Co-authored-by: Dawson <dawson@funkemunky.cc>
2025-04-22 12:07:30 -04:00
Dawson 2dbe465b9e Fixing Bukkit player kicking not functioning properly (#62)
* Fixing bug where player is not kicked when using proxy on Bukkit servers

* Removing from PlayerLoginEvent and doing PlayerJoinEvent only instead.

---------

Co-authored-by: Dawson <dawson@funkemunky.cc>
2025-04-22 09:58:46 -04:00
funkemunky ac628811cc Merge branch 'master' into bugfix/bukkit-kick 2025-04-22 09:55:08 -04:00
Dawson 3aae8d8f49 Backing up and resetting database on versioning compatibility (#61)
Co-authored-by: Dawson <dawson@funkemunky.cc>
2025-04-22 09:54:43 -04:00
funkemunky f0c37c6ff0 Merge branch 'master' into bugfix/bukkit-kick
# Conflicts:
#	Assembly/pom.xml
#	Bukkit/pom.xml
#	Bungee/pom.xml
#	Common/pom.xml
#	Sponge/pom.xml
#	Velocity/pom.xml
#	pom.xml
2025-04-22 09:53:47 -04:00
funkemunky 311f1e198b Backing up and resetting database on versioning compatibility 2025-04-21 13:20:13 -04:00
Dawson 9cbeed1df3 58-bug-plugin-fails-to-load-after-upgrading-to-193-on-velocity (#60)
* Moving to previous H2 version that was in 1.9.2 that somehow got downgraded

* Reverting back to Java 8 compile target

* removing minimize on shade for Common

* Clearly something here changed something, cause reverting it this way seems to have fixed it

* Cleaning up code here

---------

Co-authored-by: Dawson <dawson@funkemunky.cc>
2025-03-27 10:44:34 -04:00
funkemunky 6453898ca4 Removing from PlayerLoginEvent and doing PlayerJoinEvent only instead. 2025-03-20 10:29:21 -04:00
funkemunky 6243727ebf Fixing bug where player is not kicked when using proxy on Bukkit servers 2025-03-17 11:24:56 -04:00
Dawson 353b7dad78 bugfix/velocity-kick (#56)
* Updating velocity API

* Added exception logging and fixed NullPointerException with antivpn plan command in velocity from tab-complete being null. Replaced MySQL with MariaDB driver

* Added exception logging and fixed NullPointerException with antivpn plan command in velocity from tab-complete being null. Replaced MySQL with MariaDB driver.

* Added exception logging and fixed NullPointerException with antivpn plan command in velocity from tab-complete being null. Replaced MySQL with MariaDB driver.

* Fixing kick reason

* Corrected MySQL ssl errors and fixed visual bug with velocity users kicking

* Reverted to mysql driver

---------

Co-authored-by: Dawson <dawson@funkemunky.cc>
2025-01-26 14:56:12 -05:00
dependabot[bot] 0291aca052 Merge pull request #52
* Bump com.h2database:h2 from 2.1.210 to 2.2.220 in /Common

* Merge branch 'master' into dependabot/maven/Common/com.h2database-h2-…

* Merge branch 'master' into dependabot/maven/Common/com.h2database-h2-…

* Updating mysql depends to non-vulnerable
2024-11-24 14:38:27 -05:00
funkemunky ae5893be89 Worflow now uses setup-java for caching as well. 2024-11-18 09:23:52 -05:00
funkemunky f9ed53bfec Updating action to latest versions 2024-11-18 09:19:23 -05:00
Dawson cb32dfc370 Allowing manual dispatch 2024-11-18 09:14:37 -05:00
Dawson 4c7ff3d061 Merge pull request #48 from funkemunky/bugfix-bukkit-login
Fixing bukkit and antivirus problems - Release v1.9.3
2024-03-17 14:23:42 -04:00
funkemunky aec0bb2738 Corrected error on bukkit servers when vanilla kicking players 2024-03-15 11:17:25 -04:00
funkemunky 3f5ab39877 - Correct command concurrency issues on Bukkit servers
- Removed dynamic class loading of libraries to fix antivirus flags of the plugin.
2024-03-15 11:06:49 -04:00
Dawson f2e59c0075 Merge pull request #47 from Michielo1/patch-1
Correct ISO codes link in config
2024-03-08 22:37:00 -05:00
Michielo a01b595953 Correct ISO codes link 2024-02-18 20:51:25 +01:00
funkemunky b2fcc4ff26 Changing event priority on bungee so KauriVPN checks run before anything else. 2024-01-25 13:24:51 -05:00
funkemunky 5363b7c469 In process of correcting kick issues 2024-01-06 19:56:14 -05:00
Dawson df48e3dfd4 Update issue templates 2023-12-31 13:39:31 -05:00
Dawson 0686c5fd3e Update issue templates 2023-12-31 13:38:17 -05:00
funkemunky 5b6d214e6f Patches bug on Bukkit/Spigot servers may result in some players not being kicked on VPN detection.
I believe this occurs when the API response is below 50ms, and we attempt to Player#kickPlayer() or run commands on console that attempt to kick the player. The problem is that if this is running before the tick PlayerLoginEvent runs on ends, the player wouldn't be considered "online". Therefore, the player would never be removed from the server even if desired.

I assumed wrongly that the async processing of the query would always end up on the next tick. So now I update the PlayerLoginEvent result to KICK_BANNED no matter if the processing is async or in the same thread stack as the event.
2023-12-30 14:06:44 -05:00
funkemunky 2bdd7d2c34 Cleaned up files 2023-12-04 08:55:01 -05:00
funkemunky 31a9412c0a Updated libraries, and cleaned up code for performance purposes. 2023-12-04 08:54:48 -05:00
Dawson Hessler 7f96c49ce8 Delete nightly.yml 2023-10-30 10:41:45 -04:00
Dawson Hessler 7b3f9fc6ae Update nightly.yml 2023-10-30 10:38:01 -04:00
Dawson Hessler edd08b27ce Update maven.yml 2023-10-30 10:37:36 -04:00
Dawson Hessler 158045217e Create nightly.yml 2023-10-30 10:37:01 -04:00
Dawson 63bdb0a4da Merge pull request #45 from funkemunky/dependabot/maven/Common/com.google.guava-guava-32.0.0-jre
Bump com.google.guava:guava from 31.1-jre to 32.0.0-jre in /Common
2023-10-30 10:27:05 -04:00
Dawson b9e23ba34e Updating to only run on commits and pull requests to main 2023-10-30 10:26:26 -04:00
dependabot[bot] 9f6b0f8b27 Bump com.google.guava:guava from 31.1-jre to 32.0.0-jre in /Common
Bumps [com.google.guava:guava](https://github.com/google/guava) from 31.1-jre to 32.0.0-jre.
- [Release notes](https://github.com/google/guava/releases)
- [Commits](https://github.com/google/guava/commits)

---
updated-dependencies:
- dependency-name: com.google.guava:guava
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-30 14:24:01 +00:00
Dawson fec1dcdef1 Merge pull request #29 from funkemunky/dependabot/maven/Common/org.yaml-snakeyaml-2.0
Bump snakeyaml from 1.30 to 2.0 in /Common
2023-10-30 10:23:21 -04:00
Dawson Hessler c062e3d910 Tested and correcting dependency issues 2023-10-30 10:20:49 -04:00
Dawson Hessler a79fb1fe9a Updating more vulnerable depends 2023-10-30 10:14:15 -04:00
Dawson Hessler d224efce3c Updating depends to make it work 2023-10-30 10:12:14 -04:00
Dawson Hessler a2554c2bba Merge branch 'master' into dependabot/maven/Common/org.yaml-snakeyaml-2.0 2023-10-30 10:06:28 -04:00
Dawson 70bfb4e83d Merge pull request #42 from C0D3-M4513R/master
Fix PR #35
2023-10-30 10:06:07 -04:00
Dawson bc666447c5 Merge pull request #43 from C0D3-M4513R/patch-bukkit-banned-whitelist
Add null checks to ip check on login
2023-10-30 10:05:51 -04:00
Dawson Hessler 26cfc3e3f8 Update maven.yml 2023-10-30 10:03:04 -04:00
Dawson Hessler 7974b24271 Revert "Revert "Revert "Update maven.yml"""
This reverts commit 87cdd57383.
2023-10-30 10:02:30 -04:00
Dawson Hessler 87cdd57383 Revert "Revert "Update maven.yml""
This reverts commit 6fe928ca14.
2023-10-30 10:02:17 -04:00
Dawson Hessler 6fe928ca14 Revert "Update maven.yml"
This reverts commit d461b5945b.
2023-10-30 10:02:02 -04:00
Dawson Hessler dae9111a34 Revert "Merge branch 'master' of https://github.com/funkemunky/AntiVPN"
This reverts commit f4d6fc2b4b, reversing
changes made to d461b5945b.
2023-10-30 10:01:50 -04:00
Dawson Hessler f4d6fc2b4b Merge branch 'master' of https://github.com/funkemunky/AntiVPN 2023-10-30 10:00:12 -04:00
Dawson Hessler d461b5945b Update maven.yml 2023-10-30 09:59:50 -04:00
Dawson c6303ec1b2 Merge pull request #44 from funkemunky/funkemunky-patch-1
Updating build action to run on pull request
2023-10-30 09:57:55 -04:00
Dawson Hessler ca8fb24134 Caching maven repos properly 2023-10-30 09:54:58 -04:00
Dawson 5e37d2c371 Updating to run on pull request 2023-10-30 09:50:47 -04:00
Dawson Hessler 48c6dd63ee Revert "Merge pull request #35 from C0D3-M4513R/patch-1"
This reverts commit db1cdad4e1, reversing
changes made to 9f66570088.
2023-10-30 09:48:04 -04:00
C0D3 M4513R 50e7059597 Add null checks to ip check on login
Supersedes: #41
Fixes: #39
2023-10-18 05:57:22 +02:00
C0D3 M4513R 464b02f416 Fix PR #35 2023-10-18 05:45:27 +02:00
Dawson db1cdad4e1 Merge pull request #35 from C0D3-M4513R/patch-1 2023-10-17 18:58:31 -04:00
Dawson 9f66570088 Merge pull request #37 from C0D3-M4513R/command-suggestion 2023-10-17 18:57:47 -04:00
Dawson 5f0b2796b3 Merge pull request #40 from alexkarezin/master 2023-10-17 18:56:42 -04:00
Alex Karezin be5eb4e953 Update README.md by adding a link to repository map
Adding a link to the high-level diagrams including module, library dependency and others (https://sourcespy.com/github/funkemunkyantivpn/). Built directly from source and updated on schedule. Intended to simplify developer's introduction to the project.

In the spirit of transparency - I am the author of the diagrams. Hope contributors find it useful.
2023-07-31 14:43:20 -04:00
C0D3 M4513R dde81b0495 Bump version 2023-07-14 13:35:50 +02:00
C0D3 M4513R cbc00b79e2 Add command suggestion for empty args 2023-07-14 13:31:36 +02:00
C0D3 M4513R 3b2a463e58 Update PlanCommand.java 2023-07-14 13:17:47 +02:00
Dawson Hessler 96e48594d8 Fixing velocity loading issues 2023-07-12 08:45:39 -04:00
Dawson Hessler c1ab71c7ed Merge branch 'master' of https://github.com/funkemunky/AntiVPN 2023-07-06 20:26:07 -04:00
Dawson Hessler 4bda24f10c Bump to 1.9.0 2023-07-06 20:26:02 -04:00
Dawson 259cff4402 Merge pull request #32 from AlexProgrammerDE/patch-1
Remove unused import
2023-07-06 20:15:51 -04:00
Dawson Hessler c54e90dca1 Removing usages of System.out.print 2023-07-06 20:13:47 -04:00
Alex 4f1e3848de Remove unused import 2023-07-04 17:30:39 +02:00
Dawson 1606ad192e Working on spongepowered version of plugin 2023-05-25 10:24:42 -04:00
Dawson Hessler 40308869c0 1.8.4 Added cache for logins for faster response 2023-05-23 08:09:41 -04:00
Dawson Hessler 6959f35d0c Fixing whitelist issues in bukkit and bungee 2023-04-18 06:45:28 -04:00
Dawson Hessler 21b6924cce Fixing error with inserted type mismatch in H2 and MySQL 2023-04-18 06:42:07 -04:00
Dawson Hessler 9c843cd061 Updating to 1.8.3.1
Turns out we accidentally already used the 1.8.3 version
2023-03-13 15:08:04 -04:00
Dawson Hessler 91a09f6940 Updating version to 1.8.3 2023-03-13 15:04:14 -04:00
Dawson Hessler 36b44200c4 Adding response expiry since I just realized it never expires 2023-03-13 15:00:39 -04:00
dependabot[bot] 903dd8e73e Bump snakeyaml from 1.30 to 2.0 in /Common
Bumps [snakeyaml](https://bitbucket.org/snakeyaml/snakeyaml) from 1.30 to 2.0.
- [Commits](https://bitbucket.org/snakeyaml/snakeyaml/branches/compare/snakeyaml-2.0..snakeyaml-1.30)

---
updated-dependencies:
- dependency-name: org.yaml:snakeyaml
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-28 19:31:59 +00:00
Dawson e6bc601372 Fixing compile bug 2023-02-28 14:31:40 -05:00
Dawson 9dbf0e8635 Merge branch 'master' of https://github.com/funkemunky/AntiVPN 2022-11-02 10:39:50 -04:00
Dawson dd1b6afbb7 Working on adding sponge support 2022-11-02 10:39:48 -04:00
Dawson Hessler 14e266b978 Fixing load issue with snakeyaml on 1.12+ servers 2022-09-02 09:34:47 -04:00
Dawson Hessler cc289f41ff Instead of deleting old files, make new one 2022-08-29 07:34:53 -04:00
Dawson Hessler bf5b81b750 1.8.2.1, fixed H2 and removed debug 2022-08-29 07:19:42 -04:00
69 changed files with 2659 additions and 1834 deletions
+34
View File
@@ -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.
+20
View File
@@ -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.
+93
View File
@@ -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-Universal/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: ./AntiVPN-Sponge/Sponge-*.jar
asset_name: AntiVPN-Sponge-v${{ env.VERSION }}.jar
asset_content_type: application/java-archive
+30 -15
View File
@@ -1,29 +1,44 @@
on:
push:
branches: ["**"]
branches: [master]
pull_request:
branches: [master]
workflow_dispatch:
jobs:
build:
name: Build and Test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up JDK 17.0.2
uses: actions/setup-java@v1
- name: Cache local Maven repository
uses: actions/cache@v4
with:
java-version: 17.0
- name: Cache Maven packages
uses: actions/cache@v2
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:
path: ~/.m2
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
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 -Pclean install
run: mvn -B package --file pom.xml
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload AntiVPN
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v4
with:
name: AntiVPN
path: Assembly/target/Assembly-*.jar
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
-38
View File
@@ -1,38 +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.8.2</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>Assembly</artifactId>
<build>
<plugins>
<plugin>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.4</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</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.target>8</maven.compiler.target>
<maven.compiler.source>8</maven.compiler.source>
</properties>
</project>
-64
View File
@@ -1,64 +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.8.2</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.2.4</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</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>Velocity</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>
-74
View File
@@ -1,74 +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.8.2</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>Bukkit</artifactId>
<build>
<resources>
<resource>
<filtering>true</filtering>
<directory>src/main/resources</directory>
</resource>
</resources>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<source>8</source>
<target>8</target>
<compilerArgument>-XDignore.symbol.file</compilerArgument>
</configuration>
</plugin>
<plugin>
<artifactId>maven-shade-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
<configuration>
<relocations>
<relocation>
<pattern>org.bstats</pattern>
<shadedPattern>dev.brighten.antivpn.bukkit.org.bstats</shadedPattern>
</relocation>
</relocations>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.github.spigot</groupId>
<artifactId>1.13.2</artifactId>
<version>1.13.2</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>dev.brighten.antivpn</groupId>
<artifactId>Common</artifactId>
<version>1.8.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.target>8</maven.compiler.target>
<maven.compiler.source>8</maven.compiler.source>
</properties>
</project>
+32 -20
View File
@@ -5,7 +5,7 @@
<parent>
<artifactId>AntiVPN</artifactId>
<groupId>dev.brighten.antivpn</groupId>
<version>1.8.2</version>
<version>1.9.4</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -16,32 +16,37 @@
<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.1.0</version>
<configuration>
<relocations>
<relocation>
<pattern>org.bstats</pattern>
<!-- Replace this with your package! -->
<shadedPattern>dev.brighten.antivpn.bukkit.org.bstats</shadedPattern>
</relocation>
</relocations>
</configuration>
<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>
@@ -55,21 +60,28 @@
</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.8.2</version>
<version>1.9.4</version>
<scope>provided</scope>
</dependency>
<dependency>
@@ -2,25 +2,27 @@ 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.entity.Player;
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.PlayerJoinEvent;
import org.bukkit.event.player.PlayerLoginEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.scheduler.BukkitRunnable;
import org.bukkit.scheduler.BukkitTask;
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()
@@ -28,136 +30,100 @@ 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();
}
@Override
public void shutdown() {
if(cacheResetTask != null && !cacheResetTask.isCancelled()) cacheResetTask.cancel();
public void log(Level level, String log, Object... objects) {
Bukkit.getLogger().log(level, String.format(log, objects));
}
@Override
public void log(String log, Object... objects) {
Bukkit.getLogger().log(Level.INFO, String.format(log, objects));
log(Level.INFO, String.format(log, objects));
}
@EventHandler
@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) {
AntiVPN.getInstance().getExecutor().log("Enabled");
player.setAlertsEnabled(true);
player.sendMessage(AntiVPN.getInstance().getMessageHandler()
.getString("command-alerts-toggled")
.getFormattedMessage(new VpnString.Var<>("state", true)));
} else AntiVPN.getInstance().getExecutor().log("Not enabled");
});
});
}
@EventHandler
public void onListener(final PlayerLoginEvent event) {
//If they're exempt, don't check.
System.out.println(event.getAddress().getHostAddress() + ";" + event.getPlayer().getUniqueId() + ";" + event.getPlayer().getName());
if(event.getPlayer().hasPermission("antivpn.bypass") //Has bypass permission
|| AntiVPN.getInstance().getExecutor().isWhitelisted(event.getPlayer().getUniqueId()) //Is exempt
//Or has a name that starts with a certain prefix. This is for Bedrock exempting.
|| AntiVPN.getInstance().getExecutor().isWhitelisted(event.getAddress().getHostAddress())
|| AntiVPN.getInstance().getVpnConfig().getPrefixWhitelists().stream()
.anyMatch(prefix -> event.getPlayer().getName().startsWith(prefix))) return;
final Player player = event.getPlayer();
checkIp(event.getAddress().getHostAddress(),
AntiVPN.getInstance().getVpnConfig().cachedResults(), result -> {
if(result.isSuccess()) {
//We need to run on main thread or kicking and running commands will cause errors
new BukkitRunnable() {
public void run() {
// If the countryList() size is zero, no need to check.
// Running country check first
if(AntiVPN.getInstance().getVpnConfig().countryList().size() > 0
&& !(AntiVPN.getInstance().getExecutor()
.isWhitelisted(event.getPlayer().getUniqueId()) //Is exempt
//Or has a name that starts with a certain prefix. This is for Bedrock exempting.
|| AntiVPN.getInstance().getExecutor().isWhitelisted(event.getPlayer()
.getAddress().getAddress()
.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
if(AntiVPN.getInstance().getVpnConfig().countryKickCommands().size() == 0) {
final String kickReason = AntiVPN.getInstance().getVpnConfig()
.countryVanillaKickReason();
// Kicking our player
event.getPlayer().kickPlayer(ChatColor
.translateAlternateColorCodes('&',
kickReason
.replace("%player%", event.getPlayer().getName())
.replace("%country%", result.getCountryName())
.replace("%code%", result.getCountryCode())));
} else {
for (String cmd : AntiVPN.getInstance().getVpnConfig().countryKickCommands()) {
final String formattedCommand = ChatColor.translateAlternateColorCodes('&',
cmd.replace("%player%", event.getPlayer().getName())
.replace("%country%", result.getCountryName())
.replace("%code%", result.getCountryCode()));
// Runs our command from console
Bukkit.dispatchCommand(Bukkit.getConsoleSender(), formattedCommand);
}
}
} else if(result.isProxy()) {
if(AntiVPN.getInstance().getVpnConfig().kickPlayersOnDetect())
player.kickPlayer(org.bukkit.ChatColor.translateAlternateColorCodes('&',
AntiVPN.getInstance().getVpnConfig().getKickString()));
Bukkit.getLogger().info(event.getPlayer().getName()
+ " joined on a VPN/Proxy (" + result.getMethod() + ")");
//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(AntiVPN.getInstance().getVpnConfig().alertMessage()
.replace("%player%", event.getPlayer().getName())
.replace("%reason%", result.getMethod())
.replace("%country%", result.getCountryName())
.replace("%city%", result.getCity())));
//In case the user wants to run their own commands instead of using the built in kicking
if(AntiVPN.getInstance().getVpnConfig().runCommands()) {
for (String command : AntiVPN.getInstance().getVpnConfig().commands()) {
Bukkit.dispatchCommand(Bukkit.getConsoleSender(),
ChatColor.translateAlternateColorCodes('&',
command.replace("%player%",
event.getPlayer().getName())));
}
}
AntiVPN.getInstance().detections++;
}
}
}.runTask(BukkitPlugin.pluginInstance);
} else {
Bukkit.getLogger()
.log(Level.WARNING,
"The API query was not a success! " +
"You may need to upgrade your license on https://funkemunky.cc/shop");
.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)));
}
AntiVPN.getInstance().checked++;
});
}));
}
@EventHandler
@@ -3,8 +3,13 @@ package dev.brighten.antivpn.bukkit;
import dev.brighten.antivpn.AntiVPN;
import dev.brighten.antivpn.bukkit.command.BukkitCommand;
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 lombok.Getter;
import org.bstats.bukkit.Metrics;
import org.bstats.charts.SingleLineChart;
import org.bstats.charts.SimplePie;
import org.bukkit.Bukkit;
import org.bukkit.command.SimpleCommandMap;
import org.bukkit.event.HandlerList;
@@ -21,8 +26,10 @@ public class BukkitPlugin extends JavaPlugin {
public static BukkitPlugin pluginInstance;
private SimpleCommandMap commandMap;
private List<org.bukkit.command.Command> registeredCommands = new ArrayList<>();
private SingleLineChart vpnDetections, ipsChecked;
private final List<org.bukkit.command.Command> registeredCommands = new ArrayList<>();
@Getter
private PlayerCommandRunner playerCommandRunner;
public void onEnable() {
pluginInstance = this;
@@ -30,14 +37,14 @@ public class BukkitPlugin extends JavaPlugin {
Bukkit.getLogger().info("Starting AntiVPN services...");
AntiVPN.start(new BukkitListener(), new BukkitPlayerExecutor(), getDataFolder());
playerCommandRunner = new PlayerCommandRunner();
playerCommandRunner.start();
// 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(vpnDetections = new SingleLineChart("vpn_detections",
() -> AntiVPN.getInstance().detections));
metrics.addCustomChart(ipsChecked = new SingleLineChart("ips_checked",
() -> AntiVPN.getInstance().checked));
metrics.addCustomChart(new SimplePie("database_used", this::getDatabaseType));
new BukkitRunnable() {
public void run() {
AntiVPN.getInstance().checked = AntiVPN.getInstance().detections = 0;
@@ -47,14 +54,13 @@ public class BukkitPlugin extends JavaPlugin {
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) {
SimplePluginManager manager = (SimplePluginManager) pluginInstance.getServer().getPluginManager();
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);
}
}
@@ -81,21 +87,25 @@ public class BukkitPlugin extends JavaPlugin {
}
@Override
@SuppressWarnings("unchecked")
public void onDisable() {
Bukkit.getLogger().info("Stopping plugin services...");
AntiVPN.getInstance().stop();
playerCommandRunner.stop();
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);
}
Bukkit.getLogger().info("Unregistering listeners...");
@@ -104,4 +114,18 @@ public class BukkitPlugin extends JavaPlugin {
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;
}
}
-74
View File
@@ -1,74 +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.8.2</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>Bungee</artifactId>
<build>
<resources>
<resource>
<filtering>true</filtering>
<directory>src/main/resources</directory>
</resource>
</resources>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<source>8</source>
<target>8</target>
<compilerArgument>-XDignore.symbol.file</compilerArgument>
</configuration>
</plugin>
<plugin>
<artifactId>maven-shade-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
<configuration>
<relocations>
<relocation>
<pattern>org.bstats</pattern>
<shadedPattern>dev.brighten.antivpn.bungee.org.bstats</shadedPattern>
</relocation>
</relocations>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>dev.brighten.antivpn</groupId>
<artifactId>Common</artifactId>
<version>1.8.2</version>
<scope>provided</scope>
</dependency>
<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.target>8</maven.compiler.target>
<maven.compiler.source>8</maven.compiler.source>
</properties>
</project>
+16 -11
View File
@@ -5,7 +5,7 @@
<parent>
<artifactId>AntiVPN</artifactId>
<groupId>dev.brighten.antivpn</groupId>
<version>1.8.2</version>
<version>1.9.4</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -16,17 +16,17 @@
<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.1.0</version>
<version>3.6.0</version>
<configuration>
<relocations>
<relocation>
@@ -34,6 +34,10 @@
<!-- 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>
@@ -55,21 +59,22 @@
</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.8.2</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>
@@ -1,18 +1,21 @@
package dev.brighten.antivpn.bungee;
import dev.brighten.antivpn.AntiVPN;
import dev.brighten.antivpn.api.APIPlayer;
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.PlayerDisconnectEvent;
import net.md_5.bungee.api.event.PostLoginEvent;
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 {
@@ -26,107 +29,87 @@ 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);
}
@Override
public void shutdown() {
if(cacheResetTask != null) {
cacheResetTask.cancel();
cacheResetTask = null;
}
threadExecutor.shutdown();
BungeePlugin.pluginInstance.getProxy().getPluginManager().unregisterListener(this);
public void log(Level level, String log, Object... objects) {
BungeePlugin.pluginInstance.getProxy().getLogger().log(Level.INFO, String.format(log, objects));
}
@Override
public void log(String log, Object... objects) {
BungeeCord.getInstance().getLogger().log(Level.INFO, String.format(log, objects));
log(Level.INFO, String.format(log, objects));
}
@EventHandler
public void onListener(final PostLoginEvent event) {
if(event.getPlayer().hasPermission("antivpn.bypass") //Has bypass permission
|| AntiVPN.getInstance().getVpnConfig().getPrefixWhitelists().stream()
.anyMatch(prefix -> event.getPlayer().getName().startsWith(prefix))) return;
@Override
public void logException(String message, Throwable ex) {
BungeePlugin.pluginInstance.getProxy().getLogger().log(Level.SEVERE, message, ex);
}
checkIp(event.getPlayer().getAddress().getAddress().getHostAddress(),
AntiVPN.getInstance().getVpnConfig().cachedResults(), result -> {
if(result.isSuccess()) {
// If the countryList() size is zero, no need to check.
// Running country check first
if(AntiVPN.getInstance().getVpnConfig().countryList().size() > 0
&& !(AntiVPN.getInstance().getExecutor().isWhitelisted(event.getPlayer().getUniqueId()) //Is exempt
//Or has a name that starts with a certain prefix. This is for Bedrock exempting.
|| AntiVPN.getInstance().getExecutor().isWhitelisted(event.getPlayer().getAddress().getAddress()
.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
if(AntiVPN.getInstance().getVpnConfig().countryKickCommands().size() == 0) {
final String kickReason = AntiVPN.getInstance().getVpnConfig()
.countryVanillaKickReason();
// Kicking our player
event.getPlayer().disconnect(TextComponent.fromLegacyText(ChatColor
.translateAlternateColorCodes('&',
kickReason
.replace("%player%", event.getPlayer().getName())
.replace("%country%", result.getCountryName())
.replace("%code%", result.getCountryCode()))));
} else {
for (String cmd : AntiVPN.getInstance().getVpnConfig().countryKickCommands()) {
final String formattedCommand = ChatColor.translateAlternateColorCodes('&',
cmd.replace("%player%", event.getPlayer().getName())
.replace("%country%", result.getCountryName())
.replace("%code%", result.getCountryCode()));
@Override
public void runCommand(String command) {
BungeePlugin.pluginInstance.getProxy().getPluginManager()
.dispatchCommand(BungeePlugin.pluginInstance.getProxy().getConsole(), command);
}
// Runs our command from console
BungeeCord.getInstance().getPluginManager().dispatchCommand(
BungeeCord.getInstance().getConsole(), formattedCommand);
}
}
} else if(result.isProxy()) {
if(AntiVPN.getInstance().getVpnConfig().kickPlayersOnDetect())
event.getPlayer().disconnect(TextComponent.fromLegacyText(ChatColor
.translateAlternateColorCodes('&',
AntiVPN.getInstance().getVpnConfig().getKickString())));
BungeeCord.getInstance().getLogger().info(event.getPlayer().getName()
+ " joined on a VPN/Proxy (" + result.getMethod() + ")");
@Override
public void disablePlugin() {
BungeePlugin.pluginInstance.getProxy().getPluginManager().unregisterListeners(BungeePlugin.pluginInstance);
if (cacheResetTask != null) {
cacheResetTask.cancel();
cacheResetTask = null;
}
BungeePlugin.pluginInstance.getProxy().getPluginManager().unregisterCommands(BungeePlugin.pluginInstance);
BungeePlugin.pluginInstance.onDisable();
}
if(AntiVPN.getInstance().getVpnConfig().alertToStaff()) //Ensuring the user wishes to alert to staff
AntiVPN.getInstance().getPlayerExecutor().getOnlinePlayers().stream()
.filter(APIPlayer::isAlertsEnabled)
.forEach(pl -> pl.sendMessage(AntiVPN.getInstance().getVpnConfig().alertMessage()
.replace("%player%", event.getPlayer().getName())
.replace("%reason%", result.getMethod())
.replace("%country%", result.getCountryName())
.replace("%city%", result.getCity())));
@EventHandler(priority = EventPriority.HIGH)
public void onListener(final PreLoginEvent event) {
//In case the user wants to run their own commands instead of using the built in kicking
if(AntiVPN.getInstance().getVpnConfig().runCommands()) {
for (String command : AntiVPN.getInstance().getVpnConfig().commands()) {
BungeeCord.getInstance().getPluginManager()
.dispatchCommand(BungeeCord.getInstance().getConsole(),
ChatColor.translateAlternateColorCodes('&',
command.replace("%player%", event.getPlayer().getName())));
}
}
AntiVPN.getInstance().detections++;
}
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);
} else {
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");
}
AntiVPN.getInstance().checked++;
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
@@ -2,7 +2,6 @@ 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.*;
@@ -14,7 +13,7 @@ public class BungeePlayerExecutor implements PlayerExecutor {
@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();
@@ -23,7 +22,7 @@ public class BungeePlayerExecutor implements PlayerExecutor {
@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();
@@ -37,7 +36,7 @@ public class BungeePlayerExecutor implements PlayerExecutor {
@Override
public List<APIPlayer> getOnlinePlayers() {
return BungeeCord.getInstance().getPlayers().stream()
return BungeePlugin.pluginInstance.getProxy().getPlayers().stream()
.map(pl -> cachedPlayers.computeIfAbsent(pl.getUniqueId(), key -> new BungeePlayer(pl)))
.collect(Collectors.toList());
}
@@ -3,10 +3,13 @@ package dev.brighten.antivpn.bungee;
import dev.brighten.antivpn.AntiVPN;
import dev.brighten.antivpn.bungee.command.BungeeCommand;
import dev.brighten.antivpn.command.Command;
import net.md_5.bungee.BungeeCord;
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.SingleLineChart;
import org.bstats.charts.SimplePie;
import java.util.concurrent.TimeUnit;
@@ -14,34 +17,29 @@ public class BungeePlugin extends Plugin {
public static BungeePlugin pluginInstance;
private SingleLineChart vpnDetections, ipsChecked;
@Override
public void onEnable() {
pluginInstance = this;
//Setting up config
BungeeCord.getInstance().getLogger().info("Loading config...");
getProxy().getLogger().info("Loading config...");
//Loading plugin
BungeeCord.getInstance().getLogger().info("Starting AntiVPN services...");
getProxy().getLogger().info("Starting AntiVPN services...");
AntiVPN.start(new BungeeListener(), new BungeePlayerExecutor(), getDataFolder());
if(AntiVPN.getInstance().getVpnConfig().metrics()) {
BungeeCord.getInstance().getLogger().info("Starting bStats metrics...");
getProxy().getLogger().info("Starting bStats metrics...");
Metrics metrics = new Metrics(this, 12616);
metrics.addCustomChart(vpnDetections = new SingleLineChart("vpn_detections",
() -> AntiVPN.getInstance().detections));
metrics.addCustomChart(ipsChecked = new SingleLineChart("ips_checked",
() -> AntiVPN.getInstance().checked));
BungeeCord.getInstance().getScheduler().schedule(this,
metrics.addCustomChart(new SimplePie("database_used", this::getDatabaseType));
getProxy().getScheduler().schedule(this,
() -> AntiVPN.getInstance().checked = AntiVPN.getInstance().detections = 0,
10, 10, TimeUnit.MINUTES);
}
for (Command command : AntiVPN.getInstance().getCommands()) {
BungeeCord.getInstance().getPluginManager().registerCommand(pluginInstance, new BungeeCommand(command));
getProxy().getPluginManager().registerCommand(pluginInstance, new BungeeCommand(command));
}
}
@@ -49,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";
}
}
}
@@ -4,8 +4,6 @@ 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.BaseComponent;
import net.md_5.bungee.api.chat.ComponentBuilder;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.plugin.Command;
import net.md_5.bungee.api.plugin.TabExecutor;
+39
View File
@@ -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
+114 -12
View File
@@ -5,15 +5,15 @@
<parent>
<artifactId>AntiVPN</artifactId>
<groupId>dev.brighten.antivpn</groupId>
<version>1.8.2</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>
@@ -21,13 +21,96 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<version>3.13.0</version>
<configuration>
<source>8</source>
<target>8</target>
<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>
@@ -46,28 +129,47 @@
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.30</version>
<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.1.214</version>
<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>1.30</version>
<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.11</version>
<version>3.12.14</version>
<scope>provided</scope>
</dependency>
</dependencies>
@@ -11,6 +11,7 @@ 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;
@@ -31,10 +32,25 @@ import java.util.List;
@Getter
@Setter(AccessLevel.PRIVATE)
@MavenLibrary(groupId = "com.h2database", artifactId ="h2", version = "2.1.214")
@MavenLibrary(groupId = "org.yaml", artifactId = "snakeyaml", version = "1.30")
@MavenLibrary(groupId = "org.mongodb", artifactId = "mongo-java-driver", version = "3.12.11")
@MavenLibrary(groupId = "mysql", artifactId = "mysql-connector-java", version = "8.0.30")
@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;
@@ -62,13 +78,17 @@ public class AntiVPN {
try {
File configFile = new File(pluginFolder, "config.yml");
if(!configFile.exists()){
configFile.getParentFile().mkdirs();
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) {
e.printStackTrace();
AntiVPN.getInstance().getExecutor().logException("Could not load config.yml, plugin disabling...", e);
executor.disablePlugin();
return;
}
INSTANCE.vpnConfig = new VPNConfig();
@@ -78,34 +98,40 @@ public class AntiVPN {
INSTANCE.messageHandler = new MessageHandler();
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;
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
@@ -125,6 +151,9 @@ public class AntiVPN {
(vpnString.getDefaultMessage(), "messages." + vpnString.getKey(), AntiVPN.getInstance())
.get());
AntiVPN.getInstance().getMessageHandler().reloadStrings();
// Starting kick checks
AntiVPN.getInstance().getExecutor().startKickChecks();
}
public InputStream getResource(String filename) {
@@ -147,7 +176,21 @@ public class AntiVPN {
}
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();
}
@@ -196,7 +239,7 @@ public class AntiVPN {
ConfigurationProvider.getProvider(YamlConfiguration.class)
.save(getConfig(), new File(pluginFolder.getPath() + File.separator + "config.yml"));
} catch (IOException e) {
e.printStackTrace();
AntiVPN.getInstance().getExecutor().logException(e);
}
}
@@ -1,18 +1,30 @@
package dev.brighten.antivpn.api;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import dev.brighten.antivpn.AntiVPN;
import lombok.Getter;
import lombok.Setter;
import java.net.InetAddress;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.logging.Level;
@Getter
public abstract class APIPlayer {
private final UUID uuid;
private final String name;
private final InetAddress ip;
@Setter
private boolean alertsEnabled;
private static final Cache<String, CheckResult> checkResultCache = Caffeine.newBuilder()
.expireAfterWrite(5, TimeUnit.MINUTES)
.maximumSize(2000)
.build();
public APIPlayer(UUID uuid, String name, InetAddress ip) {
this.uuid = uuid;
this.name = name;
@@ -25,12 +37,65 @@ public abstract class APIPlayer {
public abstract boolean hasPermission(String permission);
public void setAlertsEnabled(boolean alertsEnabled) {
this.alertsEnabled = alertsEnabled;
}
public void updateAlertsState() {
//Updating into database so its synced across servers and saved on logout.
AntiVPN.getInstance().getDatabase().updateAlertsState(uuid, alertsEnabled);
}
public CheckResult checkPlayer(Consumer<CheckResult> onKick) {
if (hasPermission("antivpn.bypass") //Has bypass permission
//Is exempt
|| (uuid != null && AntiVPN.getInstance().getExecutor().isWhitelisted(uuid))
//Or has a name that starts with a certain prefix. This is for Bedrock exempting.
|| AntiVPN.getInstance().getExecutor().isWhitelisted(ip.getHostAddress())
|| AntiVPN.getInstance().getVpnConfig().getPrefixWhitelists().stream()
.anyMatch(name::startsWith)) return new CheckResult(null, ResultType.WHITELISTED);
CheckResult cachedResult = checkResultCache.getIfPresent(ip.getHostAddress());
if(cachedResult != null) {
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;
}
}
@@ -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,39 +1,107 @@
package dev.brighten.antivpn.api;
import dev.brighten.antivpn.AntiVPN;
import dev.brighten.antivpn.utils.EvictingMap;
import dev.brighten.antivpn.web.objects.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.*;
import java.util.concurrent.ExecutorService;
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);
public static final Map<String, VPNResponse> responseCache = new EvictingMap<>(5000);
@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 void resetCache() {
responseCache.clear();
}
public abstract void shutdown();
public abstract void log(Level level, String log, Object... objects);
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 void startKickChecks() {
threadExecutor.scheduleAtFixedRate(() -> {
synchronized (toKick) {
if(toKick.isEmpty()) return;
Iterator<Tuple<CheckResult, UUID>> i = toKick.iterator();
while(i.hasNext()) {
var toCheck = i.next();
Optional<APIPlayer> player = AntiVPN.getInstance().getPlayerExecutor().getPlayer(toCheck.second());
if(player.isEmpty()) {
continue;
}
handleKickingOfPlayer(toCheck.first(), player.get());
i.remove();
}
}
}, 8, 2, TimeUnit.SECONDS);
}
public void handleKickingOfPlayer(CheckResult result, APIPlayer player) {
if (AntiVPN.getInstance().getVpnConfig().alertToStaff()) AntiVPN.getInstance().getPlayerExecutor()
.getOnlinePlayers()
.stream()
.filter(APIPlayer::isAlertsEnabled)
.forEach(pl ->
pl.sendMessage(StringUtil.translateAlternateColorCodes('&',
StringUtil.varReplace(dev.brighten.antivpn.AntiVPN.getInstance().getVpnConfig()
.alertMessage(), player, result.response()))));
if(AntiVPN.getInstance().getVpnConfig().kickPlayersOnDetect()) {
switch (result.resultType()) {
case DENIED_PROXY -> player.kickPlayer(StringUtil.varReplace(AntiVPN.getInstance().getVpnConfig()
.getKickString(), player, result.response()));
case DENIED_COUNTRY -> player.kickPlayer(StringUtil.varReplace(AntiVPN.getInstance().getVpnConfig()
.countryVanillaKickReason(), player, result.response()));
}
}
if(!AntiVPN.getInstance().getVpnConfig().runCommands()) return;
switch (result.resultType()) {
case DENIED_PROXY -> {
for (String command : AntiVPN.getInstance().getVpnConfig().commands()) {
runCommand(StringUtil.translateAlternateColorCodes('&',
StringUtil.varReplace(command, player, result.response())));
}
}
case DENIED_COUNTRY -> {
for (String command : AntiVPN.getInstance().getVpnConfig().countryKickCommands()) {
runCommand(StringUtil.translateAlternateColorCodes('&',
StringUtil.varReplace(command, player, result.response())));
}
}
}
}
public boolean isWhitelisted(UUID uuid) {
if(AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled()) {
return AntiVPN.getInstance().getDatabase().isWhitelisted(uuid);
@@ -48,61 +116,32 @@ public abstract class VPNExecutor {
return whitelistedIps.contains(ip);
}
public void checkIp(String ip, boolean cachedResults, Consumer<VPNResponse> result) {
threadExecutor.execute(() -> result.accept(responseCache.compute(ip, (key, val) -> {
if(val == null) {
Optional<VPNResponse> cachedRes = AntiVPN.getInstance().getDatabase().getStoredResponse(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(), cachedResults);
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: Java Exception");
e.printStackTrace();
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;
}
}
return val;
})));
}, threadExecutor);
}
public VPNResponse checkIp(String ip, boolean cachedResults) {
return responseCache.compute(ip, (key, val) -> {
if(val == null) {
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(), cachedResults);
if(response.isSuccess()) {
threadExecutor.execute(() -> 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: Java Exception");
e.printStackTrace();
}
}
}
return val;
});
}
public abstract void disablePlugin();
}
@@ -2,7 +2,6 @@ package dev.brighten.antivpn.command;
import dev.brighten.antivpn.api.APIPlayer;
import java.util.Objects;
import java.util.Optional;
public interface CommandExecutor {
@@ -1,7 +1,6 @@
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;
@@ -47,7 +46,6 @@ public class ClearCacheCommand extends Command {
@Override
public String execute(CommandExecutor executor, String[] args) {
AntiVPN.getInstance().getDatabase().clearResponses();
VPNExecutor.responseCache.clear();
return "&aCleared all cached API response information!";
}
@@ -55,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("&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"));
}
});
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() + "...";
@@ -10,6 +10,7 @@ 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 {
@@ -53,7 +54,7 @@ public class PlanCommand extends Command {
VPNExecutor.threadExecutor.execute(() -> {
QueryResponse result;
try {
if(AntiVPN.getInstance().getVpnConfig().getLicense().equals("")) {
if(AntiVPN.getInstance().getVpnConfig().getLicense().isEmpty()) {
result = FunkemunkyAPI.getQueryResponse();
} else {
result = FunkemunkyAPI.getQueryResponse(AntiVPN.getInstance().getVpnConfig().getLicense());
@@ -81,11 +82,11 @@ public class PlanCommand extends Command {
result.getQueries(), queryMax);
executor.sendMessage(StringUtil.line("&8"));
} catch(JSONException e) {
e.printStackTrace();
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) {
e.printStackTrace();
AntiVPN.getInstance().getExecutor().logException(e);
executor.sendMessage("&cThere was a IOException thrown while looking up your query " +
"information. Check console for more details.");
}
@@ -95,6 +96,6 @@ public class PlanCommand extends Command {
@Override
public List<String> tabComplete(CommandExecutor executor, String alias, String[] args) {
return null;
return Collections.emptyList();
}
}
@@ -1,13 +1,8 @@
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.database.local.H2VPN;
import dev.brighten.antivpn.database.mongo.MongoVPN;
import dev.brighten.antivpn.database.sql.MySqlVPN;
import dev.brighten.antivpn.message.VpnString;
import java.util.Collections;
import java.util.List;
@@ -58,9 +53,6 @@ public class ReloadCommand extends Command {
AntiVPN.getInstance().getMessageHandler().reloadStrings();
// Clearing the local response cache
VPNExecutor.responseCache.clear();
AntiVPN.getInstance().reloadDatabase();
return AntiVPN.getInstance().getMessageHandler().getString("command-reload-complete").getMessage();
@@ -12,6 +12,8 @@ public interface VPNDatabase {
void cacheResponse(VPNResponse toCache);
void deleteResponse(String ip);
boolean isWhitelisted(UUID uuid);
boolean isWhitelisted(String ip);
@@ -1,5 +1,7 @@
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;
@@ -20,34 +22,26 @@ import java.util.function.Consumer;
public class H2VPN implements VPNDatabase {
private Thread whitelistedThread;
private final Cache<String, VPNResponse> cachedResponses = Caffeine.newBuilder()
.expireAfterWrite(20, TimeUnit.MINUTES)
.maximumSize(4000)
.build();
public H2VPN() {
whitelistedThread = new Thread(() -> {
try {
Thread.sleep(TimeUnit.SECONDS.toMillis(2));
} catch (InterruptedException e) {
e.printStackTrace();
}
while (true) {
// Updating from database
if (AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled()) {
AntiVPN.getInstance().getExecutor().getWhitelisted().clear();
AntiVPN.getInstance().getExecutor().getWhitelisted()
.addAll(AntiVPN.getInstance().getDatabase().getAllWhitelisted());
AntiVPN.getInstance().getExecutor().getWhitelistedIps().clear();
AntiVPN.getInstance().getExecutor().getWhitelistedIps()
.addAll(AntiVPN.getInstance().getDatabase().getAllWhitelistedIps());
}
try {
Thread.sleep(TimeUnit.SECONDS.toMillis(4));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
VPNExecutor.threadExecutor.scheduleAtFixedRate(() -> {
if(!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed()) return;
whitelistedThread.start();
//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
@@ -55,24 +49,26 @@ public class H2VPN implements VPNDatabase {
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled()|| MySQL.isClosed())
return Optional.empty();
ResultSet rs = Query.prepare("select * from `responses` where `ip` = ? limit 1").append(ip).executeQuery();
try {
if (rs != null && rs.next()) {
VPNResponse response = 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"),
System.currentTimeMillis(), -1);
return Optional.of(response);
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);
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return null;
});
return Optional.empty();
return Optional.ofNullable(response);
}
/*
@@ -89,6 +85,8 @@ public class H2VPN implements VPNDatabase {
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())
@@ -98,6 +96,14 @@ public class H2VPN implements VPNDatabase {
.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) {
@@ -199,13 +205,12 @@ public class H2VPN implements VPNDatabase {
if(MySQL.isClosed()) return;
VPNExecutor.threadExecutor.execute(() -> {
ResultSet set = Query.prepare("select * from `alerts` where `uuid` = ? limit 1")
.append(uuid.toString()).executeQuery();
try {
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) {
e.printStackTrace();
AntiVPN.getInstance().getExecutor().logException("There was a problem getting alerts state for " + uuid, e);
result.accept(false);
}
});
@@ -272,6 +277,7 @@ public class H2VPN implements VPNDatabase {
public void shutdown() {
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled())
return;
MySQL.shutdown();
}
}
@@ -1,5 +1,7 @@
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;
@@ -7,41 +9,76 @@ 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 MongoDatabase antivpnDatabase;
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) {
Document rdoc = cacheDocument.find(Filters.eq("ip", ip)).first();
VPNResponse response = cachedResponses.get(ip, ip2 -> {
Document rdoc = cacheDocument.find(Filters.eq("ip", ip)).first();
if(rdoc != null) {
return Optional.of(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"))
.build());
}
return Optional.empty();
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
@@ -60,13 +97,22 @@ public class MongoVPN implements VPNDatabase {
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(() -> {
cacheDocument.deleteMany(Filters.eq("ip", toCache.getIp()));
cacheDocument.insertOne(rdoc);
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
@@ -100,8 +146,8 @@ public class MongoVPN implements VPNDatabase {
@Override
public void setWhitelisted(String ip, boolean whitelisted) {
if(whitelisted) {
Document wdoc = new Document("setting", "whitelist");
wdoc.put("ip", ip);
Document wdoc = new Document("setting", "whitelist").append("ip", ip);
AntiVPN.getInstance().getExecutor().getWhitelistedIps().add(ip);
VPNExecutor.threadExecutor.execute(() -> settingsDocument.insertOne(wdoc));
} else {
@@ -174,18 +220,20 @@ public class MongoVPN implements VPNDatabase {
@Override
public void init() {
if(AntiVPN.getInstance().getVpnConfig().mongoDatabaseURL().length() > 0) { //URL
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())));
});
.applyToClusterSettings(builder -> builder.
hosts(Collections.singletonList(
new ServerAddress(
AntiVPN.getInstance().getVpnConfig().getIp(),
AntiVPN.getInstance().getVpnConfig().getPort())
)));
if(AntiVPN.getInstance().getVpnConfig().useDatabaseCreds()) {
settingsBld = settingsBld.credential(MongoCredential
settingsBld.credential(MongoCredential
.createCredential(AntiVPN.getInstance().getVpnConfig().getUsername(),
AntiVPN.getInstance().getVpnConfig().getDatabaseName(),
AntiVPN.getInstance().getVpnConfig().getPassword().toCharArray()));
@@ -193,7 +241,7 @@ public class MongoVPN implements VPNDatabase {
client = MongoClients.create(settingsBld.build());
}
antivpnDatabase = client.getDatabase(AntiVPN.getInstance().getVpnConfig().getDatabaseName());
MongoDatabase antivpnDatabase = client.getDatabase(AntiVPN.getInstance().getVpnConfig().getDatabaseName());
settingsDocument = antivpnDatabase.getCollection("settings");
if(settingsDocument.listIndexes().first() == null) {
@@ -1,5 +1,7 @@
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;
@@ -20,59 +22,61 @@ import java.util.function.Consumer;
public class MySqlVPN implements VPNDatabase {
private Thread whitelistedThread;
private final Cache<String, VPNResponse> cachedResponses = Caffeine.newBuilder()
.expireAfterWrite(20, TimeUnit.MINUTES)
.maximumSize(4000)
.build();
public MySqlVPN() {
whitelistedThread = new Thread(() -> {
try {
Thread.sleep(TimeUnit.SECONDS.toMillis(2));
} catch (InterruptedException e) {
e.printStackTrace();
}
while (true) {
// Updating from database
if (AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled()) {
AntiVPN.getInstance().getExecutor().getWhitelisted().clear();
AntiVPN.getInstance().getExecutor().getWhitelisted()
.addAll(AntiVPN.getInstance().getDatabase().getAllWhitelisted());
AntiVPN.getInstance().getExecutor().getWhitelistedIps().clear();
AntiVPN.getInstance().getExecutor().getWhitelistedIps()
.addAll(AntiVPN.getInstance().getDatabase().getAllWhitelistedIps());
}
try {
Thread.sleep(TimeUnit.SECONDS.toMillis(4));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
VPNExecutor.threadExecutor.scheduleAtFixedRate(() -> {
if(!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed()) return;
whitelistedThread.start();
//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())
if (isDisabled())
return Optional.empty();
ResultSet rs = Query.prepare("select * from `responses` where `ip` = ? limit 1").append(ip).executeQuery();
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);
try {
if (rs != null && rs.next()) {
VPNResponse response = 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"),
System.currentTimeMillis(), -1);
return Optional.of(response);
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);
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return null;
});
return Optional.empty();
return Optional.ofNullable(response);
}
/*
@@ -86,9 +90,11 @@ public class MySqlVPN implements VPNDatabase {
*/
@Override
public void cacheResponse(VPNResponse toCache) {
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed())
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())
@@ -98,32 +104,39 @@ public class MySqlVPN implements VPNDatabase {
.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 (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed())
if (isDisabled())
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;
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 (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed())
if (isDisabled())
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;
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 (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed())
if (isDisabled())
return;
if (whitelisted) {
@@ -139,7 +152,7 @@ public class MySqlVPN implements VPNDatabase {
@Override
public void setWhitelisted(String ip, boolean whitelisted) {
if (!AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled() || MySQL.isClosed())
if (isDisabled())
return;
if(whitelisted) {
@@ -199,13 +212,13 @@ public class MySqlVPN implements VPNDatabase {
if(MySQL.isClosed()) return;
VPNExecutor.threadExecutor.execute(() -> {
ResultSet set = Query.prepare("select * from `alerts` where `uuid` = ? limit 1")
.append(uuid.toString()).executeQuery();
try {
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) {
e.printStackTrace();
AntiVPN.getInstance().getExecutor()
.logException("Failed to get alerts state from database for: " + uuid, e);
result.accept(false);
}
});
@@ -247,18 +260,16 @@ public class MySqlVPN implements VPNDatabase {
AntiVPN.getInstance().getExecutor().log("Creating tables...");
//Running check for old table types to update
oldTableCheck: {
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("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();
@@ -275,21 +286,21 @@ public class MySqlVPN implements VPNDatabase {
String query = "SELECT COUNT(1) IndexExists FROM INFORMATION_SCHEMA.STATISTICS WHERE table_schema=DATABASE()" +
" AND table_name='whitelisted' AND index_name='uuid_1';";
ResultSet rs = Query.prepare(query).executeQuery();
int id = 0;
whitelistedIndex: {
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;
}
responsesIndex: {
query = "SELECT COUNT(1) IndexExists FROM INFORMATION_SCHEMA.STATISTICS WHERE table_schema=DATABASE() " +
"AND table_name='responses' AND index_name='ip_1';";
rs = Query.prepare(query).executeQuery();
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");
}
@@ -297,9 +308,11 @@ public class MySqlVPN implements VPNDatabase {
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';";
rs = Query.prepare(query).executeQuery();
}
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");
}
@@ -307,9 +320,10 @@ public class MySqlVPN implements VPNDatabase {
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';";
rs = Query.prepare(query).executeQuery();
}
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");
}
@@ -318,10 +332,11 @@ public class MySqlVPN implements VPNDatabase {
}
id = 0;
}
whitelistedIpsIndex: {
query = "SELECT COUNT(1) IndexExists FROM INFORMATION_SCHEMA.STATISTICS WHERE table_schema=DATABASE()" +
" AND table_name='whitelisted-ips' AND index_name='ip_1';";
rs = Query.prepare(query).executeQuery();
//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");
}
@@ -333,6 +348,10 @@ public class MySqlVPN implements VPNDatabase {
System.err.println("MySQL Excepton created" + e.getMessage());
}
}
private boolean isDisabled() {
return !AntiVPN.getInstance().getVpnConfig().isDatabaseEnabled()|| MySQL.isClosed();
}
@Override
public void shutdown() {
@@ -1,11 +1,18 @@
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;
@@ -13,11 +20,7 @@ public class MySQL {
public static void init() {
try {
if (conn == null || conn.isClosed()) {
try {
Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {
Class.forName("com.mysql.jdbc.Driver");
}
DriverManager.registerDriver(new Driver());
conn = DriverManager.getConnection("jdbc:mysql://" + AntiVPN.getInstance().getVpnConfig().getIp()
+ ":" + AntiVPN.getInstance().getVpnConfig().getPort()
+ "/?useSSL=true&autoReconnect=true",
@@ -31,26 +34,77 @@ public class MySQL {
AntiVPN.getInstance().getExecutor().log("Connection to MySQL has been established.");
}
} catch (Exception e) {
AntiVPN.getInstance().getExecutor().log("Failed to load mysql: " + e.getMessage());
e.printStackTrace();
AntiVPN.getInstance().getExecutor().logException("Failed to load mysql: " + e.getMessage(), e);
}
}
private static boolean didRetry = false;
public static void initH2() {
File dataFolder = new File(AntiVPN.getInstance().getPluginFolder(), "databases" + File.separator + "database");
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 {
Class.forName("org.h2.Driver");
conn = new NonClosableConnection(DriverManager.getConnection ("jdbc:h2:file:" +
dataFolder.getAbsolutePath(),
AntiVPN.getInstance().getVpnConfig().getUsername(),AntiVPN.getInstance().getVpnConfig().getPassword()));
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().log("H2 exception on initialize");
ex.printStackTrace();
} catch (ClassNotFoundException ex) {
AntiVPN.getInstance().getExecutor().log("No H2 library found!");
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);
}
}
}
@@ -58,7 +112,7 @@ public class MySQL {
try {
init();
} catch (Exception e) {
e.printStackTrace();
AntiVPN.getInstance().getExecutor().logException(e);
}
}
@@ -71,7 +125,7 @@ public class MySQL {
conn = null;
}
} catch (Exception e) {
e.printStackTrace();
AntiVPN.getInstance().getExecutor().logException(e);
}
}
@@ -82,7 +136,7 @@ public class MySQL {
try {
return conn.isClosed();
} catch (SQLException e) {
e.printStackTrace();
AntiVPN.getInstance().getExecutor().logException(e);
return true;
}
}
@@ -1,8 +1,7 @@
package dev.brighten.antivpn.database.sql.utils;
import lombok.SneakyThrows;
import java.sql.Connection;
import java.sql.SQLException;
public class Query {
private static Connection conn;
@@ -11,15 +10,11 @@ public class Query {
Query.conn = conn;
}
@SneakyThrows
public static ExecutableStatement prepare(String query) {
return new ExecutableStatement(conn.prepareStatement(query));
}
@SneakyThrows
public static ExecutableStatement prepare(String query, Connection con) {
return new ExecutableStatement(con.prepareStatement(query));
try {
return new ExecutableStatement(conn.prepareStatement(query));
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
@@ -29,130 +29,328 @@ 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.File;
import java.io.InputStream;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.util.Objects;
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 = Suppliers.memoize(() -> URLClassLoaderAccess.create((URLClassLoader) AntiVPN.getInstance().getClass().getClassLoader()));
private static final Supplier<URLClassLoaderAccess> URL_INJECTOR = AntiVPN.getInstance().getClass().getClassLoader() instanceof URLClassLoader ?
Suppliers.memoize(() ->
URLClassLoaderAccess.create((URLClassLoader) AntiVPN.getInstance().getClass().getClassLoader()))
: null;
/**
* Resolves all {@link MavenLibrary} annotations on the given object.
*
* @param object the object to load libraries for.
*/
public static void loadAll(Object object) {
if(URL_INJECTOR == null)
return;
loadAll(object.getClass());
}
/**
* Resolves all {@link MavenLibrary} annotations on the given class.
*
* @param clazz the class to load libraries for.
*/
public static void loadAll(Class<?> clazz) {
MavenLibrary[] libs = clazz.getDeclaredAnnotationsByType(MavenLibrary.class);
if (libs == null) {
if(URL_INJECTOR == null)
return;
}
MavenLibrary[] libs = clazz.getDeclaredAnnotationsByType(MavenLibrary.class);
for (MavenLibrary lib : libs) {
load(lib.groupId(), lib.artifactId(), lib.version(), lib.repo().url());
// 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) {
load(groupId, artifactId, version, "https://repo1.maven.org/maven2");
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(String groupId, String artifactId, String version, String repoUrl) {
load(new Dependency(groupId, artifactId, version, repoUrl));
}
public static void load(Dependency d) {
AntiVPN.getInstance().getExecutor().log(String.format("Loading dependency %s:%s:%s from %s", d.getGroupId(), d.getArtifactId(), d.getVersion(), d.getRepoUrl()));
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();
File saveLocation = new File(getLibFolder(), name + ".jar");
if (!saveLocation.exists()) {
// 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 {
AntiVPN.getInstance().getExecutor().log("Dependency '" + name + "' is not already in the libraries folder. Attempting to download...");
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, saveLocation.toPath());
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);
}
AntiVPN.getInstance().getExecutor().log("Dependency '" + name + "' successfully downloaded.");
}
if (!saveLocation.exists()) {
throw new RuntimeException("Unable to download dependency: " + d.toString());
// 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(saveLocation.toURI().toURL());
URL_INJECTOR.get().addURL(jarToLoad.toURI().toURL());
} catch (Exception e) {
throw new RuntimeException("Unable to load dependency: " + saveLocation.toString(), e);
throw new RuntimeException("Unable to load dependency: " + jarToLoad, e);
}
AntiVPN.getInstance().getExecutor().log("Loaded dependency '" + name + "' successfully.");
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");
libs.mkdirs();
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.groupId = Objects.requireNonNull(groupId, "groupId");
this.artifactId = Objects.requireNonNull(artifactId, "artifactId");
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 String getGroupId() {
return this.groupId;
}
public String getArtifactId() {
return this.artifactId;
}
public String getVersion() {
return this.version;
}
public String getRepoUrl() {
return this.repoUrl;
}
public URL getUrl() throws MalformedURLException {
String repo = this.repoUrl;
if (!repo.endsWith("/")) {
@@ -160,40 +358,13 @@ public final class LibraryLoader {
}
repo += "%s/%s/%s/%s-%s.jar";
String url = String.format(repo, this.groupId.replace(".", "/"), this.artifactId, this.version, this.artifactId, this.version);
// 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);
}
@Override
public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof Dependency)) return false;
final Dependency other = (Dependency) o;
return this.getGroupId().equals(other.getGroupId()) &&
this.getArtifactId().equals(other.getArtifactId()) &&
this.getVersion().equals(other.getVersion()) &&
this.getRepoUrl().equals(other.getRepoUrl());
}
@Override
public int hashCode() {
final int PRIME = 59;
int result = 1;
result = result * PRIME + this.getGroupId().hashCode();
result = result * PRIME + this.getArtifactId().hashCode();
result = result * PRIME + this.getVersion().hashCode();
result = result * PRIME + this.getRepoUrl().hashCode();
return result;
}
@Override
public String toString() {
return "LibraryLoader.Dependency(" +
"groupId=" + this.getGroupId() + ", " +
"artifactId=" + this.getArtifactId() + ", " +
"version=" + this.getVersion() + ", " +
"repoUrl=" + this.getRepoUrl() + ")";
}
// Rest of the class unchanged
}
@@ -64,4 +64,6 @@ public @interface MavenLibrary {
*/
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();
}
@@ -104,7 +104,7 @@ public abstract class URLClassLoaderAccess {
/**
* Accesses using sun.misc.Unsafe, supported on Java 9+.
*
* @author Vaishnav Anil (https://github.com/slimjar/slimjar)
* @author Vaishnav Anil (<a href="https://github.com/slimjar/slimjar">...</a>)
*/
private static class Unsafe extends URLClassLoaderAccess {
private static final sun.misc.Unsafe UNSAFE;
@@ -30,7 +30,7 @@ public class MessageHandler {
public void addString(VpnString string, Function<VpnString, String> getter) {
string.setConfigStringGetter(getter);
getter.apply(string);
System.out.println("Added string " + string.getKey());
AntiVPN.getInstance().getExecutor().log("Added string " + string.getKey());
messages.put(string.getKey(), string);
}
@@ -1,6 +1,13 @@
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 {
@@ -11,7 +18,7 @@ public class MiscUtils {
try {
for (Closeable closeable : closeables) if (closeable != null) closeable.close();
} catch (Exception e) {
e.printStackTrace();
AntiVPN.getInstance().getExecutor().logException(e);
}
}
@@ -19,7 +26,7 @@ public class MiscUtils {
try {
for (AutoCloseable closeable : closeables) if (closeable != null) closeable.close();
} catch (Exception e) {
e.printStackTrace();
AntiVPN.getInstance().getExecutor().logException(e);
}
}
@@ -37,10 +44,45 @@ public class MiscUtils {
out.close();
in.close();
} catch (Exception e) {
e.printStackTrace();
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();
@@ -45,7 +45,7 @@ final class NullnessCasts {
* value may be legitimately {@code null}.)
*/
@SuppressWarnings("nullness")
static <T extends Object> T uncheckedCastNullableTToT(T t) {
static <T> T uncheckedCastNullableTToT(T t) {
return t;
}
@@ -1,78 +0,0 @@
/*
* 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 com.mongodb.lang.Nullable;
import java.util.Arrays;
/**
* Helper functions that can operate on any {@code Object}.
*
* <p>See the Guava User Guide on <a
* href="https://github.com/google/guava/wiki/CommonObjectUtilitiesExplained">writing {@code Object}
* methods with {@code Objects}</a>.
*
* @author Laurence Gonsalves
* @since 2.0
*/
public final class Objects extends ExtraObjectsMethodsForWeb {
private Objects() {}
/**
* Determines whether two possibly-null objects are equal. Returns:
*
* <ul>
* <li>{@code true} if {@code a} and {@code b} are both null.
* <li>{@code true} if {@code a} and {@code b} are both non-null and they are equal according to
* {@link Object#equals(Object)}.
* <li>{@code false} in all other situations.
* </ul>
*
* <p>This assumes that any non-null objects passed to this function conform to the {@code
* equals()} contract.
*
* <p><b>Note for Java 7 and later:</b> This method should be treated as deprecated; use {@link
* java.util.Objects#equals} instead.
*/
public static boolean equal(Object a, Object b) {
return a == b || (a != null && a.equals(b));
}
/**
* Generates a hash code for multiple values. The hash code is generated by calling {@link
* Arrays#hashCode(Object[])}. Note that array arguments to this method, with the exception of a
* single Object array, do not get any special handling; their hash codes are based on identity
* and not contents.
*
* <p>This is useful for implementing {@link Object#hashCode()}. For example, in an object that
* has three properties, {@code x}, {@code y}, and {@code z}, one could write:
*
* <pre>{@code
* public int hashCode() {
* return Objects.hashCode(getX(), getY(), getZ());
* }
* }</pre>
*
* <p><b>Warning:</b> When a single object is supplied, the returned hash code does not equal the
* hash code of that object.
*
* <p><b>Note for Java 7 and later:</b> This method should be treated as deprecated; use {@link
* java.util.Objects#hash} instead.
*/
public static int hashCode(@Nullable Object... objects) {
return Arrays.hashCode(objects);
}
}
@@ -9,306 +9,6 @@ public final class Preconditions {
private Preconditions() {
}
public static void checkArgument(boolean expression) {
if (!expression) {
throw new IllegalArgumentException();
}
}
public static void checkArgument(boolean expression, Object errorMessage) {
if (!expression) {
throw new IllegalArgumentException(String.valueOf(errorMessage));
}
}
public static void checkArgument(boolean expression, String errorMessageTemplate, Object... errorMessageArgs) {
if (!expression) {
throw new IllegalArgumentException(format(errorMessageTemplate, errorMessageArgs));
}
}
public static void checkArgument(boolean b, String errorMessageTemplate, char p1) {
if (!b) {
throw new IllegalArgumentException(format(errorMessageTemplate, p1));
}
}
public static void checkArgument(boolean b, String errorMessageTemplate, int p1) {
if (!b) {
throw new IllegalArgumentException(format(errorMessageTemplate, p1));
}
}
public static void checkArgument(boolean b, String errorMessageTemplate, long p1) {
if (!b) {
throw new IllegalArgumentException(format(errorMessageTemplate, p1));
}
}
public static void checkArgument(boolean b, String errorMessageTemplate, Object p1) {
if (!b) {
throw new IllegalArgumentException(format(errorMessageTemplate, p1));
}
}
public static void checkArgument(boolean b, String errorMessageTemplate, char p1, char p2) {
if (!b) {
throw new IllegalArgumentException(format(errorMessageTemplate, p1, p2));
}
}
public static void checkArgument(boolean b, String errorMessageTemplate, char p1, int p2) {
if (!b) {
throw new IllegalArgumentException(format(errorMessageTemplate, p1, p2));
}
}
public static void checkArgument(boolean b, String errorMessageTemplate, char p1, long p2) {
if (!b) {
throw new IllegalArgumentException(format(errorMessageTemplate, p1, p2));
}
}
public static void checkArgument(boolean b, String errorMessageTemplate, char p1, Object p2) {
if (!b) {
throw new IllegalArgumentException(format(errorMessageTemplate, p1, p2));
}
}
public static void checkArgument(boolean b, String errorMessageTemplate, int p1, char p2) {
if (!b) {
throw new IllegalArgumentException(format(errorMessageTemplate, p1, p2));
}
}
public static void checkArgument(boolean b, String errorMessageTemplate, int p1, int p2) {
if (!b) {
throw new IllegalArgumentException(format(errorMessageTemplate, p1, p2));
}
}
public static void checkArgument(boolean b, String errorMessageTemplate, int p1, long p2) {
if (!b) {
throw new IllegalArgumentException(format(errorMessageTemplate, p1, p2));
}
}
public static void checkArgument(boolean b, String errorMessageTemplate, int p1, Object p2) {
if (!b) {
throw new IllegalArgumentException(format(errorMessageTemplate, p1, p2));
}
}
public static void checkArgument(boolean b, String errorMessageTemplate, long p1, char p2) {
if (!b) {
throw new IllegalArgumentException(format(errorMessageTemplate, p1, p2));
}
}
public static void checkArgument(boolean b, String errorMessageTemplate, long p1, int p2) {
if (!b) {
throw new IllegalArgumentException(format(errorMessageTemplate, p1, p2));
}
}
public static void checkArgument(boolean b, String errorMessageTemplate, long p1, long p2) {
if (!b) {
throw new IllegalArgumentException(format(errorMessageTemplate, p1, p2));
}
}
public static void checkArgument(boolean b, String errorMessageTemplate, long p1, Object p2) {
if (!b) {
throw new IllegalArgumentException(format(errorMessageTemplate, p1, p2));
}
}
public static void checkArgument(boolean b, String errorMessageTemplate, Object p1, char p2) {
if (!b) {
throw new IllegalArgumentException(format(errorMessageTemplate, p1, p2));
}
}
public static void checkArgument(boolean b, String errorMessageTemplate, Object p1, int p2) {
if (!b) {
throw new IllegalArgumentException(format(errorMessageTemplate, p1, p2));
}
}
public static void checkArgument(boolean b, String errorMessageTemplate, Object p1, long p2) {
if (!b) {
throw new IllegalArgumentException(format(errorMessageTemplate, p1, p2));
}
}
public static void checkArgument(boolean b, String errorMessageTemplate, Object p1, Object p2) {
if (!b) {
throw new IllegalArgumentException(format(errorMessageTemplate, p1, p2));
}
}
public static void checkArgument(boolean b, String errorMessageTemplate, Object p1, Object p2, Object p3) {
if (!b) {
throw new IllegalArgumentException(format(errorMessageTemplate, p1, p2, p3));
}
}
public static void checkArgument(boolean b, String errorMessageTemplate, Object p1, Object p2, Object p3, Object p4) {
if (!b) {
throw new IllegalArgumentException(format(errorMessageTemplate, p1, p2, p3, p4));
}
}
public static void checkState(boolean expression) {
if (!expression) {
throw new IllegalStateException();
}
}
public static void checkState(boolean expression, Object errorMessage) {
if (!expression) {
throw new IllegalStateException(String.valueOf(errorMessage));
}
}
public static void checkState(boolean expression, String errorMessageTemplate, Object... errorMessageArgs) {
if (!expression) {
throw new IllegalStateException(format(errorMessageTemplate, errorMessageArgs));
}
}
public static void checkState(boolean b, String errorMessageTemplate, char p1) {
if (!b) {
throw new IllegalStateException(format(errorMessageTemplate, p1));
}
}
public static void checkState(boolean b, String errorMessageTemplate, int p1) {
if (!b) {
throw new IllegalStateException(format(errorMessageTemplate, p1));
}
}
public static void checkState(boolean b, String errorMessageTemplate, long p1) {
if (!b) {
throw new IllegalStateException(format(errorMessageTemplate, p1));
}
}
public static void checkState(boolean b, String errorMessageTemplate, Object p1) {
if (!b) {
throw new IllegalStateException(format(errorMessageTemplate, p1));
}
}
public static void checkState(boolean b, String errorMessageTemplate, char p1, char p2) {
if (!b) {
throw new IllegalStateException(format(errorMessageTemplate, p1, p2));
}
}
public static void checkState(boolean b, String errorMessageTemplate, char p1, int p2) {
if (!b) {
throw new IllegalStateException(format(errorMessageTemplate, p1, p2));
}
}
public static void checkState(boolean b, String errorMessageTemplate, char p1, long p2) {
if (!b) {
throw new IllegalStateException(format(errorMessageTemplate, p1, p2));
}
}
public static void checkState(boolean b, String errorMessageTemplate, char p1, Object p2) {
if (!b) {
throw new IllegalStateException(format(errorMessageTemplate, p1, p2));
}
}
public static void checkState(boolean b, String errorMessageTemplate, int p1, char p2) {
if (!b) {
throw new IllegalStateException(format(errorMessageTemplate, p1, p2));
}
}
public static void checkState(boolean b, String errorMessageTemplate, int p1, int p2) {
if (!b) {
throw new IllegalStateException(format(errorMessageTemplate, p1, p2));
}
}
public static void checkState(boolean b, String errorMessageTemplate, int p1, long p2) {
if (!b) {
throw new IllegalStateException(format(errorMessageTemplate, p1, p2));
}
}
public static void checkState(boolean b, String errorMessageTemplate, int p1, Object p2) {
if (!b) {
throw new IllegalStateException(format(errorMessageTemplate, p1, p2));
}
}
public static void checkState(boolean b, String errorMessageTemplate, long p1, char p2) {
if (!b) {
throw new IllegalStateException(format(errorMessageTemplate, p1, p2));
}
}
public static void checkState(boolean b, String errorMessageTemplate, long p1, int p2) {
if (!b) {
throw new IllegalStateException(format(errorMessageTemplate, p1, p2));
}
}
public static void checkState(boolean b, String errorMessageTemplate, long p1, long p2) {
if (!b) {
throw new IllegalStateException(format(errorMessageTemplate, p1, p2));
}
}
public static void checkState(boolean b, String errorMessageTemplate, long p1, Object p2) {
if (!b) {
throw new IllegalStateException(format(errorMessageTemplate, p1, p2));
}
}
public static void checkState(boolean b, String errorMessageTemplate, Object p1, char p2) {
if (!b) {
throw new IllegalStateException(format(errorMessageTemplate, p1, p2));
}
}
public static void checkState(boolean b, String errorMessageTemplate, Object p1, int p2) {
if (!b) {
throw new IllegalStateException(format(errorMessageTemplate, p1, p2));
}
}
public static void checkState(boolean b, String errorMessageTemplate, Object p1, long p2) {
if (!b) {
throw new IllegalStateException(format(errorMessageTemplate, p1, p2));
}
}
public static void checkState(boolean b, String errorMessageTemplate, Object p1, Object p2) {
if (!b) {
throw new IllegalStateException(format(errorMessageTemplate, p1, p2));
}
}
public static void checkState(boolean b, String errorMessageTemplate, Object p1, Object p2, Object p3) {
if (!b) {
throw new IllegalStateException(format(errorMessageTemplate, p1, p2, p3));
}
}
public static void checkState(boolean b, String errorMessageTemplate, Object p1, Object p2, Object p3, Object p4) {
if (!b) {
throw new IllegalStateException(format(errorMessageTemplate, p1, p2, p3, p4));
}
}
public static <T> T checkNotNull(T reference) {
if (reference == null) {
throw new NullPointerException();
@@ -509,64 +209,6 @@ public final class Preconditions {
}
}
public static int checkElementIndex(int index, int size) {
return checkElementIndex(index, size, "index");
}
public static int checkElementIndex(int index, int size, String desc) {
if (index >= 0 && index < size) {
return index;
} else {
throw new IndexOutOfBoundsException(badElementIndex(index, size, desc));
}
}
private static String badElementIndex(int index, int size, String desc) {
if (index < 0) {
return format("%s (%s) must not be negative", desc, index);
} else if (size < 0) {
throw new IllegalArgumentException("negative size: " + size);
} else {
return format("%s (%s) must be less than size (%s)", desc, index, size);
}
}
public static int checkPositionIndex(int index, int size) {
return checkPositionIndex(index, size, "index");
}
public static int checkPositionIndex(int index, int size, String desc) {
if (index >= 0 && index <= size) {
return index;
} else {
throw new IndexOutOfBoundsException(badPositionIndex(index, size, desc));
}
}
private static String badPositionIndex(int index, int size, String desc) {
if (index < 0) {
return format("%s (%s) must not be negative", desc, index);
} else if (size < 0) {
throw new IllegalArgumentException("negative size: " + size);
} else {
return format("%s (%s) must not be greater than size (%s)", desc, index, size);
}
}
public static void checkPositionIndexes(int start, int end, int size) {
if (start < 0 || end < start || end > size) {
throw new IndexOutOfBoundsException(badPositionIndexes(start, end, size));
}
}
private static String badPositionIndexes(int start, int end, int size) {
if (start >= 0 && start <= size) {
return end >= 0 && end <= size ? format("end index (%s) must not be less than start index (%s)", end, start) : badPositionIndex(end, size, "end index");
} else {
return badPositionIndex(start, size, "start index");
}
}
static String format(String template, Object... args) {
template = String.valueOf(template);
StringBuilder builder = new StringBuilder(template.length() + 16 * args.length);
@@ -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);
}
}
@@ -14,17 +14,11 @@
package dev.brighten.antivpn.utils;
import static java.util.Objects.requireNonNull;
import java.io.Serializable;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import static dev.brighten.antivpn.utils.Preconditions.checkArgument;
import static dev.brighten.antivpn.utils.Preconditions.checkNotNull;
import static dev.brighten.antivpn.utils.NullnessCasts.uncheckedCastNullableTToT;
import static java.util.Objects.requireNonNull;
import jdk.tools.jlink.internal.Platform;
import static dev.brighten.antivpn.utils.NullnessCasts.uncheckedCastNullableTToT;
import static dev.brighten.antivpn.utils.Preconditions.checkNotNull;
import static java.util.Objects.requireNonNull;
/**
* Useful suppliers.
@@ -38,54 +32,6 @@ import jdk.tools.jlink.internal.Platform;
public final class Suppliers {
private Suppliers() {}
/**
* Returns a new supplier which is the composition of the provided function and supplier. In other
* words, the new supplier's value will be computed by retrieving the value from {@code supplier},
* and then applying {@code function} to that value. Note that the resulting supplier will not
* call {@code supplier} or invoke {@code function} until it is called.
*/
public static <F extends Object, T extends Object> Supplier<T> compose(
Function<? super F, T> function, Supplier<F> supplier) {
return new SupplierComposition<>(function, supplier);
}
private static class SupplierComposition<F extends Object, T extends Object>
implements Supplier<T>, Serializable {
final Function<? super F, T> function;
final Supplier<F> supplier;
SupplierComposition(Function<? super F, T> function, Supplier<F> supplier) {
this.function = checkNotNull(function);
this.supplier = checkNotNull(supplier);
}
@Override
public T get() {
return function.apply(supplier.get());
}
@Override
public boolean equals(Object obj) {
if (obj instanceof SupplierComposition) {
SupplierComposition<?, ?> that = (SupplierComposition<?, ?>) obj;
return function.equals(that.function) && supplier.equals(that.supplier);
}
return false;
}
@Override
public int hashCode() {
return Objects.hashCode(function, supplier);
}
@Override
public String toString() {
return "Suppliers.compose(" + function + ", " + supplier + ")";
}
private static final long serialVersionUID = 0;
}
/**
* 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
@@ -102,17 +48,17 @@ public final class Suppliers {
* <p>If {@code delegate} is an instance created by an earlier call to {@code memoize}, it is
* returned directly.
*/
public static <T extends Object> Supplier<T> memoize(Supplier<T> delegate) {
public static <T> Supplier<T> memoize(Supplier<T> delegate) {
if (delegate instanceof NonSerializableMemoizingSupplier
|| delegate instanceof MemoizingSupplier) {
return delegate;
}
return delegate instanceof Serializable
? new MemoizingSupplier<T>(delegate)
: new NonSerializableMemoizingSupplier<T>(delegate);
? new MemoizingSupplier<>(delegate)
: new NonSerializableMemoizingSupplier<>(delegate);
}
static class MemoizingSupplier<T extends Object> implements Supplier<T>, Serializable {
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
@@ -150,7 +96,7 @@ public final class Suppliers {
private static final long serialVersionUID = 0;
}
static class NonSerializableMemoizingSupplier<T extends Object> implements Supplier<T> {
static class NonSerializableMemoizingSupplier<T> implements Supplier<T> {
volatile Supplier<T> delegate;
volatile boolean initialized;
// "value" does not need to be volatile; visibility piggy-backs
@@ -194,187 +140,4 @@ public final class Suppliers {
+ ")";
}
}
/**
* Returns a supplier that caches the instance supplied by the delegate and removes the cached
* value after the specified time has passed. Subsequent calls to {@code get()} return the cached
* value if the expiration time has not passed. After the expiration time, a new value is
* retrieved, cached, and returned. See: <a
* href="http://en.wikipedia.org/wiki/Memoization">memoization</a>
*
* <p>The returned supplier is thread-safe. The supplier's serialized form does not contain the
* cached value, which will be recalculated when {@code get()} is called on the reserialized
* instance. The actual memoization does not happen when the underlying delegate throws an
* exception.
*
* <p>When the underlying delegate throws an exception then this memoizing supplier will keep
* delegating calls until it returns valid data.
*
* @param duration the length of time after a value is created that it should stop being returned
* by subsequent {@code get()} calls
* @param unit the unit that {@code duration} is expressed in
* @throws IllegalArgumentException if {@code duration} is not positive
* @since 2.0
*/
@SuppressWarnings("GoodTime") // should accept a java.time.Duration
public static <T extends Object> Supplier<T> memoizeWithExpiration(
Supplier<T> delegate, long duration, TimeUnit unit) {
return new ExpiringMemoizingSupplier<>(delegate, duration, unit);
}
@SuppressWarnings("GoodTime") // lots of violations
static class ExpiringMemoizingSupplier<T extends Object>
implements Supplier<T>, Serializable {
final Supplier<T> delegate;
final long durationNanos;
transient volatile T value;
// The special value 0 means "not yet initialized".
transient volatile long expirationNanos;
ExpiringMemoizingSupplier(Supplier<T> delegate, long duration, TimeUnit unit) {
this.delegate = checkNotNull(delegate);
this.durationNanos = unit.toNanos(duration);
checkArgument(duration > 0, "duration (%s %s) must be > 0", duration, unit);
}
@Override
public T get() {
// Another variant of Double Checked Locking.
//
// We use two volatile reads. We could reduce this to one by
// putting our fields into a holder class, but (at least on x86)
// the extra memory consumption and indirection are more
// expensive than the extra volatile reads.
long nanos = expirationNanos;
long now = System.nanoTime();
if (nanos == 0 || now - nanos >= 0) {
synchronized (this) {
if (nanos == expirationNanos) { // recheck for lost race
T t = delegate.get();
value = t;
nanos = now + durationNanos;
// In the very unlikely event that nanos is 0, set it to 1;
// no one will notice 1 ns of tardiness.
expirationNanos = (nanos == 0) ? 1 : nanos;
return t;
}
}
}
// This is safe because we checked `expirationNanos.`
return uncheckedCastNullableTToT(value);
}
@Override
public String toString() {
// This is a little strange if the unit the user provided was not NANOS,
// but we don't want to store the unit just for toString
return "Suppliers.memoizeWithExpiration(" + delegate + ", " + durationNanos + ", NANOS)";
}
private static final long serialVersionUID = 0;
}
/** Returns a supplier that always supplies {@code instance}. */
public static <T extends Object> Supplier<T> ofInstance(
T instance) {
return new SupplierOfInstance<>(instance);
}
private static class SupplierOfInstance<T extends Object>
implements Supplier<T>, Serializable {
final T instance;
SupplierOfInstance(T instance) {
this.instance = instance;
}
@Override
public T get() {
return instance;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof SupplierOfInstance) {
SupplierOfInstance<?> that = (SupplierOfInstance<?>) obj;
return Objects.equal(instance, that.instance);
}
return false;
}
@Override
public int hashCode() {
return Objects.hashCode(instance);
}
@Override
public String toString() {
return "Suppliers.ofInstance(" + instance + ")";
}
private static final long serialVersionUID = 0;
}
/**
* Returns a supplier whose {@code get()} method synchronizes on {@code delegate} before calling
* it, making it thread-safe.
*/
public static <T extends Object> Supplier<T> synchronizedSupplier(
Supplier<T> delegate) {
return new ThreadSafeSupplier<>(delegate);
}
private static class ThreadSafeSupplier<T extends Object>
implements Supplier<T>, Serializable {
final Supplier<T> delegate;
ThreadSafeSupplier(Supplier<T> delegate) {
this.delegate = checkNotNull(delegate);
}
@Override
public T get() {
synchronized (delegate) {
return delegate.get();
}
}
@Override
public String toString() {
return "Suppliers.synchronizedSupplier(" + delegate + ")";
}
private static final long serialVersionUID = 0;
}
/**
* Returns a function that accepts a supplier and returns the result of invoking {@link
* Supplier#get} on that supplier.
*
* <p><b>Java 8 users:</b> use the method reference {@code Supplier::get} instead.
*
* @since 8.0
*/
public static <T extends Object> Function<Supplier<T>, T> supplierFunction() {
@SuppressWarnings("unchecked") // implementation is "fully variant"
SupplierFunction<T> sf = (SupplierFunction<T>) SupplierFunctionImpl.INSTANCE;
return sf;
}
private interface SupplierFunction<T extends Object> extends Function<Supplier<T>, T> {}
private enum SupplierFunctionImpl implements SupplierFunction<Object> {
INSTANCE;
// Note: This makes T a "pass-through type"
@Override
public Object apply(Supplier<Object> input) {
return input.get();
}
@Override
public String toString() {
return "Suppliers.supplierFunction()";
}
}
}
@@ -0,0 +1,5 @@
package dev.brighten.antivpn.utils;
public record Tuple<F, S>(F first, S second) {
}
@@ -23,17 +23,17 @@ public class YamlConfiguration extends ConfigurationProvider
@Override
protected Yaml initialValue()
{
Representer representer = new Representer()
DumperOptions options = new DumperOptions();
options.setDefaultFlowStyle( DumperOptions.FlowStyle.BLOCK );
Representer representer = new Representer(options)
{
{
representers.put( Configuration.class, data -> represent( ( (Configuration) data ).self ));
}
};
DumperOptions options = new DumperOptions();
options.setDefaultFlowStyle( DumperOptions.FlowStyle.BLOCK );
representer.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
return new Yaml( new Constructor(), representer, options );
return new Yaml( new Constructor(new LoaderOptions()), representer, options );
}
};
@@ -1,10 +1,10 @@
package dev.brighten.antivpn.web;
import dev.brighten.antivpn.web.objects.QueryResponse;
import dev.brighten.antivpn.web.objects.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.web.objects.QueryResponse;
import dev.brighten.antivpn.web.objects.VPNResponse;
import java.io.IOException;
@@ -12,7 +12,7 @@ public class FunkemunkyAPI {
/**
*
* Queries https://funkemunky.cc/vpn API and returns information on the IP
* Queries <a href="https://funkemunky.cc/vpn">...</a> API and returns information on the IP
*
* @param ip String
* @param license String
@@ -25,7 +25,7 @@ public class FunkemunkyAPI {
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));
ip, license.isEmpty() ? "none" : license, cachedResults));
return VPNResponse.fromJson(result);
}
@@ -43,7 +43,7 @@ public class FunkemunkyAPI {
}
/**
* Queries https://funkemunky.cc/vpn/queryCheck and returns information based on the
* Queries <a href="https://funkemunky.cc/vpn/queryCheck">...</a> and returns information based on the
* provided licence input.
*
* @param license String
@@ -7,7 +7,7 @@ import lombok.Data;
/**
* Used to format the JSON response from https://funkemunky.cc/vpn/queryCheck into an object for project use.
* 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)
@@ -19,18 +19,7 @@ public class QueryResponse {
private long queriesMax;
/**
* Takes a JSON String and feeds it into {@link QueryResponse#fromJson(JSONObject)}
*
* @param jsonString String (formatted in JSON)
* @return QueryResponse
* @throws JSONException Throws when JSON is not formatted properly.
*/
public static QueryResponse fromJson(String jsonString) throws JSONException {
return fromJson(new JSONObject(jsonString));
}
/**
* Formats response from https://funkemunky.cc/vpn/queryCheck into {@link QueryResponse} for project use.
* Formats response from <a href="https://funkemunky.cc/vpn/queryCheck">...</a> into {@link QueryResponse} for project use.
*
* @param object JSONObject
* @return QueryResponse
@@ -2,7 +2,9 @@ package dev.brighten.antivpn.web.objects;
import dev.brighten.antivpn.utils.json.JSONException;
import dev.brighten.antivpn.utils.json.JSONObject;
import lombok.*;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
@Data
@AllArgsConstructor
@@ -39,14 +41,18 @@ public class VPNResponse {
*
* @param json String
* @return VPNResponse
* @throws JSONException
*/
public static VPNResponse fromJson(String json) throws JSONException {
return fromJson(new JSONObject(json));
}
public static final VPNResponse FAILED_RESPONSE = VPNResponse.builder()
.success(false)
.failureReason("Internal plugin API error.")
.build();
/**
* Formats response from https://funkemunky.cc/vpn into {@link VPNResponse} for project use.
* Formats response from <a href="https://funkemunky.cc/vpn">...</a> into {@link VPNResponse} for project use.
*
* @param jsonObject JSONObject
* @return VPNResponse
+5 -4
View File
@@ -11,12 +11,13 @@ 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: []
prefixWhitelists:
- "*"
# Configure your database here.
database:
# Enable to cache queries and save alerts state beyond restarts
enabled: false
useCredentials: true
enabled: true
useCredentials: false
#Options Mongo, MySQL, or H2
type: H2
# The database name you would like to use
@@ -50,7 +51,7 @@ alerts:
&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-code
# 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.
+2
View File
@@ -1,3 +1,5 @@
[![Project Map](https://sourcespy.com/shield.svg)](https://sourcespy.com/github/funkemunkyantivpn/)
# AntiVPN
An antivpn plugin utilizing the KauriVPN API
+177
View File
@@ -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;
}
}
@@ -1,8 +1,8 @@
package dev.brighten.antivpn.velocity.util;
package dev.brighten.antivpn.sponge.util;
public class StringUtils {
public class StringUtil {
public static String translateAlternateColorCodes(char altColorChar, String textToTranslate) {
public static String translateColorCodes(char altColorChar, String textToTranslate) {
char[] b = textToTranslate.toCharArray();
for(int i = 0; i < b.length - 1; ++i) {
@@ -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
}
]
}
]
}
+87
View File
@@ -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>
+13 -9
View File
@@ -5,15 +5,15 @@
<parent>
<artifactId>AntiVPN</artifactId>
<groupId>dev.brighten.antivpn</groupId>
<version>1.8.2</version>
<version>1.9.4</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>Velocity</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>
<repositories>
@@ -27,13 +27,13 @@
<dependency>
<groupId>com.velocitypowered</groupId>
<artifactId>velocity-api</artifactId>
<version>3.0.1</version>
<version>3.4.0-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>dev.brighten.antivpn</groupId>
<artifactId>Common</artifactId>
<version>1.8.2</version>
<version>1.9.4</version>
<scope>provided</scope>
</dependency>
<dependency>
@@ -49,17 +49,17 @@
<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.1.0</version>
<version>3.6.0</version>
<configuration>
<relocations>
<relocation>
@@ -67,6 +67,10 @@
<!-- 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>
@@ -1,12 +1,15 @@
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 com.velocitypowered.api.scheduler.ScheduledTask;
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.velocity.util.StringUtils;
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;
@@ -14,142 +17,181 @@ import java.util.logging.Level;
public class VelocityListener extends VPNExecutor {
private ScheduledTask cacheResetTask;
@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()));
event -> AntiVPN.getInstance()
.getPlayerExecutor()
.unloadPlayer(event.getPlayer().getUniqueId()));
VelocityPlugin.INSTANCE.getServer().getEventManager().register(VelocityPlugin.INSTANCE, LoginEvent.class,
event -> {
if (event.getResult().isAllowed()) {
if (event.getPlayer().hasPermission("antivpn.bypass") //Has bypass permission
//Is exempt
|| AntiVPN.getInstance().getExecutor().isWhitelisted(event.getPlayer().getUniqueId())
//Or has a name that starts with a certain prefix. This is for Bedrock exempting.
|| AntiVPN.getInstance().getExecutor().isWhitelisted(event.getPlayer().getRemoteAddress()
.getAddress().getHostAddress())
|| AntiVPN.getInstance().getVpnConfig().getPrefixWhitelists().stream()
.anyMatch(prefix -> event.getPlayer().getUsername().startsWith(prefix))) return;
APIPlayer player = AntiVPN.getInstance().getPlayerExecutor().getPlayer(event.getPlayer().getUniqueId())
.orElse(new OfflinePlayer(
event.getPlayer().getUniqueId(),
event.getPlayer().getUsername(),
event.getPlayer().getRemoteAddress().getAddress()
));
checkIp(event.getPlayer().getRemoteAddress().getAddress().getHostAddress(),
AntiVPN.getInstance().getVpnConfig().cachedResults(), result -> {
if (result.isSuccess()) {
// If the countryList() size is zero, no need to check.
// Running country check first
if (AntiVPN.getInstance().getVpnConfig().countryList().size() > 0
&& !(AntiVPN.getInstance().getExecutor()
.isWhitelisted(event.getPlayer().getUniqueId()) //Is exempt
//Or has a name that starts with a certain prefix. This is for Bedrock exempting.
|| AntiVPN.getInstance().getExecutor().isWhitelisted(event.getPlayer()
.getRemoteAddress().getAddress().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
if (AntiVPN.getInstance().getVpnConfig().countryKickCommands().size() == 0) {
final String kickReason = AntiVPN.getInstance().getVpnConfig()
.countryVanillaKickReason();
// Kicking our player
event.getPlayer().disconnect(LegacyComponentSerializer.builder().character('&')
.build().deserialize(kickReason
.replace("%player%", event.getPlayer().getUsername())
.replace("%country%", result.getCountryName())
.replace("%code%", result.getCountryCode())));
} else {
for (String cmd : AntiVPN.getInstance().getVpnConfig().countryKickCommands()) {
final String formattedCommand = StringUtils
.translateAlternateColorCodes('&',
cmd.replace("%player%",
event.getPlayer().getUsername())
.replace("%country%", result.getCountryName())
.replace("%code%", result.getCountryCode()));
// Running the command from console
VelocityPlugin.INSTANCE.getServer().getCommandManager()
.executeAsync(VelocityPlugin.INSTANCE.getServer()
.getConsoleCommandSource(),
StringUtils.translateAlternateColorCodes('&',
formattedCommand));
}
}
} else if (result.isProxy()) {
if (AntiVPN.getInstance().getVpnConfig().kickPlayersOnDetect())
event.getPlayer().disconnect(LegacyComponentSerializer.builder().character('&')
.build().deserialize(AntiVPN.getInstance().getVpnConfig()
.getKickString()));
VelocityPlugin.INSTANCE.getLogger().info(event.getPlayer().getUsername()
+ " joined on a VPN/Proxy (" + result.getMethod() + ")");
//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(AntiVPN.getInstance().getVpnConfig()
.alertMessage()
.replace("%player%",
event.getPlayer().getUsername())
.replace("%reason%",
result.getMethod())
.replace("%country%",
result.getCountryName())
.replace("%city%",
result.getCity())));
CheckResult instantResult = player.checkPlayer(result -> {
if(!result.resultType().isShouldBlock()) return;
//In case the user wants to run their own commands instead of using the
// built in kicking
if (AntiVPN.getInstance().getVpnConfig().runCommands()) {
for (String command : AntiVPN.getInstance().getVpnConfig().commands()) {
VelocityPlugin.INSTANCE.getServer().getCommandManager()
.executeAsync(VelocityPlugin.INSTANCE.getServer()
.getConsoleCommandSource(),
StringUtils.translateAlternateColorCodes('&',
command.replace("%player%",
event.getPlayer().getUsername())));
}
}
AntiVPN.getInstance().detections++;
}
} else {
VelocityPlugin.INSTANCE.getLogger()
.log(Level.WARNING,
"The API query was not a success! " +
"You may need to upgrade your license on " +
"https://funkemunky.cc/shop");
}
AntiVPN.getInstance().checked++;
});
}
});
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);
});
}
@Override
public void runCacheReset() {
cacheResetTask = VelocityPlugin.INSTANCE.getServer().getScheduler()
.buildTask(VelocityPlugin.INSTANCE, this::resetCache)
.repeat(20, TimeUnit.MINUTES)
.schedule();
private void handleDeniedTasks(LoginEvent event, CheckResult result) {
handleDeniedTasks(event, result, false);
}
@Override
public void shutdown() {
if (cacheResetTask != null) {
cacheResetTask.cancel();
cacheResetTask = null;
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();
}
}
threadExecutor.shutdown();
VelocityPlugin.INSTANCE.getServer().getEventManager().unregisterListener(VelocityPlugin.INSTANCE, this);
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) {
VelocityPlugin.INSTANCE.getLogger().log(Level.INFO, String.format(log, 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");
}
}
@@ -3,15 +3,22 @@ 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;
@@ -24,6 +31,10 @@ public class VelocityPlugin {
private final Metrics.Factory metricsFactory;
private final Path configDir;
@Nullable
private Metrics metrics;
public static VelocityPlugin INSTANCE;
@Inject
@@ -45,7 +56,9 @@ public class VelocityPlugin {
if(AntiVPN.getInstance().getVpnConfig().metrics()) {
logger.info("Starting metrics...");
Metrics metrics = metricsFactory.make(this, 12791);
metrics = metricsFactory.make(this, 12791);
metrics.addCustomChart(new SimplePie("database_used", this::getDatabaseType));
}
logger.info("Registering commands...");
@@ -54,4 +67,35 @@ public class VelocityPlugin {
.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";
}
}
}
@@ -12,6 +12,7 @@ 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 {
@@ -78,7 +79,10 @@ public class VelocityCommand implements SimpleCommand {
.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);
}
+10 -9
View File
@@ -7,19 +7,20 @@
<groupId>dev.brighten.antivpn</groupId>
<artifactId>AntiVPN</artifactId>
<packaging>pom</packaging>
<version>1.8.2</version>
<version>1.9.4</version>
<modules>
<module>Common</module>
<module>Bungee</module>
<module>Bukkit</module>
<module>Assembly</module>
<module>Velocity</module>
<module>Sponge</module>
<module>Universal</module>
</modules>
<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>
@@ -29,15 +30,15 @@
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<source>8</source>
<target>8</target>
<source>17</source>
<target>17</target>
<compilerArgument>-XDignore.symbol.file</compilerArgument>
<useIncrementalCompilation>false</useIncrementalCompilation>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
<version>1.18.30</version>
</path>
</annotationProcessorPaths>
</configuration>
@@ -60,9 +61,9 @@
<dependencies>
<dependency>
<groupId>cc.funkemunky.utils</groupId>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.0</version>
<version>1.18.30</version>
<scope>provided</scope>
</dependency>
</dependencies>