mirror of
https://github.com/TuxCoding/FastLogin.git
synced 2025-12-23 23:28:08 +01:00
Compare commits
13 Commits
main
...
integratio
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2fdcea47d0 | ||
|
|
5d89273dad | ||
|
|
54a8c4c08c | ||
|
|
8167afa769 | ||
|
|
6140160a5e | ||
|
|
9a9a75fbb5 | ||
|
|
f355bf7ff2 | ||
|
|
5f13f5ab91 | ||
|
|
3e57b8baa4 | ||
|
|
0f205de1c0 | ||
|
|
ca7be278e1 | ||
|
|
f8c2a09014 | ||
|
|
e0f1cb1729 |
15
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
15
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
@@ -1,6 +1,6 @@
|
||||
name: 🐞 Bug Report
|
||||
description: Something isn't working, broken, not expected behavior
|
||||
labels: [ bug ]
|
||||
labels: [bug]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
@@ -12,6 +12,10 @@ body:
|
||||
description: What behavior is observed?
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: What did you expect?
|
||||
description: What behavior is expected?
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Steps to reproduce
|
||||
@@ -32,7 +36,8 @@ body:
|
||||
attributes:
|
||||
label: Server log
|
||||
description: The error, stacktrace or link the complete log. You can use the links above for long versions.
|
||||
placeholder: https://www.toptal.com/developers/hastebin / https://gist.github.com/
|
||||
render: shell
|
||||
placeholder: https://hastebin.com/ / https://gist.github.com/
|
||||
- type: input
|
||||
attributes:
|
||||
label: Plugin version
|
||||
@@ -55,11 +60,9 @@ body:
|
||||
label: Relevance
|
||||
description: Check list for previous tickets
|
||||
options:
|
||||
- label: |
|
||||
I tried the [latest build](https://ci.codemc.io/job/Games647/job/FastLogin/)
|
||||
(build refers to development builds not necessary a release version; i.e. v1.10 is out of date)
|
||||
- label: I tried the latest build
|
||||
required: true
|
||||
- label: |
|
||||
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
|
||||
|
||||
3
.github/ISSUE_TEMPLATE/config.yml
vendored
3
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -7,6 +7,5 @@
|
||||
contact_links:
|
||||
- name: 📌 Questions
|
||||
url: https://github.com/games647/FastLogin/discussions
|
||||
about:
|
||||
You want to ask something - general questions. Example includes how to set it up or how it is working internally
|
||||
about: You want to ask something
|
||||
|
||||
|
||||
25
.github/ISSUE_TEMPLATE/enhancement_request.md
vendored
Normal file
25
.github/ISSUE_TEMPLATE/enhancement_request.md
vendored
Normal file
@@ -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.)
|
||||
43
.github/ISSUE_TEMPLATE/enhancement_request.yaml
vendored
43
.github/ISSUE_TEMPLATE/enhancement_request.yaml
vendored
@@ -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
|
||||
37
.github/dependabot.yml
vendored
37
.github/dependabot.yml
vendored
@@ -10,23 +10,26 @@ updates:
|
||||
interval: "monthly"
|
||||
|
||||
# Maven project
|
||||
- package-ecosystem: "maven"
|
||||
- package-ecosystem: maven
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "monthly"
|
||||
|
||||
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"
|
||||
|
||||
interval: weekly
|
||||
ignore:
|
||||
# HikariCP dropped Java 8 support with 5.0
|
||||
- dependency-name: "com.zaxxer:HikariCP"
|
||||
update-types: ["version-update:semver-major"]
|
||||
- dependency-name: com.google.code.gson:gson
|
||||
versions:
|
||||
- "> 2.2.4"
|
||||
- dependency-name: com.google.guava
|
||||
- dependency-name: me.clip:placeholderapi
|
||||
versions:
|
||||
- "> 2.10.8, < 2.11"
|
||||
- dependency-name: net.md-5:bungeecord-config
|
||||
versions:
|
||||
- "> 1.12-SNAPSHOT"
|
||||
- dependency-name: de.xxschrandxx.bca:BungeeCordAuthenticator
|
||||
versions:
|
||||
- 0.0.3
|
||||
- dependency-name: com.zaxxer:HikariCP
|
||||
versions:
|
||||
- 4.0.0
|
||||
- 4.0.2
|
||||
- 4.0.3
|
||||
|
||||
5
.github/pull_request_template.md
vendored
5
.github/pull_request_template.md
vendored
@@ -1,11 +1,8 @@
|
||||
[//]: # (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.)
|
||||
|
||||
### Summary of your change
|
||||
|
||||
[//]: # (Example: motivation, enhancement)
|
||||
[//]: # (Example: motiviation, enhancement)
|
||||
|
||||
### Related issue
|
||||
|
||||
[//]: # (Reference it using '#NUMBER'. Ex: Fixes/Related #...)
|
||||
|
||||
25
.github/release.yml
vendored
25
.github/release.yml
vendored
@@ -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:
|
||||
- "*"
|
||||
71
.github/workflows/codeql-analysis.yml
vendored
71
.github/workflows/codeql-analysis.yml
vendored
@@ -4,11 +4,11 @@
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: ["Maven Build"]
|
||||
branches: [main]
|
||||
types:
|
||||
- completed
|
||||
# Scan only for push on the primary branch for now
|
||||
push:
|
||||
branches: [ main ]
|
||||
pull_request:
|
||||
branches: [ main ]
|
||||
|
||||
jobs:
|
||||
# job i
|
||||
@@ -20,45 +20,48 @@ jobs:
|
||||
# Environment
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }}
|
||||
|
||||
if: ${{ github.event.workflow_run.conclusion == 'success' }}
|
||||
|
||||
permissions:
|
||||
# Only allow write for security, then all others default to read only
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
strategy:
|
||||
fail-fast: true
|
||||
fail-fast: false
|
||||
matrix:
|
||||
# Languages to scan
|
||||
language: [ 'java' ]
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# Setup Java
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@v2.3.0
|
||||
with:
|
||||
distribution: 'adopt'
|
||||
# Use Java 16, because it's minimum required version by Geyser
|
||||
java-version: 16
|
||||
cache: 'maven'
|
||||
|
||||
# Setup Java
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version-file: '.java-version'
|
||||
cache: 'maven'
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
|
||||
# Manually start the autobuild process, because autobuild always selects Java 8 as build toolchain, but
|
||||
# we are doing cross-crompiling from a newer Java version
|
||||
- name: Build with Maven
|
||||
# Extracted from autobuild
|
||||
run: mvn package -f "pom.xml" --batch-mode -V -e -Dfindbugs.skip -Dcheckstyle.skip -Dpmd.skip=true -Dspotbugs.skip -Denforcer.skip -Dmaven.javadoc.skip -DskipTests -Dmaven.test.skip.exec -Dlicense.skip=true -Drat.skip=true -Dspotless.check.skip=true -t /home/runner/.m2/toolchains.xml
|
||||
# Cache build process too like in the maven config
|
||||
- uses: actions/cache@v2.1.4
|
||||
with:
|
||||
path: ~/.m2/repository
|
||||
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-maven-
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v3
|
||||
with:
|
||||
category: "/language:${{matrix.language}}"
|
||||
# Auto build attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v1
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
||||
|
||||
40
.github/workflows/maven.yml
vendored
40
.github/workflows/maven.yml
vendored
@@ -2,7 +2,7 @@
|
||||
# including making pull requests review easier
|
||||
|
||||
# Human-readable name in the actions tab
|
||||
name: Maven Build
|
||||
name: Java CI
|
||||
|
||||
# Build on every pull request regardless of the branch
|
||||
# Wiki: https://help.github.com/en/actions/reference/events-that-trigger-workflows
|
||||
@@ -20,48 +20,24 @@ jobs:
|
||||
|
||||
# Environment image - always use the newest OS
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
# With at least one permission given, all default to read
|
||||
contents: read
|
||||
|
||||
# Run steps
|
||||
steps:
|
||||
# Pull changes
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v2.3.4
|
||||
|
||||
# Setup Java
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@v4
|
||||
uses: actions/setup-java@v2.3.0
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version-file: '.java-version'
|
||||
# Use Java 16, because it's minimum required version by Geyser
|
||||
java-version: 16
|
||||
cache: 'maven'
|
||||
|
||||
# Build and test (included in package)
|
||||
- name: Build with Maven and test
|
||||
# Run non-interactive, package (with compile+test),
|
||||
# ignore snapshot updates, because they are likely to have breaking changes, enforce checksums
|
||||
run: mvn test --batch-mode --threads 2.0C --no-snapshot-updates --strict-checksums --file pom.xml
|
||||
|
||||
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
|
||||
# ignore snapshot updates, because they are likely to have breaking changes, enforce checksums to validate
|
||||
# possible errors in dependencies
|
||||
run: mvn test --batch-mode --no-snapshot-updates --strict-checksums --file pom.xml
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
# Latest GitHub Action java release that is installed on the runners
|
||||
21
|
||||
@@ -35,7 +35,7 @@
|
||||
* Automatically register accounts if they are not in the auth plugin database but in the FastLogin database
|
||||
* Update BungeeAuth dependency and use the new API. Please update your plugin if you still use the old one.
|
||||
* Remove deprecated API methods from the last version
|
||||
* Finally, update the IP column on every login
|
||||
* Finally update the IP column on every login
|
||||
* No duplicate session login
|
||||
* Fix timestamp parsing in newer versions of SQLite
|
||||
* Fix Spigot console command invocation sends result to in game players
|
||||
@@ -82,7 +82,7 @@
|
||||
* Fix player entry is not saved if namechangecheck is enabled
|
||||
* Fix skin applies for third-party plugins
|
||||
* Switch to mcapi.ca for uuid lookups
|
||||
* Fix BungeeCord not setting a premium uuid
|
||||
* Fix BungeeCord not setting an premium uuid
|
||||
* Fix setting skin on Cauldron
|
||||
* Fix saving on name change
|
||||
|
||||
@@ -148,7 +148,7 @@
|
||||
### 1.2
|
||||
|
||||
* Fix race condition in BungeeCord
|
||||
* Fix deadlock in xAuth
|
||||
* Fix dead lock in xAuth
|
||||
* Added API methods for plugins to set their own password generator
|
||||
* Added API methods for plugins to set their own auth plugin hook
|
||||
=> Added support for AdvancedLogin
|
||||
@@ -182,7 +182,7 @@
|
||||
* Added a forwardSkin config option
|
||||
* Added premium UUID support
|
||||
* Updated to the newest changes of Spigot
|
||||
* Removes the need of a Bukkit auth plugin if you use a bungeecord one
|
||||
* Removes the need of an Bukkit auth plugin if you use a bungeecord one
|
||||
* Optimize performance and thread-safety
|
||||
* Fixed BungeeCord support
|
||||
* Changed config option auto-login to auto-register to clarify the usage
|
||||
|
||||
3
LICENSE
3
LICENSE
@@ -1,6 +1,6 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015-2024 games647 and contributors
|
||||
Copyright (c) 2015-2021 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
|
||||
@@ -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,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
|
||||
72
README.md
72
README.md
@@ -1,7 +1,5 @@
|
||||
# FastLogin
|
||||
|
||||

|
||||
|
||||
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).
|
||||
|
||||
@@ -10,15 +8,18 @@ So they don't need to enter passwords. This is also called auto login (auto-logi
|
||||
* Detect paid accounts from others
|
||||
* Automatically login paid accounts (premium)
|
||||
* Support various of auth plugins
|
||||
* Cauldron support
|
||||
* Forge/Sponge message support
|
||||
* Premium UUID support
|
||||
* Forward skins
|
||||
* Detect username changed and will update the existing database record
|
||||
* BungeeCord/Velocity support
|
||||
* BungeeCord support
|
||||
* Auto register new premium players
|
||||
* Plugin: ProtocolSupport is supported and can be used as an alternative to ProtocolLib
|
||||
* No client modifications needed
|
||||
* Good performance by using async operations
|
||||
* Locale messages
|
||||
* Support for Bedrock players proxies through FloodGate
|
||||
* Support for Bedrock players proxied through FloodGate
|
||||
|
||||
## Issues
|
||||
|
||||
@@ -32,11 +33,7 @@ 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
|
||||
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/)~~
|
||||
|
||||
Currently broken due changed usernames. Download it from [here](https://github.com/TuxCoding/FastLogin/releases)
|
||||
|
||||
|
||||
You can download them from here: https://ci.codemc.org/job/Games647/job/FastLogin/
|
||||
|
||||
***
|
||||
|
||||
@@ -49,7 +46,6 @@ Currently broken due changed usernames. Download it from [here](https://github.c
|
||||
|
||||
fastlogin.bukkit.command.premium
|
||||
fastlogin.bukkit.command.cracked
|
||||
|
||||
fastlogin.command.premium.other
|
||||
fastlogin.command.cracked.other
|
||||
|
||||
@@ -64,15 +60,12 @@ Possible values: `Premium`, `Cracked`, `Unknown`
|
||||
|
||||
## Requirements
|
||||
|
||||
* Java: 21+ recommended for improved multi-threading code by FastLogin
|
||||
* Spigot: 8+
|
||||
* BungeeCord and Velocity: 17+
|
||||
* Server software in offlinemode:
|
||||
* Spigot (or a fork e.g. Paper) 1.8.8+
|
||||
* Protocol plugin:
|
||||
* [ProtocolLib 5.3+ with development build above 720](https://www.spigotmc.org/resources/protocollib.1997/) or
|
||||
* [ProtocolSupport](https://www.spigotmc.org/resources/protocolsupport.7201/)
|
||||
* Latest BungeeCord (or a fork e.g. Waterfall) or Velocity proxy
|
||||
* Plugin:
|
||||
* [ProtocolLib](https://www.spigotmc.org/resources/protocollib.1997/) or
|
||||
* [ProtocolSupport](https://www.spigotmc.org/resources/protocolsupport.7201/)
|
||||
* [Spigot](https://www.spigotmc.org) 1.8.8+
|
||||
* Java 8+
|
||||
* Run Spigot (or a fork e.g. Paper) and/or BungeeCord (or a fork e.g. Waterfall) in offline mode
|
||||
* An auth plugin.
|
||||
|
||||
### Supported auth plugins
|
||||
@@ -84,6 +77,7 @@ Possible values: `Premium`, `Cracked`, `Unknown`
|
||||
* [CrazyLogin](https://dev.bukkit.org/bukkit-plugins/crazylogin/)
|
||||
* [LoginSecurity](https://dev.bukkit.org/bukkit-plugins/loginsecurity/)
|
||||
* [LogIt](https://github.com/games647/LogIt)
|
||||
* [SodionAuth (2.0+)](https://github.com/MohistMC/SodionAuth)
|
||||
* [UltraAuth](https://dev.bukkit.org/bukkit-plugins/ultraauth-aa/)
|
||||
* [UserLogin](https://www.spigotmc.org/resources/userlogin.80669/)
|
||||
* [xAuth](https://dev.bukkit.org/bukkit-plugins/xauth/)
|
||||
@@ -91,6 +85,8 @@ Possible values: `Premium`, `Cracked`, `Unknown`
|
||||
#### BungeeCord/Waterfall
|
||||
|
||||
* [BungeeAuth](https://www.spigotmc.org/resources/bungeeauth.493/)
|
||||
* [BungeeAuthenticator](https://www.spigotmc.org/resources/bungeecordauthenticator.87669/)
|
||||
* [SodionAuth (2.0+)](https://github.com/MohistMC/SodionAuth)
|
||||
|
||||
## Network requests
|
||||
|
||||
@@ -106,28 +102,20 @@ This plugin performs network requests to:
|
||||
### Spigot/Paper
|
||||
|
||||
1. Download and install ProtocolLib/ProtocolSupport
|
||||
2. Download and install `FastLoginBukkit`
|
||||
3. Set your server in offline mode by setting the value `onlinemode` in your server.properties to `false`
|
||||
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
|
||||
|
||||
### BungeeCord/Waterfall or Velocity
|
||||
### BungeeCord/Waterfall
|
||||
|
||||
Install the plugin on both platforms, that is proxy (BungeeCord or Velocity) and backend server (Spigot).
|
||||
|
||||
1. Activate proxy support in the server configuration
|
||||
* This is often found in `spigot.yml` or `paper.yml`
|
||||
2. Restart the backend 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
|
||||
* 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
|
||||
5. Check your database settings in the config of FastLogin on your proxy
|
||||
* The proxies only ship with a limited set of drivers where Spigot supports more. Therefore, these are supported:
|
||||
* BungeeCord: `mysql` for MySQL/MariaDB
|
||||
* Velocity: `mariadb` for MySQL/MariaDB
|
||||
* 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
|
||||
or install one.
|
||||
6. Set proxy and Spigot in offline mode by setting the value `onlinemode` in your `config.yml` to false
|
||||
7. You should *always* configure the firewall for your Spigot server so that it's only accessible through your proxy
|
||||
* This is also the case without this plugin
|
||||
* https://www.spigotmc.org/wiki/bungeecord-installation/#post-installation
|
||||
1. Activate BungeeCord in the Spigot configuration
|
||||
2. Restart your server
|
||||
3. Now there is `allowed-proxies.txt` file in the FastLogin folder
|
||||
Put your stats id from the BungeeCord config into this file
|
||||
4. Activate ipForward in your BungeeCord config
|
||||
5. Download and Install FastLogin (or FastLoginBungee/FastLoginBukkit in newer versions) on BungeeCord AND Spigot
|
||||
(on the servers where your login plugin is or where player should be able to execute the commands of FastLogin)
|
||||
6. Check your database settings in the config of FastLogin on BungeeCord
|
||||
7. Set proxy and Spigot in offline mode by setting the value onlinemode in your config.yml to false
|
||||
8. You should *always* firewall your Spigot server that it's only accessible through BungeeCord
|
||||
* https://www.spigotmc.org/wiki/bungeecord-installation/#post-installation
|
||||
* BungeeCord doesn't support SQLite per default, so you should change the configuration to MySQL or MariaDB. For that you have to install MariaDB/MySQL on your root server first and put the credentials you made in the FastLogin config files.
|
||||
|
||||
181
bukkit/pom.xml
181
bukkit/pom.xml
@@ -4,7 +4,7 @@
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015-2024 games647 and contributors
|
||||
Copyright (c) 2015-2021 <Your name and contributors>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -25,18 +25,14 @@
|
||||
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">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.release>8</maven.compiler.release>
|
||||
</properties>
|
||||
|
||||
<parent>
|
||||
<groupId>com.github.games647</groupId>
|
||||
<artifactId>fastlogin</artifactId>
|
||||
<version>1.12-SNAPSHOT</version>
|
||||
<version>1.11-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -49,16 +45,13 @@
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.6.0</version>
|
||||
<version>3.2.4</version>
|
||||
<configuration>
|
||||
<createDependencyReducedPom>false</createDependencyReducedPom>
|
||||
<shadedArtifactAttached>false</shadedArtifactAttached>
|
||||
<relocations>
|
||||
<relocation>
|
||||
<pattern>org.yaml.snakeyaml</pattern>
|
||||
<shadedPattern>fastlogin.yaml</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>com.zaxxer.hikari</pattern>
|
||||
<shadedPattern>fastlogin.hikari</shadedPattern>
|
||||
@@ -90,18 +83,9 @@
|
||||
<!-- Rename the service file too to let SLF4J api find our own relocated jdk logger -->
|
||||
<!-- Located in META-INF/services -->
|
||||
<transformers>
|
||||
<transformer
|
||||
implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
|
||||
</transformers>
|
||||
<minimizeJar>true</minimizeJar>
|
||||
<filters>
|
||||
<filter>
|
||||
<artifact>*:*</artifact>
|
||||
<excludes>
|
||||
<exclude>**/module-info.class</exclude>
|
||||
</excludes>
|
||||
</filter>
|
||||
</filters>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
@@ -116,22 +100,31 @@
|
||||
</build>
|
||||
|
||||
<repositories>
|
||||
<!-- PaperSpigot API, PaperLib, datafixupper and bungeecord-chat -->
|
||||
<!-- PaperSpigot API and PaperLib -->
|
||||
<repository>
|
||||
<id>papermc</id>
|
||||
<url>https://repo.papermc.io/repository/maven-public/</url>
|
||||
<url>https://papermc.io/repo/repository/maven-public/</url>
|
||||
</repository>
|
||||
|
||||
<!-- ProtocolLib -->
|
||||
<repository>
|
||||
<id>dmulloy2-repo</id>
|
||||
<url>https://repo.dmulloy2.net/repository/public/</url>
|
||||
<url>https://repo.dmulloy2.net/nexus/repository/public/</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
|
||||
<!-- AuthMe Reloaded, xAuth and LoginSecurity -->
|
||||
<repository>
|
||||
<id>codemc-releases</id>
|
||||
<url>https://repo.codemc.io/repository/maven-public/</url>
|
||||
</repository>
|
||||
|
||||
<!-- GitHub automatic maven builds -->
|
||||
<repository>
|
||||
<id>jitpack.io</id>
|
||||
<url>https://jitpack.io</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
@@ -145,15 +138,6 @@
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
|
||||
<!-- GitHub automatic maven builds -->
|
||||
<repository>
|
||||
<id>jitpack.io</id>
|
||||
<url>https://jitpack.io</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
@@ -168,7 +152,7 @@
|
||||
<dependency>
|
||||
<groupId>io.papermc.paper</groupId>
|
||||
<artifactId>paper-api</artifactId>
|
||||
<version>1.21.6-R0.1-SNAPSHOT</version>
|
||||
<version>1.18-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
<!-- Use our own newer api version -->
|
||||
<exclusions>
|
||||
@@ -176,39 +160,21 @@
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</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>
|
||||
</dependency>
|
||||
|
||||
<!-- PaperLib for checking if server uses PaperSpigot -->
|
||||
<dependency>
|
||||
<groupId>com.mojang</groupId>
|
||||
<artifactId>datafixerupper</artifactId>
|
||||
<version>7.1.15</version>
|
||||
<scope>provided</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>*</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
<groupId>io.papermc</groupId>
|
||||
<artifactId>paperlib</artifactId>
|
||||
<version>1.0.7</version>
|
||||
</dependency>
|
||||
|
||||
<!--Library for listening and sending Minecraft packets-->
|
||||
<dependency>
|
||||
<groupId>com.comphenix.protocol</groupId>
|
||||
<artifactId>ProtocolLib</artifactId>
|
||||
<version>5.3.0</version>
|
||||
<version>4.7.0</version>
|
||||
<scope>provided</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
@@ -223,7 +189,7 @@
|
||||
<groupId>com.github.ProtocolSupport</groupId>
|
||||
<artifactId>ProtocolSupport</artifactId>
|
||||
<!--4.29.dev after commit about API improvements-->
|
||||
<version>master-66b494a8dd-1</version>
|
||||
<version>3a80c661fe</version>
|
||||
<scope>provided</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
@@ -237,11 +203,15 @@
|
||||
<dependency>
|
||||
<groupId>org.geysermc.floodgate</groupId>
|
||||
<artifactId>api</artifactId>
|
||||
<version>${floodgate.version}</version>
|
||||
<version>2.0-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>*</groupId>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.geysermc.cumulus</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
@@ -249,7 +219,7 @@
|
||||
|
||||
<!-- Bedrock player bridge -->
|
||||
<dependency>
|
||||
<groupId>org.geysermc.geyser</groupId>
|
||||
<groupId>org.geysermc</groupId>
|
||||
<artifactId>core</artifactId>
|
||||
<version>${geyser.version}</version>
|
||||
<scope>provided</scope>
|
||||
@@ -263,23 +233,17 @@
|
||||
|
||||
<!-- We need the API, but it was excluded above -->
|
||||
<dependency>
|
||||
<groupId>org.geysermc.geyser</groupId>
|
||||
<artifactId>api</artifactId>
|
||||
<groupId>org.geysermc</groupId>
|
||||
<artifactId>geyser-api</artifactId>
|
||||
<version>${geyser.version}</version>
|
||||
<scope>provided</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>*</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!--Provide premium placeholders-->
|
||||
<dependency>
|
||||
<groupId>me.clip</groupId>
|
||||
<artifactId>placeholderapi</artifactId>
|
||||
<version>2.11.6</version>
|
||||
<version>2.11.0</version>
|
||||
<scope>provided</scope>
|
||||
<optional>true</optional>
|
||||
<exclusions>
|
||||
@@ -294,7 +258,7 @@
|
||||
<dependency>
|
||||
<groupId>fr.xephi</groupId>
|
||||
<artifactId>authme</artifactId>
|
||||
<version>5.6.0</version>
|
||||
<version>5.4.0</version>
|
||||
<scope>provided</scope>
|
||||
<optional>true</optional>
|
||||
<exclusions>
|
||||
@@ -308,7 +272,7 @@
|
||||
<dependency>
|
||||
<groupId>com.lenis0012.bukkit</groupId>
|
||||
<artifactId>loginsecurity</artifactId>
|
||||
<version>3.3.0</version>
|
||||
<version>3.0.2</version>
|
||||
<scope>provided</scope>
|
||||
<optional>true</optional>
|
||||
<exclusions>
|
||||
@@ -348,22 +312,6 @@
|
||||
</exclusions>
|
||||
</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 :(-->
|
||||
<dependency>
|
||||
<groupId>de.st_ddt.crazy</groupId>
|
||||
@@ -393,9 +341,58 @@
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk18on</artifactId>
|
||||
<version>1.80</version>
|
||||
<groupId>com.github.Mohist-Community.SodionAuth</groupId>
|
||||
<artifactId>SodionAuth-Bukkit</artifactId>
|
||||
<version>2bdfdc854b</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.github.Mohist-Community.SodionAuth</groupId>
|
||||
<artifactId>SodionAuth-Libs</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
<optional>true</optional>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>testcontainers</artifactId>
|
||||
<version>1.16.3</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>mockserver</artifactId>
|
||||
<version>1.16.3</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.mock-server</groupId>
|
||||
<artifactId>mockserver-client-java</artifactId>
|
||||
<version>5.12.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.steveice10</groupId>
|
||||
<artifactId>mcprotocollib</artifactId>
|
||||
<version>1.18-2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.mojang</groupId>
|
||||
<artifactId>authlib</artifactId>
|
||||
<version>3.2.38</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<version>1.2.10</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2024 games647 and contributors
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -26,16 +26,14 @@
|
||||
package com.github.games647.fastlogin.bukkit;
|
||||
|
||||
import com.github.games647.craftapi.model.skin.SkinProperty;
|
||||
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.storage.StoredProfile;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Represents a client connecting to the server.
|
||||
* <p>
|
||||
*
|
||||
* This session is invalid if the player disconnects or the login was successful
|
||||
*/
|
||||
public class BukkitLoginSession extends LoginSession {
|
||||
@@ -44,37 +42,35 @@ public class BukkitLoginSession extends LoginSession {
|
||||
|
||||
private final byte[] verifyToken;
|
||||
|
||||
private final ClientPublicKey clientPublicKey;
|
||||
|
||||
private boolean verified;
|
||||
|
||||
private SkinProperty skinProperty;
|
||||
|
||||
public BukkitLoginSession(String username, byte[] verifyToken, ClientPublicKey publicKey, boolean registered,
|
||||
StoredProfile profile) {
|
||||
public BukkitLoginSession(String username, byte[] verifyToken, boolean registered
|
||||
, StoredProfile profile) {
|
||||
super(username, registered, profile);
|
||||
|
||||
this.clientPublicKey = publicKey;
|
||||
this.verifyToken = verifyToken.clone();
|
||||
}
|
||||
|
||||
//available for BungeeCord
|
||||
public BukkitLoginSession(String username, boolean registered) {
|
||||
this(username, EMPTY_ARRAY, null, registered, null);
|
||||
this(username, EMPTY_ARRAY, registered, null);
|
||||
}
|
||||
|
||||
//cracked player
|
||||
public BukkitLoginSession(String username, StoredProfile profile) {
|
||||
this(username, EMPTY_ARRAY, null, false, profile);
|
||||
this(username, EMPTY_ARRAY, false, profile);
|
||||
}
|
||||
|
||||
//ProtocolSupport
|
||||
public BukkitLoginSession(String username, boolean registered, StoredProfile profile) {
|
||||
this(username, EMPTY_ARRAY, null, registered, profile);
|
||||
this(username, EMPTY_ARRAY, registered, profile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the verify-token the server sent to the client.
|
||||
* <p>
|
||||
*
|
||||
* Empty if it's a BungeeCord connection
|
||||
*
|
||||
* @return verify token from the server
|
||||
@@ -83,11 +79,6 @@ public class BukkitLoginSession extends LoginSession {
|
||||
return verifyToken.clone();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ClientPublicKey getClientPublicKey() {
|
||||
return clientPublicKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return premium skin if available
|
||||
*/
|
||||
@@ -108,7 +99,7 @@ public class BukkitLoginSession extends LoginSession {
|
||||
*
|
||||
* @param verified whether the player has valid session
|
||||
*/
|
||||
public synchronized void setVerifiedPremium(boolean verified) {
|
||||
public synchronized void setVerified(boolean verified) {
|
||||
this.verified = verified;
|
||||
}
|
||||
|
||||
@@ -117,7 +108,7 @@ public class BukkitLoginSession extends LoginSession {
|
||||
*
|
||||
* @return whether the player has a valid session
|
||||
*/
|
||||
public synchronized boolean isVerifiedPremium() {
|
||||
public synchronized boolean isVerified() {
|
||||
return verified;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2024 games647 and contributors
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -25,21 +25,23 @@
|
||||
*/
|
||||
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 java.util.concurrent.ThreadFactory;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
public class BukkitScheduler extends AsyncScheduler {
|
||||
|
||||
private final Executor syncExecutor;
|
||||
|
||||
public BukkitScheduler(Plugin plugin, Logger logger) {
|
||||
super(logger, command -> Bukkit.getScheduler().runTaskAsynchronously(plugin, command));
|
||||
public BukkitScheduler(Plugin plugin, Logger logger, ThreadFactory threadFactory) {
|
||||
super(logger, threadFactory);
|
||||
|
||||
syncExecutor = task -> Bukkit.getScheduler().runTask(plugin, task);
|
||||
syncExecutor = r -> Bukkit.getScheduler().runTask(plugin, r);
|
||||
}
|
||||
|
||||
public Executor getSyncExecutor() {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2024 games647 and contributors
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -31,24 +31,21 @@ import com.github.games647.fastlogin.core.message.LoginActionMessage;
|
||||
import com.github.games647.fastlogin.core.message.NamespaceKey;
|
||||
import com.google.common.io.ByteArrayDataOutput;
|
||||
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.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
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.SuccessMessage.SUCCESS_CHANNEL;
|
||||
import static java.util.stream.Collectors.toSet;
|
||||
@@ -64,7 +61,7 @@ public class BungeeManager {
|
||||
private final FastLoginBukkit plugin;
|
||||
private boolean enabled;
|
||||
|
||||
private final Collection<UUID> firedJoinEvents = new HashSet<>();
|
||||
private final Set<UUID> firedJoinEvents = new HashSet<>();
|
||||
|
||||
public BungeeManager(FastLoginBukkit plugin) {
|
||||
this.plugin = plugin;
|
||||
@@ -90,77 +87,33 @@ public class BungeeManager {
|
||||
}
|
||||
|
||||
public void initialize() {
|
||||
enabled = detectProxy();
|
||||
try {
|
||||
enabled = detectProxy();
|
||||
} catch (Exception ex) {
|
||||
plugin.getLog().warn("Cannot check proxy support. Fallback to non-proxy mode", ex);
|
||||
}
|
||||
|
||||
if (enabled) {
|
||||
proxyIds = loadBungeeCordIds();
|
||||
if (proxyIds.isEmpty()) {
|
||||
plugin.getLog().info("No valid IDs found. Minecraft proxy support cannot work in the current state");
|
||||
}
|
||||
|
||||
registerPluginChannels();
|
||||
plugin.getLog().info("Found enabled proxy configuration");
|
||||
plugin.getLog().info("Remember to follow the proxy guide to complete your setup");
|
||||
} else {
|
||||
plugin.getLog().warn("Disabling Minecraft proxy configuration. Assuming direct connections from now on.");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isProxySupported(String className, String fieldName)
|
||||
throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
|
||||
return Class.forName(className).getDeclaredField(fieldName).getBoolean(null);
|
||||
}
|
||||
|
||||
private boolean isVelocityEnabled()
|
||||
throws NoSuchFieldException, IllegalArgumentException, IllegalAccessException, ClassNotFoundException,
|
||||
NoSuchMethodException, InvocationTargetException {
|
||||
private boolean isProxySupported(String className, String fieldName) {
|
||||
try {
|
||||
Class<?> globalConfig = Class.forName("io.papermc.paper.configuration.GlobalConfiguration");
|
||||
Object global = globalConfig.getDeclaredMethod("get").invoke(null);
|
||||
Object proxiesConfiguration = global.getClass().getDeclaredField("proxies").get(global);
|
||||
|
||||
Field velocitySectionField = proxiesConfiguration.getClass().getDeclaredField("velocity");
|
||||
Object velocityConfig = velocitySectionField.get(proxiesConfiguration);
|
||||
|
||||
return velocityConfig.getClass().getDeclaredField("enabled").getBoolean(velocityConfig);
|
||||
} catch (ClassNotFoundException classNotFoundException) {
|
||||
// try again using the older Paper configuration, because the old class file still exists in newer versions
|
||||
if (isProxySupported("com.destroystokyo.paper.PaperConfig", "velocitySupport")) {
|
||||
return true;
|
||||
}
|
||||
return Class.forName(className).getDeclaredField(fieldName).getBoolean(null);
|
||||
} catch (ClassNotFoundException notFoundEx) {
|
||||
//ignore server has no proxy support
|
||||
} catch (NoSuchFieldException | IllegalAccessException noSuchFieldException) {
|
||||
plugin.getLog().warn("Cannot access proxy field", noSuchFieldException);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean detectProxy() {
|
||||
try {
|
||||
ServerConfiguration.class.getDeclaredMethod("isProxyEnabled");
|
||||
return Bukkit.getServerConfig().isProxyEnabled();
|
||||
} catch (NoClassDefFoundError | NoSuchMethodException noSuchClassMethodEx) {
|
||||
// Ignore continue below
|
||||
}
|
||||
|
||||
try {
|
||||
if (isProxySupported("org.spigotmc.SpigotConfig", "bungee")) {
|
||||
return true;
|
||||
}
|
||||
} catch (ClassNotFoundException classNotFoundException) {
|
||||
// leave stacktrace for class not found out
|
||||
plugin.getLog().warn("Cannot check for BungeeCord support: {}", classNotFoundException.getMessage());
|
||||
} catch (NoSuchFieldException | IllegalAccessException ex) {
|
||||
plugin.getLog().warn("Cannot check for BungeeCord support", ex);
|
||||
}
|
||||
|
||||
try {
|
||||
return isVelocityEnabled();
|
||||
} catch (ClassNotFoundException classNotFoundException) {
|
||||
plugin.getLog().warn("Cannot check for Velocity support in Paper: {}", classNotFoundException.getMessage());
|
||||
} catch (NoSuchFieldException | IllegalAccessException | NoSuchMethodException | InvocationTargetException ex) {
|
||||
plugin.getLog().warn("Cannot check for Velocity support in Paper", ex);
|
||||
}
|
||||
|
||||
return false;
|
||||
return isProxySupported("org.spigotmc.SpigotConfig", "bungee")
|
||||
|| isProxySupported("com.destroystokyo.paper.PaperConfig", "velocitySupport");
|
||||
}
|
||||
|
||||
private void registerPluginChannels() {
|
||||
@@ -194,7 +147,9 @@ public class BungeeManager {
|
||||
|
||||
Files.deleteIfExists(legacyFile);
|
||||
try (Stream<String> lines = Files.lines(proxiesFile)) {
|
||||
return lines.map(String::trim).map(UUID::fromString).collect(toSet());
|
||||
return lines.map(String::trim)
|
||||
.map(UUID::fromString)
|
||||
.collect(toSet());
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
plugin.getLog().error("Failed to read proxies", ex);
|
||||
@@ -221,7 +176,7 @@ public class BungeeManager {
|
||||
/**
|
||||
* Check if the event fired including with the task delay. This necessary to restore the order of processing the
|
||||
* BungeeCord messages after the PlayerJoinEvent fires including the delay.
|
||||
* <p>
|
||||
*
|
||||
* If the join event fired, the delay exceeded, but it ran earlier and couldn't find the recently started login
|
||||
* session. If not fired, we can start a new force login task. This will still match the requirement that we wait
|
||||
* a certain time after the player join event fired.
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2024 games647 and contributors
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -25,24 +25,33 @@
|
||||
*/
|
||||
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.PremiumCommand;
|
||||
import com.github.games647.fastlogin.bukkit.command.DeleteCommand;
|
||||
import com.github.games647.fastlogin.bukkit.listener.ConnectionListener;
|
||||
import com.github.games647.fastlogin.bukkit.listener.PaperCacheListener;
|
||||
import com.github.games647.fastlogin.bukkit.listener.protocollib.ManualNameChange;
|
||||
import com.github.games647.fastlogin.bukkit.listener.protocollib.ProtocolLibListener;
|
||||
import com.github.games647.fastlogin.bukkit.listener.protocollib.SkinApplyListener;
|
||||
import com.github.games647.fastlogin.bukkit.listener.protocolsupport.ProtocolSupportListener;
|
||||
import com.github.games647.fastlogin.bukkit.task.DelayedAuthHook;
|
||||
import com.github.games647.fastlogin.core.CommonUtil;
|
||||
import com.github.games647.fastlogin.core.PremiumStatus;
|
||||
import com.github.games647.fastlogin.core.antibot.AntiBotService;
|
||||
import com.github.games647.fastlogin.core.hooks.bedrock.BedrockService;
|
||||
import com.github.games647.fastlogin.core.hooks.bedrock.FloodgateService;
|
||||
import com.github.games647.fastlogin.core.hooks.bedrock.GeyserService;
|
||||
import com.github.games647.fastlogin.core.shared.FastLoginCore;
|
||||
import com.github.games647.fastlogin.core.shared.PlatformPlugin;
|
||||
|
||||
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.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
@@ -50,28 +59,15 @@ import org.bukkit.plugin.PluginManager;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.geysermc.floodgate.api.FloodgateApi;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
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.
|
||||
*/
|
||||
public class FastLoginBukkit extends JavaPlugin implements PlatformPlugin<CommandSender> {
|
||||
|
||||
//1 minutes should be enough as a timeout for bad internet connection (Server, Client and Mojang)
|
||||
private final ConcurrentMap<String, BukkitLoginSession> loginSession = CommonUtil.buildCache(
|
||||
Duration.ofMinutes(1), -1
|
||||
);
|
||||
|
||||
private final ConcurrentMap<String, BukkitLoginSession> loginSession = CommonUtil.buildCache(1, -1);
|
||||
private final Map<UUID, PremiumStatus> premiumPlayers = new ConcurrentHashMap<>();
|
||||
private final Logger logger;
|
||||
|
||||
@@ -86,7 +82,7 @@ public class FastLoginBukkit extends JavaPlugin implements PlatformPlugin<Comman
|
||||
|
||||
public FastLoginBukkit() {
|
||||
this.logger = CommonUtil.initializeLoggerService(getLogger());
|
||||
this.scheduler = new BukkitScheduler(this, logger);
|
||||
this.scheduler = new BukkitScheduler(this, logger, getThreadFactory());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -117,14 +113,28 @@ public class FastLoginBukkit extends JavaPlugin implements PlatformPlugin<Comman
|
||||
return;
|
||||
}
|
||||
|
||||
AntiBotService antiBotService = core.getAntiBotService();
|
||||
if (pluginManager.isPluginEnabled("ProtocolSupport")) {
|
||||
pluginManager.registerEvents(new ProtocolSupportListener(this, antiBotService), this);
|
||||
pluginManager.registerEvents(new ProtocolSupportListener(this, core.getRateLimiter()), this);
|
||||
} else if (pluginManager.isPluginEnabled("ProtocolLib")) {
|
||||
ProtocolLibListener.register(this, antiBotService, core.getConfig().getBoolean("verifyClientKeys"));
|
||||
ProtocolLibListener.register(this, core.getRateLimiter());
|
||||
|
||||
if (isPluginInstalled("floodgate")) {
|
||||
if (getConfig().getBoolean("floodgatePrefixWorkaround")){
|
||||
ManualNameChange.register(this, floodgateService);
|
||||
logger.info("Floodgate prefix injection workaround has been enabled.");
|
||||
logger.info("If you have problems joining the server, try disabling it in the configuration.");
|
||||
} else {
|
||||
logger.warn("We have detected that you are runnging FastLogin alongside Floodgate and ProtocolLib.");
|
||||
logger.warn("Currently there is an issue with FastLogin that prevents Floodgate name prefixes from showing up "
|
||||
+ "when it is together used with ProtocolLib.");
|
||||
logger.warn("If you would like to use Floodgate name prefixes, you can enable an experimental workaround by changing "
|
||||
+ "the value 'floodgatePrefixWorkaround' to true in config.yml.");
|
||||
logger.warn("For more information visit https://github.com/games647/FastLogin/issues/493");
|
||||
}
|
||||
}
|
||||
|
||||
//if server is using paper - we need to set the skin at pre login anyway, so no need for this listener
|
||||
if (!isPaper() && getConfig().getBoolean("forwardSkin")) {
|
||||
if (!PaperLib.isPaper() && getConfig().getBoolean("forwardSkin")) {
|
||||
pluginManager.registerEvents(new SkinApplyListener(this), this);
|
||||
}
|
||||
} else {
|
||||
@@ -140,23 +150,20 @@ public class FastLoginBukkit extends JavaPlugin implements PlatformPlugin<Comman
|
||||
pluginManager.registerEvents(new ConnectionListener(this), this);
|
||||
|
||||
//if server is using paper - we need to add one more listener to correct the user cache usage
|
||||
if (isPaper()) {
|
||||
if (PaperLib.isPaper()) {
|
||||
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")) {
|
||||
premiumPlaceholder = new PremiumPlaceholder(this);
|
||||
premiumPlaceholder.register();
|
||||
}
|
||||
}
|
||||
|
||||
private void 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)));
|
||||
Optional.ofNullable(getCommand("fldelete")).ifPresent(c -> c.setExecutor(new DeleteCommand(this)));
|
||||
dependencyWarnings();
|
||||
}
|
||||
|
||||
private boolean initializeFloodgate() {
|
||||
@@ -195,10 +202,6 @@ public class FastLoginBukkit extends JavaPlugin implements PlatformPlugin<Comman
|
||||
logger.error("Failed to unregister placeholder", exception);
|
||||
}
|
||||
}
|
||||
|
||||
if (getServer().getPluginManager().isPluginEnabled("ProtocolLib")) {
|
||||
ProtocolLibrary.getProtocolManager().getAsynchronousManager().unregisterAsyncHandlers(this);
|
||||
}
|
||||
}
|
||||
|
||||
public FastLoginCore<Player, CommandSender, FastLoginBukkit> getCore() {
|
||||
@@ -240,28 +243,12 @@ public class FastLoginBukkit extends JavaPlugin implements PlatformPlugin<Comman
|
||||
|
||||
/**
|
||||
* Fetches the premium status of an online player.
|
||||
* {@snippet :
|
||||
* // Bukkit's players object after successful authentication i.e. PlayerJoinEvent
|
||||
* // except for proxies like BungeeCord and Velocity where the details are sent delayed (1-2 seconds)
|
||||
* Player player;
|
||||
* PremiumStatus status = JavaPlugin.getPlugin(FastLoginBukkit.class).getStatus(player.getUniqueId());
|
||||
* switch (status) {
|
||||
* case CRACKED:
|
||||
* // player is offline
|
||||
* break;
|
||||
* case PREMIUM:
|
||||
* // account is premium and player passed the verification
|
||||
* break;
|
||||
* case UNKNOWN:
|
||||
* // no record about this player
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* @param onlinePlayer player that is currently online player (play state)
|
||||
* @return the online status or unknown if an error happened, the player isn't online or BungeeCord doesn't send
|
||||
* 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);
|
||||
}
|
||||
|
||||
@@ -303,17 +290,16 @@ public class FastLoginBukkit extends JavaPlugin implements PlatformPlugin<Comman
|
||||
receiver.sendMessage(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a plugin is installed on the server
|
||||
*
|
||||
* @param name the name of the plugin
|
||||
* @return true if the plugin is installed
|
||||
*/
|
||||
@Override
|
||||
public boolean isPluginInstalled(String name) {
|
||||
// the plugin may be enabled after FastLogin, so isPluginEnabled() won't work here
|
||||
return Bukkit.getServer().getPluginManager().getPlugin(name) != null;
|
||||
}
|
||||
/**
|
||||
* Checks if a plugin is installed on the server
|
||||
* @param name the name of the plugin
|
||||
* @return true if the plugin is installed
|
||||
*/
|
||||
@Override
|
||||
public boolean isPluginInstalled(String name) {
|
||||
// the plugin may be enabled after FastLogin, so isPluginEnabled() won't work here
|
||||
return Bukkit.getServer().getPluginManager().getPlugin(name) != null;
|
||||
}
|
||||
|
||||
public FloodgateService getFloodgateService() {
|
||||
return floodgateService;
|
||||
@@ -331,16 +317,17 @@ public class FastLoginBukkit extends JavaPlugin implements PlatformPlugin<Comman
|
||||
return geyserService;
|
||||
}
|
||||
|
||||
private boolean isPaper() {
|
||||
return isClassAvailable("com.destroystokyo.paper.PaperConfig").isPresent()
|
||||
|| isClassAvailable("io.papermc.paper.configuration.Configuration").isPresent();
|
||||
}
|
||||
|
||||
private Optional<Class<?>> isClassAvailable(String clazzName) {
|
||||
try {
|
||||
return Optional.of(Class.forName(clazzName));
|
||||
} catch (ClassNotFoundException e) {
|
||||
return Optional.empty();
|
||||
}
|
||||
/**
|
||||
* Send warning messages to log if incompatible plugins are used
|
||||
*/
|
||||
private void dependencyWarnings() {
|
||||
if (isPluginInstalled("floodgate-bukkit")) {
|
||||
logger.warn("We have detected that you are running Floodgate 1.0 which is not supported by the Bukkit "
|
||||
+ "version of FastLogin.");
|
||||
logger.warn("If you would like to use FastLogin with Floodgate, you can download development builds of "
|
||||
+ "Floodgate 2.0 from https://ci.opencollab.dev/job/GeyserMC/job/Floodgate/job/dev%252F2.0/");
|
||||
logger.warn("Don't forget to update Geyser to a supported version as well from "
|
||||
+ "https://ci.opencollab.dev/job/GeyserMC/job/Geyser/job/floodgate-2.0/");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
*
|
||||
* Copyright (c) 2015-2024 games647 and contributors
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -26,12 +26,10 @@
|
||||
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.List;
|
||||
|
||||
public class PremiumPlaceholder extends PlaceholderExpansion {
|
||||
|
||||
private static final String PLACEHOLDER_VARIABLE = "status";
|
||||
@@ -42,16 +40,6 @@ public class PremiumPlaceholder extends PlaceholderExpansion {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean persist() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull List<String> getPlaceholders() {
|
||||
return Collections.singletonList(PLACEHOLDER_VARIABLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String onRequest(OfflinePlayer player, @NotNull String identifier) {
|
||||
// player is null if offline
|
||||
@@ -74,13 +62,11 @@ public class PremiumPlaceholder extends PlaceholderExpansion {
|
||||
|
||||
@Override
|
||||
public @NotNull String getAuthor() {
|
||||
//noinspection deprecation
|
||||
return String.join(", ", plugin.getDescription().getAuthors());
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getVersion() {
|
||||
//noinspection deprecation
|
||||
return plugin.getDescription().getVersion();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2024 games647 and contributors
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -27,10 +27,10 @@ package com.github.games647.fastlogin.bukkit.command;
|
||||
|
||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||
import com.github.games647.fastlogin.bukkit.event.BukkitFastLoginPremiumToggleEvent;
|
||||
import com.github.games647.fastlogin.core.storage.StoredProfile;
|
||||
import com.github.games647.fastlogin.core.StoredProfile;
|
||||
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import static com.github.games647.fastlogin.core.shared.event.FastLoginPremiumToggleEvent.PremiumToggleReason;
|
||||
@@ -42,10 +42,9 @@ public class CrackedCommand extends ToggleCommand {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label,
|
||||
String[] args) {
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
|
||||
if (args.length == 0) {
|
||||
onCrackedSelf(sender);
|
||||
onCrackedSelf(sender, command, args);
|
||||
} else {
|
||||
onCrackedOther(sender, command, args);
|
||||
}
|
||||
@@ -53,39 +52,34 @@ public class CrackedCommand extends ToggleCommand {
|
||||
return true;
|
||||
}
|
||||
|
||||
private void onCrackedSelf(CommandSender sender) {
|
||||
private void onCrackedSelf(CommandSender sender, Command cmd, String[] args) {
|
||||
if (isConsole(sender)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Player player = (Player) sender;
|
||||
if (forwardCrackedCommand(sender, sender.getName())) {
|
||||
return;
|
||||
}
|
||||
|
||||
// todo: load async if
|
||||
StoredProfile profile = plugin.getCore().getStorage().loadProfile(sender.getName());
|
||||
if (profile.isOnlinemodePreferred()) {
|
||||
plugin.getCore().sendLocaleMessage("remove-premium", sender);
|
||||
|
||||
profile.setOnlinemodePreferred(false);
|
||||
profile.setId(null);
|
||||
plugin.getScheduler().runAsync(() -> {
|
||||
plugin.getCore().getStorage().save(profile);
|
||||
plugin.getServer().getPluginManager().callEvent(
|
||||
new BukkitFastLoginPremiumToggleEvent(sender, 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);
|
||||
}
|
||||
});
|
||||
});
|
||||
if (plugin.getBungeeManager().isEnabled()) {
|
||||
sendBungeeActivateMessage(sender, sender.getName(), false);
|
||||
plugin.getCore().sendLocaleMessage("wait-on-proxy", sender);
|
||||
} else {
|
||||
plugin.getCore().sendLocaleMessage("not-premium", sender);
|
||||
//todo: load async if
|
||||
StoredProfile profile = plugin.getCore().getStorage().loadProfile(sender.getName());
|
||||
if (profile.isPremium()) {
|
||||
plugin.getCore().sendLocaleMessage("remove-premium", sender);
|
||||
|
||||
profile.setPremium(false);
|
||||
profile.setId(null);
|
||||
plugin.getScheduler().runAsync(() -> {
|
||||
plugin.getCore().getStorage().save(profile);
|
||||
plugin.getServer().getPluginManager().callEvent(
|
||||
new BukkitFastLoginPremiumToggleEvent(profile, PremiumToggleReason.COMMAND_OTHER));
|
||||
});
|
||||
} else {
|
||||
plugin.getCore().sendLocaleMessage("not-premium", sender);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,16 +100,16 @@ public class CrackedCommand extends ToggleCommand {
|
||||
}
|
||||
|
||||
//existing player is already cracked
|
||||
if (profile.isExistingPlayer() && !profile.isOnlinemodePreferred()) {
|
||||
if (profile.isSaved() && !profile.isPremium()) {
|
||||
plugin.getCore().sendLocaleMessage("not-premium-other", sender);
|
||||
} else {
|
||||
plugin.getCore().sendLocaleMessage("remove-premium", sender);
|
||||
|
||||
profile.setOnlinemodePreferred(false);
|
||||
profile.setPremium(false);
|
||||
plugin.getScheduler().runAsync(() -> {
|
||||
plugin.getCore().getStorage().save(profile);
|
||||
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)
|
||||
*
|
||||
* Copyright (c) 2015-2024 games647 and contributors
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -27,15 +27,16 @@ package com.github.games647.fastlogin.bukkit.command;
|
||||
|
||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||
import com.github.games647.fastlogin.bukkit.event.BukkitFastLoginPremiumToggleEvent;
|
||||
import com.github.games647.fastlogin.core.StoredProfile;
|
||||
import com.github.games647.fastlogin.core.shared.event.FastLoginPremiumToggleEvent.PremiumToggleReason;
|
||||
import com.github.games647.fastlogin.core.storage.StoredProfile;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 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
|
||||
@@ -48,10 +49,9 @@ public class PremiumCommand extends ToggleCommand {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label,
|
||||
String[] args) {
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
|
||||
if (args.length == 0) {
|
||||
onPremiumSelf(sender);
|
||||
onPremiumSelf(sender, command, args);
|
||||
} else {
|
||||
onPremiumOther(sender, command, args);
|
||||
}
|
||||
@@ -59,7 +59,7 @@ public class PremiumCommand extends ToggleCommand {
|
||||
return true;
|
||||
}
|
||||
|
||||
private void onPremiumSelf(CommandSender sender) {
|
||||
private void onPremiumSelf(CommandSender sender, Command cmd, String[] args) {
|
||||
if (isConsole(sender)) {
|
||||
return;
|
||||
}
|
||||
@@ -68,8 +68,7 @@ public class PremiumCommand extends ToggleCommand {
|
||||
return;
|
||||
}
|
||||
|
||||
Player player = (Player) sender;
|
||||
UUID id = player.getUniqueId();
|
||||
UUID id = ((Player) sender).getUniqueId();
|
||||
if (plugin.getConfig().getBoolean("premium-warning") && !plugin.getCore().getPendingConfirms().contains(id)) {
|
||||
sender.sendMessage(plugin.getCore().getMessage("premium-warning"));
|
||||
plugin.getCore().getPendingConfirms().add(id);
|
||||
@@ -79,25 +78,18 @@ public class PremiumCommand extends ToggleCommand {
|
||||
plugin.getCore().getPendingConfirms().remove(id);
|
||||
//todo: load async
|
||||
StoredProfile profile = plugin.getCore().getStorage().loadProfile(sender.getName());
|
||||
if (profile.isOnlinemodePreferred()) {
|
||||
if (profile.isPremium()) {
|
||||
plugin.getCore().sendLocaleMessage("already-exists", sender);
|
||||
} else {
|
||||
//todo: resolve uuid
|
||||
profile.setOnlinemodePreferred(true);
|
||||
profile.setPremium(true);
|
||||
plugin.getScheduler().runAsync(() -> {
|
||||
plugin.getCore().getStorage().save(profile);
|
||||
plugin.getServer().getPluginManager().callEvent(
|
||||
new BukkitFastLoginPremiumToggleEvent(sender, 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);
|
||||
}
|
||||
});
|
||||
new BukkitFastLoginPremiumToggleEvent(profile, PremiumToggleReason.COMMAND_SELF));
|
||||
});
|
||||
|
||||
plugin.getCore().sendLocaleMessage("add-premium", sender);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,16 +109,15 @@ public class PremiumCommand extends ToggleCommand {
|
||||
return;
|
||||
}
|
||||
|
||||
if (profile.isOnlinemodePreferred()) {
|
||||
if (profile.isPremium()) {
|
||||
plugin.getCore().sendLocaleMessage("already-exists-other", sender);
|
||||
} else {
|
||||
//todo: resolve uuid
|
||||
profile.setOnlinemodePreferred(true);
|
||||
profile.setPremium(true);
|
||||
plugin.getScheduler().runAsync(() -> {
|
||||
plugin.getCore().getStorage().save(profile);
|
||||
plugin.getServer().getPluginManager().callEvent(
|
||||
new BukkitFastLoginPremiumToggleEvent(sender, profile, PremiumToggleReason.COMMAND_OTHER)
|
||||
);
|
||||
new BukkitFastLoginPremiumToggleEvent(profile, PremiumToggleReason.COMMAND_OTHER));
|
||||
});
|
||||
|
||||
plugin.getCore().sendLocaleMessage("add-premium-other", sender);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2024 games647 and contributors
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -28,6 +28,9 @@ package com.github.games647.fastlogin.bukkit.command;
|
||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||
import com.github.games647.fastlogin.core.message.ChangePremiumMessage;
|
||||
import com.github.games647.fastlogin.core.message.ChannelMessage;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
@@ -35,8 +38,6 @@ import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.messaging.PluginMessageRecipient;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public abstract class ToggleCommand implements CommandExecutor {
|
||||
|
||||
protected final FastLoginBukkit plugin;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2024 games647 and contributors
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -25,9 +25,9 @@
|
||||
*/
|
||||
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.event.FastLoginAutoLoginEvent;
|
||||
import com.github.games647.fastlogin.core.storage.StoredProfile;
|
||||
import org.bukkit.event.Cancellable;
|
||||
import org.bukkit.event.Event;
|
||||
import org.bukkit.event.HandlerList;
|
||||
@@ -35,7 +35,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class BukkitFastLoginAutoLoginEvent extends Event implements FastLoginAutoLoginEvent, Cancellable {
|
||||
|
||||
private static final HandlerList HANDLERS = new HandlerList();
|
||||
private static final HandlerList handlers = new HandlerList();
|
||||
private final LoginSession session;
|
||||
private final StoredProfile profile;
|
||||
private boolean cancelled;
|
||||
@@ -69,10 +69,10 @@ public class BukkitFastLoginAutoLoginEvent extends Event implements FastLoginAut
|
||||
|
||||
@Override
|
||||
public @NotNull HandlerList getHandlers() {
|
||||
return HANDLERS;
|
||||
return handlers;
|
||||
}
|
||||
|
||||
public static HandlerList getHandlerList() {
|
||||
return HANDLERS;
|
||||
return handlers;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2024 games647 and contributors
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -25,16 +25,16 @@
|
||||
*/
|
||||
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.event.FastLoginPreLoginEvent;
|
||||
import com.github.games647.fastlogin.core.storage.StoredProfile;
|
||||
import org.bukkit.event.Event;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class BukkitFastLoginPreLoginEvent extends Event implements FastLoginPreLoginEvent {
|
||||
|
||||
private static final HandlerList HANDLERS = new HandlerList();
|
||||
private static final HandlerList handlers = new HandlerList();
|
||||
private final String username;
|
||||
private final LoginSource source;
|
||||
private final StoredProfile profile;
|
||||
@@ -64,10 +64,10 @@ public class BukkitFastLoginPreLoginEvent extends Event implements FastLoginPreL
|
||||
|
||||
@Override
|
||||
public @NotNull HandlerList getHandlers() {
|
||||
return HANDLERS;
|
||||
return handlers;
|
||||
}
|
||||
|
||||
public static HandlerList getHandlerList() {
|
||||
return HANDLERS;
|
||||
return handlers;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2024 games647 and contributors
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -25,25 +25,20 @@
|
||||
*/
|
||||
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.storage.StoredProfile;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.event.Event;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class BukkitFastLoginPremiumToggleEvent extends Event implements FastLoginPremiumToggleEvent {
|
||||
|
||||
private static final HandlerList HANDLERS = new HandlerList();
|
||||
|
||||
private final CommandSender invoker;
|
||||
private static final HandlerList handlers = new HandlerList();
|
||||
private final StoredProfile profile;
|
||||
private final PremiumToggleReason reason;
|
||||
|
||||
public BukkitFastLoginPremiumToggleEvent(CommandSender invoker, StoredProfile profile, PremiumToggleReason reason) {
|
||||
public BukkitFastLoginPremiumToggleEvent(StoredProfile profile, PremiumToggleReason reason) {
|
||||
super(true);
|
||||
|
||||
this.invoker = invoker;
|
||||
this.profile = profile;
|
||||
this.reason = reason;
|
||||
}
|
||||
@@ -53,13 +48,6 @@ public class BukkitFastLoginPremiumToggleEvent extends Event implements FastLogi
|
||||
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
|
||||
public PremiumToggleReason getReason() {
|
||||
return reason;
|
||||
@@ -67,10 +55,10 @@ public class BukkitFastLoginPremiumToggleEvent extends Event implements FastLogi
|
||||
|
||||
@Override
|
||||
public @NotNull HandlerList getHandlers() {
|
||||
return HANDLERS;
|
||||
return handlers;
|
||||
}
|
||||
|
||||
public static HandlerList getHandlerList() {
|
||||
return HANDLERS;
|
||||
return handlers;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2024 games647 and contributors
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -41,13 +41,13 @@ import org.bukkit.event.Listener;
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
/**
|
||||
* GitHub: <a href="https://github.com/Xephi/AuthMeReloaded/">...</a>
|
||||
* GitHub: https://github.com/Xephi/AuthMeReloaded/
|
||||
* <p>
|
||||
* Project page:
|
||||
* <p>
|
||||
* <a href="https://dev.bukkit.org/bukkit-plugins/authme-reloaded/">Bukkit</a>
|
||||
* Bukkit: https://dev.bukkit.org/bukkit-plugins/authme-reloaded/
|
||||
* <p>
|
||||
* <a href="https://www.spigotmc.org/resources/authme-reloaded.6269/">Spigot</a>
|
||||
* Spigot: https://www.spigotmc.org/resources/authme-reloaded.6269/
|
||||
*/
|
||||
public class AuthMeHook implements AuthPlugin<Player>, Listener {
|
||||
|
||||
@@ -60,7 +60,7 @@ public class AuthMeHook implements AuthPlugin<Player>, Listener {
|
||||
this.plugin = plugin;
|
||||
this.authmeAPI = AuthMeApi.getInstance();
|
||||
|
||||
if (plugin.getCore().getConfig().getBoolean("respectIpLimit", false)) {
|
||||
if (plugin.getConfig().getBoolean("respectIpLimit", false)) {
|
||||
try {
|
||||
Field managementField = this.authmeAPI.getClass().getDeclaredField("management");
|
||||
managementField.setAccessible(true);
|
||||
@@ -75,8 +75,8 @@ public class AuthMeHook implements AuthPlugin<Player>, Listener {
|
||||
public void onSessionRestore(RestoreSessionEvent restoreSessionEvent) {
|
||||
Player player = restoreSessionEvent.getPlayer();
|
||||
|
||||
BukkitLoginSession session = plugin.getSession(player.spigot().getRawAddress());
|
||||
if (session != null && session.isVerifiedPremium()) {
|
||||
BukkitLoginSession session = plugin.getSession(player.getAddress());
|
||||
if (session != null && session.isVerified()) {
|
||||
restoreSessionEvent.setCancelled(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2024 games647 and contributors
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -25,28 +25,29 @@
|
||||
*/
|
||||
package com.github.games647.fastlogin.bukkit.hook;
|
||||
|
||||
import com.comphenix.protocol.reflect.accessors.Accessors;
|
||||
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
|
||||
import com.comphenix.protocol.reflect.FieldUtils;
|
||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
|
||||
|
||||
import de.st_ddt.crazylogin.CrazyLogin;
|
||||
import de.st_ddt.crazylogin.data.LoginPlayerData;
|
||||
import de.st_ddt.crazylogin.databases.CrazyLoginDataDatabase;
|
||||
import de.st_ddt.crazylogin.listener.PlayerListener;
|
||||
import de.st_ddt.crazylogin.metadata.Authenticated;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
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: https://github.com/ST-DDT/CrazyLogin
|
||||
* <p>
|
||||
* Project page:
|
||||
* <p>
|
||||
* <a href="https://dev.bukkit.org/server-mods/crazylogin/">Bukkit</a>
|
||||
* Bukkit: https://dev.bukkit.org/server-mods/crazylogin/
|
||||
*/
|
||||
public class CrazyLoginHook implements AuthPlugin<Player> {
|
||||
|
||||
@@ -133,8 +134,15 @@ public class CrazyLoginHook implements AuthPlugin<Player> {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected PlayerListener getListener() {
|
||||
FieldAccessor accessor = Accessors.getFieldAccessor(crazyLoginPlugin.getClass(), PlayerListener.class, true);
|
||||
return (PlayerListener) accessor.get(crazyLoginPlugin);
|
||||
private PlayerListener getListener() {
|
||||
PlayerListener listener;
|
||||
try {
|
||||
listener = (PlayerListener) FieldUtils.readField(crazyLoginPlugin, "playerListener", true);
|
||||
} catch (IllegalAccessException ex) {
|
||||
plugin.getLog().error("Failed to get the listener instance for auto login", ex);
|
||||
listener = null;
|
||||
}
|
||||
|
||||
return listener;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2024 games647 and contributors
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -27,16 +27,18 @@ package com.github.games647.fastlogin.bukkit.hook;
|
||||
|
||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
|
||||
|
||||
import io.github.lucaseasedup.logit.CancelledState;
|
||||
import io.github.lucaseasedup.logit.LogItCore;
|
||||
import io.github.lucaseasedup.logit.account.Account;
|
||||
import io.github.lucaseasedup.logit.session.SessionManager;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
/**
|
||||
* GitHub: <a href="https://github.com/XziomekX/LogIt">...</a>
|
||||
* GitHub: https://github.com/XziomekX/LogIt
|
||||
* <p>
|
||||
* Project page:
|
||||
* <p>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2024 games647 and contributors
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -32,16 +32,17 @@ import com.lenis0012.bukkit.loginsecurity.session.AuthService;
|
||||
import com.lenis0012.bukkit.loginsecurity.session.PlayerSession;
|
||||
import com.lenis0012.bukkit.loginsecurity.session.action.LoginAction;
|
||||
import com.lenis0012.bukkit.loginsecurity.session.action.RegisterAction;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
/**
|
||||
* GitHub: <a href="https://github.com/lenis0012/LoginSecurity-2">...</a>
|
||||
* GitHub: https://github.com/lenis0012/LoginSecurity-2
|
||||
* <p>
|
||||
* Project page:
|
||||
* <p>
|
||||
* <a href="https://dev.bukkit.org/bukkit-plugins/loginsecurity/">Bukkit</a>
|
||||
* Bukkit: https://dev.bukkit.org/bukkit-plugins/loginsecurity/
|
||||
* <p>
|
||||
* <a href="https://www.spigotmc.org/resources/loginsecurity.19362/">Spigot</a>
|
||||
* Spigot: https://www.spigotmc.org/resources/loginsecurity.19362/
|
||||
*/
|
||||
public class LoginSecurityHook implements AuthPlugin<Player> {
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2024 games647 and contributors
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -27,42 +27,52 @@ 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;
|
||||
import red.mohist.sodionauth.bukkit.implementation.BukkitPlayer;
|
||||
import red.mohist.sodionauth.core.SodionAuthApi;
|
||||
import red.mohist.sodionauth.core.exception.AuthenticatedException;
|
||||
|
||||
public class PasskyHook implements AuthPlugin<Player> {
|
||||
/**
|
||||
* GitHub: https://github.com/Mohist-Community/SodionAuth
|
||||
* <p>
|
||||
* Project page: https://gitea.e-loli.com/SodionAuth/SodionAuth
|
||||
* <p>
|
||||
* Bukkit: Unknown
|
||||
* <p>
|
||||
* Spigot: https://www.spigotmc.org/resources/sodionauth.76944/
|
||||
*/
|
||||
public class SodionAuthHook implements AuthPlugin<Player> {
|
||||
|
||||
private final FastLoginBukkit plugin;
|
||||
|
||||
public PasskyHook(FastLoginBukkit plugin) {
|
||||
public SodionAuthHook(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);
|
||||
try {
|
||||
SodionAuthApi.login(new BukkitPlayer(player));
|
||||
} catch (AuthenticatedException e) {
|
||||
plugin.getLog().warn(ALREADY_AUTHENTICATED, player);
|
||||
return false;
|
||||
}
|
||||
|
||||
return result.success;
|
||||
return true;
|
||||
}
|
||||
|
||||
@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);
|
||||
try{
|
||||
return SodionAuthApi.register(new BukkitPlayer(player), password);
|
||||
} catch (UnsupportedOperationException e){
|
||||
plugin.getLog().warn("Currently SodionAuth is not accepting forceRegister, " +
|
||||
"It may be caused by unsupported AuthBackend");
|
||||
return false;
|
||||
}
|
||||
|
||||
return result.success;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRegistered(String playerName) {
|
||||
return PasskyAPI.isRegistered(new Identifier(playerName));
|
||||
return SodionAuthApi.isRegistered(playerName);
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2024 games647 and contributors
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -27,20 +27,21 @@ package com.github.games647.fastlogin.bukkit.hook;
|
||||
|
||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||
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.entity.Player;
|
||||
import ultraauth.api.UltraAuthAPI;
|
||||
import ultraauth.managers.PlayerManager;
|
||||
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
/**
|
||||
* Project page:
|
||||
* <p>
|
||||
* <a href="https://dev.bukkit.org/bukkit-plugins/ultraauth-aa/">Bukkit</a>
|
||||
* Bukkit: https://dev.bukkit.org/bukkit-plugins/ultraauth-aa/
|
||||
* <p>
|
||||
* <a href="https://www.spigotmc.org/resources/ultraauth.17044/">Spigot</a>
|
||||
* Spigot: https://www.spigotmc.org/resources/ultraauth.17044/
|
||||
*/
|
||||
public class UltraAuthHook implements AuthPlugin<Player> {
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2024 games647 and contributors
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -27,27 +27,29 @@ package com.github.games647.fastlogin.bukkit.hook;
|
||||
|
||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
|
||||
|
||||
import de.luricos.bukkit.xAuth.xAuth;
|
||||
import de.luricos.bukkit.xAuth.xAuthPlayer;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
/**
|
||||
* GitHub: <a href="https://github.com/LycanDevelopment/xAuth/">...</a>
|
||||
* GitHub: https://github.com/LycanDevelopment/xAuth/
|
||||
* <p>
|
||||
* Project page:
|
||||
* <p>
|
||||
* <a href="https://dev.bukkit.org/bukkit-plugins/xauth/">Bukkit</a>
|
||||
* Bukkit: https://dev.bukkit.org/bukkit-plugins/xauth/
|
||||
*/
|
||||
public class XAuthHook implements AuthPlugin<Player> {
|
||||
public class xAuthHook implements AuthPlugin<Player> {
|
||||
|
||||
private final xAuth xAuthPlugin = xAuth.getPlugin();
|
||||
private final FastLoginBukkit plugin;
|
||||
|
||||
public XAuthHook(FastLoginBukkit plugin) {
|
||||
public xAuthHook(FastLoginBukkit plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2024 games647 and contributors
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -34,16 +34,18 @@ import com.github.games647.fastlogin.core.message.LoginActionMessage;
|
||||
import com.github.games647.fastlogin.core.message.LoginActionMessage.Type;
|
||||
import com.google.common.io.ByteArrayDataInput;
|
||||
import com.google.common.io.ByteStreams;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.messaging.PluginMessageListener;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 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
|
||||
* the connection is in online mode.
|
||||
*/
|
||||
@@ -91,23 +93,24 @@ public class BungeeListener implements PluginMessageListener {
|
||||
String playerName = message.getPlayerName();
|
||||
Type type = message.getType();
|
||||
|
||||
InetSocketAddress address = player.getAddress();
|
||||
plugin.getLog().info("Player info {} command for {} from proxy", type, playerName);
|
||||
if (type == Type.LOGIN) {
|
||||
onLoginMessage(player, playerName);
|
||||
onLoginMessage(player, playerName, address);
|
||||
} else if (type == Type.REGISTER) {
|
||||
onRegisterMessage(player, playerName);
|
||||
onRegisterMessage(player, playerName, address);
|
||||
} else if (type == Type.CRACKED) {
|
||||
//we don't start a force login task here so update it manually
|
||||
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);
|
||||
startLoginTaskIfReady(player, playerSession);
|
||||
}
|
||||
|
||||
private void onRegisterMessage(Player player, String playerName) {
|
||||
private void onRegisterMessage(Player player, String playerName, InetSocketAddress address) {
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||
AuthPlugin<Player> authPlugin = plugin.getCore().getAuthPluginHook();
|
||||
try {
|
||||
@@ -123,8 +126,8 @@ public class BungeeListener implements PluginMessageListener {
|
||||
}
|
||||
|
||||
private void startLoginTaskIfReady(Player player, BukkitLoginSession session) {
|
||||
session.setVerifiedPremium(true);
|
||||
plugin.putSession(player.spigot().getRawAddress(), session);
|
||||
session.setVerified(true);
|
||||
plugin.putSession(player.getAddress(), session);
|
||||
|
||||
// only start a new login task if the join event fired earlier. This event then didn't
|
||||
boolean result = plugin.getBungeeManager().didJoinEventFired(player);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2024 games647 and contributors
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -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.ForceLoginTask;
|
||||
import com.github.games647.fastlogin.core.hooks.bedrock.FloodgateService;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
@@ -79,7 +80,7 @@ public class ConnectionListener implements Listener {
|
||||
// session exists so the player is ready for force login
|
||||
// cases: Paper (firing BungeeCord message before PlayerJoinEvent) or not running BungeeCord and already
|
||||
// having the login session from the login process
|
||||
BukkitLoginSession session = plugin.getSession(player.spigot().getRawAddress());
|
||||
BukkitLoginSession session = plugin.getSession(player.getAddress());
|
||||
|
||||
if (session == null) {
|
||||
// Floodgate players usually don't have a session at this point
|
||||
@@ -90,15 +91,12 @@ public class ConnectionListener implements Listener {
|
||||
if (floodgatePlayer != null) {
|
||||
Runnable floodgateAuthTask = new FloodgateAuthTask(plugin.getCore(), player, floodgatePlayer);
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, floodgateAuthTask);
|
||||
plugin.getBungeeManager().markJoinEventFired(player);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
String sessionId = plugin.getSessionId(player.spigot().getRawAddress());
|
||||
plugin.getLog().info("No on-going login session for player: {} with ID {}. ", player, sessionId);
|
||||
plugin.getLog().info("Setups using Minecraft proxies will start delayed "
|
||||
+ "when the command from the proxy is received");
|
||||
String sessionId = plugin.getSessionId(player.getAddress());
|
||||
plugin.getLog().info("No on-going login session for player: {} with ID {}", player, sessionId);
|
||||
} else {
|
||||
Runnable forceLoginTask = new ForceLoginTask(plugin.getCore(), player, session);
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, forceLoginTask);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2024 games647 and contributors
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2024 games647 and contributors
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -25,72 +25,34 @@
|
||||
*/
|
||||
package com.github.games647.fastlogin.bukkit.listener.protocollib;
|
||||
|
||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||
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.io.Resources;
|
||||
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.math.BigInteger;
|
||||
import java.net.URL;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.Signature;
|
||||
import java.security.SignatureException;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.security.spec.KeySpec;
|
||||
import java.security.spec.X509EncodedKeySpec;
|
||||
import java.time.Instant;
|
||||
import java.util.Arrays;
|
||||
import java.util.Base64;
|
||||
import java.util.Base64.Encoder;
|
||||
import java.util.Random;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
/**
|
||||
* Encryption and decryption minecraft util for connection between servers
|
||||
* and paid Minecraft account clients.
|
||||
*
|
||||
* @see net.minecraft.server.MinecraftEncryption
|
||||
*/
|
||||
final class EncryptionUtil {
|
||||
public class EncryptionUtil {
|
||||
|
||||
public static final int VERIFY_TOKEN_LENGTH = 4;
|
||||
public static final String KEY_PAIR_ALGORITHM = "RSA";
|
||||
|
||||
private static final int RSA_LENGTH = 2_048;
|
||||
|
||||
private static final PublicKey MOJANG_SESSION_KEY;
|
||||
private static final int LINE_LENGTH = 76;
|
||||
private static final Encoder KEY_ENCODER = Base64.getMimeEncoder(
|
||||
LINE_LENGTH, "\n".getBytes(StandardCharsets.UTF_8)
|
||||
);
|
||||
private static final int MILLISECOND_SIZE = 8;
|
||||
private static final int UUID_SIZE = 2 * MILLISECOND_SIZE;
|
||||
|
||||
static {
|
||||
try {
|
||||
MOJANG_SESSION_KEY = loadMojangSessionKey();
|
||||
} catch (IOException | NoSuchAlgorithmException | InvalidKeySpecException ex) {
|
||||
throw new RuntimeException("Failed to load Mojang session key", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private EncryptionUtil() {
|
||||
throw new RuntimeException("No instantiation of utility classes allowed");
|
||||
// utility
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -99,10 +61,11 @@ final class EncryptionUtil {
|
||||
* @return The RSA key pair.
|
||||
*/
|
||||
public static KeyPair generateKeyPair() {
|
||||
// KeyPair b()
|
||||
try {
|
||||
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_PAIR_ALGORITHM);
|
||||
|
||||
keyPairGenerator.initialize(RSA_LENGTH);
|
||||
keyPairGenerator.initialize(1_024);
|
||||
return keyPairGenerator.generateKeyPair();
|
||||
} catch (NoSuchAlgorithmException nosuchalgorithmexception) {
|
||||
// Should be existing in every vm
|
||||
@@ -115,9 +78,10 @@ final class EncryptionUtil {
|
||||
* in a login session.
|
||||
*
|
||||
* @param random random generator
|
||||
* @return a token with 4 bytes long
|
||||
* @return an error with 4 bytes long
|
||||
*/
|
||||
public static byte[] generateVerifyToken(Random random) {
|
||||
// extracted from LoginListener
|
||||
byte[] token = new byte[VERIFY_TOKEN_LENGTH];
|
||||
random.nextBytes(token);
|
||||
return token;
|
||||
@@ -126,102 +90,68 @@ final class EncryptionUtil {
|
||||
/**
|
||||
* Generate the server id based on client and server data.
|
||||
*
|
||||
* @param serverId session for the current login attempt
|
||||
* @param sessionId session for the current login attempt
|
||||
* @param sharedSecret shared secret between the client and the server
|
||||
* @param publicKey public key of the server
|
||||
* @param publicKey public key of the server
|
||||
* @return the server id formatted as a hexadecimal string.
|
||||
*/
|
||||
public static String getServerIdHashString(String serverId, SecretKey sharedSecret, PublicKey publicKey) {
|
||||
byte[] serverHash = getServerIdHash(serverId, publicKey, sharedSecret);
|
||||
return (new BigInteger(serverHash)).toString(16);
|
||||
public static String getServerIdHashString(String sessionId, SecretKey sharedSecret, PublicKey publicKey) {
|
||||
// found in LoginListener
|
||||
try {
|
||||
byte[] serverHash = getServerIdHash(sessionId, publicKey, sharedSecret);
|
||||
return (new BigInteger(serverHash)).toString(16);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypts the content and extracts the key spec.
|
||||
*
|
||||
* @param privateKey private server key
|
||||
* @param sharedKey the encrypted shared key
|
||||
* @param sharedKey the encrypted shared key
|
||||
* @return shared secret key
|
||||
* @throws GeneralSecurityException if it fails to decrypt the data
|
||||
*/
|
||||
public static SecretKey decryptSharedKey(PrivateKey privateKey, byte[] sharedKey)
|
||||
throws NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException,
|
||||
BadPaddingException, InvalidKeyException {
|
||||
public static SecretKey decryptSharedKey(PrivateKey privateKey, byte[] sharedKey) throws GeneralSecurityException {
|
||||
// SecretKey a(PrivateKey var0, byte[] var1)
|
||||
return new SecretKeySpec(decrypt(privateKey, sharedKey), "AES");
|
||||
}
|
||||
|
||||
public static boolean verifyClientKey(ClientPublicKey clientKey, Instant verifyTimestamp, UUID premiumId)
|
||||
throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
|
||||
if (clientKey.isExpired(verifyTimestamp)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Signature verifier = Signature.getInstance("SHA1withRSA");
|
||||
// key of the signer
|
||||
verifier.initVerify(MOJANG_SESSION_KEY);
|
||||
verifier.update(toSignable(clientKey, premiumId));
|
||||
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)
|
||||
throws NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException,
|
||||
BadPaddingException, InvalidKeyException {
|
||||
byte[] decryptedNonce = decrypt(decryptionKey, encryptedNonce);
|
||||
return Arrays.equals(expected, decryptedNonce);
|
||||
}
|
||||
|
||||
public static boolean verifySignedNonce(byte[] nonce, PublicKey clientKey, long signatureSalt, byte[] signature)
|
||||
throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
|
||||
Signature verifier = Signature.getInstance("SHA256withRSA");
|
||||
// key of the signer
|
||||
verifier.initVerify(clientKey);
|
||||
|
||||
verifier.update(nonce);
|
||||
verifier.update(Longs.toByteArray(signatureSalt));
|
||||
return verifier.verify(signature);
|
||||
}
|
||||
|
||||
private static PublicKey loadMojangSessionKey()
|
||||
throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
|
||||
URL keyUrl = FastLoginBukkit.class.getClassLoader().getResource("yggdrasil_session_pubkey.der");
|
||||
byte[] keyData = Resources.toByteArray(keyUrl);
|
||||
KeySpec keySpec = new X509EncodedKeySpec(keyData);
|
||||
|
||||
return KeyFactory.getInstance("RSA").generatePublic(keySpec);
|
||||
}
|
||||
|
||||
private static byte[] decrypt(PrivateKey key, byte[] data)
|
||||
throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException,
|
||||
IllegalBlockSizeException, BadPaddingException {
|
||||
public static byte[] decrypt(PrivateKey key, byte[] data) throws GeneralSecurityException {
|
||||
// b(Key var0, byte[] var1)
|
||||
Cipher cipher = Cipher.getInstance(key.getAlgorithm());
|
||||
cipher.init(Cipher.DECRYPT_MODE, key);
|
||||
return decrypt(cipher, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypted the given data using the cipher.
|
||||
*
|
||||
* @param cipher decryption cypher initialized with the private key
|
||||
* @param data the encrypted data
|
||||
* @return clear text data
|
||||
* @throws GeneralSecurityException if it fails to decrypt the data
|
||||
*/
|
||||
private static byte[] decrypt(Cipher cipher, byte[] data) throws GeneralSecurityException {
|
||||
// inlined: byte[] a(int var0, Key var1, byte[] var2), Cipher a(int var0, String var1, Key
|
||||
// var2)
|
||||
return cipher.doFinal(data);
|
||||
}
|
||||
|
||||
private static byte[] getServerIdHash(String sessionId, PublicKey publicKey, SecretKey sharedSecret) {
|
||||
@SuppressWarnings("deprecation")
|
||||
Hasher hasher = Hashing.sha1().newHasher();
|
||||
private static byte[] getServerIdHash(
|
||||
String sessionId, PublicKey publicKey, SecretKey sharedSecret)
|
||||
throws NoSuchAlgorithmException {
|
||||
// byte[] a(String var0, PublicKey var1, SecretKey var2)
|
||||
MessageDigest digest = MessageDigest.getInstance("SHA-1");
|
||||
|
||||
hasher.putBytes(sessionId.getBytes(StandardCharsets.ISO_8859_1));
|
||||
hasher.putBytes(sharedSecret.getEncoded());
|
||||
hasher.putBytes(publicKey.getEncoded());
|
||||
// inlined from byte[] a(String var0, byte[]... var1)
|
||||
digest.update(sessionId.getBytes(StandardCharsets.ISO_8859_1));
|
||||
digest.update(sharedSecret.getEncoded());
|
||||
digest.update(publicKey.getEncoded());
|
||||
|
||||
return hasher.hash().asBytes();
|
||||
return digest.digest();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package com.github.games647.fastlogin.bukkit.listener.protocollib;
|
||||
|
||||
import com.comphenix.protocol.events.PacketAdapter;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.comphenix.protocol.wrappers.WrappedGameProfile;
|
||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||
import com.github.games647.fastlogin.core.hooks.bedrock.FloodgateService;
|
||||
|
||||
import org.geysermc.floodgate.api.FloodgateApi;
|
||||
|
||||
import static com.comphenix.protocol.PacketType.Login.Client.START;
|
||||
|
||||
import com.comphenix.protocol.ProtocolLibrary;
|
||||
|
||||
/**
|
||||
* Manually inject Floodgate player name prefixes.
|
||||
* <br>
|
||||
* This is used as a workaround, because Floodgate fails to inject
|
||||
* the prefixes when it's used together with ProtocolLib and FastLogin.
|
||||
* <br>
|
||||
* For more information visit: https://github.com/games647/FastLogin/issues/493
|
||||
*/
|
||||
public class ManualNameChange extends PacketAdapter {
|
||||
|
||||
private final FloodgateService floodgate;
|
||||
|
||||
public ManualNameChange(FastLoginBukkit plugin, FloodgateService floodgate) {
|
||||
super(params()
|
||||
.plugin(plugin)
|
||||
.types(START));
|
||||
|
||||
this.plugin = plugin;
|
||||
this.floodgate = floodgate;
|
||||
}
|
||||
|
||||
public static void register(FastLoginBukkit plugin, FloodgateService floodgate) {
|
||||
// they will be created with a static builder, because otherwise it will throw a NoClassDefFoundError
|
||||
ProtocolLibrary.getProtocolManager()
|
||||
.getAsynchronousManager()
|
||||
.registerAsyncHandler(new ManualNameChange(plugin, floodgate))
|
||||
.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketReceiving(PacketEvent packetEvent) {
|
||||
PacketContainer packet = packetEvent.getPacket();
|
||||
WrappedGameProfile originalProfile = packet.getGameProfiles().read(0);
|
||||
|
||||
if (floodgate.getBedrockPlayer(originalProfile.getName()) == null) {
|
||||
//not a Floodgate player, no need to add a prefix
|
||||
return;
|
||||
}
|
||||
|
||||
packet.setMeta("original_name", originalProfile.getName());
|
||||
String prefixedName = FloodgateApi.getInstance().getPlayerPrefix() + originalProfile.getName();
|
||||
WrappedGameProfile updatedProfile = originalProfile.withName(prefixedName);
|
||||
packet.getGameProfiles().write(0, updatedProfile);
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2024 games647 and contributors
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -30,24 +30,22 @@ import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
|
||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||
import com.github.games647.fastlogin.bukkit.event.BukkitFastLoginPreLoginEvent;
|
||||
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.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.util.Random;
|
||||
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class NameCheckTask extends JoinManagement<Player, CommandSender, ProtocolLibLoginSource>
|
||||
implements Runnable {
|
||||
implements Runnable {
|
||||
|
||||
private final FastLoginBukkit plugin;
|
||||
private final PacketEvent packetEvent;
|
||||
|
||||
private final ClientPublicKey clientKey;
|
||||
private final PublicKey serverKey;
|
||||
private final PublicKey publicKey;
|
||||
|
||||
private final Random random;
|
||||
|
||||
@@ -55,13 +53,12 @@ public class NameCheckTask extends JoinManagement<Player, CommandSender, Protoco
|
||||
private final String username;
|
||||
|
||||
public NameCheckTask(FastLoginBukkit plugin, Random random, Player player, PacketEvent packetEvent,
|
||||
String username, ClientPublicKey clientKey, PublicKey serverKey) {
|
||||
String username, PublicKey publicKey) {
|
||||
super(plugin.getCore(), plugin.getCore().getAuthPluginHook(), plugin.getBedrockService());
|
||||
|
||||
this.plugin = plugin;
|
||||
this.packetEvent = packetEvent;
|
||||
this.clientKey = clientKey;
|
||||
this.serverKey = serverKey;
|
||||
this.publicKey = publicKey;
|
||||
this.random = random;
|
||||
this.player = player;
|
||||
this.username = username;
|
||||
@@ -70,7 +67,7 @@ public class NameCheckTask extends JoinManagement<Player, CommandSender, Protoco
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
super.onLogin(username, new ProtocolLibLoginSource(player, random, serverKey, clientKey));
|
||||
super.onLogin(username, new ProtocolLibLoginSource(player, random, publicKey));
|
||||
} finally {
|
||||
ProtocolLibrary.getProtocolManager().getAsynchronousManager().signalPacketTransmission(packetEvent);
|
||||
}
|
||||
@@ -87,8 +84,8 @@ public class NameCheckTask extends JoinManagement<Player, CommandSender, Protoco
|
||||
//Minecraft server implementation
|
||||
//https://github.com/bergerkiller/CraftSource/blob/master/net.minecraft.server/LoginListener.java#L161
|
||||
@Override
|
||||
public void requestPremiumLogin(ProtocolLibLoginSource source, StoredProfile profile,
|
||||
String username, boolean registered) {
|
||||
public void requestPremiumLogin(ProtocolLibLoginSource source, StoredProfile profile
|
||||
, String username, boolean registered) {
|
||||
try {
|
||||
source.enableOnlinemode();
|
||||
} catch (Exception ex) {
|
||||
@@ -97,12 +94,11 @@ public class NameCheckTask extends JoinManagement<Player, CommandSender, Protoco
|
||||
}
|
||||
|
||||
String ip = player.getAddress().getAddress().getHostAddress();
|
||||
core.addLoginAttempt(ip, username);
|
||||
core.getPendingLogin().put(ip + username, new Object());
|
||||
|
||||
byte[] verify = source.getVerifyToken();
|
||||
ClientPublicKey clientKey = source.getClientKey();
|
||||
|
||||
BukkitLoginSession playerSession = new BukkitLoginSession(username, verify, clientKey, registered, profile);
|
||||
BukkitLoginSession playerSession = new BukkitLoginSession(username, verify, registered, profile);
|
||||
plugin.putSession(player.getAddress(), playerSession);
|
||||
//cancel only if the player has a paid account otherwise login as normal offline player
|
||||
synchronized (packetEvent.getAsyncMarker().getProcessingLock()) {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2024 games647 and contributors
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -30,59 +30,29 @@ import com.comphenix.protocol.ProtocolLibrary;
|
||||
import com.comphenix.protocol.events.PacketAdapter;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
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.accessors.Accessors;
|
||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||
import com.comphenix.protocol.wrappers.BukkitConverters;
|
||||
import com.comphenix.protocol.wrappers.Converters;
|
||||
import com.comphenix.protocol.wrappers.WrappedGameProfile;
|
||||
import com.comphenix.protocol.wrappers.WrappedProfilePublicKey.WrappedProfileKeyData;
|
||||
import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
|
||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||
import com.github.games647.fastlogin.bukkit.listener.protocollib.packet.ClientPublicKey;
|
||||
import com.github.games647.fastlogin.core.antibot.AntiBotService;
|
||||
import com.github.games647.fastlogin.core.antibot.AntiBotService.Action;
|
||||
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 com.github.games647.fastlogin.core.RateLimiter;
|
||||
|
||||
import javax.crypto.BadPaddingException;
|
||||
import javax.crypto.IllegalBlockSizeException;
|
||||
import javax.crypto.NoSuchPaddingException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.KeyPair;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PublicKey;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.SignatureException;
|
||||
import java.time.Instant;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import static com.comphenix.protocol.PacketType.Login.Client.ENCRYPTION_BEGIN;
|
||||
import static com.comphenix.protocol.PacketType.Login.Client.START;
|
||||
|
||||
public class ProtocolLibListener extends PacketAdapter {
|
||||
|
||||
public static final String SOURCE_META_KEY = "source";
|
||||
|
||||
private final FastLoginBukkit plugin;
|
||||
|
||||
//just create a new once on plugin enable. This used for verify token generation
|
||||
private final SecureRandom random = new SecureRandom();
|
||||
private final KeyPair keyPair = EncryptionUtil.generateKeyPair();
|
||||
private final AntiBotService antiBotService;
|
||||
private final RateLimiter rateLimiter;
|
||||
|
||||
private final boolean verifyClientKeys;
|
||||
|
||||
public ProtocolLibListener(FastLoginBukkit plugin, AntiBotService antiBotService, boolean verifyClientKeys) {
|
||||
public ProtocolLibListener(FastLoginBukkit plugin, RateLimiter rateLimiter) {
|
||||
//run async in order to not block the server, because we are making api calls to Mojang
|
||||
super(params()
|
||||
.plugin(plugin)
|
||||
@@ -90,16 +60,15 @@ public class ProtocolLibListener extends PacketAdapter {
|
||||
.optionAsync());
|
||||
|
||||
this.plugin = plugin;
|
||||
this.antiBotService = antiBotService;
|
||||
this.verifyClientKeys = verifyClientKeys;
|
||||
this.rateLimiter = rateLimiter;
|
||||
}
|
||||
|
||||
public static void register(FastLoginBukkit plugin, AntiBotService antiBotService, boolean verifyClientKeys) {
|
||||
public static void register(FastLoginBukkit plugin, RateLimiter rateLimiter) {
|
||||
// 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
|
||||
ProtocolLibrary.getProtocolManager()
|
||||
.getAsynchronousManager()
|
||||
.registerAsyncHandler(new ProtocolLibListener(plugin, antiBotService, verifyClientKeys))
|
||||
.registerAsyncHandler(new ProtocolLibListener(plugin, rateLimiter))
|
||||
.start();
|
||||
}
|
||||
|
||||
@@ -111,240 +80,60 @@ public class ProtocolLibListener extends PacketAdapter {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isFastLoginPacket(packetEvent)) {
|
||||
// this is our own packet
|
||||
return;
|
||||
}
|
||||
|
||||
Player sender = packetEvent.getPlayer();
|
||||
PacketType packetType = getOverriddenType(packetEvent.getPacketType());
|
||||
|
||||
plugin.getLog().info("New incoming packet {} from {}", packetType, sender.getName());
|
||||
try {
|
||||
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();
|
||||
|
||||
InetSocketAddress address = sender.getAddress();
|
||||
String username = getUsername(packet);
|
||||
|
||||
Action action = antiBotService.onIncomingConnection(address, username);
|
||||
switch (action) {
|
||||
case Ignore:
|
||||
// just ignore
|
||||
return;
|
||||
case Block:
|
||||
String message = plugin.getCore().getMessage("kick-antibot");
|
||||
sender.kickPlayer(message);
|
||||
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);
|
||||
PacketType packetType = packetEvent.getPacketType();
|
||||
if (packetType == START) {
|
||||
if (!rateLimiter.tryAcquire()) {
|
||||
plugin.getLog().warn("Simple Anti-Bot join limit - Ignoring {}", sender);
|
||||
return;
|
||||
}
|
||||
} catch (FieldAccessException fieldAccessEx) {
|
||||
plugin.getLog().error("Failed to parse packet {}", packetEvent.getPacketType(), fieldAccessEx);
|
||||
|
||||
onLogin(packetEvent, sender);
|
||||
} else {
|
||||
onEncryptionBegin(packetEvent, sender);
|
||||
}
|
||||
}
|
||||
|
||||
private @NotNull PacketType getOverriddenType(PacketType packetType) {
|
||||
if (packetType.isDynamic()) {
|
||||
String vanillaName = packetType.getPacketClass().getName();
|
||||
plugin.getLog().info("Overriding packet type for unregistered packet type to fix ProtocolLib bug");
|
||||
if (vanillaName.endsWith("ServerboundHelloPacket")) {
|
||||
return START;
|
||||
}
|
||||
|
||||
if (vanillaName.endsWith("ServerboundKeyPacket")) {
|
||||
return ENCRYPTION_BEGIN;
|
||||
}
|
||||
}
|
||||
|
||||
return packetType;
|
||||
private Boolean isFastLoginPacket(PacketEvent packetEvent) {
|
||||
return packetEvent.getPacket().getMeta(SOURCE_META_KEY)
|
||||
.map(val -> val.equals(plugin.getName()))
|
||||
.orElse(false);
|
||||
}
|
||||
|
||||
private void onEncryptionBegin(PacketEvent packetEvent, Player sender) {
|
||||
byte[] sharedSecret = packetEvent.getPacket().getByteArrays().read(0);
|
||||
|
||||
BukkitLoginSession session = plugin.getSession(sender.getAddress());
|
||||
if (session == null) {
|
||||
plugin.getLog().warn("Profile {} tried to send encryption response at invalid state", sender.getAddress());
|
||||
sender.kickPlayer(plugin.getCore().getMessage("invalid-request"));
|
||||
} else {
|
||||
byte[] expectedVerifyToken = session.getVerifyToken();
|
||||
if (verifyNonce(sender, packetEvent.getPacket(), session.getClientPublicKey(), expectedVerifyToken)) {
|
||||
packetEvent.getAsyncMarker().incrementProcessingDelay();
|
||||
|
||||
Runnable verifyTask = new VerifyResponseTask(
|
||||
plugin, packetEvent, sender, session, sharedSecret, keyPair
|
||||
);
|
||||
plugin.getScheduler().runAsync(verifyTask);
|
||||
} else {
|
||||
sender.kickPlayer(plugin.getCore().getMessage("invalid-verify-token"));
|
||||
}
|
||||
}
|
||||
packetEvent.getAsyncMarker().incrementProcessingDelay();
|
||||
Runnable verifyTask = new VerifyResponseTask(plugin, packetEvent, sender, sharedSecret, keyPair);
|
||||
plugin.getScheduler().runAsync(verifyTask);
|
||||
}
|
||||
|
||||
private boolean verifyNonce(Player sender, PacketContainer packet,
|
||||
ClientPublicKey clientPublicKey, byte[] expectedToken) {
|
||||
try {
|
||||
if (new MinecraftVersion(1, 19, 0).atOrAbove()
|
||||
&& !new MinecraftVersion(1, 19, 3).atOrAbove()) {
|
||||
Either<byte[], ?> either = packet.getSpecificModifier(Either.class).read(0);
|
||||
if (clientPublicKey == null) {
|
||||
Optional<byte[]> left = either.left();
|
||||
if (!left.isPresent()) {
|
||||
plugin.getLog().error("No verify token sent if requested without player signed key {}", sender);
|
||||
return false;
|
||||
}
|
||||
|
||||
return EncryptionUtil.verifyNonce(expectedToken, keyPair.getPrivate(), left.get());
|
||||
} else {
|
||||
Optional<?> optSignatureData = either.right();
|
||||
if (!optSignatureData.isPresent()) {
|
||||
plugin.getLog().error("No signature given to sent player signing key {}", sender);
|
||||
return false;
|
||||
}
|
||||
|
||||
Object signatureData = optSignatureData.get();
|
||||
long salt = FuzzyReflection.getFieldValue(signatureData, Long.TYPE, true);
|
||||
byte[] signature = FuzzyReflection.getFieldValue(signatureData, byte[].class, true);
|
||||
|
||||
PublicKey publicKey = clientPublicKey.key();
|
||||
return EncryptionUtil.verifySignedNonce(expectedToken, publicKey, salt, signature);
|
||||
}
|
||||
} else {
|
||||
byte[] nonce = packet.getByteArrays().read(1);
|
||||
return EncryptionUtil.verifyNonce(expectedToken, keyPair.getPrivate(), nonce);
|
||||
}
|
||||
} catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException | NoSuchPaddingException
|
||||
| IllegalBlockSizeException | BadPaddingException signatureEx) {
|
||||
plugin.getLog().error("Invalid signature from player {}", sender, signatureEx);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void onLoginStart(PacketEvent packetEvent, Player player, String username) {
|
||||
private void onLogin(PacketEvent packetEvent, Player player) {
|
||||
//this includes ip:port. Should be unique for an incoming login request with a timeout of 2 minutes
|
||||
String sessionKey = player.getAddress().toString();
|
||||
|
||||
//remove old data every time on a new login in order to keep the session only for one person
|
||||
plugin.removeSession(player.getAddress());
|
||||
|
||||
//player.getName() won't work at this state
|
||||
PacketContainer packet = packetEvent.getPacket();
|
||||
Optional<ClientPublicKey> clientKey;
|
||||
if (new MinecraftVersion(1, 19, 3).atOrAbove()) {
|
||||
// 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 -> {
|
||||
Instant expires = data.getExpireTime();
|
||||
PublicKey key = data.getKey();
|
||||
byte[] signature = data.getSignature();
|
||||
return Optional.of(ClientPublicKey.of(expires, key, signature));
|
||||
});
|
||||
String username = packet.getGameProfiles().read(0).getName();
|
||||
|
||||
// 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;
|
||||
}
|
||||
if (packetEvent.getPacket().getMeta("original_name").isPresent()) {
|
||||
//username has been injected by ManualNameChange.java
|
||||
username = (String) packetEvent.getPacket().getMeta("original_name").get();
|
||||
}
|
||||
|
||||
plugin.getLog().trace("GameProfile {} with {} connecting", sessionKey, username);
|
||||
|
||||
packetEvent.getAsyncMarker().incrementProcessingDelay();
|
||||
Runnable nameCheckTask = new NameCheckTask(
|
||||
plugin, random, player, packetEvent, username, clientKey.orElse(null), keyPair.getPublic()
|
||||
);
|
||||
Runnable nameCheckTask = new NameCheckTask(plugin, random, player, packetEvent, username, keyPair.getPublic());
|
||||
plugin.getScheduler().runAsync(nameCheckTask);
|
||||
}
|
||||
|
||||
private boolean verifyPublicKey(ClientPublicKey clientKey, UUID sessionPremiumUUID) {
|
||||
try {
|
||||
return EncryptionUtil.verifyClientKey(clientKey, Instant.now(), sessionPremiumUUID);
|
||||
} catch (SignatureException | InvalidKeyException | NoSuchAlgorithmException ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private String getUsername(PacketContainer packet) {
|
||||
WrappedGameProfile profile = packet.getGameProfiles().readSafely(0);
|
||||
if (profile == null) {
|
||||
return packet.getStrings().read(0);
|
||||
}
|
||||
|
||||
//player.getName() won't work at this state
|
||||
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)
|
||||
*
|
||||
* Copyright (c) 2015-2024 games647 and contributors
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -30,15 +30,16 @@ import com.comphenix.protocol.ProtocolManager;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.reflect.StructureModifier;
|
||||
import com.comphenix.protocol.wrappers.WrappedChatComponent;
|
||||
import com.github.games647.fastlogin.bukkit.listener.protocollib.packet.ClientPublicKey;
|
||||
import com.github.games647.fastlogin.core.shared.LoginSource;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.security.PublicKey;
|
||||
import java.util.Arrays;
|
||||
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.ENCRYPTION_BEGIN;
|
||||
|
||||
@@ -47,21 +48,19 @@ class ProtocolLibLoginSource implements LoginSource {
|
||||
private final Player player;
|
||||
|
||||
private final Random random;
|
||||
|
||||
private final ClientPublicKey clientKey;
|
||||
private final PublicKey publicKey;
|
||||
|
||||
private final String serverId = "";
|
||||
private byte[] verifyToken;
|
||||
|
||||
ProtocolLibLoginSource(Player player, Random random, PublicKey serverPublicKey, ClientPublicKey clientKey) {
|
||||
public ProtocolLibLoginSource(Player player, Random random, PublicKey publicKey) {
|
||||
this.player = player;
|
||||
this.random = random;
|
||||
this.publicKey = serverPublicKey;
|
||||
this.clientKey = clientKey;
|
||||
this.publicKey = publicKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enableOnlinemode() {
|
||||
public void enableOnlinemode() throws InvocationTargetException {
|
||||
verifyToken = EncryptionUtil.generateVerifyToken(random);
|
||||
|
||||
/*
|
||||
@@ -71,7 +70,7 @@ class ProtocolLibLoginSource implements LoginSource {
|
||||
*/
|
||||
PacketContainer newPacket = new PacketContainer(ENCRYPTION_BEGIN);
|
||||
|
||||
newPacket.getStrings().write(0, "");
|
||||
newPacket.getStrings().write(0, serverId);
|
||||
StructureModifier<PublicKey> keyModifier = newPacket.getSpecificModifier(PublicKey.class);
|
||||
int verifyField = 0;
|
||||
if (keyModifier.getFields().isEmpty()) {
|
||||
@@ -83,15 +82,13 @@ class ProtocolLibLoginSource implements LoginSource {
|
||||
}
|
||||
|
||||
newPacket.getByteArrays().write(verifyField, verifyToken);
|
||||
// shouldAuthenticate, but why does this field even exist?
|
||||
newPacket.getBooleans().writeSafely(0, true);
|
||||
|
||||
//serverId is an empty string
|
||||
ProtocolLibrary.getProtocolManager().sendServerPacket(player, newPacket);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void kick(String message) {
|
||||
public void kick(String message) throws InvocationTargetException {
|
||||
ProtocolManager protocolManager = ProtocolLibrary.getProtocolManager();
|
||||
|
||||
PacketContainer kickPacket = new PacketContainer(DISCONNECT);
|
||||
@@ -112,8 +109,8 @@ class ProtocolLibLoginSource implements LoginSource {
|
||||
return player.getAddress();
|
||||
}
|
||||
|
||||
public ClientPublicKey getClientKey() {
|
||||
return clientKey;
|
||||
public String getServerId() {
|
||||
return serverId;
|
||||
}
|
||||
|
||||
public byte[] getVerifyToken() {
|
||||
@@ -122,10 +119,11 @@ class ProtocolLibLoginSource implements LoginSource {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.getClass().getSimpleName() + '{'
|
||||
+ "player=" + player
|
||||
+ ", random=" + random
|
||||
+ ", verifyToken=" + Arrays.toString(verifyToken)
|
||||
+ '}';
|
||||
return this.getClass().getSimpleName() + '{' +
|
||||
"player=" + player +
|
||||
", random=" + random +
|
||||
", serverId='" + serverId + '\'' +
|
||||
", verifyToken=" + Arrays.toString(verifyToken) +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2024 games647 and contributors
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -25,6 +25,10 @@
|
||||
*/
|
||||
package com.github.games647.fastlogin.bukkit.listener.protocollib;
|
||||
|
||||
import com.comphenix.protocol.reflect.MethodUtils;
|
||||
import com.comphenix.protocol.reflect.accessors.Accessors;
|
||||
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.wrappers.WrappedGameProfile;
|
||||
import com.comphenix.protocol.wrappers.WrappedSignedProperty;
|
||||
import com.github.games647.craftapi.model.skin.Textures;
|
||||
@@ -37,8 +41,13 @@ import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerLoginEvent;
|
||||
import org.bukkit.event.player.PlayerLoginEvent.Result;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
public class SkinApplyListener implements Listener {
|
||||
|
||||
private static final Class<?> GAME_PROFILE = MinecraftReflection.getGameProfileClass();
|
||||
private static final MethodAccessor GET_PROPERTIES = Accessors.getMethodAccessor(GAME_PROFILE, "getProperties");
|
||||
|
||||
private final FastLoginBukkit plugin;
|
||||
|
||||
public SkinApplyListener(FastLoginBukkit plugin) {
|
||||
@@ -68,6 +77,16 @@ public class SkinApplyListener implements Listener {
|
||||
WrappedGameProfile gameProfile = WrappedGameProfile.fromPlayer(player);
|
||||
|
||||
WrappedSignedProperty skin = WrappedSignedProperty.fromValues(Textures.KEY, skinData, signature);
|
||||
gameProfile.getProperties().put(Textures.KEY, skin);
|
||||
try {
|
||||
gameProfile.getProperties().put(Textures.KEY, skin);
|
||||
} catch (ClassCastException castException) {
|
||||
//Cauldron, MCPC, Thermos, ...
|
||||
Object map = GET_PROPERTIES.invoke(gameProfile.getHandle());
|
||||
try {
|
||||
MethodUtils.invokeMethod(map, "put", new Object[]{Textures.KEY, skin.getHandle()});
|
||||
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ex) {
|
||||
plugin.getLog().error("Error setting premium skin of: {}", player, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2024 games647 and contributors
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -28,33 +28,20 @@ package com.github.games647.fastlogin.bukkit.listener.protocollib;
|
||||
import com.comphenix.protocol.ProtocolLibrary;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
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.reflect.EquivalentConverter;
|
||||
import com.comphenix.protocol.injector.server.TemporaryPlayerFactory;
|
||||
import com.comphenix.protocol.reflect.FieldUtils;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
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.utility.MinecraftReflection;
|
||||
import com.comphenix.protocol.utility.MinecraftVersion;
|
||||
import com.comphenix.protocol.wrappers.BukkitConverters;
|
||||
import com.comphenix.protocol.wrappers.Converters;
|
||||
import com.comphenix.protocol.wrappers.WrappedChatComponent;
|
||||
import com.comphenix.protocol.wrappers.WrappedGameProfile;
|
||||
import com.comphenix.protocol.wrappers.WrappedProfilePublicKey.WrappedProfileKeyData;
|
||||
import com.github.games647.craftapi.model.auth.Verification;
|
||||
import com.github.games647.craftapi.model.skin.SkinProperty;
|
||||
import com.github.games647.craftapi.resolver.MojangResolver;
|
||||
import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
|
||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||
import com.github.games647.fastlogin.bukkit.InetUtils;
|
||||
import com.github.games647.fastlogin.bukkit.listener.protocollib.packet.ClientPublicKey;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.SecretKey;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
@@ -66,19 +53,22 @@ import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.SecretKey;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import static com.comphenix.protocol.PacketType.Login.Client.START;
|
||||
import static com.comphenix.protocol.PacketType.Login.Server.DISCONNECT;
|
||||
|
||||
public class VerifyResponseTask implements Runnable {
|
||||
|
||||
private static final String ENCRYPTION_CLASS_NAME = "MinecraftEncryption";
|
||||
private static final String ADDRESS_VERIFY_WARNING = "This indicates the use of reverse-proxy like HAProxy, "
|
||||
+ "TCPShield, BungeeCord, Velocity, etc. "
|
||||
+ "By default (configurable in the config) this plugin requests Mojang to verify the connecting IP "
|
||||
+ "to this server with the one used to log into Minecraft to prevent MITM attacks. In "
|
||||
+ "order to work this security feature, the actual client IP needs to be forwarding "
|
||||
+ "(keyword IP forwarding). This process will also be useful for other server "
|
||||
+ "features like IP banning, so that it doesn't ban the proxy IP.";
|
||||
private static final Class<?> ENCRYPTION_CLASS;
|
||||
|
||||
static {
|
||||
ENCRYPTION_CLASS = MinecraftReflection.getMinecraftClass("util." + ENCRYPTION_CLASS_NAME, ENCRYPTION_CLASS_NAME);
|
||||
}
|
||||
|
||||
private final FastLoginBukkit plugin;
|
||||
private final PacketEvent packetEvent;
|
||||
@@ -86,22 +76,16 @@ public class VerifyResponseTask implements Runnable {
|
||||
|
||||
private final Player player;
|
||||
|
||||
private final BukkitLoginSession session;
|
||||
|
||||
private final byte[] sharedSecret;
|
||||
|
||||
private static Method encryptMethod;
|
||||
private static Method encryptKeyMethod;
|
||||
|
||||
private static Method cipherMethod;
|
||||
|
||||
public VerifyResponseTask(FastLoginBukkit plugin, PacketEvent packetEvent,
|
||||
Player player, BukkitLoginSession session,
|
||||
public VerifyResponseTask(FastLoginBukkit plugin, PacketEvent packetEvent, Player player,
|
||||
byte[] sharedSecret, KeyPair keyPair) {
|
||||
this.plugin = plugin;
|
||||
this.packetEvent = packetEvent;
|
||||
this.player = player;
|
||||
this.session = session;
|
||||
this.sharedSecret = Arrays.copyOf(sharedSecret, sharedSecret.length);
|
||||
this.serverKey = keyPair;
|
||||
}
|
||||
@@ -109,7 +93,13 @@ public class VerifyResponseTask implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
verifyResponse(session);
|
||||
BukkitLoginSession session = plugin.getSession(player.getAddress());
|
||||
if (session == null) {
|
||||
disconnect("invalid-request", true
|
||||
, "GameProfile {0} tried to send encryption response at invalid state", player.getAddress());
|
||||
} else {
|
||||
verifyResponse(session);
|
||||
}
|
||||
} finally {
|
||||
//this is a fake packet; it shouldn't be sent to the server
|
||||
synchronized (packetEvent.getAsyncMarker().getProcessingLock()) {
|
||||
@@ -127,16 +117,16 @@ public class VerifyResponseTask implements Runnable {
|
||||
try {
|
||||
loginKey = EncryptionUtil.decryptSharedKey(privateKey, sharedSecret);
|
||||
} catch (GeneralSecurityException securityEx) {
|
||||
disconnect("error-kick", "Cannot decrypt received contents", securityEx);
|
||||
disconnect("error-kick", false, "Cannot decrypt received contents", securityEx);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (!enableEncryption(loginKey)) {
|
||||
if (!checkVerifyToken(session) || !enableEncryption(loginKey)) {
|
||||
return;
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
disconnect("error-kick", "Cannot decrypt received contents", ex);
|
||||
disconnect("error-kick", false, "Cannot decrypt received contents", ex);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -149,105 +139,92 @@ public class VerifyResponseTask implements Runnable {
|
||||
InetAddress address = socketAddress.getAddress();
|
||||
Optional<Verification> response = resolver.hasJoined(requestedUsername, serverId, address);
|
||||
if (response.isPresent()) {
|
||||
encryptConnection(session, requestedUsername, response.get());
|
||||
} else {
|
||||
//user tried to fake an authentication
|
||||
disconnect(
|
||||
"invalid-session",
|
||||
"Session server rejected incoming connection for GameProfile {} ({}). Possible reasons are "
|
||||
+ "1) Client IP address contacting Mojang and server during server join were different "
|
||||
+ "(Do you use a reverse proxy? -> Enable IP forwarding, "
|
||||
+ "or disable the feature in the config). "
|
||||
+ "2) Player is offline, but tried to bypass the authentication "
|
||||
+ "3) Client uses an outdated username for connecting (Fix: Restart client)",
|
||||
requestedUsername, address
|
||||
);
|
||||
|
||||
if (InetUtils.isLocalAddress(address)) {
|
||||
plugin.getLog().warn(
|
||||
"The incoming request for player {} uses a local IP address",
|
||||
requestedUsername
|
||||
);
|
||||
} 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);
|
||||
Verification verification = response.get();
|
||||
plugin.getLog().info("Profile {} has a verified premium account", requestedUsername);
|
||||
String realUsername = verification.getName();
|
||||
if (realUsername == null) {
|
||||
disconnect("invalid-session", true, "Username field null for {}", requestedUsername);
|
||||
return;
|
||||
}
|
||||
|
||||
plugin.getLog().warn(ADDRESS_VERIFY_WARNING);
|
||||
SkinProperty[] properties = verification.getProperties();
|
||||
if (properties.length > 0) {
|
||||
session.setSkinProperty(properties[0]);
|
||||
}
|
||||
|
||||
session.setVerifiedUsername(realUsername);
|
||||
session.setUuid(verification.getId());
|
||||
session.setVerified(true);
|
||||
|
||||
setPremiumUUID(session.getUuid());
|
||||
receiveFakeStartPacket(realUsername);
|
||||
} else {
|
||||
//user tried to fake an authentication
|
||||
disconnect("invalid-session", true
|
||||
, "GameProfile {0} ({1}) tried to log in with an invalid session ServerId: {2}"
|
||||
, session.getRequestUsername(), socketAddress, serverId);
|
||||
}
|
||||
} catch (IOException ioEx) {
|
||||
disconnect("error-kick", "Failed to connect to session server", ioEx);
|
||||
disconnect("error-kick", false, "Failed to connect to session server", ioEx);
|
||||
}
|
||||
}
|
||||
|
||||
private void encryptConnection(BukkitLoginSession session, String requestedUsername, Verification verification) {
|
||||
plugin.getLog().info("Profile {} has a verified premium account", requestedUsername);
|
||||
String realUsername = verification.getName();
|
||||
if (realUsername == null) {
|
||||
disconnect("invalid-session", "Username field null for {}", requestedUsername);
|
||||
return;
|
||||
}
|
||||
|
||||
SkinProperty[] properties = verification.getProperties();
|
||||
if (properties.length > 0) {
|
||||
session.setSkinProperty(properties[0]);
|
||||
}
|
||||
|
||||
session.setVerifiedUsername(realUsername);
|
||||
session.setUuid(verification.getId());
|
||||
session.setVerifiedPremium(true);
|
||||
|
||||
setPremiumUUID(session.getUuid());
|
||||
receiveFakeStartPacket(realUsername, session.getClientPublicKey(), session.getUuid());
|
||||
}
|
||||
|
||||
private void setPremiumUUID(UUID premiumUUID) {
|
||||
if (plugin.getConfig().getBoolean("premiumUuid") && premiumUUID != null) {
|
||||
try {
|
||||
Object networkManager = getNetworkManager();
|
||||
//https://github.com/bergerkiller/CraftSource/blob/master/net.minecraft.server/NetworkManager.java#L69
|
||||
|
||||
Class<?> managerClass = networkManager.getClass();
|
||||
FieldAccessor accessor = Accessors.getFieldAccessorOrNull(managerClass, "spoofedUUID", UUID.class);
|
||||
accessor.set(networkManager, premiumUUID);
|
||||
FieldUtils.writeField(networkManager, "spoofedUUID", premiumUUID, true);
|
||||
} catch (Exception exc) {
|
||||
plugin.getLog().error("Error setting premium uuid of {}", player, exc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//try to get the networkManager from ProtocolLib
|
||||
private Object getNetworkManager() throws ClassNotFoundException {
|
||||
NettyChannelInjector injectorContainer = (NettyChannelInjector) Accessors.getMethodAccessorOrNull(
|
||||
TemporaryPlayerFactory.class, "getInjectorFromPlayer", Player.class
|
||||
).invoke(null, player);
|
||||
private boolean checkVerifyToken(BukkitLoginSession session) throws GeneralSecurityException {
|
||||
byte[] requestVerify = session.getVerifyToken();
|
||||
//encrypted verify token
|
||||
byte[] responseVerify = packetEvent.getPacket().getByteArrays().read(1);
|
||||
|
||||
FieldAccessor accessor = Accessors.getFieldAccessorOrNull(
|
||||
NettyChannelInjector.class, "networkManager", Object.class
|
||||
);
|
||||
return accessor.get(injectorContainer);
|
||||
//https://github.com/bergerkiller/CraftSource/blob/master/net.minecraft.server/LoginListener.java#L182
|
||||
if (!Arrays.equals(requestVerify, EncryptionUtil.decrypt(serverKey.getPrivate(), responseVerify))) {
|
||||
//check if the verify-token are equal to the server sent one
|
||||
disconnect("invalid-verify-token", true
|
||||
, "GameProfile {0} ({1}) tried to login with an invalid verify token. Server: {2} Client: {3}"
|
||||
, session.getRequestUsername(), packetEvent.getPlayer().getAddress(), requestVerify, responseVerify);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//try to get the networkManager from ProtocolLib
|
||||
private Object getNetworkManager() throws IllegalAccessException, ClassNotFoundException {
|
||||
Object injectorContainer = TemporaryPlayerFactory.getInjectorFromPlayer(player);
|
||||
|
||||
// ChannelInjector
|
||||
Class<?> injectorClass = Class.forName("com.comphenix.protocol.injector.netty.Injector");
|
||||
Object rawInjector = FuzzyReflection.getFieldValue(injectorContainer, injectorClass, true);
|
||||
return FieldUtils.readField(rawInjector, "networkManager", true);
|
||||
}
|
||||
|
||||
private boolean enableEncryption(SecretKey loginKey) throws IllegalArgumentException {
|
||||
plugin.getLog().info("Enabling onlinemode encryption for {}", player.getAddress());
|
||||
plugin.getLog().info("Enabling onlinemode encryption for {}", player.getName());
|
||||
// Initialize method reflections
|
||||
if (encryptKeyMethod == null || encryptMethod == null) {
|
||||
if (encryptMethod == null) {
|
||||
Class<?> networkManagerClass = MinecraftReflection.getNetworkManagerClass();
|
||||
|
||||
try {
|
||||
// Try to get the old (pre MC 1.16.4) encryption method
|
||||
encryptKeyMethod = FuzzyReflection.fromClass(networkManagerClass)
|
||||
encryptMethod = FuzzyReflection.fromClass(networkManagerClass)
|
||||
.getMethodByParameters("a", SecretKey.class);
|
||||
} catch (IllegalArgumentException exception) {
|
||||
// Get the new encryption method
|
||||
encryptMethod = FuzzyReflection.fromClass(networkManagerClass)
|
||||
.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)
|
||||
cipherMethod = FuzzyReflection.fromClass(encryptionClass)
|
||||
cipherMethod = FuzzyReflection.fromClass(ENCRYPTION_CLASS)
|
||||
.getMethodByParameters("a", int.class, Key.class);
|
||||
}
|
||||
}
|
||||
@@ -256,9 +233,9 @@ public class VerifyResponseTask implements Runnable {
|
||||
Object networkManager = this.getNetworkManager();
|
||||
|
||||
// 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
|
||||
encryptKeyMethod.invoke(networkManager, loginKey);
|
||||
encryptMethod.invoke(networkManager, loginKey);
|
||||
} else {
|
||||
// Create ciphers from login key
|
||||
Object decryptionCipher = cipherMethod.invoke(null, Cipher.DECRYPT_MODE, loginKey);
|
||||
@@ -268,60 +245,53 @@ public class VerifyResponseTask implements Runnable {
|
||||
encryptMethod.invoke(networkManager, decryptionCipher, encryptionCipher);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
disconnect("error-kick", "Couldn't enable encryption", ex);
|
||||
disconnect("error-kick", false, "Couldn't enable encryption", ex);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void disconnect(String reasonKey, String logMessage, Object... arguments) {
|
||||
plugin.getLog().error(logMessage, arguments);
|
||||
private void disconnect(String reasonKey, boolean debug, String logMessage, Object... arguments) {
|
||||
if (debug) {
|
||||
plugin.getLog().debug(logMessage, arguments);
|
||||
} else {
|
||||
plugin.getLog().error(logMessage, arguments);
|
||||
}
|
||||
|
||||
kickPlayer(plugin.getCore().getMessage(reasonKey));
|
||||
}
|
||||
|
||||
private void kickPlayer(String reason) {
|
||||
PacketContainer kickPacket = new PacketContainer(DISCONNECT);
|
||||
kickPacket.getChatComponents().write(0, WrappedChatComponent.fromText(reason));
|
||||
//send kick packet at login state
|
||||
//the normal event.getPlayer.kickPlayer(String) method does only work at play state
|
||||
ProtocolLibrary.getProtocolManager().sendServerPacket(player, kickPacket);
|
||||
//tell the server that we want to close the connection
|
||||
player.kickPlayer("Disconnect");
|
||||
try {
|
||||
//send kick packet at login state
|
||||
//the normal event.getPlayer.kickPlayer(String) method does only work at play state
|
||||
ProtocolLibrary.getProtocolManager().sendServerPacket(player, kickPacket);
|
||||
//tell the server that we want to close the connection
|
||||
player.kickPlayer("Disconnect");
|
||||
} catch (InvocationTargetException ex) {
|
||||
plugin.getLog().error("Error sending kick packet for: {}", player, ex);
|
||||
}
|
||||
}
|
||||
|
||||
//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) {
|
||||
PacketContainer startPacket;
|
||||
if (new MinecraftVersion(1, 20, 2).atOrAbove()) {
|
||||
startPacket = new PacketContainer(START);
|
||||
startPacket.getStrings().write(0, username);
|
||||
startPacket.getUUIDs().write(0, uuid);
|
||||
} else if (new MinecraftVersion(1, 19, 3).atOrAbove()) {
|
||||
startPacket = new PacketContainer(START);
|
||||
startPacket.getStrings().write(0, username);
|
||||
startPacket.getOptionals(Converters.passthrough(UUID.class)).write(0, Optional.of(uuid));
|
||||
} else if (new MinecraftVersion(1, 19, 0).atOrAbove()) {
|
||||
startPacket = new PacketContainer(START);
|
||||
startPacket.getStrings().write(0, username);
|
||||
private void receiveFakeStartPacket(String username) {
|
||||
//see StartPacketListener for packet information
|
||||
PacketContainer startPacket = new PacketContainer(START);
|
||||
|
||||
EquivalentConverter<WrappedProfileKeyData> converter = BukkitConverters.getWrappedPublicKeyDataConverter();
|
||||
Optional<WrappedProfileKeyData> wrappedKey = Optional.ofNullable(clientKey).map(key ->
|
||||
new WrappedProfileKeyData(clientKey.expiry(), clientKey.key(), clientKey.signature())
|
||||
);
|
||||
|
||||
startPacket.getOptionals(converter).write(0, wrappedKey);
|
||||
} else {
|
||||
//uuid is ignored by the packet definition
|
||||
WrappedGameProfile fakeProfile = new WrappedGameProfile(UUID.randomUUID(), username);
|
||||
|
||||
Class<?> profileHandleType = fakeProfile.getHandleType();
|
||||
Class<?> packetHandleType = PacketRegistry.getPacketClassFromType(START);
|
||||
ConstructorAccessor startCons = Accessors.getConstructorAccessorOrNull(packetHandleType, profileHandleType);
|
||||
startPacket = new PacketContainer(START, startCons.invoke(fakeProfile.getHandle()));
|
||||
//uuid is ignored by the packet definition
|
||||
WrappedGameProfile fakeProfile = new WrappedGameProfile(UUID.randomUUID(), username);
|
||||
startPacket.getGameProfiles().write(0, fakeProfile);
|
||||
try {
|
||||
//we don't want to handle our own packets so ignore filters
|
||||
startPacket.setMeta(ProtocolLibListener.SOURCE_META_KEY, plugin.getName());
|
||||
ProtocolLibrary.getProtocolManager().recieveClientPacket(player, startPacket, true);
|
||||
} catch (InvocationTargetException | IllegalAccessException ex) {
|
||||
plugin.getLog().warn("Failed to fake a new start packet for: {}", username, ex);
|
||||
//cancel the event in order to prevent the server receiving an invalid packet
|
||||
kickPlayer(plugin.getCore().getMessage("error-kick"));
|
||||
}
|
||||
|
||||
//we don't want to handle our own packets so ignore filters
|
||||
ProtocolLibrary.getProtocolManager().receiveClientPacket(player, startPacket, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,73 +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.listener.protocollib.packet;
|
||||
|
||||
import java.security.PublicKey;
|
||||
import java.time.Instant;
|
||||
import java.util.Base64;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
public class ClientPublicKey {
|
||||
|
||||
private final Instant expiry;
|
||||
private final PublicKey key;
|
||||
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) {
|
||||
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)
|
||||
*
|
||||
* Copyright (c) 2015-2024 games647 and contributors
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -26,10 +26,11 @@
|
||||
package com.github.games647.fastlogin.bukkit.listener.protocolsupport;
|
||||
|
||||
import com.github.games647.fastlogin.core.shared.LoginSource;
|
||||
import protocolsupport.api.events.PlayerLoginStartEvent;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
|
||||
import protocolsupport.api.events.PlayerLoginStartEvent;
|
||||
|
||||
public class ProtocolLoginSource implements LoginSource {
|
||||
|
||||
private final PlayerLoginStartEvent loginStartEvent;
|
||||
@@ -50,7 +51,7 @@ public class ProtocolLoginSource implements LoginSource {
|
||||
|
||||
@Override
|
||||
public InetSocketAddress getAddress() {
|
||||
return loginStartEvent.getConnection().getRawAddress();
|
||||
return loginStartEvent.getAddress();
|
||||
}
|
||||
|
||||
public PlayerLoginStartEvent getLoginStartEvent() {
|
||||
@@ -59,8 +60,8 @@ public class ProtocolLoginSource implements LoginSource {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.getClass().getSimpleName() + '{'
|
||||
+ "loginStartEvent=" + loginStartEvent
|
||||
+ '}';
|
||||
return this.getClass().getSimpleName() + '{' +
|
||||
"loginStartEvent=" + loginStartEvent +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2024 games647 and contributors
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -29,11 +29,14 @@ import com.github.games647.craftapi.UUIDAdapter;
|
||||
import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
|
||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||
import com.github.games647.fastlogin.bukkit.event.BukkitFastLoginPreLoginEvent;
|
||||
import com.github.games647.fastlogin.core.antibot.AntiBotService;
|
||||
import com.github.games647.fastlogin.core.antibot.AntiBotService.Action;
|
||||
import com.github.games647.fastlogin.core.RateLimiter;
|
||||
import com.github.games647.fastlogin.core.StoredProfile;
|
||||
import com.github.games647.fastlogin.core.shared.JoinManagement;
|
||||
import com.github.games647.fastlogin.core.shared.event.FastLoginPreLoginEvent;
|
||||
import com.github.games647.fastlogin.core.storage.StoredProfile;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
@@ -42,20 +45,17 @@ import protocolsupport.api.events.ConnectionCloseEvent;
|
||||
import protocolsupport.api.events.PlayerLoginStartEvent;
|
||||
import protocolsupport.api.events.PlayerProfileCompleteEvent;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.Optional;
|
||||
|
||||
public class ProtocolSupportListener extends JoinManagement<Player, CommandSender, ProtocolLoginSource>
|
||||
implements Listener {
|
||||
|
||||
private final FastLoginBukkit plugin;
|
||||
private final AntiBotService antiBotService;
|
||||
private final RateLimiter rateLimiter;
|
||||
|
||||
public ProtocolSupportListener(FastLoginBukkit plugin, AntiBotService antiBotService) {
|
||||
public ProtocolSupportListener(FastLoginBukkit plugin, RateLimiter rateLimiter) {
|
||||
super(plugin.getCore(), plugin.getCore().getAuthPluginHook(), plugin.getBedrockService());
|
||||
|
||||
this.plugin = plugin;
|
||||
this.antiBotService = antiBotService;
|
||||
this.rateLimiter = rateLimiter;
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
@@ -64,44 +64,34 @@ public class ProtocolSupportListener extends JoinManagement<Player, CommandSende
|
||||
return;
|
||||
}
|
||||
|
||||
String username = loginStartEvent.getConnection().getProfile().getName();
|
||||
InetSocketAddress address = loginStartEvent.getConnection().getRawAddress();
|
||||
plugin.getLog().info("Incoming login request for {} from {}", username, address);
|
||||
|
||||
Action action = antiBotService.onIncomingConnection(address, username);
|
||||
switch (action) {
|
||||
case Ignore:
|
||||
// just ignore
|
||||
return;
|
||||
case Block:
|
||||
String message = plugin.getCore().getMessage("kick-antibot");
|
||||
loginStartEvent.denyLogin(message);
|
||||
break;
|
||||
case Continue:
|
||||
default:
|
||||
//remove old data every time on a new login in order to keep the session only for one person
|
||||
plugin.removeSession(address);
|
||||
|
||||
ProtocolLoginSource source = new ProtocolLoginSource(loginStartEvent);
|
||||
super.onLogin(username, source);
|
||||
break;
|
||||
if (!rateLimiter.tryAcquire()) {
|
||||
plugin.getLog().warn("Simple Anti-Bot join limit - Ignoring {}", loginStartEvent.getConnection());
|
||||
return;
|
||||
}
|
||||
|
||||
String username = loginStartEvent.getConnection().getProfile().getName();
|
||||
InetSocketAddress address = loginStartEvent.getAddress();
|
||||
|
||||
//remove old data every time on a new login in order to keep the session only for one person
|
||||
plugin.removeSession(address);
|
||||
|
||||
ProtocolLoginSource source = new ProtocolLoginSource(loginStartEvent);
|
||||
super.onLogin(username, source);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onConnectionClosed(ConnectionCloseEvent closeEvent) {
|
||||
InetSocketAddress address = closeEvent.getConnection().getRawAddress();
|
||||
InetSocketAddress address = closeEvent.getConnection().getAddress();
|
||||
plugin.removeSession(address);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPropertiesResolve(PlayerProfileCompleteEvent profileCompleteEvent) {
|
||||
InetSocketAddress address = profileCompleteEvent.getConnection().getRawAddress();
|
||||
|
||||
InetSocketAddress address = profileCompleteEvent.getAddress();
|
||||
BukkitLoginSession session = plugin.getSession(address);
|
||||
|
||||
if (session != null && profileCompleteEvent.getConnection().getProfile().isOnlineMode()) {
|
||||
session.setVerifiedPremium(true);
|
||||
session.setVerified(true);
|
||||
|
||||
if (!plugin.getConfig().getBoolean("premiumUuid")) {
|
||||
String username = Optional.ofNullable(profileCompleteEvent.getForcedName())
|
||||
@@ -112,20 +102,19 @@ public class ProtocolSupportListener extends JoinManagement<Player, CommandSende
|
||||
}
|
||||
|
||||
@Override
|
||||
public FastLoginPreLoginEvent callFastLoginPreLoginEvent(String username, ProtocolLoginSource source,
|
||||
StoredProfile profile) {
|
||||
public FastLoginPreLoginEvent callFastLoginPreLoginEvent(String username, ProtocolLoginSource source, StoredProfile profile) {
|
||||
BukkitFastLoginPreLoginEvent event = new BukkitFastLoginPreLoginEvent(username, source, profile);
|
||||
plugin.getServer().getPluginManager().callEvent(event);
|
||||
return event;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestPremiumLogin(ProtocolLoginSource source, StoredProfile profile, String username,
|
||||
boolean registered) {
|
||||
public void requestPremiumLogin(ProtocolLoginSource source, StoredProfile profile, String username
|
||||
, boolean registered) {
|
||||
source.enableOnlinemode();
|
||||
|
||||
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);
|
||||
plugin.putSession(source.getAddress(), playerSession);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2024 games647 and contributors
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -31,17 +31,18 @@ import com.github.games647.fastlogin.bukkit.hook.CrazyLoginHook;
|
||||
import com.github.games647.fastlogin.bukkit.hook.LogItHook;
|
||||
import com.github.games647.fastlogin.bukkit.hook.LoginSecurityHook;
|
||||
import com.github.games647.fastlogin.bukkit.hook.UltraAuthHook;
|
||||
import com.github.games647.fastlogin.bukkit.hook.XAuthHook;
|
||||
import com.github.games647.fastlogin.bukkit.hook.PasskyHook;
|
||||
import com.github.games647.fastlogin.bukkit.hook.xAuthHook;
|
||||
import com.github.games647.fastlogin.bukkit.hook.SodionAuthHook;
|
||||
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.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.Listener;
|
||||
|
||||
public class DelayedAuthHook implements Runnable {
|
||||
|
||||
private final FastLoginBukkit plugin;
|
||||
@@ -94,8 +95,8 @@ public class DelayedAuthHook implements Runnable {
|
||||
private AuthPlugin<Player> getAuthHook() {
|
||||
try {
|
||||
List<Class<? extends AuthPlugin<Player>>> hooks = Arrays.asList(AuthMeHook.class,
|
||||
CrazyLoginHook.class, LogItHook.class, LoginSecurityHook.class, UltraAuthHook.class,
|
||||
XAuthHook.class, PasskyHook.class);
|
||||
CrazyLoginHook.class, LogItHook.class, LoginSecurityHook.class,
|
||||
SodionAuthHook.class, UltraAuthHook.class, xAuthHook.class);
|
||||
|
||||
for (Class<? extends AuthPlugin<Player>> clazz : hooks) {
|
||||
String pluginName = clazz.getSimpleName();
|
||||
@@ -113,7 +114,7 @@ public class DelayedAuthHook implements Runnable {
|
||||
return null;
|
||||
}
|
||||
|
||||
protected AuthPlugin<Player> newInstance(Class<? extends AuthPlugin<Player>> clazz)
|
||||
private AuthPlugin<Player> newInstance(Class<? extends AuthPlugin<Player>> clazz)
|
||||
throws ReflectiveOperationException {
|
||||
try {
|
||||
Constructor<? extends AuthPlugin<Player>> cons = clazz.getDeclaredConstructor(FastLoginBukkit.class);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2024 games647 and contributors
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -25,22 +25,22 @@
|
||||
*/
|
||||
package com.github.games647.fastlogin.bukkit.task;
|
||||
|
||||
import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
|
||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||
import com.github.games647.fastlogin.core.shared.FastLoginCore;
|
||||
import com.github.games647.fastlogin.core.shared.FloodgateManagement;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.geysermc.floodgate.api.player.FloodgatePlayer;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.UUID;
|
||||
import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
|
||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||
import com.github.games647.fastlogin.core.shared.FastLoginCore;
|
||||
import com.github.games647.fastlogin.core.shared.FloodgateManagement;
|
||||
|
||||
public class FloodgateAuthTask extends FloodgateManagement<Player, CommandSender, BukkitLoginSession, FastLoginBukkit> {
|
||||
|
||||
public FloodgateAuthTask(FastLoginCore<Player, CommandSender, FastLoginBukkit> core, Player player,
|
||||
FloodgatePlayer floodgatePlayer) {
|
||||
public FloodgateAuthTask(FastLoginCore<Player, CommandSender, FastLoginBukkit> core, Player player, FloodgatePlayer floodgatePlayer) {
|
||||
super(core, player, floodgatePlayer);
|
||||
}
|
||||
|
||||
@@ -49,7 +49,8 @@ public class FloodgateAuthTask extends FloodgateManagement<Player, CommandSender
|
||||
BukkitLoginSession session = new BukkitLoginSession(player.getName(), isRegistered, profile);
|
||||
|
||||
// enable auto login based on the value of 'autoLoginFloodgate' in config.yml
|
||||
session.setVerifiedPremium(isAutoAuthAllowed(autoLoginFloodgate));
|
||||
session.setVerified(autoLoginFloodgate.equals("true")
|
||||
|| (autoLoginFloodgate.equals("linked") && isLinked));
|
||||
|
||||
// run login task
|
||||
Runnable forceLoginTask = new ForceLoginTask(core.getPlugin().getCore(), player, session);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2024 games647 and contributors
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -29,19 +29,20 @@ import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
|
||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||
import com.github.games647.fastlogin.bukkit.event.BukkitFastLoginAutoLoginEvent;
|
||||
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.shared.FastLoginCore;
|
||||
import com.github.games647.fastlogin.core.shared.ForceLoginManagement;
|
||||
import com.github.games647.fastlogin.core.shared.LoginSession;
|
||||
import com.github.games647.fastlogin.core.shared.event.FastLoginAutoLoginEvent;
|
||||
import com.github.games647.fastlogin.core.storage.StoredProfile;
|
||||
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.metadata.FixedMetadataValue;
|
||||
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
public class ForceLoginTask extends ForceLoginManagement<Player, CommandSender, BukkitLoginSession, FastLoginBukkit> {
|
||||
|
||||
public ForceLoginTask(FastLoginCore<Player, CommandSender, FastLoginBukkit> core, Player player,
|
||||
@@ -103,6 +104,6 @@ public class ForceLoginTask extends ForceLoginManagement<Player, CommandSender,
|
||||
|
||||
@Override
|
||||
public boolean isOnlineMode() {
|
||||
return session.isVerifiedPremium();
|
||||
return session.isVerified();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,12 +25,12 @@ softdepend:
|
||||
- floodgate
|
||||
# Auth plugins
|
||||
- AuthMe
|
||||
- CrazyLogin
|
||||
- LoginSecurity
|
||||
- SodionAuth
|
||||
- xAuth
|
||||
- LogIt
|
||||
- UltraAuth
|
||||
- xAuth
|
||||
- Passky
|
||||
- CrazyLogin
|
||||
|
||||
commands:
|
||||
${project.parent.name}:
|
||||
@@ -45,11 +45,6 @@ commands:
|
||||
usage: /<command> [player]
|
||||
permission: ${project.artifactId}.command.cracked
|
||||
|
||||
fldelete:
|
||||
description: 'Delete player profile data'
|
||||
usage: /<command> [player]
|
||||
permission: ${project.artifactId}.command.delete
|
||||
|
||||
permissions:
|
||||
${project.artifactId}.command.premium:
|
||||
description: 'Label themselves as premium'
|
||||
@@ -68,7 +63,3 @@ permissions:
|
||||
description: 'Label others as cracked'
|
||||
children:
|
||||
${project.artifactId}.command.cracked: true
|
||||
|
||||
${project.artifactId}.command.delete:
|
||||
description: 'Delete other players profile data'
|
||||
default: op
|
||||
Binary file not shown.
@@ -1,50 +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 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.chat.ComponentSerializer;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
class FastLoginBukkitTest {
|
||||
|
||||
@Test
|
||||
void testRGB() {
|
||||
String message = "&x00002a00002b&lText";
|
||||
String msg = CommonUtil.translateColorCodes(message);
|
||||
assertEquals(msg, "§x00002a00002b§lText");
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
BaseComponent[] components = TextComponent.fromLegacyText(msg);
|
||||
String expected = "{\"bold\":true,\"color\":\"#00a00b\",\"text\":\"Text\"}";
|
||||
//noinspection deprecation
|
||||
assertEquals(ComponentSerializer.toString(components), expected);
|
||||
}
|
||||
}
|
||||
@@ -1,43 +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 com.comphenix.protocol.reflect.accessors.Accessors;
|
||||
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
|
||||
import fr.xephi.authme.api.v3.AuthMeApi;
|
||||
import fr.xephi.authme.process.Management;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
|
||||
class ReflectionTest {
|
||||
|
||||
@Test
|
||||
void testAuthMeManagementField() {
|
||||
FieldAccessor accessor = Accessors.getFieldAccessor(AuthMeApi.class, Management.class, true);
|
||||
assertNotNull(accessor.getField());
|
||||
}
|
||||
}
|
||||
@@ -1,44 +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.comphenix.protocol.reflect.accessors.Accessors;
|
||||
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
|
||||
import de.st_ddt.crazylogin.CrazyLogin;
|
||||
import de.st_ddt.crazylogin.listener.PlayerListener;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
|
||||
class CrazyLoginHookTest {
|
||||
|
||||
@Test
|
||||
void testPlayerListener() {
|
||||
FieldAccessor accessor = Accessors.getFieldAccessor(CrazyLogin.class, PlayerListener.class, true);
|
||||
assertNotNull(accessor.getField());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,48 +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.listener.protocollib;
|
||||
|
||||
import com.google.gson.TypeAdapter;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Base64;
|
||||
|
||||
public class Base64Adapter extends TypeAdapter<byte[]> {
|
||||
|
||||
@Override
|
||||
public void write(JsonWriter out, byte[] value) throws IOException {
|
||||
String encoded = Base64.getEncoder().encodeToString(value);
|
||||
out.value(encoded);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] read(JsonReader in) throws IOException {
|
||||
String encoded = in.nextString();
|
||||
return Base64.getDecoder().decode(encoded);
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2024 games647 and contributors
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -25,275 +25,22 @@
|
||||
*/
|
||||
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.packet.ClientPublicKey;
|
||||
import com.google.common.hash.Hasher;
|
||||
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 java.security.SecureRandom;
|
||||
|
||||
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.nio.charset.StandardCharsets;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.Key;
|
||||
import java.security.KeyPair;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PublicKey;
|
||||
import java.security.SignatureException;
|
||||
import java.security.interfaces.RSAPublicKey;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Random;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.CoreMatchers.notNullValue;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
|
||||
class EncryptionUtilTest {
|
||||
public class EncryptionUtilTest {
|
||||
|
||||
@Test
|
||||
void testVerifyToken() {
|
||||
Random random = ThreadLocalRandom.current();
|
||||
public void testVerifyToken() {
|
||||
SecureRandom random = new SecureRandom();
|
||||
byte[] token = EncryptionUtil.generateVerifyToken(random);
|
||||
|
||||
assertAll(
|
||||
() -> assertNotNull(token),
|
||||
() -> assertEquals(token.length, 4)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testServerKey() {
|
||||
KeyPair keyPair = EncryptionUtil.generateKeyPair();
|
||||
|
||||
Key privateKey = keyPair.getPrivate();
|
||||
assertEquals(privateKey.getAlgorithm(), "RSA");
|
||||
|
||||
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
|
||||
assertEquals(publicKey.getAlgorithm(), "RSA");
|
||||
|
||||
// clients accept larger values than the standard vanilla server, but we shouldn't crash them
|
||||
assertAll(
|
||||
() -> assertTrue(publicKey.getModulus().bitLength() >= 1024),
|
||||
() -> assertTrue(publicKey.getModulus().bitLength() < 8192)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testExpiredClientKey() throws Exception {
|
||||
ClientPublicKey clientKey = ResourceLoader.loadClientKey("client_keys/valid_public_key.json");
|
||||
|
||||
// Client expires at the exact second mentioned, so use it for verification
|
||||
Instant expiredTimestamp = clientKey.expiry();
|
||||
assertFalse(EncryptionUtil.verifyClientKey(clientKey, expiredTimestamp, null));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = {
|
||||
// expiration date changed should make the signature invalid while still being not expired
|
||||
"client_keys/invalid_wrong_expiration.json",
|
||||
// changed public key no longer corresponding to the signature
|
||||
"client_keys/invalid_wrong_key.json",
|
||||
// signature modified no longer corresponding to key and expiration date
|
||||
"client_keys/invalid_wrong_signature.json"
|
||||
})
|
||||
void testInvalidClientKey(String clientKeySource) throws Exception {
|
||||
ClientPublicKey clientKey = ResourceLoader.loadClientKey(clientKeySource);
|
||||
Instant expireTimestamp = clientKey.expiry().minus(5, ChronoUnit.HOURS);
|
||||
|
||||
assertFalse(EncryptionUtil.verifyClientKey(clientKey, expireTimestamp, null));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testValidClientKey() throws Exception {
|
||||
ClientPublicKey clientKey = ResourceLoader.loadClientKey("client_keys/valid_public_key.json");
|
||||
Instant verificationTimestamp = clientKey.expiry().minus(5, ChronoUnit.HOURS);
|
||||
|
||||
assertTrue(EncryptionUtil.verifyClientKey(clientKey, verificationTimestamp, null));
|
||||
}
|
||||
|
||||
@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
|
||||
void testDecryptSharedSecret() throws Exception {
|
||||
KeyPair serverPair = EncryptionUtil.generateKeyPair();
|
||||
PublicKey serverPK = serverPair.getPublic();
|
||||
|
||||
SecretKey secretKey = generateSharedKey();
|
||||
byte[] encryptedSecret = encrypt(serverPK, secretKey.getEncoded());
|
||||
|
||||
SecretKey decryptSharedKey = EncryptionUtil.decryptSharedKey(serverPair.getPrivate(), encryptedSecret);
|
||||
assertEquals(decryptSharedKey, secretKey);
|
||||
}
|
||||
|
||||
private static byte[] encrypt(PublicKey receiverKey, byte... message)
|
||||
throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException,
|
||||
IllegalBlockSizeException, BadPaddingException {
|
||||
Cipher encryptCipher = Cipher.getInstance(receiverKey.getAlgorithm());
|
||||
encryptCipher.init(Cipher.ENCRYPT_MODE, receiverKey);
|
||||
return encryptCipher.doFinal(message);
|
||||
}
|
||||
|
||||
private static SecretKeySpec generateSharedKey() {
|
||||
// according to wiki.vg 16 bytes long
|
||||
byte[] sharedKey = new byte[16];
|
||||
ThreadLocalRandom.current().nextBytes(sharedKey);
|
||||
// shared key is to be used for the AES/CFB8 stream cipher to encrypt the traffic
|
||||
// therefore the encryption/decryption has to be AES
|
||||
return new SecretKeySpec(sharedKey, "AES");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testServerIdHash() throws Exception {
|
||||
String serverId = "";
|
||||
SecretKeySpec sharedSecret = generateSharedKey();
|
||||
PublicKey serverPK = ResourceLoader.loadClientKey("client_keys/valid_public_key.json").key();
|
||||
|
||||
String sessionHash = getServerHash(serverId, sharedSecret, serverPK);
|
||||
assertEquals(EncryptionUtil.getServerIdHashString(serverId, sharedSecret, serverPK), sessionHash);
|
||||
}
|
||||
|
||||
private static String getServerHash(@SuppressWarnings("SameParameterValue") CharSequence serverId,
|
||||
SecretKey sharedSecret, PublicKey serverPK) {
|
||||
// https://wiki.vg/Protocol_Encryption#Client
|
||||
// sha1 := Sha1()
|
||||
// sha1.update(ASCII encoding of the server id string from Encryption Request)
|
||||
// sha1.update(shared secret)
|
||||
// sha1.update(server's encoded public key from Encryption Request)
|
||||
// hash := sha1.hexdigest() # String of hex characters
|
||||
@SuppressWarnings("deprecation")
|
||||
Hasher hasher = Hashing.sha1().newHasher();
|
||||
hasher.putString(serverId, StandardCharsets.US_ASCII);
|
||||
hasher.putBytes(sharedSecret.getEncoded());
|
||||
hasher.putBytes(serverPK.getEncoded());
|
||||
// It works by treating the sha1 output bytes as one large integer in two's complement and then printing the
|
||||
// integer in base 16, placing a minus sign if the interpreted number is negative.
|
||||
// reference:
|
||||
// https://github.com/SpigotMC/BungeeCord/blob/ff5727c5ef9c0b56ad35f9816ae6bd660b622cf0/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java#L456
|
||||
return new BigInteger(hasher.hash().asBytes()).toString(16);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testServerIdHashWrongSecret() throws Exception {
|
||||
String serverId = "";
|
||||
SecretKeySpec sharedSecret = generateSharedKey();
|
||||
PublicKey serverPK = ResourceLoader.loadClientKey("client_keys/valid_public_key.json").key();
|
||||
|
||||
String sessionHash = getServerHash(serverId, sharedSecret, serverPK);
|
||||
assertNotEquals(EncryptionUtil.getServerIdHashString("", generateSharedKey(), serverPK), sessionHash);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testServerIdHashWrongServerKey() {
|
||||
String serverId = "";
|
||||
SecretKeySpec sharedSecret = generateSharedKey();
|
||||
PublicKey serverPK = EncryptionUtil.generateKeyPair().getPublic();
|
||||
|
||||
String sessionHash = getServerHash(serverId, sharedSecret, serverPK);
|
||||
PublicKey wrongPK = EncryptionUtil.generateKeyPair().getPublic();
|
||||
assertNotEquals(EncryptionUtil.getServerIdHashString("", sharedSecret, wrongPK), sessionHash);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testValidSignedNonce() throws Exception {
|
||||
ClientPublicKey clientKey = ResourceLoader.loadClientKey("client_keys/valid_public_key.json");
|
||||
SignatureTestData testData = SignatureTestData.fromResource("signature/valid_signature.json");
|
||||
assertTrue(verifySignedNonce(testData, clientKey));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = {
|
||||
"signature/incorrect_nonce.json",
|
||||
"signature/incorrect_salt.json",
|
||||
"signature/incorrect_signature.json",
|
||||
})
|
||||
void testIncorrectNonce(String signatureSource) throws Exception {
|
||||
ClientPublicKey clientKey = ResourceLoader.loadClientKey("client_keys/valid_public_key.json");
|
||||
SignatureTestData testData = SignatureTestData.fromResource(signatureSource);
|
||||
assertFalse(verifySignedNonce(testData, clientKey));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testWrongPublicKeySigned() throws Exception {
|
||||
// load a different public key
|
||||
ClientPublicKey clientKey = ResourceLoader.loadClientKey("client_keys/invalid_wrong_key.json");
|
||||
SignatureTestData testData = SignatureTestData.fromResource("signature/valid_signature.json");
|
||||
assertFalse(verifySignedNonce(testData, clientKey));
|
||||
}
|
||||
|
||||
private static boolean verifySignedNonce(SignatureTestData testData, ClientPublicKey clientKey)
|
||||
throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
|
||||
PublicKey clientPublicKey = clientKey.key();
|
||||
|
||||
byte[] nonce = testData.getNonce();
|
||||
SignatureData signature = testData.getSignature();
|
||||
long salt = signature.getSalt();
|
||||
return EncryptionUtil.verifySignedNonce(nonce, clientPublicKey, salt, signature.getSignature());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNonce() throws Exception {
|
||||
byte[] expected = {1, 2, 3, 4};
|
||||
KeyPair serverKey = EncryptionUtil.generateKeyPair();
|
||||
byte[] encryptedNonce = encrypt(serverKey.getPublic(), expected);
|
||||
|
||||
assertTrue(EncryptionUtil.verifyNonce(expected, serverKey.getPrivate(), encryptedNonce));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNonceIncorrect() throws Exception {
|
||||
byte[] expected = {1, 2, 3, 4};
|
||||
KeyPair serverKey = EncryptionUtil.generateKeyPair();
|
||||
|
||||
// flipped first character
|
||||
byte[] encryptedNonce = encrypt(serverKey.getPublic(), new byte[]{0, 2, 3, 4});
|
||||
assertFalse(EncryptionUtil.verifyNonce(expected, serverKey.getPrivate(), encryptedNonce));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNonceFailedDecryption() throws Exception {
|
||||
byte[] expected = {1, 2, 3, 4};
|
||||
KeyPair serverKey = EncryptionUtil.generateKeyPair();
|
||||
// generate a new keypair that is different
|
||||
byte[] encryptedNonce = encrypt(EncryptionUtil.generateKeyPair().getPublic(), expected);
|
||||
|
||||
assertThrows(GeneralSecurityException.class,
|
||||
() -> EncryptionUtil.verifyNonce(expected, serverKey.getPrivate(), encryptedNonce)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNonceIncorrectEmpty() {
|
||||
byte[] expected = {1, 2, 3, 4};
|
||||
KeyPair serverKey = EncryptionUtil.generateKeyPair();
|
||||
byte[] encryptedNonce = {};
|
||||
|
||||
assertThrows(GeneralSecurityException.class,
|
||||
() -> EncryptionUtil.verifyNonce(expected, serverKey.getPrivate(), encryptedNonce)
|
||||
);
|
||||
assertThat(token, notNullValue());
|
||||
assertThat(token.length, is(4));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,97 +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.listener.protocollib;
|
||||
|
||||
import com.github.games647.fastlogin.bukkit.listener.protocollib.packet.ClientPublicKey;
|
||||
import com.google.common.io.Resources;
|
||||
import com.google.gson.Gson;
|
||||
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.Reader;
|
||||
import java.io.StringReader;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.interfaces.RSAPrivateKey;
|
||||
import java.security.interfaces.RSAPublicKey;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.security.spec.KeySpec;
|
||||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
import java.security.spec.X509EncodedKeySpec;
|
||||
import java.time.Instant;
|
||||
import java.util.Base64;
|
||||
|
||||
public class ResourceLoader {
|
||||
|
||||
public static RSAPrivateKey parsePrivateKey(String keySpec)
|
||||
throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
|
||||
try (
|
||||
Reader reader = new StringReader(keySpec);
|
||||
PemReader pemReader = new PemReader(reader)
|
||||
) {
|
||||
PemObject pemObject = pemReader.readPemObject();
|
||||
byte[] content = pemObject.getContent();
|
||||
KeySpec privateKeySpec = new PKCS8EncodedKeySpec(content);
|
||||
|
||||
KeyFactory factory = KeyFactory.getInstance("RSA");
|
||||
return (RSAPrivateKey) factory.generatePrivate(privateKeySpec);
|
||||
}
|
||||
}
|
||||
|
||||
protected static ClientPublicKey loadClientKey(String path)
|
||||
throws NoSuchAlgorithmException, IOException, InvalidKeySpecException {
|
||||
URL keyUrl = Resources.getResource(path);
|
||||
|
||||
String lines = Resources.toString(keyUrl, StandardCharsets.US_ASCII);
|
||||
JsonObject object = new Gson().fromJson(lines, JsonObject.class);
|
||||
|
||||
Instant expires = Instant.parse(object.getAsJsonPrimitive("expires_at").getAsString());
|
||||
String key = object.getAsJsonPrimitive("key").getAsString();
|
||||
RSAPublicKey publicKey = parsePublicKey(key);
|
||||
|
||||
byte[] signature = Base64.getDecoder().decode(object.getAsJsonPrimitive("signature").getAsString());
|
||||
return ClientPublicKey.of(expires, publicKey, signature);
|
||||
}
|
||||
|
||||
private static RSAPublicKey parsePublicKey(String keySpec)
|
||||
throws IOException, InvalidKeySpecException, NoSuchAlgorithmException {
|
||||
try (
|
||||
Reader reader = new StringReader(keySpec);
|
||||
PemReader pemReader = new PemReader(reader)
|
||||
) {
|
||||
PemObject pemObject = pemReader.readPemObject();
|
||||
byte[] content = pemObject.getContent();
|
||||
KeySpec pubKeySpec = new X509EncodedKeySpec(content);
|
||||
|
||||
KeyFactory factory = KeyFactory.getInstance("RSA");
|
||||
return (RSAPublicKey) factory.generatePublic(pubKeySpec);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,73 +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.listener.protocollib;
|
||||
|
||||
import com.google.common.io.Resources;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.annotations.JsonAdapter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
public class SignatureTestData {
|
||||
|
||||
public static SignatureTestData fromResource(String resourceName) throws IOException {
|
||||
URL keyUrl = Resources.getResource(resourceName);
|
||||
String encodedSignature = Resources.toString(keyUrl, StandardCharsets.US_ASCII);
|
||||
|
||||
return new Gson().fromJson(encodedSignature, SignatureTestData.class);
|
||||
}
|
||||
|
||||
@JsonAdapter(Base64Adapter.class)
|
||||
private byte[] nonce;
|
||||
|
||||
private SignatureData signature;
|
||||
|
||||
public byte[] getNonce() {
|
||||
return nonce;
|
||||
}
|
||||
|
||||
public SignatureData getSignature() {
|
||||
return signature;
|
||||
}
|
||||
|
||||
public static class SignatureData {
|
||||
|
||||
private long salt;
|
||||
|
||||
@JsonAdapter(Base64Adapter.class)
|
||||
private byte[] signature;
|
||||
|
||||
public long getSalt() {
|
||||
return salt;
|
||||
}
|
||||
|
||||
public byte[] getSignature() {
|
||||
return signature;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,66 +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.listener.protocollib;
|
||||
|
||||
import com.comphenix.protocol.injector.packet.PacketRegistry;
|
||||
import com.comphenix.protocol.reflect.accessors.Accessors;
|
||||
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
|
||||
import com.comphenix.protocol.utility.MinecraftReflection;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.MockedStatic;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.mockStatic;
|
||||
|
||||
class VerifyResponseTaskTest {
|
||||
|
||||
private static final String NETTY_INJECTOR_CLASS =
|
||||
"com.comphenix.protocol.injector.netty.channel.NettyChannelInjector";
|
||||
|
||||
@Test
|
||||
void getNetworkManagerReflection() throws ClassNotFoundException {
|
||||
try (
|
||||
MockedStatic<Bukkit> bukkitMock = mockStatic(Bukkit.class);
|
||||
MockedStatic<MinecraftReflection> reflectionMock = mockStatic(MinecraftReflection.class);
|
||||
MockedStatic<PacketRegistry> registryMock = mockStatic(PacketRegistry.class)
|
||||
) {
|
||||
bukkitMock.when(Bukkit::getVersion).thenReturn("git-Bukkit-18fbb24 (MC: 1.17)");
|
||||
reflectionMock.when(MinecraftReflection::getMinecraftPackage).thenReturn("xyz");
|
||||
reflectionMock.when(MinecraftReflection::getEnumProtocolClass).thenReturn(Object.class);
|
||||
|
||||
registryMock.when(() -> PacketRegistry.tryGetPacketClass(any())).thenReturn(Optional.empty());
|
||||
|
||||
|
||||
Class<?> injectorClass = Class.forName(NETTY_INJECTOR_CLASS);
|
||||
FieldAccessor accessor = Accessors.getFieldAccessorOrNull(injectorClass, "networkManager", Object.class);
|
||||
assertNotNull(accessor.getField());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,59 +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.task;
|
||||
|
||||
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
|
||||
class DelayedAuthHookTest {
|
||||
|
||||
@Test
|
||||
void createNewReflectiveInstance() throws ReflectiveOperationException {
|
||||
DelayedAuthHook authHook = new DelayedAuthHook(null);
|
||||
assertNotNull(authHook.newInstance(DummyHook.class));
|
||||
}
|
||||
|
||||
public static class DummyHook implements AuthPlugin<Player> {
|
||||
|
||||
@Override
|
||||
public boolean forceLogin(Player player) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean forceRegister(Player player, String password) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRegistered(String playerName) throws Exception {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
218
bukkit/src/test/java/integration/LoginIT.java
Normal file
218
bukkit/src/test/java/integration/LoginIT.java
Normal file
@@ -0,0 +1,218 @@
|
||||
package integration;
|
||||
|
||||
import com.github.steveice10.mc.protocol.MinecraftProtocol;
|
||||
import com.github.steveice10.packetlib.Session;
|
||||
import com.github.steveice10.packetlib.event.session.DisconnectedEvent;
|
||||
import com.github.steveice10.packetlib.event.session.SessionAdapter;
|
||||
import com.github.steveice10.packetlib.packet.Packet;
|
||||
import com.github.steveice10.packetlib.tcp.TcpClientSession;
|
||||
import com.google.common.io.CharStreams;
|
||||
import com.mojang.authlib.EnvironmentParser;
|
||||
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.mockserver.client.MockServerClient;
|
||||
import org.mockserver.model.HttpRequest;
|
||||
import org.mockserver.verify.VerificationTimes;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.testcontainers.containers.GenericContainer;
|
||||
import org.testcontainers.containers.MockServerContainer;
|
||||
import org.testcontainers.containers.output.Slf4jLogConsumer;
|
||||
import org.testcontainers.containers.wait.strategy.Wait;
|
||||
import org.testcontainers.utility.DockerImageName;
|
||||
import org.testcontainers.utility.MountableFile;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.mockserver.model.HttpRequest.request;
|
||||
import static org.mockserver.model.HttpResponse.response;
|
||||
|
||||
// Warning name is sensitive to the surefire plugin
|
||||
public class LoginIT {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(LoginIT.class);
|
||||
|
||||
private static final String API_TAG = "mockserver-5.11.2";
|
||||
private static final String API_IMAGE_NAME = "mockserver/mockserver";
|
||||
private static final String API_IMAGE = API_IMAGE_NAME + ':' + API_TAG;
|
||||
|
||||
@Rule
|
||||
public MockServerContainer mockServer = new MockServerContainer(DockerImageName.parse(API_IMAGE))
|
||||
.withReuse(true);
|
||||
|
||||
private static final String SERVER_TAG = "1.18.1@sha256:dd3c8d212de585ec73113a0c0c73ac755ec1ff53e65bb09089061584fac02053";
|
||||
private static final String SERVER_IMAGE_NAME = "ghcr.io/games647/paperclip";
|
||||
private static final String SERVER_IMAGE = SERVER_IMAGE_NAME + ':' + SERVER_TAG;
|
||||
|
||||
private static final String HOME_FOLDER = "/home/nonroot/";
|
||||
|
||||
private static final long MINECRAFT_MAX_MEMORY = 1024 * 1024 * 1024L;
|
||||
|
||||
@Rule
|
||||
public GenericContainer<?> minecraftServer = new GenericContainer<>(DockerImageName.parse(SERVER_IMAGE))
|
||||
.withEnv("JDK_JAVA_OPTIONS", buildJVMFlags())
|
||||
.withExposedPorts(25565)
|
||||
// use server settings that use minimal minecraft log to quickly ramp up the server
|
||||
.withCopyFileToContainer(MountableFile.forClasspathResource("server.properties"), HOME_FOLDER + "server.properties")
|
||||
.withCopyFileToContainer(MountableFile.forClasspathResource("bukkit.yml"), HOME_FOLDER + "bukkit.yml")
|
||||
.withCopyFileToContainer(MountableFile.forClasspathResource("spigot.yml"), HOME_FOLDER + "spigot.yml")
|
||||
// create folders that are volatile
|
||||
.withTmpFs(getTempFS())
|
||||
// Done (XXXXs)! For help, type "help"
|
||||
.waitingFor(
|
||||
Wait.forLogMessage(".*For help, type \"help\"*\\n", 1)
|
||||
)
|
||||
.withReuse(true)
|
||||
.withLogConsumer(new Slf4jLogConsumer(LOG))
|
||||
.withCreateContainerCmdModifier(cmd -> cmd.getHostConfig().withMemory(MINECRAFT_MAX_MEMORY));
|
||||
|
||||
private Map<String, String> getTempFS() {
|
||||
Map<String, String> tmpfs = new HashMap<>();
|
||||
tmpfs.put(HOME_FOLDER + "world", "rw,noexec,nosuid,nodev");
|
||||
tmpfs.put(HOME_FOLDER + "logs", "rw,noexec,nosuid,nodev");
|
||||
return tmpfs;
|
||||
}
|
||||
|
||||
private String buildJVMFlags() {
|
||||
Map<String, String> systemProperties = new HashMap<>();
|
||||
systemProperties.put("com.mojang.eula.agree", Boolean.toString(true));
|
||||
|
||||
// set the Yggdrasil hosts that will also be used by the vanilla server
|
||||
systemProperties.put(EnvironmentParser.PROP_ACCOUNT_HOST, getProxyHost());
|
||||
systemProperties.put(EnvironmentParser.PROP_SESSION_HOST, getProxyHost());
|
||||
|
||||
return systemProperties.entrySet().stream()
|
||||
.map(entry -> "-D" + entry.getKey() + '=' + entry.getValue())
|
||||
.collect(Collectors.joining(" ")) + " -client";
|
||||
}
|
||||
|
||||
@Test
|
||||
public void checkRunning() throws Exception {
|
||||
assertThat(minecraftServer.isRunning(), is(true));
|
||||
|
||||
String host = minecraftServer.getHost();
|
||||
int port = minecraftServer.getMappedPort(25565);
|
||||
Session clientSession = new TcpClientSession(host, port, new MinecraftProtocol());
|
||||
try {
|
||||
CompletableFuture<Boolean> connectionResult = new CompletableFuture<>();
|
||||
clientSession.addListener(new SessionAdapter() {
|
||||
@Override
|
||||
public void packetReceived(Session session, Packet packet) {
|
||||
LOG.info("Client received: {}", packet.getClass());
|
||||
connectionResult.complete(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnected(DisconnectedEvent event) {
|
||||
connectionResult.complete(false);
|
||||
}
|
||||
});
|
||||
|
||||
clientSession.connect();
|
||||
assertThat(connectionResult.get(2, TimeUnit.SECONDS), is(true));
|
||||
} finally {
|
||||
clientSession.disconnect("Status test complete.");
|
||||
}
|
||||
}
|
||||
|
||||
private String getProxyHost() {
|
||||
return String.format("https://%s:%d", mockServer.getHost(), mockServer.getServerPort());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void autoRegisterNewUser() throws Exception {
|
||||
assertThat(mockServer.isRunning(), is(true));
|
||||
|
||||
try (MockServerClient client = new MockServerClient(mockServer.getHost(), mockServer.getServerPort())) {
|
||||
HttpRequest profileReq = request("/users/profiles/minecraft/" + "username");
|
||||
HttpRequest hasJoinedReq = request()
|
||||
.withPath("/session/minecraft/hasJoined")
|
||||
.withQueryStringParameter("username", "")
|
||||
.withQueryStringParameter("serverId", "")
|
||||
.withQueryStringParameter("ip", "");
|
||||
|
||||
// check call network request times
|
||||
client.verify(profileReq, VerificationTimes.once());
|
||||
client.verify(hasJoinedReq, VerificationTimes.once());
|
||||
|
||||
// Verify order
|
||||
client.verify(profileReq, hasJoinedReq);
|
||||
|
||||
client
|
||||
.when(request()
|
||||
.withPath("/users/profiles/minecraft/" + "username"))
|
||||
.respond(response()
|
||||
.withBody("bla"));
|
||||
|
||||
client
|
||||
.when(hasJoinedReq)
|
||||
.respond(response()
|
||||
.withBody("Test"));
|
||||
|
||||
URLConnection urlConnection = new URL(mockServer.getEndpoint() + "/users/profiles/minecraft/username").openConnection();
|
||||
String out = CharStreams.toString(new InputStreamReader(urlConnection.getInputStream(), StandardCharsets.UTF_8));
|
||||
LOG.info("OUTPUT: {}", out);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void failedJoinedVerification() {
|
||||
// has joined fails
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void offlineLoginNewUserDisabledRegister() {
|
||||
// auto register disabled, always offline login for new users
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void offlineLoginNewUser() {
|
||||
// auto register enabled, but no paid account
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void autoLoginRegistered() {
|
||||
// registered premium user and paid account login in
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void failedLoginPremiumRegistered() {
|
||||
// registered premium, but tried offline login
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void offlineLoginRegistered() {
|
||||
// assume registered user marked as offline - tried to login
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void alreadyOnlineDuplicateOwner() {
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void alreadyOnlineDuplicateCracked() {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
# Integration tests for authentication
|
||||
|
||||
## Description
|
||||
|
||||
Projects require integration tests in order to check against errors that could only occur if connected to other
|
||||
components. However, they are heavier in terms of performance and require a more complex setup. Unit tests often make
|
||||
use of fake, mock, stubs, etc. implementations to test the unit in isolation and thus could hide issues across
|
||||
boundaries of a unit. Nevertheless, both are not replacement for each other.
|
||||
|
||||
## Usage in this project
|
||||
|
||||
The authentication system is a core component, so it requires some kind of testing. Here we are going to
|
||||
spin up a Spigot server and test with the supported authentication schemes against the implementation of MCProtocolLib.
|
||||
|
||||
### Goals
|
||||
|
||||
* OS platform independent
|
||||
* Reproducible, but not fixed to a specific image hash
|
||||
* This is a dev container, so fixing it to feature/major version is enough instead of a version fixed by hash
|
||||
* Improve container spin up
|
||||
* E.g. Remove/Reduce world generation
|
||||
|
||||
### Note on automation
|
||||
|
||||
The simplest solution it to use the official Mojang session and authentication servers. However, this would require
|
||||
a spare Minecraft account. Mocking the auth servers would be a solution to avoid this.
|
||||
|
||||
## Related
|
||||
|
||||
Interest blog article about integration tests and why they are necessary.
|
||||
https://software.rajivprab.com/2019/04/28/rethinking-software-testing-perspectives-from-the-world-of-hardware/
|
||||
|
||||
## Issues
|
||||
|
||||
### Slow startup
|
||||
|
||||
Tried a lot of optimizations like only loading a single world without the nether or the end. However, there the startup
|
||||
is still slow. If you have any ideas on how to tune the startup parameters of the Minecraft server or the JVM
|
||||
itself to reduce the startup time, please suggest it.
|
||||
|
||||
### Checkpoint
|
||||
|
||||
An idea to optimize the time is to use CRIU (checkpoint and restore). So to save the process at a certain stage and
|
||||
restore all data multiple times. This could cause a lot of issues like open files have to be present. However, the
|
||||
impact is significant and since it runs inside the container all files, pids (pid=1) should be matching. Potential
|
||||
checkpoint locations are:
|
||||
|
||||
* Direct before loading the plugins
|
||||
* Likely before binding the port to prevent issues
|
||||
* After loading the libraries
|
||||
|
||||
Nevertheless, the current state requires to run it with root and the Java support is currently still in progress.
|
||||
|
||||
29
bukkit/src/test/resources/bukkit.yml
Normal file
29
bukkit/src/test/resources/bukkit.yml
Normal file
@@ -0,0 +1,29 @@
|
||||
settings:
|
||||
allow-end: false
|
||||
warn-on-overload: true
|
||||
permissions-file: permissions.yml
|
||||
update-folder: update
|
||||
plugin-profiling: false
|
||||
connection-throttle: 4000
|
||||
query-plugins: false
|
||||
deprecated-verbose: default
|
||||
shutdown-message: Server closed
|
||||
minimum-api: none
|
||||
spawn-limits:
|
||||
monsters: 70
|
||||
animals: 10
|
||||
water-animals: 5
|
||||
water-ambient: 20
|
||||
water-underground-creature: 5
|
||||
ambient: 15
|
||||
chunk-gc:
|
||||
period-in-ticks: 600
|
||||
ticks-per:
|
||||
animal-spawns: 400
|
||||
monster-spawns: 1
|
||||
water-spawns: 1
|
||||
water-ambient-spawns: 1
|
||||
water-underground-creature-spawns: 1
|
||||
ambient-spawns: 1
|
||||
autosave: 6000
|
||||
aliases: now-in-commands.yml
|
||||
@@ -1,27 +0,0 @@
|
||||
# About
|
||||
|
||||
This contains test resources for the unit tests. The files are extracted from the Minecraft directory with slight
|
||||
modifications. The files are found in `$MINECRAFT_HOME$/profilekeys/`, where `$MINECRAFT_HOME$` represents the
|
||||
OS-dependent minecraft folder.
|
||||
|
||||
**Notable the files in this folder do not contain the private key information. It should be explicitly
|
||||
stripped before including it.**
|
||||
|
||||
## Minecraft folder
|
||||
|
||||
* Windows: `%appdata%\.minecraft`
|
||||
* Linux: `/home/username/.minecraft`
|
||||
* Mac: `~/Library/Application Support/minecraft`
|
||||
|
||||
## Directory structure
|
||||
|
||||
* `valid_public_key.json`: Extracted from the actual file
|
||||
* `invalid_wrong_expiration.json`: Changed the expiration date
|
||||
* `invalid_wrong_key.json`: Modified public key while keeping the RSA structure valid
|
||||
* `invalid_wrong_signature.json`: Changed a character in the public key signature
|
||||
|
||||
## File content
|
||||
|
||||
* `expires_at`: Expiration date
|
||||
* `key`: Public key from the original file out of `public_key.key`
|
||||
* `signature`: Mojang signed signature of this public key
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"expires_at": "2022-06-20T07:31:47.318722344Z",
|
||||
"key": "-----BEGIN RSA PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApd3ZxDhcRWWru1XEBke6uYqmbnS2Oxyk\nOMj+QDKrkwUqhVJYciyXGsMx46Mgr/KIoGCcokP5OtIxc6+69/ZLqJ9PvM81kLIxAqyvfBMKMGjP\n376LgxTF1FeDpbe5zXaNRxfmnvQhS5YTLbzgk36qWVjqxJMG4VLVmh7RV5zWsb7XlckZb2zRHM2Y\nMHbEC+ggX+l6zQyfG1KK0MH5k+O6b0xD0rv1wm24sLOesTXH6RZG8cNE3ofdnavxjFodTOnra6w1\naiVcoUTdEPSS86wQwq9j0YCcAKOwMXsqbk9NhpujrdyJ94dev+ELwkNS7P0pPrcfiyFTQeJCZTXz\nJB36MwIDAQAB\n-----END RSA PUBLIC KEY-----\n",
|
||||
"signature": "lfRXK4zL213wBKg760eiPV7yvnLZ6a6v9Iohmw78yxIzqXO3tfrC5Z+P2LGiO1BdI4xckx8yz4ktn82zX97+r2zktBw0As7g71H/FjInpoZ76j3gMUaiFNrQJ0vKCCI7xsjonemroWAVDCAqlvdyqwUu/Fnz85+WoR2kCQ721vwy6IjWA3xhq8XrWjkI/AlBmoS/kVqnvjjjc9vocdddJXbUYzCse/hWWIbsFeBXyiGCd3v7apgtXwQfM++tt87fq7444zQskiYb14oQP8/uNwqZWQ9jAs00i1BZ0MNM6+TZYGHOfS6rbHZ1bcX34VZdcCwpapK/Z2HBRIgDN4QOcgJkyq1GcjvlM2wjfhN8gXTsmbF9Ee+5Y6a4ONRkxRZK2sT8oAXdm0OlTEGB0P0+WRRFOQ/PnRqbI7lvANao2METT2EUHHrtqFMe53kqCHdzy5qyuHxdCEa6l/gSR08fybx9DdRRmhOlhSPGxhgwqyi1fEMrN4CsSKNrv5u+sMqhspA05b3DQJeLDX+UV5ujRHwm0A49NF+h1ZYlrcefz5IMUUisOOw6HiLc/YGLD2jHwSePGdfMwMnrIxbxjCta7/7A91aaN7eYm16KW9erCOwAfJmBSQC6Pbmg5f7rd7rAKVOPxglq7nayXmd+BK53Mal5tltMSgd/0iY6SEtGSEU="
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"expires_at": "2022-06-20T08:31:47.318722344Z",
|
||||
"key": "-----BEGIN RSA PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoOv23jt2QPyab6bPRBwH2ggmzQU3I+xmDpi3X5ZB5Em/4uzyZqNVLJc0gShpk0XsdoB28Nq1bPxczOTBxuXi3rg5ax5gL+iymDSU27DLM8s/33lOofzGPJUEQGKlFm0QelDKZ/q5Y/9inHE3hEJKf7h0tnmGahXFmZSF/nRz9GHnfSYpjtDr9bsZOzQuLhHXT5E4ksNRTFW41h0MlZ1qOhO+NiiVgk7LmgVYiV7RRbgO8U6RaCEqg5n28Ewo6QtzB+DF4NTDeu3E9BLH5G0npdUrVNhdRUWCFDmH6n9hqSIz2J7o6GvWqEvp0h9e/3qtLsoS60hnQXunrcWcPaEIYQIDAQAB\n-----END RSA PUBLIC KEY-----\n",
|
||||
"signature": "lfRXK4zL213wBKg760eiPV7yvnLZ6a6v9Iohmw78yxIzqXO3tfrC5Z+P2LGiO1BdI4xckx8yz4ktn82zX97+r2zktBw0As7g71H/FjInpoZ76j3gMUaiFNrQJ0vKCCI7xsjonemroWAVDCAqlvdyqwUu/Fnz85+WoR2kCQ721vwy6IjWA3xhq8XrWjkI/AlBmoS/kVqnvjjjc9vocdddJXbUYzCse/hWWIbsFeBXyiGCd3v7apgtXwQfM++tt87fq7444zQskiYb14oQP8/uNwqZWQ9jAs00i1BZ0MNM6+TZYGHOfS6rbHZ1bcX34VZdcCwpapK/Z2HBRIgDN4QOcgJkyq1GcjvlM2wjfhN8gXTsmbF9Ee+5Y6a4ONRkxRZK2sT8oAXdm0OlTEGB0P0+WRRFOQ/PnRqbI7lvANao2METT2EUHHrtqFMe53kqCHdzy5qyuHxdCEa6l/gSR08fybx9DdRRmhOlhSPGxhgwqyi1fEMrN4CsSKNrv5u+sMqhspA05b3DQJeLDX+UV5ujRHwm0A49NF+h1ZYlrcefz5IMUUisOOw6HiLc/YGLD2jHwSePGdfMwMnrIxbxjCta7/7A91aaN7eYm16KW9erCOwAfJmBSQC6Pbmg5f7rd7rAKVOPxglq7nayXmd+BK53Mal5tltMSgd/0iY6SEtGSEU="
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"expires_at": "2022-06-20T08:31:47.318722344Z",
|
||||
"key": "-----BEGIN RSA PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApd3ZxDhcRWWru1XEBke6uYqmbnS2Oxyk\nOMj+QDKrkwUqhVJYciyXGsMx46Mgr/KIoGCcokP5OtIxc6+69/ZLqJ9PvM81kLIxAqyvfBMKMGjP\n376LgxTF1FeDpbe5zXaNRxfmnvQhS5YTLbzgk36qWVjqxJMG4VLVmh7RV5zWsb7XlckZb2zRHM2Y\nMHbEC+ggX+l6zQyfG1KK0MH5k+O6b0xD0rv1wm24sLOesTXH6RZG8cNE3ofdnavxjFodTOnra6w1\naiVcoUTdEPSS86wQwq9j0YCcAKOwMXsqbk9NhpujrdyJ94dev+ELwkNS7P0pPrcfiyFTQeJCZTXz\nJB36MwIDAQAB\n-----END RSA PUBLIC KEY-----\n",
|
||||
"signature": "lfRxK4zL213wBKg760eiPV7yvnLZ6a6v9Iohmw78yxIzqXO3tfrC5Z+P2LGiO1BdI4xckx8yz4ktn82zX97+r2zktBw0As7g71H/FjInpoZ76j3gMUaiFNrQJ0vKCCI7xsjonemroWAVDCAqlvdyqwUu/Fnz85+WoR2kCQ721vwy6IjWA3xhq8XrWjkI/AlBmoS/kVqnvjjjc9vocdddJXbUYzCse/hWWIbsFeBXyiGCd3v7apgtXwQfM++tt87fq7444zQskiYb14oQP8/uNwqZWQ9jAs00i1BZ0MNM6+TZYGHOfS6rbHZ1bcX34VZdcCwpapK/Z2HBRIgDN4QOcgJkyq1GcjvlM2wjfhN8gXTsmbF9Ee+5Y6a4ONRkxRZK2sT8oAXdm0OlTEGB0P0+WRRFOQ/PnRqbI7lvANao2METT2EUHHrtqFMe53kqCHdzy5qyuHxdCEa6l/gSR08fybx9DdRRmhOlhSPGxhgwqyi1fEMrN4CsSKNrv5u+sMqhspA05b3DQJeLDX+UV5ujRHwm0A49NF+h1ZYlrcefz5IMUUisOOw6HiLc/YGLD2jHwSePGdfMwMnrIxbxjCta7/7A91aaN7eYm16KW9erCOwAfJmBSQC6Pbmg5f7rd7rAKVOPxglq7nayXmd+BK53Mal5tltMSgd/0iY6SEtGSEU="
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"expires_at": "2022-06-20T08:31:47.318722344Z",
|
||||
"key": "-----BEGIN RSA PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApd3ZxDhcRWWru1XEBke6uYqmbnS2Oxyk\nOMj+QDKrkwUqhVJYciyXGsMx46Mgr/KIoGCcokP5OtIxc6+69/ZLqJ9PvM81kLIxAqyvfBMKMGjP\n376LgxTF1FeDpbe5zXaNRxfmnvQhS5YTLbzgk36qWVjqxJMG4VLVmh7RV5zWsb7XlckZb2zRHM2Y\nMHbEC+ggX+l6zQyfG1KK0MH5k+O6b0xD0rv1wm24sLOesTXH6RZG8cNE3ofdnavxjFodTOnra6w1\naiVcoUTdEPSS86wQwq9j0YCcAKOwMXsqbk9NhpujrdyJ94dev+ELwkNS7P0pPrcfiyFTQeJCZTXz\nJB36MwIDAQAB\n-----END RSA PUBLIC KEY-----\n",
|
||||
"signature": "lfRXK4zL213wBKg760eiPV7yvnLZ6a6v9Iohmw78yxIzqXO3tfrC5Z+P2LGiO1BdI4xckx8yz4ktn82zX97+r2zktBw0As7g71H/FjInpoZ76j3gMUaiFNrQJ0vKCCI7xsjonemroWAVDCAqlvdyqwUu/Fnz85+WoR2kCQ721vwy6IjWA3xhq8XrWjkI/AlBmoS/kVqnvjjjc9vocdddJXbUYzCse/hWWIbsFeBXyiGCd3v7apgtXwQfM++tt87fq7444zQskiYb14oQP8/uNwqZWQ9jAs00i1BZ0MNM6+TZYGHOfS6rbHZ1bcX34VZdcCwpapK/Z2HBRIgDN4QOcgJkyq1GcjvlM2wjfhN8gXTsmbF9Ee+5Y6a4ONRkxRZK2sT8oAXdm0OlTEGB0P0+WRRFOQ/PnRqbI7lvANao2METT2EUHHrtqFMe53kqCHdzy5qyuHxdCEa6l/gSR08fybx9DdRRmhOlhSPGxhgwqyi1fEMrN4CsSKNrv5u+sMqhspA05b3DQJeLDX+UV5ujRHwm0A49NF+h1ZYlrcefz5IMUUisOOw6HiLc/YGLD2jHwSePGdfMwMnrIxbxjCta7/7A91aaN7eYm16KW9erCOwAfJmBSQC6Pbmg5f7rd7rAKVOPxglq7nayXmd+BK53Mal5tltMSgd/0iY6SEtGSEU="
|
||||
}
|
||||
@@ -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="
|
||||
}
|
||||
14
bukkit/src/test/resources/logback-test.xml
Normal file
14
bukkit/src/test/resources/logback-test.xml
Normal file
@@ -0,0 +1,14 @@
|
||||
<configuration>
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<root level="info">
|
||||
<appender-ref ref="STDOUT"/>
|
||||
</root>
|
||||
|
||||
<logger name="org.testcontainers" level="INFO"/>
|
||||
<logger name="com.github.dockerjava" level="WARN"/>
|
||||
</configuration>
|
||||
292
bukkit/src/test/resources/paper.yml
Normal file
292
bukkit/src/test/resources/paper.yml
Normal file
@@ -0,0 +1,292 @@
|
||||
# This is the main configuration file for Paper.
|
||||
# As you can see, there's tons to configure. Some options may impact gameplay, so use
|
||||
# with caution, and make sure you know what each option does before configuring.
|
||||
#
|
||||
# If you need help with the configuration or have any questions related to Paper,
|
||||
# join us in our Discord or IRC channel.
|
||||
#
|
||||
# Discord: https://discord.gg/papermc
|
||||
# IRC: #paper @ irc.esper.net ( https://webchat.esper.net/?channels=paper )
|
||||
# Website: https://papermc.io/
|
||||
# Docs: https://paper.readthedocs.org/
|
||||
|
||||
verbose: false
|
||||
messages:
|
||||
kick:
|
||||
authentication-servers-down: ''
|
||||
connection-throttle: Connection throttled! Please wait before reconnecting.
|
||||
flying-player: Flying is not enabled on this server
|
||||
flying-vehicle: Flying is not enabled on this server
|
||||
no-permission: '&cI''m sorry, but you do not have permission to perform this command.
|
||||
Please contact the server administrators if you believe that this is in error.'
|
||||
timings:
|
||||
enabled: false
|
||||
verbose: true
|
||||
url: https://timings.aikar.co/
|
||||
server-name-privacy: false
|
||||
hidden-config-entries: []
|
||||
history-interval: 300
|
||||
history-length: 3600
|
||||
server-name: Unknown Server
|
||||
config-version: 24
|
||||
settings:
|
||||
max-joins-per-tick: 3
|
||||
track-plugin-scoreboards: false
|
||||
fix-entity-position-desync: true
|
||||
use-display-name-in-quit-message: false
|
||||
load-permissions-yml-before-plugins: true
|
||||
region-file-cache-size: 256
|
||||
enable-player-collisions: false
|
||||
save-empty-scoreboard-teams: false
|
||||
bungee-online-mode: true
|
||||
incoming-packet-spam-threshold: 300
|
||||
use-alternative-luck-formula: false
|
||||
velocity-support:
|
||||
enabled: false
|
||||
online-mode: false
|
||||
secret: ''
|
||||
console-has-all-permissions: false
|
||||
player-auto-save-rate: -1
|
||||
max-player-auto-save-per-tick: -1
|
||||
fix-target-selector-tag-completion: true
|
||||
lag-compensate-block-breaking: true
|
||||
time-command-affects-all-worlds: false
|
||||
log-player-ip-addresses: false
|
||||
console:
|
||||
enable-brigadier-highlighting: false
|
||||
enable-brigadier-completions: false
|
||||
suggest-player-names-when-null-tab-completions: false
|
||||
watchdog:
|
||||
early-warning-every: 5000
|
||||
early-warning-delay: 10000
|
||||
spam-limiter:
|
||||
tab-spam-increment: 1
|
||||
tab-spam-limit: 500
|
||||
recipe-spam-increment: 1
|
||||
recipe-spam-limit: 20
|
||||
book-size:
|
||||
page-max: 2560
|
||||
total-multiplier: 0.98
|
||||
loggers:
|
||||
deobfuscate-stacktraces: true
|
||||
item-validation:
|
||||
display-name: 8192
|
||||
loc-name: 8192
|
||||
lore-line: 8192
|
||||
book:
|
||||
title: 8192
|
||||
author: 8192
|
||||
page: 16384
|
||||
send-full-pos-for-hard-colliding-entities: true
|
||||
async-chunks:
|
||||
threads: -1
|
||||
unsupported-settings:
|
||||
allow-permanent-block-break-exploits: false
|
||||
allow-piston-duplication: false
|
||||
perform-username-validation: true
|
||||
allow-headless-pistons: false
|
||||
allow-permanent-block-break-exploits-readme: This setting controls if players
|
||||
should be able to break bedrock, end portals and other intended to be permanent
|
||||
blocks.
|
||||
allow-piston-duplication-readme: This setting controls if player should be able
|
||||
to use TNT duplication, but this also allows duplicating carpet, rails and potentially
|
||||
other items
|
||||
allow-headless-pistons-readme: This setting controls if players should be able
|
||||
to create headless pistons.
|
||||
packet-limiter:
|
||||
kick-message: '&cSent too many packets'
|
||||
limits: []
|
||||
world-settings:
|
||||
default:
|
||||
delay-chunk-unloads-by: 10s
|
||||
disable-teleportation-suffocation-check: true
|
||||
generator-settings:
|
||||
flat-bedrock: true
|
||||
piglins-guard-chests: true
|
||||
should-remove-dragon: false
|
||||
max-auto-save-chunks-per-tick: 24
|
||||
baby-zombie-movement-modifier: 0.5
|
||||
optimize-explosions: false
|
||||
use-vanilla-world-scoreboard-name-coloring: false
|
||||
game-mechanics:
|
||||
scan-for-legacy-ender-dragon: true
|
||||
fix-curing-zombie-villager-discount-exploit: true
|
||||
disable-pillager-patrols: true
|
||||
pillager-patrols:
|
||||
spawn-chance: 0.2
|
||||
spawn-delay:
|
||||
per-player: false
|
||||
ticks: 12000
|
||||
start:
|
||||
per-player: false
|
||||
day: 5
|
||||
disable-chest-cat-detection: true
|
||||
nerf-pigmen-from-nether-portals: false
|
||||
disable-player-crits: true
|
||||
disable-sprint-interruption-on-attack: true
|
||||
shield-blocking-delay: 5
|
||||
disable-end-credits: true
|
||||
disable-unloaded-chunk-enderpearl-exploit: true
|
||||
disable-relative-projectile-velocity: true
|
||||
disable-mob-spawner-spawn-egg-transformation: true
|
||||
prevent-moving-into-unloaded-chunks: false
|
||||
count-all-mobs-for-spawning: false
|
||||
spawn-limits:
|
||||
monster: -1
|
||||
creature: -1
|
||||
ambient: -1
|
||||
axolotls: -1
|
||||
underground_water_creature: -1
|
||||
water_creature: -1
|
||||
water_ambient: -1
|
||||
ender-dragons-death-always-places-dragon-egg: false
|
||||
experience-merge-max-value: -1
|
||||
allow-using-signs-inside-spawn-protection: false
|
||||
wandering-trader:
|
||||
spawn-minute-length: 1200
|
||||
spawn-day-length: 24000
|
||||
spawn-chance-failure-increment: 25
|
||||
spawn-chance-min: 25
|
||||
spawn-chance-max: 75
|
||||
door-breaking-difficulty:
|
||||
zombie:
|
||||
- HARD
|
||||
vindicator:
|
||||
- NORMAL
|
||||
- HARD
|
||||
max-growth-height:
|
||||
cactus: 3
|
||||
reeds: 3
|
||||
bamboo:
|
||||
max: 16
|
||||
min: 11
|
||||
fishing-time-range:
|
||||
MinimumTicks: 100
|
||||
MaximumTicks: 600
|
||||
despawn-ranges: []
|
||||
falling-block-height-nerf: 0
|
||||
tnt-entity-height-nerf: 0
|
||||
slime-spawn-height:
|
||||
swamp-biome:
|
||||
maximum: 70.0
|
||||
minimum: 50.0
|
||||
slime-chunk:
|
||||
maximum: 40.0
|
||||
frosted-ice:
|
||||
enabled: true
|
||||
delay:
|
||||
min: 20
|
||||
max: 40
|
||||
lootables:
|
||||
auto-replenish: false
|
||||
restrict-player-reloot: true
|
||||
reset-seed-on-fill: true
|
||||
max-refills: -1
|
||||
refresh-min: 12h
|
||||
refresh-max: 2d
|
||||
filter-nbt-data-from-spawn-eggs-and-related: true
|
||||
max-entity-collisions: 8
|
||||
disable-creeper-lingering-effect: true
|
||||
duplicate-uuid-resolver: saferegen
|
||||
duplicate-uuid-saferegen-delete-range: 32
|
||||
hopper:
|
||||
cooldown-when-full: true
|
||||
disable-move-event: true
|
||||
ignore-occluding-blocks: false
|
||||
mob-effects:
|
||||
undead-immune-to-certain-effects: true
|
||||
spiders-immune-to-poison-effect: true
|
||||
immune-to-wither-effect:
|
||||
wither: true
|
||||
wither-skeleton: true
|
||||
update-pathfinding-on-block-update: true
|
||||
phantoms-do-not-spawn-on-creative-players: true
|
||||
phantoms-only-attack-insomniacs: true
|
||||
mobs-can-always-pick-up-loot:
|
||||
zombies: false
|
||||
skeletons: false
|
||||
map-item-frame-cursor-update-interval: 10
|
||||
allow-player-cramming-damage: false
|
||||
anticheat:
|
||||
obfuscation:
|
||||
items:
|
||||
hide-itemmeta: false
|
||||
hide-durability: false
|
||||
monster-spawn-max-light-level: -1
|
||||
water-over-lava-flow-speed: 5
|
||||
grass-spread-tick-rate: 1
|
||||
use-faster-eigencraft-redstone: false
|
||||
nether-ceiling-void-damage-height: 0
|
||||
only-players-collide: false
|
||||
allow-vehicle-collisions: true
|
||||
allow-non-player-entities-on-scoreboards: false
|
||||
anti-xray:
|
||||
enabled: false
|
||||
engine-mode: 1
|
||||
max-block-height: 64
|
||||
update-radius: 2
|
||||
lava-obscures: false
|
||||
use-permission: false
|
||||
hidden-blocks: []
|
||||
replacement-blocks: []
|
||||
keep-spawn-loaded: true
|
||||
armor-stands-do-collision-entity-lookups: true
|
||||
parrots-are-unaffected-by-player-movement: false
|
||||
disable-explosion-knockback: true
|
||||
portal-search-radius: 128
|
||||
portal-create-radius: 16
|
||||
portal-search-vanilla-dimension-scaling: true
|
||||
fix-items-merging-through-walls: false
|
||||
disable-thunder: true
|
||||
skeleton-horse-thunder-spawn-chance: 0.01
|
||||
disable-ice-and-snow: true
|
||||
keep-spawn-loaded-range: 10
|
||||
fix-climbing-bypassing-cramming-rule: false
|
||||
container-update-tick-rate: 1
|
||||
fixed-chunk-inhabited-time: -1
|
||||
remove-corrupt-tile-entities: false
|
||||
prevent-tnt-from-moving-in-water: false
|
||||
iron-golems-can-spawn-in-air: false
|
||||
max-leash-distance: 10.0
|
||||
show-sign-click-command-failure-msgs-to-player: false
|
||||
armor-stands-tick: true
|
||||
non-player-arrow-despawn-rate: -1
|
||||
creative-arrow-despawn-rate: -1
|
||||
spawner-nerfed-mobs-should-jump: false
|
||||
entities-target-with-follow-range: false
|
||||
wateranimal-spawn-height:
|
||||
maximum: default
|
||||
minimum: default
|
||||
zombies-target-turtle-eggs: true
|
||||
zombie-villager-infection-chance: -1.0
|
||||
unsupported-settings:
|
||||
fix-invulnerable-end-crystal-exploit: true
|
||||
all-chunks-are-slime-chunks: false
|
||||
mob-spawner-tick-rate: 1
|
||||
map-item-frame-cursor-limit: 128
|
||||
per-player-mob-spawns: true
|
||||
light-queue-size: 20
|
||||
auto-save-interval: -1
|
||||
enable-treasure-maps: false
|
||||
treasure-maps-return-already-discovered: false
|
||||
split-overstacked-loot: true
|
||||
entity-per-chunk-save-limit:
|
||||
experience_orb: -1
|
||||
snowball: -1
|
||||
ender_pearl: -1
|
||||
arrow: -1
|
||||
fireball: -1
|
||||
small_fireball: -1
|
||||
alt-item-despawn-rate:
|
||||
enabled: false
|
||||
items:
|
||||
COBBLESTONE: 300
|
||||
tick-rates:
|
||||
sensor:
|
||||
villager:
|
||||
secondarypoisensor: 40
|
||||
behavior:
|
||||
villager:
|
||||
validatenearbypoi: -1
|
||||
feature-seeds:
|
||||
generate-random-seeds-for-all: false
|
||||
50
bukkit/src/test/resources/server.properties
Normal file
50
bukkit/src/test/resources/server.properties
Normal file
@@ -0,0 +1,50 @@
|
||||
#Minecraft server properties
|
||||
enable-jmx-monitoring=false
|
||||
rcon.port=25575
|
||||
gamemode=creative
|
||||
enable-command-block=false
|
||||
enable-query=false
|
||||
level-name=world
|
||||
motd=Debug server
|
||||
query.port=25565
|
||||
pvp=false
|
||||
difficulty=peaceful
|
||||
network-compression-threshold=256
|
||||
require-resource-pack=false
|
||||
max-tick-time=60000
|
||||
use-native-transport=true
|
||||
max-players=2
|
||||
online-mode=false
|
||||
enable-status=true
|
||||
allow-flight=false
|
||||
broadcast-rcon-to-ops=true
|
||||
view-distance=3
|
||||
server-ip=
|
||||
resource-pack-prompt=
|
||||
allow-nether=false
|
||||
server-port=25565
|
||||
enable-rcon=false
|
||||
sync-chunk-writes=false
|
||||
op-permission-level=4
|
||||
prevent-proxy-connections=false
|
||||
hide-online-players=true
|
||||
resource-pack=
|
||||
entity-broadcast-range-percentage=10
|
||||
simulation-distance=3
|
||||
rcon.password=
|
||||
player-idle-timeout=0
|
||||
debug=true
|
||||
force-gamemode=false
|
||||
rate-limit=0
|
||||
hardcore=false
|
||||
white-list=false
|
||||
broadcast-console-to-ops=false
|
||||
spawn-npcs=false
|
||||
spawn-animals=false
|
||||
function-permission-level=2
|
||||
text-filtering-config=
|
||||
spawn-monsters=false
|
||||
enforce-whitelist=false
|
||||
resource-pack-sha1=
|
||||
spawn-protection=0
|
||||
max-world-size=1
|
||||
@@ -1,16 +0,0 @@
|
||||
# About
|
||||
|
||||
This contains test resources for the unit tests. Files in this folder include pre-made cryptographic signatures.
|
||||
|
||||
## Directory structure
|
||||
|
||||
* `valid_signature.json`: Extracted using packet extract from an actual authentication
|
||||
* `incorrect_nonce.json`: Different nonce token simulating that the server expected a different token than signed
|
||||
* `incorrect_salt.json`: Salt sent is different to the content signed by the signature (changed salt field)
|
||||
* `incorrect_signature.json`: Changed signature
|
||||
|
||||
## File content
|
||||
|
||||
* `nonce`: Server generated nonce token
|
||||
* `salt`: Client generated random token that will be signed
|
||||
* `signature`: Nonce and salt signed using the client key from `valid_public_key.json`
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"nonce": "galNig\u003d\u003d",
|
||||
"signature": {
|
||||
"signature": "JlXAUtIGDjxUOnF5vkg/NUEN2wlzXcqADyYIw2WRTb5hgKwIgxyUPO5v/2M7xU3hxz2Zf0iYHM97h8qNMGQ43cLgfVH9VWZ1kGMuOby2LNSb6nDaMzm0b02ftThaWOWj9kJXbR8fN7qdpB+28t2CTW5ILT+2AZYI/Sn8gFFR+LvJJt1ENMfEj2ZIIkHecpNBuKyLz1aDCZ5BEASSLfAqHEAA3dpHV1DIgzfpO6xwo7bVFDtcBEeusl/Nc3KyGyT8sDFTsZWgitgz53xNKrZUK8Q2BaJfP0zrGAX36rpYURJSVD0AtI1ic9s5aG+OFUC1YhLXb/1cDv37ZjHcdV2ppw\u003d\u003d",
|
||||
"salt": -2985008842905108412
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"nonce": "GalNig\u003d\u003d",
|
||||
"signature": {
|
||||
"signature": "JlXAUtIGDjxUOnF5vkg/NUEN2wlzXcqADyYIw2WRTb5hgKwIgxyUPO5v/2M7xU3hxz2Zf0iYHM97h8qNMGQ43cLgfVH9VWZ1kGMuOby2LNSb6nDaMzm0b02ftThaWOWj9kJXbR8fN7qdpB+28t2CTW5ILT+2AZYI/Sn8gFFR+LvJJt1ENMfEj2ZIIkHecpNBuKyLz1aDCZ5BEASSLfAqHEAA3dpHV1DIgzfpO6xwo7bVFDtcBEeusl/Nc3KyGyT8sDFTsZWgitgz53xNKrZUK8Q2BaJfP0zrGAX36rpYURJSVD0AtI1ic9s5aG+OFUC1YhLXb/1cDv37ZjHcdV2ppw\u003d\u003d",
|
||||
"salt": -1985008842905108412
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"nonce": "GalNig\u003d\u003d",
|
||||
"signature": {
|
||||
"signature": "jlXAUtIGDjxUOnF5vkg/NUEN2wlzXcqADyYIw2WRTb5hgKwIgxyUPO5v/2M7xU3hxz2Zf0iYHM97h8qNMGQ43cLgfVH9VWZ1kGMuOby2LNSb6nDaMzm0b02ftThaWOWj9kJXbR8fN7qdpB+28t2CTW5ILT+2AZYI/Sn8gFFR+LvJJt1ENMfEj2ZIIkHecpNBuKyLz1aDCZ5BEASSLfAqHEAA3dpHV1DIgzfpO6xwo7bVFDtcBEeusl/Nc3KyGyT8sDFTsZWgitgz53xNKrZUK8Q2BaJfP0zrGAX36rpYURJSVD0AtI1ic9s5aG+OFUC1YhLXb/1cDv37ZjHcdV2ppw\u003d\u003d",
|
||||
"salt": -2985008842905108412
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"nonce": "GalNig\u003d\u003d",
|
||||
"signature": {
|
||||
"signature": "JlXAUtIGDjxUOnF5vkg/NUEN2wlzXcqADyYIw2WRTb5hgKwIgxyUPO5v/2M7xU3hxz2Zf0iYHM97h8qNMGQ43cLgfVH9VWZ1kGMuOby2LNSb6nDaMzm0b02ftThaWOWj9kJXbR8fN7qdpB+28t2CTW5ILT+2AZYI/Sn8gFFR+LvJJt1ENMfEj2ZIIkHecpNBuKyLz1aDCZ5BEASSLfAqHEAA3dpHV1DIgzfpO6xwo7bVFDtcBEeusl/Nc3KyGyT8sDFTsZWgitgz53xNKrZUK8Q2BaJfP0zrGAX36rpYURJSVD0AtI1ic9s5aG+OFUC1YhLXb/1cDv37ZjHcdV2ppw\u003d\u003d",
|
||||
"salt": -2985008842905108412
|
||||
}
|
||||
}
|
||||
164
bukkit/src/test/resources/spigot.yml
Normal file
164
bukkit/src/test/resources/spigot.yml
Normal file
@@ -0,0 +1,164 @@
|
||||
# This is the main configuration file for Spigot.
|
||||
# As you can see, there's tons to configure. Some options may impact gameplay, so use
|
||||
# with caution, and make sure you know what each option does before configuring.
|
||||
# For a reference for any variable inside this file, check out the Spigot wiki at
|
||||
# http://www.spigotmc.org/wiki/spigot-configuration/
|
||||
#
|
||||
# If you need help with the configuration or have any questions related to Spigot,
|
||||
# join us at the Discord or drop by our forums and leave a post.
|
||||
#
|
||||
# Discord: https://www.spigotmc.org/go/discord
|
||||
# Forums: http://www.spigotmc.org/
|
||||
|
||||
settings:
|
||||
debug: true
|
||||
bungeecord: false
|
||||
sample-count: 0
|
||||
player-shuffle: 0
|
||||
user-cache-size: 1000
|
||||
save-user-cache-on-stop-only: false
|
||||
moved-wrongly-threshold: 0.0625
|
||||
moved-too-quickly-multiplier: 10.0
|
||||
timeout-time: 60
|
||||
restart-on-crash: false
|
||||
restart-script: ./start.sh
|
||||
netty-threads: 1
|
||||
attribute:
|
||||
maxHealth:
|
||||
max: 2048.0
|
||||
movementSpeed:
|
||||
max: 2048.0
|
||||
attackDamage:
|
||||
max: 2048.0
|
||||
log-villager-deaths: false
|
||||
log-named-deaths: false
|
||||
messages:
|
||||
whitelist: You are not whitelisted on this server!
|
||||
unknown-command: Unknown command. Type "/help" for help.
|
||||
server-full: The server is full!
|
||||
outdated-client: Outdated client! Please use {0}
|
||||
outdated-server: Outdated server! I'm still on {0}
|
||||
restart: Server is restarting
|
||||
advancements:
|
||||
disable-saving: true
|
||||
disabled: []
|
||||
commands:
|
||||
replace-commands: []
|
||||
spam-exclusions: []
|
||||
silent-commandblock-console: false
|
||||
log: false
|
||||
tab-complete: 0
|
||||
send-namespaced: false
|
||||
players:
|
||||
disable-saving: true
|
||||
world-settings:
|
||||
default:
|
||||
below-zero-generation-in-existing-chunks: true
|
||||
verbose: false
|
||||
merge-radius:
|
||||
exp: 3.0
|
||||
item: 2.5
|
||||
growth:
|
||||
cactus-modifier: 100
|
||||
cane-modifier: 100
|
||||
melon-modifier: 100
|
||||
mushroom-modifier: 100
|
||||
pumpkin-modifier: 100
|
||||
sapling-modifier: 100
|
||||
beetroot-modifier: 100
|
||||
carrot-modifier: 100
|
||||
potato-modifier: 100
|
||||
wheat-modifier: 100
|
||||
netherwart-modifier: 100
|
||||
vine-modifier: 100
|
||||
cocoa-modifier: 100
|
||||
bamboo-modifier: 100
|
||||
sweetberry-modifier: 100
|
||||
kelp-modifier: 100
|
||||
twistingvines-modifier: 100
|
||||
weepingvines-modifier: 100
|
||||
cavevines-modifier: 100
|
||||
glowberry-modifier: 100
|
||||
entity-activation-range:
|
||||
animals: 32
|
||||
monsters: 32
|
||||
raiders: 48
|
||||
misc: 16
|
||||
water: 16
|
||||
villagers: 32
|
||||
flying-monsters: 32
|
||||
wake-up-inactive:
|
||||
animals-max-per-tick: 4
|
||||
animals-every: 1200
|
||||
animals-for: 100
|
||||
monsters-max-per-tick: 8
|
||||
monsters-every: 400
|
||||
monsters-for: 100
|
||||
villagers-max-per-tick: 4
|
||||
villagers-every: 600
|
||||
villagers-for: 100
|
||||
flying-monsters-max-per-tick: 8
|
||||
flying-monsters-every: 200
|
||||
flying-monsters-for: 100
|
||||
villagers-work-immunity-after: 100
|
||||
villagers-work-immunity-for: 20
|
||||
villagers-active-for-panic: true
|
||||
tick-inactive-villagers: true
|
||||
ignore-spectators: false
|
||||
entity-tracking-range:
|
||||
players: 48
|
||||
animals: 48
|
||||
monsters: 48
|
||||
misc: 32
|
||||
other: 64
|
||||
ticks-per:
|
||||
hopper-transfer: 8
|
||||
hopper-check: 1
|
||||
hopper-amount: 1
|
||||
dragon-death-sound-radius: 0
|
||||
seed-village: 10387312
|
||||
seed-desert: 14357617
|
||||
seed-igloo: 14357618
|
||||
seed-jungle: 14357619
|
||||
seed-swamp: 14357620
|
||||
seed-monument: 10387313
|
||||
seed-shipwreck: 165745295
|
||||
seed-ocean: 14357621
|
||||
seed-outpost: 165745296
|
||||
seed-endcity: 10387313
|
||||
seed-slime: 987234911
|
||||
seed-bastion: 30084232
|
||||
seed-fortress: 30084232
|
||||
seed-mansion: 10387319
|
||||
seed-fossil: 14357921
|
||||
seed-portal: 34222645
|
||||
seed-stronghold: default
|
||||
hunger:
|
||||
jump-walk-exhaustion: 0.05
|
||||
jump-sprint-exhaustion: 0.2
|
||||
combat-exhaustion: 0.1
|
||||
regen-exhaustion: 6.0
|
||||
swim-multiplier: 0.01
|
||||
sprint-multiplier: 0.1
|
||||
other-multiplier: 0.0
|
||||
max-tnt-per-tick: 100
|
||||
max-tick-time:
|
||||
tile: 50
|
||||
entity: 50
|
||||
enable-zombie-pigmen-portal-spawns: true
|
||||
item-despawn-rate: 6000
|
||||
view-distance: default
|
||||
simulation-distance: default
|
||||
thunder-chance: 100000
|
||||
wither-spawn-sound-radius: 0
|
||||
arrow-despawn-rate: 1200
|
||||
trident-despawn-rate: 1200
|
||||
hanging-tick-frequency: 100
|
||||
zombie-aggressive-towards-villager: true
|
||||
nerf-spawner-mobs: false
|
||||
mob-spawn-range: 8
|
||||
end-portal-sound-radius: 0
|
||||
config-version: 12
|
||||
stats:
|
||||
disable-saving: true
|
||||
forced-stats: {}
|
||||
142
bungee/pom.xml
142
bungee/pom.xml
@@ -4,7 +4,7 @@
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015-2024 games647 and contributors
|
||||
Copyright (c) 2015-2021 <Your name and contributors>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -25,14 +25,14 @@
|
||||
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">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>com.github.games647</groupId>
|
||||
<artifactId>fastlogin</artifactId>
|
||||
<version>1.12-SNAPSHOT</version>
|
||||
<version>1.11-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -40,23 +40,26 @@
|
||||
<artifactId>fastlogin.bungee</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.release>17</maven.compiler.release>
|
||||
</properties>
|
||||
|
||||
<!--Represents the main plugin-->
|
||||
<name>FastLoginBungee</name>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.6.0</version>
|
||||
<version>3.2.4</version>
|
||||
<configuration>
|
||||
<minimizeJar>true</minimizeJar>
|
||||
|
||||
<createDependencyReducedPom>false</createDependencyReducedPom>
|
||||
<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>
|
||||
<relocation>
|
||||
<pattern>com.zaxxer.hikari</pattern>
|
||||
@@ -70,19 +73,8 @@
|
||||
<!-- Rename the service file too to let SLF4J api find our own relocated jdk logger -->
|
||||
<!-- Located in META-INF/services -->
|
||||
<transformers>
|
||||
<transformer
|
||||
implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
|
||||
</transformers>
|
||||
<filters>
|
||||
<filter>
|
||||
<artifact>*:*</artifact>
|
||||
<excludes>
|
||||
<exclude>META-INF/MANIFEST.MF</exclude>
|
||||
<exclude>**/module-info</exclude>
|
||||
<exclude>**/module-info.class</exclude>
|
||||
</excludes>
|
||||
</filter>
|
||||
</filters>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
@@ -102,6 +94,11 @@
|
||||
<url>https://repo.codemc.io/repository/maven-public/</url>
|
||||
</repository>
|
||||
|
||||
<repository>
|
||||
<id>spigotplugins-repo</id>
|
||||
<url>https://maven.gamestrike.de/mvn/</url>
|
||||
</repository>
|
||||
|
||||
<repository>
|
||||
<id>jitpack.io</id>
|
||||
<url>https://jitpack.io</url>
|
||||
@@ -117,33 +114,13 @@
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>fastlogin.core</artifactId>
|
||||
<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>
|
||||
|
||||
<!--BungeeCord with also the part outside the API-->
|
||||
<dependency>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-proxy</artifactId>
|
||||
<version>1.20-R0.2-SNAPSHOT</version>
|
||||
<version>1.18-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
<!-- Use our own newer api version -->
|
||||
<exclusions>
|
||||
@@ -151,40 +128,6 @@
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.mysql</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-native</artifactId>
|
||||
</exclusion>
|
||||
|
||||
<exclusion>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-query</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-slf4j</artifactId>
|
||||
</exclusion>
|
||||
|
||||
<exclusion>
|
||||
<groupId>net.sf.jopt-simple</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.apache.maven</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.apache.maven.resolver</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
@@ -196,7 +139,11 @@
|
||||
<scope>provided</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>*</groupId>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.geysermc.cumulus</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
@@ -204,7 +151,7 @@
|
||||
|
||||
<!-- Bedrock player bridge -->
|
||||
<dependency>
|
||||
<groupId>org.geysermc.geyser</groupId>
|
||||
<groupId>org.geysermc</groupId>
|
||||
<artifactId>core</artifactId>
|
||||
<version>${geyser.version}</version>
|
||||
<scope>provided</scope>
|
||||
@@ -218,16 +165,10 @@
|
||||
|
||||
<!-- We need the API, but it was excluded above -->
|
||||
<dependency>
|
||||
<groupId>org.geysermc.geyser</groupId>
|
||||
<artifactId>api</artifactId>
|
||||
<groupId>org.geysermc</groupId>
|
||||
<artifactId>geyser-api</artifactId>
|
||||
<version>${geyser.version}</version>
|
||||
<scope>provided</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>*</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!--Login plugin-->
|
||||
@@ -238,5 +179,32 @@
|
||||
<scope>system</scope>
|
||||
<systemPath>${project.basedir}/lib/BungeeAuth-1.4.jar</systemPath>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>de.xxschrandxx.bca</groupId>
|
||||
<artifactId>BungeeCordAuthenticator</artifactId>
|
||||
<version>0.0.2</version>
|
||||
<scope>provided</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>*</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.Mohist-Community.SodionAuth</groupId>
|
||||
<artifactId>SodionAuth-Bungee</artifactId>
|
||||
<version>2bdfdc854b</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.github.Mohist-Community.SodionAuth</groupId>
|
||||
<artifactId>SodionAuth-Libs</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
<optional>true</optional>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2024 games647 and contributors
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -25,8 +25,8 @@
|
||||
*/
|
||||
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.storage.StoredProfile;
|
||||
|
||||
public class BungeeLoginSession extends LoginSession {
|
||||
|
||||
@@ -59,10 +59,10 @@ public class BungeeLoginSession extends LoginSession {
|
||||
|
||||
@Override
|
||||
public synchronized String toString() {
|
||||
return this.getClass().getSimpleName() + '{'
|
||||
+ "alreadySaved=" + alreadySaved
|
||||
+ ", alreadyLogged=" + alreadyLogged
|
||||
+ ", registered=" + registered
|
||||
+ "} " + super.toString();
|
||||
return this.getClass().getSimpleName() + '{' +
|
||||
"alreadySaved=" + alreadySaved +
|
||||
", alreadyLogged=" + alreadyLogged +
|
||||
", registered=" + registered +
|
||||
"} " + super.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2024 games647 and contributors
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -26,14 +26,15 @@
|
||||
package com.github.games647.fastlogin.bungee;
|
||||
|
||||
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.chat.ComponentBuilder;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import net.md_5.bungee.api.connection.PendingConnection;
|
||||
import net.md_5.bungee.api.event.PreLoginEvent;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
|
||||
public class BungeeLoginSource implements LoginSource {
|
||||
|
||||
private final PendingConnection connection;
|
||||
@@ -54,15 +55,9 @@ public class BungeeLoginSource implements LoginSource {
|
||||
preLoginEvent.setCancelled(true);
|
||||
|
||||
if (message == null) {
|
||||
preLoginEvent.setReason(
|
||||
TextComponent.fromArray(
|
||||
new ComponentBuilder("Kicked").color(ChatColor.WHITE).create()
|
||||
));
|
||||
preLoginEvent.setCancelReason(new ComponentBuilder("Kicked").color(ChatColor.WHITE).create());
|
||||
} else {
|
||||
preLoginEvent.setReason(
|
||||
TextComponent.fromArray(
|
||||
TextComponent.fromLegacyText(message)
|
||||
));
|
||||
preLoginEvent.setCancelReason(TextComponent.fromLegacyText(message));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,8 +72,8 @@ public class BungeeLoginSource implements LoginSource {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.getClass().getSimpleName() + '{'
|
||||
+ "connection=" + connection
|
||||
+ '}';
|
||||
return this.getClass().getSimpleName() + '{' +
|
||||
"connection=" + connection +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2024 games647 and contributors
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -26,8 +26,11 @@
|
||||
package com.github.games647.fastlogin.bungee;
|
||||
|
||||
import com.github.games647.fastlogin.bungee.hook.BungeeAuthHook;
|
||||
import com.github.games647.fastlogin.bungee.hook.BungeeCordAuthenticatorBungeeHook;
|
||||
import com.github.games647.fastlogin.bungee.hook.SodionAuthHook;
|
||||
import com.github.games647.fastlogin.bungee.listener.ConnectListener;
|
||||
import com.github.games647.fastlogin.bungee.listener.PluginMessageListener;
|
||||
import com.github.games647.fastlogin.core.AsyncScheduler;
|
||||
import com.github.games647.fastlogin.core.CommonUtil;
|
||||
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
|
||||
import com.github.games647.fastlogin.core.hooks.bedrock.BedrockService;
|
||||
@@ -37,32 +40,32 @@ import com.github.games647.fastlogin.core.message.ChangePremiumMessage;
|
||||
import com.github.games647.fastlogin.core.message.ChannelMessage;
|
||||
import com.github.games647.fastlogin.core.message.NamespaceKey;
|
||||
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.PlatformPlugin;
|
||||
import com.google.common.collect.MapMaker;
|
||||
import com.google.common.io.ByteArrayDataOutput;
|
||||
import com.google.common.io.ByteStreams;
|
||||
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.chat.TextComponent;
|
||||
import net.md_5.bungee.api.connection.PendingConnection;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.api.connection.Server;
|
||||
import net.md_5.bungee.api.plugin.Listener;
|
||||
import net.md_5.bungee.api.plugin.Plugin;
|
||||
import net.md_5.bungee.api.plugin.PluginManager;
|
||||
import net.md_5.bungee.api.scheduler.GroupedThreadFactory;
|
||||
|
||||
import org.geysermc.floodgate.api.FloodgateApi;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
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.
|
||||
*/
|
||||
@@ -79,7 +82,7 @@ public class FastLoginBungee extends Plugin implements PlatformPlugin<CommandSen
|
||||
@Override
|
||||
public void onEnable() {
|
||||
logger = CommonUtil.initializeLoggerService(getLogger());
|
||||
scheduler = new AsyncScheduler(logger, task -> getProxy().getScheduler().runAsync(this, task));
|
||||
scheduler = new AsyncScheduler(logger, getThreadFactory());
|
||||
|
||||
core = new FastLoginCore<>(this);
|
||||
core.load();
|
||||
@@ -98,7 +101,7 @@ public class FastLoginBungee extends Plugin implements PlatformPlugin<CommandSen
|
||||
//events
|
||||
PluginManager pluginManager = getProxy().getPluginManager();
|
||||
|
||||
Listener connectListener = new ConnectListener(this, core.getAntiBotService());
|
||||
ConnectListener connectListener = new ConnectListener(this, core.getRateLimiter());
|
||||
pluginManager.registerListener(this, connectListener);
|
||||
pluginManager.registerListener(this, new PluginMessageListener(this));
|
||||
|
||||
@@ -126,9 +129,8 @@ public class FastLoginBungee extends Plugin implements PlatformPlugin<CommandSen
|
||||
|
||||
private void registerHook() {
|
||||
try {
|
||||
List<Class<? extends AuthPlugin<ProxiedPlayer>>> hooks = Collections.singletonList(
|
||||
BungeeAuthHook.class
|
||||
);
|
||||
List<Class<? extends AuthPlugin<ProxiedPlayer>>> hooks = Arrays.asList(
|
||||
BungeeAuthHook.class, BungeeCordAuthenticatorBungeeHook.class, SodionAuthHook.class);
|
||||
|
||||
for (Class<? extends AuthPlugin<ProxiedPlayer>> clazz : hooks) {
|
||||
String pluginName = clazz.getSimpleName();
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2024 games647 and contributors
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -25,9 +25,9 @@
|
||||
*/
|
||||
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.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.Event;
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2024 games647 and contributors
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -25,9 +25,9 @@
|
||||
*/
|
||||
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.event.FastLoginPreLoginEvent;
|
||||
import com.github.games647.fastlogin.core.storage.StoredProfile;
|
||||
import net.md_5.bungee.api.plugin.Event;
|
||||
|
||||
public class BungeeFastLoginPreLoginEvent extends Event implements FastLoginPreLoginEvent {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2024 games647 and contributors
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -25,8 +25,9 @@
|
||||
*/
|
||||
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.storage.StoredProfile;
|
||||
|
||||
import net.md_5.bungee.api.plugin.Event;
|
||||
|
||||
public class BungeeFastLoginPremiumToggleEvent extends Event implements FastLoginPremiumToggleEvent {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2024 games647 and contributors
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -27,18 +27,18 @@ package com.github.games647.fastlogin.bungee.hook;
|
||||
|
||||
import com.github.games647.fastlogin.bungee.FastLoginBungee;
|
||||
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
|
||||
|
||||
import me.vik1395.BungeeAuth.Main;
|
||||
import me.vik1395.BungeeAuthAPI.RequestHandler;
|
||||
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
|
||||
/**
|
||||
* GitHub:
|
||||
* <a href="https://github.com/vik1395/BungeeAuth-Minecraft">...</a>
|
||||
* <p>
|
||||
* GitHub: https://github.com/vik1395/BungeeAuth-Minecraft
|
||||
*
|
||||
* Project page:
|
||||
* <p>
|
||||
* Spigot:
|
||||
* <a href="https://www.spigotmc.org/resources/bungeeauth.493/">...</a>
|
||||
*
|
||||
* Spigot: https://www.spigotmc.org/resources/bungeeauth.493/
|
||||
*/
|
||||
public class BungeeAuthHook implements AuthPlugin<ProxiedPlayer> {
|
||||
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package com.github.games647.fastlogin.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:
|
||||
* https://github.com/xXSchrandXx/SpigotPlugins/tree/master/BungeeCordAuthenticator
|
||||
* <p>
|
||||
* Project page:
|
||||
* <p>
|
||||
* Spigot: https://www.spigotmc.org/resources/bungeecordauthenticator.87669/
|
||||
*/
|
||||
public class BungeeCordAuthenticatorBungeeHook implements AuthPlugin<ProxiedPlayer> {
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package com.github.games647.fastlogin.bungee.hook;
|
||||
|
||||
import com.github.games647.fastlogin.bungee.FastLoginBungee;
|
||||
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import red.mohist.sodionauth.bungee.implementation.BungeePlayer;
|
||||
import red.mohist.sodionauth.core.SodionAuthApi;
|
||||
import red.mohist.sodionauth.core.exception.AuthenticatedException;
|
||||
|
||||
/**
|
||||
* GitHub: https://github.com/Mohist-Community/SodionAuth
|
||||
* <p>
|
||||
* Project page: https://gitea.e-loli.com/SodionAuth/SodionAuth
|
||||
* <p>
|
||||
* Bukkit: Unknown
|
||||
* <p>
|
||||
* Spigot: https://www.spigotmc.org/resources/sodionauth.76944/
|
||||
*/
|
||||
public class SodionAuthHook implements AuthPlugin<ProxiedPlayer> {
|
||||
|
||||
private final FastLoginBungee plugin;
|
||||
|
||||
public SodionAuthHook(FastLoginBungee plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean forceLogin(ProxiedPlayer player) {
|
||||
try {
|
||||
SodionAuthApi.login(new BungeePlayer(player));
|
||||
} catch (AuthenticatedException e) {
|
||||
plugin.getLog().warn(ALREADY_AUTHENTICATED, player);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean forceRegister(ProxiedPlayer player, String password) {
|
||||
try{
|
||||
return SodionAuthApi.register(new BungeePlayer(player), password);
|
||||
} catch (UnsupportedOperationException e){
|
||||
plugin.getLog().warn("Currently SodionAuth is not accepting forceRegister, " +
|
||||
"It may be caused by unsupported AuthBackend");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRegistered(String playerName) {
|
||||
return SodionAuthApi.isRegistered(playerName);
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2024 games647 and contributors
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -31,13 +31,18 @@ import com.github.games647.fastlogin.bungee.FastLoginBungee;
|
||||
import com.github.games647.fastlogin.bungee.task.AsyncPremiumCheck;
|
||||
import com.github.games647.fastlogin.bungee.task.FloodgateAuthTask;
|
||||
import com.github.games647.fastlogin.bungee.task.ForceLoginTask;
|
||||
import com.github.games647.fastlogin.core.antibot.AntiBotService;
|
||||
import com.github.games647.fastlogin.core.antibot.AntiBotService.Action;
|
||||
import com.github.games647.fastlogin.core.RateLimiter;
|
||||
import com.github.games647.fastlogin.core.StoredProfile;
|
||||
import com.github.games647.fastlogin.core.hooks.bedrock.FloodgateService;
|
||||
import com.github.games647.fastlogin.core.shared.LoginSession;
|
||||
import com.github.games647.fastlogin.core.storage.StoredProfile;
|
||||
import com.google.common.base.Throwables;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodHandles.Lookup;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.UUID;
|
||||
|
||||
import net.md_5.bungee.api.connection.PendingConnection;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.api.connection.Server;
|
||||
@@ -48,20 +53,14 @@ import net.md_5.bungee.api.event.ServerConnectedEvent;
|
||||
import net.md_5.bungee.api.plugin.Listener;
|
||||
import net.md_5.bungee.connection.InitialHandler;
|
||||
import net.md_5.bungee.connection.LoginResult;
|
||||
import net.md_5.bungee.connection.LoginResult.Property;
|
||||
import net.md_5.bungee.event.EventHandler;
|
||||
import net.md_5.bungee.event.EventPriority;
|
||||
import net.md_5.bungee.protocol.Property;
|
||||
|
||||
import org.geysermc.floodgate.api.player.FloodgatePlayer;
|
||||
import org.slf4j.Logger;
|
||||
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
|
||||
* order to clear that the connection is online mode.
|
||||
@@ -69,54 +68,36 @@ import java.util.UUID;
|
||||
public class ConnectListener implements Listener {
|
||||
|
||||
private static final String UUID_FIELD_NAME = "uniqueId";
|
||||
protected static final MethodHandle UNIQUE_ID_SETTER;
|
||||
|
||||
private static final String REWRITE_ID_NAME = "rewriteId";
|
||||
protected static final MethodHandle REWRITE_ID_SETTER;
|
||||
private static final MethodHandle uniqueIdSetter;
|
||||
|
||||
static {
|
||||
MethodHandle uniqueIdHandle = null;
|
||||
MethodHandle rewriterHandle = null;
|
||||
MethodHandle setHandle = null;
|
||||
try {
|
||||
Lookup lookup = MethodHandles.lookup();
|
||||
|
||||
// test for implementation class availability
|
||||
Class.forName("net.md_5.bungee.connection.InitialHandler");
|
||||
uniqueIdHandle = getHandlerSetter(lookup, UUID_FIELD_NAME);
|
||||
try {
|
||||
rewriterHandle = getHandlerSetter(lookup, REWRITE_ID_NAME);
|
||||
} catch (NoSuchFieldException noSuchFieldEx) {
|
||||
Logger logger = LoggerFactory.getLogger(ConnectListener.class);
|
||||
logger.error(
|
||||
"Rewrite field not found. Setting only legacy BungeeCord field"
|
||||
);
|
||||
}
|
||||
|
||||
Field uuidField = InitialHandler.class.getDeclaredField(UUID_FIELD_NAME);
|
||||
uuidField.setAccessible(true);
|
||||
setHandle = lookup.unreflectSetter(uuidField);
|
||||
} catch (ReflectiveOperationException reflectiveOperationException) {
|
||||
Logger logger = LoggerFactory.getLogger(ConnectListener.class);
|
||||
logger.error(
|
||||
"Cannot find Bungee UUID field implementation; Disabling premium UUID and skin won't work.",
|
||||
reflectiveOperationException
|
||||
"Cannot find Bungee initial handler; Disabling premium UUID and skin won't work.",
|
||||
reflectiveOperationException
|
||||
);
|
||||
}
|
||||
|
||||
UNIQUE_ID_SETTER = uniqueIdHandle;
|
||||
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);
|
||||
uniqueIdSetter = setHandle;
|
||||
}
|
||||
|
||||
private final FastLoginBungee plugin;
|
||||
private final AntiBotService antiBotService;
|
||||
private final RateLimiter rateLimiter;
|
||||
private final Property[] emptyProperties = {};
|
||||
|
||||
public ConnectListener(FastLoginBungee plugin, AntiBotService antiBotService) {
|
||||
public ConnectListener(FastLoginBungee plugin, RateLimiter rateLimiter) {
|
||||
this.plugin = plugin;
|
||||
this.antiBotService = antiBotService;
|
||||
this.rateLimiter = rateLimiter;
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
@@ -126,28 +107,17 @@ public class ConnectListener implements Listener {
|
||||
return;
|
||||
}
|
||||
|
||||
InetSocketAddress address = preLoginEvent.getConnection().getAddress();
|
||||
String username = connection.getName();
|
||||
if (!rateLimiter.tryAcquire()) {
|
||||
plugin.getLog().warn("Simple Anti-Bot join limit - Ignoring {}", connection);
|
||||
return;
|
||||
}
|
||||
|
||||
String username = connection.getName();
|
||||
plugin.getLog().info("Incoming login request for {} from {}", username, connection.getSocketAddress());
|
||||
|
||||
Action action = antiBotService.onIncomingConnection(address, username);
|
||||
switch (action) {
|
||||
case Ignore:
|
||||
// just ignore
|
||||
return;
|
||||
case Block:
|
||||
String message = plugin.getCore().getMessage("kick-antibot");
|
||||
preLoginEvent.setCancelReason(TextComponent.fromLegacyText(message));
|
||||
preLoginEvent.setCancelled(true);
|
||||
break;
|
||||
case Continue:
|
||||
default:
|
||||
preLoginEvent.registerIntent(plugin);
|
||||
Runnable asyncPremiumCheck = new AsyncPremiumCheck(plugin, preLoginEvent, connection, username);
|
||||
plugin.getScheduler().runAsync(asyncPremiumCheck);
|
||||
break;
|
||||
}
|
||||
preLoginEvent.registerIntent(plugin);
|
||||
Runnable asyncPremiumCheck = new AsyncPremiumCheck(plugin, preLoginEvent, connection, username);
|
||||
plugin.getScheduler().runAsync(asyncPremiumCheck);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOWEST)
|
||||
@@ -170,8 +140,8 @@ public class ConnectListener implements Listener {
|
||||
StoredProfile playerProfile = session.getProfile();
|
||||
playerProfile.setId(verifiedUUID);
|
||||
|
||||
// BungeeCord will do this automatically so override it on disabled option
|
||||
if (UNIQUE_ID_SETTER != null) {
|
||||
// bungeecord will do this automatically so override it on disabled option
|
||||
if (uniqueIdSetter != null) {
|
||||
InitialHandler initialHandler = (InitialHandler) connection;
|
||||
|
||||
if (!plugin.getCore().getConfig().get("premiumUuid", true)) {
|
||||
@@ -187,7 +157,7 @@ public class ConnectListener implements Listener {
|
||||
}
|
||||
}
|
||||
|
||||
protected void setOfflineId(InitialHandler connection, String username) {
|
||||
private void setOfflineId(InitialHandler connection, String username) {
|
||||
try {
|
||||
UUID oldPremiumId = connection.getUniqueId();
|
||||
UUID offlineUUID = UUIDAdapter.generateOfflineId(username);
|
||||
@@ -195,13 +165,7 @@ public class ConnectListener implements Listener {
|
||||
// BungeeCord only allows setting the UUID in PreLogin events and before requesting online mode
|
||||
// However if online mode is requested, it will override previous values
|
||||
// So we have to do it with reflection
|
||||
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);
|
||||
}
|
||||
uniqueIdSetter.invokeExact(connection, offlineUUID);
|
||||
|
||||
String format = "Overridden UUID from {} to {} (based of {}) on {}";
|
||||
plugin.getLog().info(format, oldPremiumId, offlineUUID, username, connection);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2024 games647 and contributors
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -28,14 +28,17 @@ package com.github.games647.fastlogin.bungee.listener;
|
||||
import com.github.games647.fastlogin.bungee.BungeeLoginSession;
|
||||
import com.github.games647.fastlogin.bungee.FastLoginBungee;
|
||||
import com.github.games647.fastlogin.bungee.task.AsyncToggleMessage;
|
||||
import com.github.games647.fastlogin.core.StoredProfile;
|
||||
import com.github.games647.fastlogin.core.hooks.bedrock.FloodgateService;
|
||||
import com.github.games647.fastlogin.core.message.ChangePremiumMessage;
|
||||
import com.github.games647.fastlogin.core.message.NamespaceKey;
|
||||
import com.github.games647.fastlogin.core.message.SuccessMessage;
|
||||
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.ByteStreams;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import net.md_5.bungee.api.CommandSender;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
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.event.EventHandler;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class PluginMessageListener implements Listener {
|
||||
|
||||
private final FastLoginBungee plugin;
|
||||
@@ -131,7 +132,7 @@ public class PluginMessageListener implements Listener {
|
||||
loginSession.setRegistered(true);
|
||||
|
||||
if (!loginSession.isAlreadySaved()) {
|
||||
playerProfile.setOnlinemodePreferred(true);
|
||||
playerProfile.setPremium(true);
|
||||
plugin.getCore().getStorage().save(playerProfile);
|
||||
loginSession.setAlreadySaved(true);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2024 games647 and contributors
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -29,9 +29,10 @@ import com.github.games647.fastlogin.bungee.BungeeLoginSession;
|
||||
import com.github.games647.fastlogin.bungee.BungeeLoginSource;
|
||||
import com.github.games647.fastlogin.bungee.FastLoginBungee;
|
||||
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.event.FastLoginPreLoginEvent;
|
||||
import com.github.games647.fastlogin.core.storage.StoredProfile;
|
||||
|
||||
import net.md_5.bungee.api.CommandSender;
|
||||
import net.md_5.bungee.api.connection.PendingConnection;
|
||||
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));
|
||||
|
||||
String ip = source.getAddress().getAddress().getHostAddress();
|
||||
plugin.getCore().addLoginAttempt(ip, username);
|
||||
plugin.getCore().getPendingLogin().put(ip + username, new Object());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2024 games647 and contributors
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -27,9 +27,10 @@ package com.github.games647.fastlogin.bungee.task;
|
||||
|
||||
import com.github.games647.fastlogin.bungee.FastLoginBungee;
|
||||
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.event.FastLoginPremiumToggleEvent.PremiumToggleReason;
|
||||
import com.github.games647.fastlogin.core.storage.StoredProfile;
|
||||
import net.md_5.bungee.api.CommandSender;
|
||||
import net.md_5.bungee.api.ProxyServer;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
@@ -64,37 +65,32 @@ public class AsyncToggleMessage implements Runnable {
|
||||
private void turnOffPremium() {
|
||||
StoredProfile playerProfile = core.getStorage().loadProfile(targetPlayer);
|
||||
//existing player is already cracked
|
||||
if (playerProfile.isExistingPlayer() && !playerProfile.isOnlinemodePreferred()) {
|
||||
if (playerProfile.isSaved() && !playerProfile.isPremium()) {
|
||||
sendMessage("not-premium");
|
||||
return;
|
||||
}
|
||||
|
||||
playerProfile.setOnlinemodePreferred(false);
|
||||
playerProfile.setPremium(false);
|
||||
playerProfile.setId(null);
|
||||
core.getStorage().save(playerProfile);
|
||||
PremiumToggleReason reason = (!isPlayerSender || !sender.getName().equalsIgnoreCase(playerProfile.getName()))
|
||||
? PremiumToggleReason.COMMAND_OTHER : PremiumToggleReason.COMMAND_SELF;
|
||||
PremiumToggleReason reason = (!isPlayerSender || !sender.getName().equalsIgnoreCase(playerProfile.getName())) ?
|
||||
PremiumToggleReason.COMMAND_OTHER : PremiumToggleReason.COMMAND_SELF;
|
||||
core.getPlugin().getProxy().getPluginManager().callEvent(
|
||||
new BungeeFastLoginPremiumToggleEvent(playerProfile, reason));
|
||||
|
||||
if (isPlayerSender && core.getConfig().getBoolean("kick-toggle", true)) {
|
||||
sender.disconnect(TextComponent.fromLegacyText(core.getMessage("remove-premium")));
|
||||
} else {
|
||||
sendMessage("remove-premium");
|
||||
}
|
||||
sendMessage("remove-premium");
|
||||
}
|
||||
|
||||
private void activatePremium() {
|
||||
StoredProfile playerProfile = core.getStorage().loadProfile(targetPlayer);
|
||||
if (playerProfile.isOnlinemodePreferred()) {
|
||||
if (playerProfile.isPremium()) {
|
||||
sendMessage("already-exists");
|
||||
return;
|
||||
}
|
||||
|
||||
playerProfile.setOnlinemodePreferred(true);
|
||||
playerProfile.setPremium(true);
|
||||
core.getStorage().save(playerProfile);
|
||||
PremiumToggleReason reason = (!isPlayerSender || !sender.getName().equalsIgnoreCase(playerProfile.getName()))
|
||||
? PremiumToggleReason.COMMAND_OTHER : PremiumToggleReason.COMMAND_SELF;
|
||||
PremiumToggleReason reason = (!isPlayerSender || !sender.getName().equalsIgnoreCase(playerProfile.getName())) ?
|
||||
PremiumToggleReason.COMMAND_OTHER : PremiumToggleReason.COMMAND_SELF;
|
||||
core.getPlugin().getProxy().getPluginManager().callEvent(
|
||||
new BungeeFastLoginPremiumToggleEvent(playerProfile, reason));
|
||||
sendMessage("add-premium");
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2024 games647 and contributors
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -25,17 +25,19 @@
|
||||
*/
|
||||
package com.github.games647.fastlogin.bungee.task;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.geysermc.floodgate.api.player.FloodgatePlayer;
|
||||
|
||||
import net.md_5.bungee.api.CommandSender;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.api.connection.Server;
|
||||
|
||||
import com.github.games647.fastlogin.bungee.BungeeLoginSession;
|
||||
import com.github.games647.fastlogin.bungee.FastLoginBungee;
|
||||
import com.github.games647.fastlogin.core.shared.FastLoginCore;
|
||||
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.util.UUID;
|
||||
|
||||
public class FloodgateAuthTask
|
||||
extends FloodgateManagement<ProxiedPlayer, CommandSender, BungeeLoginSession, FastLoginBungee> {
|
||||
@@ -53,9 +55,13 @@ public class FloodgateAuthTask
|
||||
BungeeLoginSession session = new BungeeLoginSession(player.getName(), isRegistered, profile);
|
||||
core.getPlugin().getSession().put(player.getPendingConnection(), session);
|
||||
|
||||
// enable auto login based on the value of 'autoLoginFloodgate' in config.yml
|
||||
boolean forcedOnlineMode = autoLoginFloodgate.equals("true")
|
||||
|| (autoLoginFloodgate.equals("linked") && isLinked);
|
||||
|
||||
// run login task
|
||||
Runnable forceLoginTask = new ForceLoginTask(core.getPlugin().getCore(), player, server, session,
|
||||
isAutoAuthAllowed(autoLoginFloodgate));
|
||||
forcedOnlineMode);
|
||||
core.getPlugin().getScheduler().runAsync(forceLoginTask);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2024 games647 and contributors
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -28,6 +28,7 @@ package com.github.games647.fastlogin.bungee.task;
|
||||
import com.github.games647.fastlogin.bungee.BungeeLoginSession;
|
||||
import com.github.games647.fastlogin.bungee.FastLoginBungee;
|
||||
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.LoginActionMessage;
|
||||
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.LoginSession;
|
||||
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.ProxyServer;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.api.connection.Server;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class ForceLoginTask
|
||||
extends ForceLoginManagement<ProxiedPlayer, CommandSender, BungeeLoginSession, FastLoginBungee> {
|
||||
|
||||
|
||||
@@ -11,6 +11,8 @@ author: games647, https://github.com/games647/FastLogin/graphs/contributors
|
||||
softDepends:
|
||||
# BungeeCord auth plugins
|
||||
- BungeeAuth
|
||||
- BungeeCordAuthenticatorBungee
|
||||
- SodionAuth
|
||||
# Bedrock Player Bridge
|
||||
- Geyser-BungeeCord
|
||||
- floodgate
|
||||
|
||||
@@ -1,57 +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.bungee.listener;
|
||||
|
||||
import net.md_5.bungee.BungeeCord;
|
||||
import net.md_5.bungee.conf.Configuration;
|
||||
import net.md_5.bungee.connection.InitialHandler;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
class ConnectListenerTest {
|
||||
|
||||
@Test
|
||||
void testUUIDSetter() throws Throwable {
|
||||
BungeeCord proxyMock = mock(BungeeCord.class);
|
||||
BungeeCord.setInstance(proxyMock);
|
||||
|
||||
Configuration configMock = mock(Configuration.class);
|
||||
Field configField = proxyMock.getClass().getField("config");
|
||||
configField.setAccessible(true);
|
||||
configField.set(proxyMock, configMock);
|
||||
|
||||
InitialHandler handler = new InitialHandler(proxyMock, null);
|
||||
|
||||
UUID expectedUUID = UUID.randomUUID();
|
||||
ConnectListener.UNIQUE_ID_SETTER.invokeExact(handler, expectedUUID);
|
||||
assertEquals(expectedUUID, handler.getUniqueId());
|
||||
}
|
||||
}
|
||||
202
checkstyle.xml
202
checkstyle.xml
@@ -1,202 +0,0 @@
|
||||
<?xml version="1.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.
|
||||
|
||||
-->
|
||||
<!DOCTYPE module PUBLIC
|
||||
"-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"
|
||||
"https://checkstyle.org/dtds/configuration_1_3.dtd">
|
||||
<module name="Checker">
|
||||
<!--
|
||||
If you set the basedir property below, then all reported file
|
||||
names will be relative to the specified directory. See
|
||||
https://checkstyle.org/config.html#Checker
|
||||
|
||||
<property name="basedir" value="${basedir}"/>
|
||||
-->
|
||||
<property name="severity" value="error"/>
|
||||
|
||||
<property name="fileExtensions" value="java, properties, xml"/>
|
||||
|
||||
<!-- Excludes all 'module-info.java' files -->
|
||||
<!-- See https://checkstyle.org/config_filefilters.html -->
|
||||
<module name="BeforeExecutionExclusionFileFilter">
|
||||
<property name="fileNamePattern" value="module\-info\.java$"/>
|
||||
</module>
|
||||
|
||||
<!-- https://checkstyle.org/config_filters.html#SuppressionFilter -->
|
||||
<module name="SuppressionFilter">
|
||||
<property name="file" value="${org.checkstyle.sun.suppressionfilter.config}"
|
||||
default="checkstyle-suppressions.xml" />
|
||||
<property name="optional" value="true"/>
|
||||
</module>
|
||||
|
||||
<!-- Checks that a package-info.java file exists for each package. -->
|
||||
<!-- See https://checkstyle.org/config_javadoc.html#JavadocPackage -->
|
||||
<!--<module name="JavadocPackage"/>-->
|
||||
|
||||
<!-- Checks whether files end with a new line. -->
|
||||
<!-- See https://checkstyle.org/config_misc.html#NewlineAtEndOfFile -->
|
||||
<module name="NewlineAtEndOfFile"/>
|
||||
|
||||
<!-- Checks that property files contain the same keys. -->
|
||||
<!-- See https://checkstyle.org/config_misc.html#Translation -->
|
||||
<module name="Translation"/>
|
||||
|
||||
<!-- Checks for Size Violations. -->
|
||||
<!-- See https://checkstyle.org/config_sizes.html -->
|
||||
<module name="FileLength"/>
|
||||
<module name="LineLength">
|
||||
<property name="max" value="120"/>
|
||||
<property name="fileExtensions" value="java"/>
|
||||
<property name="ignorePattern" value="^ *\* *@see.+$"/>
|
||||
</module>
|
||||
|
||||
<!-- Checks for whitespace -->
|
||||
<!-- See https://checkstyle.org/config_whitespace.html -->
|
||||
<module name="FileTabCharacter"/>
|
||||
|
||||
<!-- Miscellaneous other checks. -->
|
||||
<!-- See https://checkstyle.org/config_misc.html -->
|
||||
<module name="RegexpSingleline">
|
||||
<property name="format" value="\s+$"/>
|
||||
<property name="minimum" value="0"/>
|
||||
<property name="maximum" value="0"/>
|
||||
<property name="message" value="Line has trailing spaces."/>
|
||||
</module>
|
||||
|
||||
<!-- Checks for Headers -->
|
||||
<!-- See https://checkstyle.org/config_header.html -->
|
||||
<!-- <module name="Header"> -->
|
||||
<!-- <property name="headerFile" value="${checkstyle.header.file}"/> -->
|
||||
<!-- <property name="fileExtensions" value="java"/> -->
|
||||
<!-- </module> -->
|
||||
|
||||
<module name="TreeWalker">
|
||||
|
||||
<!-- Checks for Javadoc comments. -->
|
||||
<!-- See https://checkstyle.org/config_javadoc.html -->
|
||||
<module name="InvalidJavadocPosition"/>
|
||||
<module name="JavadocMethod"/>
|
||||
<!--<module name="JavadocType"/>-->
|
||||
<!--<module name="JavadocVariable"/>-->
|
||||
<!--<module name="JavadocStyle"/>-->
|
||||
<!--<module name="MissingJavadocMethod"/>-->
|
||||
|
||||
<!-- Checks for Naming Conventions. -->
|
||||
<!-- See https://checkstyle.org/config_naming.html -->
|
||||
<module name="ConstantName"/>
|
||||
<module name="LocalFinalVariableName"/>
|
||||
<module name="LocalVariableName"/>
|
||||
<module name="MemberName"/>
|
||||
<module name="MethodName"/>
|
||||
<module name="PackageName"/>
|
||||
<module name="ParameterName"/>
|
||||
<module name="StaticVariableName"/>
|
||||
<module name="TypeName"/>
|
||||
|
||||
<!-- Checks for imports -->
|
||||
<!-- See https://checkstyle.org/config_imports.html -->
|
||||
<module name="AvoidStarImport"/>
|
||||
<module name="IllegalImport"/> <!-- defaults to sun.* packages -->
|
||||
<module name="RedundantImport"/>
|
||||
<module name="UnusedImports">
|
||||
<property name="processJavadoc" value="false"/>
|
||||
</module>
|
||||
|
||||
<!-- Checks for Size Violations. -->
|
||||
<!-- See https://checkstyle.org/config_sizes.html -->
|
||||
<module name="MethodLength"/>
|
||||
<module name="ParameterNumber"/>
|
||||
|
||||
<!-- Checks for whitespace -->
|
||||
<!-- See https://checkstyle.org/config_whitespace.html -->
|
||||
<module name="EmptyForIteratorPad"/>
|
||||
<module name="GenericWhitespace"/>
|
||||
<module name="MethodParamPad"/>
|
||||
<module name="NoWhitespaceAfter"/>
|
||||
<module name="NoWhitespaceBefore"/>
|
||||
<module name="OperatorWrap"/>
|
||||
<module name="ParenPad"/>
|
||||
<module name="TypecastParenPad"/>
|
||||
<module name="WhitespaceAfter"/>
|
||||
<module name="WhitespaceAround"/>
|
||||
|
||||
<!-- Modifier Checks -->
|
||||
<!-- See https://checkstyle.org/config_modifier.html -->
|
||||
<module name="ModifierOrder"/>
|
||||
<module name="RedundantModifier"/>
|
||||
|
||||
<!-- Checks for blocks. You know, those {}'s -->
|
||||
<!-- See https://checkstyle.org/config_blocks.html -->
|
||||
<module name="AvoidNestedBlocks"/>
|
||||
<module name="EmptyBlock"/>
|
||||
<module name="LeftCurly"/>
|
||||
<module name="NeedBraces"/>
|
||||
<module name="RightCurly"/>
|
||||
|
||||
<!-- Checks for common coding problems -->
|
||||
<!-- See https://checkstyle.org/config_coding.html -->
|
||||
<module name="EmptyStatement"/>
|
||||
<module name="EqualsHashCode"/>
|
||||
<module name="IllegalInstantiation"/>
|
||||
<module name="InnerAssignment"/>
|
||||
<!--<module name="MagicNumber"/>-->
|
||||
<module name="MissingSwitchDefault"/>
|
||||
<module name="MultipleVariableDeclarations"/>
|
||||
<module name="SimplifyBooleanExpression"/>
|
||||
<module name="SimplifyBooleanReturn"/>
|
||||
|
||||
<!-- Checks for class design -->
|
||||
<!-- See https://checkstyle.org/config_design.html -->
|
||||
<!--<module name="DesignForExtension"/>-->
|
||||
<module name="FinalClass"/>
|
||||
<module name="HideUtilityClassConstructor"/>
|
||||
<module name="InterfaceIsType"/>
|
||||
|
||||
<!-- Miscellaneous other checks. -->
|
||||
<!-- See https://checkstyle.org/config_misc.html -->
|
||||
<module name="ArrayTypeStyle"/>
|
||||
<!--<module name="FinalParameters"/>-->
|
||||
<!-- <module name="TodoComment"/>-->
|
||||
<module name="UpperEll"/>
|
||||
|
||||
<!-- https://checkstyle.org/config_filters.html#SuppressionXpathFilter -->
|
||||
<module name="SuppressionXpathFilter">
|
||||
<property name="file" value="${org.checkstyle.sun.suppressionxpathfilter.config}"
|
||||
default="checkstyle-xpath-suppressions.xml" />
|
||||
<property name="optional" value="true"/>
|
||||
</module>
|
||||
|
||||
<!-- Suppress filters via comments -->
|
||||
<!-- https://stackoverflow.com/a/4023351/9767089 -->
|
||||
<module name="SuppressionCommentFilter">
|
||||
<property name="offCommentFormat" value="CHECKSTYLE.OFF\: ([\w\|]+)"/>
|
||||
<property name="onCommentFormat" value="CHECKSTYLE.ON\: ([\w\|]+)"/>
|
||||
<property name="checkFormat" value="$1"/>
|
||||
</module>
|
||||
</module>
|
||||
</module>
|
||||
146
core/pom.xml
146
core/pom.xml
@@ -4,7 +4,7 @@
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015-2024 games647 and contributors
|
||||
Copyright (c) 2015-2021 <Your name and contributors>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -25,31 +25,26 @@
|
||||
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">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>com.github.games647</groupId>
|
||||
<artifactId>fastlogin</artifactId>
|
||||
<version>1.12-SNAPSHOT</version>
|
||||
<version>1.11-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>fastlogin.core</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<properties>
|
||||
<!-- Still force Java 8 for the remaining project -->
|
||||
<maven.compiler.release>8</maven.compiler.release>
|
||||
</properties>
|
||||
|
||||
<name>FastLoginCore</name>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>luck-repo</id>
|
||||
<url>https://ci.lucko.me/plugin/repository/everything/</url>
|
||||
<url>https://ci.lucko.me/plugin/repository/everything</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
@@ -60,64 +55,11 @@
|
||||
</repository>
|
||||
<!-- Floodgate -->
|
||||
<repository>
|
||||
<id>opencollab</id>
|
||||
<id>opencollab-snapshot</id>
|
||||
<url>https://repo.opencollab.dev/maven-snapshots/</url>
|
||||
<releases>
|
||||
<enabled>false</enabled>
|
||||
</releases>
|
||||
</repository>
|
||||
</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>
|
||||
<!-- Libraries that we shade into the project -->
|
||||
|
||||
@@ -140,21 +82,20 @@
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-jdk14</artifactId>
|
||||
<version>2.0.17</version>
|
||||
<version>2.0.0-alpha6</version>
|
||||
</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>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-config</artifactId>
|
||||
<version>1.20-R0.2</version>
|
||||
</dependency>
|
||||
|
||||
<!-- This is optional in BungeeCord-config, so we include it here manually -->
|
||||
<dependency>
|
||||
<groupId>org.yaml</groupId>
|
||||
<artifactId>snakeyaml</artifactId>
|
||||
<version>2.4</version>
|
||||
<version>1.16-R0.4</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>*</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!--Floodgate for Xbox Live Authentication-->
|
||||
@@ -165,7 +106,11 @@
|
||||
<scope>provided</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>*</groupId>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.geysermc.cumulus</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
@@ -173,7 +118,7 @@
|
||||
|
||||
<!-- Bedrock player bridge -->
|
||||
<dependency>
|
||||
<groupId>org.geysermc.geyser</groupId>
|
||||
<groupId>org.geysermc</groupId>
|
||||
<artifactId>core</artifactId>
|
||||
<version>${geyser.version}</version>
|
||||
<scope>provided</scope>
|
||||
@@ -187,31 +132,54 @@
|
||||
|
||||
<!-- We need the API, but it was excluded above -->
|
||||
<dependency>
|
||||
<groupId>org.geysermc.geyser</groupId>
|
||||
<artifactId>api</artifactId>
|
||||
<groupId>org.geysermc</groupId>
|
||||
<artifactId>geyser-api</artifactId>
|
||||
<version>${geyser.version}</version>
|
||||
<scope>provided</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>*</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!--Common component for contacting the Mojang API-->
|
||||
<dependency>
|
||||
<groupId>com.github.games647</groupId>
|
||||
<artifactId>craftapi</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<version>0.5.1</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Database driver included in Spigot -->
|
||||
<!-- APIs we can use because they are available in all platforms (Spigot, Bungee, Velocity) -->
|
||||
<dependency>
|
||||
<groupId>org.xerial</groupId>
|
||||
<artifactId>sqlite-jdbc</artifactId>
|
||||
<version>[3.36,)</version>
|
||||
<scope>provided</scope>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<!-- Old version for velocity -->
|
||||
<version>25.1-jre</version>
|
||||
<!-- Exclude compile time dependencies not marked as such on upstream -->
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.google.code.findbugs</groupId>
|
||||
<artifactId>jsr305</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.checkerframework</groupId>
|
||||
<artifactId>checker-qual</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.google.errorprone</groupId>
|
||||
<artifactId>error_prone_annotations</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.google.j2objc</groupId>
|
||||
<artifactId>j2objc-annotations</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>animal-sniffer-annotations</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.9.0</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015-2021 <Your name and contributors>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package com.github.games647.fastlogin.core;
|
||||
|
||||
import com.google.common.util.concurrent.MoreExecutors;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
|
||||
/**
|
||||
* This limits the number of threads that are used at maximum. Thread creation can be very heavy for the CPU and
|
||||
* context switching between threads too. However, we need many threads for blocking HTTP and database calls.
|
||||
* Nevertheless, this number can be further limited, because the number of actually working database threads
|
||||
* is limited by the size of our database pool. The goal is to separate concerns into processing and blocking only
|
||||
* threads.
|
||||
*/
|
||||
public class AsyncScheduler {
|
||||
|
||||
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 ExecutorService processingPool;
|
||||
|
||||
/*
|
||||
private final ExecutorService databaseExecutor = new ThreadPoolExecutor(1, 10,
|
||||
0L, TimeUnit.MILLISECONDS,
|
||||
new LinkedBlockingQueue<>(MAX_CAPACITY));
|
||||
*/
|
||||
|
||||
public AsyncScheduler(Logger logger, ThreadFactory threadFactory) {
|
||||
this.logger = logger;
|
||||
processingPool = new ThreadPoolExecutor(6, 32,
|
||||
60L, TimeUnit.SECONDS,
|
||||
new LinkedBlockingQueue<>(MAX_CAPACITY), threadFactory);
|
||||
}
|
||||
|
||||
/*
|
||||
public <R> CompletableFuture<R> runDatabaseTask(Supplier<R> databaseTask) {
|
||||
return CompletableFuture.supplyAsync(databaseTask, databaseExecutor)
|
||||
.exceptionally(error -> {
|
||||
logger.warn("Error occurred on thread pool", error);
|
||||
return null;
|
||||
})
|
||||
// change context to the processing pool
|
||||
.thenApplyAsync(r -> r, processingPool);
|
||||
}
|
||||
*/
|
||||
|
||||
public CompletableFuture<Void> runAsync(Runnable task) {
|
||||
return CompletableFuture.runAsync(task, processingPool).exceptionally(error -> {
|
||||
logger.warn("Error occurred on thread pool", error);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
MoreExecutors.shutdownAndAwaitTermination(processingPool, 1, TimeUnit.MINUTES);
|
||||
//MoreExecutors.shutdownAndAwaitTermination(databaseExecutor, 1, TimeUnit.MINUTES);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user