forked from TuxCoding/FastLogin
Compare commits
84 Commits
test
...
add-postgr
Author | SHA1 | Date | |
---|---|---|---|
31d9f3cb4a | |||
56e43fb146 | |||
a65a5f3020 | |||
8a729e069d | |||
d89d332273 | |||
cd3cfef6d4 | |||
6ec6e4844d | |||
60a65377b8 | |||
ffbf0ae3f4 | |||
4cf65cf7e5 | |||
dbf5ae2fae | |||
707572a007 | |||
108f5ad324 | |||
2112d06162 | |||
aece7f855d | |||
33b4ac8494 | |||
09161b86dd | |||
57833bb40b | |||
a1414a38ff | |||
0021bc476d | |||
4b1e08a2f8 | |||
dbf5b8907e | |||
e6dfa97e70 | |||
434a276d30 | |||
485af5e044 | |||
e566740dde | |||
db5b818a80 | |||
b0b998d1aa | |||
62ca46fe6a | |||
cd67797b5b | |||
48e8b45f1e | |||
1c9b8cb971 | |||
dcb9540a2f | |||
cacf3c23f1 | |||
67f170694f | |||
e15ea9ce85 | |||
70960661b4 | |||
ec657faaff | |||
51898b4b34 | |||
e30de1b700 | |||
ce17441edb | |||
3244b7a87a | |||
ecacd2a715 | |||
ea6d4cf2f6 | |||
91b05289ad | |||
4e0f8cfb65 | |||
d76690dcbe | |||
3559d067ad | |||
48a92ed35e | |||
a4eec19f53 | |||
32eda941ee | |||
92d88caf0a | |||
d2202d7a12 | |||
6a68e04c87 | |||
b702f48907 | |||
159a6ed37c | |||
b48dfed709 | |||
2db327ccc4 | |||
9d907983a6 | |||
4a3d7f448f | |||
31cbb2ff11 | |||
1715c08ee6 | |||
5682df9ad5 | |||
132b560130 | |||
e204c9c579 | |||
f92863e53a | |||
23fec56ecd | |||
5cd10fd795 | |||
ecbf07c0b2 | |||
053b51e96b | |||
44d365b15d | |||
610c49d62c | |||
dbd12742fa | |||
f2fd51d4e2 | |||
f9ca318775 | |||
f06ace2f5b | |||
ee465e381c | |||
6ab81b1a2f | |||
36ee52aa6e | |||
e5c70d8597 | |||
8b1485f0b1 | |||
c106089a72 | |||
5fc27f6c4a | |||
73720277b3 |
8
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
8
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
@ -36,7 +36,7 @@ body:
|
||||
attributes:
|
||||
label: Server log
|
||||
description: The error, stacktrace or link the complete log. You can use the links above for long versions.
|
||||
placeholder: https://hastebin.com/ / https://gist.github.com/
|
||||
placeholder: https://www.toptal.com/developers/hastebin / https://gist.github.com/
|
||||
- type: input
|
||||
attributes:
|
||||
label: Plugin version
|
||||
@ -59,9 +59,11 @@ body:
|
||||
label: Relevance
|
||||
description: Check list for previous tickets
|
||||
options:
|
||||
- label: I tried the latest build (build refers to development builds not necessary a release version)
|
||||
- label: |
|
||||
I tried the [latest build](https://ci.codemc.io/job/Games647/job/FastLogin/)
|
||||
(build refers to development builds not necessary a release version; i.e. v1.10 is out of date)
|
||||
required: true
|
||||
- label: |
|
||||
I checked for existing tickets -
|
||||
If there are, please vote them with a thumps reaction and not create new ones
|
||||
If there are, please vote them with a thumbs reaction and not create new ones
|
||||
required: true
|
||||
|
30
.github/ISSUE_TEMPLATE/enhancement_request.md
vendored
30
.github/ISSUE_TEMPLATE/enhancement_request.md
vendored
@ -1,30 +0,0 @@
|
||||
---
|
||||
name: 💡 Enhancement request
|
||||
about: New feature or change request
|
||||
title: ''
|
||||
labels: 'enhancement'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
[//]: # (Lines in this format are considered as comments and will not be displayed.)
|
||||
|
||||
[//]: # (Before reporting make sure you're running the **latest build** of the plugin and checked for existing issues!)
|
||||
|
||||
[//]: # (This ticket is about suggestions for a feature or particular enhancement)
|
||||
|
||||
### Is your feature request related to a problem? Please describe.
|
||||
|
||||
[//]: # (A clear and concise description of what the problem is. Ex. I'm always frustrated when [...])
|
||||
|
||||
### Describe the solution you'd like
|
||||
|
||||
[//]: # (A clear and concise description of what you want to happen.)
|
||||
|
||||
### Describe alternatives you've considered
|
||||
|
||||
[//]: # (A clear and concise description of any alternative solutions or features you've considered.)
|
||||
|
||||
### Additional context
|
||||
|
||||
[//]: # (Add any other context or screenshots about the feature request here.)
|
43
.github/ISSUE_TEMPLATE/enhancement_request.yaml
vendored
Normal file
43
.github/ISSUE_TEMPLATE/enhancement_request.yaml
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
name: 💡 Enhancement request
|
||||
description: New feature or change request
|
||||
labels: [ enhancement ]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
This ticket is about suggestions for a feature or particular enhancement.
|
||||
Feedback about this form is appreciated.
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Is your feature request related to a problem? Please describe.
|
||||
description: A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Describe the solution you'd like
|
||||
description: A clear and concise description of what you want to happen.
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Describe alternatives you've considered
|
||||
description: A clear and concise description of any alternative solutions or features you've considered.
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: Platform
|
||||
description: Server software - choose your proxy software if you have multiple servers
|
||||
options:
|
||||
- Spigot
|
||||
- BungeeCord
|
||||
- Velocity
|
||||
- All / Shared
|
||||
validations:
|
||||
required: true
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Relevance
|
||||
description: Check list for previous tickets
|
||||
options:
|
||||
- label: |
|
||||
I checked for existing tickets -
|
||||
If there are, please vote them with a thumbs reaction and not create new ones
|
||||
required: true
|
19
.github/dependabot.yml
vendored
19
.github/dependabot.yml
vendored
@ -14,3 +14,22 @@ updates:
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: weekly
|
||||
|
||||
groups:
|
||||
production-dependencies:
|
||||
dependency-type: "production"
|
||||
development-dependencies:
|
||||
dependency-type: "development"
|
||||
exclude-patterns:
|
||||
# Create single PR for these
|
||||
# Plugin require special evaluation about their compatibility
|
||||
- "com.lenis0012.bukkit:loginsecurity"
|
||||
- "com.comphenix.protocol:ProtocolLib"
|
||||
|
||||
ignore:
|
||||
# HikariCP dropped Java 8 support with 5.0
|
||||
- dependency-name: "com.zaxxer:HikariCP"
|
||||
update-types: ["version-update:semver-major"]
|
||||
# SnakeYAML has breaking changes with 2.0
|
||||
- dependency-name: "org.yaml:snakeyaml"
|
||||
update-types: ["version-update:semver-major"]
|
||||
|
45
.github/workflows/codeql-analysis.yml
vendored
45
.github/workflows/codeql-analysis.yml
vendored
@ -4,11 +4,11 @@
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
# Scan only for push on the primary branch for now
|
||||
push:
|
||||
branches: [ main ]
|
||||
pull_request:
|
||||
branches: [ main ]
|
||||
workflow_run:
|
||||
workflows: ["Maven Build"]
|
||||
branches: [main]
|
||||
types:
|
||||
- completed
|
||||
|
||||
jobs:
|
||||
# job i
|
||||
@ -20,6 +20,10 @@ jobs:
|
||||
# Environment
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }}
|
||||
|
||||
if: ${{ github.event.workflow_run.conclusion == 'success' }}
|
||||
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
@ -33,28 +37,29 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
# Setup Java
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: 19
|
||||
cache: 'maven'
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
|
||||
# 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@v2
|
||||
# Setup Java
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version-file: '.java-version'
|
||||
cache: 'maven'
|
||||
|
||||
# Manually start the autobuild process, because autobuild always selects Java 8 as build toolchain, but
|
||||
# we are doing cross-crompiling from a newer Java version
|
||||
- name: Build with Maven
|
||||
# Extracted from autobuild
|
||||
run: mvn package -f "pom.xml" --batch-mode -V -e -Dfindbugs.skip -Dcheckstyle.skip -Dpmd.skip=true -Dspotbugs.skip -Denforcer.skip -Dmaven.javadoc.skip -DskipTests -Dmaven.test.skip.exec -Dlicense.skip=true -Drat.skip=true -Dspotless.check.skip=true -t /home/runner/.m2/toolchains.xml
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
uses: github/codeql-action/analyze@v3
|
||||
with:
|
||||
category: "/language:${{matrix.language}}"
|
||||
|
12
.github/workflows/maven.yml
vendored
12
.github/workflows/maven.yml
vendored
@ -21,19 +21,19 @@ jobs:
|
||||
# Environment image - always use the newest OS
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
contents: write
|
||||
|
||||
# Run steps
|
||||
steps:
|
||||
# Pull changes
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
# Setup Java
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@v3
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: 19
|
||||
java-version-file: '.java-version'
|
||||
cache: 'maven'
|
||||
|
||||
# Build and test (included in package)
|
||||
@ -41,3 +41,7 @@ jobs:
|
||||
# Run non-interactive, package (with compile+test),
|
||||
# ignore snapshot updates, because they are likely to have breaking changes, enforce checksums
|
||||
run: mvn test --batch-mode --threads 2.0C --no-snapshot-updates --strict-checksums --file pom.xml
|
||||
|
||||
- name: Update dependency graph
|
||||
if: ${{ github.event_name == 'push' }}
|
||||
uses: advanced-security/maven-dependency-submission-action@v4.0.0
|
||||
|
2
.java-version
Normal file
2
.java-version
Normal file
@ -0,0 +1,2 @@
|
||||
# Latest GitHub Action java release that is installed on the runners
|
||||
21
|
12
README.md
12
README.md
@ -60,13 +60,13 @@ Possible values: `Premium`, `Cracked`, `Unknown`
|
||||
|
||||
## Requirements
|
||||
|
||||
* Java 17+
|
||||
* Java 17+ (Recommended)
|
||||
* Server software in offlinemode:
|
||||
* Spigot (or a fork e.g. Paper) 1.8.8+
|
||||
* Protocol plugin:
|
||||
* [ProtocolLib 5.0+](https://www.spigotmc.org/resources/protocollib.1997/) or
|
||||
* [ProtocolLib 5.1+](https://www.spigotmc.org/resources/protocollib.1997/) or
|
||||
* [ProtocolSupport](https://www.spigotmc.org/resources/protocolsupport.7201/)
|
||||
* Latest BungeeCord (or a fork e.g. Waterfall)
|
||||
* Latest BungeeCord (or a fork e.g. Waterfall) or Velocity
|
||||
* An auth plugin.
|
||||
|
||||
### Supported auth plugins
|
||||
@ -117,10 +117,10 @@ Install the plugin on both platforms, that is proxy (BungeeCord or Velocity) and
|
||||
4. Activate ip forwarding in your proxy config
|
||||
5. Check your database settings in the config of FastLogin on your proxy
|
||||
* The proxies only ship with a limited set of drivers where Spigot supports more. Therefore, these are supported:
|
||||
* BungeeCord: `com.mysql.jdbc.Driver` for MySQL/MariaDB
|
||||
* Velocity: `fastlogin.mariadb.jdbc.Driver` for MySQL/MariaDB
|
||||
* BungeeCord: `com.mysql.jdbc.Driver` for MySQL/MariaDB/PostgreSQL
|
||||
* Velocity: `fastlogin.mariadb.jdbc.Driver` for MySQL/MariaDB/PostgreSQL
|
||||
* Note the embedded file storage SQLite is not available
|
||||
* MySQL/MariaDB requires an external database server running. Check your server provider if there is one available
|
||||
* MySQL/MariaDB/PostgreSQL requires an external database server running. Check your server provider if there is one available
|
||||
or install one.
|
||||
6. Set proxy and Spigot in offline mode by setting the value `onlinemode` in your `config.yml` to false
|
||||
7. You should *always* configure the firewall for your Spigot server so that it's only accessible through your proxy
|
||||
|
@ -50,11 +50,15 @@
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.4.1</version>
|
||||
<version>3.5.1</version>
|
||||
<configuration>
|
||||
<createDependencyReducedPom>false</createDependencyReducedPom>
|
||||
<shadedArtifactAttached>false</shadedArtifactAttached>
|
||||
<relocations>
|
||||
<relocation>
|
||||
<pattern>org.yaml.snakeyaml</pattern>
|
||||
<shadedPattern>fastlogin.yaml</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>com.zaxxer.hikari</pattern>
|
||||
<shadedPattern>fastlogin.hikari</shadedPattern>
|
||||
@ -123,6 +127,9 @@
|
||||
<repository>
|
||||
<id>dmulloy2-repo</id>
|
||||
<url>https://repo.dmulloy2.net/repository/public/</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
|
||||
<!-- AuthMe Reloaded, xAuth and LoginSecurity -->
|
||||
@ -161,6 +168,13 @@
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.yaml</groupId>
|
||||
<artifactId>snakeyaml</artifactId>
|
||||
<version>1.33</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- PaperSpigot API for correcting user cache usage -->
|
||||
<dependency>
|
||||
<groupId>io.papermc.paper</groupId>
|
||||
@ -193,7 +207,7 @@
|
||||
<dependency>
|
||||
<groupId>com.comphenix.protocol</groupId>
|
||||
<artifactId>ProtocolLib</artifactId>
|
||||
<version>5.0.0</version>
|
||||
<version>5.1.0</version>
|
||||
<scope>provided</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
@ -264,7 +278,7 @@
|
||||
<dependency>
|
||||
<groupId>me.clip</groupId>
|
||||
<artifactId>placeholderapi</artifactId>
|
||||
<version>2.11.3</version>
|
||||
<version>2.11.5</version>
|
||||
<scope>provided</scope>
|
||||
<optional>true</optional>
|
||||
<exclusions>
|
||||
@ -293,7 +307,7 @@
|
||||
<dependency>
|
||||
<groupId>com.lenis0012.bukkit</groupId>
|
||||
<artifactId>loginsecurity</artifactId>
|
||||
<version>3.1.1</version>
|
||||
<version>3.3.0</version>
|
||||
<scope>provided</scope>
|
||||
<optional>true</optional>
|
||||
<exclusions>
|
||||
@ -364,7 +378,7 @@
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk18on</artifactId>
|
||||
<version>1.72</version>
|
||||
<version>1.77</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
|
@ -39,7 +39,7 @@ public class BukkitScheduler extends AsyncScheduler {
|
||||
public BukkitScheduler(Plugin plugin, Logger logger) {
|
||||
super(logger, command -> Bukkit.getScheduler().runTaskAsynchronously(plugin, command));
|
||||
|
||||
syncExecutor = r -> Bukkit.getScheduler().runTask(plugin, r);
|
||||
syncExecutor = task -> Bukkit.getScheduler().runTask(plugin, task);
|
||||
}
|
||||
|
||||
public Executor getSyncExecutor() {
|
||||
|
@ -228,6 +228,22 @@ public class FastLoginBukkit extends JavaPlugin implements PlatformPlugin<Comman
|
||||
|
||||
/**
|
||||
* Fetches the premium status of an online player.
|
||||
* {@snippet :
|
||||
* // Bukkit's players object after successful authentication i.e. PlayerJoinEvent
|
||||
* // except for proxies like BungeeCord and Velocity where the details are sent delayed (1-2 seconds)
|
||||
* Player player;
|
||||
* PremiumStatus status = JavaPlugin.getPlugin(FastLoginBukkit.class).getStatus(player.getUniqueId());
|
||||
* switch (status) {
|
||||
* case CRACKED:
|
||||
* // player is offline
|
||||
* break;
|
||||
* case PREMIUM:
|
||||
* // account is premium and player passed the verification
|
||||
* break;
|
||||
* case UNKNOWN:
|
||||
* // no record about this player
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* @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
|
||||
|
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2023 games647 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;
|
||||
|
||||
import java.net.InetAddress;
|
||||
|
||||
public final class InetUtils {
|
||||
|
||||
private InetUtils() {
|
||||
// Utility
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies if the given IP address is from the local network
|
||||
*
|
||||
* @param address IP address
|
||||
* @return true if address is from local network or even from the device itself (loopback)
|
||||
*/
|
||||
public static boolean isLocalAddress(InetAddress address) {
|
||||
// Loopback addresses like 127.0.* (IPv4) or [::1] (IPv6)
|
||||
return address.isLoopbackAddress()
|
||||
// Example: 10.0.0.0, 172.16.0.0, 192.168.0.0, fec0::/10 (deprecated)
|
||||
// Ref: https://en.wikipedia.org/wiki/IP_address#Private_addresses
|
||||
|| address.isSiteLocalAddress()
|
||||
// Example: 169.254.0.0/16, fe80::/10
|
||||
// Ref: https://en.wikipedia.org/wiki/IP_address#Address_autoconfiguration
|
||||
|| address.isLinkLocalAddress()
|
||||
// non deprecated unique site-local that java doesn't check yet -> fc00::/7
|
||||
|| isIPv6UniqueSiteLocal(address);
|
||||
}
|
||||
|
||||
private static boolean isIPv6UniqueSiteLocal(InetAddress address) {
|
||||
// ref: https://en.wikipedia.org/wiki/Unique_local_address
|
||||
|
||||
// currently undefined but could be used in the near future fc00::/8
|
||||
return (address.getAddress()[0] & 0xFF) == 0xFC
|
||||
// in use for unique site-local fd00::/8
|
||||
|| (address.getAddress()[0] & 0xFF) == 0xFD;
|
||||
}
|
||||
}
|
@ -70,7 +70,7 @@ final class EncryptionUtil {
|
||||
public static final int VERIFY_TOKEN_LENGTH = 4;
|
||||
public static final String KEY_PAIR_ALGORITHM = "RSA";
|
||||
|
||||
private static final int RSA_LENGTH = 1_024;
|
||||
private static final int RSA_LENGTH = 2_048;
|
||||
|
||||
private static final PublicKey MOJANG_SESSION_KEY;
|
||||
private static final int LINE_LENGTH = 76;
|
||||
|
@ -177,7 +177,7 @@ public class ProtocolLibListener extends PacketAdapter {
|
||||
ClientPublicKey clientPublicKey, byte[] expectedToken) {
|
||||
try {
|
||||
if (new MinecraftVersion(1, 19, 0).atOrAbove()
|
||||
&& !(new MinecraftVersion(1, 19, 3).atOrAbove())) {
|
||||
&& !new MinecraftVersion(1, 19, 3).atOrAbove()) {
|
||||
Either<byte[], ?> either = packet.getSpecificModifier(Either.class).read(0);
|
||||
if (clientPublicKey == null) {
|
||||
Optional<byte[]> left = either.left();
|
||||
|
@ -38,6 +38,7 @@ import com.comphenix.protocol.reflect.accessors.FieldAccessor;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||
import com.comphenix.protocol.wrappers.BukkitConverters;
|
||||
import com.comphenix.protocol.wrappers.Converters;
|
||||
import com.comphenix.protocol.wrappers.WrappedChatComponent;
|
||||
import com.comphenix.protocol.wrappers.WrappedGameProfile;
|
||||
import com.comphenix.protocol.wrappers.WrappedProfilePublicKey.WrappedProfileKeyData;
|
||||
@ -46,6 +47,7 @@ import com.github.games647.craftapi.model.skin.SkinProperty;
|
||||
import com.github.games647.craftapi.resolver.MojangResolver;
|
||||
import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
|
||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||
import com.github.games647.fastlogin.bukkit.InetUtils;
|
||||
import com.github.games647.fastlogin.bukkit.listener.protocollib.packet.ClientPublicKey;
|
||||
import lombok.val;
|
||||
import org.bukkit.entity.Player;
|
||||
@ -71,10 +73,17 @@ public class VerifyResponseTask implements Runnable {
|
||||
|
||||
private static final String ENCRYPTION_CLASS_NAME = "MinecraftEncryption";
|
||||
private static final Class<?> ENCRYPTION_CLASS;
|
||||
private static final String ADDRESS_VERIFY_WARNING = "This indicates the use of reverse-proxy like HAProxy, "
|
||||
+ "TCPShield, BungeeCord, Velocity, etc. "
|
||||
+ "By default (configurable in the config) this plugin requests Mojang to verify the connecting IP "
|
||||
+ "to this server with the one used to log into Minecraft to prevent MITM attacks. In "
|
||||
+ "order to work this security feature, the actual client IP needs to be forwarding "
|
||||
+ "(keyword IP forwarding). This process will also be useful for other server "
|
||||
+ "features like IP banning, so that it doesn't ban the proxy IP.";
|
||||
|
||||
static {
|
||||
ENCRYPTION_CLASS = MinecraftReflection.getMinecraftClass(
|
||||
"util." + ENCRYPTION_CLASS_NAME, ENCRYPTION_CLASS_NAME
|
||||
"util." + ENCRYPTION_CLASS_NAME, ENCRYPTION_CLASS_NAME
|
||||
);
|
||||
}
|
||||
|
||||
@ -149,10 +158,27 @@ public class VerifyResponseTask implements Runnable {
|
||||
} else {
|
||||
//user tried to fake an authentication
|
||||
disconnect(
|
||||
"invalid-session",
|
||||
"GameProfile {} ({}) tried to log in with an invalid session. ServerId: {}",
|
||||
session.getRequestUsername(), socketAddress, serverId
|
||||
"invalid-session",
|
||||
"Session server rejected incoming connection for GameProfile {} ({}). Possible reasons are"
|
||||
+ "1) Client IP address contacting Mojang and server during server join were different "
|
||||
+ "(Do you use a reverse proxy? -> Enable IP forwarding, "
|
||||
+ "or disable the feature in the config). "
|
||||
+ "2) Player is offline, but tried to bypass the authentication"
|
||||
+ "3) Client uses an outdated username for connecting (Fix: Restart client)",
|
||||
requestedUsername, address
|
||||
);
|
||||
|
||||
if (InetUtils.isLocalAddress(address)) {
|
||||
plugin.getLog().warn(
|
||||
"The incoming request for player {} uses a local IP address",
|
||||
requestedUsername
|
||||
);
|
||||
plugin.getLog().warn(ADDRESS_VERIFY_WARNING);
|
||||
} else {
|
||||
plugin.getLog().warn("If you think this is an error, please verify that the incoming "
|
||||
+ "IP address {} is not associated with a server hosting company.", address);
|
||||
plugin.getLog().warn(ADDRESS_VERIFY_WARNING);
|
||||
}
|
||||
}
|
||||
} catch (IOException ioEx) {
|
||||
disconnect("error-kick", "Failed to connect to session server", ioEx);
|
||||
@ -177,7 +203,7 @@ public class VerifyResponseTask implements Runnable {
|
||||
session.setVerified(true);
|
||||
|
||||
setPremiumUUID(session.getUuid());
|
||||
receiveFakeStartPacket(realUsername, session.getClientPublicKey());
|
||||
receiveFakeStartPacket(realUsername, session.getClientPublicKey(), session.getUuid());
|
||||
}
|
||||
|
||||
private void setPremiumUUID(UUID premiumUUID) {
|
||||
@ -217,15 +243,15 @@ public class VerifyResponseTask implements Runnable {
|
||||
try {
|
||||
// Try to get the old (pre MC 1.16.4) encryption method
|
||||
encryptMethod = FuzzyReflection.fromClass(networkManagerClass)
|
||||
.getMethodByParameters("a", SecretKey.class);
|
||||
.getMethodByParameters("a", SecretKey.class);
|
||||
} catch (IllegalArgumentException exception) {
|
||||
// Get the new encryption method
|
||||
encryptMethod = FuzzyReflection.fromClass(networkManagerClass)
|
||||
.getMethodByParameters("a", Cipher.class, Cipher.class);
|
||||
.getMethodByParameters("a", Cipher.class, Cipher.class);
|
||||
|
||||
// Get the needed Cipher helper method (used to generate ciphers from login key)
|
||||
cipherMethod = FuzzyReflection.fromClass(ENCRYPTION_CLASS)
|
||||
.getMethodByParameters("a", int.class, Key.class);
|
||||
.getMethodByParameters("a", int.class, Key.class);
|
||||
}
|
||||
}
|
||||
|
||||
@ -268,15 +294,23 @@ public class VerifyResponseTask implements Runnable {
|
||||
}
|
||||
|
||||
//fake a new login packet in order to let the server handle all the other stuff
|
||||
private void receiveFakeStartPacket(String username, ClientPublicKey clientKey) {
|
||||
private void receiveFakeStartPacket(String username, ClientPublicKey clientKey, UUID uuid) {
|
||||
PacketContainer startPacket;
|
||||
if (new MinecraftVersion(1, 19, 0).atOrAbove()) {
|
||||
if (new MinecraftVersion(1, 20, 2).atOrAbove()) {
|
||||
startPacket = new PacketContainer(START);
|
||||
startPacket.getStrings().write(0, username);
|
||||
startPacket.getUUIDs().write(0, uuid);
|
||||
} else if (new MinecraftVersion(1, 19, 3).atOrAbove()) {
|
||||
startPacket = new PacketContainer(START);
|
||||
startPacket.getStrings().write(0, username);
|
||||
startPacket.getOptionals(Converters.passthrough(UUID.class)).write(0, Optional.of(uuid));
|
||||
} else if (new MinecraftVersion(1, 19, 0).atOrAbove()) {
|
||||
startPacket = new PacketContainer(START);
|
||||
startPacket.getStrings().write(0, username);
|
||||
|
||||
EquivalentConverter<WrappedProfileKeyData> converter = BukkitConverters.getWrappedPublicKeyDataConverter();
|
||||
val wrappedKey = Optional.ofNullable(clientKey).map(key ->
|
||||
new WrappedProfileKeyData(clientKey.expiry(), clientKey.key(), clientKey.signature())
|
||||
new WrappedProfileKeyData(clientKey.expiry(), clientKey.key(), clientKey.signature())
|
||||
);
|
||||
|
||||
startPacket.getOptionals(converter).write(0, wrappedKey);
|
||||
|
@ -47,7 +47,7 @@
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.4.1</version>
|
||||
<version>3.5.1</version>
|
||||
<configuration>
|
||||
<minimizeJar>true</minimizeJar>
|
||||
|
||||
|
@ -220,6 +220,14 @@
|
||||
<property name="optional" value="true"/>
|
||||
</module>
|
||||
|
||||
<!-- Suppress filters via comments -->
|
||||
<!-- https://stackoverflow.com/a/4023351/9767089 -->
|
||||
<module name="SuppressionCommentFilter">
|
||||
<property name="offCommentFormat" value="CHECKSTYLE.OFF\: ([\w\|]+)"/>
|
||||
<property name="onCommentFormat" value="CHECKSTYLE.ON\: ([\w\|]+)"/>
|
||||
<property name="checkFormat" value="$1"/>
|
||||
</module>
|
||||
|
||||
</module>
|
||||
|
||||
</module>
|
||||
|
27
core/pom.xml
27
core/pom.xml
@ -63,6 +63,23 @@
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>3.3.0</version>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifestEntries>
|
||||
<Automatic-Module-Name>com.github.games647.fastlogin.core</Automatic-Module-Name>
|
||||
</manifestEntries>
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<dependencies>
|
||||
<!-- Libraries that we shade into the project -->
|
||||
|
||||
@ -85,7 +102,7 @@
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-jdk14</artifactId>
|
||||
<version>2.0.7</version>
|
||||
<version>2.0.11</version>
|
||||
</dependency>
|
||||
|
||||
<!-- snakeyaml is present in Bungee, Spigot, Cauldron, so we could use this independent implementation -->
|
||||
@ -93,12 +110,6 @@
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-config</artifactId>
|
||||
<version>1.19-R0.1-SNAPSHOT</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>*</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!--Floodgate for Xbox Live Authentication-->
|
||||
@ -184,7 +195,7 @@
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.10</version>
|
||||
<version>2.10.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
@ -107,7 +107,7 @@ public class TickingRateLimiter implements RateLimiter {
|
||||
}
|
||||
}
|
||||
|
||||
private static class TimeRecord implements Comparable<Long> {
|
||||
private static class TimeRecord implements Comparable<TimeRecord> {
|
||||
|
||||
private final long firstMinuteRecord;
|
||||
private final long expireTime;
|
||||
@ -131,9 +131,9 @@ public class TickingRateLimiter implements RateLimiter {
|
||||
return firstMinuteRecord + expireTime <= now;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Long other) {
|
||||
public int compareTo(long other) {
|
||||
if (other < firstMinuteRecord) {
|
||||
// other is earlier
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -143,5 +143,10 @@ public class TickingRateLimiter implements RateLimiter {
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(TimeRecord other) {
|
||||
return compareTo(other.firstMinuteRecord);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,8 +25,17 @@
|
||||
*/
|
||||
package com.github.games647.fastlogin.core.hooks;
|
||||
|
||||
/**
|
||||
* Password generator for your auth plugin.
|
||||
* @param <P> platform dependent player class
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface PasswordGenerator<P> {
|
||||
|
||||
/**
|
||||
* Generate a password for a non-registered player
|
||||
* @param player
|
||||
* @return daw
|
||||
*/
|
||||
String getRandomPassword(P player);
|
||||
}
|
||||
|
@ -36,6 +36,7 @@ import com.github.games647.fastlogin.core.antibot.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.PostgreSQLStorage;
|
||||
import com.github.games647.fastlogin.core.storage.MySQLStorage;
|
||||
import com.github.games647.fastlogin.core.storage.SQLStorage;
|
||||
import com.github.games647.fastlogin.core.storage.SQLiteStorage;
|
||||
@ -230,6 +231,24 @@ public class FastLoginCore<P extends C, C, T extends PlatformPlugin<C>> {
|
||||
|
||||
if (type.contains("sqlite")) {
|
||||
storage = new SQLiteStorage(plugin, database, databaseConfig);
|
||||
} else if (type.contains("postgresql")) {
|
||||
String host = config.get("host", "");
|
||||
int port = config.get("port", 3306);
|
||||
boolean useSSL = config.get("useSSL", false);
|
||||
|
||||
if (useSSL) {
|
||||
boolean publicKeyRetrieval = config.getBoolean("allowPublicKeyRetrieval", false);
|
||||
String rsaPublicKeyFile = config.getString("ServerRSAPublicKeyFile");
|
||||
String sslMode = config.getString("sslMode", "Required");
|
||||
|
||||
databaseConfig.addDataSourceProperty("allowPublicKeyRetrieval", publicKeyRetrieval);
|
||||
databaseConfig.addDataSourceProperty("serverRSAPublicKeyFile", rsaPublicKeyFile);
|
||||
databaseConfig.addDataSourceProperty("sslMode", sslMode);
|
||||
}
|
||||
|
||||
databaseConfig.setUsername(config.get("username", ""));
|
||||
databaseConfig.setPassword(config.getString("password"));
|
||||
storage = new PostgreSQLStorage(plugin, type, host, port, database, databaseConfig, useSSL);
|
||||
} else {
|
||||
String host = config.get("host", "");
|
||||
int port = config.get("port", 3306);
|
||||
|
@ -33,6 +33,7 @@ import org.geysermc.floodgate.api.player.FloodgatePlayer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
@ -61,9 +62,9 @@ public abstract class FloodgateManagement<P extends C, C, L extends LoginSession
|
||||
this.username = getName(player);
|
||||
|
||||
//load values from config.yml
|
||||
autoLoginFloodgate = core.getConfig().get("autoLoginFloodgate").toString().toLowerCase();
|
||||
autoRegisterFloodgate = core.getConfig().get("autoRegisterFloodgate").toString().toLowerCase();
|
||||
allowNameConflict = core.getConfig().get("allowFloodgateNameConflict").toString().toLowerCase();
|
||||
autoLoginFloodgate = core.getConfig().getString("autoLoginFloodgate").toLowerCase(Locale.ROOT);
|
||||
autoRegisterFloodgate = core.getConfig().getString("autoRegisterFloodgate").toLowerCase(Locale.ROOT);
|
||||
allowNameConflict = core.getConfig().getString("allowFloodgateNameConflict").toLowerCase(Locale.ROOT);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -80,6 +81,40 @@ public abstract class FloodgateManagement<P extends C, C, L extends LoginSession
|
||||
}
|
||||
|
||||
profile = core.getStorage().loadProfile(username);
|
||||
|
||||
if (profile.isSaved()) {
|
||||
if (profile.isFloodgateMigrated()) {
|
||||
if (profile.getFloodgate() == FloodgateState.TRUE && isLinked) {
|
||||
core.getPlugin().getLog()
|
||||
.info("Player {} is already stored by FastLogin as a non-linked Bedrock Edition player",
|
||||
username);
|
||||
return;
|
||||
} else if (profile.getFloodgate() == FloodgateState.FALSE && isLinked) {
|
||||
profile.setFloodgate(FloodgateState.LINKED);
|
||||
core.getPlugin().getLog().info(
|
||||
"Player {} will be changed from a Java player to a linked Floodgate player",
|
||||
username);
|
||||
}
|
||||
} else {
|
||||
if (isLinked) {
|
||||
profile.setFloodgate(FloodgateState.LINKED);
|
||||
core.getPlugin().getLog().info(
|
||||
"Player {} will be migrated to the v2 database schema as a linked Floodgate user",
|
||||
username);
|
||||
} else {
|
||||
profile.setFloodgate(FloodgateState.TRUE);
|
||||
core.getPlugin().getLog().info(
|
||||
"Player {} will be migrated to the v2 database schema as a Floodgate user", username);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (isLinked) {
|
||||
profile.setFloodgate(FloodgateState.LINKED);
|
||||
} else {
|
||||
profile.setFloodgate(FloodgateState.TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
AuthPlugin<P> authPlugin = core.getAuthPluginHook();
|
||||
|
||||
try {
|
||||
@ -119,13 +154,17 @@ public abstract class FloodgateManagement<P extends C, C, L extends LoginSession
|
||||
}
|
||||
}
|
||||
|
||||
// defer auto registration, if it's not enabled in the config
|
||||
if (!isRegistered && !isAutoAuthAllowed(autoRegisterFloodgate)) {
|
||||
return;
|
||||
}
|
||||
|
||||
//logging in from bedrock for a second time threw an error with UUID
|
||||
if (profile == null) {
|
||||
profile = new StoredProfile(getUUID(player), username, true, getAddress(player).toString());
|
||||
// stop the auto login procedure, if the current connection's parameters don't match the one stored in our
|
||||
// database
|
||||
// ex. we stored a LINKED account, but the current connection is not linked
|
||||
if ((profile.getFloodgate() == FloodgateState.LINKED && !isLinked)
|
||||
|| (profile.getFloodgate() == FloodgateState.TRUE && isLinked)) {
|
||||
return;
|
||||
}
|
||||
|
||||
//start Bukkit/Bungee specific tasks
|
||||
|
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2023 games647 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.shared;
|
||||
|
||||
public enum FloodgateState {
|
||||
|
||||
/**
|
||||
* Purely Java profile
|
||||
*/
|
||||
FALSE(0),
|
||||
|
||||
/**
|
||||
* Purely Bedrock profile
|
||||
*/
|
||||
TRUE(1),
|
||||
|
||||
/**
|
||||
* Bedrock profile is bidirectional associated with the Java Mojang profile.
|
||||
*/
|
||||
LINKED(2),
|
||||
|
||||
/**
|
||||
* Data before floodgate database migration. Floodgate state is unknown.
|
||||
*/
|
||||
NOT_MIGRATED(3);
|
||||
|
||||
private int value;
|
||||
|
||||
FloodgateState(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a number to FloodgateState
|
||||
* <ol start="0">
|
||||
* <li>False</li>
|
||||
* <li>True</li>
|
||||
* <li>Linked</li>
|
||||
* <li>Not Migrated</li>
|
||||
* </ol>
|
||||
* @param num the number, most likely loaded from the database
|
||||
* @return FloodgateStatus on success, null otherwise
|
||||
*/
|
||||
public static FloodgateState fromInt(int num) {
|
||||
// using Enum.values()[i] is expensive as per https://stackoverflow.com/a/8762387/9767089
|
||||
switch (num) {
|
||||
case 0:
|
||||
return FloodgateState.FALSE;
|
||||
case 1:
|
||||
return FloodgateState.TRUE;
|
||||
case 2:
|
||||
return FloodgateState.LINKED;
|
||||
case 3:
|
||||
return FloodgateState.NOT_MIGRATED;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@ -62,7 +62,7 @@ public abstract class ForceLoginManagement<P extends C, C, L extends LoginSessio
|
||||
//premium player
|
||||
AuthPlugin<P> authPlugin = core.getAuthPluginHook();
|
||||
if (authPlugin == null) {
|
||||
//maybe only bungeecord plugin
|
||||
// maybe only bungeecord plugin
|
||||
onForceActionSuccess(session);
|
||||
} else {
|
||||
boolean success = true;
|
||||
|
@ -49,17 +49,32 @@ public abstract class JoinManagement<P extends C, C, S extends LoginSource> {
|
||||
|
||||
public void onLogin(String username, S source) {
|
||||
core.getPlugin().getLog().info("Handling player {}", username);
|
||||
|
||||
//check if the player is connecting through Bedrock Edition
|
||||
if (bedrockService != null && bedrockService.isBedrockConnection(username)) {
|
||||
//perform Bedrock specific checks and skip Java checks if no longer needed
|
||||
if (bedrockService.performChecks(username, source)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
StoredProfile profile = core.getStorage().loadProfile(username);
|
||||
|
||||
//can't be a premium Java player, if it's not saved in the database
|
||||
if (profile == null) {
|
||||
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)) {
|
||||
if (profile.isFloodgateMigrated()) {
|
||||
if (profile.getFloodgate() == FloodgateState.TRUE) {
|
||||
// migrated and enabled floodgate player, however the above bedrocks fails, so the current connection
|
||||
// isn't premium
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
profile.setFloodgate(FloodgateState.FALSE);
|
||||
core.getPlugin().getLog().info(
|
||||
"Player {} will be migrated to the v2 database schema as a JAVA user", username);
|
||||
}
|
||||
|
||||
callFastLoginPreLoginEvent(username, source, profile);
|
||||
@ -139,6 +154,12 @@ public abstract class JoinManagement<P extends C, C, S extends LoginSource> {
|
||||
if (core.getConfig().get("nameChangeCheck", false)) {
|
||||
StoredProfile storedProfile = core.getStorage().loadProfile(profile.getId());
|
||||
if (storedProfile != null) {
|
||||
if (storedProfile.getFloodgate() == FloodgateState.TRUE) {
|
||||
core.getPlugin().getLog()
|
||||
.info("Player {} is already stored by FastLogin as a Bedrock Edition player.", username);
|
||||
return false;
|
||||
}
|
||||
|
||||
//uuid exists in the database
|
||||
core.getPlugin().getLog().info("GameProfile {} changed it's username", profile);
|
||||
|
||||
|
@ -61,6 +61,7 @@ public abstract class LoginSession {
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if user needs registration once login is successful
|
||||
* @return This value is always false if we authenticate the player with a cracked authentication
|
||||
*/
|
||||
public synchronized boolean needsRegistration() {
|
||||
|
@ -29,9 +29,20 @@ import com.github.games647.fastlogin.core.shared.LoginSession;
|
||||
import com.github.games647.fastlogin.core.storage.StoredProfile;
|
||||
|
||||
/**
|
||||
* This event fires if the plugin performs an auto login on the platform where the login plugin is.
|
||||
* This event fires if the plugin performs an auto login on the platform where the login plugin is
|
||||
*
|
||||
* {@snippet :
|
||||
* @EventHandler()
|
||||
* public void onPlayerLogin(FastLoginAutoLoginEvent loginEvent) {
|
||||
* StoredProfile profile = loginEvent.getProfile();
|
||||
* LoginSession session = loginEvent.getSession();
|
||||
*
|
||||
* System.out.println("Player: " + session.getUsername() + " is about to be force logged in");
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
public interface FastLoginAutoLoginEvent extends FastLoginCancellableEvent {
|
||||
LoginSession getSession();
|
||||
|
||||
StoredProfile getProfile();
|
||||
}
|
||||
|
@ -28,5 +28,6 @@ package com.github.games647.fastlogin.core.shared.event;
|
||||
public interface FastLoginCancellableEvent {
|
||||
|
||||
boolean isCancelled();
|
||||
|
||||
void setCancelled(boolean cancelled);
|
||||
}
|
||||
|
@ -34,6 +34,8 @@ import com.github.games647.fastlogin.core.storage.StoredProfile;
|
||||
public interface FastLoginPreLoginEvent {
|
||||
|
||||
String getUsername();
|
||||
|
||||
LoginSource getSource();
|
||||
|
||||
StoredProfile getProfile();
|
||||
}
|
||||
|
@ -33,6 +33,7 @@ import com.github.games647.fastlogin.core.storage.StoredProfile;
|
||||
public interface FastLoginPremiumToggleEvent {
|
||||
|
||||
StoredProfile getProfile();
|
||||
|
||||
PremiumToggleReason getReason();
|
||||
|
||||
enum PremiumToggleReason {
|
||||
|
@ -28,6 +28,8 @@ package com.github.games647.fastlogin.core.storage;
|
||||
import com.github.games647.fastlogin.core.shared.PlatformPlugin;
|
||||
import com.zaxxer.hikari.HikariConfig;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
public class MySQLStorage extends SQLStorage {
|
||||
|
||||
private static final String JDBC_PROTOCOL = "jdbc:";
|
||||
@ -68,12 +70,12 @@ public class MySQLStorage extends SQLStorage {
|
||||
}
|
||||
|
||||
private static String buildJDBCUrl(String driver, String host, int port, String database) {
|
||||
String protocol = "mysql";
|
||||
if (driver.contains("mariadb")) {
|
||||
protocol = "mariadb";
|
||||
MySQLVariant variant = MySQLVariant.fromDriver(driver);
|
||||
if (variant == null) {
|
||||
throw new IllegalArgumentException("Unknown storage driver");
|
||||
}
|
||||
|
||||
return protocol + "://" + host + ':' + port + '/' + database;
|
||||
return variant.getJdbcPrefix() + "://" + host + ':' + port + '/' + database;
|
||||
}
|
||||
|
||||
private static void addPerformanceProperties(HikariConfig config) {
|
||||
@ -106,4 +108,32 @@ public class MySQLStorage extends SQLStorage {
|
||||
// In our case it can be useful to see the time in error messages
|
||||
// config.addDataSourceProperty("maintainTimeStats", false);
|
||||
}
|
||||
|
||||
enum MySQLVariant {
|
||||
|
||||
MYSQL("mysql"),
|
||||
|
||||
MARIADB("mariadb");
|
||||
|
||||
private final String jdbcPrefix;
|
||||
|
||||
public static MySQLVariant fromDriver(String driver) {
|
||||
String normalizedDriver = driver.toLowerCase(Locale.ENGLISH);
|
||||
if (normalizedDriver.contains("mysql")) {
|
||||
return MYSQL;
|
||||
} else if (normalizedDriver.contains("mariadb")) {
|
||||
return MARIADB;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
MySQLVariant(String jdbcPrefix) {
|
||||
this.jdbcPrefix = jdbcPrefix;
|
||||
}
|
||||
|
||||
public String getJdbcPrefix() {
|
||||
return jdbcPrefix;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,101 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2023 games647 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.storage;
|
||||
|
||||
import com.github.games647.fastlogin.core.shared.PlatformPlugin;
|
||||
import com.zaxxer.hikari.HikariConfig;
|
||||
|
||||
public class PostgreSQLStorage extends SQLStorage {
|
||||
|
||||
private static final String JDBC_PROTOCOL = "jdbc:";
|
||||
|
||||
public PostgreSQLStorage(PlatformPlugin<?> plugin, String driver, String host, int port, String database,
|
||||
HikariConfig config, boolean useSSL) {
|
||||
super(plugin.getLog(), plugin.getName(), plugin.getThreadFactory(),
|
||||
setParams(config, driver, host, port, database, useSSL));
|
||||
}
|
||||
|
||||
private static HikariConfig setParams(HikariConfig config,
|
||||
String driver, String host, int port, String database,
|
||||
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);
|
||||
|
||||
config.setJdbcUrl(JDBC_PROTOCOL + buildJDBCUrl(driver, host, port, database));
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
private static String buildJDBCUrl(String driver, String host, int port, String database) {
|
||||
return "postgresql://" + host + ':' + port + '/' + database;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getCreateTableStmt() {
|
||||
// PostgreSQL has a different syntax for id column
|
||||
return CREATE_TABLE_STMT
|
||||
.replace("`", "\"")
|
||||
.replace("INTEGER PRIMARY KEY AUTO_INCREMENT", "SERIAL PRIMARY KEY");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getAddFloodgateColumnStmt() {
|
||||
// PostgreSQL has a different syntax
|
||||
return ADD_FLOODGATE_COLUMN_STMT
|
||||
.replace("`", "\"")
|
||||
.replace("INTEGER(3)", "INTEGER");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getLoadByNameStmt() {
|
||||
return LOAD_BY_NAME_STMT
|
||||
.replace("`", "\"");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getLoadByUuidStmt() {
|
||||
return LOAD_BY_UUID_STMT
|
||||
.replace("`", "\"");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getInsertProfileStmt() {
|
||||
return INSERT_PROFILE_STMT
|
||||
.replace("`", "\"");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getUpdateProfileStmt() {
|
||||
return UPDATE_PROFILE_STMT
|
||||
.replace("`", "\"");
|
||||
}
|
||||
}
|
@ -26,11 +26,13 @@
|
||||
package com.github.games647.fastlogin.core.storage;
|
||||
|
||||
import com.github.games647.craftapi.UUIDAdapter;
|
||||
import com.github.games647.fastlogin.core.shared.FloodgateState;
|
||||
import com.zaxxer.hikari.HikariConfig;
|
||||
import com.zaxxer.hikari.HikariDataSource;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.DatabaseMetaData;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
@ -56,13 +58,19 @@ public abstract class SQLStorage implements AuthStorage {
|
||||
+ "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 (?, ?, ?, ?) ";
|
||||
protected static final String ADD_FLOODGATE_COLUMN_STMT = "ALTER TABLE `" + PREMIUM_TABLE
|
||||
+ "` ADD COLUMN `Floodgate` INTEGER(3)";
|
||||
|
||||
protected static final String LOAD_BY_NAME_STMT = "SELECT * FROM `" + PREMIUM_TABLE
|
||||
+ "` WHERE `Name`=? LIMIT 1";
|
||||
protected static final String LOAD_BY_UUID_STMT = "SELECT * FROM `" + PREMIUM_TABLE
|
||||
+ "` WHERE `UUID`=? LIMIT 1";
|
||||
protected static final String INSERT_PROFILE_STMT = "INSERT INTO `" + PREMIUM_TABLE
|
||||
+ "` (`UUID`, `Name`, `Premium`, `Floodgate`, `LastIp`) " + "VALUES (?, ?, ?, ?, ?) ";
|
||||
// limit not necessary here, because it's unique
|
||||
protected static final String UPDATE_PROFILE = "UPDATE `" + PREMIUM_TABLE
|
||||
+ "` SET `UUID`=?, `Name`=?, `Premium`=?, `LastIp`=?, `LastLogin`=CURRENT_TIMESTAMP WHERE `UserID`=?";
|
||||
protected static final String UPDATE_PROFILE_STMT = "UPDATE `" + PREMIUM_TABLE
|
||||
+ "` SET `UUID`=?, `Name`=?, `Premium`=?, `Floodgate`=?, `LastIp`=?, "
|
||||
+ "`LastLogin`=CURRENT_TIMESTAMP WHERE `UserID`=?";
|
||||
|
||||
protected final Logger log;
|
||||
protected final HikariDataSource dataSource;
|
||||
@ -81,23 +89,36 @@ public abstract class SQLStorage implements 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
|
||||
|
||||
//todo: add unique uuid index usage
|
||||
try (Connection con = dataSource.getConnection();
|
||||
Statement createStmt = con.createStatement()) {
|
||||
createStmt.executeUpdate(CREATE_TABLE_STMT);
|
||||
Statement stmt = con.createStatement()) {
|
||||
stmt.executeUpdate(getCreateTableStmt());
|
||||
|
||||
// add Floodgate column
|
||||
DatabaseMetaData md = con.getMetaData();
|
||||
if (isColumnMissing(md, "Floodgate")) {
|
||||
stmt.executeUpdate(getAddFloodgateColumnStmt());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isColumnMissing(DatabaseMetaData metaData, String columnName) throws SQLException {
|
||||
try (ResultSet rs = metaData.getColumns(null, null, PREMIUM_TABLE, columnName)) {
|
||||
return !rs.next();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public StoredProfile loadProfile(String name) {
|
||||
try (Connection con = dataSource.getConnection();
|
||||
PreparedStatement loadStmt = con.prepareStatement(LOAD_BY_NAME)
|
||||
PreparedStatement loadStmt = con.prepareStatement(getLoadByNameStmt())
|
||||
) {
|
||||
loadStmt.setString(1, name);
|
||||
|
||||
try (ResultSet resultSet = loadStmt.executeQuery()) {
|
||||
return parseResult(resultSet).orElseGet(() -> new StoredProfile(null, name, false, ""));
|
||||
return parseResult(resultSet).orElseGet(() -> new StoredProfile(null, name, false,
|
||||
FloodgateState.FALSE, ""));
|
||||
}
|
||||
} catch (SQLException sqlEx) {
|
||||
log.error("Failed to query profile: {}", name, sqlEx);
|
||||
@ -109,7 +130,7 @@ public abstract class SQLStorage implements AuthStorage {
|
||||
@Override
|
||||
public StoredProfile loadProfile(UUID uuid) {
|
||||
try (Connection con = dataSource.getConnection();
|
||||
PreparedStatement loadStmt = con.prepareStatement(LOAD_BY_UUID)) {
|
||||
PreparedStatement loadStmt = con.prepareStatement(getLoadByUuidStmt())) {
|
||||
loadStmt.setString(1, UUIDAdapter.toMojangId(uuid));
|
||||
|
||||
try (ResultSet resultSet = loadStmt.executeQuery()) {
|
||||
@ -124,15 +145,28 @@ public abstract class SQLStorage implements AuthStorage {
|
||||
|
||||
private Optional<StoredProfile> parseResult(ResultSet resultSet) throws SQLException {
|
||||
if (resultSet.next()) {
|
||||
long userId = resultSet.getInt(1);
|
||||
long userId = resultSet.getInt("UserID");
|
||||
|
||||
UUID uuid = Optional.ofNullable(resultSet.getString(2)).map(UUIDAdapter::parseId).orElse(null);
|
||||
UUID uuid = Optional.ofNullable(resultSet.getString("UUID"))
|
||||
.map(String::trim)
|
||||
.map(UUIDAdapter::parseId)
|
||||
.orElse(null);
|
||||
|
||||
String name = resultSet.getString(3);
|
||||
boolean premium = resultSet.getBoolean(4);
|
||||
String lastIp = resultSet.getString(5);
|
||||
Instant lastLogin = resultSet.getTimestamp(6).toInstant();
|
||||
return Optional.of(new StoredProfile(userId, uuid, name, premium, lastIp, lastLogin));
|
||||
String name = resultSet.getString("Name");
|
||||
boolean premium = resultSet.getBoolean("Premium");
|
||||
int floodgateNum = resultSet.getInt("Floodgate");
|
||||
FloodgateState floodgate;
|
||||
|
||||
// if the player wasn't migrated to the new database format
|
||||
if (resultSet.wasNull()) {
|
||||
floodgate = FloodgateState.NOT_MIGRATED;
|
||||
} else {
|
||||
floodgate = FloodgateState.fromInt(floodgateNum);
|
||||
}
|
||||
|
||||
String lastIp = resultSet.getString("LastIp");
|
||||
Instant lastLogin = resultSet.getTimestamp("LastLogin").toInstant();
|
||||
return Optional.of(new StoredProfile(userId, uuid, name, premium, floodgate, lastIp, lastLogin));
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
@ -146,22 +180,26 @@ public abstract class SQLStorage implements AuthStorage {
|
||||
playerProfile.getSaveLock().lock();
|
||||
try {
|
||||
if (playerProfile.isSaved()) {
|
||||
try (PreparedStatement saveStmt = con.prepareStatement(UPDATE_PROFILE)) {
|
||||
try (PreparedStatement saveStmt = con.prepareStatement(getUpdateProfileStmt())) {
|
||||
saveStmt.setString(1, uuid);
|
||||
saveStmt.setString(2, playerProfile.getName());
|
||||
saveStmt.setBoolean(3, playerProfile.isPremium());
|
||||
saveStmt.setString(4, playerProfile.getLastIp());
|
||||
saveStmt.setInt(4, playerProfile.getFloodgate().getValue());
|
||||
saveStmt.setString(5, playerProfile.getLastIp());
|
||||
|
||||
saveStmt.setLong(5, playerProfile.getRowId());
|
||||
saveStmt.setLong(6, playerProfile.getRowId());
|
||||
saveStmt.execute();
|
||||
}
|
||||
} else {
|
||||
try (PreparedStatement saveStmt = con.prepareStatement(INSERT_PROFILE, RETURN_GENERATED_KEYS)) {
|
||||
try (PreparedStatement saveStmt = con.prepareStatement(getInsertProfileStmt(),
|
||||
RETURN_GENERATED_KEYS)) {
|
||||
saveStmt.setString(1, uuid);
|
||||
|
||||
saveStmt.setString(2, playerProfile.getName());
|
||||
saveStmt.setBoolean(3, playerProfile.isPremium());
|
||||
saveStmt.setString(4, playerProfile.getLastIp());
|
||||
saveStmt.setBoolean(3, playerProfile.isPremium());
|
||||
saveStmt.setInt(4, playerProfile.getFloodgate().getValue());
|
||||
saveStmt.setString(5, playerProfile.getLastIp());
|
||||
|
||||
saveStmt.execute();
|
||||
try (ResultSet generatedKeys = saveStmt.getGeneratedKeys()) {
|
||||
@ -179,6 +217,38 @@ public abstract class SQLStorage implements AuthStorage {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* SQLite and PostgreSQL have a slightly different syntax, so this will be overridden by SQLiteStorage and so on...
|
||||
* @return An SQL Statement to create the `premium` table
|
||||
*/
|
||||
protected String getCreateTableStmt() {
|
||||
return CREATE_TABLE_STMT;
|
||||
}
|
||||
|
||||
/**
|
||||
* PostgreSQL has a slightly different syntax, so this will be overridden by PostgreSQLStorage
|
||||
* @return An SQL Statement to create the `premium` table
|
||||
*/
|
||||
protected String getAddFloodgateColumnStmt() {
|
||||
return ADD_FLOODGATE_COLUMN_STMT;
|
||||
}
|
||||
|
||||
protected String getLoadByNameStmt() {
|
||||
return LOAD_BY_NAME_STMT;
|
||||
}
|
||||
|
||||
protected String getLoadByUuidStmt() {
|
||||
return LOAD_BY_UUID_STMT;
|
||||
}
|
||||
|
||||
protected String getInsertProfileStmt() {
|
||||
return INSERT_PROFILE_STMT;
|
||||
}
|
||||
|
||||
protected String getUpdateProfileStmt() {
|
||||
return UPDATE_PROFILE_STMT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
dataSource.close();
|
||||
|
@ -31,15 +31,23 @@ import org.sqlite.JDBC;
|
||||
import org.sqlite.SQLiteConfig;
|
||||
|
||||
import java.nio.file.Path;
|
||||
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 {
|
||||
|
||||
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`) "
|
||||
+ ')';
|
||||
|
||||
private static final String SQLITE_DRIVER = "org.sqlite.SQLiteDataSource";
|
||||
private final Lock lock = new ReentrantLock();
|
||||
|
||||
@ -103,12 +111,9 @@ public class SQLiteStorage extends SQLStorage {
|
||||
}
|
||||
|
||||
@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"));
|
||||
}
|
||||
protected String getCreateTableStmt() {
|
||||
// SQLite has a different syntax for auto increment
|
||||
return CREATE_TABLE_STMT.replace("AUTO_INCREMENT", "AUTOINCREMENT");
|
||||
}
|
||||
|
||||
private static String replacePathVariables(Path dataFolder, String input) {
|
||||
|
@ -26,6 +26,7 @@
|
||||
package com.github.games647.fastlogin.core.storage;
|
||||
|
||||
import com.github.games647.craftapi.model.Profile;
|
||||
import com.github.games647.fastlogin.core.shared.FloodgateState;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Objects;
|
||||
@ -39,20 +40,28 @@ public class StoredProfile extends Profile {
|
||||
private final ReentrantLock saveLock = new ReentrantLock();
|
||||
|
||||
private boolean premium;
|
||||
private FloodgateState floodgate;
|
||||
private String lastIp;
|
||||
private Instant lastLogin;
|
||||
|
||||
public StoredProfile(long rowId, UUID uuid, String playerName, boolean premium, String lastIp, Instant lastLogin) {
|
||||
public StoredProfile(long rowId, UUID uuid, String playerName, boolean premium, FloodgateState floodgate,
|
||||
String lastIp, Instant lastLogin) {
|
||||
super(uuid, playerName);
|
||||
|
||||
this.rowId = rowId;
|
||||
this.premium = premium;
|
||||
this.floodgate = floodgate;
|
||||
this.lastIp = lastIp;
|
||||
this.lastLogin = lastLogin;
|
||||
}
|
||||
|
||||
public StoredProfile(UUID uuid, String playerName, boolean premium, FloodgateState isFloodgate, String lastIp) {
|
||||
this(-1, uuid, playerName, premium, isFloodgate, lastIp, Instant.now());
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public StoredProfile(UUID uuid, String playerName, boolean premium, String lastIp) {
|
||||
this(-1, uuid, playerName, premium, lastIp, Instant.now());
|
||||
this(-1, uuid, playerName, premium, FloodgateState.FALSE, lastIp, Instant.now());
|
||||
}
|
||||
|
||||
public ReentrantLock getSaveLock() {
|
||||
@ -96,6 +105,18 @@ public class StoredProfile extends Profile {
|
||||
this.premium = premium;
|
||||
}
|
||||
|
||||
public synchronized FloodgateState getFloodgate() {
|
||||
return floodgate;
|
||||
}
|
||||
|
||||
public synchronized boolean isFloodgateMigrated() {
|
||||
return floodgate != FloodgateState.NOT_MIGRATED;
|
||||
}
|
||||
|
||||
public synchronized void setFloodgate(FloodgateState floodgate) {
|
||||
this.floodgate = floodgate;
|
||||
}
|
||||
|
||||
public synchronized String getLastIp() {
|
||||
return lastIp;
|
||||
}
|
||||
@ -128,7 +149,7 @@ public class StoredProfile extends Profile {
|
||||
}
|
||||
|
||||
return rowId == that.rowId && premium == that.premium
|
||||
&& Objects.equals(lastIp, that.lastIp) && lastLogin.equals(that.lastLogin);
|
||||
&& Objects.equals(lastIp, that.lastIp) && lastLogin.equals(that.lastLogin);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -141,6 +162,7 @@ public class StoredProfile extends Profile {
|
||||
return this.getClass().getSimpleName() + '{'
|
||||
+ "rowId=" + rowId
|
||||
+ ", premium=" + premium
|
||||
+ ", floodgate=" + floodgate
|
||||
+ ", lastIp='" + lastIp + '\''
|
||||
+ ", lastLogin=" + lastLogin
|
||||
+ "} " + super.toString();
|
||||
|
@ -289,6 +289,15 @@ database: '{pluginDir}/FastLogin.db'
|
||||
#username: 'myUser'
|
||||
#password: 'myPassword'
|
||||
|
||||
# PostgreSQL
|
||||
# If you want to enable it, uncomment only the lines below; this not this line.
|
||||
#driver: 'postgresql'
|
||||
#host: '127.0.0.1'
|
||||
#port: 5432
|
||||
#database: 'fastlogin'
|
||||
#username: 'myUser'
|
||||
#password: 'myPassword'
|
||||
|
||||
# Advanced Connection Pool settings in seconds
|
||||
#timeout: 30
|
||||
#lifetime: 30
|
||||
|
@ -70,7 +70,7 @@ no-console: '&4You are not a player. You cannot toggle the premium state for YOU
|
||||
|
||||
# The user wants to toggle premium state, but BungeeCord support is enabled. This means the server have to communicate
|
||||
# with the BungeeCord first which will establish a connection with the database server.
|
||||
wait-on-proxy: '&6Sending request... (Do not forget to follow the BungeeCord setup guide)'
|
||||
wait-on-proxy: '&6Sending request... (Do not forget to follow the BungeeCord setup guide referenced on the plugin page)'
|
||||
|
||||
# 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 an encrypted connection or disconnect packet.
|
||||
@ -100,5 +100,3 @@ Type &a/premium&6 again to confirm'
|
||||
invalid-public-key: '&cInvalid client public key. Please try to restart your game.'
|
||||
|
||||
# ========= Bungee/Waterfall only ================================
|
||||
|
||||
|
||||
|
17
pom.xml
17
pom.xml
@ -103,7 +103,7 @@
|
||||
<plugin>
|
||||
<groupId>com.mycila</groupId>
|
||||
<artifactId>license-maven-plugin</artifactId>
|
||||
<version>4.1</version>
|
||||
<version>4.3</version>
|
||||
<configuration>
|
||||
<licenseSets>
|
||||
<licenseSet>
|
||||
@ -115,6 +115,7 @@
|
||||
<excludes>
|
||||
<exclude>src/test/resources/**</exclude>
|
||||
<exclude>src/main/resources/**</exclude>
|
||||
<exclude>.java-version</exclude>
|
||||
</excludes>
|
||||
</licenseSet>
|
||||
</licenseSets>
|
||||
@ -130,7 +131,7 @@
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||
<version>3.1.2</version>
|
||||
<version>3.3.1</version>
|
||||
<configuration>
|
||||
<configLocation>checkstyle.xml</configLocation>
|
||||
<consoleOutput>true</consoleOutput>
|
||||
@ -141,7 +142,7 @@
|
||||
<dependency>
|
||||
<groupId>com.puppycrawl.tools</groupId>
|
||||
<artifactId>checkstyle</artifactId>
|
||||
<version>10.9.3</version>
|
||||
<version>10.12.7</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<executions>
|
||||
@ -158,7 +159,7 @@
|
||||
<!-- Require newer versions for Junit5 support -->
|
||||
<plugin>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>2.22.2</version>
|
||||
<version>3.2.5</version>
|
||||
</plugin>
|
||||
</plugins>
|
||||
|
||||
@ -187,22 +188,22 @@
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.26</version>
|
||||
<version>1.18.30</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter</artifactId>
|
||||
<version>5.9.1</version>
|
||||
<version>5.10.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Require inline to support static mocks -->
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-inline</artifactId>
|
||||
<version>5.2.0</version>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<version>5.9.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
@ -55,7 +55,7 @@
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>templating-maven-plugin</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<version>3.0.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>filter-src</id>
|
||||
@ -67,7 +67,7 @@
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.4.1</version>
|
||||
<version>3.5.1</version>
|
||||
<configuration>
|
||||
<minimizeJar>true</minimizeJar>
|
||||
|
||||
@ -97,6 +97,10 @@
|
||||
<exclude>com.google.guava:guava</exclude>
|
||||
</excludes>
|
||||
</artifactSet>
|
||||
<transformers>
|
||||
<transformer
|
||||
implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
|
||||
</transformers>
|
||||
<filters>
|
||||
<filter>
|
||||
<artifact>*:*</artifact>
|
||||
@ -129,6 +133,10 @@
|
||||
<id>velocity</id>
|
||||
<url>https://repo.papermc.io/repository/maven-public/</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>opencollab-snapshot</id>
|
||||
<url>https://repo.opencollab.dev/main/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
@ -143,7 +151,7 @@
|
||||
<dependency>
|
||||
<groupId>com.velocitypowered</groupId>
|
||||
<artifactId>velocity-api</artifactId>
|
||||
<version>3.1.1</version>
|
||||
<version>3.3.0-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
@ -151,7 +159,29 @@
|
||||
<dependency>
|
||||
<groupId>org.mariadb.jdbc</groupId>
|
||||
<artifactId>mariadb-java-client</artifactId>
|
||||
<version>3.1.0</version>
|
||||
<version>3.3.2</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Bedrock player bridge -->
|
||||
<dependency>
|
||||
<groupId>org.geysermc.geyser</groupId>
|
||||
<artifactId>core</artifactId>
|
||||
<version>${geyser.version}</version>
|
||||
<scope>provided</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>*</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- Floodgate -->
|
||||
<dependency>
|
||||
<groupId>org.geysermc.floodgate</groupId>
|
||||
<artifactId>api</artifactId>
|
||||
<version>${floodgate.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
@ -27,6 +27,8 @@ 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.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.SuccessMessage;
|
||||
@ -50,6 +52,8 @@ import com.velocitypowered.api.proxy.messages.ChannelMessageSink;
|
||||
import com.velocitypowered.api.proxy.messages.ChannelRegistrar;
|
||||
import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
import org.geysermc.floodgate.api.FloodgateApi;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.io.IOException;
|
||||
@ -76,6 +80,8 @@ public class FastLoginVelocity implements PlatformPlugin<CommandSource> {
|
||||
|
||||
private FastLoginCore<Player, CommandSource, FastLoginVelocity> core;
|
||||
private AsyncScheduler scheduler;
|
||||
private FloodgateService floodgateService;
|
||||
private GeyserService geyserService;
|
||||
private UUID proxyId;
|
||||
|
||||
@Inject
|
||||
@ -95,6 +101,14 @@ public class FastLoginVelocity implements PlatformPlugin<CommandSource> {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isPluginInstalled("floodgate")) {
|
||||
floodgateService = new FloodgateService(FloodgateApi.getInstance(), core);
|
||||
}
|
||||
|
||||
if (isPluginInstalled("Geyser-Velocity")) {
|
||||
geyserService = new GeyserService(GeyserImpl.getInstance(), core);
|
||||
}
|
||||
|
||||
server.getEventManager().register(this, new ConnectListener(this, core.getAntiBot()));
|
||||
server.getEventManager().register(this, new PluginMessageListener(this));
|
||||
|
||||
@ -140,9 +154,21 @@ public class FastLoginVelocity implements PlatformPlugin<CommandSource> {
|
||||
return server.getPluginManager().isLoaded(name);
|
||||
}
|
||||
|
||||
public FloodgateService getFloodgateService() {
|
||||
return floodgateService;
|
||||
}
|
||||
|
||||
public GeyserService getGeyserService() {
|
||||
return geyserService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BedrockService<?> getBedrockService() {
|
||||
return null;
|
||||
if (floodgateService != null) {
|
||||
return floodgateService;
|
||||
}
|
||||
|
||||
return geyserService;
|
||||
}
|
||||
|
||||
public FastLoginCore<Player, CommandSource, FastLoginVelocity> getCore() {
|
||||
|
@ -53,10 +53,12 @@ public class VelocityLoginSource implements LoginSource {
|
||||
public void kick(String message) {
|
||||
if (message == null) {
|
||||
preLoginEvent.setResult(PreLoginEvent.PreLoginComponentResult.denied(
|
||||
Component.text("Kicked").color(NamedTextColor.WHITE)));
|
||||
Component.text("Kicked").color(NamedTextColor.WHITE))
|
||||
);
|
||||
} else {
|
||||
preLoginEvent.setResult(PreLoginEvent.PreLoginComponentResult.denied(
|
||||
LegacyComponentSerializer.legacyAmpersand().deserialize(message)));
|
||||
LegacyComponentSerializer.legacyAmpersand().deserialize(message))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -28,13 +28,15 @@ package com.github.games647.fastlogin.velocity.listener;
|
||||
import com.github.games647.craftapi.UUIDAdapter;
|
||||
import com.github.games647.fastlogin.core.antibot.AntiBotService;
|
||||
import com.github.games647.fastlogin.core.antibot.AntiBotService.Action;
|
||||
import com.github.games647.fastlogin.core.hooks.bedrock.FloodgateService;
|
||||
import com.github.games647.fastlogin.core.shared.LoginSession;
|
||||
import com.github.games647.fastlogin.core.storage.StoredProfile;
|
||||
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.FloodgateAuthTask;
|
||||
import com.github.games647.fastlogin.velocity.task.ForceLoginTask;
|
||||
import com.velocitypowered.api.event.Continuation;
|
||||
import com.velocitypowered.api.event.EventTask;
|
||||
import com.velocitypowered.api.event.Subscribe;
|
||||
import com.velocitypowered.api.event.connection.DisconnectEvent;
|
||||
import com.velocitypowered.api.event.connection.PreLoginEvent;
|
||||
@ -48,6 +50,7 @@ import com.velocitypowered.api.util.GameProfile;
|
||||
import com.velocitypowered.api.util.GameProfile.Property;
|
||||
import net.kyori.adventure.text.TextComponent;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
import org.geysermc.floodgate.api.player.FloodgatePlayer;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.ArrayList;
|
||||
@ -67,9 +70,9 @@ public class ConnectListener {
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onPreLogin(PreLoginEvent preLoginEvent, Continuation continuation) {
|
||||
public EventTask onPreLogin(PreLoginEvent preLoginEvent) {
|
||||
if (!preLoginEvent.getResult().isAllowed()) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
InboundConnection connection = preLoginEvent.getConnection();
|
||||
@ -81,28 +84,30 @@ public class ConnectListener {
|
||||
switch (action) {
|
||||
case Ignore:
|
||||
// just ignore
|
||||
return;
|
||||
return null;
|
||||
case Block:
|
||||
String message = plugin.getCore().getMessage("kick-antibot");
|
||||
TextComponent messageParsed = LegacyComponentSerializer.legacyAmpersand().deserialize(message);
|
||||
|
||||
PreLoginComponentResult reason = PreLoginComponentResult.denied(messageParsed);
|
||||
preLoginEvent.setResult(reason);
|
||||
break;
|
||||
return null;
|
||||
case Continue:
|
||||
default:
|
||||
Runnable asyncPremiumCheck = new AsyncPremiumCheck(
|
||||
plugin, connection, username, continuation, preLoginEvent
|
||||
return EventTask.async(
|
||||
new AsyncPremiumCheck(plugin, connection, username, preLoginEvent)
|
||||
);
|
||||
plugin.getScheduler().runAsync(asyncPremiumCheck);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onGameprofileRequest(GameProfileRequestEvent event) {
|
||||
public void onGameProfileRequest(GameProfileRequestEvent event) {
|
||||
if (event.isOnlineMode()) {
|
||||
LoginSession session = plugin.getSession().get(event.getConnection().getRemoteAddress());
|
||||
if (session == null) {
|
||||
plugin.getLog().warn("No active login session found for player {}", event.getUsername());
|
||||
return;
|
||||
}
|
||||
|
||||
UUID verifiedUUID = event.getGameProfile().getId();
|
||||
String verifiedUsername = event.getUsername();
|
||||
@ -141,8 +146,20 @@ public class ConnectListener {
|
||||
Player player = serverConnectedEvent.getPlayer();
|
||||
RegisteredServer server = serverConnectedEvent.getServer();
|
||||
|
||||
FloodgateService floodgateService = plugin.getFloodgateService();
|
||||
if (floodgateService != null) {
|
||||
FloodgatePlayer floodgatePlayer = floodgateService.getBedrockPlayer(player.getUniqueId());
|
||||
if (floodgatePlayer != null) {
|
||||
plugin.getLog().info("Running floodgate handling for {}", player);
|
||||
Runnable floodgateAuthTask = new FloodgateAuthTask(plugin.getCore(), player, floodgatePlayer, server);
|
||||
plugin.getScheduler().runAsync(floodgateAuthTask);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
VelocityLoginSession session = plugin.getSession().get(player.getRemoteAddress());
|
||||
if (session == null) {
|
||||
plugin.getLog().info("No active login session found on server connect for {}", player);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -151,7 +168,7 @@ public class ConnectListener {
|
||||
// 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))
|
||||
.buildTask(plugin, loginTask)
|
||||
.delay(1L, TimeUnit.SECONDS) // Delay at least one second, otherwise the login command can be missed
|
||||
.schedule();
|
||||
}
|
||||
|
@ -29,6 +29,7 @@ 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.core.storage.StoredProfile;
|
||||
import com.github.games647.fastlogin.core.hooks.bedrock.FloodgateService;
|
||||
import com.github.games647.fastlogin.velocity.FastLoginVelocity;
|
||||
import com.github.games647.fastlogin.velocity.VelocityLoginSession;
|
||||
import com.github.games647.fastlogin.velocity.task.AsyncToggleMessage;
|
||||
@ -115,7 +116,15 @@ public class PluginMessageListener {
|
||||
}
|
||||
|
||||
private void onSuccessMessage(Player forPlayer) {
|
||||
if (forPlayer.isOnlineMode()) {
|
||||
boolean shouldPersist = forPlayer.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
|
||||
VelocityLoginSession loginSession = plugin.getSession().get(forPlayer.getRemoteAddress());
|
||||
|
@ -33,7 +33,6 @@ 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;
|
||||
@ -45,28 +44,22 @@ public class AsyncPremiumCheck extends JoinManagement<Player, CommandSource, Vel
|
||||
|
||||
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) {
|
||||
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();
|
||||
}
|
||||
super.onLogin(username, new VelocityLoginSource(connection, preLoginEvent));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2023 games647 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.shared.FastLoginCore;
|
||||
import com.github.games647.fastlogin.core.shared.FloodgateManagement;
|
||||
import com.github.games647.fastlogin.velocity.FastLoginVelocity;
|
||||
import com.github.games647.fastlogin.velocity.VelocityLoginSession;
|
||||
import com.velocitypowered.api.command.CommandSource;
|
||||
import com.velocitypowered.api.proxy.Player;
|
||||
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
||||
import org.geysermc.floodgate.api.player.FloodgatePlayer;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class FloodgateAuthTask
|
||||
extends FloodgateManagement<Player, CommandSource, VelocityLoginSession, FastLoginVelocity> {
|
||||
|
||||
private final RegisteredServer server;
|
||||
|
||||
public FloodgateAuthTask(FastLoginCore<Player, CommandSource, FastLoginVelocity> core, Player player,
|
||||
FloodgatePlayer floodgatePlayer, RegisteredServer server) {
|
||||
super(core, player, floodgatePlayer);
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startLogin() {
|
||||
VelocityLoginSession session = new VelocityLoginSession(player.getUsername(), isRegistered, profile);
|
||||
core.getPlugin().getSession().put(player.getRemoteAddress(), session);
|
||||
|
||||
// enable auto login based on the value of 'autoLoginFloodgate' in config.yml
|
||||
boolean forcedOnlineMode = autoLoginFloodgate.equals("true")
|
||||
|| (autoLoginFloodgate.equals("linked") && isLinked);
|
||||
|
||||
// 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(core.getPlugin().getCore(), player, server, session, forcedOnlineMode);
|
||||
core.getPlugin().getProxy().getScheduler()
|
||||
.buildTask(core.getPlugin(), () -> core.getPlugin().getScheduler().runAsync(loginTask))
|
||||
.delay(1L, TimeUnit.SECONDS) // Delay at least one second, otherwise the login command can be missed
|
||||
.schedule();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getName(Player player) {
|
||||
return player.getUsername();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected UUID getUUID(Player player) {
|
||||
return player.getUniqueId();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected InetSocketAddress getAddress(Player player) {
|
||||
return player.getRemoteAddress();
|
||||
}
|
||||
}
|
@ -68,6 +68,7 @@ public class ForceLoginTask
|
||||
@Override
|
||||
public void run() {
|
||||
if (session == null) {
|
||||
core.getPlugin().getLog().info("No active login session on force handling for {}", player);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -80,6 +81,7 @@ public class ForceLoginTask
|
||||
@Override
|
||||
public boolean forceLogin(Player player) {
|
||||
if (session.isAlreadyLogged()) {
|
||||
core.getPlugin().getLog().info("Ignoring second force login attempt for {}", player);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -92,11 +94,11 @@ public class ForceLoginTask
|
||||
VelocityFastLoginAutoLoginEvent event = new VelocityFastLoginAutoLoginEvent(session, profile);
|
||||
try {
|
||||
return core.getPlugin().getProxy().getEventManager().fire(event).get();
|
||||
} catch (InterruptedException e) {
|
||||
} catch (InterruptedException interruptedEx) {
|
||||
Thread.currentThread().interrupt(); // Set the interrupt flag again
|
||||
return event;
|
||||
} catch (ExecutionException e) {
|
||||
core.getPlugin().getLog().error("Error firing event", e);
|
||||
} catch (ExecutionException executionEx) {
|
||||
core.getPlugin().getLog().error("Error firing event", executionEx);
|
||||
return event;
|
||||
}
|
||||
}
|
||||
@ -114,6 +116,8 @@ public class ForceLoginTask
|
||||
type = Type.REGISTER;
|
||||
}
|
||||
|
||||
core.getPlugin().getLog().info("Sending force {} for {} towards server {}", type, player.getUsername(), server);
|
||||
|
||||
UUID proxyId = core.getPlugin().getProxyId();
|
||||
ChannelMessage loginMessage = new LoginActionMessage(type, player.getUsername(), proxyId);
|
||||
core.getPlugin().sendPluginMessage(server, loginMessage);
|
||||
|
Reference in New Issue
Block a user