Compare commits

..
Author SHA1 Message Date
games647 9249c1ea8c Fix equality null test 2022-07-28 13:30:42 +02:00
games647 fd33523189 Perform equality check 2022-07-28 13:25:13 +02:00
games647 5af43170da Try to skip filtering 2022-07-28 13:16:25 +02:00
games647 eb51f0c83c Debug sending fake packet out 2022-07-28 13:05:46 +02:00
games647 5bdf2f4c9e Move debug message up 2022-07-27 15:08:03 +02:00
games647 155c98d601 Fix using asynchronous manager in synchronous mode 2022-07-27 14:56:59 +02:00
games647 a8f3155fc5 Add experimental synchronous processing 2022-07-27 14:56:59 +02:00
138 changed files with 1597 additions and 2861 deletions
+8 -6
View File
@@ -1,6 +1,6 @@
name: 🐞 Bug Report name: 🐞 Bug Report
description: Something isn't working, broken, not expected behavior description: Something isn't working, broken, not expected behavior
labels: [ bug ] labels: [bug]
body: body:
- type: markdown - type: markdown
attributes: attributes:
@@ -12,6 +12,10 @@ body:
description: What behavior is observed? description: What behavior is observed?
validations: validations:
required: true required: true
- type: textarea
attributes:
label: What did you expect?
description: What behavior is expected?
- type: textarea - type: textarea
attributes: attributes:
label: Steps to reproduce label: Steps to reproduce
@@ -32,7 +36,7 @@ body:
attributes: attributes:
label: Server log label: Server log
description: The error, stacktrace or link the complete log. You can use the links above for long versions. description: The error, stacktrace or link the complete log. You can use the links above for long versions.
placeholder: https://www.toptal.com/developers/hastebin / https://gist.github.com/ placeholder: https://hastebin.com/ / https://gist.github.com/
- type: input - type: input
attributes: attributes:
label: Plugin version label: Plugin version
@@ -55,11 +59,9 @@ body:
label: Relevance label: Relevance
description: Check list for previous tickets description: Check list for previous tickets
options: options:
- label: | - label: I tried the latest build (build refers to development builds not necessary a release version)
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 required: true
- label: | - label: |
I checked for existing tickets - I checked for existing tickets -
If there are, please vote them with a thumbs reaction and not create new ones If there are, please vote them with a thumps reaction and not create new ones
required: true required: true
@@ -0,0 +1,25 @@
---
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.)
@@ -1,43 +0,0 @@
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
+2 -18
View File
@@ -10,23 +10,7 @@ updates:
interval: "monthly" interval: "monthly"
# Maven project # Maven project
- package-ecosystem: "maven" - package-ecosystem: maven
directory: "/" directory: "/"
schedule: schedule:
interval: "monthly" 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"]
-3
View File
@@ -1,11 +1,8 @@
[//]: # (Lines in this format are considered as comments and will not be displayed.) [//]: # (Lines in this format are considered as comments and will not be displayed.)
[//]: # (If your work is in progress, please consider making a draft pull request.) [//]: # (If your work is in progress, please consider making a draft pull request.)
### Summary of your change ### Summary of your change
[//]: # (Example: motivation, enhancement) [//]: # (Example: motivation, enhancement)
### Related issue ### Related issue
[//]: # (Reference it using '#NUMBER'. Ex: Fixes/Related #...) [//]: # (Reference it using '#NUMBER'. Ex: Fixes/Related #...)
-25
View File
@@ -1,25 +0,0 @@
# Configure how the release notes are generated for GitHub releases
changelog:
# List of authors (like bots) and labels to exclude from pull requests
exclude:
labels:
- ignore-for-release
categories:
- title: 🛠 Breaking Changes
labels:
- Semver-Major
- breaking-change
- title: 🎉 Exciting New Features
labels:
- Semver-Minor
- enhancement
- title: 🐞 Bugfixes
labels:
- bug
- title: 👒 Dependencies
labels:
- dependencies
- title: Other Changes
labels:
- "*"
+48 -46
View File
@@ -4,61 +4,63 @@
name: "CodeQL" name: "CodeQL"
on: on:
workflow_run: # Scan only for push on the primary branch for now
workflows: ["Maven Build"] push:
branches: [main] branches: [ main ]
types: pull_request:
- completed branches: [ main ]
jobs: jobs:
# job i # job i
analyze: analyze:
# Display name # Display name
name: Analyze name: Analyze
# Environment # Environment
runs-on: ubuntu-latest runs-on: ubuntu-latest
timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }} permissions:
actions: read
contents: read
security-events: write
if: ${{ github.event.workflow_run.conclusion == 'success' }} strategy:
fail-fast: false
matrix:
# Languages to scan
language: [ 'java' ]
permissions: steps:
# Only allow write for security, then all others default to read only - name: Checkout repository
security-events: write uses: actions/checkout@v3
strategy: # Setup Java
fail-fast: true - name: Set up JDK
matrix: uses: actions/setup-java@v3
# Languages to scan with:
language: [ 'java' ] distribution: 'temurin'
java-version: 18
cache: 'maven'
steps: # Initializes the CodeQL tools for scanning.
- name: Checkout repository - name: Initialize CodeQL
uses: actions/checkout@v4 uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
# Initializes the CodeQL tools for scanning. # Cache build process too like in the maven config
- name: Initialize CodeQL - uses: actions/cache@v3.0.4
uses: github/codeql-action/init@v3 with:
with: path: ~/.m2/repository
languages: ${{ matrix.language }} key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
restore-keys: |
${{ runner.os }}-maven-
# Setup Java # Auto build attempts to build any compiled languages (C/C++, C#, or Java).
- name: Set up JDK # If this step fails, then you should remove it and run the build manually (see below)
uses: actions/setup-java@v4 - name: Autobuild
with: uses: github/codeql-action/autobuild@v2
distribution: 'temurin'
java-version-file: '.java-version'
cache: 'maven'
# Manually start the autobuild process, because autobuild always selects Java 8 as build toolchain, but - name: Perform CodeQL Analysis
# we are doing cross-crompiling from a newer Java version uses: github/codeql-action/analyze@v2
- 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@v3
with:
category: "/language:${{matrix.language}}"
+28 -53
View File
@@ -2,66 +2,41 @@
# including making pull requests review easier # including making pull requests review easier
# Human-readable name in the actions tab # Human-readable name in the actions tab
name: Maven Build name: Java CI
# Build on every pull request regardless of the branch # Build on every pull request regardless of the branch
# Wiki: https://help.github.com/en/actions/reference/events-that-trigger-workflows # Wiki: https://help.github.com/en/actions/reference/events-that-trigger-workflows
on: on:
push: push:
branches: branches:
- main - main
pull_request: pull_request:
branches: branches:
- main - main
jobs: jobs:
# job id # job id
build_and_test: build_and_test:
# Environment image - always use the newest OS # Environment image - always use the newest OS
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions:
# With at least one permission given, all default to read
contents: read
# Run steps # Run steps
steps: steps:
# Pull changes # Pull changes
- uses: actions/checkout@v4 - uses: actions/checkout@v3
# Setup Java # Setup Java
- name: Set up JDK - name: Set up JDK
uses: actions/setup-java@v4 uses: actions/setup-java@v3
with: with:
distribution: 'temurin' distribution: 'temurin'
java-version-file: '.java-version' java-version: 18
cache: 'maven' cache: 'maven'
# Build and test (included in package) # Build and test (included in package)
- name: Build with Maven and test - name: Build with Maven and test
# Run non-interactive, package (with compile+test), # Run non-interactive, package (with compile+test),
# ignore snapshot updates, because they are likely to have breaking changes, enforce checksums # ignore snapshot updates, because they are likely to have breaking changes, enforce checksums to validate
run: mvn test --batch-mode --threads 2.0C --no-snapshot-updates --strict-checksums --file pom.xml # possible errors in dependencies
run: mvn test --batch-mode --threads 2.0C --no-snapshot-updates --strict-checksums --file pom.xml
dependency:
runs-on: ubuntu-latest
permissions:
# Write only necessary for dependency submission all others then default to read
contents: write
# Run steps
steps:
# Pull changes
- uses: actions/checkout@v4
# Setup Java
- name: Set up JDK
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version-file: '.java-version'
cache: 'maven'
- name: Submit Dependency Snapshot
if: ${{ github.event_name == 'push' }}
uses: advanced-security/maven-dependency-submission-action@v5.0.0
-2
View File
@@ -1,2 +0,0 @@
# Latest GitHub Action java release that is installed on the runners
21
-71
View File
@@ -1,71 +0,0 @@
# Contributing Guide
## Architectural overview
```mermaid
graph TB
subgraph "Minecraft Server Platforms"
SPIGOT["Spigot/Paper<br/>(Bukkit Module)"]
BUNGEE["BungeeCord<br/>(Proxy Module)"]
VELOCITY["Velocity<br/>(Velocity Module)"]
end
subgraph "FastLogin Core"
CORE["FastLoginCore<br/>Main Logic Engine"]
SESSION["LoginSession<br/>Session Management"]
AUTH["AuthPlugin Hook<br/>Auth Integration"]
RESOLVER["ProxyAgnosticMojangResolver<br/>Profile Resolution"]
STORAGE["SQLStorage<br/>Database Layer"]
ANTIBOT["AntiBotService<br/>Rate Limiting"]
end
subgraph "Bedrock Support"
FLOODGATE["FloodgateManagement<br/>Bedrock Players"]
GEYSER["GeyserService<br/>Geyser Integration"]
BEDROCK["BedrockService<br/>Base Service"]
end
subgraph "External Services"
MOJANG["Mojang API<br/>api.mojang.com"]
SESSION_SERVER["Session Server<br/>sessionserver.mojang.com"]
DATABASE[(SQL Database<br/>MySQL/SQLite)]
end
subgraph "Async Processing"
SCHEDULER["AbstractAsyncScheduler<br/>Thread Pool Management"]
end
subgraph "Messaging"
MESSAGES["ChannelMessage<br/>Proxy Messages <br/>(i.e. BungeeCord)"]
NAMEKEY["NamespaceKey<br/>Message Routing"]
end
SPIGOT -->|loads| CORE
BUNGEE -->|loads| CORE
VELOCITY -->|loads| CORE
CORE -->|manages| SESSION
CORE -->|uses| AUTH
CORE -->|resolves profiles| RESOLVER
CORE -->|persists data| STORAGE
CORE -->|checks rate limits| ANTIBOT
CORE -->|handles bedrock| FLOODGATE
FLOODGATE -->|extends| BEDROCK
GEYSER -->|extends| BEDROCK
RESOLVER -->|queries| MOJANG
RESOLVER -->|verifies| SESSION_SERVER
STORAGE -->|connects to| DATABASE
CORE -->|schedules async| SCHEDULER
MESSAGES -->|uses| NAMEKEY
CORE -->|sends via| MESSAGES
AUTH -.->|delegates to| SPIGOT
AUTH -.->|delegates to| BUNGEE
ANTIBOT -->|rate limits| RESOLVER
```
+2 -1
View File
@@ -1,6 +1,6 @@
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2015-2024 games647 and contributors Copyright (c) 2015-2022 games647 and contributors
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
@@ -19,3 +19,4 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 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 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
+14 -47
View File
@@ -1,7 +1,5 @@
# FastLogin # FastLogin
![A shield-shaped emblem with a bold lightning bolt on the left, resembling Minecraft blocks. To the right, "FastLogin" is written in teal, with the tagline: "Automatically detect and login premium Minecraft players"](https://github.com/user-attachments/assets/0788ef69-029b-465e-83a2-b8e7bccc6295 "FastLogin project logo.avif")
Checks if a Minecraft player has a paid account (premium). If so, they can skip offline authentication (auth plugins). Checks if a Minecraft player has a paid account (premium). If so, they can skip offline authentication (auth plugins).
So they don't need to enter passwords. This is also called auto login (auto-login). So they don't need to enter passwords. This is also called auto login (auto-login).
@@ -10,11 +8,14 @@ So they don't need to enter passwords. This is also called auto login (auto-logi
* Detect paid accounts from others * Detect paid accounts from others
* Automatically login paid accounts (premium) * Automatically login paid accounts (premium)
* Support various of auth plugins * Support various of auth plugins
* Cauldron support
* Forge/Sponge message support
* Premium UUID support * Premium UUID support
* Forward skins * Forward skins
* Detect username changed and will update the existing database record * Detect username changed and will update the existing database record
* BungeeCord/Velocity support * BungeeCord support
* Auto register new premium players * Auto register new premium players
* Plugin: ProtocolSupport is supported and can be used as an alternative to ProtocolLib
* No client modifications needed * No client modifications needed
* Good performance by using async operations * Good performance by using async operations
* Locale messages * Locale messages
@@ -32,42 +33,10 @@ Development builds contain the latest changes from the Source-Code. They are ble
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. side on `Changes`, you can see iterative change sets leading to a specific build.
You can download them from here: [CodeMC(Jenkins)](https://ci.codemc.org/job/Games647/job/FastLogin/) You can download them from here: https://ci.codemc.org/job/Games647/job/FastLogin/
*** ***
## Technical Authentication
```mermaid
sequenceDiagram
autonumber
participant C as Client
participant S as Server
participant M as Mojang
C->>S: LOGIN_START (name)
Note over S: DB: check if username is saved as premium
S->>C: ENCRYPTION_REQUEST
Note right of C: Offline clients terminates connection here
Note right of C: In Offlinemode: LOGIN_SUCCESS is sent directly
rect rgb(240, 240, 240)
C->>M: POST /session/minecraft/join
C->>S: ENCRYPTION_RESPONSE
Note right of C: Client starts encrypting
end
Note over S: Decrypt and verify token
S->>M: GET /hasJoined (async)
M->>S: {uuid, name, skin}
Note over S: Server encrypts traffic
Note over S: Inject skin and premium UUID
S->>S: Re-inject LOGIN_START(name)
S->>C: LOGIN_SUCCESS
```
## Commands ## Commands
/premium [player] Label the invoker or the argument as paid account /premium [player] Label the invoker or the argument as paid account
@@ -77,7 +46,6 @@ sequenceDiagram
fastlogin.bukkit.command.premium fastlogin.bukkit.command.premium
fastlogin.bukkit.command.cracked fastlogin.bukkit.command.cracked
fastlogin.command.premium.other fastlogin.command.premium.other
fastlogin.command.cracked.other fastlogin.command.cracked.other
@@ -92,15 +60,13 @@ Possible values: `Premium`, `Cracked`, `Unknown`
## Requirements ## Requirements
* Java: 21+ recommended for improved multi-threading code by FastLogin * Java 17+
* Spigot: 8+
* BungeeCord and Velocity: 17+
* Server software in offlinemode: * Server software in offlinemode:
* Spigot (or a fork e.g. Paper) 1.8.8+ * Spigot (or a fork e.g. Paper) 1.8.8+
* Protocol plugin: * Protocol plugin:
* [ProtocolLib 5.3+ with development build above 720](https://www.spigotmc.org/resources/protocollib.1997/) or * [ProtocolLib 5.0+](https://www.spigotmc.org/resources/protocollib.1997/) or
* [ProtocolSupport](https://www.spigotmc.org/resources/protocolsupport.7201/) * [ProtocolSupport](https://www.spigotmc.org/resources/protocolsupport.7201/)
* Latest BungeeCord (or a fork e.g. Waterfall) or Velocity proxy * Latest BungeeCord (or a fork e.g. Waterfall)
* An auth plugin. * An auth plugin.
### Supported auth plugins ### Supported auth plugins
@@ -119,6 +85,7 @@ Possible values: `Premium`, `Cracked`, `Unknown`
#### BungeeCord/Waterfall #### BungeeCord/Waterfall
* [BungeeAuth](https://www.spigotmc.org/resources/bungeeauth.493/) * [BungeeAuth](https://www.spigotmc.org/resources/bungeeauth.493/)
* [BungeeAuthenticator](https://www.spigotmc.org/resources/bungeecordauthenticator.87669/)
## Network requests ## Network requests
@@ -134,8 +101,8 @@ This plugin performs network requests to:
### Spigot/Paper ### Spigot/Paper
1. Download and install ProtocolLib/ProtocolSupport 1. Download and install ProtocolLib/ProtocolSupport
2. Download and install `FastLoginBukkit` 2. Download and install FastLogin (or `FastLoginBukkit` for newer versions)
3. Set your server in offline mode by setting the value `onlinemode` in your server.properties to `false` 3. Set your server in offline mode by setting the value `onlinemode` in your server.properties to false
### BungeeCord/Waterfall or Velocity ### BungeeCord/Waterfall or Velocity
@@ -145,13 +112,13 @@ Install the plugin on both platforms, that is proxy (BungeeCord or Velocity) and
* This is often found in `spigot.yml` or `paper.yml` * This is often found in `spigot.yml` or `paper.yml`
2. Restart the backend server 2. Restart the backend server
3. Now there is `allowed-proxies.txt` file in the FastLogin folder of the restarted server 3. Now there is `allowed-proxies.txt` file in the FastLogin folder of the restarted server
* BungeeCord: Put your `stats`-id from the BungeeCord config into this file * BungeeCord: Put your `stats-id` from the BungeeCord config into this file
* Velocity: On plugin startup the plugin generates a `proxyId.txt` inside the plugins folder of the proxy * Velocity: On plugin startup the plugin generates a `proxyId.txt` inside the plugins folder of the proxy
4. Activate ip forwarding in your proxy config 4. Activate ip forwarding in your proxy config
5. Check your database settings in the config of FastLogin on your proxy 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: * The proxies only ship with a limited set of drivers where Spigot supports more. Therefore, these are supported:
* BungeeCord: `mysql` for MySQL/MariaDB * BungeeCord: `com.mysql.jdbc.Driver` for MySQL/MariaDB
* Velocity: `mariadb` for MySQL/MariaDB * Velocity: `fastlogin.mariadb.jdbc.Driver` for MySQL/MariaDB
* Note the embedded file storage SQLite is not available * 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 requires an external database server running. Check your server provider if there is one available
or install one. or install one.
+53 -58
View File
@@ -4,7 +4,7 @@
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2015-2024 games647 and contributors Copyright (c) 2015-2022 games647 and contributors
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
@@ -25,12 +25,12 @@
SOFTWARE. SOFTWARE.
--> -->
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0" <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"> 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> <modelVersion>4.0.0</modelVersion>
<properties> <properties>
<maven.compiler.release>8</maven.compiler.release> <nettyVersion>4.1.77.Final</nettyVersion>
</properties> </properties>
<parent> <parent>
@@ -50,15 +50,11 @@
<plugins> <plugins>
<plugin> <plugin>
<artifactId>maven-shade-plugin</artifactId> <artifactId>maven-shade-plugin</artifactId>
<version>3.6.0</version> <version>3.3.0</version>
<configuration> <configuration>
<createDependencyReducedPom>false</createDependencyReducedPom> <createDependencyReducedPom>false</createDependencyReducedPom>
<shadedArtifactAttached>false</shadedArtifactAttached> <shadedArtifactAttached>false</shadedArtifactAttached>
<relocations> <relocations>
<relocation>
<pattern>org.yaml.snakeyaml</pattern>
<shadedPattern>fastlogin.yaml</shadedPattern>
</relocation>
<relocation> <relocation>
<pattern>com.zaxxer.hikari</pattern> <pattern>com.zaxxer.hikari</pattern>
<shadedPattern>fastlogin.hikari</shadedPattern> <shadedPattern>fastlogin.hikari</shadedPattern>
@@ -90,14 +86,14 @@
<!-- Rename the service file too to let SLF4J api find our own relocated jdk logger --> <!-- Rename the service file too to let SLF4J api find our own relocated jdk logger -->
<!-- Located in META-INF/services --> <!-- Located in META-INF/services -->
<transformers> <transformers>
<transformer <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
</transformers> </transformers>
<minimizeJar>true</minimizeJar> <minimizeJar>true</minimizeJar>
<filters> <filters>
<filter> <filter>
<artifact>*:*</artifact> <artifact>*:*</artifact>
<excludes> <excludes>
<exclude>META-INF/MANIFEST.MF</exclude>
<exclude>**/module-info.class</exclude> <exclude>**/module-info.class</exclude>
</excludes> </excludes>
</filter> </filter>
@@ -116,10 +112,10 @@
</build> </build>
<repositories> <repositories>
<!-- PaperSpigot API, PaperLib, datafixupper and bungeecord-chat --> <!-- PaperSpigot API and PaperLib -->
<repository> <repository>
<id>papermc</id> <id>papermc</id>
<url>https://repo.papermc.io/repository/maven-public/</url> <url>https://papermc.io/repo/repository/maven-public/</url>
</repository> </repository>
<!-- ProtocolLib --> <!-- ProtocolLib -->
@@ -168,7 +164,7 @@
<dependency> <dependency>
<groupId>io.papermc.paper</groupId> <groupId>io.papermc.paper</groupId>
<artifactId>paper-api</artifactId> <artifactId>paper-api</artifactId>
<version>1.21.6-R0.1-SNAPSHOT</version> <version>1.19-R0.1-SNAPSHOT</version>
<scope>provided</scope> <scope>provided</scope>
<!-- Use our own newer api version --> <!-- Use our own newer api version -->
<exclusions> <exclusions>
@@ -176,25 +172,20 @@
<groupId>org.slf4j</groupId> <groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId> <artifactId>slf4j-api</artifactId>
</exclusion> </exclusion>
<exclusion>
<groupId>org.ow2.asm</groupId>
<artifactId>*</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.maven</groupId>
<artifactId>*</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions> </exclusions>
</dependency> </dependency>
<!-- PaperLib for checking if server uses PaperSpigot -->
<dependency>
<groupId>io.papermc</groupId>
<artifactId>paperlib</artifactId>
<version>1.0.7</version>
</dependency>
<dependency> <dependency>
<groupId>com.mojang</groupId> <groupId>com.mojang</groupId>
<artifactId>datafixerupper</artifactId> <artifactId>datafixerupper</artifactId>
<version>7.1.15</version> <version>5.0.28</version>
<scope>provided</scope> <scope>provided</scope>
<exclusions> <exclusions>
<exclusion> <exclusion>
@@ -208,7 +199,7 @@
<dependency> <dependency>
<groupId>com.comphenix.protocol</groupId> <groupId>com.comphenix.protocol</groupId>
<artifactId>ProtocolLib</artifactId> <artifactId>ProtocolLib</artifactId>
<version>5.3.0</version> <version>5.0.0-SNAPSHOT</version>
<scope>provided</scope> <scope>provided</scope>
<exclusions> <exclusions>
<exclusion> <exclusion>
@@ -237,11 +228,15 @@
<dependency> <dependency>
<groupId>org.geysermc.floodgate</groupId> <groupId>org.geysermc.floodgate</groupId>
<artifactId>api</artifactId> <artifactId>api</artifactId>
<version>${floodgate.version}</version> <version>2.2.0-SNAPSHOT</version>
<scope>provided</scope> <scope>provided</scope>
<exclusions> <exclusions>
<exclusion> <exclusion>
<groupId>*</groupId> <groupId>io.netty</groupId>
<artifactId>*</artifactId>
</exclusion>
<exclusion>
<groupId>org.geysermc.cumulus</groupId>
<artifactId>*</artifactId> <artifactId>*</artifactId>
</exclusion> </exclusion>
</exclusions> </exclusions>
@@ -249,7 +244,7 @@
<!-- Bedrock player bridge --> <!-- Bedrock player bridge -->
<dependency> <dependency>
<groupId>org.geysermc.geyser</groupId> <groupId>org.geysermc</groupId>
<artifactId>core</artifactId> <artifactId>core</artifactId>
<version>${geyser.version}</version> <version>${geyser.version}</version>
<scope>provided</scope> <scope>provided</scope>
@@ -263,23 +258,17 @@
<!-- We need the API, but it was excluded above --> <!-- We need the API, but it was excluded above -->
<dependency> <dependency>
<groupId>org.geysermc.geyser</groupId> <groupId>org.geysermc</groupId>
<artifactId>api</artifactId> <artifactId>geyser-api</artifactId>
<version>${geyser.version}</version> <version>${geyser.version}</version>
<scope>provided</scope> <scope>provided</scope>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency> </dependency>
<!--Provide premium placeholders--> <!--Provide premium placeholders-->
<dependency> <dependency>
<groupId>me.clip</groupId> <groupId>me.clip</groupId>
<artifactId>placeholderapi</artifactId> <artifactId>placeholderapi</artifactId>
<version>2.11.6</version> <version>2.11.2</version>
<scope>provided</scope> <scope>provided</scope>
<optional>true</optional> <optional>true</optional>
<exclusions> <exclusions>
@@ -294,7 +283,7 @@
<dependency> <dependency>
<groupId>fr.xephi</groupId> <groupId>fr.xephi</groupId>
<artifactId>authme</artifactId> <artifactId>authme</artifactId>
<version>5.6.0</version> <version>5.6.0-beta2</version>
<scope>provided</scope> <scope>provided</scope>
<optional>true</optional> <optional>true</optional>
<exclusions> <exclusions>
@@ -308,7 +297,7 @@
<dependency> <dependency>
<groupId>com.lenis0012.bukkit</groupId> <groupId>com.lenis0012.bukkit</groupId>
<artifactId>loginsecurity</artifactId> <artifactId>loginsecurity</artifactId>
<version>3.3.0</version> <version>3.1</version>
<scope>provided</scope> <scope>provided</scope>
<optional>true</optional> <optional>true</optional>
<exclusions> <exclusions>
@@ -348,22 +337,6 @@
</exclusions> </exclusions>
</dependency> </dependency>
<dependency>
<groupId>com.github.zigazajc007</groupId>
<artifactId>Passky</artifactId>
<version>v3.0.0</version>
<scope>provided</scope>
<optional>true</optional>
<!-- Exclude dependencies to prevent potential version conflicts-->
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--No maven repository :(--> <!--No maven repository :(-->
<dependency> <dependency>
<groupId>de.st_ddt.crazy</groupId> <groupId>de.st_ddt.crazy</groupId>
@@ -395,7 +368,29 @@
<dependency> <dependency>
<groupId>org.bouncycastle</groupId> <groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk18on</artifactId> <artifactId>bcprov-jdk18on</artifactId>
<version>1.80</version> <version>1.71</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-transport</artifactId>
<version>${nettyVersion}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-codec</artifactId>
<version>${nettyVersion}</version>
<scope>test</scope>
</dependency>
<!-- Provided by the spigot, required for testing ProtocolLib -->
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
</dependencies> </dependencies>
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -27,15 +27,16 @@ package com.github.games647.fastlogin.bukkit;
import com.github.games647.craftapi.model.skin.SkinProperty; import com.github.games647.craftapi.model.skin.SkinProperty;
import com.github.games647.fastlogin.bukkit.listener.protocollib.packet.ClientPublicKey; import com.github.games647.fastlogin.bukkit.listener.protocollib.packet.ClientPublicKey;
import com.github.games647.fastlogin.core.StoredProfile;
import com.github.games647.fastlogin.core.shared.LoginSession; import com.github.games647.fastlogin.core.shared.LoginSession;
import com.github.games647.fastlogin.core.storage.StoredProfile;
import org.jetbrains.annotations.Nullable;
import java.util.Optional; import java.util.Optional;
import javax.annotation.Nullable;
/** /**
* Represents a client connecting to the server. * Represents a client connecting to the server.
* <p> *
* This session is invalid if the player disconnects or the login was successful * This session is invalid if the player disconnects or the login was successful
*/ */
public class BukkitLoginSession extends LoginSession { public class BukkitLoginSession extends LoginSession {
@@ -47,6 +48,7 @@ public class BukkitLoginSession extends LoginSession {
private final ClientPublicKey clientPublicKey; private final ClientPublicKey clientPublicKey;
private boolean verified; private boolean verified;
private SkinProperty skinProperty; private SkinProperty skinProperty;
public BukkitLoginSession(String username, byte[] verifyToken, ClientPublicKey publicKey, boolean registered, public BukkitLoginSession(String username, byte[] verifyToken, ClientPublicKey publicKey, boolean registered,
@@ -74,7 +76,7 @@ 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.
* <p> *
* Empty if it's a BungeeCord connection * Empty if it's a BungeeCord connection
* *
* @return verify token from the server * @return verify token from the server
@@ -108,7 +110,7 @@ public class BukkitLoginSession extends LoginSession {
* *
* @param verified whether the player has valid session * @param verified whether the player has valid session
*/ */
public synchronized void setVerifiedPremium(boolean verified) { public synchronized void setVerified(boolean verified) {
this.verified = verified; this.verified = verified;
} }
@@ -117,7 +119,7 @@ public class BukkitLoginSession extends LoginSession {
* *
* @return whether the player has a valid session * @return whether the player has a valid session
*/ */
public synchronized boolean isVerifiedPremium() { public synchronized boolean isVerified() {
return verified; return verified;
} }
} }
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -25,13 +25,14 @@
*/ */
package com.github.games647.fastlogin.bukkit; package com.github.games647.fastlogin.bukkit;
import com.github.games647.fastlogin.core.scheduler.AsyncScheduler; import com.github.games647.fastlogin.core.AsyncScheduler;
import java.util.concurrent.Executor;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.plugin.Plugin; import org.bukkit.plugin.Plugin;
import org.slf4j.Logger; import org.slf4j.Logger;
import java.util.concurrent.Executor;
public class BukkitScheduler extends AsyncScheduler { public class BukkitScheduler extends AsyncScheduler {
private final Executor syncExecutor; private final Executor syncExecutor;
@@ -39,7 +40,7 @@ public class BukkitScheduler extends AsyncScheduler {
public BukkitScheduler(Plugin plugin, Logger logger) { public BukkitScheduler(Plugin plugin, Logger logger) {
super(logger, command -> Bukkit.getScheduler().runTaskAsynchronously(plugin, command)); super(logger, command -> Bukkit.getScheduler().runTaskAsynchronously(plugin, command));
syncExecutor = task -> Bukkit.getScheduler().runTask(plugin, task); syncExecutor = r -> Bukkit.getScheduler().runTask(plugin, r);
} }
public Executor getSyncExecutor() { public Executor getSyncExecutor() {
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -31,11 +31,6 @@ import com.github.games647.fastlogin.core.message.LoginActionMessage;
import com.github.games647.fastlogin.core.message.NamespaceKey; import com.github.games647.fastlogin.core.message.NamespaceKey;
import com.google.common.io.ByteArrayDataOutput; import com.google.common.io.ByteArrayDataOutput;
import com.google.common.io.ByteStreams; import com.google.common.io.ByteStreams;
import io.papermc.paper.configuration.ServerConfiguration;
import org.bukkit.Bukkit;
import org.bukkit.Server;
import org.bukkit.entity.Player;
import org.bukkit.plugin.messaging.PluginMessageRecipient;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.Field; import java.lang.reflect.Field;
@@ -49,6 +44,11 @@ import java.util.Set;
import java.util.UUID; import java.util.UUID;
import java.util.stream.Stream; import java.util.stream.Stream;
import org.bukkit.Bukkit;
import org.bukkit.Server;
import org.bukkit.entity.Player;
import org.bukkit.plugin.messaging.PluginMessageRecipient;
import static com.github.games647.fastlogin.core.message.ChangePremiumMessage.CHANGE_CHANNEL; import static com.github.games647.fastlogin.core.message.ChangePremiumMessage.CHANGE_CHANNEL;
import static com.github.games647.fastlogin.core.message.SuccessMessage.SUCCESS_CHANNEL; import static com.github.games647.fastlogin.core.message.SuccessMessage.SUCCESS_CHANNEL;
import static java.util.stream.Collectors.toSet; import static java.util.stream.Collectors.toSet;
@@ -94,10 +94,6 @@ public class BungeeManager {
if (enabled) { if (enabled) {
proxyIds = loadBungeeCordIds(); proxyIds = loadBungeeCordIds();
if (proxyIds.isEmpty()) {
plugin.getLog().info("No valid IDs found. Minecraft proxy support cannot work in the current state");
}
registerPluginChannels(); registerPluginChannels();
plugin.getLog().info("Found enabled proxy configuration"); plugin.getLog().info("Found enabled proxy configuration");
plugin.getLog().info("Remember to follow the proxy guide to complete your setup"); plugin.getLog().info("Remember to follow the proxy guide to complete your setup");
@@ -134,13 +130,6 @@ public class BungeeManager {
} }
private boolean detectProxy() { private boolean detectProxy() {
try {
ServerConfiguration.class.getDeclaredMethod("isProxyEnabled");
return Bukkit.getServerConfig().isProxyEnabled();
} catch (NoClassDefFoundError | NoSuchMethodException noSuchClassMethodEx) {
// Ignore continue below
}
try { try {
if (isProxySupported("org.spigotmc.SpigotConfig", "bungee")) { if (isProxySupported("org.spigotmc.SpigotConfig", "bungee")) {
return true; return true;
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -25,24 +25,33 @@
*/ */
package com.github.games647.fastlogin.bukkit; package com.github.games647.fastlogin.bukkit;
import com.comphenix.protocol.ProtocolLibrary;
import com.github.games647.fastlogin.bukkit.command.CrackedCommand; import com.github.games647.fastlogin.bukkit.command.CrackedCommand;
import com.github.games647.fastlogin.bukkit.command.PremiumCommand; import com.github.games647.fastlogin.bukkit.command.PremiumCommand;
import com.github.games647.fastlogin.bukkit.command.DeleteCommand;
import com.github.games647.fastlogin.bukkit.listener.ConnectionListener; import com.github.games647.fastlogin.bukkit.listener.ConnectionListener;
import com.github.games647.fastlogin.bukkit.listener.PaperCacheListener; 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.ProtocolLibListener;
import com.github.games647.fastlogin.bukkit.listener.protocollib.SkinApplyListener; import com.github.games647.fastlogin.bukkit.listener.protocollib.SkinApplyListener;
import com.github.games647.fastlogin.bukkit.listener.protocolsupport.ProtocolSupportListener; import com.github.games647.fastlogin.bukkit.listener.protocolsupport.ProtocolSupportListener;
import com.github.games647.fastlogin.bukkit.task.DelayedAuthHook; import com.github.games647.fastlogin.bukkit.task.DelayedAuthHook;
import com.github.games647.fastlogin.core.CommonUtil; import com.github.games647.fastlogin.core.CommonUtil;
import com.github.games647.fastlogin.core.PremiumStatus; import com.github.games647.fastlogin.core.PremiumStatus;
import com.github.games647.fastlogin.core.antibot.AntiBotService;
import com.github.games647.fastlogin.core.hooks.bedrock.BedrockService; 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.FloodgateService;
import com.github.games647.fastlogin.core.hooks.bedrock.GeyserService; import com.github.games647.fastlogin.core.hooks.bedrock.GeyserService;
import com.github.games647.fastlogin.core.shared.FastLoginCore; import com.github.games647.fastlogin.core.shared.FastLoginCore;
import com.github.games647.fastlogin.core.shared.PlatformPlugin; import com.github.games647.fastlogin.core.shared.PlatformPlugin;
import io.papermc.lib.PaperLib;
import java.net.InetSocketAddress;
import java.nio.file.Path;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@@ -50,28 +59,15 @@ import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.plugin.java.JavaPlugin;
import org.geysermc.floodgate.api.FloodgateApi; import org.geysermc.floodgate.api.FloodgateApi;
import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserImpl;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger; import org.slf4j.Logger;
import java.net.InetSocketAddress;
import java.nio.file.Path;
import java.time.Duration;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/** /**
* This plugin checks if a player has a paid account and if so tries to skip offline mode authentication. * This plugin checks if a player has a paid account and if so tries to skip offline mode authentication.
*/ */
public class FastLoginBukkit extends JavaPlugin implements PlatformPlugin<CommandSender> { public class FastLoginBukkit extends JavaPlugin implements PlatformPlugin<CommandSender> {
//1 minutes should be enough as a timeout for bad internet connection (Server, Client and Mojang) //1 minutes should be enough as a timeout for bad internet connection (Server, Client and Mojang)
private final ConcurrentMap<String, BukkitLoginSession> loginSession = CommonUtil.buildCache( private final ConcurrentMap<String, BukkitLoginSession> loginSession = CommonUtil.buildCache(1, -1);
Duration.ofMinutes(1), -1
);
private final Map<UUID, PremiumStatus> premiumPlayers = new ConcurrentHashMap<>(); private final Map<UUID, PremiumStatus> premiumPlayers = new ConcurrentHashMap<>();
private final Logger logger; private final Logger logger;
@@ -117,14 +113,17 @@ public class FastLoginBukkit extends JavaPlugin implements PlatformPlugin<Comman
return; return;
} }
AntiBotService antiBotService = core.getAntiBotService();
if (pluginManager.isPluginEnabled("ProtocolSupport")) { if (pluginManager.isPluginEnabled("ProtocolSupport")) {
pluginManager.registerEvents(new ProtocolSupportListener(this, antiBotService), this); pluginManager.registerEvents(new ProtocolSupportListener(this, core.getAntiBot()), this);
} else if (pluginManager.isPluginEnabled("ProtocolLib")) { } else if (pluginManager.isPluginEnabled("ProtocolLib")) {
ProtocolLibListener.register(this, antiBotService, core.getConfig().getBoolean("verifyClientKeys")); ProtocolLibListener.register(this, core.getAntiBot(), core.getConfig().getBoolean("verifyClientKeys"));
if (isPluginInstalled("floodgate")) {
printFloodgateWarning();
}
//if server is using paper - we need to set the skin at pre login anyway, so no need for this listener //if server is using paper - we need to set the skin at pre login anyway, so no need for this listener
if (!isPaper() && getConfig().getBoolean("forwardSkin")) { if (!PaperLib.isPaper() && getConfig().getBoolean("forwardSkin")) {
pluginManager.registerEvents(new SkinApplyListener(this), this); pluginManager.registerEvents(new SkinApplyListener(this), this);
} }
} else { } else {
@@ -140,23 +139,35 @@ public class FastLoginBukkit extends JavaPlugin implements PlatformPlugin<Comman
pluginManager.registerEvents(new ConnectionListener(this), this); pluginManager.registerEvents(new ConnectionListener(this), this);
//if server is using paper - we need to add one more listener to correct the user cache usage //if server is using paper - we need to add one more listener to correct the user cache usage
if (isPaper()) { if (PaperLib.isPaper()) {
pluginManager.registerEvents(new PaperCacheListener(this), this); pluginManager.registerEvents(new PaperCacheListener(this), this);
} }
registerCommands(); //register commands using a unique name
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")) { if (pluginManager.isPluginEnabled("PlaceholderAPI")) {
premiumPlaceholder = new PremiumPlaceholder(this); premiumPlaceholder = new PremiumPlaceholder(this);
premiumPlaceholder.register(); premiumPlaceholder.register();
} }
dependencyWarnings();
} }
private void registerCommands() { private void printFloodgateWarning() {
//register commands using a unique name if (getConfig().getBoolean("floodgatePrefixWorkaround")) {
Optional.ofNullable(getCommand("premium")).ifPresent(c -> c.setExecutor(new PremiumCommand(this))); ManualNameChange.register(this, floodgateService);
Optional.ofNullable(getCommand("cracked")).ifPresent(c -> c.setExecutor(new CrackedCommand(this))); logger.info("Floodgate prefix injection workaround has been enabled.");
Optional.ofNullable(getCommand("fldelete")).ifPresent(c -> c.setExecutor(new DeleteCommand(this))); logger.info("If you have problems joining the server, try disabling it in the configuration.");
} else {
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 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");
}
} }
private boolean initializeFloodgate() { private boolean initializeFloodgate() {
@@ -196,9 +207,9 @@ public class FastLoginBukkit extends JavaPlugin implements PlatformPlugin<Comman
} }
} }
if (getServer().getPluginManager().isPluginEnabled("ProtocolLib")) { // if (isPluginInstalled("ProtocolLib")) {
ProtocolLibrary.getProtocolManager().getAsynchronousManager().unregisterAsyncHandlers(this); // ProtocolLibrary.getProtocolManager().getAsynchronousManager().unregisterAsyncHandlers(this);
} // }
} }
public FastLoginCore<Player, CommandSender, FastLoginBukkit> getCore() { public FastLoginCore<Player, CommandSender, FastLoginBukkit> getCore() {
@@ -240,28 +251,12 @@ public class FastLoginBukkit extends JavaPlugin implements PlatformPlugin<Comman
/** /**
* Fetches the premium status of an online player. * 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) * @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 * @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). * us the status message yet (This means you cannot check the login status on the PlayerJoinEvent).
*/ */
public @NotNull PremiumStatus getStatus(@NotNull UUID onlinePlayer) { public PremiumStatus getStatus(UUID onlinePlayer) {
return premiumPlayers.getOrDefault(onlinePlayer, PremiumStatus.UNKNOWN); return premiumPlayers.getOrDefault(onlinePlayer, PremiumStatus.UNKNOWN);
} }
@@ -331,16 +326,17 @@ public class FastLoginBukkit extends JavaPlugin implements PlatformPlugin<Comman
return geyserService; return geyserService;
} }
private boolean isPaper() { /**
return isClassAvailable("com.destroystokyo.paper.PaperConfig").isPresent() * Send warning messages to log if incompatible plugins are used
|| isClassAvailable("io.papermc.paper.configuration.Configuration").isPresent(); */
} private void dependencyWarnings() {
if (isPluginInstalled("floodgate-bukkit")) {
private Optional<Class<?>> isClassAvailable(String clazzName) { logger.warn("We have detected that you are running Floodgate 1.0 which is not supported by the Bukkit "
try { + "version of FastLogin.");
return Optional.of(Class.forName(clazzName)); logger.warn("If you would like to use FastLogin with Floodgate, you can download development builds of "
} catch (ClassNotFoundException e) { + "Floodgate 2.0 from https://ci.opencollab.dev/job/GeyserMC/job/Floodgate/job/dev%252F2.0/");
return Optional.empty(); 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/");
} }
} }
} }
@@ -1,63 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2024 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;
}
}
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -25,13 +25,14 @@
*/ */
package com.github.games647.fastlogin.bukkit; package com.github.games647.fastlogin.bukkit;
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
import org.bukkit.OfflinePlayer;
import org.jetbrains.annotations.NotNull;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
import org.bukkit.OfflinePlayer;
import org.jetbrains.annotations.NotNull;
public class PremiumPlaceholder extends PlaceholderExpansion { public class PremiumPlaceholder extends PlaceholderExpansion {
private static final String PLACEHOLDER_VARIABLE = "status"; private static final String PLACEHOLDER_VARIABLE = "status";
@@ -74,13 +75,11 @@ public class PremiumPlaceholder extends PlaceholderExpansion {
@Override @Override
public @NotNull String getAuthor() { public @NotNull String getAuthor() {
//noinspection deprecation
return String.join(", ", plugin.getDescription().getAuthors()); return String.join(", ", plugin.getDescription().getAuthors());
} }
@Override @Override
public @NotNull String getVersion() { public @NotNull String getVersion() {
//noinspection deprecation
return plugin.getDescription().getVersion(); return plugin.getDescription().getVersion();
} }
} }
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -27,10 +27,10 @@ package com.github.games647.fastlogin.bukkit.command;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit; import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import com.github.games647.fastlogin.bukkit.event.BukkitFastLoginPremiumToggleEvent; import com.github.games647.fastlogin.bukkit.event.BukkitFastLoginPremiumToggleEvent;
import com.github.games647.fastlogin.core.storage.StoredProfile; import com.github.games647.fastlogin.core.StoredProfile;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import static com.github.games647.fastlogin.core.shared.event.FastLoginPremiumToggleEvent.PremiumToggleReason; import static com.github.games647.fastlogin.core.shared.event.FastLoginPremiumToggleEvent.PremiumToggleReason;
@@ -58,31 +58,21 @@ public class CrackedCommand extends ToggleCommand {
return; return;
} }
Player player = (Player) sender;
if (forwardCrackedCommand(sender, sender.getName())) { if (forwardCrackedCommand(sender, sender.getName())) {
return; return;
} }
// todo: load async if // todo: load async if
StoredProfile profile = plugin.getCore().getStorage().loadProfile(sender.getName()); StoredProfile profile = plugin.getCore().getStorage().loadProfile(sender.getName());
if (profile.isOnlinemodePreferred()) { if (profile.isPremium()) {
plugin.getCore().sendLocaleMessage("remove-premium", sender); plugin.getCore().sendLocaleMessage("remove-premium", sender);
profile.setOnlinemodePreferred(false); profile.setPremium(false);
profile.setId(null); profile.setId(null);
plugin.getScheduler().runAsync(() -> { plugin.getScheduler().runAsync(() -> {
plugin.getCore().getStorage().save(profile); plugin.getCore().getStorage().save(profile);
plugin.getServer().getPluginManager().callEvent( plugin.getServer().getPluginManager().callEvent(
new BukkitFastLoginPremiumToggleEvent(sender, profile, PremiumToggleReason.COMMAND_OTHER) new BukkitFastLoginPremiumToggleEvent(profile, PremiumToggleReason.COMMAND_OTHER));
);
plugin.getScheduler().getSyncExecutor().execute(() -> {
if (plugin.getCore().getConfig().getBoolean("kick-toggle", true)) {
player.kickPlayer(plugin.getCore().getMessage("remove-premium"));
} else {
plugin.getCore().sendLocaleMessage("add-premium", sender);
}
});
}); });
} else { } else {
plugin.getCore().sendLocaleMessage("not-premium", sender); plugin.getCore().sendLocaleMessage("not-premium", sender);
@@ -106,16 +96,16 @@ public class CrackedCommand extends ToggleCommand {
} }
//existing player is already cracked //existing player is already cracked
if (profile.isExistingPlayer() && !profile.isOnlinemodePreferred()) { if (profile.isSaved() && !profile.isPremium()) {
plugin.getCore().sendLocaleMessage("not-premium-other", sender); plugin.getCore().sendLocaleMessage("not-premium-other", sender);
} else { } else {
plugin.getCore().sendLocaleMessage("remove-premium", sender); plugin.getCore().sendLocaleMessage("remove-premium", sender);
profile.setOnlinemodePreferred(false); profile.setPremium(false);
plugin.getScheduler().runAsync(() -> { plugin.getScheduler().runAsync(() -> {
plugin.getCore().getStorage().save(profile); plugin.getCore().getStorage().save(profile);
plugin.getServer().getPluginManager().callEvent( plugin.getServer().getPluginManager().callEvent(
new BukkitFastLoginPremiumToggleEvent(sender, profile, PremiumToggleReason.COMMAND_OTHER)); new BukkitFastLoginPremiumToggleEvent(profile, PremiumToggleReason.COMMAND_OTHER));
}); });
} }
} }
@@ -1,94 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2024 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.command;
import java.util.ArrayList;
import java.util.List;
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.ConsoleCommandSender;
import org.bukkit.command.TabExecutor;
import org.bukkit.entity.Player;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
public class DeleteCommand implements TabExecutor {
private final FastLoginBukkit plugin;
public DeleteCommand(FastLoginBukkit plugin) {
this.plugin = plugin;
}
/**
* Handles the command to delete profiles.
*/
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (!sender.hasPermission(command.getPermission())) {
plugin.getCore().sendLocaleMessage("no-permission", sender);
return true;
}
if (plugin.getBungeeManager().isEnabled()) {
sender.sendMessage("Error: Cannot delete profile entries when using BungeeCord!");
return false;
}
if (args.length < 1) {
sender.sendMessage("Error: Must supply username to delete!");
return false;
}
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
int count = plugin.getCore().getStorage().deleteProfile(args[0]);
if (!(sender instanceof ConsoleCommandSender)) {
Bukkit.getScheduler().runTask(plugin, () -> {
if (count == 0) {
sender.sendMessage("Error: No profile entries found!");
} else {
sender.sendMessage("Deleted " + count + " matching profile entries");
}
});
}
});
return true;
}
@Override
public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
List<String> list = new ArrayList<>();
for (Player p : Bukkit.getOnlinePlayers()) {
if (p.getName().toLowerCase().startsWith(args[0])) {
list.add(p.getName());
}
}
return null;
}
}
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -27,15 +27,16 @@ package com.github.games647.fastlogin.bukkit.command;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit; import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import com.github.games647.fastlogin.bukkit.event.BukkitFastLoginPremiumToggleEvent; 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 com.github.games647.fastlogin.core.shared.event.FastLoginPremiumToggleEvent.PremiumToggleReason;
import com.github.games647.fastlogin.core.storage.StoredProfile;
import java.util.UUID;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.UUID;
/** /**
* Let users activate fast login by command. This only be accessible if * Let users activate fast login by command. This only be accessible if
* the user has access to its account. So we can make sure that not another * the user has access to its account. So we can make sure that not another
@@ -68,8 +69,7 @@ public class PremiumCommand extends ToggleCommand {
return; return;
} }
Player player = (Player) sender; UUID id = ((Player) sender).getUniqueId();
UUID id = player.getUniqueId();
if (plugin.getConfig().getBoolean("premium-warning") && !plugin.getCore().getPendingConfirms().contains(id)) { if (plugin.getConfig().getBoolean("premium-warning") && !plugin.getCore().getPendingConfirms().contains(id)) {
sender.sendMessage(plugin.getCore().getMessage("premium-warning")); sender.sendMessage(plugin.getCore().getMessage("premium-warning"));
plugin.getCore().getPendingConfirms().add(id); plugin.getCore().getPendingConfirms().add(id);
@@ -79,25 +79,18 @@ public class PremiumCommand extends ToggleCommand {
plugin.getCore().getPendingConfirms().remove(id); plugin.getCore().getPendingConfirms().remove(id);
//todo: load async //todo: load async
StoredProfile profile = plugin.getCore().getStorage().loadProfile(sender.getName()); StoredProfile profile = plugin.getCore().getStorage().loadProfile(sender.getName());
if (profile.isOnlinemodePreferred()) { if (profile.isPremium()) {
plugin.getCore().sendLocaleMessage("already-exists", sender); plugin.getCore().sendLocaleMessage("already-exists", sender);
} else { } else {
//todo: resolve uuid //todo: resolve uuid
profile.setOnlinemodePreferred(true); profile.setPremium(true);
plugin.getScheduler().runAsync(() -> { plugin.getScheduler().runAsync(() -> {
plugin.getCore().getStorage().save(profile); plugin.getCore().getStorage().save(profile);
plugin.getServer().getPluginManager().callEvent( plugin.getServer().getPluginManager().callEvent(
new BukkitFastLoginPremiumToggleEvent(sender, profile, PremiumToggleReason.COMMAND_SELF) new BukkitFastLoginPremiumToggleEvent(profile, PremiumToggleReason.COMMAND_SELF));
);
plugin.getScheduler().getSyncExecutor().execute(() -> {
if (plugin.getCore().getConfig().getBoolean("kick-toggle", true)) {
player.kickPlayer(plugin.getCore().getMessage("remove-premium"));
} else {
plugin.getCore().sendLocaleMessage("add-premium", sender);
}
});
}); });
plugin.getCore().sendLocaleMessage("add-premium", sender);
} }
} }
@@ -117,16 +110,15 @@ public class PremiumCommand extends ToggleCommand {
return; return;
} }
if (profile.isOnlinemodePreferred()) { if (profile.isPremium()) {
plugin.getCore().sendLocaleMessage("already-exists-other", sender); plugin.getCore().sendLocaleMessage("already-exists-other", sender);
} else { } else {
//todo: resolve uuid //todo: resolve uuid
profile.setOnlinemodePreferred(true); profile.setPremium(true);
plugin.getScheduler().runAsync(() -> { plugin.getScheduler().runAsync(() -> {
plugin.getCore().getStorage().save(profile); plugin.getCore().getStorage().save(profile);
plugin.getServer().getPluginManager().callEvent( plugin.getServer().getPluginManager().callEvent(
new BukkitFastLoginPremiumToggleEvent(sender, profile, PremiumToggleReason.COMMAND_OTHER) new BukkitFastLoginPremiumToggleEvent(profile, PremiumToggleReason.COMMAND_OTHER));
);
}); });
plugin.getCore().sendLocaleMessage("add-premium-other", sender); plugin.getCore().sendLocaleMessage("add-premium-other", sender);
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -28,6 +28,9 @@ package com.github.games647.fastlogin.bukkit.command;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit; import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import com.github.games647.fastlogin.core.message.ChangePremiumMessage; import com.github.games647.fastlogin.core.message.ChangePremiumMessage;
import com.github.games647.fastlogin.core.message.ChannelMessage; import com.github.games647.fastlogin.core.message.ChannelMessage;
import java.util.Optional;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandExecutor;
@@ -35,8 +38,6 @@ import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.plugin.messaging.PluginMessageRecipient; import org.bukkit.plugin.messaging.PluginMessageRecipient;
import java.util.Optional;
public abstract class ToggleCommand implements CommandExecutor { public abstract class ToggleCommand implements CommandExecutor {
protected final FastLoginBukkit plugin; protected final FastLoginBukkit plugin;
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -25,9 +25,10 @@
*/ */
package com.github.games647.fastlogin.bukkit.event; package com.github.games647.fastlogin.bukkit.event;
import com.github.games647.fastlogin.core.StoredProfile;
import com.github.games647.fastlogin.core.shared.LoginSession; import com.github.games647.fastlogin.core.shared.LoginSession;
import com.github.games647.fastlogin.core.shared.event.FastLoginAutoLoginEvent; import com.github.games647.fastlogin.core.shared.event.FastLoginAutoLoginEvent;
import com.github.games647.fastlogin.core.storage.StoredProfile;
import org.bukkit.event.Cancellable; import org.bukkit.event.Cancellable;
import org.bukkit.event.Event; import org.bukkit.event.Event;
import org.bukkit.event.HandlerList; import org.bukkit.event.HandlerList;
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -25,9 +25,10 @@
*/ */
package com.github.games647.fastlogin.bukkit.event; package com.github.games647.fastlogin.bukkit.event;
import com.github.games647.fastlogin.core.StoredProfile;
import com.github.games647.fastlogin.core.shared.LoginSource; import com.github.games647.fastlogin.core.shared.LoginSource;
import com.github.games647.fastlogin.core.shared.event.FastLoginPreLoginEvent; import com.github.games647.fastlogin.core.shared.event.FastLoginPreLoginEvent;
import com.github.games647.fastlogin.core.storage.StoredProfile;
import org.bukkit.event.Event; import org.bukkit.event.Event;
import org.bukkit.event.HandlerList; import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -25,9 +25,9 @@
*/ */
package com.github.games647.fastlogin.bukkit.event; package com.github.games647.fastlogin.bukkit.event;
import com.github.games647.fastlogin.core.StoredProfile;
import com.github.games647.fastlogin.core.shared.event.FastLoginPremiumToggleEvent; import com.github.games647.fastlogin.core.shared.event.FastLoginPremiumToggleEvent;
import com.github.games647.fastlogin.core.storage.StoredProfile;
import org.bukkit.command.CommandSender;
import org.bukkit.event.Event; import org.bukkit.event.Event;
import org.bukkit.event.HandlerList; import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@@ -35,15 +35,11 @@ import org.jetbrains.annotations.NotNull;
public class BukkitFastLoginPremiumToggleEvent extends Event implements FastLoginPremiumToggleEvent { public class BukkitFastLoginPremiumToggleEvent extends Event implements FastLoginPremiumToggleEvent {
private static final HandlerList HANDLERS = new HandlerList(); private static final HandlerList HANDLERS = new HandlerList();
private final CommandSender invoker;
private final StoredProfile profile; private final StoredProfile profile;
private final PremiumToggleReason reason; private final PremiumToggleReason reason;
public BukkitFastLoginPremiumToggleEvent(CommandSender invoker, StoredProfile profile, PremiumToggleReason reason) { public BukkitFastLoginPremiumToggleEvent(StoredProfile profile, PremiumToggleReason reason) {
super(true); super(true);
this.invoker = invoker;
this.profile = profile; this.profile = profile;
this.reason = reason; this.reason = reason;
} }
@@ -53,13 +49,6 @@ public class BukkitFastLoginPremiumToggleEvent extends Event implements FastLogi
return profile; return profile;
} }
/**
* @return who triggered this change. This could be a Player for itself or others (Admin) or the console.
*/
public CommandSender getInvoker() {
return invoker;
}
@Override @Override
public PremiumToggleReason getReason() { public PremiumToggleReason getReason() {
return reason; return reason;
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -28,26 +28,28 @@ package com.github.games647.fastlogin.bukkit.hook;
import com.github.games647.fastlogin.bukkit.BukkitLoginSession; import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit; import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import com.github.games647.fastlogin.core.hooks.AuthPlugin; import com.github.games647.fastlogin.core.hooks.AuthPlugin;
import fr.xephi.authme.api.v3.AuthMeApi; import fr.xephi.authme.api.v3.AuthMeApi;
import fr.xephi.authme.events.RestoreSessionEvent; import fr.xephi.authme.events.RestoreSessionEvent;
import fr.xephi.authme.process.Management; import fr.xephi.authme.process.Management;
import fr.xephi.authme.process.register.executors.ApiPasswordRegisterParams; import fr.xephi.authme.process.register.executors.ApiPasswordRegisterParams;
import fr.xephi.authme.process.register.executors.RegistrationMethod; import fr.xephi.authme.process.register.executors.RegistrationMethod;
import java.lang.reflect.Field;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority; import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import java.lang.reflect.Field;
/** /**
* GitHub: <a href="https://github.com/Xephi/AuthMeReloaded/">...</a> * GitHub: <a href="https://github.com/Xephi/AuthMeReloaded/">...</a>
* <p> * <p>
* Project page: * Project page:
* <p> * <p>
* <a href="https://dev.bukkit.org/bukkit-plugins/authme-reloaded/">Bukkit</a> * Bukkit: <a href="https://dev.bukkit.org/bukkit-plugins/authme-reloaded/">...</a>
* <p> * <p>
* <a href="https://www.spigotmc.org/resources/authme-reloaded.6269/">Spigot</a> * Spigot: <a href="https://www.spigotmc.org/resources/authme-reloaded.6269/">...</a>
*/ */
public class AuthMeHook implements AuthPlugin<Player>, Listener { public class AuthMeHook implements AuthPlugin<Player>, Listener {
@@ -60,7 +62,7 @@ public class AuthMeHook implements AuthPlugin<Player>, Listener {
this.plugin = plugin; this.plugin = plugin;
this.authmeAPI = AuthMeApi.getInstance(); this.authmeAPI = AuthMeApi.getInstance();
if (plugin.getCore().getConfig().getBoolean("respectIpLimit", false)) { if (plugin.getConfig().getBoolean("respectIpLimit", false)) {
try { try {
Field managementField = this.authmeAPI.getClass().getDeclaredField("management"); Field managementField = this.authmeAPI.getClass().getDeclaredField("management");
managementField.setAccessible(true); managementField.setAccessible(true);
@@ -76,7 +78,7 @@ public class AuthMeHook implements AuthPlugin<Player>, Listener {
Player player = restoreSessionEvent.getPlayer(); Player player = restoreSessionEvent.getPlayer();
BukkitLoginSession session = plugin.getSession(player.spigot().getRawAddress()); BukkitLoginSession session = plugin.getSession(player.spigot().getRawAddress());
if (session != null && session.isVerifiedPremium()) { if (session != null && session.isVerified()) {
restoreSessionEvent.setCancelled(true); restoreSessionEvent.setCancelled(true);
} }
} }
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -29,24 +29,26 @@ import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.FieldAccessor; import com.comphenix.protocol.reflect.accessors.FieldAccessor;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit; import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import com.github.games647.fastlogin.core.hooks.AuthPlugin; import com.github.games647.fastlogin.core.hooks.AuthPlugin;
import de.st_ddt.crazylogin.CrazyLogin; import de.st_ddt.crazylogin.CrazyLogin;
import de.st_ddt.crazylogin.data.LoginPlayerData; import de.st_ddt.crazylogin.data.LoginPlayerData;
import de.st_ddt.crazylogin.databases.CrazyLoginDataDatabase; import de.st_ddt.crazylogin.databases.CrazyLoginDataDatabase;
import de.st_ddt.crazylogin.listener.PlayerListener; import de.st_ddt.crazylogin.listener.PlayerListener;
import de.st_ddt.crazylogin.metadata.Authenticated; import de.st_ddt.crazylogin.metadata.Authenticated;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
/** /**
* GitHub: <a href="https://github.com/ST-DDT/CrazyLogin">...</a> * GitHub: <a href="https://github.com/ST-DDT/CrazyLogin">...</a>
* <p> * <p>
* Project page: * Project page:
* <p> * <p>
* <a href="https://dev.bukkit.org/server-mods/crazylogin/">Bukkit</a> * Bukkit: <a href="https://dev.bukkit.org/server-mods/crazylogin/">...</a>
*/ */
public class CrazyLoginHook implements AuthPlugin<Player> { public class CrazyLoginHook implements AuthPlugin<Player> {
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -27,14 +27,16 @@ package com.github.games647.fastlogin.bukkit.hook;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit; import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import com.github.games647.fastlogin.core.hooks.AuthPlugin; import com.github.games647.fastlogin.core.hooks.AuthPlugin;
import io.github.lucaseasedup.logit.CancelledState; import io.github.lucaseasedup.logit.CancelledState;
import io.github.lucaseasedup.logit.LogItCore; import io.github.lucaseasedup.logit.LogItCore;
import io.github.lucaseasedup.logit.account.Account; import io.github.lucaseasedup.logit.account.Account;
import io.github.lucaseasedup.logit.session.SessionManager; import io.github.lucaseasedup.logit.session.SessionManager;
import org.bukkit.entity.Player;
import java.time.Instant; import java.time.Instant;
import org.bukkit.entity.Player;
/** /**
* GitHub: <a href="https://github.com/XziomekX/LogIt">...</a> * GitHub: <a href="https://github.com/XziomekX/LogIt">...</a>
* <p> * <p>
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -32,6 +32,7 @@ import com.lenis0012.bukkit.loginsecurity.session.AuthService;
import com.lenis0012.bukkit.loginsecurity.session.PlayerSession; import com.lenis0012.bukkit.loginsecurity.session.PlayerSession;
import com.lenis0012.bukkit.loginsecurity.session.action.LoginAction; import com.lenis0012.bukkit.loginsecurity.session.action.LoginAction;
import com.lenis0012.bukkit.loginsecurity.session.action.RegisterAction; import com.lenis0012.bukkit.loginsecurity.session.action.RegisterAction;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
/** /**
@@ -39,9 +40,9 @@ import org.bukkit.entity.Player;
* <p> * <p>
* Project page: * Project page:
* <p> * <p>
* <a href="https://dev.bukkit.org/bukkit-plugins/loginsecurity/">Bukkit</a> * Bukkit: <a href="https://dev.bukkit.org/bukkit-plugins/loginsecurity/">...</a>
* <p> * <p>
* <a href="https://www.spigotmc.org/resources/loginsecurity.19362/">Spigot</a> * Spigot: <a href="https://www.spigotmc.org/resources/loginsecurity.19362/">...</a>
*/ */
public class LoginSecurityHook implements AuthPlugin<Player> { public class LoginSecurityHook implements AuthPlugin<Player> {
@@ -1,68 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2024 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.hook;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
import com.rabbitcomapny.api.Identifier;
import com.rabbitcomapny.api.LoginResult;
import com.rabbitcomapny.api.PasskyAPI;
import com.rabbitcomapny.api.RegisterResult;
import org.bukkit.entity.Player;
public class PasskyHook implements AuthPlugin<Player> {
private final FastLoginBukkit plugin;
public PasskyHook(FastLoginBukkit plugin) {
this.plugin = plugin;
}
@Override
public boolean forceLogin(Player player) {
LoginResult result = PasskyAPI.forceLogin(new Identifier(player), true);
if (!result.success) {
plugin.getLog().error("Failed to force login {} via Passky: {}", player.getName(), result.status);
}
return result.success;
}
@Override
public boolean forceRegister(Player player, String password) {
RegisterResult result = PasskyAPI.forceRegister(new Identifier(player), password, true);
if (!result.success) {
plugin.getLog().error("Failed to register {} via Passky: {}", player.getName(), result.status);
}
return result.success;
}
@Override
public boolean isRegistered(String playerName) {
return PasskyAPI.isRegistered(new Identifier(playerName));
}
}
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -27,20 +27,21 @@ package com.github.games647.fastlogin.bukkit.hook;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit; import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import com.github.games647.fastlogin.core.hooks.AuthPlugin; import com.github.games647.fastlogin.core.hooks.AuthPlugin;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import ultraauth.api.UltraAuthAPI; import ultraauth.api.UltraAuthAPI;
import ultraauth.managers.PlayerManager; import ultraauth.managers.PlayerManager;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
/** /**
* Project page: * Project page:
* <p> * <p>
* <a href="https://dev.bukkit.org/bukkit-plugins/ultraauth-aa/">Bukkit</a> * Bukkit: <a href="https://dev.bukkit.org/bukkit-plugins/ultraauth-aa/">...</a>
* <p> * <p>
* <a href="https://www.spigotmc.org/resources/ultraauth.17044/">Spigot</a> * Spigot: <a href="https://www.spigotmc.org/resources/ultraauth.17044/">...</a>
*/ */
public class UltraAuthHook implements AuthPlugin<Player> { public class UltraAuthHook implements AuthPlugin<Player> {
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -27,20 +27,22 @@ package com.github.games647.fastlogin.bukkit.hook;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit; import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import com.github.games647.fastlogin.core.hooks.AuthPlugin; import com.github.games647.fastlogin.core.hooks.AuthPlugin;
import de.luricos.bukkit.xAuth.xAuth; import de.luricos.bukkit.xAuth.xAuth;
import de.luricos.bukkit.xAuth.xAuthPlayer; import de.luricos.bukkit.xAuth.xAuthPlayer;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
/** /**
* GitHub: <a href="https://github.com/LycanDevelopment/xAuth/">...</a> * GitHub: <a href="https://github.com/LycanDevelopment/xAuth/">...</a>
* <p> * <p>
* Project page: * Project page:
* <p> * <p>
* <a href="https://dev.bukkit.org/bukkit-plugins/xauth/">Bukkit</a> * Bukkit: <a href="https://dev.bukkit.org/bukkit-plugins/xauth/">...</a>
*/ */
public class XAuthHook implements AuthPlugin<Player> { public class XAuthHook implements AuthPlugin<Player> {
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -34,16 +34,18 @@ import com.github.games647.fastlogin.core.message.LoginActionMessage;
import com.github.games647.fastlogin.core.message.LoginActionMessage.Type; import com.github.games647.fastlogin.core.message.LoginActionMessage.Type;
import com.google.common.io.ByteArrayDataInput; import com.google.common.io.ByteArrayDataInput;
import com.google.common.io.ByteStreams; import com.google.common.io.ByteStreams;
import java.net.InetSocketAddress;
import java.util.UUID;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.plugin.messaging.PluginMessageListener; import org.bukkit.plugin.messaging.PluginMessageListener;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.UUID;
/** /**
* Responsible for receiving messages from a BungeeCord instance. * Responsible for receiving messages from a BungeeCord instance.
* <p> *
* This class also receives the plugin message from the bungeecord version of this plugin in order to get notified if * This class also receives the plugin message from the bungeecord version of this plugin in order to get notified if
* the connection is in online mode. * the connection is in online mode.
*/ */
@@ -91,23 +93,24 @@ public class BungeeListener implements PluginMessageListener {
String playerName = message.getPlayerName(); String playerName = message.getPlayerName();
Type type = message.getType(); Type type = message.getType();
InetSocketAddress address = player.getAddress();
plugin.getLog().info("Player info {} command for {} from proxy", type, playerName); plugin.getLog().info("Player info {} command for {} from proxy", type, playerName);
if (type == Type.LOGIN) { if (type == Type.LOGIN) {
onLoginMessage(player, playerName); onLoginMessage(player, playerName, address);
} else if (type == Type.REGISTER) { } else if (type == Type.REGISTER) {
onRegisterMessage(player, playerName); onRegisterMessage(player, playerName, address);
} else if (type == Type.CRACKED) { } else if (type == Type.CRACKED) {
//we don't start a force login task here so update it manually //we don't start a force login task here so update it manually
plugin.getPremiumPlayers().put(player.getUniqueId(), PremiumStatus.CRACKED); plugin.getPremiumPlayers().put(player.getUniqueId(), PremiumStatus.CRACKED);
} }
} }
private void onLoginMessage(Player player, String playerName) { private void onLoginMessage(Player player, String playerName, InetSocketAddress address) {
BukkitLoginSession playerSession = new BukkitLoginSession(playerName, true); BukkitLoginSession playerSession = new BukkitLoginSession(playerName, true);
startLoginTaskIfReady(player, playerSession); startLoginTaskIfReady(player, playerSession);
} }
private void onRegisterMessage(Player player, String playerName) { private void onRegisterMessage(Player player, String playerName, InetSocketAddress address) {
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> { Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
AuthPlugin<Player> authPlugin = plugin.getCore().getAuthPluginHook(); AuthPlugin<Player> authPlugin = plugin.getCore().getAuthPluginHook();
try { try {
@@ -123,7 +126,7 @@ public class BungeeListener implements PluginMessageListener {
} }
private void startLoginTaskIfReady(Player player, BukkitLoginSession session) { private void startLoginTaskIfReady(Player player, BukkitLoginSession session) {
session.setVerifiedPremium(true); session.setVerified(true);
plugin.putSession(player.spigot().getRawAddress(), session); plugin.putSession(player.spigot().getRawAddress(), session);
// only start a new login task if the join event fired earlier. This event then didn't // only start a new login task if the join event fired earlier. This event then didn't
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -30,6 +30,7 @@ import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import com.github.games647.fastlogin.bukkit.task.FloodgateAuthTask; import com.github.games647.fastlogin.bukkit.task.FloodgateAuthTask;
import com.github.games647.fastlogin.bukkit.task.ForceLoginTask; import com.github.games647.fastlogin.bukkit.task.ForceLoginTask;
import com.github.games647.fastlogin.core.hooks.bedrock.FloodgateService; import com.github.games647.fastlogin.core.hooks.bedrock.FloodgateService;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
@@ -90,7 +91,6 @@ public class ConnectionListener implements Listener {
if (floodgatePlayer != null) { if (floodgatePlayer != null) {
Runnable floodgateAuthTask = new FloodgateAuthTask(plugin.getCore(), player, floodgatePlayer); Runnable floodgateAuthTask = new FloodgateAuthTask(plugin.getCore(), player, floodgatePlayer);
Bukkit.getScheduler().runTaskAsynchronously(plugin, floodgateAuthTask); Bukkit.getScheduler().runTaskAsynchronously(plugin, floodgateAuthTask);
plugin.getBungeeManager().markJoinEventFired(player);
return; return;
} }
} }
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -29,6 +29,7 @@ import com.destroystokyo.paper.profile.ProfileProperty;
import com.github.games647.craftapi.model.skin.Textures; import com.github.games647.craftapi.model.skin.Textures;
import com.github.games647.fastlogin.bukkit.BukkitLoginSession; import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit; import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority; import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -32,16 +32,8 @@ import com.google.common.hash.Hashing;
import com.google.common.io.Resources; import com.google.common.io.Resources;
import com.google.common.primitives.Longs; import com.google.common.primitives.Longs;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException; import java.io.IOException;
import java.math.BigInteger; import java.math.BigInteger;
import java.net.URL;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException; import java.security.InvalidKeyException;
import java.security.KeyFactory; import java.security.KeyFactory;
@@ -53,14 +45,21 @@ import java.security.PublicKey;
import java.security.Signature; import java.security.Signature;
import java.security.SignatureException; import java.security.SignatureException;
import java.security.spec.InvalidKeySpecException; import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.security.spec.X509EncodedKeySpec; import java.security.spec.X509EncodedKeySpec;
import java.time.Instant; import java.time.Instant;
import java.util.Arrays; import java.util.Arrays;
import java.util.Base64; import java.util.Base64;
import java.util.Base64.Encoder; import java.util.Base64.Encoder;
import java.util.Random; import java.util.Random;
import java.util.UUID;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import lombok.val;
/** /**
* Encryption and decryption minecraft util for connection between servers * Encryption and decryption minecraft util for connection between servers
@@ -71,15 +70,13 @@ final class EncryptionUtil {
public static final int VERIFY_TOKEN_LENGTH = 4; public static final int VERIFY_TOKEN_LENGTH = 4;
public static final String KEY_PAIR_ALGORITHM = "RSA"; public static final String KEY_PAIR_ALGORITHM = "RSA";
private static final int RSA_LENGTH = 2_048; private static final int RSA_LENGTH = 1_024;
private static final PublicKey MOJANG_SESSION_KEY; private static final PublicKey MOJANG_SESSION_KEY;
private static final int LINE_LENGTH = 76; private static final int LINE_LENGTH = 76;
private static final Encoder KEY_ENCODER = Base64.getMimeEncoder( private static final Encoder KEY_ENCODER = Base64.getMimeEncoder(
LINE_LENGTH, "\n".getBytes(StandardCharsets.UTF_8) LINE_LENGTH, "\n".getBytes(StandardCharsets.UTF_8)
); );
private static final int MILLISECOND_SIZE = 8;
private static final int UUID_SIZE = 2 * MILLISECOND_SIZE;
static { static {
try { try {
@@ -149,7 +146,7 @@ final class EncryptionUtil {
return new SecretKeySpec(decrypt(privateKey, sharedKey), "AES"); return new SecretKeySpec(decrypt(privateKey, sharedKey), "AES");
} }
public static boolean verifyClientKey(ClientPublicKey clientKey, Instant verifyTimestamp, UUID premiumId) public static boolean verifyClientKey(ClientPublicKey clientKey, Instant verifyTimestamp)
throws NoSuchAlgorithmException, InvalidKeyException, SignatureException { throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
if (clientKey.isExpired(verifyTimestamp)) { if (clientKey.isExpired(verifyTimestamp)) {
return false; return false;
@@ -158,27 +155,10 @@ final class EncryptionUtil {
Signature verifier = Signature.getInstance("SHA1withRSA"); Signature verifier = Signature.getInstance("SHA1withRSA");
// key of the signer // key of the signer
verifier.initVerify(MOJANG_SESSION_KEY); verifier.initVerify(MOJANG_SESSION_KEY);
verifier.update(toSignable(clientKey, premiumId)); verifier.update(toSignable(clientKey).getBytes(StandardCharsets.US_ASCII));
return verifier.verify(clientKey.signature()); return verifier.verify(clientKey.signature());
} }
private static byte[] toSignable(ClientPublicKey clientPublicKey, UUID ownerPremiumId) {
if (ownerPremiumId == null) {
long expiry = clientPublicKey.expiry().toEpochMilli();
String encoded = KEY_ENCODER.encodeToString(clientPublicKey.key().getEncoded());
return (expiry + "-----BEGIN RSA PUBLIC KEY-----\n" + encoded + "\n-----END RSA PUBLIC KEY-----\n")
.getBytes(StandardCharsets.US_ASCII);
}
byte[] keyData = clientPublicKey.key().getEncoded();
return ByteBuffer.allocate(keyData.length + UUID_SIZE + MILLISECOND_SIZE)
.putLong(ownerPremiumId.getMostSignificantBits())
.putLong(ownerPremiumId.getLeastSignificantBits())
.putLong(clientPublicKey.expiry().toEpochMilli())
.put(keyData)
.array();
}
public static boolean verifyNonce(byte[] expected, PrivateKey decryptionKey, byte[] encryptedNonce) public static boolean verifyNonce(byte[] expected, PrivateKey decryptionKey, byte[] encryptedNonce)
throws NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, throws NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException,
BadPaddingException, InvalidKeyException { BadPaddingException, InvalidKeyException {
@@ -199,13 +179,19 @@ final class EncryptionUtil {
private static PublicKey loadMojangSessionKey() private static PublicKey loadMojangSessionKey()
throws IOException, NoSuchAlgorithmException, InvalidKeySpecException { throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
URL keyUrl = FastLoginBukkit.class.getClassLoader().getResource("yggdrasil_session_pubkey.der"); val keyUrl = FastLoginBukkit.class.getClassLoader().getResource("yggdrasil_session_pubkey.der");
byte[] keyData = Resources.toByteArray(keyUrl); val keyData = Resources.toByteArray(keyUrl);
KeySpec keySpec = new X509EncodedKeySpec(keyData); val keySpec = new X509EncodedKeySpec(keyData);
return KeyFactory.getInstance("RSA").generatePublic(keySpec); return KeyFactory.getInstance("RSA").generatePublic(keySpec);
} }
private static String toSignable(ClientPublicKey clientPublicKey) {
long expiry = clientPublicKey.expiry().toEpochMilli();
String encoded = KEY_ENCODER.encodeToString(clientPublicKey.key().getEncoded());
return expiry + "-----BEGIN RSA PUBLIC KEY-----\n" + encoded + "\n-----END RSA PUBLIC KEY-----\n";
}
private static byte[] decrypt(PrivateKey key, byte[] data) private static byte[] decrypt(PrivateKey key, byte[] data)
throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException,
IllegalBlockSizeException, BadPaddingException { IllegalBlockSizeException, BadPaddingException {
@@ -0,0 +1,101 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2022 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.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 java.util.UUID;
import org.geysermc.floodgate.api.FloodgateApi;
import static com.comphenix.protocol.PacketType.Login.Client.START;
/**
* 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: <a href="https://github.com/games647/FastLogin/issues/493">...</a>
*/
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();
String username = readUsername(packet);
if (floodgate.getBedrockPlayer(username) == null) {
//not a Floodgate player, no need to add a prefix
return;
}
packet.setMeta("original_name", username);
String prefixedName = FloodgateApi.getInstance().getPlayerPrefix() + username;
setUsername(packet, prefixedName);
}
private void setUsername(PacketContainer packet, String name) {
if (packet.getGameProfiles().size() > 0) {
WrappedGameProfile updatedProfile = new WrappedGameProfile(UUID.randomUUID(), name);
packet.getGameProfiles().write(0, updatedProfile);
} else {
packet.getStrings().write(0, name);
}
}
private String readUsername(PacketContainer packet) {
if (packet.getGameProfiles().size() > 0) {
return packet.getGameProfiles().read(0).getName();
} else {
return packet.getStrings().read(0);
}
}
}
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -25,23 +25,23 @@
*/ */
package com.github.games647.fastlogin.bukkit.listener.protocollib; package com.github.games647.fastlogin.bukkit.listener.protocollib;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.events.PacketEvent; import com.comphenix.protocol.events.PacketEvent;
import com.github.games647.fastlogin.bukkit.BukkitLoginSession; import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit; import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import com.github.games647.fastlogin.bukkit.event.BukkitFastLoginPreLoginEvent; import com.github.games647.fastlogin.bukkit.event.BukkitFastLoginPreLoginEvent;
import com.github.games647.fastlogin.bukkit.listener.protocollib.packet.ClientPublicKey; import com.github.games647.fastlogin.bukkit.listener.protocollib.packet.ClientPublicKey;
import com.github.games647.fastlogin.core.StoredProfile;
import com.github.games647.fastlogin.core.shared.JoinManagement; import com.github.games647.fastlogin.core.shared.JoinManagement;
import com.github.games647.fastlogin.core.shared.event.FastLoginPreLoginEvent; import com.github.games647.fastlogin.core.shared.event.FastLoginPreLoginEvent;
import com.github.games647.fastlogin.core.storage.StoredProfile;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.security.PublicKey; import java.security.PublicKey;
import java.util.Random; import java.util.Random;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
public class NameCheckTask extends JoinManagement<Player, CommandSender, ProtocolLibLoginSource> public class NameCheckTask extends JoinManagement<Player, CommandSender, ProtocolLibLoginSource>
implements Runnable { implements Runnable {
private final FastLoginBukkit plugin; private final FastLoginBukkit plugin;
private final PacketEvent packetEvent; private final PacketEvent packetEvent;
@@ -69,11 +69,11 @@ public class NameCheckTask extends JoinManagement<Player, CommandSender, Protoco
@Override @Override
public void run() { public void run() {
try { // try {
super.onLogin(username, new ProtocolLibLoginSource(player, random, serverKey, clientKey)); super.onLogin(username, new ProtocolLibLoginSource(player, random, serverKey, clientKey));
} finally { // } finally {
ProtocolLibrary.getProtocolManager().getAsynchronousManager().signalPacketTransmission(packetEvent); // ProtocolLibrary.getProtocolManager().getAsynchronousManager().signalPacketTransmission(packetEvent);
} // }
} }
@Override @Override
@@ -97,7 +97,7 @@ public class NameCheckTask extends JoinManagement<Player, CommandSender, Protoco
} }
String ip = player.getAddress().getAddress().getHostAddress(); String ip = player.getAddress().getAddress().getHostAddress();
core.addLoginAttempt(ip, username); core.getPendingLogin().put(ip + username, new Object());
byte[] verify = source.getVerifyToken(); byte[] verify = source.getVerifyToken();
ClientPublicKey clientKey = source.getClientKey(); ClientPublicKey clientKey = source.getClientKey();
@@ -105,9 +105,9 @@ public class NameCheckTask extends JoinManagement<Player, CommandSender, Protoco
BukkitLoginSession playerSession = new BukkitLoginSession(username, verify, clientKey, registered, profile); BukkitLoginSession playerSession = new BukkitLoginSession(username, verify, clientKey, registered, profile);
plugin.putSession(player.getAddress(), playerSession); plugin.putSession(player.getAddress(), playerSession);
//cancel only if the player has a paid account otherwise login as normal offline player //cancel only if the player has a paid account otherwise login as normal offline player
synchronized (packetEvent.getAsyncMarker().getProcessingLock()) { // synchronized (packetEvent.getAsyncMarker().getProcessingLock()) {
packetEvent.setCancelled(true); packetEvent.setCancelled(true);
} // }
} }
@Override @Override
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -30,14 +30,9 @@ import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.events.PacketAdapter; import com.comphenix.protocol.events.PacketAdapter;
import com.comphenix.protocol.events.PacketContainer; import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.events.PacketEvent; import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.injector.netty.channel.NettyChannelInjector;
import com.comphenix.protocol.injector.temporary.TemporaryPlayerFactory;
import com.comphenix.protocol.reflect.FieldAccessException;
import com.comphenix.protocol.reflect.FuzzyReflection; import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.utility.MinecraftVersion; import com.comphenix.protocol.utility.MinecraftVersion;
import com.comphenix.protocol.wrappers.BukkitConverters; import com.comphenix.protocol.wrappers.BukkitConverters;
import com.comphenix.protocol.wrappers.Converters;
import com.comphenix.protocol.wrappers.WrappedGameProfile; import com.comphenix.protocol.wrappers.WrappedGameProfile;
import com.comphenix.protocol.wrappers.WrappedProfilePublicKey.WrappedProfileKeyData; import com.comphenix.protocol.wrappers.WrappedProfilePublicKey.WrappedProfileKeyData;
import com.github.games647.fastlogin.bukkit.BukkitLoginSession; import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
@@ -46,16 +41,7 @@ import com.github.games647.fastlogin.bukkit.listener.protocollib.packet.ClientPu
import com.github.games647.fastlogin.core.antibot.AntiBotService; import com.github.games647.fastlogin.core.antibot.AntiBotService;
import com.github.games647.fastlogin.core.antibot.AntiBotService.Action; import com.github.games647.fastlogin.core.antibot.AntiBotService.Action;
import com.mojang.datafixers.util.Either; import com.mojang.datafixers.util.Either;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.util.AttributeKey;
import org.bukkit.entity.Player;
import org.geysermc.floodgate.api.player.FloodgatePlayer;
import org.jetbrains.annotations.NotNull;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.security.InvalidKeyException; import java.security.InvalidKeyException;
import java.security.KeyPair; import java.security.KeyPair;
@@ -64,15 +50,24 @@ import java.security.PublicKey;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.security.SignatureException; import java.security.SignatureException;
import java.time.Instant; import java.time.Instant;
import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.UUID;
import java.util.function.Function; import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import lombok.var;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.Nullable;
import static com.comphenix.protocol.PacketType.Login.Client.ENCRYPTION_BEGIN; import static com.comphenix.protocol.PacketType.Login.Client.ENCRYPTION_BEGIN;
import static com.comphenix.protocol.PacketType.Login.Client.START; import static com.comphenix.protocol.PacketType.Login.Client.START;
public class ProtocolLibListener extends PacketAdapter { public class ProtocolLibListener extends PacketAdapter {
public static final String SOURCE_META_KEY = "source";
private final FastLoginBukkit plugin; private final FastLoginBukkit plugin;
//just create a new once on plugin enable. This used for verify token generation //just create a new once on plugin enable. This used for verify token generation
@@ -82,12 +77,14 @@ public class ProtocolLibListener extends PacketAdapter {
private final boolean verifyClientKeys; private final boolean verifyClientKeys;
@Nullable
private PacketContainer lastStartPacket;
public ProtocolLibListener(FastLoginBukkit plugin, AntiBotService antiBotService, boolean verifyClientKeys) { public ProtocolLibListener(FastLoginBukkit plugin, AntiBotService antiBotService, boolean verifyClientKeys) {
//run async in order to not block the server, because we are making api calls to Mojang //run async in order to not block the server, because we are making api calls to Mojang
super(params() super(params()
.plugin(plugin) .plugin(plugin)
.types(START, ENCRYPTION_BEGIN) .types(START, ENCRYPTION_BEGIN));
.optionAsync());
this.plugin = plugin; this.plugin = plugin;
this.antiBotService = antiBotService; this.antiBotService = antiBotService;
@@ -98,77 +95,73 @@ public class ProtocolLibListener extends PacketAdapter {
// they will be created with a static builder, because otherwise it will throw a NoClassDefFoundError // they will be created with a static builder, because otherwise it will throw a NoClassDefFoundError
// TODO: make synchronous processing, but do web or database requests async // TODO: make synchronous processing, but do web or database requests async
ProtocolLibrary.getProtocolManager() ProtocolLibrary.getProtocolManager()
.getAsynchronousManager() .addPacketListener(new ProtocolLibListener(plugin, antiBotService, verifyClientKeys));
.registerAsyncHandler(new ProtocolLibListener(plugin, antiBotService, verifyClientKeys))
.start();
} }
@Override @Override
public void onPacketReceiving(PacketEvent packetEvent) { public void onPacketReceiving(PacketEvent packetEvent) {
PacketContainer packet = packetEvent.getPacket();
plugin.getLog().info("New packet {} from {}; Cancellation: {}, Meta: {}",
packetEvent.getPacketType(), packetEvent.getPlayer(), packetEvent.isCancelled(),
packet.getMeta(SOURCE_META_KEY)
);
if (packetEvent.getPacketType() == START) {
if (lastStartPacket != null) {
plugin.getLog().info("Start-packet equality (Last/New): {}/{}, {}",
lastStartPacket.getHandle().hashCode(), packet.getHandle().hashCode(),
Objects.equals(lastStartPacket.getHandle(), packet.getHandle())
);
plugin.getLog().info("Content: {}, {}", lastStartPacket.getHandle(), packet.getHandle());
}
lastStartPacket = packet;
}
if (packetEvent.isCancelled() if (packetEvent.isCancelled()
|| plugin.getCore().getAuthPluginHook() == null || plugin.getCore().getAuthPluginHook() == null
|| !plugin.isServerFullyStarted()) { || !plugin.isServerFullyStarted()) {
return; return;
} }
if (isFastLoginPacket(packetEvent)) {
// this is our own packet
return;
}
Player sender = packetEvent.getPlayer(); Player sender = packetEvent.getPlayer();
PacketType packetType = getOverriddenType(packetEvent.getPacketType()); PacketType packetType = packetEvent.getPacketType();
if (packetType == START) {
// PacketContainer packet = packet;
plugin.getLog().info("New incoming packet {} from {}", packetType, sender.getName()); InetSocketAddress address = sender.getAddress();
try { String username = getUsername(packet);
if (packetType == START) {
if (plugin.getFloodgateService() != null) {
boolean success = processFloodgateTasks(packetEvent);
if (!success) {
// don't continue execution if the player was kicked by Floodgate
return;
}
}
PacketContainer packet = packetEvent.getPacket(); Action action = antiBotService.onIncomingConnection(address, username);
switch (action) {
InetSocketAddress address = sender.getAddress(); case Ignore:
String username = getUsername(packet); // just ignore
return;
Action action = antiBotService.onIncomingConnection(address, username); case Block:
switch (action) { String message = plugin.getCore().getMessage("kick-antibot");
case Ignore: sender.kickPlayer(message);
// just ignore break;
return; case Continue:
case Block: default:
String message = plugin.getCore().getMessage("kick-antibot"); //player.getName() won't work at this state
sender.kickPlayer(message); onLoginStart(packetEvent, sender, username);
break; break;
case Continue:
default:
//player.getName() won't work at this state
onLoginStart(packetEvent, sender, username);
break;
}
} else if (packetType == ENCRYPTION_BEGIN) {
onEncryptionBegin(packetEvent, sender);
} else {
plugin.getLog().warn("Unknown packet type received {}", packetType);
} }
} catch (FieldAccessException fieldAccessEx) { } else {
plugin.getLog().error("Failed to parse packet {}", packetEvent.getPacketType(), fieldAccessEx); onEncryptionBegin(packetEvent, sender);
} }
} }
private @NotNull PacketType getOverriddenType(PacketType packetType) { private boolean isFastLoginPacket(PacketEvent packetEvent) {
if (packetType.isDynamic()) { return packetEvent.getPacket().getMeta(SOURCE_META_KEY)
String vanillaName = packetType.getPacketClass().getName(); .map(val -> val.equals(plugin.getName()))
plugin.getLog().info("Overriding packet type for unregistered packet type to fix ProtocolLib bug"); .orElse(false);
if (vanillaName.endsWith("ServerboundHelloPacket")) {
return START;
}
if (vanillaName.endsWith("ServerboundKeyPacket")) {
return ENCRYPTION_BEGIN;
}
}
return packetType;
} }
private void onEncryptionBegin(PacketEvent packetEvent, Player sender) { private void onEncryptionBegin(PacketEvent packetEvent, Player sender) {
@@ -181,12 +174,13 @@ public class ProtocolLibListener extends PacketAdapter {
} else { } else {
byte[] expectedVerifyToken = session.getVerifyToken(); byte[] expectedVerifyToken = session.getVerifyToken();
if (verifyNonce(sender, packetEvent.getPacket(), session.getClientPublicKey(), expectedVerifyToken)) { if (verifyNonce(sender, packetEvent.getPacket(), session.getClientPublicKey(), expectedVerifyToken)) {
packetEvent.getAsyncMarker().incrementProcessingDelay(); // packetEvent.getAsyncMarker().incrementProcessingDelay();
Runnable verifyTask = new VerifyResponseTask( Runnable verifyTask = new VerifyResponseTask(
plugin, packetEvent, sender, session, sharedSecret, keyPair plugin, packetEvent, sender, session, sharedSecret, keyPair
); );
plugin.getScheduler().runAsync(verifyTask); verifyTask.run();
// plugin.getScheduler().runAsync(verifyTask);
} else { } else {
sender.kickPlayer(plugin.getCore().getMessage("invalid-verify-token")); sender.kickPlayer(plugin.getCore().getMessage("invalid-verify-token"));
} }
@@ -196,8 +190,7 @@ public class ProtocolLibListener extends PacketAdapter {
private boolean verifyNonce(Player sender, PacketContainer packet, private boolean verifyNonce(Player sender, PacketContainer packet,
ClientPublicKey clientPublicKey, byte[] expectedToken) { ClientPublicKey clientPublicKey, byte[] expectedToken) {
try { try {
if (new MinecraftVersion(1, 19, 0).atOrAbove() if (MinecraftVersion.atOrAbove(new MinecraftVersion(1, 19, 0))) {
&& !new MinecraftVersion(1, 19, 3).atOrAbove()) {
Either<byte[], ?> either = packet.getSpecificModifier(Either.class).read(0); Either<byte[], ?> either = packet.getSpecificModifier(Either.class).read(0);
if (clientPublicKey == null) { if (clientPublicKey == null) {
Optional<byte[]> left = either.left(); Optional<byte[]> left = either.left();
@@ -239,50 +232,48 @@ public class ProtocolLibListener extends PacketAdapter {
//remove old data every time on a new login in order to keep the session only for one person //remove old data every time on a new login in order to keep the session only for one person
plugin.removeSession(player.getAddress()); plugin.removeSession(player.getAddress());
if (packetEvent.getPacket().getMeta("original_name").isPresent()) {
//username has been injected by ManualNameChange.java
username = (String) packetEvent.getPacket().getMeta("original_name").get();
}
PacketContainer packet = packetEvent.getPacket(); PacketContainer packet = packetEvent.getPacket();
Optional<ClientPublicKey> clientKey; var profileKey = packet.getOptionals(BukkitConverters.getWrappedPublicKeyDataConverter())
if (new MinecraftVersion(1, 19, 3).atOrAbove()) { .optionRead(0);
// public key is sent separate
clientKey = Optional.empty();
} else {
Optional<Optional<WrappedProfileKeyData>> profileKey = packet.getOptionals(
BukkitConverters.getWrappedPublicKeyDataConverter()
).optionRead(0);
clientKey = profileKey.flatMap(Function.identity()).flatMap(data -> { var clientKey = profileKey.flatMap(opt -> opt).flatMap(this::verifyPublicKey);
Instant expires = data.getExpireTime(); if (verifyClientKeys && !clientKey.isPresent()) {
PublicKey key = data.getKey(); // missing or incorrect
byte[] signature = data.getSignature(); // expired always not allowed
return Optional.of(ClientPublicKey.of(expires, key, signature)); player.kickPlayer(plugin.getCore().getMessage("invalid-public-key"));
}); plugin.getLog().warn("Invalid public key from player {}", username);
return;
// start reading from index 1, because 0 is already used by the public key
Optional<UUID> sessionUUID = packet.getOptionals(Converters.passthrough(UUID.class)).readSafely(1);
if (verifyClientKeys && sessionUUID.isPresent() && clientKey.isPresent()
&& verifyPublicKey(clientKey.get(), sessionUUID.get())) {
// missing or incorrect
// expired always not allowed
player.kickPlayer(plugin.getCore().getMessage("invalid-public-key"));
plugin.getLog().warn("Invalid public key from player {}", username);
return;
}
} }
plugin.getLog().trace("GameProfile {} with {} connecting", sessionKey, username); plugin.getLog().trace("GameProfile {} with {} connecting", sessionKey, username);
packetEvent.getAsyncMarker().incrementProcessingDelay(); // packetEvent.getAsyncMarker().incrementProcessingDelay();
Runnable nameCheckTask = new NameCheckTask( Runnable nameCheckTask = new NameCheckTask(
plugin, random, player, packetEvent, username, clientKey.orElse(null), keyPair.getPublic() plugin, random, player, packetEvent, username, clientKey.orElse(null), keyPair.getPublic()
); );
plugin.getScheduler().runAsync(nameCheckTask); // plugin.getScheduler().runAsync(nameCheckTask);
nameCheckTask.run();
} }
private boolean verifyPublicKey(ClientPublicKey clientKey, UUID sessionPremiumUUID) { private Optional<ClientPublicKey> verifyPublicKey(WrappedProfileKeyData profileKey) {
Instant expires = profileKey.getExpireTime();
PublicKey key = profileKey.getKey();
byte[] signature = profileKey.getSignature();
ClientPublicKey clientKey = ClientPublicKey.of(expires, key, signature);
try { try {
return EncryptionUtil.verifyClientKey(clientKey, Instant.now(), sessionPremiumUUID); if (EncryptionUtil.verifyClientKey(clientKey, Instant.now())) {
return Optional.of(clientKey);
}
} catch (SignatureException | InvalidKeyException | NoSuchAlgorithmException ex) { } catch (SignatureException | InvalidKeyException | NoSuchAlgorithmException ex) {
return false; return Optional.empty();
} }
return Optional.empty();
} }
private String getUsername(PacketContainer packet) { private String getUsername(PacketContainer packet) {
@@ -294,57 +285,4 @@ public class ProtocolLibListener extends PacketAdapter {
//player.getName() won't work at this state //player.getName() won't work at this state
return profile.getName(); return profile.getName();
} }
private FloodgatePlayer getFloodgatePlayer(Player player) {
Channel channel = getChannel(player);
AttributeKey<FloodgatePlayer> floodgateAttribute = AttributeKey.valueOf("floodgate-player");
return channel.attr(floodgateAttribute).get();
}
private static Channel getChannel(Player player) {
NettyChannelInjector injector = (NettyChannelInjector) Accessors.getMethodAccessorOrNull(
TemporaryPlayerFactory.class, "getInjectorFromPlayer", Player.class
).invoke(null, player);
return FuzzyReflection.getFieldValue(injector, Channel.class, true);
}
/**
* Reimplementation of the tasks injected Floodgate in ProtocolLib that are not run due to a bug
* @see <a href="https://github.com/GeyserMC/Floodgate/issues/143">Issue Floodgate#143</a>
* @see <a href="https://github.com/GeyserMC/Floodgate/blob/5d5713ed9e9eeab0f4abdaa9cf5cd8619dc1909b/spigot/src/main/java/org/geysermc/floodgate/addon/data/SpigotDataHandler.java#L121-L175">Floodgate/SpigotDataHandler</a>
* @param packetEvent the PacketEvent that won't be processed by Floodgate
* @return false if the player was kicked
*/
private boolean processFloodgateTasks(PacketEvent packetEvent) {
PacketContainer packet = packetEvent.getPacket();
Player player = packetEvent.getPlayer();
FloodgatePlayer floodgatePlayer = getFloodgatePlayer(player);
if (floodgatePlayer == null) {
return true;
}
// kick the player, if necessary
Channel channel = getChannel(packetEvent.getPlayer());
AttributeKey<String> kickMessageAttribute = AttributeKey.valueOf("floodgate-kick-message");
String kickMessage = channel.attr(kickMessageAttribute).get();
if (kickMessage != null) {
player.kickPlayer(kickMessage);
return false;
}
// add prefix
String username = floodgatePlayer.getCorrectUsername();
if (packet.getGameProfiles().size() > 0) {
packet.getGameProfiles().write(0,
new WrappedGameProfile(floodgatePlayer.getCorrectUniqueId(), username));
} else {
packet.getStrings().write(0, username);
}
// remove real Floodgate data handler
ChannelHandler floodgateHandler = channel.pipeline().get("floodgate_data_handler");
channel.pipeline().remove(floodgateHandler);
return true;
}
} }
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -32,13 +32,14 @@ import com.comphenix.protocol.reflect.StructureModifier;
import com.comphenix.protocol.wrappers.WrappedChatComponent; import com.comphenix.protocol.wrappers.WrappedChatComponent;
import com.github.games647.fastlogin.bukkit.listener.protocollib.packet.ClientPublicKey; import com.github.games647.fastlogin.bukkit.listener.protocollib.packet.ClientPublicKey;
import com.github.games647.fastlogin.core.shared.LoginSource; import com.github.games647.fastlogin.core.shared.LoginSource;
import org.bukkit.entity.Player;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.security.PublicKey; import java.security.PublicKey;
import java.util.Arrays; import java.util.Arrays;
import java.util.Random; import java.util.Random;
import org.bukkit.entity.Player;
import static com.comphenix.protocol.PacketType.Login.Server.DISCONNECT; import static com.comphenix.protocol.PacketType.Login.Server.DISCONNECT;
import static com.comphenix.protocol.PacketType.Login.Server.ENCRYPTION_BEGIN; import static com.comphenix.protocol.PacketType.Login.Server.ENCRYPTION_BEGIN;
@@ -51,6 +52,7 @@ class ProtocolLibLoginSource implements LoginSource {
private final ClientPublicKey clientKey; private final ClientPublicKey clientKey;
private final PublicKey publicKey; private final PublicKey publicKey;
private final String serverId = "";
private byte[] verifyToken; private byte[] verifyToken;
ProtocolLibLoginSource(Player player, Random random, PublicKey serverPublicKey, ClientPublicKey clientKey) { ProtocolLibLoginSource(Player player, Random random, PublicKey serverPublicKey, ClientPublicKey clientKey) {
@@ -71,7 +73,7 @@ class ProtocolLibLoginSource implements LoginSource {
*/ */
PacketContainer newPacket = new PacketContainer(ENCRYPTION_BEGIN); PacketContainer newPacket = new PacketContainer(ENCRYPTION_BEGIN);
newPacket.getStrings().write(0, ""); newPacket.getStrings().write(0, serverId);
StructureModifier<PublicKey> keyModifier = newPacket.getSpecificModifier(PublicKey.class); StructureModifier<PublicKey> keyModifier = newPacket.getSpecificModifier(PublicKey.class);
int verifyField = 0; int verifyField = 0;
if (keyModifier.getFields().isEmpty()) { if (keyModifier.getFields().isEmpty()) {
@@ -83,8 +85,6 @@ class ProtocolLibLoginSource implements LoginSource {
} }
newPacket.getByteArrays().write(verifyField, verifyToken); newPacket.getByteArrays().write(verifyField, verifyToken);
// shouldAuthenticate, but why does this field even exist?
newPacket.getBooleans().writeSafely(0, true);
//serverId is an empty string //serverId is an empty string
ProtocolLibrary.getProtocolManager().sendServerPacket(player, newPacket); ProtocolLibrary.getProtocolManager().sendServerPacket(player, newPacket);
@@ -116,6 +116,10 @@ class ProtocolLibLoginSource implements LoginSource {
return clientKey; return clientKey;
} }
public String getServerId() {
return serverId;
}
public byte[] getVerifyToken() { public byte[] getVerifyToken() {
return verifyToken.clone(); return verifyToken.clone();
} }
@@ -125,6 +129,7 @@ class ProtocolLibLoginSource implements LoginSource {
return this.getClass().getSimpleName() + '{' return this.getClass().getSimpleName() + '{'
+ "player=" + player + "player=" + player
+ ", random=" + random + ", random=" + random
+ ", serverId='" + serverId + '\''
+ ", verifyToken=" + Arrays.toString(verifyToken) + ", verifyToken=" + Arrays.toString(verifyToken)
+ '}'; + '}';
} }
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -30,6 +30,7 @@ import com.comphenix.protocol.wrappers.WrappedSignedProperty;
import com.github.games647.craftapi.model.skin.Textures; import com.github.games647.craftapi.model.skin.Textures;
import com.github.games647.fastlogin.bukkit.BukkitLoginSession; import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit; import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority; import org.bukkit.event.EventPriority;
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -28,18 +28,14 @@ package com.github.games647.fastlogin.bukkit.listener.protocollib;
import com.comphenix.protocol.ProtocolLibrary; import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.events.PacketContainer; import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.events.PacketEvent; import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.injector.netty.channel.NettyChannelInjector;
import com.comphenix.protocol.injector.packet.PacketRegistry;
import com.comphenix.protocol.injector.temporary.TemporaryPlayerFactory; import com.comphenix.protocol.injector.temporary.TemporaryPlayerFactory;
import com.comphenix.protocol.reflect.EquivalentConverter; import com.comphenix.protocol.reflect.EquivalentConverter;
import com.comphenix.protocol.reflect.FuzzyReflection; import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.reflect.accessors.Accessors; import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
import com.comphenix.protocol.reflect.accessors.FieldAccessor; import com.comphenix.protocol.reflect.accessors.FieldAccessor;
import com.comphenix.protocol.utility.MinecraftReflection; import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.utility.MinecraftVersion; import com.comphenix.protocol.utility.MinecraftVersion;
import com.comphenix.protocol.wrappers.BukkitConverters; import com.comphenix.protocol.wrappers.BukkitConverters;
import com.comphenix.protocol.wrappers.Converters;
import com.comphenix.protocol.wrappers.WrappedChatComponent; import com.comphenix.protocol.wrappers.WrappedChatComponent;
import com.comphenix.protocol.wrappers.WrappedGameProfile; import com.comphenix.protocol.wrappers.WrappedGameProfile;
import com.comphenix.protocol.wrappers.WrappedProfilePublicKey.WrappedProfileKeyData; import com.comphenix.protocol.wrappers.WrappedProfilePublicKey.WrappedProfileKeyData;
@@ -48,12 +44,8 @@ import com.github.games647.craftapi.model.skin.SkinProperty;
import com.github.games647.craftapi.resolver.MojangResolver; import com.github.games647.craftapi.resolver.MojangResolver;
import com.github.games647.fastlogin.bukkit.BukkitLoginSession; import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit; 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 com.github.games647.fastlogin.bukkit.listener.protocollib.packet.ClientPublicKey;
import org.bukkit.entity.Player;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.net.InetAddress; import java.net.InetAddress;
@@ -66,19 +58,25 @@ import java.util.Arrays;
import java.util.Optional; import java.util.Optional;
import java.util.UUID; import java.util.UUID;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import lombok.val;
import org.bukkit.entity.Player;
import static com.comphenix.protocol.PacketType.Login.Client.START; import static com.comphenix.protocol.PacketType.Login.Client.START;
import static com.comphenix.protocol.PacketType.Login.Server.DISCONNECT; import static com.comphenix.protocol.PacketType.Login.Server.DISCONNECT;
public class VerifyResponseTask implements Runnable { public class VerifyResponseTask implements Runnable {
private static final String ENCRYPTION_CLASS_NAME = "MinecraftEncryption"; private static final String ENCRYPTION_CLASS_NAME = "MinecraftEncryption";
private static final String ADDRESS_VERIFY_WARNING = "This indicates the use of reverse-proxy like HAProxy, " private static final Class<?> ENCRYPTION_CLASS;
+ "TCPShield, BungeeCord, Velocity, etc. "
+ "By default (configurable in the config) this plugin requests Mojang to verify the connecting IP " static {
+ "to this server with the one used to log into Minecraft to prevent MITM attacks. In " ENCRYPTION_CLASS = MinecraftReflection.getMinecraftClass(
+ "order to work this security feature, the actual client IP needs to be forwarding " "util." + ENCRYPTION_CLASS_NAME, ENCRYPTION_CLASS_NAME
+ "(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."; }
private final FastLoginBukkit plugin; private final FastLoginBukkit plugin;
private final PacketEvent packetEvent; private final PacketEvent packetEvent;
@@ -91,8 +89,6 @@ public class VerifyResponseTask implements Runnable {
private final byte[] sharedSecret; private final byte[] sharedSecret;
private static Method encryptMethod; private static Method encryptMethod;
private static Method encryptKeyMethod;
private static Method cipherMethod; private static Method cipherMethod;
public VerifyResponseTask(FastLoginBukkit plugin, PacketEvent packetEvent, public VerifyResponseTask(FastLoginBukkit plugin, PacketEvent packetEvent,
@@ -112,11 +108,11 @@ public class VerifyResponseTask implements Runnable {
verifyResponse(session); verifyResponse(session);
} finally { } finally {
//this is a fake packet; it shouldn't be sent to the server //this is a fake packet; it shouldn't be sent to the server
synchronized (packetEvent.getAsyncMarker().getProcessingLock()) { // synchronized (packetEvent.getAsyncMarker().getProcessingLock()) {
packetEvent.setCancelled(true); packetEvent.setCancelled(true);
} // }
ProtocolLibrary.getProtocolManager().getAsynchronousManager().signalPacketTransmission(packetEvent); // ProtocolLibrary.getProtocolManager().getAsynchronousManager().signalPacketTransmission(packetEvent);
} }
} }
@@ -154,26 +150,9 @@ public class VerifyResponseTask implements Runnable {
//user tried to fake an authentication //user tried to fake an authentication
disconnect( disconnect(
"invalid-session", "invalid-session",
"Session server rejected incoming connection for GameProfile {} ({}). Possible reasons are " "GameProfile {} ({}) tried to log in with an invalid session. ServerId: {}",
+ "1) Client IP address contacting Mojang and server during server join were different " session.getRequestUsername(), socketAddress, serverId
+ "(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
);
} 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) { } catch (IOException ioEx) {
disconnect("error-kick", "Failed to connect to session server", ioEx); disconnect("error-kick", "Failed to connect to session server", ioEx);
@@ -195,10 +174,10 @@ public class VerifyResponseTask implements Runnable {
session.setVerifiedUsername(realUsername); session.setVerifiedUsername(realUsername);
session.setUuid(verification.getId()); session.setUuid(verification.getId());
session.setVerifiedPremium(true); session.setVerified(true);
setPremiumUUID(session.getUuid()); setPremiumUUID(session.getUuid());
receiveFakeStartPacket(realUsername, session.getClientPublicKey(), session.getUuid()); receiveFakeStartPacket(realUsername, session.getClientPublicKey());
} }
private void setPremiumUUID(UUID premiumUUID) { private void setPremiumUUID(UUID premiumUUID) {
@@ -218,36 +197,34 @@ public class VerifyResponseTask implements Runnable {
//try to get the networkManager from ProtocolLib //try to get the networkManager from ProtocolLib
private Object getNetworkManager() throws ClassNotFoundException { private Object getNetworkManager() throws ClassNotFoundException {
NettyChannelInjector injectorContainer = (NettyChannelInjector) Accessors.getMethodAccessorOrNull( Object injectorContainer = TemporaryPlayerFactory.getInjectorFromPlayer(player);
TemporaryPlayerFactory.class, "getInjectorFromPlayer", Player.class
).invoke(null, player);
FieldAccessor accessor = Accessors.getFieldAccessorOrNull( // ChannelInjector
NettyChannelInjector.class, "networkManager", Object.class Class<?> injectorClass = Class.forName("com.comphenix.protocol.injector.netty.Injector");
); Object rawInjector = FuzzyReflection.getFieldValue(injectorContainer, injectorClass, true);
return accessor.get(injectorContainer);
Class<?> rawInjectorClass = rawInjector.getClass();
FieldAccessor accessor = Accessors.getFieldAccessorOrNull(rawInjectorClass, "networkManager", Object.class);
return accessor.get(rawInjector);
} }
private boolean enableEncryption(SecretKey loginKey) throws IllegalArgumentException { private boolean enableEncryption(SecretKey loginKey) throws IllegalArgumentException {
plugin.getLog().info("Enabling onlinemode encryption for {}", player.getAddress()); plugin.getLog().info("Enabling onlinemode encryption for {}", player.getAddress());
// Initialize method reflections // Initialize method reflections
if (encryptKeyMethod == null || encryptMethod == null) { if (encryptMethod == null) {
Class<?> networkManagerClass = MinecraftReflection.getNetworkManagerClass(); Class<?> networkManagerClass = MinecraftReflection.getNetworkManagerClass();
try { try {
// Try to get the old (pre MC 1.16.4) encryption method // Try to get the old (pre MC 1.16.4) encryption method
encryptKeyMethod = FuzzyReflection.fromClass(networkManagerClass) encryptMethod = FuzzyReflection.fromClass(networkManagerClass)
.getMethodByParameters("a", SecretKey.class); .getMethodByParameters("a", SecretKey.class);
} catch (IllegalArgumentException exception) { } catch (IllegalArgumentException exception) {
// Get the new encryption method // Get the new encryption method
encryptMethod = FuzzyReflection.fromClass(networkManagerClass) encryptMethod = FuzzyReflection.fromClass(networkManagerClass)
.getMethodByParameters("a", Cipher.class, Cipher.class); .getMethodByParameters("a", Cipher.class, Cipher.class);
Class<?> encryptionClass = MinecraftReflection.getMinecraftClass(
"util." + ENCRYPTION_CLASS_NAME, ENCRYPTION_CLASS_NAME
);
// Get the needed Cipher helper method (used to generate ciphers from login key) // Get the needed Cipher helper method (used to generate ciphers from login key)
cipherMethod = FuzzyReflection.fromClass(encryptionClass) cipherMethod = FuzzyReflection.fromClass(ENCRYPTION_CLASS)
.getMethodByParameters("a", int.class, Key.class); .getMethodByParameters("a", int.class, Key.class);
} }
} }
@@ -256,9 +233,9 @@ public class VerifyResponseTask implements Runnable {
Object networkManager = this.getNetworkManager(); Object networkManager = this.getNetworkManager();
// If cipherMethod is null - use old encryption (pre MC 1.16.4), otherwise use the new cipher one // If cipherMethod is null - use old encryption (pre MC 1.16.4), otherwise use the new cipher one
if (encryptKeyMethod != null) { if (cipherMethod == null) {
// Encrypt/decrypt packet flow, this behaviour is expected by the client // Encrypt/decrypt packet flow, this behaviour is expected by the client
encryptKeyMethod.invoke(networkManager, loginKey); encryptMethod.invoke(networkManager, loginKey);
} else { } else {
// Create ciphers from login key // Create ciphers from login key
Object decryptionCipher = cipherMethod.invoke(null, Cipher.DECRYPT_MODE, loginKey); Object decryptionCipher = cipherMethod.invoke(null, Cipher.DECRYPT_MODE, loginKey);
@@ -291,22 +268,15 @@ public class VerifyResponseTask implements Runnable {
} }
//fake a new login packet in order to let the server handle all the other stuff //fake a new login packet in order to let the server handle all the other stuff
private void receiveFakeStartPacket(String username, ClientPublicKey clientKey, UUID uuid) { private void receiveFakeStartPacket(String username, ClientPublicKey clientKey) {
PacketContainer startPacket; //see StartPacketListener for packet information
if (new MinecraftVersion(1, 20, 2).atOrAbove()) { PacketContainer startPacket = new PacketContainer(START);
startPacket = new PacketContainer(START);
startPacket.getStrings().write(0, username); if (MinecraftVersion.atOrAbove(new MinecraftVersion(1, 19, 0))) {
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); startPacket.getStrings().write(0, username);
EquivalentConverter<WrappedProfileKeyData> converter = BukkitConverters.getWrappedPublicKeyDataConverter(); EquivalentConverter<WrappedProfileKeyData> converter = BukkitConverters.getWrappedPublicKeyDataConverter();
Optional<WrappedProfileKeyData> wrappedKey = Optional.ofNullable(clientKey).map(key -> val wrappedKey = Optional.ofNullable(clientKey).map(key ->
new WrappedProfileKeyData(clientKey.expiry(), clientKey.key(), clientKey.signature()) new WrappedProfileKeyData(clientKey.expiry(), clientKey.key(), clientKey.signature())
); );
@@ -314,14 +284,12 @@ public class VerifyResponseTask implements Runnable {
} else { } else {
//uuid is ignored by the packet definition //uuid is ignored by the packet definition
WrappedGameProfile fakeProfile = new WrappedGameProfile(UUID.randomUUID(), username); WrappedGameProfile fakeProfile = new WrappedGameProfile(UUID.randomUUID(), username);
startPacket.getGameProfiles().write(0, fakeProfile);
Class<?> profileHandleType = fakeProfile.getHandleType();
Class<?> packetHandleType = PacketRegistry.getPacketClassFromType(START);
ConstructorAccessor startCons = Accessors.getConstructorAccessorOrNull(packetHandleType, profileHandleType);
startPacket = new PacketContainer(START, startCons.invoke(fakeProfile.getHandle()));
} }
//we don't want to handle our own packets so ignore filters //we don't want to handle our own packets so ignore filters
ProtocolLibrary.getProtocolManager().receiveClientPacket(player, startPacket, false); startPacket.setMeta(ProtocolLibListener.SOURCE_META_KEY, plugin.getName());
ProtocolLibrary.getProtocolManager().receiveClientPacket(player, startPacket, true);
plugin.getLog().info("Sending new fake login start packet to {}-{}", player, username);
} }
} }
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -27,47 +27,18 @@ package com.github.games647.fastlogin.bukkit.listener.protocollib.packet;
import java.security.PublicKey; import java.security.PublicKey;
import java.time.Instant; import java.time.Instant;
import java.util.Base64;
import java.util.StringJoiner;
import lombok.Value;
import lombok.experimental.Accessors;
@Accessors(fluent = true)
@Value(staticConstructor = "of")
public class ClientPublicKey { public class ClientPublicKey {
Instant expiry;
private final Instant expiry; PublicKey key;
private final PublicKey key; byte[] signature;
private final byte[] signature;
public Instant expiry() {
return expiry;
}
public PublicKey key() {
return key;
}
public byte[] signature() {
return signature;
}
public ClientPublicKey(Instant expiry, PublicKey key, byte[] signature) {
this.expiry = expiry;
this.key = key;
this.signature = signature;
}
public static ClientPublicKey of(Instant expiry, PublicKey key, byte[] signature) {
return new ClientPublicKey(expiry, key, signature);
}
public boolean isExpired(Instant verifyTimestamp) { public boolean isExpired(Instant verifyTimestamp) {
return !verifyTimestamp.isBefore(expiry); return !verifyTimestamp.isBefore(expiry);
} }
@Override
public String toString() {
return new StringJoiner(", ", ClientPublicKey.class.getSimpleName() + '[', "]")
.add("expiry=" + expiry)
.add("key=" + Base64.getEncoder().encodeToString(key.getEncoded()))
.add("signature=" + Base64.getEncoder().encodeToString(signature))
.toString();
}
} }
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -26,10 +26,11 @@
package com.github.games647.fastlogin.bukkit.listener.protocolsupport; package com.github.games647.fastlogin.bukkit.listener.protocolsupport;
import com.github.games647.fastlogin.core.shared.LoginSource; import com.github.games647.fastlogin.core.shared.LoginSource;
import protocolsupport.api.events.PlayerLoginStartEvent;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import protocolsupport.api.events.PlayerLoginStartEvent;
public class ProtocolLoginSource implements LoginSource { public class ProtocolLoginSource implements LoginSource {
private final PlayerLoginStartEvent loginStartEvent; private final PlayerLoginStartEvent loginStartEvent;
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -29,11 +29,15 @@ import com.github.games647.craftapi.UUIDAdapter;
import com.github.games647.fastlogin.bukkit.BukkitLoginSession; import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit; import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import com.github.games647.fastlogin.bukkit.event.BukkitFastLoginPreLoginEvent; import com.github.games647.fastlogin.bukkit.event.BukkitFastLoginPreLoginEvent;
import com.github.games647.fastlogin.core.StoredProfile;
import com.github.games647.fastlogin.core.antibot.AntiBotService; import com.github.games647.fastlogin.core.antibot.AntiBotService;
import com.github.games647.fastlogin.core.antibot.AntiBotService.Action; import com.github.games647.fastlogin.core.antibot.AntiBotService.Action;
import com.github.games647.fastlogin.core.shared.JoinManagement; import com.github.games647.fastlogin.core.shared.JoinManagement;
import com.github.games647.fastlogin.core.shared.event.FastLoginPreLoginEvent; import com.github.games647.fastlogin.core.shared.event.FastLoginPreLoginEvent;
import com.github.games647.fastlogin.core.storage.StoredProfile;
import java.net.InetSocketAddress;
import java.util.Optional;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
@@ -42,9 +46,6 @@ import protocolsupport.api.events.ConnectionCloseEvent;
import protocolsupport.api.events.PlayerLoginStartEvent; import protocolsupport.api.events.PlayerLoginStartEvent;
import protocolsupport.api.events.PlayerProfileCompleteEvent; import protocolsupport.api.events.PlayerProfileCompleteEvent;
import java.net.InetSocketAddress;
import java.util.Optional;
public class ProtocolSupportListener extends JoinManagement<Player, CommandSender, ProtocolLoginSource> public class ProtocolSupportListener extends JoinManagement<Player, CommandSender, ProtocolLoginSource>
implements Listener { implements Listener {
@@ -101,7 +102,7 @@ public class ProtocolSupportListener extends JoinManagement<Player, CommandSende
BukkitLoginSession session = plugin.getSession(address); BukkitLoginSession session = plugin.getSession(address);
if (session != null && profileCompleteEvent.getConnection().getProfile().isOnlineMode()) { if (session != null && profileCompleteEvent.getConnection().getProfile().isOnlineMode()) {
session.setVerifiedPremium(true); session.setVerified(true);
if (!plugin.getConfig().getBoolean("premiumUuid")) { if (!plugin.getConfig().getBoolean("premiumUuid")) {
String username = Optional.ofNullable(profileCompleteEvent.getForcedName()) String username = Optional.ofNullable(profileCompleteEvent.getForcedName())
@@ -125,7 +126,7 @@ public class ProtocolSupportListener extends JoinManagement<Player, CommandSende
source.enableOnlinemode(); source.enableOnlinemode();
String ip = source.getAddress().getAddress().getHostAddress(); String ip = source.getAddress().getAddress().getHostAddress();
plugin.getCore().addLoginAttempt(ip, username); plugin.getCore().getPendingLogin().put(ip + username, new Object());
BukkitLoginSession playerSession = new BukkitLoginSession(username, registered, profile); BukkitLoginSession playerSession = new BukkitLoginSession(username, registered, profile);
plugin.putSession(source.getAddress(), playerSession); plugin.putSession(source.getAddress(), playerSession);
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -32,16 +32,16 @@ import com.github.games647.fastlogin.bukkit.hook.LogItHook;
import com.github.games647.fastlogin.bukkit.hook.LoginSecurityHook; import com.github.games647.fastlogin.bukkit.hook.LoginSecurityHook;
import com.github.games647.fastlogin.bukkit.hook.UltraAuthHook; import com.github.games647.fastlogin.bukkit.hook.UltraAuthHook;
import com.github.games647.fastlogin.bukkit.hook.XAuthHook; import com.github.games647.fastlogin.bukkit.hook.XAuthHook;
import com.github.games647.fastlogin.bukkit.hook.PasskyHook;
import com.github.games647.fastlogin.core.hooks.AuthPlugin; import com.github.games647.fastlogin.core.hooks.AuthPlugin;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
public class DelayedAuthHook implements Runnable { public class DelayedAuthHook implements Runnable {
private final FastLoginBukkit plugin; private final FastLoginBukkit plugin;
@@ -95,7 +95,7 @@ public class DelayedAuthHook implements Runnable {
try { try {
List<Class<? extends AuthPlugin<Player>>> hooks = Arrays.asList(AuthMeHook.class, List<Class<? extends AuthPlugin<Player>>> hooks = Arrays.asList(AuthMeHook.class,
CrazyLoginHook.class, LogItHook.class, LoginSecurityHook.class, UltraAuthHook.class, CrazyLoginHook.class, LogItHook.class, LoginSecurityHook.class, UltraAuthHook.class,
XAuthHook.class, PasskyHook.class); XAuthHook.class);
for (Class<? extends AuthPlugin<Player>> clazz : hooks) { for (Class<? extends AuthPlugin<Player>> clazz : hooks) {
String pluginName = clazz.getSimpleName(); String pluginName = clazz.getSimpleName();
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -29,14 +29,15 @@ import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit; import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import com.github.games647.fastlogin.core.shared.FastLoginCore; import com.github.games647.fastlogin.core.shared.FastLoginCore;
import com.github.games647.fastlogin.core.shared.FloodgateManagement; import com.github.games647.fastlogin.core.shared.FloodgateManagement;
import java.net.InetSocketAddress;
import java.util.UUID;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.geysermc.floodgate.api.player.FloodgatePlayer; import org.geysermc.floodgate.api.player.FloodgatePlayer;
import java.net.InetSocketAddress;
import java.util.UUID;
public class FloodgateAuthTask extends FloodgateManagement<Player, CommandSender, BukkitLoginSession, FastLoginBukkit> { public class FloodgateAuthTask extends FloodgateManagement<Player, CommandSender, BukkitLoginSession, FastLoginBukkit> {
public FloodgateAuthTask(FastLoginCore<Player, CommandSender, FastLoginBukkit> core, Player player, public FloodgateAuthTask(FastLoginCore<Player, CommandSender, FastLoginBukkit> core, Player player,
@@ -49,7 +50,7 @@ public class FloodgateAuthTask extends FloodgateManagement<Player, CommandSender
BukkitLoginSession session = new BukkitLoginSession(player.getName(), isRegistered, profile); BukkitLoginSession session = new BukkitLoginSession(player.getName(), isRegistered, profile);
// enable auto login based on the value of 'autoLoginFloodgate' in config.yml // enable auto login based on the value of 'autoLoginFloodgate' in config.yml
session.setVerifiedPremium(isAutoAuthAllowed(autoLoginFloodgate)); session.setVerified(isAutoAuthAllowed(autoLoginFloodgate));
// run login task // run login task
Runnable forceLoginTask = new ForceLoginTask(core.getPlugin().getCore(), player, session); Runnable forceLoginTask = new ForceLoginTask(core.getPlugin().getCore(), player, session);
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -29,19 +29,20 @@ import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit; import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import com.github.games647.fastlogin.bukkit.event.BukkitFastLoginAutoLoginEvent; import com.github.games647.fastlogin.bukkit.event.BukkitFastLoginAutoLoginEvent;
import com.github.games647.fastlogin.core.PremiumStatus; import com.github.games647.fastlogin.core.PremiumStatus;
import com.github.games647.fastlogin.core.StoredProfile;
import com.github.games647.fastlogin.core.message.SuccessMessage; import com.github.games647.fastlogin.core.message.SuccessMessage;
import com.github.games647.fastlogin.core.shared.FastLoginCore; import com.github.games647.fastlogin.core.shared.FastLoginCore;
import com.github.games647.fastlogin.core.shared.ForceLoginManagement; import com.github.games647.fastlogin.core.shared.ForceLoginManagement;
import com.github.games647.fastlogin.core.shared.LoginSession; import com.github.games647.fastlogin.core.shared.LoginSession;
import com.github.games647.fastlogin.core.shared.event.FastLoginAutoLoginEvent; import com.github.games647.fastlogin.core.shared.event.FastLoginAutoLoginEvent;
import com.github.games647.fastlogin.core.storage.StoredProfile;
import java.util.concurrent.ExecutionException;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.metadata.FixedMetadataValue; import org.bukkit.metadata.FixedMetadataValue;
import java.util.concurrent.ExecutionException;
public class ForceLoginTask extends ForceLoginManagement<Player, CommandSender, BukkitLoginSession, FastLoginBukkit> { public class ForceLoginTask extends ForceLoginManagement<Player, CommandSender, BukkitLoginSession, FastLoginBukkit> {
public ForceLoginTask(FastLoginCore<Player, CommandSender, FastLoginBukkit> core, Player player, public ForceLoginTask(FastLoginCore<Player, CommandSender, FastLoginBukkit> core, Player player,
@@ -103,6 +104,6 @@ public class ForceLoginTask extends ForceLoginManagement<Player, CommandSender,
@Override @Override
public boolean isOnlineMode() { public boolean isOnlineMode() {
return session.isVerifiedPremium(); return session.isVerified();
} }
} }
+3 -12
View File
@@ -25,12 +25,12 @@ softdepend:
- floodgate - floodgate
# Auth plugins # Auth plugins
- AuthMe - AuthMe
- CrazyLogin
- LoginSecurity - LoginSecurity
- SodionAuth
- xAuth
- LogIt - LogIt
- UltraAuth - UltraAuth
- xAuth - CrazyLogin
- Passky
commands: commands:
${project.parent.name}: ${project.parent.name}:
@@ -45,11 +45,6 @@ commands:
usage: /<command> [player] usage: /<command> [player]
permission: ${project.artifactId}.command.cracked permission: ${project.artifactId}.command.cracked
fldelete:
description: 'Delete player profile data'
usage: /<command> [player]
permission: ${project.artifactId}.command.delete
permissions: permissions:
${project.artifactId}.command.premium: ${project.artifactId}.command.premium:
description: 'Label themselves as premium' description: 'Label themselves as premium'
@@ -68,7 +63,3 @@ permissions:
description: 'Label others as cracked' description: 'Label others as cracked'
children: children:
${project.artifactId}.command.cracked: true ${project.artifactId}.command.cracked: true
${project.artifactId}.command.delete:
description: 'Delete other players profile data'
default: op
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -26,9 +26,11 @@
package com.github.games647.fastlogin.bukkit; package com.github.games647.fastlogin.bukkit;
import com.github.games647.fastlogin.core.CommonUtil; import com.github.games647.fastlogin.core.CommonUtil;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.TextComponent; import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.chat.ComponentSerializer; import net.md_5.bungee.chat.ComponentSerializer;
import lombok.val;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -37,14 +39,12 @@ class FastLoginBukkitTest {
@Test @Test
void testRGB() { void testRGB() {
String message = "&x00002a00002b&lText"; val message = "&x00002a00002b&lText";
String msg = CommonUtil.translateColorCodes(message); val msg = CommonUtil.translateColorCodes(message);
assertEquals(msg, "§x00002a00002b§lText"); assertEquals(msg, "§x00002a00002b§lText");
@SuppressWarnings("deprecation") val components = TextComponent.fromLegacyText(msg);
BaseComponent[] components = TextComponent.fromLegacyText(msg); val expected = "{\"bold\":true,\"color\":\"#00a00b\",\"text\":\"Text\"}";
String expected = "{\"bold\":true,\"color\":\"#00a00b\",\"text\":\"Text\"}";
//noinspection deprecation
assertEquals(ComponentSerializer.toString(components), expected); assertEquals(ComponentSerializer.toString(components), expected);
} }
} }
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -27,8 +27,10 @@ package com.github.games647.fastlogin.bukkit;
import com.comphenix.protocol.reflect.accessors.Accessors; import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.FieldAccessor; import com.comphenix.protocol.reflect.accessors.FieldAccessor;
import fr.xephi.authme.api.v3.AuthMeApi; import fr.xephi.authme.api.v3.AuthMeApi;
import fr.xephi.authme.process.Management; import fr.xephi.authme.process.Management;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotNull;
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -27,8 +27,10 @@ package com.github.games647.fastlogin.bukkit.hook;
import com.comphenix.protocol.reflect.accessors.Accessors; import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.FieldAccessor; import com.comphenix.protocol.reflect.accessors.FieldAccessor;
import de.st_ddt.crazylogin.CrazyLogin; import de.st_ddt.crazylogin.CrazyLogin;
import de.st_ddt.crazylogin.listener.PlayerListener; import de.st_ddt.crazylogin.listener.PlayerListener;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotNull;
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -32,11 +32,13 @@ import com.google.gson.stream.JsonWriter;
import java.io.IOException; import java.io.IOException;
import java.util.Base64; import java.util.Base64;
import lombok.val;
public class Base64Adapter extends TypeAdapter<byte[]> { public class Base64Adapter extends TypeAdapter<byte[]> {
@Override @Override
public void write(JsonWriter out, byte[] value) throws IOException { public void write(JsonWriter out, byte[] value) throws IOException {
String encoded = Base64.getEncoder().encodeToString(value); val encoded = Base64.getEncoder().encodeToString(value);
out.value(encoded); out.value(encoded);
} }
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -27,18 +27,8 @@ package com.github.games647.fastlogin.bukkit.listener.protocollib;
import com.github.games647.fastlogin.bukkit.listener.protocollib.SignatureTestData.SignatureData; import com.github.games647.fastlogin.bukkit.listener.protocollib.SignatureTestData.SignatureData;
import com.github.games647.fastlogin.bukkit.listener.protocollib.packet.ClientPublicKey; import com.github.games647.fastlogin.bukkit.listener.protocollib.packet.ClientPublicKey;
import com.google.common.hash.Hasher;
import com.google.common.hash.Hashing; import com.google.common.hash.Hashing;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.math.BigInteger; import java.math.BigInteger;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
@@ -51,17 +41,27 @@ import java.security.SignatureException;
import java.security.interfaces.RSAPublicKey; import java.security.interfaces.RSAPublicKey;
import java.time.Instant; import java.time.Instant;
import java.time.temporal.ChronoUnit; import java.time.temporal.ChronoUnit;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.ThreadLocalRandom;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import lombok.val;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assertions.*;
class EncryptionUtilTest { class EncryptionUtilTest {
@Test @Test
void testVerifyToken() { void testVerifyToken() {
Random random = ThreadLocalRandom.current(); val random = ThreadLocalRandom.current();
byte[] token = EncryptionUtil.generateVerifyToken(random); byte[] token = EncryptionUtil.generateVerifyToken(random);
assertAll( assertAll(
@@ -89,11 +89,11 @@ class EncryptionUtilTest {
@Test @Test
void testExpiredClientKey() throws Exception { void testExpiredClientKey() throws Exception {
ClientPublicKey clientKey = ResourceLoader.loadClientKey("client_keys/valid_public_key.json"); val clientKey = ResourceLoader.loadClientKey("client_keys/valid_public_key.json");
// Client expires at the exact second mentioned, so use it for verification // Client expires at the exact second mentioned, so use it for verification
Instant expiredTimestamp = clientKey.expiry(); val expiredTimestamp = clientKey.expiry();
assertFalse(EncryptionUtil.verifyClientKey(clientKey, expiredTimestamp, null)); assertFalse(EncryptionUtil.verifyClientKey(clientKey, expiredTimestamp));
} }
@ParameterizedTest @ParameterizedTest
@@ -106,42 +106,24 @@ class EncryptionUtilTest {
"client_keys/invalid_wrong_signature.json" "client_keys/invalid_wrong_signature.json"
}) })
void testInvalidClientKey(String clientKeySource) throws Exception { void testInvalidClientKey(String clientKeySource) throws Exception {
ClientPublicKey clientKey = ResourceLoader.loadClientKey(clientKeySource); val clientKey = ResourceLoader.loadClientKey(clientKeySource);
Instant expireTimestamp = clientKey.expiry().minus(5, ChronoUnit.HOURS); Instant expireTimestamp = clientKey.expiry().minus(5, ChronoUnit.HOURS);
assertFalse(EncryptionUtil.verifyClientKey(clientKey, expireTimestamp, null)); assertFalse(EncryptionUtil.verifyClientKey(clientKey, expireTimestamp));
} }
@Test @Test
void testValidClientKey() throws Exception { void testValidClientKey() throws Exception {
ClientPublicKey clientKey = ResourceLoader.loadClientKey("client_keys/valid_public_key.json"); val clientKey = ResourceLoader.loadClientKey("client_keys/valid_public_key.json");
Instant verificationTimestamp = clientKey.expiry().minus(5, ChronoUnit.HOURS); val verificationTimestamp = clientKey.expiry().minus(5, ChronoUnit.HOURS);
assertTrue(EncryptionUtil.verifyClientKey(clientKey, verificationTimestamp, null)); assertTrue(EncryptionUtil.verifyClientKey(clientKey, verificationTimestamp));
}
@Test
void testValid191ClientKey() throws Exception {
ClientPublicKey clientKey = ResourceLoader.loadClientKey("client_keys/valid_public_key_19_1.json");
Instant verificationTimestamp = clientKey.expiry().minus(5, ChronoUnit.HOURS);
UUID ownerPremiumId = UUID.fromString("0aaa2c13-922a-411b-b655-9b8c08404695");
assertTrue(EncryptionUtil.verifyClientKey(clientKey, verificationTimestamp, ownerPremiumId));
}
@Test
void testIncorrect191ClientOwner() throws Exception {
ClientPublicKey clientKey = ResourceLoader.loadClientKey("client_keys/valid_public_key_19_1.json");
Instant verificationTimestamp = clientKey.expiry().minus(5, ChronoUnit.HOURS);
UUID ownerPremiumId = UUID.fromString("61699b2e-d327-4a01-9f1e-0ea8c3f06bc6");
assertFalse(EncryptionUtil.verifyClientKey(clientKey, verificationTimestamp, ownerPremiumId));
} }
@Test @Test
void testDecryptSharedSecret() throws Exception { void testDecryptSharedSecret() throws Exception {
KeyPair serverPair = EncryptionUtil.generateKeyPair(); KeyPair serverPair = EncryptionUtil.generateKeyPair();
PublicKey serverPK = serverPair.getPublic(); val serverPK = serverPair.getPublic();
SecretKey secretKey = generateSharedKey(); SecretKey secretKey = generateSharedKey();
byte[] encryptedSecret = encrypt(serverPK, secretKey.getEncoded()); byte[] encryptedSecret = encrypt(serverPK, secretKey.getEncoded());
@@ -153,7 +135,7 @@ class EncryptionUtilTest {
private static byte[] encrypt(PublicKey receiverKey, byte... message) private static byte[] encrypt(PublicKey receiverKey, byte... message)
throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException,
IllegalBlockSizeException, BadPaddingException { IllegalBlockSizeException, BadPaddingException {
Cipher encryptCipher = Cipher.getInstance(receiverKey.getAlgorithm()); val encryptCipher = Cipher.getInstance(receiverKey.getAlgorithm());
encryptCipher.init(Cipher.ENCRYPT_MODE, receiverKey); encryptCipher.init(Cipher.ENCRYPT_MODE, receiverKey);
return encryptCipher.doFinal(message); return encryptCipher.doFinal(message);
} }
@@ -169,16 +151,15 @@ class EncryptionUtilTest {
@Test @Test
void testServerIdHash() throws Exception { void testServerIdHash() throws Exception {
String serverId = ""; val serverId = "";
SecretKeySpec sharedSecret = generateSharedKey(); val sharedSecret = generateSharedKey();
PublicKey serverPK = ResourceLoader.loadClientKey("client_keys/valid_public_key.json").key(); val serverPK = ResourceLoader.loadClientKey("client_keys/valid_public_key.json").key();
String sessionHash = getServerHash(serverId, sharedSecret, serverPK); String sessionHash = getServerHash(serverId, sharedSecret, serverPK);
assertEquals(EncryptionUtil.getServerIdHashString(serverId, sharedSecret, serverPK), sessionHash); assertEquals(EncryptionUtil.getServerIdHashString(serverId, sharedSecret, serverPK), sessionHash);
} }
private static String getServerHash(@SuppressWarnings("SameParameterValue") CharSequence serverId, private static String getServerHash(CharSequence serverId, SecretKey sharedSecret, PublicKey serverPK) {
SecretKey sharedSecret, PublicKey serverPK) {
// https://wiki.vg/Protocol_Encryption#Client // https://wiki.vg/Protocol_Encryption#Client
// sha1 := Sha1() // sha1 := Sha1()
// sha1.update(ASCII encoding of the server id string from Encryption Request) // sha1.update(ASCII encoding of the server id string from Encryption Request)
@@ -186,7 +167,7 @@ class EncryptionUtilTest {
// sha1.update(server's encoded public key from Encryption Request) // sha1.update(server's encoded public key from Encryption Request)
// hash := sha1.hexdigest() # String of hex characters // hash := sha1.hexdigest() # String of hex characters
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
Hasher hasher = Hashing.sha1().newHasher(); val hasher = Hashing.sha1().newHasher();
hasher.putString(serverId, StandardCharsets.US_ASCII); hasher.putString(serverId, StandardCharsets.US_ASCII);
hasher.putBytes(sharedSecret.getEncoded()); hasher.putBytes(sharedSecret.getEncoded());
hasher.putBytes(serverPK.getEncoded()); hasher.putBytes(serverPK.getEncoded());
@@ -199,9 +180,9 @@ class EncryptionUtilTest {
@Test @Test
void testServerIdHashWrongSecret() throws Exception { void testServerIdHashWrongSecret() throws Exception {
String serverId = ""; val serverId = "";
SecretKeySpec sharedSecret = generateSharedKey(); val sharedSecret = generateSharedKey();
PublicKey serverPK = ResourceLoader.loadClientKey("client_keys/valid_public_key.json").key(); val serverPK = ResourceLoader.loadClientKey("client_keys/valid_public_key.json").key();
String sessionHash = getServerHash(serverId, sharedSecret, serverPK); String sessionHash = getServerHash(serverId, sharedSecret, serverPK);
assertNotEquals(EncryptionUtil.getServerIdHashString("", generateSharedKey(), serverPK), sessionHash); assertNotEquals(EncryptionUtil.getServerIdHashString("", generateSharedKey(), serverPK), sessionHash);
@@ -209,12 +190,12 @@ class EncryptionUtilTest {
@Test @Test
void testServerIdHashWrongServerKey() { void testServerIdHashWrongServerKey() {
String serverId = ""; val serverId = "";
SecretKeySpec sharedSecret = generateSharedKey(); val sharedSecret = generateSharedKey();
PublicKey serverPK = EncryptionUtil.generateKeyPair().getPublic(); val serverPK = EncryptionUtil.generateKeyPair().getPublic();
String sessionHash = getServerHash(serverId, sharedSecret, serverPK); String sessionHash = getServerHash(serverId, sharedSecret, serverPK);
PublicKey wrongPK = EncryptionUtil.generateKeyPair().getPublic(); val wrongPK = EncryptionUtil.generateKeyPair().getPublic();
assertNotEquals(EncryptionUtil.getServerIdHashString("", sharedSecret, wrongPK), sessionHash); assertNotEquals(EncryptionUtil.getServerIdHashString("", sharedSecret, wrongPK), sessionHash);
} }
@@ -258,8 +239,8 @@ class EncryptionUtilTest {
@Test @Test
void testNonce() throws Exception { void testNonce() throws Exception {
byte[] expected = {1, 2, 3, 4}; byte[] expected = {1, 2, 3, 4};
KeyPair serverKey = EncryptionUtil.generateKeyPair(); val serverKey = EncryptionUtil.generateKeyPair();
byte[] encryptedNonce = encrypt(serverKey.getPublic(), expected); val encryptedNonce = encrypt(serverKey.getPublic(), expected);
assertTrue(EncryptionUtil.verifyNonce(expected, serverKey.getPrivate(), encryptedNonce)); assertTrue(EncryptionUtil.verifyNonce(expected, serverKey.getPrivate(), encryptedNonce));
} }
@@ -267,19 +248,19 @@ class EncryptionUtilTest {
@Test @Test
void testNonceIncorrect() throws Exception { void testNonceIncorrect() throws Exception {
byte[] expected = {1, 2, 3, 4}; byte[] expected = {1, 2, 3, 4};
KeyPair serverKey = EncryptionUtil.generateKeyPair(); val serverKey = EncryptionUtil.generateKeyPair();
// flipped first character // flipped first character
byte[] encryptedNonce = encrypt(serverKey.getPublic(), new byte[]{0, 2, 3, 4}); val encryptedNonce = encrypt(serverKey.getPublic(), new byte[]{0, 2, 3, 4});
assertFalse(EncryptionUtil.verifyNonce(expected, serverKey.getPrivate(), encryptedNonce)); assertFalse(EncryptionUtil.verifyNonce(expected, serverKey.getPrivate(), encryptedNonce));
} }
@Test @Test
void testNonceFailedDecryption() throws Exception { void testNonceFailedDecryption() throws Exception {
byte[] expected = {1, 2, 3, 4}; byte[] expected = {1, 2, 3, 4};
KeyPair serverKey = EncryptionUtil.generateKeyPair(); val serverKey = EncryptionUtil.generateKeyPair();
// generate a new keypair that is different // generate a new keypair that is different
byte[] encryptedNonce = encrypt(EncryptionUtil.generateKeyPair().getPublic(), expected); val encryptedNonce = encrypt(EncryptionUtil.generateKeyPair().getPublic(), expected);
assertThrows(GeneralSecurityException.class, assertThrows(GeneralSecurityException.class,
() -> EncryptionUtil.verifyNonce(expected, serverKey.getPrivate(), encryptedNonce) () -> EncryptionUtil.verifyNonce(expected, serverKey.getPrivate(), encryptedNonce)
@@ -289,7 +270,7 @@ class EncryptionUtilTest {
@Test @Test
void testNonceIncorrectEmpty() { void testNonceIncorrectEmpty() {
byte[] expected = {1, 2, 3, 4}; byte[] expected = {1, 2, 3, 4};
KeyPair serverKey = EncryptionUtil.generateKeyPair(); val serverKey = EncryptionUtil.generateKeyPair();
byte[] encryptedNonce = {}; byte[] encryptedNonce = {};
assertThrows(GeneralSecurityException.class, assertThrows(GeneralSecurityException.class,
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -29,8 +29,6 @@ import com.github.games647.fastlogin.bukkit.listener.protocollib.packet.ClientPu
import com.google.common.io.Resources; import com.google.common.io.Resources;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemReader;
import java.io.IOException; import java.io.IOException;
import java.io.Reader; import java.io.Reader;
@@ -42,12 +40,14 @@ import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey; import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException; import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec; import java.security.spec.X509EncodedKeySpec;
import java.time.Instant; import java.time.Instant;
import java.util.Base64; import java.util.Base64;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemReader;
public class ResourceLoader { public class ResourceLoader {
public static RSAPrivateKey parsePrivateKey(String keySpec) public static RSAPrivateKey parsePrivateKey(String keySpec)
@@ -58,7 +58,7 @@ public class ResourceLoader {
) { ) {
PemObject pemObject = pemReader.readPemObject(); PemObject pemObject = pemReader.readPemObject();
byte[] content = pemObject.getContent(); byte[] content = pemObject.getContent();
KeySpec privateKeySpec = new PKCS8EncodedKeySpec(content); PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(content);
KeyFactory factory = KeyFactory.getInstance("RSA"); KeyFactory factory = KeyFactory.getInstance("RSA");
return (RSAPrivateKey) factory.generatePrivate(privateKeySpec); return (RSAPrivateKey) factory.generatePrivate(privateKeySpec);
@@ -88,7 +88,7 @@ public class ResourceLoader {
) { ) {
PemObject pemObject = pemReader.readPemObject(); PemObject pemObject = pemReader.readPemObject();
byte[] content = pemObject.getContent(); byte[] content = pemObject.getContent();
KeySpec pubKeySpec = new X509EncodedKeySpec(content); X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(content);
KeyFactory factory = KeyFactory.getInstance("RSA"); KeyFactory factory = KeyFactory.getInstance("RSA");
return (RSAPublicKey) factory.generatePublic(pubKeySpec); return (RSAPublicKey) factory.generatePublic(pubKeySpec);
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -30,14 +30,15 @@ import com.google.gson.Gson;
import com.google.gson.annotations.JsonAdapter; import com.google.gson.annotations.JsonAdapter;
import java.io.IOException; import java.io.IOException;
import java.net.URL;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import lombok.val;
public class SignatureTestData { public class SignatureTestData {
public static SignatureTestData fromResource(String resourceName) throws IOException { public static SignatureTestData fromResource(String resourceName) throws IOException {
URL keyUrl = Resources.getResource(resourceName); val keyUrl = Resources.getResource(resourceName);
String encodedSignature = Resources.toString(keyUrl, StandardCharsets.US_ASCII); val encodedSignature = Resources.toString(keyUrl, StandardCharsets.US_ASCII);
return new Gson().fromJson(encodedSignature, SignatureTestData.class); return new Gson().fromJson(encodedSignature, SignatureTestData.class);
} }
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -29,14 +29,16 @@ import com.comphenix.protocol.injector.packet.PacketRegistry;
import com.comphenix.protocol.reflect.accessors.Accessors; import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.FieldAccessor; import com.comphenix.protocol.reflect.accessors.FieldAccessor;
import com.comphenix.protocol.utility.MinecraftReflection; import com.comphenix.protocol.utility.MinecraftReflection;
import java.util.Optional;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic; import org.mockito.MockedStatic;
import java.util.Optional; import static org.mockito.ArgumentMatchers.any;
import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mockStatic; import static org.mockito.Mockito.mockStatic;
class VerifyResponseTaskTest { class VerifyResponseTaskTest {
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -26,6 +26,8 @@
package com.github.games647.fastlogin.bukkit.task; package com.github.games647.fastlogin.bukkit.task;
import com.github.games647.fastlogin.core.hooks.AuthPlugin; import com.github.games647.fastlogin.core.hooks.AuthPlugin;
import lombok.val;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@@ -35,7 +37,7 @@ class DelayedAuthHookTest {
@Test @Test
void createNewReflectiveInstance() throws ReflectiveOperationException { void createNewReflectiveInstance() throws ReflectiveOperationException {
DelayedAuthHook authHook = new DelayedAuthHook(null); val authHook = new DelayedAuthHook(null);
assertNotNull(authHook.newInstance(DummyHook.class)); assertNotNull(authHook.newInstance(DummyHook.class));
} }
@@ -1,5 +0,0 @@
{
"expires_at": "2022-07-29T12:57:39.011Z",
"key": "-----BEGIN RSA PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtYOUXdid0c09/eYoseLf8qG9fKQ/G2DY9wlSyEZaFMflwZ8ZpLFYigxzfaimpT3A5cbFIdIH2W2sYl5PwsKSs128GBh/rxXUEZlLkIkS+EfxyuMp9ITclxAjCqvFgfJbZHugtB9Ofi6knCEEgjFwMDh2efdpOXkCxtHuPFfnVzDJBbHWdlCCtJesMAnA2jCT7CqCwsi7sW2QxuTarqHP/cHKiBeBIu/SngGUB6eWmvAwERW5x2D+O26w8Z5sQCND3xQ4D868RALiPNG94TyKoJV+jKi0tTUmjGGs/1ksbSGDQb5xqIH0NYKZhoZrczYPNmJX4k7g5BA5RHX8AGORaQIDAQAB\n-----END RSA PUBLIC KEY-----\n",
"signature": "Fto/GDqEMTWpNrktWSi3tnP3ZZlo8r4Jled/5PKYRvaL/zksfjB2RK2O8pZL+w5mI2VAViTVAQmSJEF2o/BCb2L0zXp3/hC9VhZj5NTVi4KbHfnfMorj7/WJP2vvMgVxIxgLb3EEQXGS2Mmo0w2ikUVauwXgLWECvVt10KAZnTAWNIvpM8NUoZ2oCCxVimYHBtlwWQ7WvowAatP4ypa7fo3xhQg8Im1hvQDsFTNp58pgnd7l3l99xLj1uYOUJM06HGZJ/Xd0kzzJz44Csh4m50Q0RP4Nq5L+fYPeUx990Z1r1lw0sSayk+vA2Qnxgbs/z6KgkxfhBg7oOlp4ykl9cLC2kA/LdV6igqsdr/KoP4GWxwTA7RgQbhMkDFdmIg1W+gh3XqwASFQK2BAN/eAfmDTf8u9BtOEF7Ehn9uPOaiFiGztyaHxXNIkVSPTG2GXMFSijnd3Ms28jHYVY/67INTnDRmN0//KzBAoTRMe1S5idai19kug4EUVIRKDziipowzCPdbD18trdQGpK0dYOrw9XQiQd4N4V3eItpyAULGiZd8KcjgKo4orqgsUfNyhLI1keig7TyJUE3FkBOfX4hlZBm7Q/Wq4hwarlc5yZIjhsuivKV/q4tcnYYPwjP7kNMRsIApWG+yHmSIo8QfZhBiPxvtWSSLZgoFgnlxfaEko="
}
+40 -52
View File
@@ -4,7 +4,7 @@
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2015-2024 games647 and contributors Copyright (c) 2015-2022 games647 and contributors
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
@@ -25,7 +25,7 @@
SOFTWARE. SOFTWARE.
--> -->
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0" <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"> 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> <modelVersion>4.0.0</modelVersion>
@@ -40,10 +40,6 @@
<artifactId>fastlogin.bungee</artifactId> <artifactId>fastlogin.bungee</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>
<properties>
<maven.compiler.release>17</maven.compiler.release>
</properties>
<!--Represents the main plugin--> <!--Represents the main plugin-->
<name>FastLoginBungee</name> <name>FastLoginBungee</name>
@@ -51,12 +47,20 @@
<plugins> <plugins>
<plugin> <plugin>
<artifactId>maven-shade-plugin</artifactId> <artifactId>maven-shade-plugin</artifactId>
<version>3.6.0</version> <version>3.3.0</version>
<configuration> <configuration>
<minimizeJar>true</minimizeJar> <minimizeJar>true</minimizeJar>
<createDependencyReducedPom>false</createDependencyReducedPom> <createDependencyReducedPom>false</createDependencyReducedPom>
<shadedArtifactAttached>false</shadedArtifactAttached> <shadedArtifactAttached>false</shadedArtifactAttached>
<artifactSet>
<excludes>
<!--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> <relocations>
<relocation> <relocation>
<pattern>com.zaxxer.hikari</pattern> <pattern>com.zaxxer.hikari</pattern>
@@ -70,15 +74,13 @@
<!-- Rename the service file too to let SLF4J api find our own relocated jdk logger --> <!-- Rename the service file too to let SLF4J api find our own relocated jdk logger -->
<!-- Located in META-INF/services --> <!-- Located in META-INF/services -->
<transformers> <transformers>
<transformer <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
</transformers> </transformers>
<filters> <filters>
<filter> <filter>
<artifact>*:*</artifact> <artifact>*:*</artifact>
<excludes> <excludes>
<exclude>META-INF/MANIFEST.MF</exclude> <exclude>META-INF/MANIFEST.MF</exclude>
<exclude>**/module-info</exclude>
<exclude>**/module-info.class</exclude> <exclude>**/module-info.class</exclude>
</excludes> </excludes>
</filter> </filter>
@@ -102,6 +104,11 @@
<url>https://repo.codemc.io/repository/maven-public/</url> <url>https://repo.codemc.io/repository/maven-public/</url>
</repository> </repository>
<repository>
<id>spigotplugins-repo</id>
<url>https://maven.gamestrike.de/mvn/</url>
</repository>
<repository> <repository>
<id>jitpack.io</id> <id>jitpack.io</id>
<url>https://jitpack.io</url> <url>https://jitpack.io</url>
@@ -117,33 +124,13 @@
<groupId>${project.groupId}</groupId> <groupId>${project.groupId}</groupId>
<artifactId>fastlogin.core</artifactId> <artifactId>fastlogin.core</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
<!--Those classes are already present in BungeeCord version-->
<exclusions>
<exclusion>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-config</artifactId>
</exclusion>
<exclusion>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</exclusion>
<exclusion>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</exclusion>
<!-- Exclude snakeyaml, because it is included in BungeeCord with the correct version -->
<exclusion>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
</exclusion>
</exclusions>
</dependency> </dependency>
<!--BungeeCord with also the part outside the API--> <!--BungeeCord with also the part outside the API-->
<dependency> <dependency>
<groupId>net.md-5</groupId> <groupId>net.md-5</groupId>
<artifactId>bungeecord-proxy</artifactId> <artifactId>bungeecord-proxy</artifactId>
<version>1.20-R0.2-SNAPSHOT</version> <version>1.19-R0.1-SNAPSHOT</version>
<scope>provided</scope> <scope>provided</scope>
<!-- Use our own newer api version --> <!-- Use our own newer api version -->
<exclusions> <exclusions>
@@ -152,14 +139,13 @@
<artifactId>slf4j-api</artifactId> <artifactId>slf4j-api</artifactId>
</exclusion> </exclusion>
<exclusion> <exclusion>
<groupId>com.mysql</groupId> <groupId>mysql</groupId>
<artifactId>*</artifactId> <artifactId>*</artifactId>
</exclusion> </exclusion>
<exclusion> <exclusion>
<groupId>net.md-5</groupId> <groupId>net.md-5</groupId>
<artifactId>bungeecord-native</artifactId> <artifactId>bungeecord-native</artifactId>
</exclusion> </exclusion>
<exclusion> <exclusion>
<groupId>net.md-5</groupId> <groupId>net.md-5</groupId>
<artifactId>bungeecord-query</artifactId> <artifactId>bungeecord-query</artifactId>
@@ -168,11 +154,6 @@
<groupId>net.md-5</groupId> <groupId>net.md-5</groupId>
<artifactId>bungeecord-slf4j</artifactId> <artifactId>bungeecord-slf4j</artifactId>
</exclusion> </exclusion>
<exclusion>
<groupId>net.sf.jopt-simple</groupId>
<artifactId>*</artifactId>
</exclusion>
<exclusion> <exclusion>
<groupId>org.apache.maven</groupId> <groupId>org.apache.maven</groupId>
<artifactId>*</artifactId> <artifactId>*</artifactId>
@@ -181,10 +162,6 @@
<groupId>org.apache.maven.resolver</groupId> <groupId>org.apache.maven.resolver</groupId>
<artifactId>*</artifactId> <artifactId>*</artifactId>
</exclusion> </exclusion>
<exclusion>
<groupId>io.netty</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions> </exclusions>
</dependency> </dependency>
@@ -196,7 +173,11 @@
<scope>provided</scope> <scope>provided</scope>
<exclusions> <exclusions>
<exclusion> <exclusion>
<groupId>*</groupId> <groupId>io.netty</groupId>
<artifactId>*</artifactId>
</exclusion>
<exclusion>
<groupId>org.geysermc.cumulus</groupId>
<artifactId>*</artifactId> <artifactId>*</artifactId>
</exclusion> </exclusion>
</exclusions> </exclusions>
@@ -204,7 +185,7 @@
<!-- Bedrock player bridge --> <!-- Bedrock player bridge -->
<dependency> <dependency>
<groupId>org.geysermc.geyser</groupId> <groupId>org.geysermc</groupId>
<artifactId>core</artifactId> <artifactId>core</artifactId>
<version>${geyser.version}</version> <version>${geyser.version}</version>
<scope>provided</scope> <scope>provided</scope>
@@ -218,16 +199,10 @@
<!-- We need the API, but it was excluded above --> <!-- We need the API, but it was excluded above -->
<dependency> <dependency>
<groupId>org.geysermc.geyser</groupId> <groupId>org.geysermc</groupId>
<artifactId>api</artifactId> <artifactId>geyser-api</artifactId>
<version>${geyser.version}</version> <version>${geyser.version}</version>
<scope>provided</scope> <scope>provided</scope>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency> </dependency>
<!--Login plugin--> <!--Login plugin-->
@@ -238,5 +213,18 @@
<scope>system</scope> <scope>system</scope>
<systemPath>${project.basedir}/lib/BungeeAuth-1.4.jar</systemPath> <systemPath>${project.basedir}/lib/BungeeAuth-1.4.jar</systemPath>
</dependency> </dependency>
<dependency>
<groupId>de.xxschrandxx.bca</groupId>
<artifactId>BungeeCordAuthenticator</artifactId>
<version>0.0.3</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies> </dependencies>
</project> </project>
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -25,8 +25,8 @@
*/ */
package com.github.games647.fastlogin.bungee; package com.github.games647.fastlogin.bungee;
import com.github.games647.fastlogin.core.StoredProfile;
import com.github.games647.fastlogin.core.shared.LoginSession; import com.github.games647.fastlogin.core.shared.LoginSession;
import com.github.games647.fastlogin.core.storage.StoredProfile;
public class BungeeLoginSession extends LoginSession { public class BungeeLoginSession extends LoginSession {
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -26,14 +26,15 @@
package com.github.games647.fastlogin.bungee; package com.github.games647.fastlogin.bungee;
import com.github.games647.fastlogin.core.shared.LoginSource; import com.github.games647.fastlogin.core.shared.LoginSource;
import java.net.InetSocketAddress;
import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.chat.ComponentBuilder; import net.md_5.bungee.api.chat.ComponentBuilder;
import net.md_5.bungee.api.chat.TextComponent; import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.connection.PendingConnection; import net.md_5.bungee.api.connection.PendingConnection;
import net.md_5.bungee.api.event.PreLoginEvent; import net.md_5.bungee.api.event.PreLoginEvent;
import java.net.InetSocketAddress;
public class BungeeLoginSource implements LoginSource { public class BungeeLoginSource implements LoginSource {
private final PendingConnection connection; private final PendingConnection connection;
@@ -54,15 +55,9 @@ public class BungeeLoginSource implements LoginSource {
preLoginEvent.setCancelled(true); preLoginEvent.setCancelled(true);
if (message == null) { if (message == null) {
preLoginEvent.setReason( preLoginEvent.setCancelReason(new ComponentBuilder("Kicked").color(ChatColor.WHITE).create());
TextComponent.fromArray(
new ComponentBuilder("Kicked").color(ChatColor.WHITE).create()
));
} else { } else {
preLoginEvent.setReason( preLoginEvent.setCancelReason(TextComponent.fromLegacyText(message));
TextComponent.fromArray(
TextComponent.fromLegacyText(message)
));
} }
} }
@@ -78,7 +73,7 @@ public class BungeeLoginSource implements LoginSource {
@Override @Override
public String toString() { public String toString() {
return this.getClass().getSimpleName() + '{' return this.getClass().getSimpleName() + '{'
+ "connection=" + connection + "connection=" + connection
+ '}'; + '}';
} }
} }
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -26,8 +26,10 @@
package com.github.games647.fastlogin.bungee; package com.github.games647.fastlogin.bungee;
import com.github.games647.fastlogin.bungee.hook.BungeeAuthHook; import com.github.games647.fastlogin.bungee.hook.BungeeAuthHook;
import com.github.games647.fastlogin.bungee.hook.BungeeCordAuthenticatorBungeeHook;
import com.github.games647.fastlogin.bungee.listener.ConnectListener; import com.github.games647.fastlogin.bungee.listener.ConnectListener;
import com.github.games647.fastlogin.bungee.listener.PluginMessageListener; 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.CommonUtil;
import com.github.games647.fastlogin.core.hooks.AuthPlugin; 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.BedrockService;
@@ -37,13 +39,19 @@ import com.github.games647.fastlogin.core.message.ChangePremiumMessage;
import com.github.games647.fastlogin.core.message.ChannelMessage; import com.github.games647.fastlogin.core.message.ChannelMessage;
import com.github.games647.fastlogin.core.message.NamespaceKey; import com.github.games647.fastlogin.core.message.NamespaceKey;
import com.github.games647.fastlogin.core.message.SuccessMessage; import com.github.games647.fastlogin.core.message.SuccessMessage;
import com.github.games647.fastlogin.core.scheduler.AsyncScheduler;
import com.github.games647.fastlogin.core.shared.FastLoginCore; import com.github.games647.fastlogin.core.shared.FastLoginCore;
import com.github.games647.fastlogin.core.shared.PlatformPlugin; import com.github.games647.fastlogin.core.shared.PlatformPlugin;
import com.google.common.collect.MapMaker; import com.google.common.collect.MapMaker;
import com.google.common.io.ByteArrayDataOutput; import com.google.common.io.ByteArrayDataOutput;
import com.google.common.io.ByteStreams; import com.google.common.io.ByteStreams;
import com.google.common.util.concurrent.ThreadFactoryBuilder; import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ThreadFactory;
import net.md_5.bungee.api.CommandSender; import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.chat.TextComponent; import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.connection.PendingConnection; import net.md_5.bungee.api.connection.PendingConnection;
@@ -53,16 +61,11 @@ import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.api.plugin.Plugin; import net.md_5.bungee.api.plugin.Plugin;
import net.md_5.bungee.api.plugin.PluginManager; import net.md_5.bungee.api.plugin.PluginManager;
import net.md_5.bungee.api.scheduler.GroupedThreadFactory; import net.md_5.bungee.api.scheduler.GroupedThreadFactory;
import org.geysermc.floodgate.api.FloodgateApi; import org.geysermc.floodgate.api.FloodgateApi;
import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserImpl;
import org.slf4j.Logger; import org.slf4j.Logger;
import java.nio.file.Path;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ThreadFactory;
/** /**
* BungeeCord version of FastLogin. This plugin keeps track on online mode connections. * BungeeCord version of FastLogin. This plugin keeps track on online mode connections.
*/ */
@@ -98,7 +101,7 @@ public class FastLoginBungee extends Plugin implements PlatformPlugin<CommandSen
//events //events
PluginManager pluginManager = getProxy().getPluginManager(); PluginManager pluginManager = getProxy().getPluginManager();
Listener connectListener = new ConnectListener(this, core.getAntiBotService()); Listener connectListener = new ConnectListener(this, core.getAntiBot());
pluginManager.registerListener(this, connectListener); pluginManager.registerListener(this, connectListener);
pluginManager.registerListener(this, new PluginMessageListener(this)); pluginManager.registerListener(this, new PluginMessageListener(this));
@@ -126,9 +129,8 @@ public class FastLoginBungee extends Plugin implements PlatformPlugin<CommandSen
private void registerHook() { private void registerHook() {
try { try {
List<Class<? extends AuthPlugin<ProxiedPlayer>>> hooks = Collections.singletonList( List<Class<? extends AuthPlugin<ProxiedPlayer>>> hooks = Arrays.asList(
BungeeAuthHook.class BungeeAuthHook.class, BungeeCordAuthenticatorBungeeHook.class);
);
for (Class<? extends AuthPlugin<ProxiedPlayer>> clazz : hooks) { for (Class<? extends AuthPlugin<ProxiedPlayer>> clazz : hooks) {
String pluginName = clazz.getSimpleName(); String pluginName = clazz.getSimpleName();
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -25,9 +25,10 @@
*/ */
package com.github.games647.fastlogin.bungee.event; package com.github.games647.fastlogin.bungee.event;
import com.github.games647.fastlogin.core.StoredProfile;
import com.github.games647.fastlogin.core.shared.LoginSession; import com.github.games647.fastlogin.core.shared.LoginSession;
import com.github.games647.fastlogin.core.shared.event.FastLoginAutoLoginEvent; import com.github.games647.fastlogin.core.shared.event.FastLoginAutoLoginEvent;
import com.github.games647.fastlogin.core.storage.StoredProfile;
import net.md_5.bungee.api.plugin.Cancellable; import net.md_5.bungee.api.plugin.Cancellable;
import net.md_5.bungee.api.plugin.Event; import net.md_5.bungee.api.plugin.Event;
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -25,9 +25,10 @@
*/ */
package com.github.games647.fastlogin.bungee.event; package com.github.games647.fastlogin.bungee.event;
import com.github.games647.fastlogin.core.StoredProfile;
import com.github.games647.fastlogin.core.shared.LoginSource; import com.github.games647.fastlogin.core.shared.LoginSource;
import com.github.games647.fastlogin.core.shared.event.FastLoginPreLoginEvent; import com.github.games647.fastlogin.core.shared.event.FastLoginPreLoginEvent;
import com.github.games647.fastlogin.core.storage.StoredProfile;
import net.md_5.bungee.api.plugin.Event; import net.md_5.bungee.api.plugin.Event;
public class BungeeFastLoginPreLoginEvent extends Event implements FastLoginPreLoginEvent { public class BungeeFastLoginPreLoginEvent extends Event implements FastLoginPreLoginEvent {
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -25,8 +25,9 @@
*/ */
package com.github.games647.fastlogin.bungee.event; package com.github.games647.fastlogin.bungee.event;
import com.github.games647.fastlogin.core.StoredProfile;
import com.github.games647.fastlogin.core.shared.event.FastLoginPremiumToggleEvent; import com.github.games647.fastlogin.core.shared.event.FastLoginPremiumToggleEvent;
import com.github.games647.fastlogin.core.storage.StoredProfile;
import net.md_5.bungee.api.plugin.Event; import net.md_5.bungee.api.plugin.Event;
public class BungeeFastLoginPremiumToggleEvent extends Event implements FastLoginPremiumToggleEvent { public class BungeeFastLoginPremiumToggleEvent extends Event implements FastLoginPremiumToggleEvent {
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -27,16 +27,18 @@ package com.github.games647.fastlogin.bungee.hook;
import com.github.games647.fastlogin.bungee.FastLoginBungee; import com.github.games647.fastlogin.bungee.FastLoginBungee;
import com.github.games647.fastlogin.core.hooks.AuthPlugin; import com.github.games647.fastlogin.core.hooks.AuthPlugin;
import me.vik1395.BungeeAuth.Main; import me.vik1395.BungeeAuth.Main;
import me.vik1395.BungeeAuthAPI.RequestHandler; import me.vik1395.BungeeAuthAPI.RequestHandler;
import net.md_5.bungee.api.connection.ProxiedPlayer; import net.md_5.bungee.api.connection.ProxiedPlayer;
/** /**
* GitHub: * GitHub:
* <a href="https://github.com/vik1395/BungeeAuth-Minecraft">...</a> * <a href="https://github.com/vik1395/BungeeAuth-Minecraft">...</a>
* <p> *
* Project page: * Project page:
* <p> *
* Spigot: * Spigot:
* <a href="https://www.spigotmc.org/resources/bungeeauth.493/">...</a> * <a href="https://www.spigotmc.org/resources/bungeeauth.493/">...</a>
*/ */
@@ -0,0 +1,92 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2022 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.bungee.hook;
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:
* <a href="https://github.com/xXSchrandXx/SpigotPlugins/tree/master/BungeeCordAuthenticator">...</a>
* <p>
* Project page:
* <p>
* Spigot: <a href="https://www.spigotmc.org/resources/bungeecordauthenticator.87669/">...</a>
*/
public class BungeeCordAuthenticatorBungeeHook implements AuthPlugin<ProxiedPlayer> {
public final BungeeCordAuthenticatorBungeeAPI api;
public BungeeCordAuthenticatorBungeeHook(FastLoginBungee plugin) {
api = ((BungeeCordAuthenticatorBungee) plugin.getProxy().getPluginManager()
.getPlugin("BungeeCordAuthenticatorBungee")).getAPI();
plugin.getLog().info("BungeeCordAuthenticatorHook | Hooked successful!");
}
@Override
public boolean forceLogin(ProxiedPlayer player) {
if (api.isAuthenticated(player)) {
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 sqlEx) {
api.getLogger().log(Level.WARNING, "Failed to check registration", sqlEx);
return false;
}
}
@Override
public boolean forceRegister(ProxiedPlayer player, String password) {
try {
return api.createPlayerEntry(player, password);
} catch (SQLException sqlEx) {
api.getLogger().log(Level.WARNING, "Failed to force register", sqlEx);
return false;
}
}
}
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -31,12 +31,20 @@ import com.github.games647.fastlogin.bungee.FastLoginBungee;
import com.github.games647.fastlogin.bungee.task.AsyncPremiumCheck; import com.github.games647.fastlogin.bungee.task.AsyncPremiumCheck;
import com.github.games647.fastlogin.bungee.task.FloodgateAuthTask; import com.github.games647.fastlogin.bungee.task.FloodgateAuthTask;
import com.github.games647.fastlogin.bungee.task.ForceLoginTask; import com.github.games647.fastlogin.bungee.task.ForceLoginTask;
import com.github.games647.fastlogin.core.StoredProfile;
import com.github.games647.fastlogin.core.antibot.AntiBotService; import com.github.games647.fastlogin.core.antibot.AntiBotService;
import com.github.games647.fastlogin.core.antibot.AntiBotService.Action; import com.github.games647.fastlogin.core.antibot.AntiBotService.Action;
import com.github.games647.fastlogin.core.hooks.bedrock.FloodgateService; import com.github.games647.fastlogin.core.hooks.bedrock.FloodgateService;
import com.github.games647.fastlogin.core.shared.LoginSession; import com.github.games647.fastlogin.core.shared.LoginSession;
import com.github.games647.fastlogin.core.storage.StoredProfile;
import com.google.common.base.Throwables; import com.google.common.base.Throwables;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.reflect.Field;
import java.net.InetSocketAddress;
import java.util.UUID;
import net.md_5.bungee.api.chat.TextComponent; import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.connection.PendingConnection; import net.md_5.bungee.api.connection.PendingConnection;
import net.md_5.bungee.api.connection.ProxiedPlayer; import net.md_5.bungee.api.connection.ProxiedPlayer;
@@ -51,17 +59,11 @@ import net.md_5.bungee.connection.LoginResult;
import net.md_5.bungee.event.EventHandler; import net.md_5.bungee.event.EventHandler;
import net.md_5.bungee.event.EventPriority; import net.md_5.bungee.event.EventPriority;
import net.md_5.bungee.protocol.Property; import net.md_5.bungee.protocol.Property;
import org.geysermc.floodgate.api.player.FloodgatePlayer; import org.geysermc.floodgate.api.player.FloodgatePlayer;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.reflect.Field;
import java.net.InetSocketAddress;
import java.util.UUID;
/** /**
* Enables online mode logins for specified users and sends plugin message to the Bukkit version of this plugin in * Enables online mode logins for specified users and sends plugin message to the Bukkit version of this plugin in
* order to clear that the connection is online mode. * order to clear that the connection is online mode.
@@ -71,43 +73,25 @@ public class ConnectListener implements Listener {
private static final String UUID_FIELD_NAME = "uniqueId"; private static final String UUID_FIELD_NAME = "uniqueId";
protected static final MethodHandle UNIQUE_ID_SETTER; protected static final MethodHandle UNIQUE_ID_SETTER;
private static final String REWRITE_ID_NAME = "rewriteId";
protected static final MethodHandle REWRITE_ID_SETTER;
static { static {
MethodHandle uniqueIdHandle = null; MethodHandle setHandle = null;
MethodHandle rewriterHandle = null;
try { try {
Lookup lookup = MethodHandles.lookup(); Lookup lookup = MethodHandles.lookup();
// test for implementation class availability
Class.forName("net.md_5.bungee.connection.InitialHandler"); Class.forName("net.md_5.bungee.connection.InitialHandler");
uniqueIdHandle = getHandlerSetter(lookup, UUID_FIELD_NAME);
try { Field uuidField = InitialHandler.class.getDeclaredField(UUID_FIELD_NAME);
rewriterHandle = getHandlerSetter(lookup, REWRITE_ID_NAME); uuidField.setAccessible(true);
} catch (NoSuchFieldException noSuchFieldEx) { setHandle = lookup.unreflectSetter(uuidField);
Logger logger = LoggerFactory.getLogger(ConnectListener.class);
logger.error(
"Rewrite field not found. Setting only legacy BungeeCord field"
);
}
} catch (ReflectiveOperationException reflectiveOperationException) { } catch (ReflectiveOperationException reflectiveOperationException) {
Logger logger = LoggerFactory.getLogger(ConnectListener.class); Logger logger = LoggerFactory.getLogger(ConnectListener.class);
logger.error( logger.error(
"Cannot find Bungee UUID field implementation; Disabling premium UUID and skin won't work.", "Cannot find Bungee initial handler; Disabling premium UUID and skin won't work.",
reflectiveOperationException reflectiveOperationException
); );
} }
UNIQUE_ID_SETTER = uniqueIdHandle; UNIQUE_ID_SETTER = setHandle;
REWRITE_ID_SETTER = rewriterHandle;
}
private static MethodHandle getHandlerSetter(Lookup lookup, String fieldName)
throws NoSuchFieldException, IllegalAccessException {
Field uuidField = InitialHandler.class.getDeclaredField(fieldName);
uuidField.setAccessible(true);
return lookup.unreflectSetter(uuidField);
} }
private final FastLoginBungee plugin; private final FastLoginBungee plugin;
@@ -197,12 +181,6 @@ public class ConnectListener implements Listener {
// So we have to do it with reflection // So we have to do it with reflection
UNIQUE_ID_SETTER.invokeExact(connection, offlineUUID); UNIQUE_ID_SETTER.invokeExact(connection, offlineUUID);
// if available set rewrite id to forward the UUID for newer BungeeCord versions since
// https://github.com/SpigotMC/BungeeCord/commit/1be25b6c74ec2be4b15adf8ca53a0497f01e2afe
if (REWRITE_ID_SETTER != null) {
REWRITE_ID_SETTER.invokeExact(connection, offlineUUID);
}
String format = "Overridden UUID from {} to {} (based of {}) on {}"; String format = "Overridden UUID from {} to {} (based of {}) on {}";
plugin.getLog().info(format, oldPremiumId, offlineUUID, username, connection); plugin.getLog().info(format, oldPremiumId, offlineUUID, username, connection);
} catch (Exception ex) { } catch (Exception ex) {
@@ -235,7 +213,7 @@ public class ConnectListener implements Listener {
// delay sending force command, because Paper will process the login event asynchronously // 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 // 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 // player is still in the login phase and reported to be offline.
Runnable loginTask = new ForceLoginTask(plugin.getCore(), player, server, session); Runnable loginTask = new ForceLoginTask(plugin.getCore(), player, server, session);
plugin.getScheduler().runAsync(loginTask); plugin.getScheduler().runAsync(loginTask);
} }
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -28,14 +28,17 @@ package com.github.games647.fastlogin.bungee.listener;
import com.github.games647.fastlogin.bungee.BungeeLoginSession; import com.github.games647.fastlogin.bungee.BungeeLoginSession;
import com.github.games647.fastlogin.bungee.FastLoginBungee; import com.github.games647.fastlogin.bungee.FastLoginBungee;
import com.github.games647.fastlogin.bungee.task.AsyncToggleMessage; 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.hooks.bedrock.FloodgateService;
import com.github.games647.fastlogin.core.message.ChangePremiumMessage; import com.github.games647.fastlogin.core.message.ChangePremiumMessage;
import com.github.games647.fastlogin.core.message.NamespaceKey; import com.github.games647.fastlogin.core.message.NamespaceKey;
import com.github.games647.fastlogin.core.message.SuccessMessage; import com.github.games647.fastlogin.core.message.SuccessMessage;
import com.github.games647.fastlogin.core.shared.FastLoginCore; import com.github.games647.fastlogin.core.shared.FastLoginCore;
import com.github.games647.fastlogin.core.storage.StoredProfile;
import com.google.common.io.ByteArrayDataInput; import com.google.common.io.ByteArrayDataInput;
import com.google.common.io.ByteStreams; import com.google.common.io.ByteStreams;
import java.util.Arrays;
import net.md_5.bungee.api.CommandSender; import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.chat.TextComponent; import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.connection.ProxiedPlayer; import net.md_5.bungee.api.connection.ProxiedPlayer;
@@ -44,8 +47,6 @@ import net.md_5.bungee.api.event.PluginMessageEvent;
import net.md_5.bungee.api.plugin.Listener; import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.event.EventHandler; import net.md_5.bungee.event.EventHandler;
import java.util.Arrays;
public class PluginMessageListener implements Listener { public class PluginMessageListener implements Listener {
private final FastLoginBungee plugin; private final FastLoginBungee plugin;
@@ -131,7 +132,7 @@ public class PluginMessageListener implements Listener {
loginSession.setRegistered(true); loginSession.setRegistered(true);
if (!loginSession.isAlreadySaved()) { if (!loginSession.isAlreadySaved()) {
playerProfile.setOnlinemodePreferred(true); playerProfile.setPremium(true);
plugin.getCore().getStorage().save(playerProfile); plugin.getCore().getStorage().save(playerProfile);
loginSession.setAlreadySaved(true); loginSession.setAlreadySaved(true);
} }
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -29,9 +29,10 @@ import com.github.games647.fastlogin.bungee.BungeeLoginSession;
import com.github.games647.fastlogin.bungee.BungeeLoginSource; import com.github.games647.fastlogin.bungee.BungeeLoginSource;
import com.github.games647.fastlogin.bungee.FastLoginBungee; import com.github.games647.fastlogin.bungee.FastLoginBungee;
import com.github.games647.fastlogin.bungee.event.BungeeFastLoginPreLoginEvent; import com.github.games647.fastlogin.bungee.event.BungeeFastLoginPreLoginEvent;
import com.github.games647.fastlogin.core.StoredProfile;
import com.github.games647.fastlogin.core.shared.JoinManagement; import com.github.games647.fastlogin.core.shared.JoinManagement;
import com.github.games647.fastlogin.core.shared.event.FastLoginPreLoginEvent; import com.github.games647.fastlogin.core.shared.event.FastLoginPreLoginEvent;
import com.github.games647.fastlogin.core.storage.StoredProfile;
import net.md_5.bungee.api.CommandSender; import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.connection.PendingConnection; import net.md_5.bungee.api.connection.PendingConnection;
import net.md_5.bungee.api.connection.ProxiedPlayer; import net.md_5.bungee.api.connection.ProxiedPlayer;
@@ -81,7 +82,7 @@ public class AsyncPremiumCheck extends JoinManagement<ProxiedPlayer, CommandSend
plugin.getSession().put(source.getConnection(), new BungeeLoginSession(username, registered, profile)); plugin.getSession().put(source.getConnection(), new BungeeLoginSession(username, registered, profile));
String ip = source.getAddress().getAddress().getHostAddress(); String ip = source.getAddress().getAddress().getHostAddress();
plugin.getCore().addLoginAttempt(ip, username); plugin.getCore().getPendingLogin().put(ip + username, new Object());
} }
@Override @Override
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -27,9 +27,10 @@ package com.github.games647.fastlogin.bungee.task;
import com.github.games647.fastlogin.bungee.FastLoginBungee; import com.github.games647.fastlogin.bungee.FastLoginBungee;
import com.github.games647.fastlogin.bungee.event.BungeeFastLoginPremiumToggleEvent; import com.github.games647.fastlogin.bungee.event.BungeeFastLoginPremiumToggleEvent;
import com.github.games647.fastlogin.core.StoredProfile;
import com.github.games647.fastlogin.core.shared.FastLoginCore; import com.github.games647.fastlogin.core.shared.FastLoginCore;
import com.github.games647.fastlogin.core.shared.event.FastLoginPremiumToggleEvent.PremiumToggleReason; import com.github.games647.fastlogin.core.shared.event.FastLoginPremiumToggleEvent.PremiumToggleReason;
import com.github.games647.fastlogin.core.storage.StoredProfile;
import net.md_5.bungee.api.CommandSender; import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.ProxyServer; import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.chat.TextComponent; import net.md_5.bungee.api.chat.TextComponent;
@@ -64,34 +65,29 @@ public class AsyncToggleMessage implements Runnable {
private void turnOffPremium() { private void turnOffPremium() {
StoredProfile playerProfile = core.getStorage().loadProfile(targetPlayer); StoredProfile playerProfile = core.getStorage().loadProfile(targetPlayer);
//existing player is already cracked //existing player is already cracked
if (playerProfile.isExistingPlayer() && !playerProfile.isOnlinemodePreferred()) { if (playerProfile.isSaved() && !playerProfile.isPremium()) {
sendMessage("not-premium"); sendMessage("not-premium");
return; return;
} }
playerProfile.setOnlinemodePreferred(false); playerProfile.setPremium(false);
playerProfile.setId(null); playerProfile.setId(null);
core.getStorage().save(playerProfile); core.getStorage().save(playerProfile);
PremiumToggleReason reason = (!isPlayerSender || !sender.getName().equalsIgnoreCase(playerProfile.getName())) PremiumToggleReason reason = (!isPlayerSender || !sender.getName().equalsIgnoreCase(playerProfile.getName()))
? PremiumToggleReason.COMMAND_OTHER : PremiumToggleReason.COMMAND_SELF; ? PremiumToggleReason.COMMAND_OTHER : PremiumToggleReason.COMMAND_SELF;
core.getPlugin().getProxy().getPluginManager().callEvent( core.getPlugin().getProxy().getPluginManager().callEvent(
new BungeeFastLoginPremiumToggleEvent(playerProfile, reason)); new BungeeFastLoginPremiumToggleEvent(playerProfile, reason));
sendMessage("remove-premium");
if (isPlayerSender && core.getConfig().getBoolean("kick-toggle", true)) {
sender.disconnect(TextComponent.fromLegacyText(core.getMessage("remove-premium")));
} else {
sendMessage("remove-premium");
}
} }
private void activatePremium() { private void activatePremium() {
StoredProfile playerProfile = core.getStorage().loadProfile(targetPlayer); StoredProfile playerProfile = core.getStorage().loadProfile(targetPlayer);
if (playerProfile.isOnlinemodePreferred()) { if (playerProfile.isPremium()) {
sendMessage("already-exists"); sendMessage("already-exists");
return; return;
} }
playerProfile.setOnlinemodePreferred(true); playerProfile.setPremium(true);
core.getStorage().save(playerProfile); core.getStorage().save(playerProfile);
PremiumToggleReason reason = (!isPlayerSender || !sender.getName().equalsIgnoreCase(playerProfile.getName())) PremiumToggleReason reason = (!isPlayerSender || !sender.getName().equalsIgnoreCase(playerProfile.getName()))
? PremiumToggleReason.COMMAND_OTHER : PremiumToggleReason.COMMAND_SELF; ? PremiumToggleReason.COMMAND_OTHER : PremiumToggleReason.COMMAND_SELF;
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -29,14 +29,16 @@ import com.github.games647.fastlogin.bungee.BungeeLoginSession;
import com.github.games647.fastlogin.bungee.FastLoginBungee; import com.github.games647.fastlogin.bungee.FastLoginBungee;
import com.github.games647.fastlogin.core.shared.FastLoginCore; import com.github.games647.fastlogin.core.shared.FastLoginCore;
import com.github.games647.fastlogin.core.shared.FloodgateManagement; import com.github.games647.fastlogin.core.shared.FloodgateManagement;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.connection.Server;
import org.geysermc.floodgate.api.player.FloodgatePlayer;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.util.UUID; import java.util.UUID;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.connection.Server;
import org.geysermc.floodgate.api.player.FloodgatePlayer;
public class FloodgateAuthTask public class FloodgateAuthTask
extends FloodgateManagement<ProxiedPlayer, CommandSender, BungeeLoginSession, FastLoginBungee> { extends FloodgateManagement<ProxiedPlayer, CommandSender, BungeeLoginSession, FastLoginBungee> {
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -28,6 +28,7 @@ package com.github.games647.fastlogin.bungee.task;
import com.github.games647.fastlogin.bungee.BungeeLoginSession; import com.github.games647.fastlogin.bungee.BungeeLoginSession;
import com.github.games647.fastlogin.bungee.FastLoginBungee; import com.github.games647.fastlogin.bungee.FastLoginBungee;
import com.github.games647.fastlogin.bungee.event.BungeeFastLoginAutoLoginEvent; import com.github.games647.fastlogin.bungee.event.BungeeFastLoginAutoLoginEvent;
import com.github.games647.fastlogin.core.StoredProfile;
import com.github.games647.fastlogin.core.message.ChannelMessage; import com.github.games647.fastlogin.core.message.ChannelMessage;
import com.github.games647.fastlogin.core.message.LoginActionMessage; import com.github.games647.fastlogin.core.message.LoginActionMessage;
import com.github.games647.fastlogin.core.message.LoginActionMessage.Type; import com.github.games647.fastlogin.core.message.LoginActionMessage.Type;
@@ -35,14 +36,14 @@ import com.github.games647.fastlogin.core.shared.FastLoginCore;
import com.github.games647.fastlogin.core.shared.ForceLoginManagement; import com.github.games647.fastlogin.core.shared.ForceLoginManagement;
import com.github.games647.fastlogin.core.shared.LoginSession; import com.github.games647.fastlogin.core.shared.LoginSession;
import com.github.games647.fastlogin.core.shared.event.FastLoginAutoLoginEvent; import com.github.games647.fastlogin.core.shared.event.FastLoginAutoLoginEvent;
import com.github.games647.fastlogin.core.storage.StoredProfile;
import java.util.UUID;
import net.md_5.bungee.api.CommandSender; import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.ProxyServer; import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.connection.ProxiedPlayer; import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.connection.Server; import net.md_5.bungee.api.connection.Server;
import java.util.UUID;
public class ForceLoginTask public class ForceLoginTask
extends ForceLoginManagement<ProxiedPlayer, CommandSender, BungeeLoginSession, FastLoginBungee> { extends ForceLoginManagement<ProxiedPlayer, CommandSender, BungeeLoginSession, FastLoginBungee> {
+2
View File
@@ -11,6 +11,8 @@ author: games647, https://github.com/games647/FastLogin/graphs/contributors
softDepends: softDepends:
# BungeeCord auth plugins # BungeeCord auth plugins
- BungeeAuth - BungeeAuth
- BungeeCordAuthenticatorBungee
- SodionAuth
# Bedrock Player Bridge # Bedrock Player Bridge
- Geyser-BungeeCord - Geyser-BungeeCord
- floodgate - floodgate
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -25,13 +25,14 @@
*/ */
package com.github.games647.fastlogin.bungee.listener; package com.github.games647.fastlogin.bungee.listener;
import java.lang.reflect.Field;
import java.util.UUID;
import net.md_5.bungee.BungeeCord; import net.md_5.bungee.BungeeCord;
import net.md_5.bungee.conf.Configuration; import net.md_5.bungee.conf.Configuration;
import net.md_5.bungee.connection.InitialHandler; import net.md_5.bungee.connection.InitialHandler;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Field; import org.junit.jupiter.api.Test;
import java.util.UUID;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
+31 -9
View File
@@ -5,7 +5,7 @@
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2015-2024 games647 and contributors Copyright (c) 2015-2022 games647 and contributors
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
@@ -29,6 +29,35 @@
<!DOCTYPE module PUBLIC <!DOCTYPE module PUBLIC
"-//Checkstyle//DTD Checkstyle Configuration 1.3//EN" "-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"
"https://checkstyle.org/dtds/configuration_1_3.dtd"> "https://checkstyle.org/dtds/configuration_1_3.dtd">
<!--
Checkstyle configuration that checks the sun coding conventions from:
- the Java Language Specification at
https://docs.oracle.com/javase/specs/jls/se11/html/index.html
- the Sun Code Conventions at https://www.oracle.com/java/technologies/javase/codeconventions-contents.html
- the Javadoc guidelines at
https://www.oracle.com/technical-resources/articles/java/javadoc-tool.html
- the JDK Api documentation https://docs.oracle.com/en/java/javase/11/
- some best practices
Checkstyle is very configurable. Be sure to read the documentation at
https://checkstyle.org (or in your downloaded distribution).
Most Checks are configurable, be sure to consult the documentation.
To completely disable a check, just comment it out or delete it from the file.
To suppress certain violations please review suppression filters.
Finally, it is worth reading the documentation.
-->
<module name="Checker"> <module name="Checker">
<!-- <!--
If you set the basedir property below, then all reported file If you set the basedir property below, then all reported file
@@ -72,7 +101,6 @@
<module name="LineLength"> <module name="LineLength">
<property name="max" value="120"/> <property name="max" value="120"/>
<property name="fileExtensions" value="java"/> <property name="fileExtensions" value="java"/>
<property name="ignorePattern" value="^ *\* *@see.+$"/>
</module> </module>
<!-- Checks for whitespace --> <!-- Checks for whitespace -->
@@ -191,12 +219,6 @@
<property name="optional" value="true"/> <property name="optional" value="true"/>
</module> </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>
</module> </module>
+56 -85
View File
@@ -4,7 +4,7 @@
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2015-2024 games647 and contributors Copyright (c) 2015-2022 games647 and contributors
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
@@ -25,7 +25,7 @@
SOFTWARE. SOFTWARE.
--> -->
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0" <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"> 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> <modelVersion>4.0.0</modelVersion>
@@ -39,17 +39,12 @@
<artifactId>fastlogin.core</artifactId> <artifactId>fastlogin.core</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>
<properties>
<!-- Still force Java 8 for the remaining project -->
<maven.compiler.release>8</maven.compiler.release>
</properties>
<name>FastLoginCore</name> <name>FastLoginCore</name>
<repositories> <repositories>
<repository> <repository>
<id>luck-repo</id> <id>luck-repo</id>
<url>https://ci.lucko.me/plugin/repository/everything/</url> <url>https://ci.lucko.me/plugin/repository/everything</url>
<snapshots> <snapshots>
<enabled>false</enabled> <enabled>false</enabled>
</snapshots> </snapshots>
@@ -60,7 +55,7 @@
</repository> </repository>
<!-- Floodgate --> <!-- Floodgate -->
<repository> <repository>
<id>opencollab</id> <id>opencollab-snapshot</id>
<url>https://repo.opencollab.dev/maven-snapshots/</url> <url>https://repo.opencollab.dev/maven-snapshots/</url>
<releases> <releases>
<enabled>false</enabled> <enabled>false</enabled>
@@ -68,56 +63,6 @@
</repository> </repository>
</repositories> </repositories>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.4.2</version>
<configuration>
<archive>
<manifestEntries>
<Automatic-Module-Name>com.github.games647.fastlogin.core</Automatic-Module-Name>
</manifestEntries>
</archive>
</configuration>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>jdk21</id>
<activation>
<jdk>[21,)</jdk>
</activation>
<build>
<pluginManagement>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<executions>
<execution>
<id>jdk21</id>
<goals>
<goal>compile</goal>
</goals>
<configuration>
<release>21</release>
<compileSourceRoots>
<compileSourceRoot>${project.basedir}/src/main/java21</compileSourceRoot>
</compileSourceRoots>
<multiReleaseOutput>true</multiReleaseOutput>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</pluginManagement>
</build>
</profile>
</profiles>
<dependencies> <dependencies>
<!-- Libraries that we shade into the project --> <!-- Libraries that we shade into the project -->
@@ -140,21 +85,20 @@
<dependency> <dependency>
<groupId>org.slf4j</groupId> <groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId> <artifactId>slf4j-jdk14</artifactId>
<version>2.0.17</version> <version>2.0.0-alpha7</version>
</dependency> </dependency>
<!-- snakeyaml is present in Bungee, Spigot, so we could use this independent implementation --> <!-- snakeyaml is present in Bungee, Spigot, Cauldron, so we could use this independent implementation -->
<dependency> <dependency>
<groupId>net.md-5</groupId> <groupId>net.md-5</groupId>
<artifactId>bungeecord-config</artifactId> <artifactId>bungeecord-config</artifactId>
<version>1.20-R0.2</version> <version>1.19-R0.1-20220702.004052-16</version>
</dependency> <exclusions>
<exclusion>
<!-- This is optional in BungeeCord-config, so we include it here manually --> <groupId>*</groupId>
<dependency> <artifactId>*</artifactId>
<groupId>org.yaml</groupId> </exclusion>
<artifactId>snakeyaml</artifactId> </exclusions>
<version>2.4</version>
</dependency> </dependency>
<!--Floodgate for Xbox Live Authentication--> <!--Floodgate for Xbox Live Authentication-->
@@ -165,7 +109,11 @@
<scope>provided</scope> <scope>provided</scope>
<exclusions> <exclusions>
<exclusion> <exclusion>
<groupId>*</groupId> <groupId>io.netty</groupId>
<artifactId>*</artifactId>
</exclusion>
<exclusion>
<groupId>org.geysermc.cumulus</groupId>
<artifactId>*</artifactId> <artifactId>*</artifactId>
</exclusion> </exclusion>
</exclusions> </exclusions>
@@ -173,7 +121,7 @@
<!-- Bedrock player bridge --> <!-- Bedrock player bridge -->
<dependency> <dependency>
<groupId>org.geysermc.geyser</groupId> <groupId>org.geysermc</groupId>
<artifactId>core</artifactId> <artifactId>core</artifactId>
<version>${geyser.version}</version> <version>${geyser.version}</version>
<scope>provided</scope> <scope>provided</scope>
@@ -187,31 +135,54 @@
<!-- We need the API, but it was excluded above --> <!-- We need the API, but it was excluded above -->
<dependency> <dependency>
<groupId>org.geysermc.geyser</groupId> <groupId>org.geysermc</groupId>
<artifactId>api</artifactId> <artifactId>geyser-api</artifactId>
<version>${geyser.version}</version> <version>${geyser.version}</version>
<scope>provided</scope> <scope>provided</scope>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency> </dependency>
<!--Common component for contacting the Mojang API--> <!--Common component for contacting the Mojang API-->
<dependency> <dependency>
<groupId>com.github.games647</groupId> <groupId>com.github.games647</groupId>
<artifactId>craftapi</artifactId> <artifactId>craftapi</artifactId>
<version>1.0-SNAPSHOT</version> <version>0.5.3</version>
</dependency> </dependency>
<!-- Database driver included in Spigot --> <!-- APIs we can use because they are available in all platforms (Spigot, Bungee, Velocity) -->
<dependency> <dependency>
<groupId>org.xerial</groupId> <groupId>com.google.guava</groupId>
<artifactId>sqlite-jdbc</artifactId> <artifactId>guava</artifactId>
<version>[3.36,)</version> <!-- Old version for velocity -->
<scope>provided</scope> <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.9.0</version>
</dependency> </dependency>
</dependencies> </dependencies>
</project> </project>
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -23,22 +23,36 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE. * SOFTWARE.
*/ */
package com.github.games647.fastlogin.core.scheduler; package com.github.games647.fastlogin.core;
import org.slf4j.Logger;
import java.time.Duration;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
public abstract class AbstractAsyncScheduler { import org.slf4j.Logger;
protected final Logger logger; /**
protected final Executor processingPool; * This limits the number of threads that are used at maximum. Thread creation can be very heavy for the CPU and
protected final AtomicInteger currentlyRunning = new AtomicInteger(); * 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.
*/
public class AsyncScheduler {
public AbstractAsyncScheduler(Logger logger, Executor processingPool) { private static final int MAX_CAPACITY = 1024;
//todo: single thread for delaying and scheduling tasks
private final Logger logger;
// 30 threads are still too many - the optimal solution is to separate into processing and blocking threads
// where processing threads could only be max number of cores while blocking threads could be minimized using
// non-blocking I/O and a single event executor
private final Executor processingPool;
private final AtomicInteger currentlyRunning = new AtomicInteger();
public AsyncScheduler(Logger logger, Executor processingPool) {
this.logger = logger; this.logger = logger;
this.processingPool = processingPool; this.processingPool = processingPool;
} }
@@ -50,25 +64,7 @@ public abstract class AbstractAsyncScheduler {
}); });
} }
public CompletableFuture<Void> runAsyncDelayed(Runnable task, Duration delay) { private void process(Runnable task) {
return CompletableFuture.runAsync(() -> {
currentlyRunning.incrementAndGet();
try {
Thread.sleep(delay.toMillis());
task.run();
} catch (InterruptedException interruptedException) {
// restore interrupt flag
Thread.currentThread().interrupt();
} finally {
currentlyRunning.getAndDecrement();
}
}, processingPool).exceptionally(error -> {
logger.warn("Error occurred on thread pool", error);
return null;
});
}
protected void process(Runnable task) {
currentlyRunning.incrementAndGet(); currentlyRunning.incrementAndGet();
try { try {
task.run(); task.run();
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -26,26 +26,27 @@
package com.github.games647.fastlogin.core; package com.github.games647.fastlogin.core;
import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.jul.JDK14LoggerAdapter;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.time.Duration;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level; import java.util.logging.Level;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.jul.JDK14LoggerAdapter;
public final class CommonUtil { public final class CommonUtil {
private static final char COLOR_CHAR = '&'; private static final char COLOR_CHAR = '&';
private static final char TRANSLATED_CHAR = '§'; private static final char TRANSLATED_CHAR = '§';
public static <K, V> ConcurrentMap<K, V> buildCache(Duration expireAfterWrite, int maxSize) { public static <K, V> ConcurrentMap<K, V> buildCache(int expireAfterWrite, int maxSize) {
CacheBuilder<Object, Object> builder = CacheBuilder.newBuilder(); CacheBuilder<Object, Object> builder = CacheBuilder.newBuilder();
if (expireAfterWrite != null) { if (expireAfterWrite > 0) {
builder.expireAfterWrite(expireAfterWrite); builder.expireAfterWrite(expireAfterWrite, TimeUnit.MINUTES);
} }
if (maxSize > 0) { if (maxSize > 0) {
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -23,10 +23,9 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE. * SOFTWARE.
*/ */
package com.github.games647.fastlogin.core.storage; package com.github.games647.fastlogin.core;
import com.github.games647.craftapi.model.Profile; import com.github.games647.craftapi.model.Profile;
import com.github.games647.fastlogin.core.shared.FloodgateState;
import java.time.Instant; import java.time.Instant;
import java.util.Objects; import java.util.Objects;
@@ -40,35 +39,27 @@ public class StoredProfile extends Profile {
private final ReentrantLock saveLock = new ReentrantLock(); private final ReentrantLock saveLock = new ReentrantLock();
private boolean premium; private boolean premium;
private FloodgateState floodgate;
private String lastIp; private String lastIp;
private Instant lastLogin; private Instant lastLogin;
public StoredProfile(long rowId, UUID uuid, String playerName, boolean premium, FloodgateState floodgate, public StoredProfile(long rowId, UUID uuid, String playerName, boolean premium, String lastIp, Instant lastLogin) {
String lastIp, Instant lastLogin) {
super(uuid, playerName); super(uuid, playerName);
this.rowId = rowId; this.rowId = rowId;
this.premium = premium; this.premium = premium;
this.floodgate = floodgate;
this.lastIp = lastIp; this.lastIp = lastIp;
this.lastLogin = lastLogin; 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) { public StoredProfile(UUID uuid, String playerName, boolean premium, String lastIp) {
this(-1, uuid, playerName, premium, FloodgateState.FALSE, lastIp, Instant.now()); this(-1, uuid, playerName, premium, lastIp, Instant.now());
} }
public ReentrantLock getSaveLock() { public ReentrantLock getSaveLock() {
return saveLock; return saveLock;
} }
public synchronized boolean isExistingPlayer() { public synchronized boolean isSaved() {
return rowId >= 0; return rowId >= 0;
} }
@@ -97,55 +88,14 @@ public class StoredProfile extends Profile {
this.id = uniqueId; this.id = uniqueId;
} }
/**
* @return whether the online mode should be enabled for this player
* @deprecated {@link #isOnlinemodePreferred()} is recommended, because the name represents more its
* meaning
*/
@Deprecated
public synchronized boolean isPremium() { public synchronized boolean isPremium() {
return premium; return premium;
} }
/**
* Return the online mode preference for this player.
* <p>
* <b>
* Note: {@code false} doesn't represent that the player is offline. It could also mean that the player is premium,
* but didn't activated the premium login mode yet.
* </b>
*
* @return whether the online mode should be enabled for this player
*/
public synchronized boolean isOnlinemodePreferred() {
return premium;
}
/**
* @param premium whether online mode is preferred
* @deprecated {@link #setOnlinemodePreferred(boolean)} is recommended, because of the better method name
*/
@Deprecated
public synchronized void setPremium(boolean premium) { public synchronized void setPremium(boolean premium) {
this.premium = premium; this.premium = premium;
} }
public synchronized void setOnlinemodePreferred(boolean premium) {
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() { public synchronized String getLastIp() {
return lastIp; return lastIp;
} }
@@ -178,7 +128,7 @@ public class StoredProfile extends Profile {
} }
return rowId == that.rowId && premium == that.premium 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 @Override
@@ -189,11 +139,10 @@ public class StoredProfile extends Profile {
@Override @Override
public synchronized String toString() { public synchronized String toString() {
return this.getClass().getSimpleName() + '{' return this.getClass().getSimpleName() + '{'
+ "rowId=" + rowId + "rowId=" + rowId
+ ", premium=" + premium + ", premium=" + premium
+ ", floodgate=" + floodgate + ", lastIp='" + lastIp + '\''
+ ", lastIp='" + lastIp + '\'' + ", lastLogin=" + lastLogin
+ ", lastLogin=" + lastLogin + "} " + super.toString();
+ "} " + super.toString();
} }
} }
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -25,10 +25,10 @@
*/ */
package com.github.games647.fastlogin.core.antibot; package com.github.games647.fastlogin.core.antibot;
import org.slf4j.Logger;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import org.slf4j.Logger;
public class AntiBotService { public class AntiBotService {
private final Logger logger; private final Logger logger;
@@ -57,6 +57,6 @@ public class AntiBotService {
Block, Block,
Continue Continue;
} }
} }
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -23,13 +23,14 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE. * SOFTWARE.
*/ */
package com.github.games647.fastlogin.core; package com.github.games647.fastlogin.core.mojang;
import com.github.games647.craftapi.model.auth.Verification; import com.github.games647.craftapi.model.auth.Verification;
import com.github.games647.craftapi.resolver.MojangResolver; import com.github.games647.craftapi.resolver.MojangResolver;
import com.github.games647.craftapi.resolver.Options;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.InetAddress; import java.net.InetAddress;
import java.util.Optional; import java.util.Optional;
@@ -43,12 +44,38 @@ import java.util.Optional;
*/ */
public class ProxyAgnosticMojangResolver extends MojangResolver { public class ProxyAgnosticMojangResolver extends MojangResolver {
public ProxyAgnosticMojangResolver(Options options) { private static final String HOST = "sessionserver.mojang.com";
super(options);
} /**
* A formatting string containing a URL used to call the {@code hasJoined} method on mojang session servers.
* <p>
* Formatting parameters:
* 1. The username of the player in question
* 2. The serverId of this server
*/
public static final String ENDPOINT = "https://" + HOST + "/session/minecraft/hasJoined?username=%s&serverId=%s";
@Override @Override
public Optional<Verification> hasJoined(String username, String serverHash, InetAddress hostIp) public Optional<Verification> hasJoined(String username, String serverHash, InetAddress hostIp)
throws IOException { throws IOException {
return super.hasJoined(username, serverHash, null); String url = String.format(ENDPOINT, username, serverHash);
HttpURLConnection conn = this.getConnection(url);
int responseCode = conn.getResponseCode();
Verification verification = null;
// Mojang session servers send HTTP 204 (NO CONTENT) when the authentication seems invalid
// If that's not our case, the authentication is valid, and so we can parse the response.
if (responseCode != HttpURLConnection.HTTP_NO_CONTENT) {
verification = this.parseRequest(conn, this::parseVerification);
}
return Optional.ofNullable(verification);
}
// Functional implementation of InputStreamAction, used in hasJoined method in parseRequest call
protected Verification parseVerification(InputStream input) throws IOException {
return this.readJson(input, Verification.class);
} }
} }
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -107,7 +107,7 @@ public class TickingRateLimiter implements RateLimiter {
} }
} }
private static class TimeRecord implements Comparable<TimeRecord> { private static class TimeRecord implements Comparable<Long> {
private final long firstMinuteRecord; private final long firstMinuteRecord;
private final long expireTime; private final long expireTime;
@@ -131,9 +131,9 @@ public class TickingRateLimiter implements RateLimiter {
return firstMinuteRecord + expireTime <= now; return firstMinuteRecord + expireTime <= now;
} }
public int compareTo(long other) { @Override
public int compareTo(Long other) {
if (other < firstMinuteRecord) { if (other < firstMinuteRecord) {
// other is earlier
return -1; return -1;
} }
@@ -143,10 +143,5 @@ public class TickingRateLimiter implements RateLimiter {
return 0; return 0;
} }
@Override
public int compareTo(TimeRecord other) {
return compareTo(other.firstMinuteRecord);
}
} }
} }
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -37,7 +37,7 @@ public interface AuthPlugin<P> {
/** /**
* Login the premium (paid account) player after the player joined successfully the server. * Login the premium (paid account) player after the player joined successfully the server.
* <p> *
* <strong>This operation will be performed async while the player successfully * <strong>This operation will be performed async while the player successfully
* joined the server.</strong> * joined the server.</strong>
* *
@@ -48,17 +48,17 @@ public interface AuthPlugin<P> {
/** /**
* Forces a register in order to protect the paid account. * Forces a register in order to protect the paid account.
* <p> *
* <strong>This operation will be performed async while the player successfully * <strong>This operation will be performed async while the player successfully
* joined the server.</strong> * joined the server.</strong>
* <p> *
* After a successful registration the player should be logged * After a successful registration the player should be logged
* in too. * in too.
* <p> *
* The method will be called only for premium accounts. * The method will be called only for premium accounts.
* So it's recommended to set additionally premium property * So it's recommended to set additionally premium property
* if possible. * if possible.
* <p> *
* Background: If we don't register an account, cracked players * Background: If we don't register an account, cracked players
* could steal the unregistered account from the paid * could steal the unregistered account from the paid
* player account * player account
@@ -71,11 +71,11 @@ public interface AuthPlugin<P> {
/** /**
* Checks whether an account exists for this player name. * Checks whether an account exists for this player name.
* <p> *
* 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 * so we can be sure the premium player doesn't steal the account
* of that player. * of that player.
* <p> *
* This operation will be performed async while the player is * This operation will be performed async while the player is
* connecting. * connecting.
* *
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -25,17 +25,8 @@
*/ */
package com.github.games647.fastlogin.core.hooks; package com.github.games647.fastlogin.core.hooks;
/**
* Password generator for your auth plugin.
* @param <P> platform dependent player class
*/
@FunctionalInterface @FunctionalInterface
public interface PasswordGenerator<P> { public interface PasswordGenerator<P> {
/**
* Generate a password for a non-registered player
* @param player player representation
* @return generated password
*/
String getRandomPassword(P player); String getRandomPassword(P player);
} }
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -27,9 +27,9 @@ package com.github.games647.fastlogin.core.hooks.bedrock;
import com.github.games647.craftapi.model.Profile; import com.github.games647.craftapi.model.Profile;
import com.github.games647.craftapi.resolver.RateLimitException; 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.FastLoginCore;
import com.github.games647.fastlogin.core.shared.LoginSource; import com.github.games647.fastlogin.core.shared.LoginSource;
import com.github.games647.fastlogin.core.storage.StoredProfile;
import java.io.IOException; import java.io.IOException;
import java.util.Optional; import java.util.Optional;
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -25,15 +25,16 @@
*/ */
package com.github.games647.fastlogin.core.hooks.bedrock; 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.FastLoginCore;
import com.github.games647.fastlogin.core.shared.LoginSource; import com.github.games647.fastlogin.core.shared.LoginSource;
import com.github.games647.fastlogin.core.storage.StoredProfile;
import org.geysermc.floodgate.api.FloodgateApi;
import org.geysermc.floodgate.api.player.FloodgatePlayer;
import java.util.Locale; import java.util.Locale;
import java.util.UUID; import java.util.UUID;
import org.geysermc.floodgate.api.FloodgateApi;
import org.geysermc.floodgate.api.player.FloodgatePlayer;
public class FloodgateService extends BedrockService<FloodgatePlayer> { public class FloodgateService extends BedrockService<FloodgatePlayer> {
private final FloodgateApi floodgate; private final FloodgateApi floodgate;
@@ -98,11 +99,23 @@ public class FloodgateService extends BedrockService<FloodgatePlayer> {
* The FloodgateApi does not support querying players by name, so this function * The FloodgateApi does not support querying players by name, so this function
* iterates over every online FloodgatePlayer and checks if the requested * iterates over every online FloodgatePlayer and checks if the requested
* username can be found * 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 * @param prefixedUsername the name of the player with the prefix appended
* @return FloodgatePlayer if found, null otherwise * @return FloodgatePlayer if found, null otherwise
*/ */
public FloodgatePlayer getBedrockPlayer(String prefixedUsername) { 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()) { for (FloodgatePlayer floodgatePlayer : floodgate.getPlayers()) {
if (floodgatePlayer.getCorrectUsername().equals(prefixedUsername)) { if (floodgatePlayer.getCorrectUsername().equals(prefixedUsername)) {
return floodgatePlayer; return floodgatePlayer;
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -27,12 +27,13 @@ package com.github.games647.fastlogin.core.hooks.bedrock;
import com.github.games647.fastlogin.core.shared.FastLoginCore; import com.github.games647.fastlogin.core.shared.FastLoginCore;
import com.github.games647.fastlogin.core.shared.LoginSource; import com.github.games647.fastlogin.core.shared.LoginSource;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.api.network.AuthType;
import org.geysermc.geyser.session.GeyserSession;
import java.util.UUID; 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> { public class GeyserService extends BedrockService<GeyserSession> {
private final GeyserImpl geyser; private final GeyserImpl geyser;
@@ -43,7 +44,7 @@ public class GeyserService extends BedrockService<GeyserSession> {
super(core); super(core);
this.geyser = geyser; this.geyser = geyser;
this.core = core; this.core = core;
this.authType = GeyserImpl.getInstance().getConfig().getRemote().authType(); this.authType = GeyserImpl.getInstance().getConfig().getRemote().getAuthType();
} }
@Override @Override
@@ -64,12 +65,7 @@ public class GeyserService extends BedrockService<GeyserSession> {
@Override @Override
public GeyserSession getBedrockPlayer(String username) { public GeyserSession getBedrockPlayer(String username) {
for (GeyserSession gSess : geyser.getSessionManager().getAllSessions()) { return geyser.connectionByName(username);
if (username.equals(gSess.getClientData().getUsername())) {
return gSess;
}
}
return null;
} }
@Override @Override
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -63,7 +63,7 @@ public class LoginActionMessage implements ChannelMessage {
@Override @Override
public void readFrom(ByteArrayDataInput input) { public void readFrom(ByteArrayDataInput input) {
this.type = Type.values()[input.readByte()]; this.type = Type.values()[input.readInt()];
this.playerName = input.readUTF(); this.playerName = input.readUTF();
@@ -75,7 +75,7 @@ public class LoginActionMessage implements ChannelMessage {
@Override @Override
public void writeTo(ByteArrayDataOutput output) { public void writeTo(ByteArrayDataOutput output) {
output.writeByte(type.ordinal()); output.writeInt(type.ordinal());
//Data is sent through a random player. We have to tell the Bukkit version of this plugin the target //Data is sent through a random player. We have to tell the Bukkit version of this plugin the target
output.writeUTF(playerName); output.writeUTF(playerName);
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -48,12 +48,4 @@ public class NamespaceKey {
public static String getCombined(String namespace, String key) { public static String getCombined(String namespace, String key) {
return new NamespaceKey(namespace, key).combined; return new NamespaceKey(namespace, key).combined;
} }
public String getNamespace() {
return namespace;
}
public String getKey() {
return key;
}
} }
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -1,47 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2024 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.scheduler;
import org.slf4j.Logger;
import java.util.concurrent.Executor;
/**
* 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
* is limited by the size of our database pool. The goal is to separate concerns into processing and blocking only
* threads.
*/
public class AsyncScheduler extends AbstractAsyncScheduler {
public AsyncScheduler(Logger logger, Executor processingPool) {
super(logger, processingPool);
logger.info("Using legacy platform scheduler for using an older Java version. "
+ "Upgrade Java to 21+ for improved performance");
}
}
@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2015-2024 games647 and contributors * Copyright (c) 2015-2022 games647 and contributors
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -26,10 +26,8 @@
package com.github.games647.fastlogin.core.shared; package com.github.games647.fastlogin.core.shared;
import com.github.games647.craftapi.resolver.MojangResolver; import com.github.games647.craftapi.resolver.MojangResolver;
import com.github.games647.craftapi.resolver.Options;
import com.github.games647.craftapi.resolver.http.RotatingProxySelector; import com.github.games647.craftapi.resolver.http.RotatingProxySelector;
import com.github.games647.fastlogin.core.CommonUtil; import com.github.games647.fastlogin.core.CommonUtil;
import com.github.games647.fastlogin.core.ProxyAgnosticMojangResolver;
import com.github.games647.fastlogin.core.antibot.AntiBotService; import com.github.games647.fastlogin.core.antibot.AntiBotService;
import com.github.games647.fastlogin.core.antibot.AntiBotService.Action; import com.github.games647.fastlogin.core.antibot.AntiBotService.Action;
import com.github.games647.fastlogin.core.antibot.RateLimiter; import com.github.games647.fastlogin.core.antibot.RateLimiter;
@@ -37,24 +35,24 @@ import com.github.games647.fastlogin.core.antibot.TickingRateLimiter;
import com.github.games647.fastlogin.core.hooks.AuthPlugin; import com.github.games647.fastlogin.core.hooks.AuthPlugin;
import com.github.games647.fastlogin.core.hooks.DefaultPasswordGenerator; import com.github.games647.fastlogin.core.hooks.DefaultPasswordGenerator;
import com.github.games647.fastlogin.core.hooks.PasswordGenerator; import com.github.games647.fastlogin.core.hooks.PasswordGenerator;
import com.github.games647.fastlogin.core.mojang.ProxyAgnosticMojangResolver;
import com.github.games647.fastlogin.core.storage.MySQLStorage; import com.github.games647.fastlogin.core.storage.MySQLStorage;
import com.github.games647.fastlogin.core.storage.SQLStorage; import com.github.games647.fastlogin.core.storage.SQLStorage;
import com.github.games647.fastlogin.core.storage.SQLiteStorage; import com.github.games647.fastlogin.core.storage.SQLiteStorage;
import com.google.common.base.Ticker; import com.google.common.base.Ticker;
import com.google.common.net.HostAndPort;
import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariConfig;
import net.md_5.bungee.config.Configuration;
import net.md_5.bungee.config.ConfigurationProvider;
import net.md_5.bungee.config.YamlConfiguration;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.Reader; import java.io.Reader;
import java.net.InetAddress;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.Proxy; import java.net.Proxy;
import java.net.Proxy.Type; import java.net.Proxy.Type;
import java.net.UnknownHostException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.time.Duration;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map; import java.util.Map;
@@ -64,6 +62,12 @@ import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
import net.md_5.bungee.config.Configuration;
import net.md_5.bungee.config.ConfigurationProvider;
import net.md_5.bungee.config.YamlConfiguration;
import org.slf4j.Logger;
import static java.util.function.Function.identity; import static java.util.function.Function.identity;
import static java.util.stream.Collectors.toMap; import static java.util.stream.Collectors.toMap;
import static java.util.stream.Collectors.toSet; import static java.util.stream.Collectors.toSet;
@@ -78,10 +82,7 @@ public class FastLoginCore<P extends C, C, T extends PlatformPlugin<C>> {
private static final long MAX_EXPIRE_RATE = 1_000_000; private static final long MAX_EXPIRE_RATE = 1_000_000;
private final Map<String, String> localeMessages = new ConcurrentHashMap<>(); private final Map<String, String> localeMessages = new ConcurrentHashMap<>();
private final ConcurrentMap<String, Object> pendingLogin = CommonUtil.buildCache( private final ConcurrentMap<String, Object> pendingLogin = CommonUtil.buildCache(5, -1);
Duration.ofMinutes(5), -1
);
private final Collection<UUID> pendingConfirms = new HashSet<>(); private final Collection<UUID> pendingConfirms = new HashSet<>();
private final T plugin; private final T plugin;
@@ -120,35 +121,30 @@ public class FastLoginCore<P extends C, C, T extends PlatformPlugin<C>> {
return; return;
} }
Options resolverOptions = new Options();
resolverOptions.setMaxNameRequests(config.getInt("mojang-request-limit", 600));
Set<Proxy> proxies = config.getStringList("proxies")
.stream()
.map(proxy -> proxy.split(":"))
.map(proxy -> new InetSocketAddress(proxy[0], Integer.parseInt(proxy[1])))
.map(sa -> new Proxy(Type.HTTP, sa))
.collect(toSet());
if (!proxies.isEmpty()) {
resolverOptions.setProxySelector(new RotatingProxySelector(proxies));
}
// TODO: Not available currently in craftapi?
// Collection<InetAddress> addresses = new HashSet<>();
// for (String localAddress : config.getStringList("ip-addresses")) {
// try {
// addresses.add(InetAddress.getByName(localAddress.replace('-', '.')));
// } catch (UnknownHostException ex) {
// plugin.getLog().error("IP-Address is unknown to us", ex);
// }
// }
// resolver.setOutgoingAddresses(addresses);
// Initialize the resolver based on the config parameter // Initialize the resolver based on the config parameter
this.resolver = this.config.getBoolean("useProxyAgnosticResolver", false) this.resolver = this.config.getBoolean("useProxyAgnosticResolver", false)
? new ProxyAgnosticMojangResolver(resolverOptions) : new MojangResolver(resolverOptions); ? new ProxyAgnosticMojangResolver() : new MojangResolver();
antiBot = createAntiBotService(config.getSection("anti-bot")); antiBot = createAntiBotService(config.getSection("anti-bot"));
Set<Proxy> proxies = config.getStringList("proxies")
.stream()
.map(HostAndPort::fromString)
.map(proxy -> new InetSocketAddress(proxy.getHost(), proxy.getPort()))
.map(sa -> new Proxy(Type.HTTP, sa))
.collect(toSet());
Collection<InetAddress> addresses = new HashSet<>();
for (String localAddress : config.getStringList("ip-addresses")) {
try {
addresses.add(InetAddress.getByName(localAddress.replace('-', '.')));
} catch (UnknownHostException ex) {
plugin.getLog().error("IP-Address is unknown to us", ex);
}
}
resolver.setMaxNameRequests(config.getInt("mojang-request-limit"));
resolver.setProxySelector(new RotatingProxySelector(proxies));
resolver.setOutgoingAddresses(addresses);
} }
private AntiBotService createAntiBotService(Configuration botSection) { private AntiBotService createAntiBotService(Configuration botSection) {
@@ -228,16 +224,21 @@ public class FastLoginCore<P extends C, C, T extends PlatformPlugin<C>> {
} }
public boolean setupDatabase() { public boolean setupDatabase() {
String type = config.getString("driver"); String driver = config.getString("driver");
if (!checkDriver(driver)) {
return false;
}
HikariConfig databaseConfig = new HikariConfig(); HikariConfig databaseConfig = new HikariConfig();
databaseConfig.setDriverClassName(driver);
String database = config.getString("database"); String database = config.getString("database");
databaseConfig.setConnectionTimeout(config.getInt("timeout", 30) * 1_000L); databaseConfig.setConnectionTimeout(config.getInt("timeout", 30) * 1_000L);
databaseConfig.setMaxLifetime(config.getInt("lifetime", 30) * 1_000L); databaseConfig.setMaxLifetime(config.getInt("lifetime", 30) * 1_000L);
if (type.contains("sqlite")) { if (driver.contains("sqlite")) {
storage = new SQLiteStorage(plugin, database, databaseConfig); storage = new SQLiteStorage(this, database, databaseConfig);
} else { } else {
String host = config.get("host", ""); String host = config.get("host", "");
int port = config.get("port", 3306); int port = config.get("port", 3306);
@@ -255,7 +256,7 @@ public class FastLoginCore<P extends C, C, T extends PlatformPlugin<C>> {
databaseConfig.setUsername(config.get("username", "")); databaseConfig.setUsername(config.get("username", ""));
databaseConfig.setPassword(config.getString("password")); databaseConfig.setPassword(config.getString("password"));
storage = new MySQLStorage(plugin, type, host, port, database, databaseConfig, useSSL); storage = new MySQLStorage(this, driver, host, port, database, databaseConfig, useSSL);
} }
try { try {
@@ -267,6 +268,20 @@ public class FastLoginCore<P extends C, C, T extends PlatformPlugin<C>> {
} }
} }
private boolean checkDriver(String className) {
try {
Class.forName(className);
return true;
} catch (ClassNotFoundException notFoundEx) {
Logger log = plugin.getLog();
log.warn("This driver {} is not supported on this platform", className);
log.warn("Please choose either MySQL (Spigot, BungeeCord), SQLite (Spigot, Sponge) or "
+ "MariaDB (Sponge, Velocity)", notFoundEx);
}
return false;
}
public Configuration getConfig() { public Configuration getConfig() {
return config; return config;
} }
@@ -275,21 +290,12 @@ public class FastLoginCore<P extends C, C, T extends PlatformPlugin<C>> {
return passwordGenerator; return passwordGenerator;
} }
@SuppressWarnings("unused")
public void setPasswordGenerator(PasswordGenerator<P> passwordGenerator) { public void setPasswordGenerator(PasswordGenerator<P> passwordGenerator) {
this.passwordGenerator = passwordGenerator; this.passwordGenerator = passwordGenerator;
} }
public void addLoginAttempt(String ip, String username) { public ConcurrentMap<String, Object> getPendingLogin() {
pendingLogin.put(ip + username, new Object()); return pendingLogin;
}
public boolean hasFailedLogin(String ip, String username) {
if (!config.get("secondAttemptCracked", false)) {
return false;
}
return pendingLogin.remove(ip + username) != null;
} }
public Collection<UUID> getPendingConfirms() { public Collection<UUID> getPendingConfirms() {
@@ -300,7 +306,7 @@ public class FastLoginCore<P extends C, C, T extends PlatformPlugin<C>> {
return authPlugin; return authPlugin;
} }
public AntiBotService getAntiBotService() { public AntiBotService getAntiBot() {
return antiBot; return antiBot;
} }

Some files were not shown because too many files have changed in this diff Show More