mirror of
https://github.com/TuxCoding/FastLogin.git
synced 2025-12-24 07:38:04 +01:00
Compare commits
126 Commits
tcpshield_
...
integratio
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2fdcea47d0 | ||
|
|
5d89273dad | ||
|
|
54a8c4c08c | ||
|
|
8167afa769 | ||
|
|
6140160a5e | ||
|
|
9a9a75fbb5 | ||
|
|
f355bf7ff2 | ||
|
|
5f13f5ab91 | ||
|
|
3e57b8baa4 | ||
|
|
0f205de1c0 | ||
|
|
ca7be278e1 | ||
|
|
f8c2a09014 | ||
|
|
e0f1cb1729 | ||
|
|
d581b34005 | ||
|
|
3665e15920 | ||
|
|
6f16700cdf | ||
|
|
8e6221d846 | ||
|
|
7a049b98a6 | ||
|
|
8de1546e7b | ||
|
|
7cce0f6e4a | ||
|
|
999738ef3e | ||
|
|
7951c4c893 | ||
|
|
9b04ea5c89 | ||
|
|
ac66cefd33 | ||
|
|
8a01ddc231 | ||
|
|
3bcc6c2e94 | ||
|
|
b351338e0b | ||
|
|
36c9ae2465 | ||
|
|
e0f823cbe4 | ||
|
|
17234a791b | ||
|
|
0e935e3ad0 | ||
|
|
52d778afb1 | ||
|
|
e6eb4939b4 | ||
|
|
36337f7feb | ||
|
|
aa51e98fe2 | ||
|
|
a5c7e7371d | ||
|
|
35b493a708 | ||
|
|
b02a1a54d9 | ||
|
|
253da03f9c | ||
|
|
4a5516c9f9 | ||
|
|
3ca8ae694d | ||
|
|
1d7c2aed61 | ||
|
|
03850ae4f2 | ||
|
|
b92911bf26 | ||
|
|
8859ebb454 | ||
|
|
06a8d6c625 | ||
|
|
8c33813e45 | ||
|
|
95251b611a | ||
|
|
6c47abc76d | ||
|
|
9c2068032f | ||
|
|
2110e93bd6 | ||
|
|
7439a95e16 | ||
|
|
e1c1da199e | ||
|
|
829ba79400 | ||
|
|
1dd27ff529 | ||
|
|
2cdfdcb5c5 | ||
|
|
15fee92937 | ||
|
|
cb29c5e226 | ||
|
|
d8cd39a974 | ||
|
|
9a2bc14b72 | ||
|
|
f5a60ca0b3 | ||
|
|
10a7b01bc7 | ||
|
|
909f263189 | ||
|
|
2f61a8f8ad | ||
|
|
d0a7832929 | ||
|
|
2ac638f3f9 | ||
|
|
af0bc34255 | ||
|
|
fcd2aa95f0 | ||
|
|
f76c7bd62f | ||
|
|
f570474fa3 | ||
|
|
3ee6cb2ada | ||
|
|
5612ca744b | ||
|
|
d2c94af4a7 | ||
|
|
4c514c269b | ||
|
|
40b4eae756 | ||
|
|
9978fe69d5 | ||
|
|
94299d2547 | ||
|
|
ef6f0fc436 | ||
|
|
1f83a656cc | ||
|
|
d7e0a4469f | ||
|
|
28480a0f01 | ||
|
|
a3bf875976 | ||
|
|
11c91e6428 | ||
|
|
8490ff628c | ||
|
|
0d7b8e237d | ||
|
|
8b4d4586a7 | ||
|
|
cd55441e4e | ||
|
|
17ecb186a5 | ||
|
|
aaff7710e0 | ||
|
|
37ac04c8ed | ||
|
|
c2ec8c93b0 | ||
|
|
665881d19a | ||
|
|
5192b98d78 | ||
|
|
709edc6c0a | ||
|
|
c458bd383a | ||
|
|
64fbbf759f | ||
|
|
20379d13b2 | ||
|
|
310ef4068c | ||
|
|
bc4d9857b4 | ||
|
|
d0491d44ec | ||
|
|
7ade127888 | ||
|
|
68a783bd40 | ||
|
|
352702eae4 | ||
|
|
b6dfa4802a | ||
|
|
fc226e1010 | ||
|
|
4befb35af9 | ||
|
|
01632ec125 | ||
|
|
268c70bc51 | ||
|
|
ec7c421f83 | ||
|
|
f82c85d3eb | ||
|
|
6e318ba9bb | ||
|
|
89d03bcc6b | ||
|
|
502b16a0e7 | ||
|
|
de0655cba2 | ||
|
|
0e7d8a595d | ||
|
|
7178ea4587 | ||
|
|
e639e29dee | ||
|
|
d8021931b6 | ||
|
|
47ee2cf458 | ||
|
|
d564d74443 | ||
|
|
586b357be8 | ||
|
|
172efafc2b | ||
|
|
056b8a7af7 | ||
|
|
198e8f9ea2 | ||
|
|
f39c3a1ea6 | ||
|
|
7dd0aa5bca |
5
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
5
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
@@ -48,16 +48,17 @@ body:
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: Platform
|
||||
description:
|
||||
description: Server software - choose your proxy software if you have multiple servers
|
||||
options:
|
||||
- Spigot
|
||||
- BungeeCord
|
||||
- Velocity
|
||||
validations:
|
||||
required: true
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Relevance
|
||||
description:
|
||||
description: Check list for previous tickets
|
||||
options:
|
||||
- label: I tried the latest build
|
||||
required: true
|
||||
|
||||
13
.github/workflows/codeql-analysis.yml
vendored
13
.github/workflows/codeql-analysis.yml
vendored
@@ -1,6 +1,6 @@
|
||||
# GitHub automatic code security scanning using CodeQL
|
||||
|
||||
# Human readable name in the actions tab
|
||||
# Human-readable name in the actions tab
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
@@ -35,6 +35,15 @@ jobs:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
# Setup Java
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@v2.3.0
|
||||
with:
|
||||
distribution: 'adopt'
|
||||
# Use Java 16, because it's minimum required version by Geyser
|
||||
java-version: 16
|
||||
cache: 'maven'
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
@@ -49,7 +58,7 @@ jobs:
|
||||
restore-keys: |
|
||||
${{ runner.os }}-maven-
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# Auto build attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v1
|
||||
|
||||
25
.github/workflows/maven.yml
vendored
25
.github/workflows/maven.yml
vendored
@@ -1,7 +1,7 @@
|
||||
# Automatically build, run unit and integration tests to detect errors early (CI provided by GitHub)
|
||||
# including making pull requests review easier
|
||||
|
||||
# Human readable name in the actions tab
|
||||
# Human-readable name in the actions tab
|
||||
name: Java CI
|
||||
|
||||
# Build on every pull request regardless of the branch
|
||||
@@ -18,7 +18,7 @@ jobs:
|
||||
# job id
|
||||
build_and_test:
|
||||
|
||||
# Environment image - always newest OS
|
||||
# Environment image - always use the newest OS
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
# Run steps
|
||||
@@ -26,25 +26,14 @@ jobs:
|
||||
# Pull changes
|
||||
- uses: actions/checkout@v2.3.4
|
||||
|
||||
# Cache artifacts - however this has the downside that we don't get notified of
|
||||
# artifact resolution failures like invalid repository
|
||||
# Nevertheless the repositories should be more stable and it makes no sense to pull
|
||||
# a same version every time
|
||||
# A dry run would make more sense
|
||||
- uses: actions/cache@v2.1.4
|
||||
with:
|
||||
path: ~/.m2/repository
|
||||
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-maven-
|
||||
|
||||
# Setup Java
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@v2.1.0
|
||||
uses: actions/setup-java@v2.3.0
|
||||
with:
|
||||
distribution: 'adopt'
|
||||
# Use Java 11, because it's minimum required version
|
||||
java-version: 11
|
||||
distribution: 'temurin'
|
||||
# Use Java 16, because it's minimum required version by Geyser
|
||||
java-version: 16
|
||||
cache: 'maven'
|
||||
|
||||
# Build and test (included in package)
|
||||
- name: Build with Maven and test
|
||||
|
||||
24
README.md
24
README.md
@@ -12,7 +12,7 @@ So they don't need to enter passwords. This is also called auto login (auto-logi
|
||||
* Forge/Sponge message support
|
||||
* Premium UUID support
|
||||
* Forward skins
|
||||
* Detect user name changed and will update the existing database record
|
||||
* Detect username changed and will update the existing database record
|
||||
* BungeeCord support
|
||||
* Auto register new premium players
|
||||
* Plugin: ProtocolSupport is supported and can be used as an alternative to ProtocolLib
|
||||
@@ -23,14 +23,14 @@ So they don't need to enter passwords. This is also called auto login (auto-logi
|
||||
|
||||
## Issues
|
||||
|
||||
Please use issues for bug reports, suggestions, questions and more. Please check for existing issues. Existing issues
|
||||
Please use issues for bug reports, suggestions, questions and more. Please check for existing issues. Existing issues
|
||||
can be voted up by adding up vote to the original post. Closing issues means that they are marked as resolved. Comments
|
||||
are still allowed and it could be re-opened.
|
||||
|
||||
## Development builds
|
||||
|
||||
Development builds contain the latest changes from the Source-Code. They are bleeding edge and could introduce new bugs,
|
||||
but also include features, enhancements and bug fixes that are not yet in a released version. If you click on the left
|
||||
but also include features, enhancements and bug fixes that are not yet in a released version. If you click on the left
|
||||
side on `Changes`, you can see iterative change sets leading to a specific build.
|
||||
|
||||
You can download them from here: https://ci.codemc.org/job/Games647/job/FastLogin/
|
||||
@@ -51,17 +51,17 @@ You can download them from here: https://ci.codemc.org/job/Games647/job/FastLogi
|
||||
|
||||
## Placeholder
|
||||
|
||||
This plugin supports `PlaceholderAPI` on `Spigot`. It exports the following variable
|
||||
`%fastlogin_status%`. In BungeeCord environments, the status of a player will be delivered with a delay after the player
|
||||
This plugin supports `PlaceholderAPI` on `Spigot`. It exports the following variable
|
||||
`%fastlogin_status%`. In BungeeCord environments, the status of a player will be delivered with a delay after the player
|
||||
already successful joined the server. This takes about a couple of milliseconds. In this case the value
|
||||
will be `Unknown`.
|
||||
will be `Unknown`.
|
||||
|
||||
Possible values: `Premium`, `Cracked`, `Unknown`
|
||||
Possible values: `Premium`, `Cracked`, `Unknown`
|
||||
|
||||
## Requirements
|
||||
|
||||
* Plugin:
|
||||
* [ProtocolLib](https://www.spigotmc.org/resources/protocollib.1997/) or
|
||||
* Plugin:
|
||||
* [ProtocolLib](https://www.spigotmc.org/resources/protocollib.1997/) or
|
||||
* [ProtocolSupport](https://www.spigotmc.org/resources/protocolsupport.7201/)
|
||||
* [Spigot](https://www.spigotmc.org) 1.8.8+
|
||||
* Java 8+
|
||||
@@ -112,10 +112,10 @@ This plugin performs network requests to:
|
||||
3. Now there is `allowed-proxies.txt` file in the FastLogin folder
|
||||
Put your stats id from the BungeeCord config into this file
|
||||
4. Activate ipForward in your BungeeCord config
|
||||
5. Download and Install FastLogin (or FastLoginBungee in newer versions) on BungeeCord AND Spigot
|
||||
5. Download and Install FastLogin (or FastLoginBungee/FastLoginBukkit in newer versions) on BungeeCord AND Spigot
|
||||
(on the servers where your login plugin is or where player should be able to execute the commands of FastLogin)
|
||||
6. Check your database settings in the config of FastLogin on BungeeCord
|
||||
7. Set proxy and Spigot in offline mode by setting the value onlinemode in your config.yml to false
|
||||
8. You should *always* firewall your Spigot server that it's only accessible through BungeeCord
|
||||
8. You should *always* firewall your Spigot server that it's only accessible through BungeeCord
|
||||
* https://www.spigotmc.org/wiki/bungeecord-installation/#post-installation
|
||||
* BungeeCord doesn't support SQLite per default, so you should change the configuration to MySQL or MariaDB
|
||||
* BungeeCord doesn't support SQLite per default, so you should change the configuration to MySQL or MariaDB. For that you have to install MariaDB/MySQL on your root server first and put the credentials you made in the FastLogin config files.
|
||||
|
||||
131
bukkit/pom.xml
131
bukkit/pom.xml
@@ -36,7 +36,7 @@
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<!--This have to be in lowercase because it's used by plugin.yml-->
|
||||
<!--This has to be in lowercase because it's used by plugin.yml-->
|
||||
<artifactId>fastlogin.bukkit</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
@@ -68,11 +68,24 @@
|
||||
<pattern>com.google.gson</pattern>
|
||||
<shadedPattern>fastlogin.gson</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>com.google.common</pattern>
|
||||
<shadedPattern>fastlogin.guava</shadedPattern>
|
||||
<excludes>
|
||||
<exclude>com.google.common.collect.Multimap</exclude>
|
||||
</excludes>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>io.papermc.lib</pattern>
|
||||
<shadedPattern>fastlogin.paperlib</shadedPattern>
|
||||
</relocation>
|
||||
</relocations>
|
||||
<!-- Rename the service file too to let SLF4J api find our own relocated jdk logger -->
|
||||
<!-- Located in META-INF/services -->
|
||||
<transformers>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
|
||||
</transformers>
|
||||
<minimizeJar>true</minimizeJar>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
@@ -125,12 +138,6 @@
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
|
||||
<!-- Floodgate -->
|
||||
<repository>
|
||||
<id>nukkitx-snapshot</id>
|
||||
<url>https://repo.nukkitx.com/maven-snapshots/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
@@ -141,27 +148,33 @@
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- PaperSpigot API for correcting usercache usage -->
|
||||
<!-- PaperSpigot API for correcting user cache usage -->
|
||||
<dependency>
|
||||
<groupId>com.destroystokyo.paper</groupId>
|
||||
<groupId>io.papermc.paper</groupId>
|
||||
<artifactId>paper-api</artifactId>
|
||||
<version>1.16.5-R0.1-SNAPSHOT</version>
|
||||
<version>1.18-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
<!-- Use our own newer api version -->
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- PaperLib for checking if server uses PaperSpigot -->
|
||||
<dependency>
|
||||
<groupId>io.papermc</groupId>
|
||||
<artifactId>paperlib</artifactId>
|
||||
<version>1.0.6</version>
|
||||
<scope>compile</scope>
|
||||
<version>1.0.7</version>
|
||||
</dependency>
|
||||
|
||||
<!--Library for listening and sending Minecraft packets-->
|
||||
<dependency>
|
||||
<groupId>com.comphenix.protocol</groupId>
|
||||
<artifactId>ProtocolLib</artifactId>
|
||||
<version>4.6.0</version>
|
||||
<version>4.7.0</version>
|
||||
<scope>provided</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
@@ -186,13 +199,30 @@
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!--Provide premium placeholders-->
|
||||
<!--Floodgate for Xbox Live Authentication-->
|
||||
<dependency>
|
||||
<groupId>me.clip</groupId>
|
||||
<artifactId>placeholderapi</artifactId>
|
||||
<version>2.10.8</version>
|
||||
<groupId>org.geysermc.floodgate</groupId>
|
||||
<artifactId>api</artifactId>
|
||||
<version>2.0-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.geysermc.cumulus</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- Bedrock player bridge -->
|
||||
<dependency>
|
||||
<groupId>org.geysermc</groupId>
|
||||
<artifactId>core</artifactId>
|
||||
<version>${geyser.version}</version>
|
||||
<scope>provided</scope>
|
||||
<optional>true</optional>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>*</groupId>
|
||||
@@ -201,14 +231,29 @@
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!--Floodgate for Xbox Live Authentication-->
|
||||
<!-- We need the API, but it was excluded above -->
|
||||
<dependency>
|
||||
<groupId>org.geysermc.floodgate</groupId>
|
||||
<artifactId>api</artifactId>
|
||||
<version>2.0-SNAPSHOT</version>
|
||||
<groupId>org.geysermc</groupId>
|
||||
<artifactId>geyser-api</artifactId>
|
||||
<version>${geyser.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!--Provide premium placeholders-->
|
||||
<dependency>
|
||||
<groupId>me.clip</groupId>
|
||||
<artifactId>placeholderapi</artifactId>
|
||||
<version>2.11.0</version>
|
||||
<scope>provided</scope>
|
||||
<optional>true</optional>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>*</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!--Login Plugins-->
|
||||
<dependency>
|
||||
<groupId>fr.xephi</groupId>
|
||||
@@ -308,5 +353,47 @@
|
||||
<optional>true</optional>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>testcontainers</artifactId>
|
||||
<version>1.16.3</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>mockserver</artifactId>
|
||||
<version>1.16.3</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.mock-server</groupId>
|
||||
<artifactId>mockserver-client-java</artifactId>
|
||||
<version>5.12.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.steveice10</groupId>
|
||||
<artifactId>mcprotocollib</artifactId>
|
||||
<version>1.18-2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.mojang</groupId>
|
||||
<artifactId>authlib</artifactId>
|
||||
<version>3.2.38</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<version>1.2.10</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
||||
@@ -69,11 +69,11 @@ public class BukkitLoginSession extends LoginSession {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the verify token the server sent to the client.
|
||||
* Gets the verify-token the server sent to the client.
|
||||
*
|
||||
* Empty if it's a BungeeCord connection
|
||||
*
|
||||
* @return the verify token from the server
|
||||
* @return verify token from the server
|
||||
*/
|
||||
public synchronized byte[] getVerifyToken() {
|
||||
return verifyToken.clone();
|
||||
|
||||
@@ -88,9 +88,9 @@ public class BungeeManager {
|
||||
|
||||
public void initialize() {
|
||||
try {
|
||||
enabled = detectBungeeCord();
|
||||
enabled = detectProxy();
|
||||
} catch (Exception ex) {
|
||||
plugin.getLog().warn("Cannot check bungeecord support. Fallback to non-bungee mode", ex);
|
||||
plugin.getLog().warn("Cannot check proxy support. Fallback to non-proxy mode", ex);
|
||||
}
|
||||
|
||||
if (enabled) {
|
||||
@@ -99,16 +99,21 @@ public class BungeeManager {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean detectBungeeCord() throws Exception {
|
||||
private boolean isProxySupported(String className, String fieldName) {
|
||||
try {
|
||||
enabled = Class.forName("org.spigotmc.SpigotConfig").getDeclaredField("bungee").getBoolean(null);
|
||||
return enabled;
|
||||
return Class.forName(className).getDeclaredField(fieldName).getBoolean(null);
|
||||
} catch (ClassNotFoundException notFoundEx) {
|
||||
//ignore server has no bungee support
|
||||
return false;
|
||||
} catch (Exception ex) {
|
||||
throw ex;
|
||||
//ignore server has no proxy support
|
||||
} catch (NoSuchFieldException | IllegalAccessException noSuchFieldException) {
|
||||
plugin.getLog().warn("Cannot access proxy field", noSuchFieldException);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean detectProxy() {
|
||||
return isProxySupported("org.spigotmc.SpigotConfig", "bungee")
|
||||
|| isProxySupported("com.destroystokyo.paper.PaperConfig", "velocitySupport");
|
||||
}
|
||||
|
||||
private void registerPluginChannels() {
|
||||
@@ -162,7 +167,7 @@ public class BungeeManager {
|
||||
/**
|
||||
* Mark the event to be fired including the task delay.
|
||||
*
|
||||
* @param player
|
||||
* @param player joining player
|
||||
*/
|
||||
public synchronized void markJoinEventFired(Player player) {
|
||||
firedJoinEvents.add(player.getUniqueId());
|
||||
@@ -176,7 +181,7 @@ public class BungeeManager {
|
||||
* session. If not fired, we can start a new force login task. This will still match the requirement that we wait
|
||||
* a certain time after the player join event fired.
|
||||
*
|
||||
* @param player
|
||||
* @param player joining player
|
||||
* @return event fired including delay
|
||||
*/
|
||||
public synchronized boolean didJoinEventFired(Player player) {
|
||||
@@ -186,7 +191,7 @@ public class BungeeManager {
|
||||
/**
|
||||
* Player quit clean up
|
||||
*
|
||||
* @param player
|
||||
* @param player joining player
|
||||
*/
|
||||
public synchronized void cleanup(Player player) {
|
||||
firedJoinEvents.remove(player.getUniqueId());
|
||||
|
||||
@@ -25,17 +25,20 @@
|
||||
*/
|
||||
package com.github.games647.fastlogin.bukkit;
|
||||
|
||||
import com.destroystokyo.paper.event.player.PlayerHandshakeEvent;
|
||||
import com.github.games647.fastlogin.bukkit.command.CrackedCommand;
|
||||
import com.github.games647.fastlogin.bukkit.command.PremiumCommand;
|
||||
import com.github.games647.fastlogin.bukkit.listener.ConnectionListener;
|
||||
import com.github.games647.fastlogin.bukkit.listener.PaperCacheListener;
|
||||
import com.github.games647.fastlogin.bukkit.listener.protocollib.ManualNameChange;
|
||||
import com.github.games647.fastlogin.bukkit.listener.protocollib.ProtocolLibListener;
|
||||
import com.github.games647.fastlogin.bukkit.listener.protocollib.SkinApplyListener;
|
||||
import com.github.games647.fastlogin.bukkit.listener.protocolsupport.ProtocolSupportListener;
|
||||
import com.github.games647.fastlogin.bukkit.task.DelayedAuthHook;
|
||||
import com.github.games647.fastlogin.core.CommonUtil;
|
||||
import com.github.games647.fastlogin.core.PremiumStatus;
|
||||
import com.github.games647.fastlogin.core.hooks.bedrock.BedrockService;
|
||||
import com.github.games647.fastlogin.core.hooks.bedrock.FloodgateService;
|
||||
import com.github.games647.fastlogin.core.hooks.bedrock.GeyserService;
|
||||
import com.github.games647.fastlogin.core.shared.FastLoginCore;
|
||||
import com.github.games647.fastlogin.core.shared.PlatformPlugin;
|
||||
|
||||
@@ -43,8 +46,8 @@ import io.papermc.lib.PaperLib;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
@@ -52,10 +55,10 @@ import java.util.concurrent.ConcurrentMap;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.plugin.PluginManager;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.geysermc.floodgate.api.FloodgateApi;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
/**
|
||||
@@ -67,15 +70,18 @@ public class FastLoginBukkit extends JavaPlugin implements PlatformPlugin<Comman
|
||||
private final ConcurrentMap<String, BukkitLoginSession> loginSession = CommonUtil.buildCache(1, -1);
|
||||
private final Map<UUID, PremiumStatus> premiumPlayers = new ConcurrentHashMap<>();
|
||||
private final Logger logger;
|
||||
private final BukkitScheduler scheduler;
|
||||
|
||||
private boolean serverStarted;
|
||||
private BungeeManager bungeeManager;
|
||||
private final BukkitScheduler scheduler;
|
||||
private FastLoginCore<Player, CommandSender, FastLoginBukkit> core;
|
||||
private FloodgateService floodgateService;
|
||||
private GeyserService geyserService;
|
||||
|
||||
private PremiumPlaceholder premiumPlaceholder;
|
||||
|
||||
public FastLoginBukkit() {
|
||||
this.logger = CommonUtil.createLoggerFromJDK(getLogger());
|
||||
this.logger = CommonUtil.initializeLoggerService(getLogger());
|
||||
this.scheduler = new BukkitScheduler(this, logger, getThreadFactory());
|
||||
}
|
||||
|
||||
@@ -85,31 +91,19 @@ public class FastLoginBukkit extends JavaPlugin implements PlatformPlugin<Comman
|
||||
core.load();
|
||||
|
||||
if (getServer().getOnlineMode()) {
|
||||
//we need to require offline to prevent a loginSession request for a offline player
|
||||
//we need to require offline to prevent a loginSession request for an offline player
|
||||
logger.error("Server has to be in offline mode");
|
||||
setEnabled(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check Floodgate config values
|
||||
if (!isValidFloodgateConfigString("autoLoginFloodgate")
|
||||
|| !isValidFloodgateConfigString("allowFloodgateNameConflict")) {
|
||||
if (!initializeFloodgate()) {
|
||||
setEnabled(false);
|
||||
return;
|
||||
}
|
||||
|
||||
bungeeManager = new BungeeManager(this);
|
||||
bungeeManager.initialize();
|
||||
|
||||
getServer().getPluginManager().registerEvents(new Listener() {
|
||||
|
||||
@EventHandler
|
||||
void onHandshake(PlayerHandshakeEvent handshakeEvent) {
|
||||
handshakeEvent.setCancelled(false);
|
||||
handshakeEvent.setSocketAddressHostname("192.168.0.1");
|
||||
}
|
||||
}, this);
|
||||
|
||||
PluginManager pluginManager = getServer().getPluginManager();
|
||||
if (bungeeManager.isEnabled()) {
|
||||
markInitialized();
|
||||
@@ -124,6 +118,21 @@ public class FastLoginBukkit extends JavaPlugin implements PlatformPlugin<Comman
|
||||
} else if (pluginManager.isPluginEnabled("ProtocolLib")) {
|
||||
ProtocolLibListener.register(this, core.getRateLimiter());
|
||||
|
||||
if (isPluginInstalled("floodgate")) {
|
||||
if (getConfig().getBoolean("floodgatePrefixWorkaround")){
|
||||
ManualNameChange.register(this, floodgateService);
|
||||
logger.info("Floodgate prefix injection workaround has been enabled.");
|
||||
logger.info("If you have problems joining the server, try disabling it in the configuration.");
|
||||
} else {
|
||||
logger.warn("We have detected that you are runnging FastLogin alongside Floodgate and ProtocolLib.");
|
||||
logger.warn("Currently there is an issue with FastLogin that prevents Floodgate name prefixes from showing up "
|
||||
+ "when it is together used with ProtocolLib.");
|
||||
logger.warn("If you would like to use Floodgate name prefixes, you can enable an experimental workaround by changing "
|
||||
+ "the value 'floodgatePrefixWorkaround' to true in config.yml.");
|
||||
logger.warn("For more information visit https://github.com/games647/FastLogin/issues/493");
|
||||
}
|
||||
}
|
||||
|
||||
//if server is using paper - we need to set the skin at pre login anyway, so no need for this listener
|
||||
if (!PaperLib.isPaper() && getConfig().getBoolean("forwardSkin")) {
|
||||
pluginManager.registerEvents(new SkinApplyListener(this), this);
|
||||
@@ -140,14 +149,14 @@ public class FastLoginBukkit extends JavaPlugin implements PlatformPlugin<Comman
|
||||
|
||||
pluginManager.registerEvents(new ConnectionListener(this), this);
|
||||
|
||||
//if server is using paper - we need to add one more listener to correct the usercache usage
|
||||
//if server is using paper - we need to add one more listener to correct the user cache usage
|
||||
if (PaperLib.isPaper()) {
|
||||
pluginManager.registerEvents(new PaperCacheListener(this), this);
|
||||
}
|
||||
|
||||
//register commands using a unique name
|
||||
getCommand("premium").setExecutor(new PremiumCommand(this));
|
||||
getCommand("cracked").setExecutor(new CrackedCommand(this));
|
||||
Optional.ofNullable(getCommand("premium")).ifPresent(c -> c.setExecutor(new PremiumCommand(this)));
|
||||
Optional.ofNullable(getCommand("cracked")).ifPresent(c -> c.setExecutor(new CrackedCommand(this)));
|
||||
|
||||
if (pluginManager.isPluginEnabled("PlaceholderAPI")) {
|
||||
premiumPlaceholder = new PremiumPlaceholder(this);
|
||||
@@ -157,6 +166,22 @@ public class FastLoginBukkit extends JavaPlugin implements PlatformPlugin<Comman
|
||||
dependencyWarnings();
|
||||
}
|
||||
|
||||
private boolean initializeFloodgate() {
|
||||
if (getServer().getPluginManager().getPlugin("Geyser-Spigot") != null) {
|
||||
geyserService = new GeyserService(GeyserImpl.getInstance(), core);
|
||||
}
|
||||
|
||||
if (getServer().getPluginManager().getPlugin("floodgate") != null) {
|
||||
floodgateService = new FloodgateService(FloodgateApi.getInstance(), core);
|
||||
|
||||
// Check Floodgate config values and return
|
||||
return floodgateService.isValidFloodgateConfigString("autoLoginFloodgate")
|
||||
&& floodgateService.isValidFloodgateConfigString("allowFloodgateNameConflict");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
loginSession.clear();
|
||||
@@ -166,7 +191,10 @@ public class FastLoginBukkit extends JavaPlugin implements PlatformPlugin<Comman
|
||||
core.close();
|
||||
}
|
||||
|
||||
bungeeManager.cleanup();
|
||||
if (bungeeManager != null) {
|
||||
bungeeManager.cleanup();
|
||||
}
|
||||
|
||||
if (premiumPlaceholder != null && getServer().getPluginManager().isPluginEnabled("PlaceholderAPI")) {
|
||||
try {
|
||||
premiumPlaceholder.unregister();
|
||||
@@ -190,23 +218,22 @@ public class FastLoginBukkit extends JavaPlugin implements PlatformPlugin<Comman
|
||||
return loginSession;
|
||||
}
|
||||
|
||||
public BukkitLoginSession getSession(InetSocketAddress addr) {
|
||||
String id = getSessionId(addr);
|
||||
BukkitLoginSession session = loginSession.get(id);
|
||||
return session;
|
||||
public BukkitLoginSession getSession(InetSocketAddress address) {
|
||||
String id = getSessionId(address);
|
||||
return loginSession.get(id);
|
||||
}
|
||||
|
||||
public String getSessionId(InetSocketAddress addr) {
|
||||
return addr.getAddress().getHostAddress() + ':' + addr.getPort();
|
||||
public String getSessionId(InetSocketAddress address) {
|
||||
return address.getAddress().getHostAddress() + ':' + address.getPort();
|
||||
}
|
||||
|
||||
public void putSession(InetSocketAddress addr, BukkitLoginSession session) {
|
||||
String id = getSessionId(addr);
|
||||
public void putSession(InetSocketAddress address, BukkitLoginSession session) {
|
||||
String id = getSessionId(address);
|
||||
loginSession.put(id, session);
|
||||
}
|
||||
|
||||
public void removeSession(InetSocketAddress addr) {
|
||||
String id = getSessionId(addr);
|
||||
public void removeSession(InetSocketAddress address) {
|
||||
String id = getSessionId(address);
|
||||
loginSession.remove(id);
|
||||
}
|
||||
|
||||
@@ -217,7 +244,7 @@ public class FastLoginBukkit extends JavaPlugin implements PlatformPlugin<Comman
|
||||
/**
|
||||
* Fetches the premium status of an online player.
|
||||
*
|
||||
* @param onlinePlayer
|
||||
* @param onlinePlayer player that is currently online player (play state)
|
||||
* @return the online status or unknown if an error happened, the player isn't online or BungeeCord doesn't send
|
||||
* us the status message yet (This means you cannot check the login status on the PlayerJoinEvent).
|
||||
*/
|
||||
@@ -263,40 +290,31 @@ public class FastLoginBukkit extends JavaPlugin implements PlatformPlugin<Comman
|
||||
receiver.sendMessage(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a config entry (related to Floodgate) is valid. <br>
|
||||
* Writes to Log if the value is invalid.
|
||||
* <p>
|
||||
* This should be used for:
|
||||
* <ul>
|
||||
* <li>allowFloodgateNameConflict
|
||||
* <li>autoLoginFloodgate
|
||||
* <li>autoRegisterFloodgate
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* @param key the key of the entry in config.yml
|
||||
* @return <b>true</b> if the entry's value is "true", "false", or "linked"
|
||||
*/
|
||||
private boolean isValidFloodgateConfigString(String key) {
|
||||
String value = core.getConfig().get(key).toString().toLowerCase(Locale.ENGLISH);
|
||||
if (!value.equals("true") && !value.equals("linked") && !value.equals("false") && !value.equals("no-conflict")) {
|
||||
logger.error("Invalid value detected for {} in FastLogin/config.yml.", key);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
/**
|
||||
* Checks if a plugin is installed on the server
|
||||
* @param name the name of the plugin
|
||||
* @return true if the plugin is installed
|
||||
*/
|
||||
@Override
|
||||
public boolean isPluginInstalled(String name) {
|
||||
// the plugin may be enabled after FastLogin, so isPluginEnabled() won't work here
|
||||
return Bukkit.getServer().getPluginManager().getPlugin(name) != null;
|
||||
}
|
||||
|
||||
public FloodgateService getFloodgateService() {
|
||||
return floodgateService;
|
||||
}
|
||||
|
||||
public GeyserService getGeyserService() {
|
||||
return geyserService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a plugin is installed on the server
|
||||
*
|
||||
* @param name the name of the plugin
|
||||
* @return true if the plugin is installed
|
||||
*/
|
||||
@Override
|
||||
public boolean isPluginInstalled(String name) {
|
||||
// the plugin may be enabled after FastLogin, so isPluginEnabled() won't work here
|
||||
return Bukkit.getServer().getPluginManager().getPlugin(name) != null;
|
||||
public BedrockService<?> getBedrockService() {
|
||||
if (floodgateService != null) {
|
||||
return floodgateService;
|
||||
}
|
||||
return geyserService;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -310,13 +328,6 @@ public class FastLoginBukkit extends JavaPlugin implements PlatformPlugin<Comman
|
||||
+ "Floodgate 2.0 from https://ci.opencollab.dev/job/GeyserMC/job/Floodgate/job/dev%252F2.0/");
|
||||
logger.warn("Don't forget to update Geyser to a supported version as well from "
|
||||
+ "https://ci.opencollab.dev/job/GeyserMC/job/Geyser/job/floodgate-2.0/");
|
||||
} else if (isPluginInstalled("floodgate") && isPluginInstalled("ProtocolLib")) {
|
||||
logger.warn("We have detected that you are running FastLogin alongside Floodgate and ProtocolLib.");
|
||||
logger.warn("Currently there is an issue with FastLogin that prevents Floodgate's name prefixes from " +
|
||||
"showing up when it is together used with ProtocolLib.");
|
||||
logger.warn("If you would like to use Floodgate name prefixes, you can replace ProtocolLib with " +
|
||||
"ProtocolSupport which does not have this issue.");
|
||||
logger.warn("For more information visit https://github.com/games647/FastLogin/issues/493");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ package com.github.games647.fastlogin.bukkit;
|
||||
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
|
||||
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class PremiumPlaceholder extends PlaceholderExpansion {
|
||||
|
||||
@@ -40,7 +41,7 @@ public class PremiumPlaceholder extends PlaceholderExpansion {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String onRequest(OfflinePlayer player, String identifier) {
|
||||
public String onRequest(OfflinePlayer player, @NotNull String identifier) {
|
||||
// player is null if offline
|
||||
if (player != null && PLACEHOLDER_VARIABLE.equals(identifier)) {
|
||||
return plugin.getStatus(player.getUniqueId()).getReadableName();
|
||||
@@ -50,7 +51,7 @@ public class PremiumPlaceholder extends PlaceholderExpansion {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIdentifier() {
|
||||
public @NotNull String getIdentifier() {
|
||||
return plugin.getName();
|
||||
}
|
||||
|
||||
@@ -60,12 +61,12 @@ public class PremiumPlaceholder extends PlaceholderExpansion {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAuthor() {
|
||||
public @NotNull String getAuthor() {
|
||||
return String.join(", ", plugin.getDescription().getAuthors());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getVersion() {
|
||||
public @NotNull String getVersion() {
|
||||
return plugin.getDescription().getVersion();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ import com.github.games647.fastlogin.core.StoredProfile;
|
||||
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import static com.github.games647.fastlogin.core.shared.event.FastLoginPremiumToggleEvent.PremiumToggleReason;
|
||||
|
||||
@@ -41,7 +42,7 @@ public class CrackedCommand extends ToggleCommand {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
|
||||
if (args.length == 0) {
|
||||
onCrackedSelf(sender, command, args);
|
||||
} else {
|
||||
|
||||
@@ -28,17 +28,18 @@ package com.github.games647.fastlogin.bukkit.command;
|
||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||
import com.github.games647.fastlogin.bukkit.event.BukkitFastLoginPremiumToggleEvent;
|
||||
import com.github.games647.fastlogin.core.StoredProfile;
|
||||
import com.github.games647.fastlogin.core.shared.event.FastLoginPremiumToggleEvent.PremiumToggleReason;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import com.github.games647.fastlogin.core.shared.event.FastLoginPremiumToggleEvent.PremiumToggleReason;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Let users activate fast login by command. This only be accessible if
|
||||
* the user has access to it's account. So we can make sure that not another
|
||||
* the user has access to its account. So we can make sure that not another
|
||||
* person with a paid account and the same username can steal their account.
|
||||
*/
|
||||
public class PremiumCommand extends ToggleCommand {
|
||||
@@ -48,7 +49,7 @@ public class PremiumCommand extends ToggleCommand {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
|
||||
if (args.length == 0) {
|
||||
onPremiumSelf(sender, command, args);
|
||||
} else {
|
||||
|
||||
@@ -47,12 +47,12 @@ public abstract class ToggleCommand implements CommandExecutor {
|
||||
}
|
||||
|
||||
protected boolean hasOtherPermission(CommandSender sender, Command cmd) {
|
||||
if (!sender.hasPermission(cmd.getPermission() + ".other")) {
|
||||
plugin.getCore().sendLocaleMessage("no-permission", sender);
|
||||
return false;
|
||||
if (sender.hasPermission(cmd.getPermission() + ".other")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
plugin.getCore().sendLocaleMessage("no-permission", sender);
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean forwardBungeeCommand(CommandSender sender, String target, boolean activate) {
|
||||
|
||||
@@ -31,6 +31,7 @@ import com.github.games647.fastlogin.core.shared.event.FastLoginAutoLoginEvent;
|
||||
import org.bukkit.event.Cancellable;
|
||||
import org.bukkit.event.Event;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class BukkitFastLoginAutoLoginEvent extends Event implements FastLoginAutoLoginEvent, Cancellable {
|
||||
|
||||
@@ -67,7 +68,7 @@ public class BukkitFastLoginAutoLoginEvent extends Event implements FastLoginAut
|
||||
}
|
||||
|
||||
@Override
|
||||
public HandlerList getHandlers() {
|
||||
public @NotNull HandlerList getHandlers() {
|
||||
return handlers;
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ import com.github.games647.fastlogin.core.shared.LoginSource;
|
||||
import com.github.games647.fastlogin.core.shared.event.FastLoginPreLoginEvent;
|
||||
import org.bukkit.event.Event;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class BukkitFastLoginPreLoginEvent extends Event implements FastLoginPreLoginEvent {
|
||||
|
||||
@@ -62,7 +63,7 @@ public class BukkitFastLoginPreLoginEvent extends Event implements FastLoginPreL
|
||||
}
|
||||
|
||||
@Override
|
||||
public HandlerList getHandlers() {
|
||||
public @NotNull HandlerList getHandlers() {
|
||||
return handlers;
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ import com.github.games647.fastlogin.core.StoredProfile;
|
||||
import com.github.games647.fastlogin.core.shared.event.FastLoginPremiumToggleEvent;
|
||||
import org.bukkit.event.Event;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class BukkitFastLoginPremiumToggleEvent extends Event implements FastLoginPremiumToggleEvent {
|
||||
|
||||
@@ -53,7 +54,7 @@ public class BukkitFastLoginPremiumToggleEvent extends Event implements FastLogi
|
||||
}
|
||||
|
||||
@Override
|
||||
public HandlerList getHandlers() {
|
||||
public @NotNull HandlerList getHandlers() {
|
||||
return handlers;
|
||||
}
|
||||
|
||||
|
||||
@@ -28,20 +28,18 @@ package com.github.games647.fastlogin.bukkit.hook;
|
||||
import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
|
||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
|
||||
|
||||
import fr.xephi.authme.api.v3.AuthMeApi;
|
||||
import fr.xephi.authme.events.RestoreSessionEvent;
|
||||
import fr.xephi.authme.process.Management;
|
||||
import fr.xephi.authme.process.register.executors.ApiPasswordRegisterParams;
|
||||
import fr.xephi.authme.process.register.executors.RegistrationMethod;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
/**
|
||||
* GitHub: https://github.com/Xephi/AuthMeReloaded/
|
||||
* <p>
|
||||
@@ -77,7 +75,7 @@ public class AuthMeHook implements AuthPlugin<Player>, Listener {
|
||||
public void onSessionRestore(RestoreSessionEvent restoreSessionEvent) {
|
||||
Player player = restoreSessionEvent.getPlayer();
|
||||
|
||||
BukkitLoginSession session = plugin.getSession(player.spigot().getRawAddress());
|
||||
BukkitLoginSession session = plugin.getSession(player.getAddress());
|
||||
if (session != null && session.isVerified()) {
|
||||
restoreSessionEvent.setCancelled(true);
|
||||
}
|
||||
|
||||
@@ -121,11 +121,11 @@ public class CrazyLoginHook implements AuthPlugin<Player> {
|
||||
public boolean forceRegister(Player player, String password) {
|
||||
CrazyLoginDataDatabase crazyDatabase = crazyLoginPlugin.getCrazyDatabase();
|
||||
|
||||
//this executes a sql query and accesses only thread safe collections so we can run it async
|
||||
//this executes a sql query and accesses only thread safe collections, so we can run it async
|
||||
LoginPlayerData playerData = crazyLoginPlugin.getPlayerData(player.getName());
|
||||
if (playerData == null) {
|
||||
//create a fake account - this will be saved to the database with the password=FAILEDLOADING
|
||||
//user cannot login with that password unless the admin uses plain text
|
||||
//user cannot log in with that password unless the admin uses plain text
|
||||
//this automatically marks the player as logged in
|
||||
crazyDatabase.save(new LoginPlayerData(player));
|
||||
return forceLogin(player);
|
||||
|
||||
@@ -41,6 +41,7 @@ import java.util.UUID;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.messaging.PluginMessageListener;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Responsible for receiving messages from a BungeeCord instance.
|
||||
@@ -57,7 +58,7 @@ public class BungeeListener implements PluginMessageListener {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPluginMessageReceived(String channel, Player player, byte[] message) {
|
||||
public void onPluginMessageReceived(@NotNull String channel, Player player, byte[] message) {
|
||||
ByteArrayDataInput dataInput = ByteStreams.newDataInput(message);
|
||||
|
||||
LoginActionMessage loginMessage = new LoginActionMessage();
|
||||
@@ -67,7 +68,7 @@ public class BungeeListener implements PluginMessageListener {
|
||||
|
||||
Player targetPlayer = player;
|
||||
if (!loginMessage.getPlayerName().equals(player.getName())) {
|
||||
targetPlayer = Bukkit.getPlayerExact(loginMessage.getPlayerName());;
|
||||
targetPlayer = Bukkit.getPlayerExact(loginMessage.getPlayerName());
|
||||
}
|
||||
|
||||
if (targetPlayer == null) {
|
||||
@@ -126,9 +127,9 @@ public class BungeeListener implements PluginMessageListener {
|
||||
|
||||
private void startLoginTaskIfReady(Player player, BukkitLoginSession session) {
|
||||
session.setVerified(true);
|
||||
plugin.putSession(player.spigot().getRawAddress(), session);
|
||||
plugin.putSession(player.getAddress(), session);
|
||||
|
||||
// only start a new login task if the join event fired earlier. This event then didn
|
||||
// only start a new login task if the join event fired earlier. This event then didn't
|
||||
boolean result = plugin.getBungeeManager().didJoinEventFired(player);
|
||||
plugin.getLog().info("Delaying force login until join event fired?: {}", result);
|
||||
if (result) {
|
||||
|
||||
@@ -29,6 +29,7 @@ import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
|
||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||
import com.github.games647.fastlogin.bukkit.task.FloodgateAuthTask;
|
||||
import com.github.games647.fastlogin.bukkit.task.ForceLoginTask;
|
||||
import com.github.games647.fastlogin.core.hooks.bedrock.FloodgateService;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
@@ -38,12 +39,12 @@ import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.bukkit.event.player.PlayerLoginEvent;
|
||||
import org.bukkit.event.player.PlayerLoginEvent.Result;
|
||||
import org.geysermc.floodgate.api.FloodgateApi;
|
||||
import org.geysermc.floodgate.api.player.FloodgatePlayer;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
import org.bukkit.metadata.Metadatable;
|
||||
import org.geysermc.floodgate.api.player.FloodgatePlayer;
|
||||
|
||||
/**
|
||||
* This listener tells authentication plugins if the player has a premium account and we checked it successfully. So the
|
||||
* This listener tells authentication plugins weather the player has a premium account. So the
|
||||
* plugin can skip authentication.
|
||||
*/
|
||||
public class ConnectionListener implements Listener {
|
||||
@@ -69,37 +70,41 @@ public class ConnectionListener implements Listener {
|
||||
Player player = joinEvent.getPlayer();
|
||||
|
||||
Bukkit.getScheduler().runTaskLater(plugin, () -> {
|
||||
// session exists so the player is ready for force login
|
||||
// cases: Paper (firing BungeeCord message before PlayerJoinEvent) or not running BungeeCord and already
|
||||
// having the login session from the login process
|
||||
BukkitLoginSession session = plugin.getSession(player.spigot().getRawAddress());
|
||||
|
||||
boolean isFloodgateLogin = false;
|
||||
if (Bukkit.getServer().getPluginManager().isPluginEnabled("floodgate")) {
|
||||
FloodgatePlayer floodgatePlayer = FloodgateApi.getInstance().getPlayer(player.getUniqueId());
|
||||
if (floodgatePlayer != null) {
|
||||
isFloodgateLogin = true;
|
||||
Runnable floodgateAuthTask = new FloodgateAuthTask(plugin.getCore(), player, floodgatePlayer);
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, floodgateAuthTask);
|
||||
}
|
||||
}
|
||||
|
||||
if (!isFloodgateLogin) {
|
||||
if (session == null) {
|
||||
String sessionId = plugin.getSessionId(player.spigot().getRawAddress());
|
||||
plugin.getLog().info("No on-going login session for player: {} with ID {}", player, sessionId);
|
||||
} else {
|
||||
Runnable forceLoginTask = new ForceLoginTask(plugin.getCore(), player, session);
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, forceLoginTask);
|
||||
}
|
||||
}
|
||||
|
||||
plugin.getBungeeManager().markJoinEventFired(player);
|
||||
delayForceLogin(player);
|
||||
// delay the login process to let auth plugins initialize the player
|
||||
// Magic number however as there is no direct event from those plugins
|
||||
}, DELAY_LOGIN);
|
||||
}
|
||||
|
||||
private void delayForceLogin(Player player) {
|
||||
// session exists so the player is ready for force login
|
||||
// cases: Paper (firing BungeeCord message before PlayerJoinEvent) or not running BungeeCord and already
|
||||
// having the login session from the login process
|
||||
BukkitLoginSession session = plugin.getSession(player.getAddress());
|
||||
|
||||
if (session == null) {
|
||||
// Floodgate players usually don't have a session at this point
|
||||
// exception: if force login by bungee message had been delayed
|
||||
FloodgateService floodgateService = plugin.getFloodgateService();
|
||||
if (floodgateService != null) {
|
||||
FloodgatePlayer floodgatePlayer = floodgateService.getBedrockPlayer(player.getUniqueId());
|
||||
if (floodgatePlayer != null) {
|
||||
Runnable floodgateAuthTask = new FloodgateAuthTask(plugin.getCore(), player, floodgatePlayer);
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, floodgateAuthTask);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
String sessionId = plugin.getSessionId(player.getAddress());
|
||||
plugin.getLog().info("No on-going login session for player: {} with ID {}", player, sessionId);
|
||||
} else {
|
||||
Runnable forceLoginTask = new ForceLoginTask(plugin.getCore(), player, session);
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, forceLoginTask);
|
||||
}
|
||||
|
||||
plugin.getBungeeManager().markJoinEventFired(player);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerQuit(PlayerQuitEvent quitEvent) {
|
||||
Player player = quitEvent.getPlayer();
|
||||
@@ -110,7 +115,7 @@ public class ConnectionListener implements Listener {
|
||||
plugin.getBungeeManager().cleanup(player);
|
||||
}
|
||||
|
||||
private void removeBlockedStatus(Player player) {
|
||||
private void removeBlockedStatus(Metadatable player) {
|
||||
player.removeMetadata(plugin.getName(), plugin);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,8 +44,8 @@ public class PaperCacheListener implements Listener {
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST)
|
||||
//if paper is used - player skin must be set at pre login, otherwise usercache is used
|
||||
//using usercache makes premium name change basically impossible
|
||||
//if paper is used - player skin must be set at pre login, otherwise user cache is used
|
||||
// user cache makes premium name change basically impossible
|
||||
public void onAsyncPlayerPreLogin(AsyncPlayerPreLoginEvent event) {
|
||||
if (event.getLoginResult() != Result.ALLOWED) {
|
||||
return;
|
||||
|
||||
@@ -35,6 +35,7 @@ import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.util.Random;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
@@ -55,7 +56,7 @@ public class EncryptionUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a RSA key pair
|
||||
* Generate an RSA key pair
|
||||
*
|
||||
* @return The RSA key pair.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package com.github.games647.fastlogin.bukkit.listener.protocollib;
|
||||
|
||||
import com.comphenix.protocol.events.PacketAdapter;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.comphenix.protocol.wrappers.WrappedGameProfile;
|
||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||
import com.github.games647.fastlogin.core.hooks.bedrock.FloodgateService;
|
||||
|
||||
import org.geysermc.floodgate.api.FloodgateApi;
|
||||
|
||||
import static com.comphenix.protocol.PacketType.Login.Client.START;
|
||||
|
||||
import com.comphenix.protocol.ProtocolLibrary;
|
||||
|
||||
/**
|
||||
* Manually inject Floodgate player name prefixes.
|
||||
* <br>
|
||||
* This is used as a workaround, because Floodgate fails to inject
|
||||
* the prefixes when it's used together with ProtocolLib and FastLogin.
|
||||
* <br>
|
||||
* For more information visit: https://github.com/games647/FastLogin/issues/493
|
||||
*/
|
||||
public class ManualNameChange extends PacketAdapter {
|
||||
|
||||
private final FloodgateService floodgate;
|
||||
|
||||
public ManualNameChange(FastLoginBukkit plugin, FloodgateService floodgate) {
|
||||
super(params()
|
||||
.plugin(plugin)
|
||||
.types(START));
|
||||
|
||||
this.plugin = plugin;
|
||||
this.floodgate = floodgate;
|
||||
}
|
||||
|
||||
public static void register(FastLoginBukkit plugin, FloodgateService floodgate) {
|
||||
// they will be created with a static builder, because otherwise it will throw a NoClassDefFoundError
|
||||
ProtocolLibrary.getProtocolManager()
|
||||
.getAsynchronousManager()
|
||||
.registerAsyncHandler(new ManualNameChange(plugin, floodgate))
|
||||
.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketReceiving(PacketEvent packetEvent) {
|
||||
PacketContainer packet = packetEvent.getPacket();
|
||||
WrappedGameProfile originalProfile = packet.getGameProfiles().read(0);
|
||||
|
||||
if (floodgate.getBedrockPlayer(originalProfile.getName()) == null) {
|
||||
//not a Floodgate player, no need to add a prefix
|
||||
return;
|
||||
}
|
||||
|
||||
packet.setMeta("original_name", originalProfile.getName());
|
||||
String prefixedName = FloodgateApi.getInstance().getPlayerPrefix() + originalProfile.getName();
|
||||
WrappedGameProfile updatedProfile = originalProfile.withName(prefixedName);
|
||||
packet.getGameProfiles().write(0, updatedProfile);
|
||||
}
|
||||
}
|
||||
@@ -54,7 +54,7 @@ public class NameCheckTask extends JoinManagement<Player, CommandSender, Protoco
|
||||
|
||||
public NameCheckTask(FastLoginBukkit plugin, Random random, Player player, PacketEvent packetEvent,
|
||||
String username, PublicKey publicKey) {
|
||||
super(plugin.getCore(), plugin.getCore().getAuthPluginHook());
|
||||
super(plugin.getCore(), plugin.getCore().getAuthPluginHook(), plugin.getBedrockService());
|
||||
|
||||
this.plugin = plugin;
|
||||
this.packetEvent = packetEvent;
|
||||
|
||||
@@ -124,6 +124,12 @@ public class ProtocolLibListener extends PacketAdapter {
|
||||
PacketContainer packet = packetEvent.getPacket();
|
||||
|
||||
String username = packet.getGameProfiles().read(0).getName();
|
||||
|
||||
if (packetEvent.getPacket().getMeta("original_name").isPresent()) {
|
||||
//username has been injected by ManualNameChange.java
|
||||
username = (String) packetEvent.getPacket().getMeta("original_name").get();
|
||||
}
|
||||
|
||||
plugin.getLog().trace("GameProfile {} with {} connecting", sessionKey, username);
|
||||
|
||||
packetEvent.getAsyncMarker().incrementProcessingDelay();
|
||||
|
||||
@@ -60,7 +60,7 @@ class ProtocolLibLoginSource implements LoginSource {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enableOnlinemode() throws Exception {
|
||||
public void enableOnlinemode() throws InvocationTargetException {
|
||||
verifyToken = EncryptionUtil.generateVerifyToken(random);
|
||||
|
||||
/*
|
||||
@@ -83,7 +83,7 @@ class ProtocolLibLoginSource implements LoginSource {
|
||||
|
||||
newPacket.getByteArrays().write(verifyField, verifyToken);
|
||||
|
||||
//serverId is a empty string
|
||||
//serverId is an empty string
|
||||
ProtocolLibrary.getProtocolManager().sendServerPacket(player, newPacket);
|
||||
}
|
||||
|
||||
|
||||
@@ -101,7 +101,7 @@ public class VerifyResponseTask implements Runnable {
|
||||
verifyResponse(session);
|
||||
}
|
||||
} finally {
|
||||
//this is a fake packet; it shouldn't be send to the server
|
||||
//this is a fake packet; it shouldn't be sent to the server
|
||||
synchronized (packetEvent.getAsyncMarker().getProcessingLock()) {
|
||||
packetEvent.setCancelled(true);
|
||||
}
|
||||
@@ -159,7 +159,7 @@ public class VerifyResponseTask implements Runnable {
|
||||
setPremiumUUID(session.getUuid());
|
||||
receiveFakeStartPacket(realUsername);
|
||||
} else {
|
||||
//user tried to fake a authentication
|
||||
//user tried to fake an authentication
|
||||
disconnect("invalid-session", true
|
||||
, "GameProfile {0} ({1}) tried to log in with an invalid session ServerId: {2}"
|
||||
, session.getRequestUsername(), socketAddress, serverId);
|
||||
@@ -209,7 +209,7 @@ public class VerifyResponseTask implements Runnable {
|
||||
}
|
||||
|
||||
private boolean enableEncryption(SecretKey loginKey) throws IllegalArgumentException {
|
||||
plugin.getLog().info("Enabling onlinemode encryption for {}", player.getAddress());
|
||||
plugin.getLog().info("Enabling onlinemode encryption for {}", player.getName());
|
||||
// Initialize method reflections
|
||||
if (encryptMethod == null) {
|
||||
Class<?> networkManagerClass = MinecraftReflection.getNetworkManagerClass();
|
||||
|
||||
@@ -51,7 +51,7 @@ public class ProtocolLoginSource implements LoginSource {
|
||||
|
||||
@Override
|
||||
public InetSocketAddress getAddress() {
|
||||
return loginStartEvent.getConnection().getRawAddress();
|
||||
return loginStartEvent.getAddress();
|
||||
}
|
||||
|
||||
public PlayerLoginStartEvent getLoginStartEvent() {
|
||||
|
||||
@@ -41,7 +41,6 @@ import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
|
||||
import protocolsupport.api.events.ConnectionCloseEvent;
|
||||
import protocolsupport.api.events.PlayerLoginStartEvent;
|
||||
import protocolsupport.api.events.PlayerProfileCompleteEvent;
|
||||
@@ -53,7 +52,7 @@ public class ProtocolSupportListener extends JoinManagement<Player, CommandSende
|
||||
private final RateLimiter rateLimiter;
|
||||
|
||||
public ProtocolSupportListener(FastLoginBukkit plugin, RateLimiter rateLimiter) {
|
||||
super(plugin.getCore(), plugin.getCore().getAuthPluginHook());
|
||||
super(plugin.getCore(), plugin.getCore().getAuthPluginHook(), plugin.getBedrockService());
|
||||
|
||||
this.plugin = plugin;
|
||||
this.rateLimiter = rateLimiter;
|
||||
@@ -71,7 +70,7 @@ public class ProtocolSupportListener extends JoinManagement<Player, CommandSende
|
||||
}
|
||||
|
||||
String username = loginStartEvent.getConnection().getProfile().getName();
|
||||
InetSocketAddress address = loginStartEvent.getConnection().getRawAddress();
|
||||
InetSocketAddress address = loginStartEvent.getAddress();
|
||||
|
||||
//remove old data every time on a new login in order to keep the session only for one person
|
||||
plugin.removeSession(address);
|
||||
@@ -82,14 +81,13 @@ public class ProtocolSupportListener extends JoinManagement<Player, CommandSende
|
||||
|
||||
@EventHandler
|
||||
public void onConnectionClosed(ConnectionCloseEvent closeEvent) {
|
||||
InetSocketAddress address = closeEvent.getConnection().getRawAddress();
|
||||
InetSocketAddress address = closeEvent.getConnection().getAddress();
|
||||
plugin.removeSession(address);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPropertiesResolve(PlayerProfileCompleteEvent profileCompleteEvent) {
|
||||
InetSocketAddress address = profileCompleteEvent.getConnection().getRawAddress();
|
||||
|
||||
InetSocketAddress address = profileCompleteEvent.getAddress();
|
||||
BukkitLoginSession session = plugin.getSession(address);
|
||||
|
||||
if (session != null && profileCompleteEvent.getConnection().getProfile().isOnlineMode()) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# project data for Bukkit in order to register our plugin with all it components
|
||||
# project data for Bukkit in order to register our plugin with all it's components
|
||||
# ${-} are variables from Maven (pom.xml) which will be replaced after the build
|
||||
name: ${project.parent.name}
|
||||
version: ${project.version}-${git.commit.id.abbrev}
|
||||
@@ -11,15 +11,17 @@ description: |
|
||||
website: ${project.url}
|
||||
dev-url: ${project.url}
|
||||
|
||||
# This plugin don't have to be transformed for compatibility with Minecraft >= 1.13
|
||||
# This plugin doesn't have to be transformed for compatibility with Minecraft >= 1.13
|
||||
api-version: '1.13'
|
||||
|
||||
softdepend:
|
||||
# We depend either ProtocolLib or ProtocolSupport
|
||||
# We depend on either ProtocolLib or ProtocolSupport
|
||||
- ProtocolSupport
|
||||
- ProtocolLib
|
||||
# Premium variable
|
||||
- PlaceholderAPI
|
||||
# Bedrock Player Bridge
|
||||
- Geyser-Spigot
|
||||
- floodgate
|
||||
# Auth plugins
|
||||
- AuthMe
|
||||
|
||||
@@ -36,31 +36,11 @@ import static org.hamcrest.MatcherAssert.assertThat;
|
||||
public class EncryptionUtilTest {
|
||||
|
||||
@Test
|
||||
public void testVerifyToken() throws Exception {
|
||||
public void testVerifyToken() {
|
||||
SecureRandom random = new SecureRandom();
|
||||
byte[] token = EncryptionUtil.generateVerifyToken(random);
|
||||
|
||||
assertThat(token, notNullValue());
|
||||
assertThat(token.length, is(4));
|
||||
}
|
||||
|
||||
// @Test
|
||||
// public void testDecryptSharedSecret() throws Exception {
|
||||
//
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// public void testDecryptData() throws Exception {
|
||||
//
|
||||
// }
|
||||
|
||||
// private static SecretKey createNewSharedKey() {
|
||||
// try {
|
||||
// KeyGenerator keygenerator = KeyGenerator.getInstance("AES");
|
||||
// keygenerator.init(128);
|
||||
// return keygenerator.generateKey();
|
||||
// } catch (NoSuchAlgorithmException nosuchalgorithmexception) {
|
||||
// throw new Error(nosuchalgorithmexception);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
218
bukkit/src/test/java/integration/LoginIT.java
Normal file
218
bukkit/src/test/java/integration/LoginIT.java
Normal file
@@ -0,0 +1,218 @@
|
||||
package integration;
|
||||
|
||||
import com.github.steveice10.mc.protocol.MinecraftProtocol;
|
||||
import com.github.steveice10.packetlib.Session;
|
||||
import com.github.steveice10.packetlib.event.session.DisconnectedEvent;
|
||||
import com.github.steveice10.packetlib.event.session.SessionAdapter;
|
||||
import com.github.steveice10.packetlib.packet.Packet;
|
||||
import com.github.steveice10.packetlib.tcp.TcpClientSession;
|
||||
import com.google.common.io.CharStreams;
|
||||
import com.mojang.authlib.EnvironmentParser;
|
||||
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.mockserver.client.MockServerClient;
|
||||
import org.mockserver.model.HttpRequest;
|
||||
import org.mockserver.verify.VerificationTimes;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.testcontainers.containers.GenericContainer;
|
||||
import org.testcontainers.containers.MockServerContainer;
|
||||
import org.testcontainers.containers.output.Slf4jLogConsumer;
|
||||
import org.testcontainers.containers.wait.strategy.Wait;
|
||||
import org.testcontainers.utility.DockerImageName;
|
||||
import org.testcontainers.utility.MountableFile;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.mockserver.model.HttpRequest.request;
|
||||
import static org.mockserver.model.HttpResponse.response;
|
||||
|
||||
// Warning name is sensitive to the surefire plugin
|
||||
public class LoginIT {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(LoginIT.class);
|
||||
|
||||
private static final String API_TAG = "mockserver-5.11.2";
|
||||
private static final String API_IMAGE_NAME = "mockserver/mockserver";
|
||||
private static final String API_IMAGE = API_IMAGE_NAME + ':' + API_TAG;
|
||||
|
||||
@Rule
|
||||
public MockServerContainer mockServer = new MockServerContainer(DockerImageName.parse(API_IMAGE))
|
||||
.withReuse(true);
|
||||
|
||||
private static final String SERVER_TAG = "1.18.1@sha256:dd3c8d212de585ec73113a0c0c73ac755ec1ff53e65bb09089061584fac02053";
|
||||
private static final String SERVER_IMAGE_NAME = "ghcr.io/games647/paperclip";
|
||||
private static final String SERVER_IMAGE = SERVER_IMAGE_NAME + ':' + SERVER_TAG;
|
||||
|
||||
private static final String HOME_FOLDER = "/home/nonroot/";
|
||||
|
||||
private static final long MINECRAFT_MAX_MEMORY = 1024 * 1024 * 1024L;
|
||||
|
||||
@Rule
|
||||
public GenericContainer<?> minecraftServer = new GenericContainer<>(DockerImageName.parse(SERVER_IMAGE))
|
||||
.withEnv("JDK_JAVA_OPTIONS", buildJVMFlags())
|
||||
.withExposedPorts(25565)
|
||||
// use server settings that use minimal minecraft log to quickly ramp up the server
|
||||
.withCopyFileToContainer(MountableFile.forClasspathResource("server.properties"), HOME_FOLDER + "server.properties")
|
||||
.withCopyFileToContainer(MountableFile.forClasspathResource("bukkit.yml"), HOME_FOLDER + "bukkit.yml")
|
||||
.withCopyFileToContainer(MountableFile.forClasspathResource("spigot.yml"), HOME_FOLDER + "spigot.yml")
|
||||
// create folders that are volatile
|
||||
.withTmpFs(getTempFS())
|
||||
// Done (XXXXs)! For help, type "help"
|
||||
.waitingFor(
|
||||
Wait.forLogMessage(".*For help, type \"help\"*\\n", 1)
|
||||
)
|
||||
.withReuse(true)
|
||||
.withLogConsumer(new Slf4jLogConsumer(LOG))
|
||||
.withCreateContainerCmdModifier(cmd -> cmd.getHostConfig().withMemory(MINECRAFT_MAX_MEMORY));
|
||||
|
||||
private Map<String, String> getTempFS() {
|
||||
Map<String, String> tmpfs = new HashMap<>();
|
||||
tmpfs.put(HOME_FOLDER + "world", "rw,noexec,nosuid,nodev");
|
||||
tmpfs.put(HOME_FOLDER + "logs", "rw,noexec,nosuid,nodev");
|
||||
return tmpfs;
|
||||
}
|
||||
|
||||
private String buildJVMFlags() {
|
||||
Map<String, String> systemProperties = new HashMap<>();
|
||||
systemProperties.put("com.mojang.eula.agree", Boolean.toString(true));
|
||||
|
||||
// set the Yggdrasil hosts that will also be used by the vanilla server
|
||||
systemProperties.put(EnvironmentParser.PROP_ACCOUNT_HOST, getProxyHost());
|
||||
systemProperties.put(EnvironmentParser.PROP_SESSION_HOST, getProxyHost());
|
||||
|
||||
return systemProperties.entrySet().stream()
|
||||
.map(entry -> "-D" + entry.getKey() + '=' + entry.getValue())
|
||||
.collect(Collectors.joining(" ")) + " -client";
|
||||
}
|
||||
|
||||
@Test
|
||||
public void checkRunning() throws Exception {
|
||||
assertThat(minecraftServer.isRunning(), is(true));
|
||||
|
||||
String host = minecraftServer.getHost();
|
||||
int port = minecraftServer.getMappedPort(25565);
|
||||
Session clientSession = new TcpClientSession(host, port, new MinecraftProtocol());
|
||||
try {
|
||||
CompletableFuture<Boolean> connectionResult = new CompletableFuture<>();
|
||||
clientSession.addListener(new SessionAdapter() {
|
||||
@Override
|
||||
public void packetReceived(Session session, Packet packet) {
|
||||
LOG.info("Client received: {}", packet.getClass());
|
||||
connectionResult.complete(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnected(DisconnectedEvent event) {
|
||||
connectionResult.complete(false);
|
||||
}
|
||||
});
|
||||
|
||||
clientSession.connect();
|
||||
assertThat(connectionResult.get(2, TimeUnit.SECONDS), is(true));
|
||||
} finally {
|
||||
clientSession.disconnect("Status test complete.");
|
||||
}
|
||||
}
|
||||
|
||||
private String getProxyHost() {
|
||||
return String.format("https://%s:%d", mockServer.getHost(), mockServer.getServerPort());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void autoRegisterNewUser() throws Exception {
|
||||
assertThat(mockServer.isRunning(), is(true));
|
||||
|
||||
try (MockServerClient client = new MockServerClient(mockServer.getHost(), mockServer.getServerPort())) {
|
||||
HttpRequest profileReq = request("/users/profiles/minecraft/" + "username");
|
||||
HttpRequest hasJoinedReq = request()
|
||||
.withPath("/session/minecraft/hasJoined")
|
||||
.withQueryStringParameter("username", "")
|
||||
.withQueryStringParameter("serverId", "")
|
||||
.withQueryStringParameter("ip", "");
|
||||
|
||||
// check call network request times
|
||||
client.verify(profileReq, VerificationTimes.once());
|
||||
client.verify(hasJoinedReq, VerificationTimes.once());
|
||||
|
||||
// Verify order
|
||||
client.verify(profileReq, hasJoinedReq);
|
||||
|
||||
client
|
||||
.when(request()
|
||||
.withPath("/users/profiles/minecraft/" + "username"))
|
||||
.respond(response()
|
||||
.withBody("bla"));
|
||||
|
||||
client
|
||||
.when(hasJoinedReq)
|
||||
.respond(response()
|
||||
.withBody("Test"));
|
||||
|
||||
URLConnection urlConnection = new URL(mockServer.getEndpoint() + "/users/profiles/minecraft/username").openConnection();
|
||||
String out = CharStreams.toString(new InputStreamReader(urlConnection.getInputStream(), StandardCharsets.UTF_8));
|
||||
LOG.info("OUTPUT: {}", out);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void failedJoinedVerification() {
|
||||
// has joined fails
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void offlineLoginNewUserDisabledRegister() {
|
||||
// auto register disabled, always offline login for new users
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void offlineLoginNewUser() {
|
||||
// auto register enabled, but no paid account
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void autoLoginRegistered() {
|
||||
// registered premium user and paid account login in
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void failedLoginPremiumRegistered() {
|
||||
// registered premium, but tried offline login
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void offlineLoginRegistered() {
|
||||
// assume registered user marked as offline - tried to login
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void alreadyOnlineDuplicateOwner() {
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void alreadyOnlineDuplicateCracked() {
|
||||
|
||||
}
|
||||
}
|
||||
29
bukkit/src/test/resources/bukkit.yml
Normal file
29
bukkit/src/test/resources/bukkit.yml
Normal file
@@ -0,0 +1,29 @@
|
||||
settings:
|
||||
allow-end: false
|
||||
warn-on-overload: true
|
||||
permissions-file: permissions.yml
|
||||
update-folder: update
|
||||
plugin-profiling: false
|
||||
connection-throttle: 4000
|
||||
query-plugins: false
|
||||
deprecated-verbose: default
|
||||
shutdown-message: Server closed
|
||||
minimum-api: none
|
||||
spawn-limits:
|
||||
monsters: 70
|
||||
animals: 10
|
||||
water-animals: 5
|
||||
water-ambient: 20
|
||||
water-underground-creature: 5
|
||||
ambient: 15
|
||||
chunk-gc:
|
||||
period-in-ticks: 600
|
||||
ticks-per:
|
||||
animal-spawns: 400
|
||||
monster-spawns: 1
|
||||
water-spawns: 1
|
||||
water-ambient-spawns: 1
|
||||
water-underground-creature-spawns: 1
|
||||
ambient-spawns: 1
|
||||
autosave: 6000
|
||||
aliases: now-in-commands.yml
|
||||
14
bukkit/src/test/resources/logback-test.xml
Normal file
14
bukkit/src/test/resources/logback-test.xml
Normal file
@@ -0,0 +1,14 @@
|
||||
<configuration>
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<root level="info">
|
||||
<appender-ref ref="STDOUT"/>
|
||||
</root>
|
||||
|
||||
<logger name="org.testcontainers" level="INFO"/>
|
||||
<logger name="com.github.dockerjava" level="WARN"/>
|
||||
</configuration>
|
||||
292
bukkit/src/test/resources/paper.yml
Normal file
292
bukkit/src/test/resources/paper.yml
Normal file
@@ -0,0 +1,292 @@
|
||||
# This is the main configuration file for Paper.
|
||||
# As you can see, there's tons to configure. Some options may impact gameplay, so use
|
||||
# with caution, and make sure you know what each option does before configuring.
|
||||
#
|
||||
# If you need help with the configuration or have any questions related to Paper,
|
||||
# join us in our Discord or IRC channel.
|
||||
#
|
||||
# Discord: https://discord.gg/papermc
|
||||
# IRC: #paper @ irc.esper.net ( https://webchat.esper.net/?channels=paper )
|
||||
# Website: https://papermc.io/
|
||||
# Docs: https://paper.readthedocs.org/
|
||||
|
||||
verbose: false
|
||||
messages:
|
||||
kick:
|
||||
authentication-servers-down: ''
|
||||
connection-throttle: Connection throttled! Please wait before reconnecting.
|
||||
flying-player: Flying is not enabled on this server
|
||||
flying-vehicle: Flying is not enabled on this server
|
||||
no-permission: '&cI''m sorry, but you do not have permission to perform this command.
|
||||
Please contact the server administrators if you believe that this is in error.'
|
||||
timings:
|
||||
enabled: false
|
||||
verbose: true
|
||||
url: https://timings.aikar.co/
|
||||
server-name-privacy: false
|
||||
hidden-config-entries: []
|
||||
history-interval: 300
|
||||
history-length: 3600
|
||||
server-name: Unknown Server
|
||||
config-version: 24
|
||||
settings:
|
||||
max-joins-per-tick: 3
|
||||
track-plugin-scoreboards: false
|
||||
fix-entity-position-desync: true
|
||||
use-display-name-in-quit-message: false
|
||||
load-permissions-yml-before-plugins: true
|
||||
region-file-cache-size: 256
|
||||
enable-player-collisions: false
|
||||
save-empty-scoreboard-teams: false
|
||||
bungee-online-mode: true
|
||||
incoming-packet-spam-threshold: 300
|
||||
use-alternative-luck-formula: false
|
||||
velocity-support:
|
||||
enabled: false
|
||||
online-mode: false
|
||||
secret: ''
|
||||
console-has-all-permissions: false
|
||||
player-auto-save-rate: -1
|
||||
max-player-auto-save-per-tick: -1
|
||||
fix-target-selector-tag-completion: true
|
||||
lag-compensate-block-breaking: true
|
||||
time-command-affects-all-worlds: false
|
||||
log-player-ip-addresses: false
|
||||
console:
|
||||
enable-brigadier-highlighting: false
|
||||
enable-brigadier-completions: false
|
||||
suggest-player-names-when-null-tab-completions: false
|
||||
watchdog:
|
||||
early-warning-every: 5000
|
||||
early-warning-delay: 10000
|
||||
spam-limiter:
|
||||
tab-spam-increment: 1
|
||||
tab-spam-limit: 500
|
||||
recipe-spam-increment: 1
|
||||
recipe-spam-limit: 20
|
||||
book-size:
|
||||
page-max: 2560
|
||||
total-multiplier: 0.98
|
||||
loggers:
|
||||
deobfuscate-stacktraces: true
|
||||
item-validation:
|
||||
display-name: 8192
|
||||
loc-name: 8192
|
||||
lore-line: 8192
|
||||
book:
|
||||
title: 8192
|
||||
author: 8192
|
||||
page: 16384
|
||||
send-full-pos-for-hard-colliding-entities: true
|
||||
async-chunks:
|
||||
threads: -1
|
||||
unsupported-settings:
|
||||
allow-permanent-block-break-exploits: false
|
||||
allow-piston-duplication: false
|
||||
perform-username-validation: true
|
||||
allow-headless-pistons: false
|
||||
allow-permanent-block-break-exploits-readme: This setting controls if players
|
||||
should be able to break bedrock, end portals and other intended to be permanent
|
||||
blocks.
|
||||
allow-piston-duplication-readme: This setting controls if player should be able
|
||||
to use TNT duplication, but this also allows duplicating carpet, rails and potentially
|
||||
other items
|
||||
allow-headless-pistons-readme: This setting controls if players should be able
|
||||
to create headless pistons.
|
||||
packet-limiter:
|
||||
kick-message: '&cSent too many packets'
|
||||
limits: []
|
||||
world-settings:
|
||||
default:
|
||||
delay-chunk-unloads-by: 10s
|
||||
disable-teleportation-suffocation-check: true
|
||||
generator-settings:
|
||||
flat-bedrock: true
|
||||
piglins-guard-chests: true
|
||||
should-remove-dragon: false
|
||||
max-auto-save-chunks-per-tick: 24
|
||||
baby-zombie-movement-modifier: 0.5
|
||||
optimize-explosions: false
|
||||
use-vanilla-world-scoreboard-name-coloring: false
|
||||
game-mechanics:
|
||||
scan-for-legacy-ender-dragon: true
|
||||
fix-curing-zombie-villager-discount-exploit: true
|
||||
disable-pillager-patrols: true
|
||||
pillager-patrols:
|
||||
spawn-chance: 0.2
|
||||
spawn-delay:
|
||||
per-player: false
|
||||
ticks: 12000
|
||||
start:
|
||||
per-player: false
|
||||
day: 5
|
||||
disable-chest-cat-detection: true
|
||||
nerf-pigmen-from-nether-portals: false
|
||||
disable-player-crits: true
|
||||
disable-sprint-interruption-on-attack: true
|
||||
shield-blocking-delay: 5
|
||||
disable-end-credits: true
|
||||
disable-unloaded-chunk-enderpearl-exploit: true
|
||||
disable-relative-projectile-velocity: true
|
||||
disable-mob-spawner-spawn-egg-transformation: true
|
||||
prevent-moving-into-unloaded-chunks: false
|
||||
count-all-mobs-for-spawning: false
|
||||
spawn-limits:
|
||||
monster: -1
|
||||
creature: -1
|
||||
ambient: -1
|
||||
axolotls: -1
|
||||
underground_water_creature: -1
|
||||
water_creature: -1
|
||||
water_ambient: -1
|
||||
ender-dragons-death-always-places-dragon-egg: false
|
||||
experience-merge-max-value: -1
|
||||
allow-using-signs-inside-spawn-protection: false
|
||||
wandering-trader:
|
||||
spawn-minute-length: 1200
|
||||
spawn-day-length: 24000
|
||||
spawn-chance-failure-increment: 25
|
||||
spawn-chance-min: 25
|
||||
spawn-chance-max: 75
|
||||
door-breaking-difficulty:
|
||||
zombie:
|
||||
- HARD
|
||||
vindicator:
|
||||
- NORMAL
|
||||
- HARD
|
||||
max-growth-height:
|
||||
cactus: 3
|
||||
reeds: 3
|
||||
bamboo:
|
||||
max: 16
|
||||
min: 11
|
||||
fishing-time-range:
|
||||
MinimumTicks: 100
|
||||
MaximumTicks: 600
|
||||
despawn-ranges: []
|
||||
falling-block-height-nerf: 0
|
||||
tnt-entity-height-nerf: 0
|
||||
slime-spawn-height:
|
||||
swamp-biome:
|
||||
maximum: 70.0
|
||||
minimum: 50.0
|
||||
slime-chunk:
|
||||
maximum: 40.0
|
||||
frosted-ice:
|
||||
enabled: true
|
||||
delay:
|
||||
min: 20
|
||||
max: 40
|
||||
lootables:
|
||||
auto-replenish: false
|
||||
restrict-player-reloot: true
|
||||
reset-seed-on-fill: true
|
||||
max-refills: -1
|
||||
refresh-min: 12h
|
||||
refresh-max: 2d
|
||||
filter-nbt-data-from-spawn-eggs-and-related: true
|
||||
max-entity-collisions: 8
|
||||
disable-creeper-lingering-effect: true
|
||||
duplicate-uuid-resolver: saferegen
|
||||
duplicate-uuid-saferegen-delete-range: 32
|
||||
hopper:
|
||||
cooldown-when-full: true
|
||||
disable-move-event: true
|
||||
ignore-occluding-blocks: false
|
||||
mob-effects:
|
||||
undead-immune-to-certain-effects: true
|
||||
spiders-immune-to-poison-effect: true
|
||||
immune-to-wither-effect:
|
||||
wither: true
|
||||
wither-skeleton: true
|
||||
update-pathfinding-on-block-update: true
|
||||
phantoms-do-not-spawn-on-creative-players: true
|
||||
phantoms-only-attack-insomniacs: true
|
||||
mobs-can-always-pick-up-loot:
|
||||
zombies: false
|
||||
skeletons: false
|
||||
map-item-frame-cursor-update-interval: 10
|
||||
allow-player-cramming-damage: false
|
||||
anticheat:
|
||||
obfuscation:
|
||||
items:
|
||||
hide-itemmeta: false
|
||||
hide-durability: false
|
||||
monster-spawn-max-light-level: -1
|
||||
water-over-lava-flow-speed: 5
|
||||
grass-spread-tick-rate: 1
|
||||
use-faster-eigencraft-redstone: false
|
||||
nether-ceiling-void-damage-height: 0
|
||||
only-players-collide: false
|
||||
allow-vehicle-collisions: true
|
||||
allow-non-player-entities-on-scoreboards: false
|
||||
anti-xray:
|
||||
enabled: false
|
||||
engine-mode: 1
|
||||
max-block-height: 64
|
||||
update-radius: 2
|
||||
lava-obscures: false
|
||||
use-permission: false
|
||||
hidden-blocks: []
|
||||
replacement-blocks: []
|
||||
keep-spawn-loaded: true
|
||||
armor-stands-do-collision-entity-lookups: true
|
||||
parrots-are-unaffected-by-player-movement: false
|
||||
disable-explosion-knockback: true
|
||||
portal-search-radius: 128
|
||||
portal-create-radius: 16
|
||||
portal-search-vanilla-dimension-scaling: true
|
||||
fix-items-merging-through-walls: false
|
||||
disable-thunder: true
|
||||
skeleton-horse-thunder-spawn-chance: 0.01
|
||||
disable-ice-and-snow: true
|
||||
keep-spawn-loaded-range: 10
|
||||
fix-climbing-bypassing-cramming-rule: false
|
||||
container-update-tick-rate: 1
|
||||
fixed-chunk-inhabited-time: -1
|
||||
remove-corrupt-tile-entities: false
|
||||
prevent-tnt-from-moving-in-water: false
|
||||
iron-golems-can-spawn-in-air: false
|
||||
max-leash-distance: 10.0
|
||||
show-sign-click-command-failure-msgs-to-player: false
|
||||
armor-stands-tick: true
|
||||
non-player-arrow-despawn-rate: -1
|
||||
creative-arrow-despawn-rate: -1
|
||||
spawner-nerfed-mobs-should-jump: false
|
||||
entities-target-with-follow-range: false
|
||||
wateranimal-spawn-height:
|
||||
maximum: default
|
||||
minimum: default
|
||||
zombies-target-turtle-eggs: true
|
||||
zombie-villager-infection-chance: -1.0
|
||||
unsupported-settings:
|
||||
fix-invulnerable-end-crystal-exploit: true
|
||||
all-chunks-are-slime-chunks: false
|
||||
mob-spawner-tick-rate: 1
|
||||
map-item-frame-cursor-limit: 128
|
||||
per-player-mob-spawns: true
|
||||
light-queue-size: 20
|
||||
auto-save-interval: -1
|
||||
enable-treasure-maps: false
|
||||
treasure-maps-return-already-discovered: false
|
||||
split-overstacked-loot: true
|
||||
entity-per-chunk-save-limit:
|
||||
experience_orb: -1
|
||||
snowball: -1
|
||||
ender_pearl: -1
|
||||
arrow: -1
|
||||
fireball: -1
|
||||
small_fireball: -1
|
||||
alt-item-despawn-rate:
|
||||
enabled: false
|
||||
items:
|
||||
COBBLESTONE: 300
|
||||
tick-rates:
|
||||
sensor:
|
||||
villager:
|
||||
secondarypoisensor: 40
|
||||
behavior:
|
||||
villager:
|
||||
validatenearbypoi: -1
|
||||
feature-seeds:
|
||||
generate-random-seeds-for-all: false
|
||||
50
bukkit/src/test/resources/server.properties
Normal file
50
bukkit/src/test/resources/server.properties
Normal file
@@ -0,0 +1,50 @@
|
||||
#Minecraft server properties
|
||||
enable-jmx-monitoring=false
|
||||
rcon.port=25575
|
||||
gamemode=creative
|
||||
enable-command-block=false
|
||||
enable-query=false
|
||||
level-name=world
|
||||
motd=Debug server
|
||||
query.port=25565
|
||||
pvp=false
|
||||
difficulty=peaceful
|
||||
network-compression-threshold=256
|
||||
require-resource-pack=false
|
||||
max-tick-time=60000
|
||||
use-native-transport=true
|
||||
max-players=2
|
||||
online-mode=false
|
||||
enable-status=true
|
||||
allow-flight=false
|
||||
broadcast-rcon-to-ops=true
|
||||
view-distance=3
|
||||
server-ip=
|
||||
resource-pack-prompt=
|
||||
allow-nether=false
|
||||
server-port=25565
|
||||
enable-rcon=false
|
||||
sync-chunk-writes=false
|
||||
op-permission-level=4
|
||||
prevent-proxy-connections=false
|
||||
hide-online-players=true
|
||||
resource-pack=
|
||||
entity-broadcast-range-percentage=10
|
||||
simulation-distance=3
|
||||
rcon.password=
|
||||
player-idle-timeout=0
|
||||
debug=true
|
||||
force-gamemode=false
|
||||
rate-limit=0
|
||||
hardcore=false
|
||||
white-list=false
|
||||
broadcast-console-to-ops=false
|
||||
spawn-npcs=false
|
||||
spawn-animals=false
|
||||
function-permission-level=2
|
||||
text-filtering-config=
|
||||
spawn-monsters=false
|
||||
enforce-whitelist=false
|
||||
resource-pack-sha1=
|
||||
spawn-protection=0
|
||||
max-world-size=1
|
||||
164
bukkit/src/test/resources/spigot.yml
Normal file
164
bukkit/src/test/resources/spigot.yml
Normal file
@@ -0,0 +1,164 @@
|
||||
# This is the main configuration file for Spigot.
|
||||
# As you can see, there's tons to configure. Some options may impact gameplay, so use
|
||||
# with caution, and make sure you know what each option does before configuring.
|
||||
# For a reference for any variable inside this file, check out the Spigot wiki at
|
||||
# http://www.spigotmc.org/wiki/spigot-configuration/
|
||||
#
|
||||
# If you need help with the configuration or have any questions related to Spigot,
|
||||
# join us at the Discord or drop by our forums and leave a post.
|
||||
#
|
||||
# Discord: https://www.spigotmc.org/go/discord
|
||||
# Forums: http://www.spigotmc.org/
|
||||
|
||||
settings:
|
||||
debug: true
|
||||
bungeecord: false
|
||||
sample-count: 0
|
||||
player-shuffle: 0
|
||||
user-cache-size: 1000
|
||||
save-user-cache-on-stop-only: false
|
||||
moved-wrongly-threshold: 0.0625
|
||||
moved-too-quickly-multiplier: 10.0
|
||||
timeout-time: 60
|
||||
restart-on-crash: false
|
||||
restart-script: ./start.sh
|
||||
netty-threads: 1
|
||||
attribute:
|
||||
maxHealth:
|
||||
max: 2048.0
|
||||
movementSpeed:
|
||||
max: 2048.0
|
||||
attackDamage:
|
||||
max: 2048.0
|
||||
log-villager-deaths: false
|
||||
log-named-deaths: false
|
||||
messages:
|
||||
whitelist: You are not whitelisted on this server!
|
||||
unknown-command: Unknown command. Type "/help" for help.
|
||||
server-full: The server is full!
|
||||
outdated-client: Outdated client! Please use {0}
|
||||
outdated-server: Outdated server! I'm still on {0}
|
||||
restart: Server is restarting
|
||||
advancements:
|
||||
disable-saving: true
|
||||
disabled: []
|
||||
commands:
|
||||
replace-commands: []
|
||||
spam-exclusions: []
|
||||
silent-commandblock-console: false
|
||||
log: false
|
||||
tab-complete: 0
|
||||
send-namespaced: false
|
||||
players:
|
||||
disable-saving: true
|
||||
world-settings:
|
||||
default:
|
||||
below-zero-generation-in-existing-chunks: true
|
||||
verbose: false
|
||||
merge-radius:
|
||||
exp: 3.0
|
||||
item: 2.5
|
||||
growth:
|
||||
cactus-modifier: 100
|
||||
cane-modifier: 100
|
||||
melon-modifier: 100
|
||||
mushroom-modifier: 100
|
||||
pumpkin-modifier: 100
|
||||
sapling-modifier: 100
|
||||
beetroot-modifier: 100
|
||||
carrot-modifier: 100
|
||||
potato-modifier: 100
|
||||
wheat-modifier: 100
|
||||
netherwart-modifier: 100
|
||||
vine-modifier: 100
|
||||
cocoa-modifier: 100
|
||||
bamboo-modifier: 100
|
||||
sweetberry-modifier: 100
|
||||
kelp-modifier: 100
|
||||
twistingvines-modifier: 100
|
||||
weepingvines-modifier: 100
|
||||
cavevines-modifier: 100
|
||||
glowberry-modifier: 100
|
||||
entity-activation-range:
|
||||
animals: 32
|
||||
monsters: 32
|
||||
raiders: 48
|
||||
misc: 16
|
||||
water: 16
|
||||
villagers: 32
|
||||
flying-monsters: 32
|
||||
wake-up-inactive:
|
||||
animals-max-per-tick: 4
|
||||
animals-every: 1200
|
||||
animals-for: 100
|
||||
monsters-max-per-tick: 8
|
||||
monsters-every: 400
|
||||
monsters-for: 100
|
||||
villagers-max-per-tick: 4
|
||||
villagers-every: 600
|
||||
villagers-for: 100
|
||||
flying-monsters-max-per-tick: 8
|
||||
flying-monsters-every: 200
|
||||
flying-monsters-for: 100
|
||||
villagers-work-immunity-after: 100
|
||||
villagers-work-immunity-for: 20
|
||||
villagers-active-for-panic: true
|
||||
tick-inactive-villagers: true
|
||||
ignore-spectators: false
|
||||
entity-tracking-range:
|
||||
players: 48
|
||||
animals: 48
|
||||
monsters: 48
|
||||
misc: 32
|
||||
other: 64
|
||||
ticks-per:
|
||||
hopper-transfer: 8
|
||||
hopper-check: 1
|
||||
hopper-amount: 1
|
||||
dragon-death-sound-radius: 0
|
||||
seed-village: 10387312
|
||||
seed-desert: 14357617
|
||||
seed-igloo: 14357618
|
||||
seed-jungle: 14357619
|
||||
seed-swamp: 14357620
|
||||
seed-monument: 10387313
|
||||
seed-shipwreck: 165745295
|
||||
seed-ocean: 14357621
|
||||
seed-outpost: 165745296
|
||||
seed-endcity: 10387313
|
||||
seed-slime: 987234911
|
||||
seed-bastion: 30084232
|
||||
seed-fortress: 30084232
|
||||
seed-mansion: 10387319
|
||||
seed-fossil: 14357921
|
||||
seed-portal: 34222645
|
||||
seed-stronghold: default
|
||||
hunger:
|
||||
jump-walk-exhaustion: 0.05
|
||||
jump-sprint-exhaustion: 0.2
|
||||
combat-exhaustion: 0.1
|
||||
regen-exhaustion: 6.0
|
||||
swim-multiplier: 0.01
|
||||
sprint-multiplier: 0.1
|
||||
other-multiplier: 0.0
|
||||
max-tnt-per-tick: 100
|
||||
max-tick-time:
|
||||
tile: 50
|
||||
entity: 50
|
||||
enable-zombie-pigmen-portal-spawns: true
|
||||
item-despawn-rate: 6000
|
||||
view-distance: default
|
||||
simulation-distance: default
|
||||
thunder-chance: 100000
|
||||
wither-spawn-sound-radius: 0
|
||||
arrow-despawn-rate: 1200
|
||||
trident-despawn-rate: 1200
|
||||
hanging-tick-frequency: 100
|
||||
zombie-aggressive-towards-villager: true
|
||||
nerf-spawner-mobs: false
|
||||
mob-spawn-range: 8
|
||||
end-portal-sound-radius: 0
|
||||
config-version: 12
|
||||
stats:
|
||||
disable-saving: true
|
||||
forced-stats: {}
|
||||
@@ -36,7 +36,7 @@
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<!--This have to be in lowercase because it's used by plugin.yml-->
|
||||
<!--This has to be in lowercase because it's used by plugin.yml-->
|
||||
<artifactId>fastlogin.bungee</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
@@ -57,6 +57,7 @@
|
||||
<!--Those classes are already present in BungeeCord version-->
|
||||
<exclude>net.md-5:bungeecord-config</exclude>
|
||||
<exclude>com.google.code.gson:gson</exclude>
|
||||
<exclude>com.google.guava:guava</exclude>
|
||||
</excludes>
|
||||
</artifactSet>
|
||||
<relocations>
|
||||
@@ -69,6 +70,11 @@
|
||||
<shadedPattern>fastlogin.slf4j</shadedPattern>
|
||||
</relocation>
|
||||
</relocations>
|
||||
<!-- Rename the service file too to let SLF4J api find our own relocated jdk logger -->
|
||||
<!-- Located in META-INF/services -->
|
||||
<transformers>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
|
||||
</transformers>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
@@ -88,11 +94,6 @@
|
||||
<url>https://repo.codemc.io/repository/maven-public/</url>
|
||||
</repository>
|
||||
|
||||
<repository>
|
||||
<id>nukkitx-repo</id>
|
||||
<url>https://repo.nukkitx.com/maven-snapshots/</url>
|
||||
</repository>
|
||||
|
||||
<repository>
|
||||
<id>spigotplugins-repo</id>
|
||||
<url>https://maven.gamestrike.de/mvn/</url>
|
||||
@@ -119,25 +120,54 @@
|
||||
<dependency>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-proxy</artifactId>
|
||||
<version>1.16-R0.5-SNAPSHOT</version>
|
||||
<version>1.18-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
<!-- Use our own newer api version -->
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- Bedrock player bridge -->
|
||||
<!-- Should be removed one Floodgate 2.0 gets a stable release -->
|
||||
<dependency>
|
||||
<groupId>org.geysermc</groupId>
|
||||
<artifactId>floodgate-bungee</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Bedrock player bridge -->
|
||||
<!-- Version 2.0 -->
|
||||
<!--Floodgate for Xbox Live Authentication-->
|
||||
<dependency>
|
||||
<groupId>org.geysermc.floodgate</groupId>
|
||||
<artifactId>api</artifactId>
|
||||
<version>2.0-SNAPSHOT</version>
|
||||
<version>${floodgate.version}</version>
|
||||
<scope>provided</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.geysermc.cumulus</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- Bedrock player bridge -->
|
||||
<dependency>
|
||||
<groupId>org.geysermc</groupId>
|
||||
<artifactId>core</artifactId>
|
||||
<version>${geyser.version}</version>
|
||||
<scope>provided</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>*</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- We need the API, but it was excluded above -->
|
||||
<dependency>
|
||||
<groupId>org.geysermc</groupId>
|
||||
<artifactId>geyser-api</artifactId>
|
||||
<version>${geyser.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
@@ -33,6 +33,9 @@ import com.github.games647.fastlogin.bungee.listener.PluginMessageListener;
|
||||
import com.github.games647.fastlogin.core.AsyncScheduler;
|
||||
import com.github.games647.fastlogin.core.CommonUtil;
|
||||
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
|
||||
import com.github.games647.fastlogin.core.hooks.bedrock.BedrockService;
|
||||
import com.github.games647.fastlogin.core.hooks.bedrock.FloodgateService;
|
||||
import com.github.games647.fastlogin.core.hooks.bedrock.GeyserService;
|
||||
import com.github.games647.fastlogin.core.message.ChangePremiumMessage;
|
||||
import com.github.games647.fastlogin.core.message.ChannelMessage;
|
||||
import com.github.games647.fastlogin.core.message.NamespaceKey;
|
||||
@@ -59,6 +62,8 @@ import net.md_5.bungee.api.plugin.Plugin;
|
||||
import net.md_5.bungee.api.plugin.PluginManager;
|
||||
import net.md_5.bungee.api.scheduler.GroupedThreadFactory;
|
||||
|
||||
import org.geysermc.floodgate.api.FloodgateApi;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
/**
|
||||
@@ -70,11 +75,13 @@ public class FastLoginBungee extends Plugin implements PlatformPlugin<CommandSen
|
||||
|
||||
private FastLoginCore<ProxiedPlayer, CommandSender, FastLoginBungee> core;
|
||||
private AsyncScheduler scheduler;
|
||||
private FloodgateService floodgateService;
|
||||
private GeyserService geyserService;
|
||||
private Logger logger;
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
logger = CommonUtil.createLoggerFromJDK(getLogger());
|
||||
logger = CommonUtil.initializeLoggerService(getLogger());
|
||||
scheduler = new AsyncScheduler(logger, getThreadFactory());
|
||||
|
||||
core = new FastLoginCore<>(this);
|
||||
@@ -83,11 +90,18 @@ public class FastLoginBungee extends Plugin implements PlatformPlugin<CommandSen
|
||||
return;
|
||||
}
|
||||
|
||||
if (isPluginInstalled("floodgate")) {
|
||||
floodgateService = new FloodgateService(FloodgateApi.getInstance(), core);
|
||||
}
|
||||
|
||||
if (isPluginInstalled("Geyser-BungeeCord")) {
|
||||
geyserService = new GeyserService(GeyserImpl.getInstance(), core);
|
||||
}
|
||||
|
||||
//events
|
||||
PluginManager pluginManager = getProxy().getPluginManager();
|
||||
|
||||
ConnectListener connectListener = new ConnectListener(this, core.getRateLimiter());
|
||||
|
||||
pluginManager.registerListener(this, connectListener);
|
||||
pluginManager.registerListener(this, new PluginMessageListener(this));
|
||||
|
||||
@@ -185,4 +199,20 @@ public class FastLoginBungee extends Plugin implements PlatformPlugin<CommandSen
|
||||
public boolean isPluginInstalled(String name) {
|
||||
return getProxy().getPluginManager().getPlugin(name) != null;
|
||||
}
|
||||
|
||||
public FloodgateService getFloodgateService() {
|
||||
return floodgateService;
|
||||
}
|
||||
|
||||
public GeyserService getGeyserService() {
|
||||
return geyserService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BedrockService<?> getBedrockService() {
|
||||
if (floodgateService != null) {
|
||||
return floodgateService;
|
||||
}
|
||||
return geyserService;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ package com.github.games647.fastlogin.bungee.event;
|
||||
|
||||
import com.github.games647.fastlogin.core.StoredProfile;
|
||||
import com.github.games647.fastlogin.core.shared.event.FastLoginPremiumToggleEvent;
|
||||
|
||||
import net.md_5.bungee.api.plugin.Event;
|
||||
|
||||
public class BungeeFastLoginPremiumToggleEvent extends Event implements FastLoginPremiumToggleEvent {
|
||||
@@ -45,7 +46,7 @@ public class BungeeFastLoginPremiumToggleEvent extends Event implements FastLogi
|
||||
}
|
||||
|
||||
@Override
|
||||
public FastLoginPremiumToggleEvent.PremiumToggleReason getReason() {
|
||||
public PremiumToggleReason getReason() {
|
||||
return reason;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,22 +25,23 @@
|
||||
*/
|
||||
package com.github.games647.fastlogin.bungee.hook;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
import com.github.games647.fastlogin.bungee.FastLoginBungee;
|
||||
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
|
||||
|
||||
import de.xxschrandxx.bca.bungee.BungeeCordAuthenticatorBungee;
|
||||
import de.xxschrandxx.bca.bungee.api.BungeeCordAuthenticatorBungeeAPI;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
|
||||
/**
|
||||
* GitHub:
|
||||
* https://github.com/xXSchrandXx/SpigotPlugins/tree/master/BungeeCordAuthenticator
|
||||
*
|
||||
* <p>
|
||||
* Project page:
|
||||
*
|
||||
* <p>
|
||||
* Spigot: https://www.spigotmc.org/resources/bungeecordauthenticator.87669/
|
||||
*/
|
||||
public class BungeeCordAuthenticatorBungeeHook implements AuthPlugin<ProxiedPlayer> {
|
||||
@@ -49,7 +50,7 @@ public class BungeeCordAuthenticatorBungeeHook implements AuthPlugin<ProxiedPlay
|
||||
|
||||
public BungeeCordAuthenticatorBungeeHook(FastLoginBungee plugin) {
|
||||
api = ((BungeeCordAuthenticatorBungee) plugin.getProxy().getPluginManager()
|
||||
.getPlugin("BungeeCordAuthenticatorBungee")).getAPI();
|
||||
.getPlugin("BungeeCordAuthenticatorBungee")).getAPI();
|
||||
plugin.getLog().info("BungeeCordAuthenticatorHook | Hooked successful!");
|
||||
}
|
||||
|
||||
@@ -57,25 +58,24 @@ public class BungeeCordAuthenticatorBungeeHook implements AuthPlugin<ProxiedPlay
|
||||
public boolean forceLogin(ProxiedPlayer player) {
|
||||
if (api.isAuthenticated(player)) {
|
||||
return true;
|
||||
} else {
|
||||
try {
|
||||
api.setAuthenticated(player);
|
||||
}
|
||||
catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
api.setAuthenticated(player);
|
||||
} catch (SQLException sqlEx) {
|
||||
api.getLogger().log(Level.WARNING, "Failed to force login", sqlEx);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRegistered(String playerName) {
|
||||
try {
|
||||
return api.getSQL().checkPlayerEntry(playerName);
|
||||
}
|
||||
catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
} catch (SQLException sqlEx) {
|
||||
api.getLogger().log(Level.WARNING, "Failed to check registration", sqlEx);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -84,9 +84,8 @@ public class BungeeCordAuthenticatorBungeeHook implements AuthPlugin<ProxiedPlay
|
||||
public boolean forceRegister(ProxiedPlayer player, String password) {
|
||||
try {
|
||||
return api.createPlayerEntry(player, password);
|
||||
}
|
||||
catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
} catch (SQLException sqlEx) {
|
||||
api.getLogger().log(Level.WARNING, "Failed to force register", sqlEx);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ import com.github.games647.fastlogin.bungee.task.FloodgateAuthTask;
|
||||
import com.github.games647.fastlogin.bungee.task.ForceLoginTask;
|
||||
import com.github.games647.fastlogin.core.RateLimiter;
|
||||
import com.github.games647.fastlogin.core.StoredProfile;
|
||||
import com.github.games647.fastlogin.core.hooks.bedrock.FloodgateService;
|
||||
import com.github.games647.fastlogin.core.shared.LoginSession;
|
||||
import com.google.common.base.Throwables;
|
||||
|
||||
@@ -56,7 +57,6 @@ import net.md_5.bungee.connection.LoginResult.Property;
|
||||
import net.md_5.bungee.event.EventHandler;
|
||||
import net.md_5.bungee.event.EventPriority;
|
||||
|
||||
import org.geysermc.floodgate.api.FloodgateApi;
|
||||
import org.geysermc.floodgate.api.player.FloodgatePlayer;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -80,14 +80,12 @@ public class ConnectListener implements Listener {
|
||||
Field uuidField = InitialHandler.class.getDeclaredField(UUID_FIELD_NAME);
|
||||
uuidField.setAccessible(true);
|
||||
setHandle = lookup.unreflectSetter(uuidField);
|
||||
} catch (ClassNotFoundException classNotFoundException) {
|
||||
} catch (ReflectiveOperationException reflectiveOperationException) {
|
||||
Logger logger = LoggerFactory.getLogger(ConnectListener.class);
|
||||
logger.error(
|
||||
"Cannot find Bungee initial handler; Disabling premium UUID and skin won't work.",
|
||||
classNotFoundException
|
||||
reflectiveOperationException
|
||||
);
|
||||
} catch (ReflectiveOperationException reflectiveOperationException) {
|
||||
reflectiveOperationException.printStackTrace();
|
||||
}
|
||||
|
||||
uniqueIdSetter = setHandle;
|
||||
@@ -161,8 +159,8 @@ public class ConnectListener implements Listener {
|
||||
|
||||
private void setOfflineId(InitialHandler connection, String username) {
|
||||
try {
|
||||
final UUID oldPremiumId = connection.getUniqueId();
|
||||
final UUID offlineUUID = UUIDAdapter.generateOfflineId(username);
|
||||
UUID oldPremiumId = connection.getUniqueId();
|
||||
UUID offlineUUID = UUIDAdapter.generateOfflineId(username);
|
||||
|
||||
// BungeeCord only allows setting the UUID in PreLogin events and before requesting online mode
|
||||
// However if online mode is requested, it will override previous values
|
||||
@@ -174,7 +172,7 @@ public class ConnectListener implements Listener {
|
||||
} catch (Exception ex) {
|
||||
plugin.getLog().error("Failed to set offline uuid of {}", username, ex);
|
||||
} catch (Throwable throwable) {
|
||||
// throw remaining exceptions like outofmemory that we shouldn't handle ourself
|
||||
// throw remaining exceptions like out of memory that we shouldn't handle ourselves
|
||||
Throwables.throwIfUnchecked(throwable);
|
||||
}
|
||||
}
|
||||
@@ -184,8 +182,9 @@ public class ConnectListener implements Listener {
|
||||
ProxiedPlayer player = serverConnectedEvent.getPlayer();
|
||||
Server server = serverConnectedEvent.getServer();
|
||||
|
||||
if (plugin.isPluginInstalled("floodgate")) {
|
||||
FloodgatePlayer floodgatePlayer = FloodgateApi.getInstance().getPlayer(player.getUniqueId());
|
||||
FloodgateService floodgateService = plugin.getFloodgateService();
|
||||
if (floodgateService != null) {
|
||||
FloodgatePlayer floodgatePlayer = floodgateService.getBedrockPlayer(player.getUniqueId());
|
||||
if (floodgatePlayer != null) {
|
||||
Runnable floodgateAuthTask = new FloodgateAuthTask(plugin.getCore(), player, floodgatePlayer, server);
|
||||
plugin.getScheduler().runAsync(floodgateAuthTask);
|
||||
|
||||
@@ -29,6 +29,7 @@ import com.github.games647.fastlogin.bungee.BungeeLoginSession;
|
||||
import com.github.games647.fastlogin.bungee.FastLoginBungee;
|
||||
import com.github.games647.fastlogin.bungee.task.AsyncToggleMessage;
|
||||
import com.github.games647.fastlogin.core.StoredProfile;
|
||||
import com.github.games647.fastlogin.core.hooks.bedrock.FloodgateService;
|
||||
import com.github.games647.fastlogin.core.message.ChangePremiumMessage;
|
||||
import com.github.games647.fastlogin.core.message.NamespaceKey;
|
||||
import com.github.games647.fastlogin.core.message.SuccessMessage;
|
||||
@@ -68,7 +69,7 @@ public class PluginMessageListener implements Listener {
|
||||
}
|
||||
|
||||
//the client shouldn't be able to read the messages in order to know something about server internal states
|
||||
//moreover the client shouldn't be able fake a running premium check by sending the result message
|
||||
//moreover the client shouldn't be able to fake a running premium check by sending the result message
|
||||
pluginMessageEvent.setCancelled(true);
|
||||
|
||||
if (!(pluginMessageEvent.getSender() instanceof Server)) {
|
||||
@@ -115,7 +116,15 @@ public class PluginMessageListener implements Listener {
|
||||
}
|
||||
|
||||
private void onSuccessMessage(ProxiedPlayer forPlayer) {
|
||||
if (forPlayer.getPendingConnection().isOnlineMode()) {
|
||||
boolean shouldPersist = forPlayer.getPendingConnection().isOnlineMode();
|
||||
|
||||
FloodgateService floodgateService = plugin.getFloodgateService();
|
||||
if (!shouldPersist && floodgateService != null) {
|
||||
// always save floodgate players to lock this username
|
||||
shouldPersist = floodgateService.isBedrockPlayer(forPlayer.getUniqueId());
|
||||
}
|
||||
|
||||
if (shouldPersist) {
|
||||
//bukkit module successfully received and force logged in the user
|
||||
//update only on success to prevent corrupt data
|
||||
BungeeLoginSession loginSession = plugin.getSession().get(forPlayer.getPendingConnection());
|
||||
|
||||
@@ -49,7 +49,7 @@ public class AsyncPremiumCheck extends JoinManagement<ProxiedPlayer, CommandSend
|
||||
|
||||
public AsyncPremiumCheck(FastLoginBungee plugin, PreLoginEvent preLoginEvent, PendingConnection connection,
|
||||
String username) {
|
||||
super(plugin.getCore(), plugin.getCore().getAuthPluginHook());
|
||||
super(plugin.getCore(), plugin.getCore().getAuthPluginHook(), plugin.getBedrockService());
|
||||
|
||||
this.plugin = plugin;
|
||||
this.preLoginEvent = preLoginEvent;
|
||||
|
||||
@@ -53,6 +53,7 @@ public class FloodgateAuthTask
|
||||
@Override
|
||||
protected void startLogin() {
|
||||
BungeeLoginSession session = new BungeeLoginSession(player.getName(), isRegistered, profile);
|
||||
core.getPlugin().getSession().put(player.getPendingConnection(), session);
|
||||
|
||||
// enable auto login based on the value of 'autoLoginFloodgate' in config.yml
|
||||
boolean forcedOnlineMode = autoLoginFloodgate.equals("true")
|
||||
|
||||
@@ -50,7 +50,7 @@ public class ForceLoginTask
|
||||
private final Server server;
|
||||
|
||||
//treat player as if they had a premium account, even when they don't
|
||||
//used for Floodgate auto login/register
|
||||
//use for Floodgate auto login/register
|
||||
private final boolean forcedOnlineMode;
|
||||
|
||||
public ForceLoginTask(FastLoginCore<ProxiedPlayer, CommandSender, FastLoginBungee> core,
|
||||
|
||||
@@ -13,6 +13,9 @@ softDepends:
|
||||
- BungeeAuth
|
||||
- BungeeCordAuthenticatorBungee
|
||||
- SodionAuth
|
||||
# Bedrock Player Bridge
|
||||
- Geyser-BungeeCord
|
||||
- floodgate
|
||||
|
||||
description: |
|
||||
${project.description}
|
||||
|
||||
85
core/pom.xml
85
core/pom.xml
@@ -55,8 +55,8 @@
|
||||
</repository>
|
||||
<!-- Floodgate -->
|
||||
<repository>
|
||||
<id>nukkitx-snapshot</id>
|
||||
<url>https://repo.nukkitx.com/maven-snapshots/</url>
|
||||
<id>opencollab-snapshot</id>
|
||||
<url>https://repo.opencollab.dev/maven-snapshots/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
@@ -68,20 +68,28 @@
|
||||
<groupId>com.zaxxer</groupId>
|
||||
<artifactId>HikariCP</artifactId>
|
||||
<version>4.0.3</version>
|
||||
<exclusions>
|
||||
<!-- HikariCP uses an old version of this API that has a typo in the service interface -->
|
||||
<!-- We will use the api provided by the jdk14 dependency -->
|
||||
<exclusion>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!--Logging framework implements slf4j which is required by hikari-->
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-jdk14</artifactId>
|
||||
<version>1.7.32</version>
|
||||
<version>2.0.0-alpha6</version>
|
||||
</dependency>
|
||||
|
||||
<!-- snakeyaml is present in Bungee, Spigot, Cauldron and so we could use this independent implementation -->
|
||||
<!-- snakeyaml is present in Bungee, Spigot, Cauldron, so we could use this independent implementation -->
|
||||
<dependency>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-config</artifactId>
|
||||
<version>1.12-SNAPSHOT</version>
|
||||
<version>1.16-R0.4</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>*</groupId>
|
||||
@@ -94,7 +102,39 @@
|
||||
<dependency>
|
||||
<groupId>org.geysermc.floodgate</groupId>
|
||||
<artifactId>api</artifactId>
|
||||
<version>2.0-SNAPSHOT</version>
|
||||
<version>${floodgate.version}</version>
|
||||
<scope>provided</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.geysermc.cumulus</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- Bedrock player bridge -->
|
||||
<dependency>
|
||||
<groupId>org.geysermc</groupId>
|
||||
<artifactId>core</artifactId>
|
||||
<version>${geyser.version}</version>
|
||||
<scope>provided</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>*</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- We need the API, but it was excluded above -->
|
||||
<dependency>
|
||||
<groupId>org.geysermc</groupId>
|
||||
<artifactId>geyser-api</artifactId>
|
||||
<version>${geyser.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -102,21 +142,44 @@
|
||||
<dependency>
|
||||
<groupId>com.github.games647</groupId>
|
||||
<artifactId>craftapi</artifactId>
|
||||
<version>0.4</version>
|
||||
<version>0.5.1</version>
|
||||
</dependency>
|
||||
|
||||
<!-- APIs we can use because they are available in all platforms (Spigot, Bungee) -->
|
||||
<!-- APIs we can use because they are available in all platforms (Spigot, Bungee, Velocity) -->
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<version>17.0</version>
|
||||
<scope>provided</scope>
|
||||
<!-- Old version for velocity -->
|
||||
<version>25.1-jre</version>
|
||||
<!-- Exclude compile time dependencies not marked as such on upstream -->
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.google.code.findbugs</groupId>
|
||||
<artifactId>jsr305</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.checkerframework</groupId>
|
||||
<artifactId>checker-qual</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.google.errorprone</groupId>
|
||||
<artifactId>error_prone_annotations</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.google.j2objc</groupId>
|
||||
<artifactId>j2objc-annotations</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>animal-sniffer-annotations</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.2.4</version>
|
||||
<version>2.9.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
||||
@@ -38,8 +38,8 @@ import org.slf4j.Logger;
|
||||
|
||||
/**
|
||||
* This limits the number of threads that are used at maximum. Thread creation can be very heavy for the CPU and
|
||||
* context switching between threads too. However we need many threads for blocking HTTP and database calls.
|
||||
* Nevertheless this number can be further limited, because the number of actually working database threads
|
||||
* context switching between threads too. However, we need many threads for blocking HTTP and database calls.
|
||||
* Nevertheless, this number can be further limited, because the number of actually working database threads
|
||||
* is limited by the size of our database pool. The goal is to separate concerns into processing and blocking only
|
||||
* threads.
|
||||
*/
|
||||
|
||||
@@ -35,7 +35,7 @@ import java.util.logging.Level;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.slf4j.impl.JDK14LoggerAdapter;
|
||||
import org.slf4j.jul.JDK14LoggerAdapter;
|
||||
|
||||
public class CommonUtil {
|
||||
|
||||
@@ -70,7 +70,27 @@ public class CommonUtil {
|
||||
return new String(chars);
|
||||
}
|
||||
|
||||
public static Logger createLoggerFromJDK(java.util.logging.Logger parent) {
|
||||
/**
|
||||
* This creates a SLF4J logger. In the process it initializes the SLF4J service provider. This method looks
|
||||
* for the provider in the plugin jar instead of in the server jar when creating a Logger. The provider is only
|
||||
* initialized once, so this method should be called early.
|
||||
*
|
||||
* The provider is bound to the service class `SLF4JServiceProvider`. Relocating this class makes it available
|
||||
* for exclusive own usage. Other dependencies will use the relocated service too, and therefore will find the
|
||||
* initialized provider.
|
||||
*
|
||||
* @param parent JDK logger
|
||||
* @return slf4j logger
|
||||
*/
|
||||
public static Logger initializeLoggerService(java.util.logging.Logger parent) {
|
||||
// set the class loader to the plugin one to find our own SLF4J provider in the plugin jar and not in the global
|
||||
ClassLoader oldLoader = Thread.currentThread().getContextClassLoader();
|
||||
|
||||
ClassLoader pluginLoader = CommonUtil.class.getClassLoader();
|
||||
Thread.currentThread().setContextClassLoader(pluginLoader);
|
||||
|
||||
// Trigger provider search
|
||||
LoggerFactory.getLogger(parent.getName()).info("Initialize logging service");
|
||||
try {
|
||||
parent.setLevel(Level.ALL);
|
||||
|
||||
@@ -82,6 +102,9 @@ public class CommonUtil {
|
||||
parent.log(Level.WARNING, "Cannot create slf4j logging adapter", reflectEx);
|
||||
parent.log(Level.WARNING, "Creating logger instance manually...");
|
||||
return LoggerFactory.getLogger(parent.getName());
|
||||
} finally {
|
||||
// restore previous class loader
|
||||
Thread.currentThread().setContextClassLoader(oldLoader);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,66 +1,7 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package com.github.games647.fastlogin.core;
|
||||
|
||||
/**
|
||||
* Limit the number of requests with a maximum size. Each requests expires after the specified time making it available
|
||||
* for another request.
|
||||
*/
|
||||
public class RateLimiter {
|
||||
@FunctionalInterface
|
||||
public interface RateLimiter {
|
||||
|
||||
private final long[] requests;
|
||||
private final long expireTime;
|
||||
private int position;
|
||||
|
||||
public RateLimiter(int maxLimit, long expireTime) {
|
||||
this.requests = new long[maxLimit];
|
||||
this.expireTime = expireTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ask if access is allowed. If so register the request.
|
||||
*
|
||||
* @return true if allowed - false otherwise without any side effects
|
||||
*/
|
||||
public boolean tryAcquire() {
|
||||
// current time millis is not monotonic - it can jump back depending on user choice or NTP
|
||||
long now = System.nanoTime() / 1_000_000;
|
||||
|
||||
// after this the request should be expired
|
||||
long toBeExpired = now - expireTime;
|
||||
synchronized (this) {
|
||||
// having synchronized will limit the amount of concurrency a lot
|
||||
long oldest = requests[position];
|
||||
if (oldest < toBeExpired) {
|
||||
requests[position] = now;
|
||||
position = (position + 1) % requests.length;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
boolean tryAcquire();
|
||||
}
|
||||
|
||||
@@ -113,7 +113,7 @@ public class StoredProfile extends Profile {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
public synchronized boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof StoredProfile)) return false;
|
||||
if (!super.equals(o)) return false;
|
||||
@@ -123,7 +123,7 @@ public class StoredProfile extends Profile {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
public synchronized int hashCode() {
|
||||
return Objects.hash(super.hashCode(), rowId, premium, lastIp, lastLogin);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package com.github.games647.fastlogin.core;
|
||||
|
||||
import com.google.common.base.Ticker;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Limit the number of requests with a maximum size. Each requests expire after the specified time making it available
|
||||
* for another request.
|
||||
*/
|
||||
public class TickingRateLimiter implements RateLimiter {
|
||||
|
||||
private final Ticker ticker;
|
||||
|
||||
private final long[] requests;
|
||||
private final long expireTime;
|
||||
private int position;
|
||||
|
||||
public TickingRateLimiter(Ticker ticker, int maxLimit, long expireTime) {
|
||||
this.ticker = ticker;
|
||||
|
||||
this.requests = new long[maxLimit];
|
||||
this.expireTime = expireTime;
|
||||
|
||||
// fill the array with expired entry, because nanoTime could overflow and include negative numbers
|
||||
long nowMilli = ticker.read() / 1_000_000;
|
||||
long initialVal = nowMilli - expireTime;
|
||||
Arrays.fill(requests, initialVal);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ask if access is allowed. If so register the request.
|
||||
*
|
||||
* @return true if allowed - false otherwise without any side effects
|
||||
*/
|
||||
@Override
|
||||
public boolean tryAcquire() {
|
||||
// current time millis is not monotonic - it can jump back depending on user choice or NTP
|
||||
long nowMilli = ticker.read() / 1_000_000;
|
||||
synchronized (this) {
|
||||
// having synchronized will limit the amount of concurrency a lot
|
||||
long oldest = requests[position];
|
||||
if (nowMilli - oldest >= expireTime) {
|
||||
requests[position] = nowMilli;
|
||||
position = (position + 1) % requests.length;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -72,7 +72,7 @@ public interface AuthPlugin<P> {
|
||||
/**
|
||||
* Checks whether an account exists for this player name.
|
||||
*
|
||||
* This check should check if a cracked player account exists
|
||||
* This check should check if a cracked player account exists,
|
||||
* so we can be sure the premium player doesn't steal the account
|
||||
* of that player.
|
||||
*
|
||||
|
||||
@@ -1,112 +0,0 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package com.github.games647.fastlogin.core.hooks;
|
||||
|
||||
import com.github.games647.craftapi.model.Profile;
|
||||
import com.github.games647.craftapi.resolver.RateLimitException;
|
||||
import com.github.games647.fastlogin.core.shared.FastLoginCore;
|
||||
import com.github.games647.fastlogin.core.shared.LoginSource;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.geysermc.floodgate.api.FloodgateApi;
|
||||
import org.geysermc.floodgate.api.player.FloodgatePlayer;
|
||||
|
||||
public class FloodgateHook<P extends C, C, S extends LoginSource> {
|
||||
|
||||
private final FastLoginCore<P, C, ?> core;
|
||||
|
||||
public FloodgateHook(FastLoginCore<P, C, ?> core) {
|
||||
this.core = core;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the player's name conflicts an existing Java player's name, and
|
||||
* kick them if it does
|
||||
*
|
||||
* @param username the name of the player
|
||||
* @param source an instance of LoginSource
|
||||
*/
|
||||
public void checkFloodgateNameConflict(String username, LoginSource source, FloodgatePlayer floodgatePlayer) {
|
||||
String allowConflict = core.getConfig().get("allowFloodgateNameConflict").toString().toLowerCase();
|
||||
|
||||
// check if the Bedrock player is linked to a Java account
|
||||
boolean isLinked = ((FloodgatePlayer) floodgatePlayer).getLinkedPlayer() != null;
|
||||
|
||||
if (allowConflict.equals("false")
|
||||
|| allowConflict.equals("linked") && !isLinked) {
|
||||
|
||||
// check for conflicting Premium Java name
|
||||
Optional<Profile> premiumUUID = Optional.empty();
|
||||
try {
|
||||
premiumUUID = core.getResolver().findProfile(username);
|
||||
} catch (IOException | RateLimitException e) {
|
||||
core.getPlugin().getLog().error(
|
||||
"Could not check wether Floodgate Player {}'s name conflicts a premium Java player's name.",
|
||||
username);
|
||||
try {
|
||||
source.kick("Could not check if your name conflicts an existing premium Java account's name.\n"
|
||||
+ "This is usually a serverside error.");
|
||||
} catch (Exception e1) {
|
||||
core.getPlugin().getLog().error("Could not kick Player {}", username);
|
||||
}
|
||||
}
|
||||
|
||||
if (premiumUUID.isPresent()) {
|
||||
core.getPlugin().getLog().info("Bedrock Player {}'s name conflicts an existing premium Java account's name",
|
||||
username);
|
||||
try {
|
||||
source.kick("Your name conflicts an existing premium Java account's name");
|
||||
} catch (Exception e) {
|
||||
core.getPlugin().getLog().error("Could not kick Player {}", username);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
core.getPlugin().getLog().info("Skipping name conflict checking for player {}", username);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The FloodgateApi does not support querying players by name, so this function
|
||||
* iterates over every online FloodgatePlayer and checks if the requested
|
||||
* username can be found
|
||||
*
|
||||
* @param username the name of the player
|
||||
* @return FloodgatePlayer if found, null otherwise
|
||||
*/
|
||||
public FloodgatePlayer getFloodgatePlayer(String username) {
|
||||
if (core.getPlugin().isPluginInstalled("floodgate")) {
|
||||
for (FloodgatePlayer floodgatePlayer : FloodgateApi.getInstance().getPlayers()) {
|
||||
if (floodgatePlayer.getUsername().equals(username)) {
|
||||
return floodgatePlayer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package com.github.games647.fastlogin.core.hooks.bedrock;
|
||||
|
||||
import com.github.games647.craftapi.model.Profile;
|
||||
import com.github.games647.craftapi.resolver.RateLimitException;
|
||||
import com.github.games647.fastlogin.core.StoredProfile;
|
||||
import com.github.games647.fastlogin.core.shared.FastLoginCore;
|
||||
import com.github.games647.fastlogin.core.shared.LoginSource;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @param <B> is an instance of either FloodgatePlayer or GeyserSession
|
||||
*/
|
||||
public abstract class BedrockService<B> {
|
||||
|
||||
protected final FastLoginCore<?, ?, ?> core;
|
||||
protected final String allowConflict;
|
||||
|
||||
public BedrockService(FastLoginCore<?, ?, ?> core) {
|
||||
this.core = core;
|
||||
this.allowConflict = core.getConfig().get("allowFloodgateNameConflict").toString().toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform every packet level check needed on a Bedrock player.
|
||||
*
|
||||
* @param username the name of the player
|
||||
* @param source an instance of LoginSource
|
||||
* @return true if Java specific checks can be skipped
|
||||
*/
|
||||
public abstract boolean performChecks(String username, LoginSource source);
|
||||
|
||||
/**
|
||||
* Check if the player's name conflicts an existing Java player's name, and kick
|
||||
* them if it does
|
||||
*
|
||||
* @param username the name of the player
|
||||
* @param source an instance of LoginSource
|
||||
*/
|
||||
protected void checkNameConflict(String username, LoginSource source) {
|
||||
// check for conflicting Premium Java name
|
||||
Optional<Profile> premiumUUID = Optional.empty();
|
||||
try {
|
||||
premiumUUID = core.getResolver().findProfile(username);
|
||||
} catch (IOException ioEx) {
|
||||
core.getPlugin().getLog().error(
|
||||
"Could not check whether Bedrock Player {}'s name conflicts a premium Java player's name.",
|
||||
username);
|
||||
|
||||
kickPlayer(source, username, "Could not check if your name conflicts an existing " +
|
||||
"premium Java account's name. This is usually a serverside error.");
|
||||
} catch (RateLimitException rateLimitException) {
|
||||
core.getPlugin().getLog().warn("Mojang API rate limit hit");
|
||||
kickPlayer(source, username, "Could not check if your name conflicts an existing premium " +
|
||||
"Java account's name. Try again in a few minutes");
|
||||
}
|
||||
|
||||
if (premiumUUID.isPresent()) {
|
||||
core.getPlugin().getLog().info("Bedrock Player {}'s name conflicts an existing premium Java account's name",
|
||||
username);
|
||||
kickPlayer(source, username, "Your name conflicts an existing premium Java account's name");
|
||||
}
|
||||
}
|
||||
|
||||
private void kickPlayer(LoginSource source, String username, String message) {
|
||||
try {
|
||||
source.kick(message);
|
||||
} catch (Exception ex) {
|
||||
core.getPlugin().getLog().error("Could not kick Player {}", username, ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The Floodgate / Geyser API does not support querying players by name, so this function
|
||||
* iterates over every online Bedrock Player and checks if the requested
|
||||
* username can be found
|
||||
* <br>
|
||||
* <i>Falls back to non-prefixed name checks, if ProtocolLib is installed</i>
|
||||
*
|
||||
* @param prefixedUsername the name of the player with the prefix appended
|
||||
* @return Bedrock Player if found, null otherwise
|
||||
*/
|
||||
public B getBedrockPlayer(String prefixedUsername) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public B getBedrockPlayer(UUID uuid) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean isBedrockPlayer(UUID uuid) {
|
||||
return getBedrockPlayer(uuid) != null;
|
||||
}
|
||||
|
||||
public boolean isBedrockConnection(String username) {
|
||||
return getBedrockPlayer(username) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a profile's name starts with the Floodgate prefix, if it's available
|
||||
* @param profile profile of the connecting player
|
||||
* @return true if the username is forbidden
|
||||
*/
|
||||
public boolean isUsernameForbidden(StoredProfile profile) {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package com.github.games647.fastlogin.core.hooks.bedrock;
|
||||
|
||||
import com.github.games647.fastlogin.core.StoredProfile;
|
||||
import com.github.games647.fastlogin.core.shared.FastLoginCore;
|
||||
import com.github.games647.fastlogin.core.shared.LoginSource;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.geysermc.floodgate.api.FloodgateApi;
|
||||
import org.geysermc.floodgate.api.player.FloodgatePlayer;
|
||||
|
||||
public class FloodgateService extends BedrockService<FloodgatePlayer> {
|
||||
|
||||
private final FloodgateApi floodgate;
|
||||
|
||||
public FloodgateService(FloodgateApi floodgate, FastLoginCore<?, ?, ?> core) {
|
||||
super(core);
|
||||
this.floodgate = floodgate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a config entry (related to Floodgate) is valid. <br>
|
||||
* Writes to Log if the value is invalid.
|
||||
* <p>
|
||||
* This should be used for:
|
||||
* <ul>
|
||||
* <li>allowFloodgateNameConflict
|
||||
* <li>autoLoginFloodgate
|
||||
* <li>autoRegisterFloodgate
|
||||
* </ul>
|
||||
* </p>
|
||||
*
|
||||
* @param key the key of the entry in config.yml
|
||||
* @return <b>true</b> if the entry's value is "true", "false", or "linked"
|
||||
*/
|
||||
public boolean isValidFloodgateConfigString(String key) {
|
||||
String value = core.getConfig().get(key).toString().toLowerCase(Locale.ENGLISH);
|
||||
if (!value.equals("true") && !value.equals("linked") && !value.equals("false") && !value.equals("no-conflict")) {
|
||||
core.getPlugin().getLog().error("Invalid value detected for {} in FastLogin/config.yml.", key);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUsernameForbidden(StoredProfile profile) {
|
||||
String playerPrefix = floodgate.getPlayerPrefix();
|
||||
return profile.getName().startsWith(playerPrefix) && !playerPrefix.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean performChecks(String username, LoginSource source) {
|
||||
// check if the Bedrock player is linked to a Java account
|
||||
FloodgatePlayer floodgatePlayer = getBedrockPlayer(username);
|
||||
boolean isLinked = floodgatePlayer.getLinkedPlayer() != null;
|
||||
|
||||
if ("false".equals(allowConflict)
|
||||
|| "linked".equals(allowConflict) && !isLinked) {
|
||||
super.checkNameConflict(username, source);
|
||||
} else {
|
||||
core.getPlugin().getLog().info("Skipping name conflict checking for player {}", username);
|
||||
}
|
||||
|
||||
//Floodgate users don't need Java specific checks
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* The FloodgateApi does not support querying players by name, so this function
|
||||
* iterates over every online FloodgatePlayer and checks if the requested
|
||||
* username can be found
|
||||
* <br>
|
||||
* <i>Falls back to non-prefixed name checks, if ProtocolLib is installed</i>
|
||||
*
|
||||
* @param prefixedUsername the name of the player with the prefix appended
|
||||
* @return FloodgatePlayer if found, null otherwise
|
||||
*/
|
||||
public FloodgatePlayer getBedrockPlayer(String prefixedUsername) {
|
||||
//prefixes are broken with ProtocolLib, so fall back to name checks without prefixes
|
||||
//this should be removed if #493 gets fixed
|
||||
if (core.getPlugin().isPluginInstalled("ProtocolLib")) {
|
||||
for (FloodgatePlayer floodgatePlayer : floodgate.getPlayers()) {
|
||||
if (floodgatePlayer.getUsername().equals(prefixedUsername)) {
|
||||
return floodgatePlayer;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
for (FloodgatePlayer floodgatePlayer : floodgate.getPlayers()) {
|
||||
if (floodgatePlayer.getCorrectUsername().equals(prefixedUsername)) {
|
||||
return floodgatePlayer;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public FloodgatePlayer getBedrockPlayer(UUID uuid) {
|
||||
return floodgate.getPlayer(uuid);
|
||||
}
|
||||
|
||||
public boolean isBedrockPlayer(UUID uuid) {
|
||||
return getBedrockPlayer(uuid) != null;
|
||||
}
|
||||
|
||||
public boolean isBedrockConnection(String username) {
|
||||
return getBedrockPlayer(username) != null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package com.github.games647.fastlogin.core.hooks.bedrock;
|
||||
|
||||
import com.github.games647.fastlogin.core.shared.FastLoginCore;
|
||||
import com.github.games647.fastlogin.core.shared.LoginSource;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.session.auth.AuthType;
|
||||
|
||||
public class GeyserService extends BedrockService<GeyserSession> {
|
||||
|
||||
private final GeyserImpl geyser;
|
||||
private final FastLoginCore<?, ?, ?> core;
|
||||
private final AuthType authType;
|
||||
|
||||
public GeyserService(GeyserImpl geyser, FastLoginCore<?, ?, ?> core) {
|
||||
super(core);
|
||||
this.geyser = geyser;
|
||||
this.core = core;
|
||||
this.authType = GeyserImpl.getInstance().getConfig().getRemote().getAuthType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean performChecks(String username, LoginSource source) {
|
||||
// AuthType.FLOODGATE will be handled by FloodgateService
|
||||
if (authType == AuthType.ONLINE) {
|
||||
// authenticate everyone, as if they were Java players, since they have signed
|
||||
// in through Mojang
|
||||
return false;
|
||||
}
|
||||
if ("true".equals(allowConflict)) {
|
||||
core.getPlugin().getLog().info("Skipping name conflict checking for player {}", username);
|
||||
} else {
|
||||
super.checkNameConflict(username, source);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeyserSession getBedrockPlayer(String username) {
|
||||
return geyser.connectionByName(username);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeyserSession getBedrockPlayer(UUID uuid) {
|
||||
return geyser.connectionByUuid(uuid);
|
||||
}
|
||||
}
|
||||
@@ -27,12 +27,16 @@ package com.github.games647.fastlogin.core.shared;
|
||||
|
||||
import com.github.games647.craftapi.resolver.MojangResolver;
|
||||
import com.github.games647.craftapi.resolver.http.RotatingProxySelector;
|
||||
import com.github.games647.fastlogin.core.AuthStorage;
|
||||
import com.github.games647.fastlogin.core.CommonUtil;
|
||||
import com.github.games647.fastlogin.core.RateLimiter;
|
||||
import com.github.games647.fastlogin.core.TickingRateLimiter;
|
||||
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
|
||||
import com.github.games647.fastlogin.core.hooks.DefaultPasswordGenerator;
|
||||
import com.github.games647.fastlogin.core.hooks.PasswordGenerator;
|
||||
import com.github.games647.fastlogin.core.storage.MySQLStorage;
|
||||
import com.github.games647.fastlogin.core.storage.SQLStorage;
|
||||
import com.github.games647.fastlogin.core.storage.SQLiteStorage;
|
||||
import com.google.common.base.Ticker;
|
||||
import com.google.common.net.HostAndPort;
|
||||
import com.zaxxer.hikari.HikariConfig;
|
||||
|
||||
@@ -82,7 +86,7 @@ public class FastLoginCore<P extends C, C, T extends PlatformPlugin<C>> {
|
||||
private final MojangResolver resolver = new MojangResolver();
|
||||
|
||||
private Configuration config;
|
||||
private AuthStorage storage;
|
||||
private SQLStorage storage;
|
||||
private RateLimiter rateLimiter;
|
||||
private PasswordGenerator<P> passwordGenerator = new DefaultPasswordGenerator<>();
|
||||
private AuthPlugin<P> authPlugin;
|
||||
@@ -114,17 +118,11 @@ public class FastLoginCore<P extends C, C, T extends PlatformPlugin<C>> {
|
||||
return;
|
||||
}
|
||||
|
||||
int maxCon = config.getInt("anti-bot.connections", 200);
|
||||
long expireTime = config.getLong("anti-bot.expire", 5) * 60 * 1_000L;
|
||||
if (expireTime > MAX_EXPIRE_RATE) {
|
||||
expireTime = MAX_EXPIRE_RATE;
|
||||
}
|
||||
|
||||
rateLimiter = new RateLimiter(maxCon, expireTime);
|
||||
rateLimiter = createRateLimiter(config.getSection("anti-bot"));
|
||||
Set<Proxy> proxies = config.getStringList("proxies")
|
||||
.stream()
|
||||
.map(HostAndPort::fromString)
|
||||
.map(proxy -> new InetSocketAddress(proxy.getHostText(), proxy.getPort()))
|
||||
.map(proxy -> new InetSocketAddress(proxy.getHost(), proxy.getPort()))
|
||||
.map(sa -> new Proxy(Type.HTTP, sa))
|
||||
.collect(toSet());
|
||||
|
||||
@@ -142,6 +140,22 @@ public class FastLoginCore<P extends C, C, T extends PlatformPlugin<C>> {
|
||||
resolver.setOutgoingAddresses(addresses);
|
||||
}
|
||||
|
||||
private RateLimiter createRateLimiter(Configuration botSection) {
|
||||
boolean enabled = botSection.getBoolean("enabled", true);
|
||||
if (!enabled) {
|
||||
// no-op rate limiter
|
||||
return () -> true;
|
||||
}
|
||||
|
||||
int maxCon = botSection.getInt("anti-bot.connections", 200);
|
||||
long expireTime = botSection.getLong("anti-bot.expire", 5) * 60 * 1_000L;
|
||||
if (expireTime > MAX_EXPIRE_RATE) {
|
||||
expireTime = MAX_EXPIRE_RATE;
|
||||
}
|
||||
|
||||
return new TickingRateLimiter(Ticker.systemTicker(), maxCon, expireTime);
|
||||
}
|
||||
|
||||
private Configuration loadFile(String fileName) throws IOException {
|
||||
ConfigurationProvider configProvider = ConfigurationProvider.getProvider(YamlConfiguration.class);
|
||||
|
||||
@@ -169,7 +183,7 @@ public class FastLoginCore<P extends C, C, T extends PlatformPlugin<C>> {
|
||||
return resolver;
|
||||
}
|
||||
|
||||
public AuthStorage getStorage() {
|
||||
public SQLStorage getStorage() {
|
||||
return storage;
|
||||
}
|
||||
|
||||
@@ -189,26 +203,37 @@ public class FastLoginCore<P extends C, C, T extends PlatformPlugin<C>> {
|
||||
}
|
||||
|
||||
public boolean setupDatabase() {
|
||||
if (!checkDriver(config.getString("driver"))) {
|
||||
String driver = config.getString("driver");
|
||||
if (!checkDriver(driver)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
HikariConfig databaseConfig = new HikariConfig();
|
||||
databaseConfig.setDriverClassName(config.getString("driver"));
|
||||
databaseConfig.setDriverClassName(driver);
|
||||
|
||||
String host = config.get("host", "");
|
||||
int port = config.get("port", 3306);
|
||||
String database = config.getString("database");
|
||||
|
||||
boolean useSSL = config.get("useSSL", false);
|
||||
|
||||
databaseConfig.setUsername(config.get("username", ""));
|
||||
databaseConfig.setPassword(config.getString("password"));
|
||||
|
||||
databaseConfig.setConnectionTimeout(config.getInt("timeout", 30) * 1_000L);
|
||||
databaseConfig.setMaxLifetime(config.getInt("lifetime", 30) * 1_000L);
|
||||
|
||||
storage = new AuthStorage(this, host, port, database, databaseConfig, useSSL);
|
||||
if (driver.contains("sqlite")) {
|
||||
storage = new SQLiteStorage(this, database, databaseConfig);
|
||||
} else {
|
||||
String host = config.get("host", "");
|
||||
int port = config.get("port", 3306);
|
||||
boolean useSSL = config.get("useSSL", false);
|
||||
|
||||
if (useSSL) {
|
||||
databaseConfig.addDataSourceProperty("allowPublicKeyRetrieval", config.getBoolean("allowPublicKeyRetrieval", false));
|
||||
databaseConfig.addDataSourceProperty("serverRSAPublicKeyFile", config.getString("ServerRSAPublicKeyFile"));
|
||||
databaseConfig.addDataSourceProperty("sslMode", config.getString("sslMode", "Required"));
|
||||
}
|
||||
|
||||
databaseConfig.setUsername(config.get("username", ""));
|
||||
databaseConfig.setPassword(config.getString("password"));
|
||||
storage = new MySQLStorage(this, driver, host, port, database, databaseConfig, useSSL);
|
||||
}
|
||||
|
||||
try {
|
||||
storage.createTables();
|
||||
return true;
|
||||
@@ -225,7 +250,8 @@ public class FastLoginCore<P extends C, C, T extends PlatformPlugin<C>> {
|
||||
} catch (ClassNotFoundException notFoundEx) {
|
||||
Logger log = plugin.getLog();
|
||||
log.warn("This driver {} is not supported on this platform", className);
|
||||
log.warn("Please choose MySQL (Spigot+BungeeCord), SQLite (Spigot+Sponge) or MariaDB (Sponge)", notFoundEx);
|
||||
log.warn("Please choose either MySQL (Spigot, BungeeCord), SQLite (Spigot, Sponge) or " +
|
||||
"MariaDB (Sponge, Velocity)", notFoundEx);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
@@ -73,10 +73,27 @@ public abstract class FloodgateManagement<P extends C, C, L extends LoginSession
|
||||
|
||||
// check if the Bedrock player is linked to a Java account
|
||||
isLinked = floodgatePlayer.getLinkedPlayer() != null;
|
||||
|
||||
//this happens on Bukkit if it's connected to Bungee
|
||||
//if that's the case, players will be logged in via plugin messages
|
||||
if (core.getStorage() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
profile = core.getStorage().loadProfile(username);
|
||||
AuthPlugin<P> authPlugin = core.getAuthPluginHook();
|
||||
|
||||
|
||||
try {
|
||||
isRegistered = authPlugin.isRegistered(username);
|
||||
//maybe Bungee without auth plugin
|
||||
if (authPlugin == null) {
|
||||
if (profile != null) {
|
||||
isRegistered = profile.isPremium();
|
||||
} else {
|
||||
isRegistered = false;
|
||||
}
|
||||
} else {
|
||||
isRegistered = authPlugin.isRegistered(username);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
core.getPlugin().getLog().error(
|
||||
"An error has occured while checking if player {} is registered",
|
||||
@@ -108,7 +125,6 @@ public abstract class FloodgateManagement<P extends C, C, L extends LoginSession
|
||||
}
|
||||
|
||||
//logging in from bedrock for a second time threw an error with UUID
|
||||
profile = core.getStorage().loadProfile(username);
|
||||
if (profile == null) {
|
||||
profile = new StoredProfile(getUUID(player), username, true, getAddress(player).toString());
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
*/
|
||||
package com.github.games647.fastlogin.core.shared;
|
||||
|
||||
import com.github.games647.fastlogin.core.AuthStorage;
|
||||
import com.github.games647.fastlogin.core.storage.SQLStorage;
|
||||
import com.github.games647.fastlogin.core.StoredProfile;
|
||||
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
|
||||
import com.github.games647.fastlogin.core.shared.event.FastLoginAutoLoginEvent;
|
||||
@@ -55,7 +55,7 @@ public abstract class ForceLoginManagement<P extends C, C, L extends LoginSessio
|
||||
return;
|
||||
}
|
||||
|
||||
AuthStorage storage = core.getStorage();
|
||||
SQLStorage storage = core.getStorage();
|
||||
StoredProfile playerProfile = session.getProfile();
|
||||
try {
|
||||
if (isOnlineMode()) {
|
||||
|
||||
@@ -29,25 +29,23 @@ import com.github.games647.craftapi.model.Profile;
|
||||
import com.github.games647.craftapi.resolver.RateLimitException;
|
||||
import com.github.games647.fastlogin.core.StoredProfile;
|
||||
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
|
||||
import com.github.games647.fastlogin.core.hooks.FloodgateHook;
|
||||
import com.github.games647.fastlogin.core.hooks.bedrock.BedrockService;
|
||||
import com.github.games647.fastlogin.core.shared.event.FastLoginPreLoginEvent;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.geysermc.floodgate.api.player.FloodgatePlayer;
|
||||
|
||||
import net.md_5.bungee.config.Configuration;
|
||||
|
||||
public abstract class JoinManagement<P extends C, C, S extends LoginSource> {
|
||||
|
||||
protected final FastLoginCore<P, C, ?> core;
|
||||
protected final AuthPlugin<P> authHook;
|
||||
private final FloodgateHook<P, C, ?> floodgateHook;
|
||||
private final BedrockService<?> bedrockService;
|
||||
|
||||
public JoinManagement(FastLoginCore<P, C, ?> core, AuthPlugin<P> authHook) {
|
||||
public JoinManagement(FastLoginCore<P, C, ?> core, AuthPlugin<P> authHook, BedrockService<?> bedrockService) {
|
||||
this.core = core;
|
||||
this.authHook = authHook;
|
||||
this.floodgateHook = new FloodgateHook<>(core);
|
||||
this.bedrockService = bedrockService;
|
||||
}
|
||||
|
||||
public void onLogin(String username, S source) {
|
||||
@@ -57,13 +55,14 @@ public abstract class JoinManagement<P extends C, C, S extends LoginSource> {
|
||||
return;
|
||||
}
|
||||
|
||||
//check if the player is connecting through Floodgate
|
||||
FloodgatePlayer floodgatePlayer = floodgateHook.getFloodgatePlayer(username);
|
||||
|
||||
if (floodgatePlayer != null) {
|
||||
floodgateHook.checkFloodgateNameConflict(username, source, floodgatePlayer);
|
||||
return;
|
||||
//check if the player is connecting through Bedrock Edition
|
||||
if (bedrockService != null && bedrockService.isBedrockConnection(username)) {
|
||||
//perform Bedrock specific checks and skip Java checks, if they are not needed
|
||||
if (bedrockService.performChecks(username, source)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
callFastLoginPreLoginEvent(username, source, profile);
|
||||
|
||||
Configuration config = core.getConfig();
|
||||
@@ -76,7 +75,9 @@ public abstract class JoinManagement<P extends C, C, S extends LoginSource> {
|
||||
core.getPlugin().getLog().info("Requesting premium login for registered player: {}", username);
|
||||
requestPremiumLogin(source, profile, username, true);
|
||||
} else {
|
||||
startCrackedSession(source, profile, username);
|
||||
if (isValidUsername(source, profile)) {
|
||||
startCrackedSession(source, profile, username);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (core.getPendingLogin().remove(ip + username) != null && config.get("secondAttemptCracked", false)) {
|
||||
@@ -114,6 +115,16 @@ public abstract class JoinManagement<P extends C, C, S extends LoginSource> {
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean isValidUsername(LoginSource source, StoredProfile profile) throws Exception {
|
||||
if (bedrockService != null && bedrockService.isUsernameForbidden(profile)) {
|
||||
core.getPlugin().getLog().info("Floodgate Prefix detected on cracked player");
|
||||
source.kick("Your username contains illegal characters");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean checkPremiumName(S source, String username, StoredProfile profile) throws Exception {
|
||||
core.getPlugin().getLog().info("GameProfile {} uses a premium username", username);
|
||||
if (core.getConfig().get("autoRegister", false) && (authHook == null || !authHook.isRegistered(username))) {
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
package com.github.games647.fastlogin.core.shared;
|
||||
|
||||
import com.github.games647.fastlogin.core.StoredProfile;
|
||||
import com.google.common.base.MoreObjects;
|
||||
import com.google.common.base.Objects;
|
||||
|
||||
import java.util.UUID;
|
||||
@@ -91,7 +92,7 @@ public abstract class LoginSession {
|
||||
|
||||
@Override
|
||||
public synchronized String toString() {
|
||||
return Objects.toStringHelper(this)
|
||||
return MoreObjects.toStringHelper(this)
|
||||
.add("profile", profile)
|
||||
.add("requestUsername", requestUsername)
|
||||
.add("username", username)
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
package com.github.games647.fastlogin.core.shared;
|
||||
|
||||
import com.github.games647.fastlogin.core.AsyncScheduler;
|
||||
import com.github.games647.fastlogin.core.hooks.bedrock.BedrockService;
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
|
||||
import java.nio.file.Path;
|
||||
@@ -53,6 +54,8 @@ public interface PlatformPlugin<C> {
|
||||
}
|
||||
}
|
||||
|
||||
BedrockService<?> getBedrockService();
|
||||
|
||||
default ThreadFactory getThreadFactory() {
|
||||
return new ThreadFactoryBuilder()
|
||||
.setNameFormat(getName() + " Pool Thread #%1$d")
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.github.games647.fastlogin.core.storage;
|
||||
|
||||
import com.github.games647.fastlogin.core.StoredProfile;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public interface AuthStorage {
|
||||
StoredProfile loadProfile(String name);
|
||||
|
||||
StoredProfile loadProfile(UUID uuid);
|
||||
|
||||
void save(StoredProfile playerProfile);
|
||||
|
||||
void close();
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package com.github.games647.fastlogin.core.storage;
|
||||
|
||||
import com.github.games647.fastlogin.core.shared.FastLoginCore;
|
||||
import com.zaxxer.hikari.HikariConfig;
|
||||
|
||||
public class MySQLStorage extends SQLStorage {
|
||||
|
||||
public MySQLStorage(FastLoginCore<?, ?, ?> core, String driver, String host, int port, String database,
|
||||
HikariConfig config,boolean useSSL) {
|
||||
super(core,
|
||||
buildJDBCUrl(driver, host, port, database),
|
||||
setParams(config, useSSL));
|
||||
}
|
||||
|
||||
private static String buildJDBCUrl(String driver, String host, int port, String database) {
|
||||
String protocol = "mysql";
|
||||
if (driver.contains("mariadb")) {
|
||||
protocol = "mariadb";
|
||||
}
|
||||
|
||||
return protocol + "://" + host + ':' + port + '/' + database;
|
||||
}
|
||||
|
||||
private static HikariConfig setParams(HikariConfig config, boolean useSSL) {
|
||||
// Require SSL on the server if requested in config - this will also verify certificate
|
||||
// Those values are deprecated in favor of sslMode
|
||||
config.addDataSourceProperty("useSSL", useSSL);
|
||||
config.addDataSourceProperty("requireSSL", useSSL);
|
||||
|
||||
// adding paranoid hides hostname, username, version and so
|
||||
// could be useful for hiding server details
|
||||
config.addDataSourceProperty("paranoid", true);
|
||||
|
||||
// enable MySQL specific optimizations
|
||||
addPerformanceProperties(config);
|
||||
return config;
|
||||
}
|
||||
|
||||
private static void addPerformanceProperties(HikariConfig config) {
|
||||
// disabled by default - will return the same prepared statement instance
|
||||
config.addDataSourceProperty("cachePrepStmts", true);
|
||||
// default prepStmtCacheSize 25 - amount of cached statements
|
||||
config.addDataSourceProperty("prepStmtCacheSize", 250);
|
||||
// default prepStmtCacheSqlLimit 256 - length of SQL
|
||||
config.addDataSourceProperty("prepStmtCacheSqlLimit", 2048);
|
||||
// default false - available in newer versions caches the statements server-side
|
||||
config.addDataSourceProperty("useServerPrepStmts", true);
|
||||
// default false - prefer use of local values for autocommit and
|
||||
// transaction isolation (alwaysSendSetIsolation) should only be enabled if always use the set* methods
|
||||
// instead of raw SQL
|
||||
// https://forums.mysql.com/read.php?39,626495,626512
|
||||
config.addDataSourceProperty("useLocalSessionState", true);
|
||||
// rewrite batched statements to a single statement, adding them behind each other
|
||||
// only useful for addBatch statements and inserts
|
||||
config.addDataSourceProperty("rewriteBatchedStatements", true);
|
||||
// cache result metadata
|
||||
config.addDataSourceProperty("cacheResultSetMetadata", true);
|
||||
// cache results of show variables and collation per URL
|
||||
config.addDataSourceProperty("cacheServerConfiguration", true);
|
||||
// default false - set auto commit only if not matching
|
||||
config.addDataSourceProperty("elideSetAutoCommits", true);
|
||||
|
||||
// default true - internal timers for idle calculation -> removes System.getCurrentTimeMillis call per query
|
||||
// Some platforms are slow on this and it could affect the throughput about 3% according to MySQL
|
||||
// performance gems presentation
|
||||
// In our case it can be useful to see the time in error messages
|
||||
// config.addDataSourceProperty("maintainTimeStats", false);
|
||||
}
|
||||
}
|
||||
@@ -23,9 +23,10 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package com.github.games647.fastlogin.core;
|
||||
package com.github.games647.fastlogin.core.storage;
|
||||
|
||||
import com.github.games647.craftapi.UUIDAdapter;
|
||||
import com.github.games647.fastlogin.core.StoredProfile;
|
||||
import com.github.games647.fastlogin.core.shared.FastLoginCore;
|
||||
import com.zaxxer.hikari.HikariConfig;
|
||||
import com.zaxxer.hikari.HikariDataSource;
|
||||
@@ -42,23 +43,34 @@ import java.util.concurrent.ThreadFactory;
|
||||
|
||||
import static java.sql.Statement.RETURN_GENERATED_KEYS;
|
||||
|
||||
public class AuthStorage {
|
||||
public abstract class SQLStorage implements AuthStorage {
|
||||
|
||||
private static final String PREMIUM_TABLE = "premium";
|
||||
private static final String JDBC_PROTOCOL = "jdbc:";
|
||||
|
||||
private static final String LOAD_BY_NAME = "SELECT * FROM `" + PREMIUM_TABLE + "` WHERE `Name`=? LIMIT 1";
|
||||
private static final String LOAD_BY_UUID = "SELECT * FROM `" + PREMIUM_TABLE + "` WHERE `UUID`=? LIMIT 1";
|
||||
private static final String INSERT_PROFILE = "INSERT INTO `" + PREMIUM_TABLE
|
||||
protected static final String PREMIUM_TABLE = "premium";
|
||||
protected static final String CREATE_TABLE_STMT = "CREATE TABLE IF NOT EXISTS `" + PREMIUM_TABLE + "` ("
|
||||
+ "`UserID` INTEGER PRIMARY KEY AUTO_INCREMENT, "
|
||||
+ "`UUID` CHAR(36), "
|
||||
+ "`Name` VARCHAR(16) NOT NULL, "
|
||||
+ "`Premium` BOOLEAN NOT NULL, "
|
||||
+ "`LastIp` VARCHAR(255) NOT NULL, "
|
||||
+ "`LastLogin` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "
|
||||
//the premium shouldn't steal the cracked account by changing the name
|
||||
+ "UNIQUE (`Name`) "
|
||||
+ ')';
|
||||
|
||||
protected static final String LOAD_BY_NAME = "SELECT * FROM `" + PREMIUM_TABLE + "` WHERE `Name`=? LIMIT 1";
|
||||
protected static final String LOAD_BY_UUID = "SELECT * FROM `" + PREMIUM_TABLE + "` WHERE `UUID`=? LIMIT 1";
|
||||
protected static final String INSERT_PROFILE = "INSERT INTO `" + PREMIUM_TABLE
|
||||
+ "` (`UUID`, `Name`, `Premium`, `LastIp`) " + "VALUES (?, ?, ?, ?) ";
|
||||
// limit not necessary here, because it's unique
|
||||
private static final String UPDATE_PROFILE = "UPDATE `" + PREMIUM_TABLE
|
||||
protected static final String UPDATE_PROFILE = "UPDATE `" + PREMIUM_TABLE
|
||||
+ "` SET `UUID`=?, `Name`=?, `Premium`=?, `LastIp`=?, `LastLogin`=CURRENT_TIMESTAMP WHERE `UserID`=?";
|
||||
|
||||
private final FastLoginCore<?, ?, ?> core;
|
||||
private final HikariDataSource dataSource;
|
||||
protected final FastLoginCore<?, ?, ?> core;
|
||||
protected final HikariDataSource dataSource;
|
||||
|
||||
public AuthStorage(FastLoginCore<?, ?, ?> core, String host, int port, String databasePath,
|
||||
HikariConfig config, boolean useSSL) {
|
||||
public SQLStorage(FastLoginCore<?, ?, ?> core, String jdbcURL, HikariConfig config) {
|
||||
this.core = core;
|
||||
config.setPoolName(core.getPlugin().getName());
|
||||
|
||||
@@ -67,68 +79,7 @@ public class AuthStorage {
|
||||
config.setThreadFactory(platformThreadFactory);
|
||||
}
|
||||
|
||||
String jdbcUrl = "jdbc:";
|
||||
if (config.getDriverClassName().contains("sqlite")) {
|
||||
String pluginFolder = core.getPlugin().getPluginFolder().toAbsolutePath().toString();
|
||||
databasePath = databasePath.replace("{pluginDir}", pluginFolder);
|
||||
|
||||
jdbcUrl += "sqlite://" + databasePath;
|
||||
config.setConnectionTestQuery("SELECT 1");
|
||||
config.setMaximumPoolSize(1);
|
||||
|
||||
//a try to fix https://www.spigotmc.org/threads/fastlogin.101192/page-26#post-1874647
|
||||
// format strings retrieved by the timestamp column to match them from MySQL
|
||||
config.addDataSourceProperty("date_string_format", "yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
// TODO: test first for compatibility
|
||||
// config.addDataSourceProperty("date_precision", "seconds");
|
||||
} else {
|
||||
jdbcUrl += "mysql://" + host + ':' + port + '/' + databasePath;
|
||||
|
||||
// Require SSL on the server if requested in config - this will also verify certificate
|
||||
// Those values are deprecated in favor of sslMode
|
||||
config.addDataSourceProperty("useSSL", useSSL);
|
||||
config.addDataSourceProperty("requireSSL", useSSL);
|
||||
|
||||
// prefer encrypted if possible
|
||||
config.addDataSourceProperty("sslMode", "PREFERRED");
|
||||
|
||||
// adding paranoid hides hostname, username, version and so
|
||||
// could be useful for hiding server details
|
||||
config.addDataSourceProperty("paranoid", true);
|
||||
|
||||
// enable MySQL specific optimizations
|
||||
// disabled by default - will return the same prepared statement instance
|
||||
config.addDataSourceProperty("cachePrepStmts", true);
|
||||
// default prepStmtCacheSize 25 - amount of cached statements
|
||||
config.addDataSourceProperty("prepStmtCacheSize", 250);
|
||||
// default prepStmtCacheSqlLimit 256 - length of SQL
|
||||
config.addDataSourceProperty("prepStmtCacheSqlLimit", 2048);
|
||||
// default false - available in newer versions caches the statements server-side
|
||||
config.addDataSourceProperty("useServerPrepStmts", true);
|
||||
// default false - prefer use of local values for autocommit and
|
||||
// transaction isolation (alwaysSendSetIsolation) should only be enabled if always use the set* methods
|
||||
// instead of raw SQL
|
||||
// https://forums.mysql.com/read.php?39,626495,626512
|
||||
config.addDataSourceProperty("useLocalSessionState", true);
|
||||
// rewrite batched statements to a single statement, adding them behind each other
|
||||
// only useful for addBatch statements and inserts
|
||||
config.addDataSourceProperty("rewriteBatchedStatements", true);
|
||||
// cache result metadata
|
||||
config.addDataSourceProperty("cacheResultSetMetadata", true);
|
||||
// cache results of show variables and collation per URL
|
||||
config.addDataSourceProperty("cacheServerConfiguration", true);
|
||||
// default false - set auto commit only if not matching
|
||||
config.addDataSourceProperty("elideSetAutoCommits", true);
|
||||
|
||||
// default true - internal timers for idle calculation -> removes System.getCurrentTimeMillis call per query
|
||||
// Some platforms are slow on this and it could affect the throughput about 3% according to MySQL
|
||||
// performance gems presentation
|
||||
// In our case it can be useful to see the time in error messages
|
||||
// config.addDataSourceProperty("maintainTimeStats", false);
|
||||
}
|
||||
|
||||
config.setJdbcUrl(jdbcUrl);
|
||||
config.setJdbcUrl(JDBC_PROTOCOL + jdbcURL);
|
||||
this.dataSource = new HikariDataSource(config);
|
||||
}
|
||||
|
||||
@@ -136,28 +87,15 @@ public class AuthStorage {
|
||||
// choose surrogate PK(ID), because UUID can be null for offline players
|
||||
// if UUID is always Premium UUID we would have to update offline player entries on insert
|
||||
// name cannot be PK, because it can be changed for premium players
|
||||
String createDataStmt = "CREATE TABLE IF NOT EXISTS `" + PREMIUM_TABLE + "` ("
|
||||
+ "`UserID` INTEGER PRIMARY KEY AUTO_INCREMENT, "
|
||||
+ "`UUID` CHAR(36), "
|
||||
+ "`Name` VARCHAR(16) NOT NULL, "
|
||||
+ "`Premium` BOOLEAN NOT NULL, "
|
||||
+ "`LastIp` VARCHAR(255) NOT NULL, "
|
||||
+ "`LastLogin` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "
|
||||
//the premium shouldn't steal the cracked account by changing the name
|
||||
+ "UNIQUE (`Name`) "
|
||||
+ ')';
|
||||
|
||||
if (dataSource.getJdbcUrl().contains("sqlite")) {
|
||||
createDataStmt = createDataStmt.replace("AUTO_INCREMENT", "AUTOINCREMENT");
|
||||
}
|
||||
|
||||
//todo: add unique uuid index usage
|
||||
try (Connection con = dataSource.getConnection();
|
||||
Statement createStmt = con.createStatement()) {
|
||||
createStmt.executeUpdate(createDataStmt);
|
||||
createStmt.executeUpdate(CREATE_TABLE_STMT);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public StoredProfile loadProfile(String name) {
|
||||
try (Connection con = dataSource.getConnection();
|
||||
PreparedStatement loadStmt = con.prepareStatement(LOAD_BY_NAME)
|
||||
@@ -174,6 +112,7 @@ public class AuthStorage {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StoredProfile loadProfile(UUID uuid) {
|
||||
try (Connection con = dataSource.getConnection();
|
||||
PreparedStatement loadStmt = con.prepareStatement(LOAD_BY_UUID)) {
|
||||
@@ -205,6 +144,7 @@ public class AuthStorage {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(StoredProfile playerProfile) {
|
||||
try (Connection con = dataSource.getConnection()) {
|
||||
String uuid = playerProfile.getOptId().map(UUIDAdapter::toMojangId).orElse(null);
|
||||
@@ -245,6 +185,7 @@ public class AuthStorage {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
dataSource.close();
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package com.github.games647.fastlogin.core.storage;
|
||||
|
||||
import com.github.games647.fastlogin.core.StoredProfile;
|
||||
import com.github.games647.fastlogin.core.shared.FastLoginCore;
|
||||
import com.github.games647.fastlogin.core.shared.PlatformPlugin;
|
||||
import com.zaxxer.hikari.HikariConfig;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
public class SQLiteStorage extends SQLStorage {
|
||||
|
||||
private final Lock lock = new ReentrantLock();
|
||||
|
||||
public SQLiteStorage(FastLoginCore<?, ?, ?> core, String databasePath, HikariConfig config) {
|
||||
super(core,
|
||||
"sqlite://" + replacePathVariables(core.getPlugin(), databasePath),
|
||||
setParams(config));
|
||||
}
|
||||
|
||||
private static HikariConfig setParams(HikariConfig config) {
|
||||
config.setConnectionTestQuery("SELECT 1");
|
||||
config.setMaximumPoolSize(1);
|
||||
|
||||
//a try to fix https://www.spigotmc.org/threads/fastlogin.101192/page-26#post-1874647
|
||||
// format strings retrieved by the timestamp column to match them from MySQL
|
||||
config.addDataSourceProperty("date_string_format", "yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
// TODO: test first for compatibility
|
||||
// config.addDataSourceProperty("date_precision", "seconds");
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StoredProfile loadProfile(String name) {
|
||||
lock.lock();
|
||||
try {
|
||||
return super.loadProfile(name);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public StoredProfile loadProfile(UUID uuid) {
|
||||
lock.lock();
|
||||
try {
|
||||
return super.loadProfile(uuid);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(StoredProfile playerProfile) {
|
||||
lock.lock();
|
||||
try {
|
||||
super.save(playerProfile);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createTables() throws SQLException {
|
||||
try (Connection con = dataSource.getConnection();
|
||||
Statement createStmt = con.createStatement()) {
|
||||
// SQLite has a different syntax for auto increment
|
||||
createStmt.executeUpdate(CREATE_TABLE_STMT.replace("AUTO_INCREMENT", "AUTOINCREMENT"));
|
||||
}
|
||||
}
|
||||
|
||||
private static String replacePathVariables(PlatformPlugin<?> plugin, String input) {
|
||||
String pluginFolder = plugin.getPluginFolder().toAbsolutePath().toString();
|
||||
return input.replace("{pluginDir}", pluginFolder);
|
||||
}
|
||||
}
|
||||
@@ -5,14 +5,15 @@
|
||||
# You can access the newest config here:
|
||||
# https://github.com/games647/FastLogin/blob/main/core/src/main/resources/config.yml
|
||||
|
||||
# This a **very** simple anti bot protection. Recommendation is to use a a dedicated program to approach this
|
||||
# This a **very** simple anti bot protection. Recommendation is to use a dedicated program to approach this
|
||||
# problem. Low level firewalls like uwf (or iptables direct) are more efficient than a Minecraft plugin. TCP reverse
|
||||
# proxies could also be used and offload some work even to different host.
|
||||
#
|
||||
# The settings wil limit how many connections this plugin will handle. After hitting this limit. FastLogin will
|
||||
# completely ignore incoming connections. Effectively there will be no database requests and network requests.
|
||||
# Therefore auto logins won't be possible.
|
||||
# Therefore, auto logins won't be possible.
|
||||
anti-bot:
|
||||
enabled: true
|
||||
# Image the following like bucket. The following is total amount that is allowed in this bucket, while expire
|
||||
# means how long it takes for every entry to expire.
|
||||
# Total number of connections
|
||||
@@ -30,7 +31,7 @@ anti-bot:
|
||||
# -> cracked player cannot register an account for the premium player and so cannot the steal the account
|
||||
#
|
||||
# Furthermore the premium player check have to be made based on the player name
|
||||
# This means if a cracked player connects to the server and we request a paid account login from this player
|
||||
# This means if a cracked player connects to the server, we request a paid account login from this player
|
||||
# the player just disconnect and sees the message: 'bad login' or 'invalid session'
|
||||
# There is no way to change this message
|
||||
# For more information: https://github.com/games647/FastLogin#why-do-players-have-to-invoke-a-command
|
||||
@@ -56,9 +57,9 @@ secondAttemptCracked: false
|
||||
# New cracked players will be kicked from server. Good if you want switch from offline-mode to online-mode without
|
||||
# losing players!
|
||||
#
|
||||
# Existing cracked and premium players could still join your server. Moreover you could add playernames to a
|
||||
# allowlist.
|
||||
# So that these cracked players could join too although they are new players.
|
||||
# Existing cracked and premium players could still join your server. Moreover, you could add player names to an
|
||||
# allow-list.
|
||||
# So that these cracked players could join too, although they are new players.
|
||||
switchMode: false
|
||||
|
||||
# If this plugin detected that a player has a premium, it can also set the associated
|
||||
@@ -66,14 +67,14 @@ switchMode: false
|
||||
# the same player data (inventory, permissions, ...)
|
||||
#
|
||||
# Warning: This also means that the UUID will be different if the player is connecting
|
||||
# through a offline mode connection. This **could** cause plugin compatibility issues.
|
||||
# through an offline mode connection. This **could** cause plugin compatibility issues.
|
||||
#
|
||||
# This is a example and doesn't apply for every plugin.
|
||||
# This is an example and doesn't apply for every plugin.
|
||||
# Example: If you want to ban players who aren't online at the moment, the ban plugin will look
|
||||
# after a offline uuid associated to the player, because the server is in offline mode. Then the premium
|
||||
# after an offline uuid associated to the player, because the server is in offline mode. Then the premium
|
||||
# players could still join the server, because they have different UUID.
|
||||
#
|
||||
# Moreover you may want to convert the offline UUID to a premium UUID. This will ensure that the player
|
||||
# Moreover, you may want to convert the offline UUID to a premium UUID. This will ensure that the player
|
||||
# will have the same inventory, permissions, ... if they switched to premium authentication from offline/cracked
|
||||
# authentication.
|
||||
#
|
||||
@@ -82,7 +83,7 @@ premiumUuid: false
|
||||
|
||||
# This will make an additional check (only for player names which are not in the database) against the mojang servers
|
||||
# in order to get the premium UUID. If that premium UUID is in the database, we can assume on successful login that the
|
||||
# player changed it's username and we just update the name in the database.
|
||||
# player changed its username and then update the name in the database.
|
||||
# Examples:
|
||||
# #### Case 1
|
||||
# autoRegister = false
|
||||
@@ -97,10 +98,10 @@ premiumUuid: false
|
||||
#
|
||||
# Connect the Mojang API and check what UUID the player has (UUID exists => Paid Minecraft account). If that UUID is in
|
||||
# the database it's an **existing player** and FastLogin can **assume** the player is premium and changed the username.
|
||||
# If it's not in the database, it's a new player and **could be a cracked player**. So we just use a offline mode
|
||||
# If it's not in the database, it's a new player and **could be a cracked player**. So we just use an offline mode
|
||||
# authentication for this player.
|
||||
#
|
||||
# **Limitation**: Cracked players who uses the new username of a paid account cannot join the server if the database
|
||||
# **Limitation**: Cracked players who use the new username of a paid account cannot join the server if the database
|
||||
# contains the old name. (Example: The owner of the paid account no longer plays on the server, but changed the username
|
||||
# in the meanwhile).
|
||||
#
|
||||
@@ -110,7 +111,7 @@ premiumUuid: false
|
||||
#
|
||||
# We will always request a premium authentication if the username is unknown to us, but is in use by a paid Minecraft
|
||||
# account. This means it's kind of a more aggressive check like nameChangeCheck = true and autoRegister = false, because
|
||||
# it request a premium authentication which are completely new to us, that even the premium UUID is not in our database.
|
||||
# it requests a premium authentication which are completely new to us, that even the premium UUID is not in our database.
|
||||
#
|
||||
# **Limitation**: see below
|
||||
#
|
||||
@@ -121,14 +122,14 @@ premiumUuid: false
|
||||
# Based on autoRegister it checks if the player name is premium and login using a premium authentication. After that
|
||||
# fastlogin receives the premium UUID and can update the database record.
|
||||
#
|
||||
# **Limitation from autoRegister**: New offline players who uses the username of an existing Minecraft cannot join the
|
||||
# **Limitation from autoRegister**: New offline players who use the username of an existing Minecraft cannot join the
|
||||
# server.
|
||||
nameChangeCheck: false
|
||||
|
||||
# If your players have a premium account and a skin associated to their account, this plugin
|
||||
# can download the data and set it to the online player.
|
||||
#
|
||||
# Keep in mind that this will only works if the player:
|
||||
# Keep in mind that this will only work if the player:
|
||||
# * is the owner of the premium account
|
||||
# * the server connection is established through a premium connection (paid account authentication)
|
||||
# * has a skin
|
||||
@@ -188,7 +189,7 @@ auto-register-unknown: false
|
||||
autoLogin: true
|
||||
|
||||
# Floodgate configuration
|
||||
# Connecing through Floodgate requires player's to sign in via their Xbox Live account
|
||||
# Connecting through Floodgate requires player's to sign in via their Xbox Live account
|
||||
# !!!!!!!! WARNING: FLOODGATE SUPPORT IS AN EXPERIMENTAL FEATURE !!!!!!!!
|
||||
# Enabling any of these settings might lead to people gaining unauthorized access to other's accounts!
|
||||
|
||||
@@ -205,26 +206,26 @@ autoLogin: true
|
||||
# Enabling this might lead to people gaining unauthorized access to other's accounts!
|
||||
autoLoginFloodgate: false
|
||||
|
||||
# This enables Floodgate players to join the server, even if autoRegister is true and there's an existing
|
||||
# Java **PREMIUM** account with the same name
|
||||
# This enables Floodgate or Offline Geyser players to join the server, even if they are using the name of an
|
||||
# existing Java **PREMIUM** account (so someone has bought Minecraft with that username)
|
||||
#
|
||||
# Java and Bedrock players will get different UUIDs, so their inventories, location, etc. will be different.
|
||||
# However, some plugins (such as AuthMe) rely on names instead of UUIDs to identify a player which might cause issues.
|
||||
# In the case of AuthMe (and other auth plugins), both the Java and the Bedrock player will have the same password.
|
||||
#
|
||||
# To prevent conflits from two different players having the same name, it is highly recommended to use a 'username-prefix'
|
||||
# in floodgate/config.yml
|
||||
# Note: 'username-prefix' is currently broken when used with FastLogin and ProtocolLib. For more information visit:
|
||||
# https://github.com/games647/FastLogin/issues/493
|
||||
# A solution to this is to replace ProtocolLib with ProtocolSupport
|
||||
# To prevent conflicts from two different players having the same name, it is highly recommended using a
|
||||
# 'username-prefix' in floodgate/config.yml
|
||||
# Note: 'username-prefix' is currently broken when used with FastLogin and ProtocolLib.
|
||||
# A solution to this is to enable 'floodgatePrefixWorkaround' below.
|
||||
#
|
||||
# Possible values:
|
||||
# false: Check for Premium Java name conflicts as described in 'autoRegister'
|
||||
# Note: Linked players have the same name as their Java profile, so the Bedrock player will always conflict
|
||||
# false: Kick Bedrock players, if they are using an existing Premium Java account's name
|
||||
# Note: Linked Floodgate players have the same name as their Java profile, so the Bedrock player will always conflict
|
||||
# their own Java account's name. Therefore, setting this to false will prevent any linked player from joining.
|
||||
# true: Bypass 'autoRegister's name conflict checking
|
||||
# linked: Bedrock accounts linked to a Java account will be allowed to join with conflicting names
|
||||
# !!!!!!!! WARNING: FLOODGATE SUPPORT IS AN EXPERIMENTAL FEATURE !!!!!!!!
|
||||
# true: Bypass name conflict checking.
|
||||
# linked: Floodgate accounts linked to a Java account will be allowed to join with conflicting names
|
||||
# For Offline Geyser players, 'linked' works as 'false'
|
||||
# !!!!!!!! WARNING: FLOODGATE/GEYSER SUPPORT IS AN EXPERIMENTAL FEATURE !!!!!!!!
|
||||
# Enabling this might lead to people gaining unauthorized access to other's accounts!
|
||||
allowFloodgateNameConflict: false
|
||||
|
||||
@@ -242,6 +243,14 @@ allowFloodgateNameConflict: false
|
||||
# Enabling this might lead to people gaining unauthorized access to other's accounts!
|
||||
autoRegisterFloodgate: false
|
||||
|
||||
# Make FastLogin inject the Floodgate name prefixes, instead of Floodgate.
|
||||
# This can fix prefixes, if you are using Floodgate alongside ProtocolLib.
|
||||
# If either of those plugins are not installed, this option will have no effect.
|
||||
# For more information visit: https://github.com/games647/FastLogin/issues/493
|
||||
# !!!!!!!! WARNING: FLOODGATE SUPPORT IS AN EXPERIMENTAL FEATURE !!!!!!!!
|
||||
# Enabling this might lead to people gaining unauthorized access to other's accounts!
|
||||
floodgatePrefixWorkaround: false
|
||||
|
||||
# Database configuration
|
||||
# Recommended is the use of MariaDB (a better version of MySQL)
|
||||
|
||||
@@ -252,6 +261,7 @@ database: '{pluginDir}/FastLogin.db'
|
||||
|
||||
# MySQL/MariaDB
|
||||
# If you want to enable it uncomment only the lines below this not this line.
|
||||
# If on velocity use 'fastlogin.mariadb.jdbc.Driver' as driver
|
||||
#driver: 'com.mysql.jdbc.Driver'
|
||||
#host: '127.0.0.1'
|
||||
#port: 3306
|
||||
@@ -263,9 +273,19 @@ database: '{pluginDir}/FastLogin.db'
|
||||
#timeout: 30
|
||||
#lifetime: 30
|
||||
|
||||
# It's strongly recommended to enable SSL and setup a SSL certificate if the MySQL server isn't running on the same
|
||||
# machine
|
||||
## It's recommended to enable SSL if the MySQL server isn't running on the same host
|
||||
## This will encrypt the connection for secure transportation of the sql server password
|
||||
#useSSL: false
|
||||
## Verification requirements for the server cert,
|
||||
## Values: Required (unchecked SSL connection), VerifyCA (verify CA), VerifyFull (verify CA and matching hostname)
|
||||
#sslMode=Required
|
||||
## TLS is preferred for this technique, then your host stored certificate store will be used to verify the server cert
|
||||
## Similar to HTTPS. If that's not possible RSA can be used with the following options.
|
||||
## This allows to request the public RSA key from the server to encrypt the data to it. True would allow machine-in-the-
|
||||
## middle attacks.
|
||||
#allowPublicKeyRetrieval=false
|
||||
## Path to the RSA public key if key retrieval is forbidden
|
||||
#ServerRSAPublicKeyFile=
|
||||
|
||||
# HTTP proxies for connecting to the Mojang servers in order to check if the username of a player is premium.
|
||||
# This is a workaround to prevent rate-limiting by Mojang. These proxies will only be used once your server hit
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
# Second line
|
||||
# Third line'
|
||||
|
||||
# If you want to disable a message, you can just set it to a empty value.
|
||||
# If you want to disable a message, you can just set it to an empty value.
|
||||
# In this case no message will be sent
|
||||
# Example:
|
||||
# bla: ''
|
||||
@@ -55,7 +55,7 @@ auto-login: '&2Auto logged in'
|
||||
|
||||
# FastLogin attempted to auto register user. The user account is registered to protect it from cracked players
|
||||
# If FastLogin is respecting auth plugin IP limit - the registration may have failed, however the message is still displayed
|
||||
# The password can be used if the mojang servers are down and you still want your premium users to login (PLANNED)
|
||||
# The password can be used if the mojang servers are down, and you still want your premium users to login (PLANNED)
|
||||
auto-register: '&2Tried auto registering with password: &7%password&2. You may want change it?'
|
||||
|
||||
# GameProfile is not able to toggle the premium state of other players
|
||||
@@ -70,12 +70,12 @@ no-console: '&4You are not a player. You cannot toggle the premium state for YOU
|
||||
wait-on-proxy: '&6Sending request... (Do not forget to follow the BungeeCord setup guide)'
|
||||
|
||||
# When ProtocolLib is enabled and the plugin is unable to continue handling a login request after a requested premium
|
||||
# authentication. In this state the client expects a success packet with a encrypted connection or disconnect packet.
|
||||
# authentication. In this state the client expects a success packet with an encrypted connection or disconnect packet.
|
||||
# So we kick the player, if we cannot encrypt the connection. In other situation (example: premium name check),
|
||||
# the player will be just authenticated as cracked
|
||||
error-kick: '&4Error occurred'
|
||||
|
||||
# The server sends a verify token within the premium authentication request. If this doesn't match on response,
|
||||
# The server sends a verify-token within the premium authentication request. If this doesn't match on response,
|
||||
# it could be another client sending malicious packets
|
||||
invalid-verify-token: '&4Invalid token'
|
||||
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.github.games647.fastlogin.core;
|
||||
|
||||
import com.google.common.base.Ticker;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
public class FakeTicker extends Ticker {
|
||||
|
||||
private long timestamp;
|
||||
|
||||
public FakeTicker(long initial) {
|
||||
timestamp = initial;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long read() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public void add(Duration duration) {
|
||||
timestamp += duration.toNanos();
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,7 @@
|
||||
*/
|
||||
package com.github.games647.fastlogin.core;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.junit.Test;
|
||||
@@ -43,14 +44,34 @@ public class RateLimiterTest {
|
||||
public void allowExpire() throws InterruptedException {
|
||||
int size = 3;
|
||||
|
||||
FakeTicker ticker = new FakeTicker(5_000_000L);
|
||||
|
||||
// run twice the size to fill it first and then test it
|
||||
RateLimiter rateLimiter = new RateLimiter(size, 0);
|
||||
TickingRateLimiter rateLimiter = new TickingRateLimiter(ticker, size, 0);
|
||||
for (int i = 0; i < size; i++) {
|
||||
assertTrue("Filling up", rateLimiter.tryAcquire());
|
||||
}
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
Thread.sleep(1);
|
||||
ticker.add(Duration.ofSeconds(1));
|
||||
assertTrue("Should be expired", rateLimiter.tryAcquire());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void allowExpireNegative() throws InterruptedException {
|
||||
int size = 3;
|
||||
|
||||
FakeTicker ticker = new FakeTicker(-5_000_000L);
|
||||
|
||||
// run twice the size to fill it first and then test it
|
||||
TickingRateLimiter rateLimiter = new TickingRateLimiter(ticker, size, 0);
|
||||
for (int i = 0; i < size; i++) {
|
||||
assertTrue("Filling up", rateLimiter.tryAcquire());
|
||||
}
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
ticker.add(Duration.ofSeconds(1));
|
||||
assertTrue("Should be expired", rateLimiter.tryAcquire());
|
||||
}
|
||||
}
|
||||
@@ -59,11 +80,31 @@ public class RateLimiterTest {
|
||||
* Too many requests
|
||||
*/
|
||||
@Test
|
||||
public void shoudBlock() {
|
||||
public void shouldBlock() {
|
||||
int size = 3;
|
||||
|
||||
FakeTicker ticker = new FakeTicker(5_000_000L);
|
||||
|
||||
// fill the size
|
||||
RateLimiter rateLimiter = new RateLimiter(size, TimeUnit.SECONDS.toMillis(30));
|
||||
TickingRateLimiter rateLimiter = new TickingRateLimiter(ticker, size, TimeUnit.SECONDS.toMillis(30));
|
||||
for (int i = 0; i < size; i++) {
|
||||
assertTrue("Filling up", rateLimiter.tryAcquire());
|
||||
}
|
||||
|
||||
assertFalse("Should be full and no entry should be expired", rateLimiter.tryAcquire());
|
||||
}
|
||||
|
||||
/**
|
||||
* Too many requests
|
||||
*/
|
||||
@Test
|
||||
public void shouldBlockNegative() {
|
||||
int size = 3;
|
||||
|
||||
FakeTicker ticker = new FakeTicker(-5_000_000L);
|
||||
|
||||
// fill the size
|
||||
TickingRateLimiter rateLimiter = new TickingRateLimiter(ticker, size, TimeUnit.SECONDS.toMillis(30));
|
||||
for (int i = 0; i < size; i++) {
|
||||
assertTrue("Filling up", rateLimiter.tryAcquire());
|
||||
}
|
||||
@@ -76,17 +117,40 @@ public class RateLimiterTest {
|
||||
*/
|
||||
@Test
|
||||
public void blockedNotAdded() throws InterruptedException {
|
||||
FakeTicker ticker = new FakeTicker(5_000_000L);
|
||||
|
||||
// fill the size - 100ms should be reasonable high
|
||||
RateLimiter rateLimiter = new RateLimiter(1, 100);
|
||||
TickingRateLimiter rateLimiter = new TickingRateLimiter(ticker, 1, 100);
|
||||
assertTrue("Filling up", rateLimiter.tryAcquire());
|
||||
|
||||
Thread.sleep(50);
|
||||
ticker.add(Duration.ofMillis(50));
|
||||
|
||||
// still is full - should fail
|
||||
assertFalse("Expired too early", rateLimiter.tryAcquire());
|
||||
|
||||
// wait the remaining time and add a threshold, because
|
||||
Thread.sleep(50 + THRESHOLD_MILLI);
|
||||
ticker.add(Duration.ofMillis(50));
|
||||
assertTrue("Request not released", rateLimiter.tryAcquire());
|
||||
}
|
||||
|
||||
/**
|
||||
* Blocked attempts shouldn't replace existing ones.
|
||||
*/
|
||||
@Test
|
||||
public void blockedNotAddedNegative() throws InterruptedException {
|
||||
FakeTicker ticker = new FakeTicker(-5_000_000L);
|
||||
|
||||
// fill the size - 100ms should be reasonable high
|
||||
TickingRateLimiter rateLimiter = new TickingRateLimiter(ticker, 1, 100);
|
||||
assertTrue("Filling up", rateLimiter.tryAcquire());
|
||||
|
||||
ticker.add(Duration.ofMillis(50));
|
||||
|
||||
// still is full - should fail
|
||||
assertFalse("Expired too early", rateLimiter.tryAcquire());
|
||||
|
||||
// wait the remaining time and add a threshold, because
|
||||
ticker.add(Duration.ofMillis(50));
|
||||
assertTrue("Request not released", rateLimiter.tryAcquire());
|
||||
}
|
||||
}
|
||||
|
||||
12
pom.xml
12
pom.xml
@@ -30,7 +30,7 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>com.github.games647</groupId>
|
||||
<!--This have to be in lowercase because it's used by plugin.yml-->
|
||||
<!--This has to be in lowercase because it's used by plugin.yml-->
|
||||
<artifactId>fastlogin</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
|
||||
<url>https://www.spigotmc.org/resources/fastlogin.14153/</url>
|
||||
<description>
|
||||
Automatically login premium (paid accounts) player on a offline mode server
|
||||
Automatically login premium (paid accounts) player on an offline mode server
|
||||
</description>
|
||||
|
||||
<properties>
|
||||
@@ -51,12 +51,16 @@
|
||||
<java.version>1.8</java.version>
|
||||
<maven.compiler.source>${java.version}</maven.compiler.source>
|
||||
<maven.compiler.target>${java.version}</maven.compiler.target>
|
||||
|
||||
<floodgate.version>2.0-SNAPSHOT</floodgate.version>
|
||||
<geyser.version>2.0.0-SNAPSHOT</geyser.version>
|
||||
</properties>
|
||||
|
||||
<modules>
|
||||
<module>core</module>
|
||||
<module>bukkit</module>
|
||||
<module>bungee</module>
|
||||
<module>velocity</module>
|
||||
</modules>
|
||||
|
||||
<!--Deployment configuration for the Maven repository-->
|
||||
@@ -79,7 +83,7 @@
|
||||
<plugin>
|
||||
<groupId>pl.project13.maven</groupId>
|
||||
<artifactId>git-commit-id-plugin</artifactId>
|
||||
<version>4.0.4</version>
|
||||
<version>4.9.10</version>
|
||||
<configuration>
|
||||
<failOnNoGitDirectory>false</failOnNoGitDirectory>
|
||||
</configuration>
|
||||
@@ -101,7 +105,7 @@
|
||||
<licenseSets>
|
||||
<licenseSet>
|
||||
<multi>
|
||||
<!-- Machine readable representation -->
|
||||
<!-- Machine-readable representation -->
|
||||
<preamble>SPDX-License-Identifier: MIT</preamble>
|
||||
<header>LICENSE</header>
|
||||
</multi>
|
||||
|
||||
135
velocity/pom.xml
Normal file
135
velocity/pom.xml
Normal file
@@ -0,0 +1,135 @@
|
||||
<!--
|
||||
|
||||
SPDX-License-Identifier: MIT
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015-2021 <Your name and contributors>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
-->
|
||||
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>com.github.games647</groupId>
|
||||
<artifactId>fastlogin</artifactId>
|
||||
<version>1.11-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<!--This have to be in lowercase because it's used by plugin.yml-->
|
||||
<artifactId>fastlogin.velocity</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<!--Represents the main plugin-->
|
||||
<name>FastLoginVelocity</name>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>templating-maven-plugin</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>filter-src</id>
|
||||
<goals>
|
||||
<goal>filter-sources</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.2.4</version>
|
||||
<configuration>
|
||||
<createDependencyReducedPom>false</createDependencyReducedPom>
|
||||
<shadedArtifactAttached>false</shadedArtifactAttached>
|
||||
<relocations>
|
||||
<relocation>
|
||||
<pattern>com.zaxxer.hikari</pattern>
|
||||
<shadedPattern>fastlogin.hikari</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>net.md_5.bungee.config</pattern>
|
||||
<shadedPattern>fastlogin.config</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>org.mariadb</pattern>
|
||||
<shadedPattern>fastlogin.mariadb</shadedPattern>
|
||||
</relocation>
|
||||
</relocations>
|
||||
<artifactSet>
|
||||
<!--org.slf4j is part of velocity api and runtime,
|
||||
shading and relocating it causes logger injection to fail-->
|
||||
<excludes>
|
||||
<exclude>org.slf4j:*</exclude>
|
||||
<exclude>com.google.code.gson:gson</exclude>
|
||||
<!-- Ships the same old version -->
|
||||
<exclude>com.google.guava:guava</exclude>
|
||||
</excludes>
|
||||
</artifactSet>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>velocity</id>
|
||||
<url>https://nexus.velocitypowered.com/repository/maven-public/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
<!--Common plugin component-->
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>fastlogin.core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!--Velocity api-->
|
||||
<dependency>
|
||||
<groupId>com.velocitypowered</groupId>
|
||||
<artifactId>velocity-api</artifactId>
|
||||
<version>3.1.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!--Velocity does not ship any database driver-->
|
||||
<dependency>
|
||||
<groupId>org.mariadb.jdbc</groupId>
|
||||
<artifactId>mariadb-java-client</artifactId>
|
||||
<version>3.0.3</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.github.games647.fastlogin.velocity;
|
||||
|
||||
public class PomData {
|
||||
public static final String DISPLAY_NAME = "${project.name}";
|
||||
public static final String NAME = "${project.parent.artifactId}";
|
||||
public static final String VERSION = "${project.version}-${git.commit.id.abbrev}";
|
||||
public static final String DESCRIPTION = "${project.parent.description}";
|
||||
public static final String URL = "${project.parent.url}";
|
||||
}
|
||||
@@ -0,0 +1,205 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package com.github.games647.fastlogin.velocity;
|
||||
|
||||
import com.github.games647.fastlogin.core.AsyncScheduler;
|
||||
import com.github.games647.fastlogin.core.hooks.bedrock.BedrockService;
|
||||
import com.github.games647.fastlogin.core.message.ChangePremiumMessage;
|
||||
import com.github.games647.fastlogin.core.message.ChannelMessage;
|
||||
import com.github.games647.fastlogin.core.message.SuccessMessage;
|
||||
import com.github.games647.fastlogin.core.shared.FastLoginCore;
|
||||
import com.github.games647.fastlogin.core.shared.PlatformPlugin;
|
||||
import com.github.games647.fastlogin.velocity.listener.ConnectListener;
|
||||
import com.github.games647.fastlogin.velocity.listener.PluginMessageListener;
|
||||
import com.google.common.collect.MapMaker;
|
||||
import com.google.common.io.ByteArrayDataOutput;
|
||||
import com.google.common.io.ByteStreams;
|
||||
import com.google.inject.Inject;
|
||||
import com.velocitypowered.api.command.CommandSource;
|
||||
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.Player;
|
||||
import com.velocitypowered.api.proxy.ProxyServer;
|
||||
import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier;
|
||||
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
|
||||
//TODO: Support for floodgate
|
||||
@Plugin(id = PomData.NAME, name = PomData.DISPLAY_NAME, description = PomData.DESCRIPTION, url = PomData.URL,
|
||||
version = PomData.VERSION, authors = {"games647", "https://github.com/games647/FastLogin/graphs/contributors"})
|
||||
public class FastLoginVelocity implements PlatformPlugin<CommandSource> {
|
||||
|
||||
private final ProxyServer server;
|
||||
private final Path dataDirectory;
|
||||
private final Logger logger;
|
||||
private final ConcurrentMap<InetSocketAddress, VelocityLoginSession> session = new MapMaker().weakKeys().makeMap();
|
||||
private static final String PROXY_ID_fILE = "proxyId.txt";
|
||||
|
||||
private FastLoginCore<Player, CommandSource, FastLoginVelocity> core;
|
||||
private AsyncScheduler scheduler;
|
||||
private UUID proxyId;
|
||||
|
||||
@Inject
|
||||
public FastLoginVelocity(ProxyServer server, Logger logger, @DataDirectory Path dataDirectory) {
|
||||
this.server = server;
|
||||
this.logger = logger;
|
||||
this.dataDirectory = dataDirectory;
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onProxyInitialization(ProxyInitializeEvent event) {
|
||||
scheduler = new AsyncScheduler(logger, getThreadFactory());
|
||||
core = new FastLoginCore<>(this);
|
||||
core.load();
|
||||
loadOrGenerateProxyId();
|
||||
if (!core.setupDatabase() || proxyId == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
server.getEventManager().register(this, new ConnectListener(this, core.getRateLimiter()));
|
||||
server.getEventManager().register(this, new PluginMessageListener(this));
|
||||
server.getChannelRegistrar().register(MinecraftChannelIdentifier.create(getName(), ChangePremiumMessage.CHANGE_CHANNEL));
|
||||
server.getChannelRegistrar().register(MinecraftChannelIdentifier.create(getName(), SuccessMessage.SUCCESS_CHANNEL));
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onProxyShutdown(ProxyShutdownEvent event) {
|
||||
if (core != null) {
|
||||
core.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return PomData.NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path getPluginFolder() {
|
||||
return dataDirectory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Logger getLog() {
|
||||
return logger;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(CommandSource receiver, String message) {
|
||||
receiver.sendMessage(LegacyComponentSerializer.legacyAmpersand().deserialize(message));
|
||||
}
|
||||
|
||||
@Override
|
||||
public AsyncScheduler getScheduler() {
|
||||
return scheduler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPluginInstalled(String name) {
|
||||
return server.getPluginManager().isLoaded(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BedrockService<?> getBedrockService() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public FastLoginCore<Player, CommandSource, FastLoginVelocity> getCore() {
|
||||
return core;
|
||||
}
|
||||
|
||||
public ConcurrentMap<InetSocketAddress, VelocityLoginSession> getSession() {
|
||||
return session;
|
||||
}
|
||||
|
||||
public ProxyServer getProxy() {
|
||||
return server;
|
||||
}
|
||||
|
||||
public void sendPluginMessage(RegisteredServer server, ChannelMessage message) {
|
||||
if (server != null) {
|
||||
ByteArrayDataOutput dataOutput = ByteStreams.newDataOutput();
|
||||
message.writeTo(dataOutput);
|
||||
|
||||
MinecraftChannelIdentifier channel = MinecraftChannelIdentifier.create(getName(), message.getChannelName());
|
||||
server.sendPluginMessage(channel, dataOutput.toByteArray());
|
||||
}
|
||||
}
|
||||
|
||||
private void loadOrGenerateProxyId() {
|
||||
Path idFile = dataDirectory.resolve(PROXY_ID_fILE);
|
||||
boolean shouldGenerate = false;
|
||||
|
||||
if (Files.exists(idFile)) {
|
||||
try {
|
||||
List<String> lines = Files.readAllLines(idFile, StandardCharsets.UTF_8);
|
||||
if (lines.isEmpty()) {
|
||||
shouldGenerate = true;
|
||||
} else {
|
||||
proxyId = UUID.fromString(lines.get(0));
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.error("Unable to load proxy id from '{}'", idFile.toAbsolutePath());
|
||||
logger.error("Detailed exception:", e);
|
||||
} catch (IllegalArgumentException e) {
|
||||
logger.error("'{}' contains an invalid uuid! FastLogin will not work without a valid id.", idFile.toAbsolutePath());
|
||||
}
|
||||
} else {
|
||||
shouldGenerate = true;
|
||||
}
|
||||
|
||||
if (shouldGenerate) {
|
||||
proxyId = UUID.randomUUID();
|
||||
try {
|
||||
Files.write(idFile, Collections.singletonList(proxyId.toString()), StandardOpenOption.CREATE);
|
||||
} catch (IOException e) {
|
||||
logger.error("Unable to save proxy id to '{}'", idFile.toAbsolutePath());
|
||||
logger.error("Detailed exception:", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public UUID getProxyId() {
|
||||
return proxyId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package com.github.games647.fastlogin.velocity;
|
||||
|
||||
import com.github.games647.fastlogin.core.StoredProfile;
|
||||
import com.github.games647.fastlogin.core.shared.LoginSession;
|
||||
|
||||
public class VelocityLoginSession extends LoginSession {
|
||||
private boolean alreadySaved;
|
||||
private boolean alreadyLogged;
|
||||
|
||||
public VelocityLoginSession(String requestUsername, boolean registered, StoredProfile profile) {
|
||||
super(requestUsername, registered, profile);
|
||||
}
|
||||
|
||||
public synchronized void setRegistered(boolean registered) {
|
||||
this.registered = registered;
|
||||
}
|
||||
|
||||
public synchronized boolean isAlreadySaved() {
|
||||
return alreadySaved;
|
||||
}
|
||||
|
||||
public synchronized void setAlreadySaved(boolean alreadySaved) {
|
||||
this.alreadySaved = alreadySaved;
|
||||
}
|
||||
|
||||
public synchronized boolean isAlreadyLogged() {
|
||||
return alreadyLogged;
|
||||
}
|
||||
|
||||
public synchronized void setAlreadyLogged(boolean alreadyLogged) {
|
||||
this.alreadyLogged = alreadyLogged;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized String toString() {
|
||||
return this.getClass().getSimpleName() + '{' +
|
||||
"alreadySaved=" + alreadySaved +
|
||||
", alreadyLogged=" + alreadyLogged +
|
||||
", registered=" + registered +
|
||||
"} " + super.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package com.github.games647.fastlogin.velocity;
|
||||
|
||||
import com.github.games647.fastlogin.core.shared.LoginSource;
|
||||
import com.velocitypowered.api.event.connection.PreLoginEvent;
|
||||
import com.velocitypowered.api.proxy.InboundConnection;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
|
||||
public class VelocityLoginSource implements LoginSource {
|
||||
|
||||
private final InboundConnection connection;
|
||||
private final PreLoginEvent preLoginEvent;
|
||||
|
||||
public VelocityLoginSource(InboundConnection connection, PreLoginEvent preLoginEvent) {
|
||||
this.connection = connection;
|
||||
this.preLoginEvent = preLoginEvent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enableOnlinemode() {
|
||||
preLoginEvent.setResult(PreLoginEvent.PreLoginComponentResult.forceOnlineMode());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void kick(String message) {
|
||||
if (message == null) {
|
||||
preLoginEvent.setResult(PreLoginEvent.PreLoginComponentResult.denied(
|
||||
Component.text("Kicked").color(NamedTextColor.WHITE)));
|
||||
} else {
|
||||
preLoginEvent.setResult(PreLoginEvent.PreLoginComponentResult.denied(
|
||||
LegacyComponentSerializer.legacyAmpersand().deserialize(message)));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public InetSocketAddress getAddress() {
|
||||
return connection.getRemoteAddress();
|
||||
}
|
||||
|
||||
public InboundConnection getConnection() {
|
||||
return connection;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package com.github.games647.fastlogin.velocity.event;
|
||||
|
||||
import com.github.games647.fastlogin.core.StoredProfile;
|
||||
import com.github.games647.fastlogin.core.shared.LoginSession;
|
||||
import com.github.games647.fastlogin.core.shared.event.FastLoginAutoLoginEvent;
|
||||
import com.velocitypowered.api.event.ResultedEvent;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class VelocityFastLoginAutoLoginEvent
|
||||
implements FastLoginAutoLoginEvent, ResultedEvent<ResultedEvent.GenericResult> {
|
||||
|
||||
private final LoginSession session;
|
||||
private final StoredProfile profile;
|
||||
private boolean cancelled;
|
||||
|
||||
public VelocityFastLoginAutoLoginEvent(LoginSession session, StoredProfile profile) {
|
||||
this.session = session;
|
||||
this.profile = profile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoginSession getSession() {
|
||||
return session;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StoredProfile getProfile() {
|
||||
return profile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return cancelled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCancelled(boolean cancelled) {
|
||||
this.cancelled = cancelled;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public GenericResult getResult() {
|
||||
return cancelled ? GenericResult.denied(): GenericResult.allowed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setResult(GenericResult result) {
|
||||
cancelled = Objects.requireNonNull(result) != GenericResult.allowed();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package com.github.games647.fastlogin.velocity.event;
|
||||
|
||||
import com.github.games647.fastlogin.core.StoredProfile;
|
||||
import com.github.games647.fastlogin.core.shared.LoginSource;
|
||||
import com.github.games647.fastlogin.core.shared.event.FastLoginPreLoginEvent;
|
||||
|
||||
public class VelocityFastLoginPreLoginEvent implements FastLoginPreLoginEvent {
|
||||
|
||||
private final String username;
|
||||
private final LoginSource source;
|
||||
private final StoredProfile profile;
|
||||
|
||||
public VelocityFastLoginPreLoginEvent(String username, LoginSource source, StoredProfile profile) {
|
||||
this.username = username;
|
||||
this.source = source;
|
||||
this.profile = profile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoginSource getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StoredProfile getProfile() {
|
||||
return profile;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package com.github.games647.fastlogin.velocity.event;
|
||||
|
||||
import com.github.games647.fastlogin.core.StoredProfile;
|
||||
import com.github.games647.fastlogin.core.shared.event.FastLoginPremiumToggleEvent;
|
||||
|
||||
public class VelocityFastLoginPremiumToggleEvent implements FastLoginPremiumToggleEvent {
|
||||
|
||||
private final StoredProfile profile;
|
||||
private final PremiumToggleReason reason;
|
||||
|
||||
public VelocityFastLoginPremiumToggleEvent(StoredProfile profile, PremiumToggleReason reason) {
|
||||
this.profile = profile;
|
||||
this.reason = reason;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StoredProfile getProfile() {
|
||||
return profile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PremiumToggleReason getReason() {
|
||||
return reason;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package com.github.games647.fastlogin.velocity.listener;
|
||||
|
||||
import com.github.games647.craftapi.UUIDAdapter;
|
||||
import com.github.games647.fastlogin.core.RateLimiter;
|
||||
import com.github.games647.fastlogin.core.StoredProfile;
|
||||
import com.github.games647.fastlogin.core.shared.LoginSession;
|
||||
import com.github.games647.fastlogin.velocity.FastLoginVelocity;
|
||||
import com.github.games647.fastlogin.velocity.VelocityLoginSession;
|
||||
import com.github.games647.fastlogin.velocity.task.AsyncPremiumCheck;
|
||||
import com.github.games647.fastlogin.velocity.task.ForceLoginTask;
|
||||
import com.velocitypowered.api.event.Continuation;
|
||||
import com.velocitypowered.api.event.Subscribe;
|
||||
import com.velocitypowered.api.event.connection.DisconnectEvent;
|
||||
import com.velocitypowered.api.event.connection.PreLoginEvent;
|
||||
import com.velocitypowered.api.event.player.GameProfileRequestEvent;
|
||||
import com.velocitypowered.api.event.player.ServerConnectedEvent;
|
||||
import com.velocitypowered.api.proxy.InboundConnection;
|
||||
import com.velocitypowered.api.proxy.Player;
|
||||
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
||||
import com.velocitypowered.api.util.GameProfile;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class ConnectListener {
|
||||
|
||||
private final FastLoginVelocity plugin;
|
||||
private final RateLimiter rateLimiter;
|
||||
|
||||
public ConnectListener(FastLoginVelocity plugin, RateLimiter rateLimiter) {
|
||||
this.plugin = plugin;
|
||||
this.rateLimiter = rateLimiter;
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onPreLogin(PreLoginEvent preLoginEvent, Continuation continuation) {
|
||||
if (!preLoginEvent.getResult().isAllowed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
InboundConnection connection = preLoginEvent.getConnection();
|
||||
if (!rateLimiter.tryAcquire()) {
|
||||
plugin.getLog().warn("Simple Anti-Bot join limit - Ignoring {}", connection);
|
||||
return;
|
||||
}
|
||||
|
||||
String username = preLoginEvent.getUsername();
|
||||
plugin.getLog().info("Incoming login request for {} from {}", username, connection.getRemoteAddress());
|
||||
|
||||
Runnable asyncPremiumCheck = new AsyncPremiumCheck(plugin, connection, username, continuation, preLoginEvent);
|
||||
plugin.getScheduler().runAsync(asyncPremiumCheck);
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onGameprofileRequest(GameProfileRequestEvent event) {
|
||||
if (event.isOnlineMode()) {
|
||||
LoginSession session = plugin.getSession().get(event.getConnection().getRemoteAddress());
|
||||
|
||||
UUID verifiedUUID = event.getGameProfile().getId();
|
||||
String verifiedUsername = event.getUsername();
|
||||
session.setUuid(verifiedUUID);
|
||||
session.setVerifiedUsername(verifiedUsername);
|
||||
|
||||
StoredProfile playerProfile = session.getProfile();
|
||||
playerProfile.setId(verifiedUUID);
|
||||
if (!plugin.getCore().getConfig().get("premiumUuid", true)) {
|
||||
UUID offlineUUID = UUIDAdapter.generateOfflineId(playerProfile.getName());
|
||||
event.setGameProfile(event.getGameProfile().withId(offlineUUID));
|
||||
plugin.getLog().info("Overridden UUID from {} to {} (based of {}) on {}",
|
||||
verifiedUUID, offlineUUID, verifiedUsername, event.getConnection());
|
||||
}
|
||||
|
||||
if (!plugin.getCore().getConfig().get("forwardSkin", true)) {
|
||||
event.setGameProfile(event.getGameProfile().withProperties(removeSkin(event.getGameProfile().getProperties())));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<GameProfile.Property> removeSkin(List<GameProfile.Property> oldProperties) {
|
||||
List<GameProfile.Property> newProperties = new ArrayList<>(oldProperties.size() - 1);
|
||||
for (GameProfile.Property property : oldProperties) {
|
||||
if (!"textures".equals(property.getName()))
|
||||
newProperties.add(property);
|
||||
}
|
||||
|
||||
return newProperties;
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onServerConnected(ServerConnectedEvent serverConnectedEvent) {
|
||||
Player player = serverConnectedEvent.getPlayer();
|
||||
RegisteredServer server = serverConnectedEvent.getServer();
|
||||
|
||||
VelocityLoginSession session = plugin.getSession().get(player.getRemoteAddress());
|
||||
if (session == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// delay sending force command, because Paper will process the login event asynchronously
|
||||
// In this case it means that the force command (plugin message) is already received and processed while
|
||||
// player is still in the login phase and reported to be offline.
|
||||
Runnable loginTask = new ForceLoginTask(plugin.getCore(), player, server, session);
|
||||
plugin.getProxy().getScheduler()
|
||||
.buildTask(plugin, () -> plugin.getScheduler().runAsync(loginTask))
|
||||
.delay(1L, TimeUnit.SECONDS) // Delay at least one second, otherwise the login command can be missed
|
||||
.schedule();
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onDisconnect(DisconnectEvent disconnectEvent) {
|
||||
Player player = disconnectEvent.getPlayer();
|
||||
plugin.getCore().getPendingConfirms().remove(player.getUniqueId());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package com.github.games647.fastlogin.velocity.listener;
|
||||
|
||||
import com.github.games647.fastlogin.core.StoredProfile;
|
||||
import com.github.games647.fastlogin.core.message.ChangePremiumMessage;
|
||||
import com.github.games647.fastlogin.core.message.SuccessMessage;
|
||||
import com.github.games647.fastlogin.core.shared.FastLoginCore;
|
||||
import com.github.games647.fastlogin.velocity.FastLoginVelocity;
|
||||
import com.github.games647.fastlogin.velocity.VelocityLoginSession;
|
||||
import com.github.games647.fastlogin.velocity.task.AsyncToggleMessage;
|
||||
import com.google.common.io.ByteArrayDataInput;
|
||||
import com.google.common.io.ByteStreams;
|
||||
import com.velocitypowered.api.command.CommandSource;
|
||||
import com.velocitypowered.api.event.Subscribe;
|
||||
import com.velocitypowered.api.event.connection.PluginMessageEvent;
|
||||
import com.velocitypowered.api.proxy.Player;
|
||||
import com.velocitypowered.api.proxy.ServerConnection;
|
||||
import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class PluginMessageListener {
|
||||
|
||||
private final FastLoginVelocity plugin;
|
||||
|
||||
private final String successChannel;
|
||||
private final String changeChannel;
|
||||
|
||||
public PluginMessageListener(FastLoginVelocity plugin) {
|
||||
this.plugin = plugin;
|
||||
|
||||
this.successChannel = MinecraftChannelIdentifier.create(plugin.getName(), SuccessMessage.SUCCESS_CHANNEL).getId();
|
||||
this.changeChannel = MinecraftChannelIdentifier.create(plugin.getName(), ChangePremiumMessage.CHANGE_CHANNEL).getId();
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onPluginMessage(PluginMessageEvent pluginMessageEvent) {
|
||||
String channel = pluginMessageEvent.getIdentifier().getId();
|
||||
if (!pluginMessageEvent.getResult().isAllowed() || !channel.startsWith(plugin.getName().toLowerCase())) {
|
||||
return;
|
||||
}
|
||||
|
||||
//the client shouldn't be able to read the messages in order to know something about server internal states
|
||||
//moreover the client shouldn't be able fake a running premium check by sending the result message
|
||||
pluginMessageEvent.setResult(PluginMessageEvent.ForwardResult.handled());
|
||||
|
||||
if (!(pluginMessageEvent.getSource() instanceof ServerConnection)) {
|
||||
//check if the message is sent from the server
|
||||
return;
|
||||
}
|
||||
|
||||
//so that we can safely process this in the background
|
||||
byte[] data = Arrays.copyOf(pluginMessageEvent.getData(), pluginMessageEvent.getData().length);
|
||||
Player forPlayer = (Player) pluginMessageEvent.getTarget();
|
||||
|
||||
plugin.getScheduler().runAsync(() -> readMessage(forPlayer, channel, data));
|
||||
}
|
||||
|
||||
private void readMessage(Player forPlayer, String channel, byte[] data) {
|
||||
FastLoginCore<Player, CommandSource, FastLoginVelocity> core = plugin.getCore();
|
||||
|
||||
ByteArrayDataInput dataInput = ByteStreams.newDataInput(data);
|
||||
if (successChannel.equals(channel)) {
|
||||
onSuccessMessage(forPlayer);
|
||||
} else if (changeChannel.equals(channel)) {
|
||||
ChangePremiumMessage changeMessage = new ChangePremiumMessage();
|
||||
changeMessage.readFrom(dataInput);
|
||||
|
||||
String playerName = changeMessage.getPlayerName();
|
||||
boolean isSourceInvoker = changeMessage.isSourceInvoker();
|
||||
if (changeMessage.shouldEnable()) {
|
||||
if (playerName.equals(forPlayer.getUsername()) && plugin.getCore().getConfig().get("premium-warning", true)
|
||||
&& !core.getPendingConfirms().contains(forPlayer.getUniqueId())) {
|
||||
String message = core.getMessage("premium-warning");
|
||||
forPlayer.sendMessage(LegacyComponentSerializer.legacyAmpersand().deserialize(message));
|
||||
core.getPendingConfirms().add(forPlayer.getUniqueId());
|
||||
return;
|
||||
}
|
||||
|
||||
core.getPendingConfirms().remove(forPlayer.getUniqueId());
|
||||
Runnable task = new AsyncToggleMessage(core, forPlayer, playerName, true, isSourceInvoker);
|
||||
plugin.getScheduler().runAsync(task);
|
||||
} else {
|
||||
Runnable task = new AsyncToggleMessage(core, forPlayer, playerName, false, isSourceInvoker);
|
||||
plugin.getScheduler().runAsync(task);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void onSuccessMessage(Player forPlayer) {
|
||||
if (forPlayer.isOnlineMode()){
|
||||
//bukkit module successfully received and force logged in the user
|
||||
//update only on success to prevent corrupt data
|
||||
VelocityLoginSession loginSession = plugin.getSession().get(forPlayer.getRemoteAddress());
|
||||
StoredProfile playerProfile = loginSession.getProfile();
|
||||
loginSession.setRegistered(true);
|
||||
if (!loginSession.isAlreadySaved()) {
|
||||
playerProfile.setPremium(true);
|
||||
plugin.getCore().getStorage().save(playerProfile);
|
||||
loginSession.setAlreadySaved(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package com.github.games647.fastlogin.velocity.task;
|
||||
|
||||
import com.github.games647.fastlogin.core.StoredProfile;
|
||||
import com.github.games647.fastlogin.core.shared.JoinManagement;
|
||||
import com.github.games647.fastlogin.core.shared.event.FastLoginPreLoginEvent;
|
||||
import com.github.games647.fastlogin.velocity.FastLoginVelocity;
|
||||
import com.github.games647.fastlogin.velocity.VelocityLoginSession;
|
||||
import com.github.games647.fastlogin.velocity.VelocityLoginSource;
|
||||
import com.github.games647.fastlogin.velocity.event.VelocityFastLoginPreLoginEvent;
|
||||
import com.velocitypowered.api.command.CommandSource;
|
||||
import com.velocitypowered.api.event.Continuation;
|
||||
import com.velocitypowered.api.event.connection.PreLoginEvent;
|
||||
import com.velocitypowered.api.proxy.InboundConnection;
|
||||
import com.velocitypowered.api.proxy.Player;
|
||||
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
public class AsyncPremiumCheck extends JoinManagement<Player, CommandSource, VelocityLoginSource>
|
||||
implements Runnable {
|
||||
|
||||
private final FastLoginVelocity plugin;
|
||||
private final String username;
|
||||
private final Continuation continuation;
|
||||
private final PreLoginEvent preLoginEvent;
|
||||
private final InboundConnection connection;
|
||||
|
||||
public AsyncPremiumCheck(FastLoginVelocity plugin, InboundConnection connection, String username, Continuation continuation, PreLoginEvent preLoginEvent) {
|
||||
super(plugin.getCore(), plugin.getCore().getAuthPluginHook(), plugin.getBedrockService());
|
||||
this.plugin = plugin;
|
||||
this.connection = connection;
|
||||
this.username = username;
|
||||
this.continuation = continuation;
|
||||
this.preLoginEvent = preLoginEvent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
plugin.getSession().remove(connection.getRemoteAddress());
|
||||
try {
|
||||
super.onLogin(username, new VelocityLoginSource(connection, preLoginEvent));
|
||||
} finally {
|
||||
continuation.resume();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public FastLoginPreLoginEvent callFastLoginPreLoginEvent(String username, VelocityLoginSource source, StoredProfile profile) {
|
||||
VelocityFastLoginPreLoginEvent event = new VelocityFastLoginPreLoginEvent(username, source, profile);
|
||||
try {
|
||||
return plugin.getProxy().getEventManager().fire(event).get();
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt(); // Restore the interrupt flag
|
||||
return event;
|
||||
} catch (ExecutionException e) {
|
||||
core.getPlugin().getLog().error("Error firing event", e);
|
||||
return event;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestPremiumLogin(VelocityLoginSource source, StoredProfile profile,
|
||||
String username, boolean registered) {
|
||||
source.enableOnlinemode();
|
||||
plugin.getSession().put(source.getConnection().getRemoteAddress(), new VelocityLoginSession(username, registered, profile));
|
||||
|
||||
String ip = source.getAddress().getAddress().getHostAddress();
|
||||
plugin.getCore().getPendingLogin().put(ip + username, new Object());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startCrackedSession(VelocityLoginSource source, StoredProfile profile, String username) {
|
||||
plugin.getSession().put(source.getConnection().getRemoteAddress(), new VelocityLoginSession(username, false, profile));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package com.github.games647.fastlogin.velocity.task;
|
||||
|
||||
import com.github.games647.fastlogin.core.StoredProfile;
|
||||
import com.github.games647.fastlogin.core.shared.FastLoginCore;
|
||||
import com.github.games647.fastlogin.core.shared.event.FastLoginPremiumToggleEvent.PremiumToggleReason;
|
||||
import com.github.games647.fastlogin.velocity.FastLoginVelocity;
|
||||
import com.github.games647.fastlogin.velocity.event.VelocityFastLoginPremiumToggleEvent;
|
||||
import com.velocitypowered.api.command.CommandSource;
|
||||
import com.velocitypowered.api.proxy.Player;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
|
||||
public class AsyncToggleMessage implements Runnable {
|
||||
|
||||
private final FastLoginCore<Player, CommandSource, FastLoginVelocity> core;
|
||||
private final CommandSource sender;
|
||||
private final String senderName;
|
||||
private final String targetPlayer;
|
||||
private final boolean toPremium;
|
||||
private final boolean isPlayerSender;
|
||||
|
||||
public AsyncToggleMessage(FastLoginCore<Player, CommandSource, FastLoginVelocity> core,
|
||||
CommandSource sender, String playerName, boolean toPremium, boolean playerSender) {
|
||||
this.core = core;
|
||||
this.sender = sender;
|
||||
this.targetPlayer = playerName;
|
||||
this.toPremium = toPremium;
|
||||
this.isPlayerSender = playerSender;
|
||||
if (sender instanceof Player)
|
||||
senderName = ((Player) sender).getUsername();
|
||||
else
|
||||
senderName = "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (toPremium) {
|
||||
activatePremium();
|
||||
} else {
|
||||
turnOffPremium();
|
||||
}
|
||||
}
|
||||
|
||||
private void turnOffPremium() {
|
||||
StoredProfile playerProfile = core.getStorage().loadProfile(targetPlayer);
|
||||
//existing player is already cracked
|
||||
if (playerProfile.isSaved() && !playerProfile.isPremium()) {
|
||||
sendMessage("not-premium");
|
||||
return;
|
||||
}
|
||||
|
||||
playerProfile.setPremium(false);
|
||||
playerProfile.setId(null);
|
||||
core.getStorage().save(playerProfile);
|
||||
PremiumToggleReason reason = (!isPlayerSender || !senderName.equalsIgnoreCase(playerProfile.getName())) ?
|
||||
PremiumToggleReason.COMMAND_OTHER : PremiumToggleReason.COMMAND_SELF;
|
||||
core.getPlugin().getProxy().getEventManager().fire(
|
||||
new VelocityFastLoginPremiumToggleEvent(playerProfile, reason));
|
||||
sendMessage("remove-premium");
|
||||
}
|
||||
|
||||
private void activatePremium() {
|
||||
StoredProfile playerProfile = core.getStorage().loadProfile(targetPlayer);
|
||||
if (playerProfile.isPremium()) {
|
||||
sendMessage("already-exists");
|
||||
return;
|
||||
}
|
||||
|
||||
playerProfile.setPremium(true);
|
||||
core.getStorage().save(playerProfile);
|
||||
PremiumToggleReason reason = (!isPlayerSender || !senderName.equalsIgnoreCase(playerProfile.getName())) ?
|
||||
PremiumToggleReason.COMMAND_OTHER : PremiumToggleReason.COMMAND_SELF;
|
||||
core.getPlugin().getProxy().getEventManager().fire(
|
||||
new VelocityFastLoginPremiumToggleEvent(playerProfile, reason));
|
||||
sendMessage("add-premium");
|
||||
}
|
||||
|
||||
private void sendMessage(String localeId) {
|
||||
String message = core.getMessage(localeId);
|
||||
if (isPlayerSender) {
|
||||
sender.sendMessage(LegacyComponentSerializer.legacyAmpersand().deserialize(message));
|
||||
} else {
|
||||
core.getPlugin().getProxy().getConsoleCommandSource().sendMessage(LegacyComponentSerializer.legacyAmpersand().deserialize(message));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package com.github.games647.fastlogin.velocity.task;
|
||||
|
||||
import com.github.games647.fastlogin.core.StoredProfile;
|
||||
import com.github.games647.fastlogin.core.message.ChannelMessage;
|
||||
import com.github.games647.fastlogin.core.message.LoginActionMessage;
|
||||
import com.github.games647.fastlogin.core.message.LoginActionMessage.Type;
|
||||
import com.github.games647.fastlogin.core.shared.FastLoginCore;
|
||||
import com.github.games647.fastlogin.core.shared.ForceLoginManagement;
|
||||
import com.github.games647.fastlogin.core.shared.LoginSession;
|
||||
import com.github.games647.fastlogin.core.shared.event.FastLoginAutoLoginEvent;
|
||||
import com.github.games647.fastlogin.velocity.FastLoginVelocity;
|
||||
import com.github.games647.fastlogin.velocity.VelocityLoginSession;
|
||||
import com.github.games647.fastlogin.velocity.event.VelocityFastLoginAutoLoginEvent;
|
||||
import com.velocitypowered.api.command.CommandSource;
|
||||
import com.velocitypowered.api.proxy.Player;
|
||||
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
public class ForceLoginTask
|
||||
extends ForceLoginManagement<Player, CommandSource, VelocityLoginSession, FastLoginVelocity> {
|
||||
|
||||
private final RegisteredServer server;
|
||||
|
||||
//treat player as if they had a premium account, even when they don't
|
||||
//used for Floodgate auto login/register
|
||||
private final boolean forcedOnlineMode;
|
||||
|
||||
public ForceLoginTask(FastLoginCore<Player, CommandSource, FastLoginVelocity> core,
|
||||
Player player, RegisteredServer server, VelocityLoginSession session, boolean forcedOnlineMode) {
|
||||
super(core, player, session);
|
||||
|
||||
this.server = server;
|
||||
this.forcedOnlineMode = forcedOnlineMode;
|
||||
}
|
||||
|
||||
public ForceLoginTask(FastLoginCore<Player, CommandSource, FastLoginVelocity> core, Player player,
|
||||
RegisteredServer server, VelocityLoginSession session) {
|
||||
this(core, player, server, session, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (session == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
super.run();
|
||||
if (!isOnlineMode()) {
|
||||
session.setAlreadySaved(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean forceLogin(Player player) {
|
||||
if (session.isAlreadyLogged()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
session.setAlreadyLogged(true);
|
||||
return super.forceLogin(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FastLoginAutoLoginEvent callFastLoginAutoLoginEvent(LoginSession session, StoredProfile profile) {
|
||||
VelocityFastLoginAutoLoginEvent event = new VelocityFastLoginAutoLoginEvent(session, profile);
|
||||
try {
|
||||
return core.getPlugin().getProxy().getEventManager().fire(event).get();
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt(); // Set the interrupt flag again
|
||||
return event;
|
||||
} catch (ExecutionException e) {
|
||||
core.getPlugin().getLog().error("Error firing event", e);
|
||||
return event;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean forceRegister(Player player) {
|
||||
return session.isAlreadyLogged() || super.forceRegister(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onForceActionSuccess(LoginSession session) {
|
||||
//sub channel name
|
||||
Type type = Type.LOGIN;
|
||||
if (session.needsRegistration()) {
|
||||
type = Type.REGISTER;
|
||||
}
|
||||
|
||||
UUID proxyId = core.getPlugin().getProxyId();
|
||||
ChannelMessage loginMessage = new LoginActionMessage(type, player.getUsername(), proxyId);
|
||||
core.getPlugin().sendPluginMessage(server, loginMessage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName(Player player) {
|
||||
return player.getUsername();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOnline(Player player) {
|
||||
return player.isActive();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOnlineMode() {
|
||||
return forcedOnlineMode || player.isOnlineMode();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user