Compare commits

..

6 Commits

141 changed files with 1110 additions and 12613 deletions

View File

@@ -1,68 +0,0 @@
name: 🐞 Bug Report
description: Something isn't working, broken, not expected behavior
labels: [bug]
body:
- type: markdown
attributes:
value: |
This ticket is about bugs, so broken, not expected behavior. Feedback about this form is appreciated.
- type: textarea
attributes:
label: What happened?
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
description: The actions that cause the issues. Please explain it in detail.
- type: input
attributes:
label: Plugin list
description: This can be found by running `/pl`
placeholder: AuthMe, ProtocolLib, ...
- type: input
attributes:
label: Configuration file
description: |
Link to the contents of your config.yml file.
You can use [GitHub](https://gist.github.com/), [Hastebin](https://hastebin.com) or similar for that.
placeholder: https://gist.github.com/games647/88c4439e1cd7810f21318b1b24a04ee0
- type: textarea
attributes:
label: Server log
description: The error, stacktrace or link the complete log. You can use the links above for long versions.
render: shell
placeholder: https://hastebin.com/ / https://gist.github.com/
- type: input
attributes:
label: Plugin version
description: Plugin version or build number. This can be found by running `/version plugin-name`
placeholder: v3.1-SNAPSHOT-570b321
validations:
required: true
- type: dropdown
attributes:
label: Platform
description: Server software - choose your proxy software if you have multiple servers
options:
- Spigot
- BungeeCord
- Velocity
validations:
required: true
- type: checkboxes
attributes:
label: Relevance
description: Check list for previous tickets
options:
- label: I tried the latest build
required: true
- label: |
I checked for existing tickets -
If there are, please vote them with a thumps reaction and not create new ones
required: true

View File

@@ -1,11 +0,0 @@
# General configuration for issue templates
# Allow issues without a template
#blank_issues_enabled: false
# Extra section on creating issues to redirect to another site
contact_links:
- name: 📌 Questions
url: https://github.com/games647/FastLogin/discussions
about: You want to ask something

View File

@@ -1,25 +0,0 @@
---
name: 💡 Enhancement request
about: New feature or change request
title: ''
labels: 'enhancement'
assignees: ''
---
[//]: # (Lines in this format are considered as comments and will not be displayed.)
[//]: # (Before reporting make sure you're running the **latest build** of the plugin and checked for existing issues!)
[//]: # (This ticket is about suggestions for a feature or particular enhancement)
### Is your feature request related to a problem? Please describe.
[//]: # (A clear and concise description of what the problem is. Ex. I'm always frustrated when [...])
### Describe the solution you'd like
[//]: # (A clear and concise description of what you want to happen.)
### Describe alternatives you've considered
[//]: # (A clear and concise description of any alternative solutions or features you've considered.)
### Additional context
[//]: # (Add any other context or screenshots about the feature request here.)

View File

@@ -1,35 +0,0 @@
version: 2
updates:
# Updates for workflow files
- package-ecosystem: "github-actions"
# Workflow files stored in the
# default location of `.github/workflows`
directory: "/"
schedule:
interval: "monthly"
# Maven project
- package-ecosystem: maven
directory: "/"
schedule:
interval: weekly
ignore:
- 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

View File

@@ -1,8 +0,0 @@
[//]: # (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: motiviation, enhancement)
### Related issue
[//]: # (Reference it using '#NUMBER'. Ex: Fixes/Related #...)

View File

@@ -1,67 +0,0 @@
# GitHub automatic code security scanning using CodeQL
# Human-readable name in the actions tab
name: "CodeQL"
on:
# Scan only for push on the primary branch for now
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
# job i
analyze:
# Display name
name: Analyze
# Environment
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
# Languages to scan
language: [ 'java' ]
steps:
- name: Checkout repository
uses: actions/checkout@v2
# Setup Java
- name: Set up JDK
uses: actions/setup-java@v2.3.0
with:
distribution: 'adopt'
# Use Java 16, because it's minimum required version by Geyser
java-version: 16
cache: 'maven'
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
# 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-
# 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

View File

@@ -1,43 +0,0 @@
# Automatically build, run unit and integration tests to detect errors early (CI provided by GitHub)
# including making pull requests review easier
# Human-readable name in the actions tab
name: Java CI
# Build on every pull request regardless of the branch
# Wiki: https://help.github.com/en/actions/reference/events-that-trigger-workflows
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
# job id
build_and_test:
# Environment image - always use the newest OS
runs-on: ubuntu-latest
# Run steps
steps:
# Pull changes
- uses: actions/checkout@v2.3.4
# Setup Java
- name: Set up JDK
uses: actions/setup-java@v2.3.0
with:
distribution: 'temurin'
# Use Java 16, because it's minimum required version by Geyser
java-version: 16
cache: 'maven'
# Build and test (included in package)
- name: Build with Maven and test
# Run non-interactive, package (with compile+test),
# 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

59
.gitignore vendored
View File

@@ -1,25 +1,37 @@
# Eclipse
.classpath
.project
.settings/
# Eclipse stuff
/.classpath
/.project
/.settings
# NetBeans
nbproject/
# netbeans
/nbproject
nb-configuration.xml
# IntelliJ
# maven
/target
# vim
.*.sw[a-p]
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
# various other potential build files
/build
/bin
/dist
/manifest.mf
*.log
# Mac filesystem dust
.DS_Store
# intellij
*.iml
*.ipr
*.iws
.idea/
# VSCode
.vscode/
# Maven
target/
pom.xml.versionsBackup
# Gradle
.gradle
@@ -28,22 +40,3 @@ gradle-app.setting
# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
!gradle-wrapper.jar
# various other potential build files
build/
bin/
dist/
manifest.mf
*.log
# Vim
.*.sw[a-p]
# virtual machine crash logs, see https://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
# Mac filesystem dust
.DS_Store
# Factorypath from Visual Studio Code
.factorypath

View File

@@ -1,260 +0,0 @@
### 1.11
* TODO: Replace reflection with methodhandles
* Use direct proxies instead of ssl factories for multiple IP-addresses
* Remove local address check for multiple IP-addresses
* Fix parsing of local IP-addresses
* Fix address rotating for contacting the Mojang API
* Optimize issue template
* Use Instant for timestamps
* Migrate SLF4J logging (Fixes #177)
* Use Gson's TypeAdapter for more type safety
* Add support for IPv6 proxies
* Shared configuration implementation for easier maintained code
* Use Gson for json parsing, because it's supported on all platforms and removes code duplicates
* Clean up project code
* Drop support for deprecated AuthMe API
* Remove legacy database migration code
* Drop support for RoyalAuth, because it doesn't seem to be supported anymore
* Clean up client-server encryption -> use only one cipher per connection, simplify code
### 1.10
* Prevent authentication proxies
* Drop database importer
* More logging by default
* Add support for HTTP proxies
* Set the fake offline UUID on lowest priority (-> as soon as possible)
* Remove bungee chatcolor for Bukkit to support KCauldron
* Minor cleanup using inspections + Https
* Increase hook delay to let ProtocolLib inject the listener
* Drop support for old AuthMe API + Add support for new AuthMe API
* Remove eBean util usage to make it compatible with 1.12
* Do not try to hook into a plugin if auth plugin hook is already set using the FastLogin API
* 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
* No duplicate session login
* Fix timestamp parsing in newer versions of SQLite
* Fix Spigot console command invocation sends result to in game players
### 1.9
* Added second attempt login -> cracked login
* Added cracked whitelist (switch-mode -> switching to online-mode from offlinemode)
* Added configuration to disable auto logins for 2Factor authentication
* Added missing add-premium-other message
* Upgrade to Java 8 -> Minimize file size
* Refactored/Cleaned up a lot of code
* [API] Deprecated platform specific auth-plugin. Please use AuthPlugin< platform specific player type >
* [API] Deprecated bukkit's password generator. Please use PasswordGenerator< platform specific player type >
* Fix ProtocolSupport autoRegister
* Fix update username in FastLogin database after nameChange
* Fix logging exceptions on encryption enabling
* Fix compatibility with older ProtocolLib versions (for 1.7) because of the missing getMethodAcccessorOrNull method
* Fix correct cracked permission for bukkit
* A try to fix SQLite timestamp parsing
* Drop support for LoginSecurity 1.X since 2.X seems to be stable
* Remove the nasty UltraAuth fakeplayer workaround by using a new api method. You should UltraAuth if you have it
### 1.8
* Added autoIn importer
* Added BFA importer
* Added ElDziAuth importer
* Fix third-party not premium player detection
* Fix ProtocolSupport BungeeCord
* Fix duplicate logins for BungeeAuth users
### 1.7.1
* Fix BungeeCord autoRegister (Fixes #46)
* Fix ProtocolSupport auto-register
### 1.7
* Added support for making requests to Mojang from different IPv4 addresses
* Added us.mcapi.com as third-party APIs to workaround rate-limits
* Fixed NPE in BungeeCord on cracked session
* Fixed skin applies if premium uuid is deactivated
* 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 an premium uuid
* Fix setting skin on Cauldron
* Fix saving on name change
### 1.6.2
* Fixed support for new LoginSecurity version
### 1.6.1
* Fix message typo in BungeeCord which created a NPE if premium-warning is activated
### 1.6
* Add a warning message if the user tries to invoke the premium command
* Added missing translation if the server isn't fully started
* Removed ProtocolLib as required dependency. You can use ProtocolSupport or BungeeCord as alternative
* Reduce the number of worker threads from 5 to 3 in ProtocolLib
* Process packets in ProtocolLib async/non-blocking -> better performance
* Fixed missing translation in commands
* Fixed cracked command not working on BungeeCord
* Fix error if forward skins is disabled
### 1.5.2
* Fixed BungeeCord force logins if there is a lobby server
* Removed cache expire in BungeeCord
* Applies skin earlier to make it visible for other plugins listening on login events
### 1.5.1
* Fixed BungeeCord support by correctly saving the proxy ids
### 1.5
* Added localization
* Fixed NPE on premium name check if it's pure cracked player
* Fixed NPE in BungeeCord on cracked login for existing players
* Fixed saving of existing cracked players
### 1.4
* Added Bungee setAuthPlugin method
* Added nameChangeCheck
* Multiple BungeeCord support
### 1.3.1
* Prevent thread create violation in BungeeCord
### 1.3
* Added support for AuthMe 3.X
* Fixed premium logins if the server is not fully started
* Added other command argument to /premium and /cracked
* Added support for LogIt
* Fixed 1.7 Minecraft support by removing guava 11+ only features -> Cauldron support
* Fixed BungeeCord support in Cauldron
### 1.2.1
* Fix premium status change notification message on BungeeCord
### 1.2
* Fix race condition in BungeeCord
* 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
### 1.1
* Make the configuration options also work under BungeeCord (premiumUUID, forwardSkin)
* Catch configuration loading exception if it's not spigot build
* Fix config loading for older Spigot builds
### 1.0
* Massive refactor to handle errors on force actions safely
* force Methods now runs async too
* force methods now returns a boolean to reflect if the method was successful
* isRegistered method should now throw an exception if the plugin was unable to query the requested data
### 0.8
* Fixed BungeeCord support for the Bukkit module
* Added database storage to save the premium state
* Fix logical error on /premium (Thanks to @NorbiPeti)
* Fixed issues with host lookup from hosts file (Thanks to @NorbiPeti)
* Remove handshake listener because it creates errors on some systems
### 0.7
* Added BungeeAuth support
* Added /premium [player] command with optional player parameter
* Added a check if the player is already on the premium list
* Added a forwardSkin config option
* Added premium UUID support
* Updated to the newest changes of Spigot
* 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
### 0.6
* Fixed 1.9 bugs
* Added UltraAuth support
### 0.5
* Added cracked command
* Added auto-login - See config
* Added config
* Added isRegistered API method
* Added forceRegister API method
* Fixed CrazyLogin player data restore -> Fixes memory leaks with this plugin
* Fixed premium name check to ProtocolSupport
* Improved permissions management
### 0.4
* Added forward premium skin
* Added plugin support for ProtocolSupport
### 0.3.2
* Run packet readers in a different thread (separated from the Netty I/O Thread)
-> Improves performance
* Fixed Plugin disable if the server is in online mode but have to be in offline mode
### 0.3.1
* Improved BungeeCord security
### 0.3
* Added BungeeCord support
* Decrease timeout checks in order to fail faster on connection problems
* Code style improvements
### 0.2.4
* Fixed NPE on invalid sessions
* Improved security by generating a randomized serverId
* Removed /premium [player] because it's safer for premium players who join without registration
### 0.2.3
* Remove useless AuthMe force-login code
* Send a kick message to the client instead of just "Disconnect"
* Reformat source code
* Fix thread safety for fake start packets (Bukkit.getOfflinePlayer doesn't look like to be thread-safe)
* Added more documentation
### 0.2.2
* Compile project with Java 7 :(
### 0.2.1
* A couple of security fixes (premium players cannot longer steal the account of a cracked account)
* Added a /premium command to mark you as premium player
### 0.2
* Added support for CrazyLogin and LoginSecurity
* Now minecraft version independent
* Added debug logging
* Code clean up
* More state validation
* Added better error handling
### 0.1
* First release

View File

@@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2015-2021 games647 and contributors
Copyright (c) 2015
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

128
README.md
View File

@@ -1,121 +1,13 @@
# 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).
Checks if a minecraft player has a valid premium (paid account). If so, they can skip offline authentification.
## Features
* 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 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 proxied through FloodGate
## Issues
Please use issues for bug reports, suggestions, questions and more. Please check for existing issues. Existing issues
can be voted up by adding up vote to the original post. Closing issues means that they are marked as resolved. Comments
are still allowed and it could be re-opened.
## Development builds
Development builds contain the latest changes from the Source-Code. They are bleeding edge and could introduce new bugs,
but also include features, enhancements and bug fixes that are not yet in a released version. If you click on the left
side on `Changes`, you can see iterative change sets leading to a specific build.
You can download them from here: https://ci.codemc.org/job/Games647/job/FastLogin/
***
## Commands
/premium [player] Label the invoker or the argument as paid account
/cracked [player] Label the invoker or the argument as cracked account
## Permissions
fastlogin.bukkit.command.premium
fastlogin.bukkit.command.cracked
fastlogin.command.premium.other
fastlogin.command.cracked.other
## Placeholder
This plugin supports `PlaceholderAPI` on `Spigot`. It exports the following variable
`%fastlogin_status%`. In BungeeCord environments, the status of a player will be delivered with a delay after the player
already successful joined the server. This takes about a couple of milliseconds. In this case the value
will be `Unknown`.
Possible values: `Premium`, `Cracked`, `Unknown`
## Requirements
* 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
#### Spigot/Paper
* [AdvancedLogin (Paid)](https://www.spigotmc.org/resources/advancedlogin.10510/)
* [AuthMe (5.X)](https://dev.bukkit.org/bukkit-plugins/authme-reloaded/)
* [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/)
#### 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
This plugin performs network requests to:
* https://api.mojang.com - retrieving uuid data to decide if we should activate premium login
* https://sessionserver.mojang.com - verify if the player is the owner of that account
***
## How to install
### Spigot/Paper
1. Download and install ProtocolLib/ProtocolSupport
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
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.
Requirements:
* [ProtocolLib](http://www.spigotmc.org/resources/protocollib.1997/)
* Bukkit 1.8.8
* Java 8 or above
* An auth plugin. Supported Plugins:
* [AuthMe](http://dev.bukkit.org/bukkit-plugins/authme-reloaded/)
* [xAuth](http://dev.bukkit.org/bukkit-plugins/xauth/)
* [CrazyLogin](http://dev.bukkit.org/bukkit-plugins/crazylogin/)
* [LoginSecurity](http://dev.bukkit.org/bukkit-plugins/loginsecurity/)

Binary file not shown.

View File

@@ -1,399 +0,0 @@
<!--
SPDX-License-Identifier: MIT
The MIT License (MIT)
Copyright (c) 2015-2021 <Your name and contributors>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.github.games647</groupId>
<artifactId>fastlogin</artifactId>
<version>1.11-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<!--This has to be in lowercase because it's used by plugin.yml-->
<artifactId>fastlogin.bukkit</artifactId>
<packaging>jar</packaging>
<name>FastLoginBukkit</name>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.4</version>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
<shadedArtifactAttached>false</shadedArtifactAttached>
<relocations>
<relocation>
<pattern>com.zaxxer.hikari</pattern>
<shadedPattern>fastlogin.hikari</shadedPattern>
</relocation>
<relocation>
<pattern>org.slf4j</pattern>
<shadedPattern>fastlogin.slf4j</shadedPattern>
</relocation>
<relocation>
<pattern>net.md_5.bungee.config</pattern>
<shadedPattern>fastlogin.config</shadedPattern>
</relocation>
<relocation>
<pattern>com.google.gson</pattern>
<shadedPattern>fastlogin.gson</shadedPattern>
</relocation>
<relocation>
<pattern>com.google.common</pattern>
<shadedPattern>fastlogin.guava</shadedPattern>
<excludes>
<exclude>com.google.common.collect.Multimap</exclude>
</excludes>
</relocation>
<relocation>
<pattern>io.papermc.lib</pattern>
<shadedPattern>fastlogin.paperlib</shadedPattern>
</relocation>
</relocations>
<!-- Rename the service file too to let SLF4J api find our own relocated jdk logger -->
<!-- Located in META-INF/services -->
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
</transformers>
<minimizeJar>true</minimizeJar>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<repositories>
<!-- PaperSpigot API and PaperLib -->
<repository>
<id>papermc</id>
<url>https://papermc.io/repo/repository/maven-public/</url>
</repository>
<!-- ProtocolLib -->
<repository>
<id>dmulloy2-repo</id>
<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>
</repository>
<!-- PlaceholderAPI -->
<repository>
<id>placeholderapi</id>
<url>https://repo.extendedclip.com/content/repositories/placeholderapi</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<dependencies>
<!--Common plugin component-->
<dependency>
<groupId>com.github.games647</groupId>
<artifactId>fastlogin.core</artifactId>
<version>${project.version}</version>
</dependency>
<!-- PaperSpigot API for correcting user cache usage -->
<dependency>
<groupId>io.papermc.paper</groupId>
<artifactId>paper-api</artifactId>
<version>1.18-R0.1-SNAPSHOT</version>
<scope>provided</scope>
<!-- Use our own newer api version -->
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- PaperLib for checking if server uses PaperSpigot -->
<dependency>
<groupId>io.papermc</groupId>
<artifactId>paperlib</artifactId>
<version>1.0.7</version>
</dependency>
<!--Library for listening and sending Minecraft packets-->
<dependency>
<groupId>com.comphenix.protocol</groupId>
<artifactId>ProtocolLib</artifactId>
<version>4.7.0</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--Changing onlinemode on login process-->
<dependency>
<groupId>com.github.ProtocolSupport</groupId>
<artifactId>ProtocolSupport</artifactId>
<!--4.29.dev after commit about API improvements-->
<version>3a80c661fe</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--Floodgate for Xbox Live Authentication-->
<dependency>
<groupId>org.geysermc.floodgate</groupId>
<artifactId>api</artifactId>
<version>2.0-SNAPSHOT</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>io.netty</groupId>
<artifactId>*</artifactId>
</exclusion>
<exclusion>
<groupId>org.geysermc.cumulus</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Bedrock player bridge -->
<dependency>
<groupId>org.geysermc</groupId>
<artifactId>core</artifactId>
<version>${geyser.version}</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- We need the API, but it was excluded above -->
<dependency>
<groupId>org.geysermc</groupId>
<artifactId>geyser-api</artifactId>
<version>${geyser.version}</version>
<scope>provided</scope>
</dependency>
<!--Provide premium placeholders-->
<dependency>
<groupId>me.clip</groupId>
<artifactId>placeholderapi</artifactId>
<version>2.11.0</version>
<scope>provided</scope>
<optional>true</optional>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--Login Plugins-->
<dependency>
<groupId>fr.xephi</groupId>
<artifactId>authme</artifactId>
<version>5.4.0</version>
<scope>provided</scope>
<optional>true</optional>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.lenis0012.bukkit</groupId>
<artifactId>loginsecurity</artifactId>
<version>3.0.2</version>
<scope>provided</scope>
<optional>true</optional>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.github.games647</groupId>
<artifactId>LogIt</artifactId>
<version>9e3581db27</version>
<scope>provided</scope>
<optional>true</optional>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>de.luricos.bukkit</groupId>
<artifactId>xAuth</artifactId>
<version>2.6</version>
<scope>provided</scope>
<optional>true</optional>
<!--These artifacts produce conflicts on downloading-->
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--No maven repository :(-->
<dependency>
<groupId>de.st_ddt.crazy</groupId>
<artifactId>CrazyCore</artifactId>
<version>10.7.7</version>
<optional>true</optional>
<scope>system</scope>
<systemPath>${project.basedir}/lib/CrazyCore v10.7.7.jar</systemPath>
</dependency>
<dependency>
<groupId>de.st_ddt.crazy</groupId>
<artifactId>CrazyLogin</artifactId>
<version>7.23</version>
<optional>true</optional>
<scope>system</scope>
<systemPath>${project.basedir}/lib/CrazyLogin v7.23.2.jar</systemPath>
</dependency>
<dependency>
<groupId>ultraauth</groupId>
<artifactId>ultraauth</artifactId>
<version>2.0.2</version>
<optional>true</optional>
<scope>system</scope>
<systemPath>${project.basedir}/lib/UltraAuth v2.1.2.jar</systemPath>
</dependency>
<dependency>
<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>
</project>

View File

@@ -1,114 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.bukkit;
import com.github.games647.craftapi.model.skin.SkinProperty;
import com.github.games647.fastlogin.core.StoredProfile;
import com.github.games647.fastlogin.core.shared.LoginSession;
import java.util.Optional;
/**
* Represents a client connecting to the server.
*
* This session is invalid if the player disconnects or the login was successful
*/
public class BukkitLoginSession extends LoginSession {
private static final byte[] EMPTY_ARRAY = {};
private final byte[] verifyToken;
private boolean verified;
private SkinProperty skinProperty;
public BukkitLoginSession(String username, byte[] verifyToken, boolean registered
, StoredProfile profile) {
super(username, registered, profile);
this.verifyToken = verifyToken.clone();
}
//available for BungeeCord
public BukkitLoginSession(String username, boolean registered) {
this(username, EMPTY_ARRAY, registered, null);
}
//cracked player
public BukkitLoginSession(String username, StoredProfile profile) {
this(username, EMPTY_ARRAY, false, profile);
}
//ProtocolSupport
public BukkitLoginSession(String username, boolean registered, StoredProfile profile) {
this(username, EMPTY_ARRAY, registered, profile);
}
/**
* Gets the verify-token the server sent to the client.
*
* Empty if it's a BungeeCord connection
*
* @return verify token from the server
*/
public synchronized byte[] getVerifyToken() {
return verifyToken.clone();
}
/**
* @return premium skin if available
*/
public synchronized Optional<SkinProperty> getSkin() {
return Optional.ofNullable(skinProperty);
}
/**
* Sets the premium skin property which was retrieved by the session server
* @param skinProperty premium skin
*/
public synchronized void setSkinProperty(SkinProperty skinProperty) {
this.skinProperty = skinProperty;
}
/**
* Sets whether the player has a premium (paid account) account and valid session
*
* @param verified whether the player has valid session
*/
public synchronized void setVerified(boolean verified) {
this.verified = verified;
}
/**
* Get whether the player has a premium (paid account) account and valid session
*
* @return whether the player has a valid session
*/
public synchronized boolean isVerified() {
return verified;
}
}

View File

@@ -1,50 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.bukkit;
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;
public class BukkitScheduler extends AsyncScheduler {
private final Executor syncExecutor;
public BukkitScheduler(Plugin plugin, Logger logger, ThreadFactory threadFactory) {
super(logger, threadFactory);
syncExecutor = r -> Bukkit.getScheduler().runTask(plugin, r);
}
public Executor getSyncExecutor() {
return syncExecutor;
}
}

View File

@@ -1,199 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.bukkit;
import com.github.games647.fastlogin.bukkit.listener.BungeeListener;
import com.github.games647.fastlogin.core.message.ChannelMessage;
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 java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
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;
public class BungeeManager {
private static final String LEGACY_FILE_NAME = "proxy-whitelist.txt";
private static final String FILE_NAME = "allowed-proxies.txt";
//null if proxies allowed list is empty so bungeecord support is disabled
private Set<UUID> proxyIds;
private final FastLoginBukkit plugin;
private boolean enabled;
private final Set<UUID> firedJoinEvents = new HashSet<>();
public BungeeManager(FastLoginBukkit plugin) {
this.plugin = plugin;
}
public void cleanup() {
//remove old blocked status
Bukkit.getOnlinePlayers().forEach(player -> player.removeMetadata(plugin.getName(), plugin));
}
public void sendPluginMessage(PluginMessageRecipient player, ChannelMessage message) {
if (player != null) {
ByteArrayDataOutput dataOutput = ByteStreams.newDataOutput();
message.writeTo(dataOutput);
NamespaceKey channel = new NamespaceKey(plugin.getName(), message.getChannelName());
player.sendPluginMessage(plugin, channel.getCombinedName(), dataOutput.toByteArray());
}
}
public boolean isEnabled() {
return enabled;
}
public void initialize() {
try {
enabled = detectProxy();
} catch (Exception ex) {
plugin.getLog().warn("Cannot check proxy support. Fallback to non-proxy mode", ex);
}
if (enabled) {
proxyIds = loadBungeeCordIds();
registerPluginChannels();
}
}
private boolean isProxySupported(String className, String fieldName) {
try {
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() {
return isProxySupported("org.spigotmc.SpigotConfig", "bungee")
|| isProxySupported("com.destroystokyo.paper.PaperConfig", "velocitySupport");
}
private void registerPluginChannels() {
Server server = Bukkit.getServer();
// check for incoming messages from the bungeecord version of this plugin
String groupId = plugin.getName();
String forceChannel = NamespaceKey.getCombined(groupId, LoginActionMessage.FORCE_CHANNEL);
server.getMessenger().registerIncomingPluginChannel(plugin, forceChannel, new BungeeListener(plugin));
// outgoing
String successChannel = new NamespaceKey(groupId, SUCCESS_CHANNEL).getCombinedName();
String changeChannel = new NamespaceKey(groupId, CHANGE_CHANNEL).getCombinedName();
server.getMessenger().registerOutgoingPluginChannel(plugin, successChannel);
server.getMessenger().registerOutgoingPluginChannel(plugin, changeChannel);
}
private Set<UUID> loadBungeeCordIds() {
Path proxiesFile = plugin.getPluginFolder().resolve(FILE_NAME);
Path legacyFile = plugin.getPluginFolder().resolve(LEGACY_FILE_NAME);
try {
if (Files.notExists(proxiesFile)) {
if (Files.exists(legacyFile)) {
Files.move(legacyFile, proxiesFile);
}
if (Files.notExists(legacyFile)) {
Files.createFile(proxiesFile);
}
}
Files.deleteIfExists(legacyFile);
try (Stream<String> lines = Files.lines(proxiesFile)) {
return lines.map(String::trim)
.map(UUID::fromString)
.collect(toSet());
}
} catch (IOException ex) {
plugin.getLog().error("Failed to read proxies", ex);
} catch (Exception ex) {
plugin.getLog().error("Failed to retrieve proxy Id. Disabling BungeeCord support", ex);
}
return Collections.emptySet();
}
public boolean isProxyAllowed(UUID proxyId) {
return proxyIds != null && proxyIds.contains(proxyId);
}
/**
* Mark the event to be fired including the task delay.
*
* @param player joining player
*/
public synchronized void markJoinEventFired(Player player) {
firedJoinEvents.add(player.getUniqueId());
}
/**
* 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.
*
* 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.
*
* @param player joining player
* @return event fired including delay
*/
public synchronized boolean didJoinEventFired(Player player) {
return firedJoinEvents.contains(player.getUniqueId());
}
/**
* Player quit clean up
*
* @param player joining player
*/
public synchronized void cleanup(Player player) {
firedJoinEvents.remove(player.getUniqueId());
}
}

View File

@@ -1,333 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.bukkit;
import com.github.games647.fastlogin.bukkit.command.CrackedCommand;
import com.github.games647.fastlogin.bukkit.command.PremiumCommand;
import com.github.games647.fastlogin.bukkit.listener.ConnectionListener;
import com.github.games647.fastlogin.bukkit.listener.PaperCacheListener;
import com.github.games647.fastlogin.bukkit.listener.protocollib.ManualNameChange;
import com.github.games647.fastlogin.bukkit.listener.protocollib.ProtocolLibListener;
import com.github.games647.fastlogin.bukkit.listener.protocollib.SkinApplyListener;
import com.github.games647.fastlogin.bukkit.listener.protocolsupport.ProtocolSupportListener;
import com.github.games647.fastlogin.bukkit.task.DelayedAuthHook;
import com.github.games647.fastlogin.core.CommonUtil;
import com.github.games647.fastlogin.core.PremiumStatus;
import com.github.games647.fastlogin.core.hooks.bedrock.BedrockService;
import com.github.games647.fastlogin.core.hooks.bedrock.FloodgateService;
import com.github.games647.fastlogin.core.hooks.bedrock.GeyserService;
import com.github.games647.fastlogin.core.shared.FastLoginCore;
import com.github.games647.fastlogin.core.shared.PlatformPlugin;
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;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.java.JavaPlugin;
import org.geysermc.floodgate.api.FloodgateApi;
import org.geysermc.geyser.GeyserImpl;
import org.slf4j.Logger;
/**
* 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(1, -1);
private final Map<UUID, PremiumStatus> premiumPlayers = new ConcurrentHashMap<>();
private final Logger logger;
private boolean serverStarted;
private BungeeManager bungeeManager;
private final BukkitScheduler scheduler;
private FastLoginCore<Player, CommandSender, FastLoginBukkit> core;
private FloodgateService floodgateService;
private GeyserService geyserService;
private PremiumPlaceholder premiumPlaceholder;
public FastLoginBukkit() {
this.logger = CommonUtil.initializeLoggerService(getLogger());
this.scheduler = new BukkitScheduler(this, logger, getThreadFactory());
}
@Override
public void onEnable() {
core = new FastLoginCore<>(this);
core.load();
if (getServer().getOnlineMode()) {
//we need to require offline to prevent a loginSession request for an offline player
logger.error("Server has to be in offline mode");
setEnabled(false);
return;
}
if (!initializeFloodgate()) {
setEnabled(false);
}
bungeeManager = new BungeeManager(this);
bungeeManager.initialize();
PluginManager pluginManager = getServer().getPluginManager();
if (bungeeManager.isEnabled()) {
markInitialized();
} else {
if (!core.setupDatabase()) {
setEnabled(false);
return;
}
if (pluginManager.isPluginEnabled("ProtocolSupport")) {
pluginManager.registerEvents(new ProtocolSupportListener(this, core.getRateLimiter()), this);
} else if (pluginManager.isPluginEnabled("ProtocolLib")) {
ProtocolLibListener.register(this, core.getRateLimiter());
if (isPluginInstalled("floodgate")) {
if (getConfig().getBoolean("floodgatePrefixWorkaround")){
ManualNameChange.register(this, floodgateService);
logger.info("Floodgate prefix injection workaround has been enabled.");
logger.info("If you have problems joining the server, try disabling it in the configuration.");
} else {
logger.warn("We have detected that you are runnging FastLogin alongside Floodgate and ProtocolLib.");
logger.warn("Currently there is an issue with FastLogin that prevents Floodgate name prefixes from showing up "
+ "when it is together used with ProtocolLib.");
logger.warn("If you would like to use Floodgate name prefixes, you can enable an experimental workaround by changing "
+ "the value 'floodgatePrefixWorkaround' to true in config.yml.");
logger.warn("For more information visit https://github.com/games647/FastLogin/issues/493");
}
}
//if server is using paper - we need to set the skin at pre login anyway, so no need for this listener
if (!PaperLib.isPaper() && getConfig().getBoolean("forwardSkin")) {
pluginManager.registerEvents(new SkinApplyListener(this), this);
}
} else {
logger.warn("Either ProtocolLib or ProtocolSupport have to be installed if you don't use BungeeCord");
setEnabled(false);
return;
}
}
//delay dependency setup because we load the plugin very early where plugins are initialized yet
getServer().getScheduler().runTaskLater(this, new DelayedAuthHook(this), 5L);
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 (PaperLib.isPaper()) {
pluginManager.registerEvents(new PaperCacheListener(this), this);
}
//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();
}
dependencyWarnings();
}
private boolean initializeFloodgate() {
if (getServer().getPluginManager().getPlugin("Geyser-Spigot") != null) {
geyserService = new GeyserService(GeyserImpl.getInstance(), core);
}
if (getServer().getPluginManager().getPlugin("floodgate") != null) {
floodgateService = new FloodgateService(FloodgateApi.getInstance(), core);
// Check Floodgate config values and return
return floodgateService.isValidFloodgateConfigString("autoLoginFloodgate")
&& floodgateService.isValidFloodgateConfigString("allowFloodgateNameConflict");
}
return true;
}
@Override
public void onDisable() {
loginSession.clear();
premiumPlayers.clear();
if (core != null) {
core.close();
}
if (bungeeManager != null) {
bungeeManager.cleanup();
}
if (premiumPlaceholder != null && getServer().getPluginManager().isPluginEnabled("PlaceholderAPI")) {
try {
premiumPlaceholder.unregister();
} catch (Exception | NoSuchMethodError exception) {
logger.error("Failed to unregister placeholder", exception);
}
}
}
public FastLoginCore<Player, CommandSender, FastLoginBukkit> getCore() {
return core;
}
/**
* Gets a thread-safe map about players which are connecting to the server are being checked to be premium (paid
* account)
*
* @return a thread-safe loginSession map
*/
public ConcurrentMap<String, BukkitLoginSession> getLoginSessions() {
return loginSession;
}
public BukkitLoginSession getSession(InetSocketAddress address) {
String id = getSessionId(address);
return loginSession.get(id);
}
public String getSessionId(InetSocketAddress address) {
return address.getAddress().getHostAddress() + ':' + address.getPort();
}
public void putSession(InetSocketAddress address, BukkitLoginSession session) {
String id = getSessionId(address);
loginSession.put(id, session);
}
public void removeSession(InetSocketAddress address) {
String id = getSessionId(address);
loginSession.remove(id);
}
public Map<UUID, PremiumStatus> getPremiumPlayers() {
return premiumPlayers;
}
/**
* Fetches the premium status of an online 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 PremiumStatus getStatus(UUID onlinePlayer) {
return premiumPlayers.getOrDefault(onlinePlayer, PremiumStatus.UNKNOWN);
}
/**
* Wait before the server is fully started. This is workaround, because connections right on startup are not
* injected by ProtocolLib
*
* @return true if ProtocolLib can now intercept packets
*/
public boolean isServerFullyStarted() {
return serverStarted;
}
public void markInitialized() {
this.serverStarted = true;
}
public BungeeManager getBungeeManager() {
return bungeeManager;
}
@Override
public Path getPluginFolder() {
return getDataFolder().toPath();
}
@Override
public Logger getLog() {
return logger;
}
@Override
public BukkitScheduler getScheduler() {
return scheduler;
}
@Override
public void sendMessage(CommandSender receiver, String message) {
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;
}
public FloodgateService getFloodgateService() {
return floodgateService;
}
public GeyserService getGeyserService() {
return geyserService;
}
@Override
public BedrockService<?> getBedrockService() {
if (floodgateService != null) {
return floodgateService;
}
return geyserService;
}
/**
* 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/");
}
}
}

View File

@@ -1,72 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.bukkit;
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
import org.bukkit.OfflinePlayer;
import org.jetbrains.annotations.NotNull;
public class PremiumPlaceholder extends PlaceholderExpansion {
private static final String PLACEHOLDER_VARIABLE = "status";
private final FastLoginBukkit plugin;
public PremiumPlaceholder(FastLoginBukkit plugin) {
this.plugin = plugin;
}
@Override
public String onRequest(OfflinePlayer player, @NotNull String identifier) {
// player is null if offline
if (player != null && PLACEHOLDER_VARIABLE.equals(identifier)) {
return plugin.getStatus(player.getUniqueId()).getReadableName();
}
return null;
}
@Override
public @NotNull String getIdentifier() {
return plugin.getName();
}
@Override
public String getRequiredPlugin() {
return plugin.getName();
}
@Override
public @NotNull String getAuthor() {
return String.join(", ", plugin.getDescription().getAuthors());
}
@Override
public @NotNull String getVersion() {
return plugin.getDescription().getVersion();
}
}

View File

@@ -1,120 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.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 org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import static com.github.games647.fastlogin.core.shared.event.FastLoginPremiumToggleEvent.PremiumToggleReason;
public class CrackedCommand extends ToggleCommand {
public CrackedCommand(FastLoginBukkit plugin) {
super(plugin);
}
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
if (args.length == 0) {
onCrackedSelf(sender, command, args);
} else {
onCrackedOther(sender, command, args);
}
return true;
}
private void onCrackedSelf(CommandSender sender, Command cmd, String[] args) {
if (isConsole(sender)) {
return;
}
if (forwardCrackedCommand(sender, sender.getName())) {
return;
}
if (plugin.getBungeeManager().isEnabled()) {
sendBungeeActivateMessage(sender, sender.getName(), false);
plugin.getCore().sendLocaleMessage("wait-on-proxy", sender);
} else {
//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);
}
}
}
private void onCrackedOther(CommandSender sender, Command command, String[] args) {
if (!hasOtherPermission(sender, command)) {
return;
}
if (forwardCrackedCommand(sender, args[0])) {
return;
}
//todo: load async
StoredProfile profile = plugin.getCore().getStorage().loadProfile(args[0]);
if (profile == null) {
sender.sendMessage("Error occurred");
return;
}
//existing player is already cracked
if (profile.isSaved() && !profile.isPremium()) {
plugin.getCore().sendLocaleMessage("not-premium-other", sender);
} else {
plugin.getCore().sendLocaleMessage("remove-premium", sender);
profile.setPremium(false);
plugin.getScheduler().runAsync(() -> {
plugin.getCore().getStorage().save(profile);
plugin.getServer().getPluginManager().callEvent(
new BukkitFastLoginPremiumToggleEvent(profile, PremiumToggleReason.COMMAND_OTHER));
});
}
}
private boolean forwardCrackedCommand(CommandSender sender, String target) {
return forwardBungeeCommand(sender, target, false);
}
}

View File

@@ -1,130 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.bukkit.command;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import com.github.games647.fastlogin.bukkit.event.BukkitFastLoginPremiumToggleEvent;
import com.github.games647.fastlogin.core.StoredProfile;
import com.github.games647.fastlogin.core.shared.event.FastLoginPremiumToggleEvent.PremiumToggleReason;
import java.util.UUID;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
/**
* Let users activate fast login by command. This only be accessible if
* the user has access to its account. So we can make sure that not another
* person with a paid account and the same username can steal their account.
*/
public class PremiumCommand extends ToggleCommand {
public PremiumCommand(FastLoginBukkit plugin) {
super(plugin);
}
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
if (args.length == 0) {
onPremiumSelf(sender, command, args);
} else {
onPremiumOther(sender, command, args);
}
return true;
}
private void onPremiumSelf(CommandSender sender, Command cmd, String[] args) {
if (isConsole(sender)) {
return;
}
if (forwardPremiumCommand(sender, sender.getName())) {
return;
}
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);
return;
}
plugin.getCore().getPendingConfirms().remove(id);
//todo: load async
StoredProfile profile = plugin.getCore().getStorage().loadProfile(sender.getName());
if (profile.isPremium()) {
plugin.getCore().sendLocaleMessage("already-exists", sender);
} else {
//todo: resolve uuid
profile.setPremium(true);
plugin.getScheduler().runAsync(() -> {
plugin.getCore().getStorage().save(profile);
plugin.getServer().getPluginManager().callEvent(
new BukkitFastLoginPremiumToggleEvent(profile, PremiumToggleReason.COMMAND_SELF));
});
plugin.getCore().sendLocaleMessage("add-premium", sender);
}
}
private void onPremiumOther(CommandSender sender, Command command, String[] args) {
if (!hasOtherPermission(sender, command)) {
return;
}
if (forwardPremiumCommand(sender, args[0])) {
return;
}
//todo: load async
StoredProfile profile = plugin.getCore().getStorage().loadProfile(args[0]);
if (profile == null) {
plugin.getCore().sendLocaleMessage("player-unknown", sender);
return;
}
if (profile.isPremium()) {
plugin.getCore().sendLocaleMessage("already-exists-other", sender);
} else {
//todo: resolve uuid
profile.setPremium(true);
plugin.getScheduler().runAsync(() -> {
plugin.getCore().getStorage().save(profile);
plugin.getServer().getPluginManager().callEvent(
new BukkitFastLoginPremiumToggleEvent(profile, PremiumToggleReason.COMMAND_OTHER));
});
plugin.getCore().sendLocaleMessage("add-premium-other", sender);
}
}
private boolean forwardPremiumCommand(CommandSender sender, String target) {
return forwardBungeeCommand(sender, target, true);
}
}

View File

@@ -1,94 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.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;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.plugin.messaging.PluginMessageRecipient;
public abstract class ToggleCommand implements CommandExecutor {
protected final FastLoginBukkit plugin;
public ToggleCommand(FastLoginBukkit plugin) {
this.plugin = plugin;
}
protected boolean hasOtherPermission(CommandSender sender, Command cmd) {
if (sender.hasPermission(cmd.getPermission() + ".other")) {
return true;
}
plugin.getCore().sendLocaleMessage("no-permission", sender);
return false;
}
protected boolean forwardBungeeCommand(CommandSender sender, String target, boolean activate) {
if (plugin.getBungeeManager().isEnabled()) {
sendBungeeActivateMessage(sender, target, activate);
plugin.getCore().sendLocaleMessage("wait-on-proxy", sender);
return true;
}
return false;
}
protected boolean isConsole(CommandSender sender) {
if (sender instanceof Player) {
return false;
}
//console or command block
sender.sendMessage(plugin.getCore().getMessage("no-console"));
return true;
}
protected void sendBungeeActivateMessage(CommandSender invoker, String target, boolean activate) {
if (invoker instanceof PluginMessageRecipient) {
ChannelMessage message = new ChangePremiumMessage(target, activate, true);
plugin.getBungeeManager().sendPluginMessage((PluginMessageRecipient) invoker, message);
} else {
Optional<? extends Player> optPlayer = Bukkit.getServer().getOnlinePlayers().stream().findFirst();
if (!optPlayer.isPresent()) {
plugin.getLog().info("No player online to send a plugin message to the proxy");
return;
}
Player sender = optPlayer.get();
ChannelMessage message = new ChangePremiumMessage(target, activate, false);
plugin.getBungeeManager().sendPluginMessage(sender, message);
}
}
}

View File

@@ -1,78 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.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 org.bukkit.event.Cancellable;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.NotNull;
public class BukkitFastLoginAutoLoginEvent extends Event implements FastLoginAutoLoginEvent, Cancellable {
private static final HandlerList handlers = new HandlerList();
private final LoginSession session;
private final StoredProfile profile;
private boolean cancelled;
public BukkitFastLoginAutoLoginEvent(LoginSession session, StoredProfile profile) {
super(true);
this.session = session;
this.profile = profile;
}
@Override
public LoginSession getSession() {
return session;
}
@Override
public StoredProfile getProfile() {
return profile;
}
@Override
public boolean isCancelled() {
return cancelled;
}
@Override
public void setCancelled(boolean cancelled) {
this.cancelled = cancelled;
}
@Override
public @NotNull HandlerList getHandlers() {
return handlers;
}
public static HandlerList getHandlerList() {
return handlers;
}
}

View File

@@ -1,73 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.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 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 final String username;
private final LoginSource source;
private final StoredProfile profile;
public BukkitFastLoginPreLoginEvent(String username, LoginSource source, StoredProfile profile) {
super(true);
this.username = username;
this.source = source;
this.profile = profile;
}
@Override
public String getUsername() {
return username;
}
@Override
public LoginSource getSource() {
return source;
}
@Override
public StoredProfile getProfile() {
return profile;
}
@Override
public @NotNull HandlerList getHandlers() {
return handlers;
}
public static HandlerList getHandlerList() {
return handlers;
}
}

View File

@@ -1,64 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.bukkit.event;
import com.github.games647.fastlogin.core.StoredProfile;
import com.github.games647.fastlogin.core.shared.event.FastLoginPremiumToggleEvent;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.NotNull;
public class BukkitFastLoginPremiumToggleEvent extends Event implements FastLoginPremiumToggleEvent {
private static final HandlerList handlers = new HandlerList();
private final StoredProfile profile;
private final PremiumToggleReason reason;
public BukkitFastLoginPremiumToggleEvent(StoredProfile profile, PremiumToggleReason reason) {
super(true);
this.profile = profile;
this.reason = reason;
}
@Override
public StoredProfile getProfile() {
return profile;
}
@Override
public PremiumToggleReason getReason() {
return reason;
}
@Override
public @NotNull HandlerList getHandlers() {
return handlers;
}
public static HandlerList getHandlerList() {
return handlers;
}
}

View File

@@ -1,114 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.bukkit.hook;
import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
import fr.xephi.authme.api.v3.AuthMeApi;
import fr.xephi.authme.events.RestoreSessionEvent;
import fr.xephi.authme.process.Management;
import fr.xephi.authme.process.register.executors.ApiPasswordRegisterParams;
import fr.xephi.authme.process.register.executors.RegistrationMethod;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import java.lang.reflect.Field;
/**
* GitHub: https://github.com/Xephi/AuthMeReloaded/
* <p>
* Project page:
* <p>
* Bukkit: https://dev.bukkit.org/bukkit-plugins/authme-reloaded/
* <p>
* Spigot: https://www.spigotmc.org/resources/authme-reloaded.6269/
*/
public class AuthMeHook implements AuthPlugin<Player>, Listener {
private final FastLoginBukkit plugin;
private final AuthMeApi authmeAPI;
private Management authmeManagement;
public AuthMeHook(FastLoginBukkit plugin) {
this.plugin = plugin;
this.authmeAPI = AuthMeApi.getInstance();
if (plugin.getConfig().getBoolean("respectIpLimit", false)) {
try {
Field managementField = this.authmeAPI.getClass().getDeclaredField("management");
managementField.setAccessible(true);
this.authmeManagement = (Management) managementField.get(this.authmeAPI);
} catch (NoSuchFieldException | IllegalAccessException exception) {
this.authmeManagement = null;
}
}
}
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onSessionRestore(RestoreSessionEvent restoreSessionEvent) {
Player player = restoreSessionEvent.getPlayer();
BukkitLoginSession session = plugin.getSession(player.getAddress());
if (session != null && session.isVerified()) {
restoreSessionEvent.setCancelled(true);
}
}
@Override
public boolean forceLogin(Player player) {
if (authmeAPI.isAuthenticated(player)) {
plugin.getLog().warn(ALREADY_AUTHENTICATED, player);
return false;
}
//skips registration and login
authmeAPI.forceLogin(player);
return true;
}
@Override
public boolean isRegistered(String playerName) {
return authmeAPI.isRegistered(playerName);
}
@Override
//this automatically login the player too
public boolean forceRegister(Player player, String password) {
//if we have the management - we can trigger register with IP limit checks
if (authmeManagement != null) {
authmeManagement.performRegister(RegistrationMethod.PASSWORD_REGISTRATION,
ApiPasswordRegisterParams.of(player, password, true));
} else {
authmeAPI.forceRegister(player, password);
}
return true;
}
}

View File

@@ -1,148 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.bukkit.hook;
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 java.util.Optional;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
/**
* GitHub: https://github.com/ST-DDT/CrazyLogin
* <p>
* Project page:
* <p>
* Bukkit: https://dev.bukkit.org/server-mods/crazylogin/
*/
public class CrazyLoginHook implements AuthPlugin<Player> {
private final FastLoginBukkit plugin;
private final CrazyLogin crazyLoginPlugin;
private final PlayerListener playerListener;
public CrazyLoginHook(FastLoginBukkit plugin) {
this.plugin = plugin;
crazyLoginPlugin = CrazyLogin.getPlugin();
playerListener = getListener();
}
@Override
public boolean forceLogin(Player player) {
//not thread-safe operation
Future<Optional<LoginPlayerData>> future = Bukkit.getScheduler().callSyncMethod(plugin, () -> {
LoginPlayerData playerData = crazyLoginPlugin.getPlayerData(player);
if (playerData != null) {
//mark the account as logged in
playerData.setLoggedIn(true);
String ip = player.getAddress().getAddress().getHostAddress();
//this should be done after login to restore the inventory, show players, prevent potential memory leaks...
//from: https://github.com/ST-DDT/CrazyLogin/blob/master/src/main/java/de/st_ddt/crazylogin/CrazyLogin.java#L1948
playerData.resetLoginFails();
player.setFireTicks(0);
if (playerListener != null) {
playerListener.removeMovementBlocker(player);
playerListener.disableHidenInventory(player);
playerListener.disableSaveLogin(player);
playerListener.unhidePlayer(player);
}
//loginFailuresPerIP.remove(IP);
//illegalCommandUsesPerIP.remove(IP);
//tempBans.remove(IP);
playerData.addIP(ip);
player.setMetadata("Authenticated", new Authenticated(crazyLoginPlugin, player));
crazyLoginPlugin.unregisterDynamicHooks();
return Optional.of(playerData);
}
return Optional.empty();
});
try {
Optional<LoginPlayerData> result = future.get().filter(LoginPlayerData::isLoggedIn);
if (result.isPresent()) {
//SQL-Queries should run async
crazyLoginPlugin.getCrazyDatabase().saveWithoutPassword(result.get());
return true;
}
} catch (InterruptedException | ExecutionException ex) {
plugin.getLog().error("Failed to forceLogin player: {}", player, ex);
return false;
}
return false;
}
@Override
public boolean isRegistered(String playerName) {
return crazyLoginPlugin.getPlayerData(playerName) != null;
}
@Override
public boolean forceRegister(Player player, String password) {
CrazyLoginDataDatabase crazyDatabase = crazyLoginPlugin.getCrazyDatabase();
//this executes a sql query and accesses only thread safe collections, so we can run it async
LoginPlayerData playerData = crazyLoginPlugin.getPlayerData(player.getName());
if (playerData == null) {
//create a fake account - this will be saved to the database with the password=FAILEDLOADING
//user cannot log in with that password unless the admin uses plain text
//this automatically marks the player as logged in
crazyDatabase.save(new LoginPlayerData(player));
return forceLogin(player);
}
return false;
}
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;
}
}

View File

@@ -1,83 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.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 java.time.Instant;
import org.bukkit.entity.Player;
/**
* GitHub: https://github.com/XziomekX/LogIt
* <p>
* Project page:
* <p>
* Bukkit: Unknown
* <p>
* Spigot: Unknown
*/
public class LogItHook implements AuthPlugin<Player> {
private final FastLoginBukkit plugin;
public LogItHook(FastLoginBukkit plugin) {
this.plugin = plugin;
}
@Override
public boolean forceLogin(Player player) {
SessionManager sessionManager = LogItCore.getInstance().getSessionManager();
if (sessionManager.isSessionAlive(player)) {
plugin.getLog().warn(ALREADY_AUTHENTICATED, player);
return false;
}
return sessionManager.startSession(player) == CancelledState.NOT_CANCELLED;
}
@Override
public boolean isRegistered(String playerName) {
return LogItCore.getInstance().getAccountManager().isRegistered(playerName);
}
@Override
public boolean forceRegister(Player player, String password) {
Account account = new Account(player.getName());
account.changePassword(password);
Instant now = Instant.now();
account.setLastActiveDate(now.getEpochSecond());
account.setRegistrationDate(now.getEpochSecond());
return LogItCore.getInstance().getAccountManager().insertAccount(account) == CancelledState.NOT_CANCELLED;
}
}

View File

@@ -1,78 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.bukkit.hook;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
import com.lenis0012.bukkit.loginsecurity.LoginSecurity;
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: https://github.com/lenis0012/LoginSecurity-2
* <p>
* Project page:
* <p>
* Bukkit: https://dev.bukkit.org/bukkit-plugins/loginsecurity/
* <p>
* Spigot: https://www.spigotmc.org/resources/loginsecurity.19362/
*/
public class LoginSecurityHook implements AuthPlugin<Player> {
private final FastLoginBukkit plugin;
public LoginSecurityHook(FastLoginBukkit plugin) {
this.plugin = plugin;
}
@Override
public boolean forceLogin(Player player) {
PlayerSession session = LoginSecurity.getSessionManager().getPlayerSession(player);
if (session.isAuthorized()) {
plugin.getLog().warn(ALREADY_AUTHENTICATED, player);
return true;
}
return session.isAuthorized()
|| session.performAction(new LoginAction(AuthService.PLUGIN, plugin)).isSuccess();
}
@Override
public boolean isRegistered(String playerName) {
PlayerSession session = LoginSecurity.getSessionManager().getOfflineSession(playerName);
return session.isRegistered();
}
@Override
public boolean forceRegister(Player player, String password) {
PlayerSession session = LoginSecurity.getSessionManager().getPlayerSession(player);
return session.performAction(new RegisterAction(AuthService.PLUGIN, plugin, password)).isSuccess();
}
}

View File

@@ -1,78 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.bukkit.hook;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
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;
/**
* 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 SodionAuthHook(FastLoginBukkit plugin) {
this.plugin = plugin;
}
@Override
public boolean forceLogin(Player player) {
try {
SodionAuthApi.login(new BukkitPlayer(player));
} catch (AuthenticatedException e) {
plugin.getLog().warn(ALREADY_AUTHENTICATED, player);
return false;
}
return true;
}
@Override
public boolean forceRegister(Player player, String password) {
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;
}
}
@Override
public boolean isRegistered(String playerName) {
return SodionAuthApi.isRegistered(playerName);
}
}

View File

@@ -1,86 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.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;
/**
* Project page:
* <p>
* Bukkit: https://dev.bukkit.org/bukkit-plugins/ultraauth-aa/
* <p>
* Spigot: https://www.spigotmc.org/resources/ultraauth.17044/
*/
public class UltraAuthHook implements AuthPlugin<Player> {
private final FastLoginBukkit plugin;
public UltraAuthHook(FastLoginBukkit plugin) {
this.plugin = plugin;
}
@Override
public boolean forceLogin(Player player) {
//not thread-safe
Future<Boolean> future = Bukkit.getScheduler().callSyncMethod(plugin, () -> {
if (UltraAuthAPI.isAuthenticated(player)) {
plugin.getLog().warn(ALREADY_AUTHENTICATED, player);
return false;
}
UltraAuthAPI.authenticatedPlayer(player);
return UltraAuthAPI.isAuthenticated(player);
});
try {
return future.get();
} catch (InterruptedException | ExecutionException ex) {
plugin.getLog().error("Failed to forceLogin player: {}", player, ex);
return false;
}
}
@Override
public boolean isRegistered(String playerName) {
return UltraAuthAPI.isRegisterd(playerName);
}
@Override
public boolean forceRegister(Player player, String password) {
UltraAuthAPI.setPlayerPasswordOnline(player, password);
//the register method silents any exception so check if our entry was saved
return PlayerManager.getInstance().checkPlayerPassword(player, password) && forceLogin(player);
}
}

View File

@@ -1,112 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.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 java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
/**
* GitHub: https://github.com/LycanDevelopment/xAuth/
* <p>
* Project page:
* <p>
* Bukkit: https://dev.bukkit.org/bukkit-plugins/xauth/
*/
public class xAuthHook implements AuthPlugin<Player> {
private final xAuth xAuthPlugin = xAuth.getPlugin();
private final FastLoginBukkit plugin;
public xAuthHook(FastLoginBukkit plugin) {
this.plugin = plugin;
}
@Override
public boolean forceLogin(Player player) {
//not thread-safe
Future<Boolean> future = Bukkit.getScheduler().callSyncMethod(plugin, () -> {
xAuthPlayer xAuthPlayer = xAuthPlugin.getPlayerManager().getPlayer(player);
if (xAuthPlayer != null) {
if (xAuthPlayer.isAuthenticated()) {
plugin.getLog().warn(ALREADY_AUTHENTICATED, player);
return false;
}
//we checked that the player is premium (paid account)
xAuthPlayer.setPremium(true);
//unprotect the inventory, op status...
return xAuthPlugin.getPlayerManager().doLogin(xAuthPlayer);
}
return false;
});
try {
return future.get();
} catch (InterruptedException | ExecutionException ex) {
plugin.getLog().error("Failed to forceLogin player: {}", player, ex);
return false;
}
}
@Override
public boolean isRegistered(String playerName) {
//this will load the player if it's not in the cache
xAuthPlayer xAuthPlayer = xAuthPlugin.getPlayerManager().getPlayer(playerName);
return xAuthPlayer != null && xAuthPlayer.isRegistered();
}
@Override
public boolean forceRegister(Player player, final String password) {
//not thread-safe
Future<Boolean> future = Bukkit.getScheduler().callSyncMethod(xAuthPlugin, () -> {
xAuthPlayer xAuthPlayer = xAuthPlugin.getPlayerManager().getPlayer(player);
//this should run async because the plugin executes a sql query, but the method
//accesses non thread-safe collections :(
return xAuthPlayer != null
&& xAuthPlugin.getAuthClass(xAuthPlayer).adminRegister(player.getName(), password, null);
});
try {
//login in the player after registration
return future.get() && forceLogin(player);
} catch (InterruptedException | ExecutionException ex) {
plugin.getLog().error("Failed to forceRegister player: {}", player, ex);
return false;
}
}
}

View File

@@ -1,140 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.bukkit.listener;
import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import com.github.games647.fastlogin.bukkit.task.ForceLoginTask;
import com.github.games647.fastlogin.core.PremiumStatus;
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
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;
/**
* Responsible for receiving messages from a BungeeCord instance.
*
* 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.
*/
public class BungeeListener implements PluginMessageListener {
private final FastLoginBukkit plugin;
public BungeeListener(FastLoginBukkit plugin) {
this.plugin = plugin;
}
@Override
public void onPluginMessageReceived(@NotNull String channel, Player player, byte[] message) {
ByteArrayDataInput dataInput = ByteStreams.newDataInput(message);
LoginActionMessage loginMessage = new LoginActionMessage();
loginMessage.readFrom(dataInput);
plugin.getLog().debug("Received plugin message {}", loginMessage);
Player targetPlayer = player;
if (!loginMessage.getPlayerName().equals(player.getName())) {
targetPlayer = Bukkit.getPlayerExact(loginMessage.getPlayerName());
}
if (targetPlayer == null) {
plugin.getLog().warn("Force action player {} not found", loginMessage.getPlayerName());
return;
}
// fail if target player is blocked because already authenticated or wrong bungeecord id
if (targetPlayer.hasMetadata(plugin.getName())) {
plugin.getLog().warn("Received message {} from a blocked player {}", loginMessage, targetPlayer);
} else {
UUID sourceId = loginMessage.getProxyId();
if (plugin.getBungeeManager().isProxyAllowed(sourceId)) {
readMessage(targetPlayer, loginMessage);
} else {
plugin.getLog().warn("Received proxy id: {} that doesn't exist in the proxy file", sourceId);
}
}
}
private void readMessage(Player player, LoginActionMessage message) {
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, address);
} else if (type == Type.REGISTER) {
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, InetSocketAddress address) {
BukkitLoginSession playerSession = new BukkitLoginSession(playerName, true);
startLoginTaskIfReady(player, playerSession);
}
private void onRegisterMessage(Player player, String playerName, InetSocketAddress address) {
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
AuthPlugin<Player> authPlugin = plugin.getCore().getAuthPluginHook();
try {
//we need to check if the player is registered on Bukkit too
if (authPlugin == null || !authPlugin.isRegistered(playerName)) {
BukkitLoginSession playerSession = new BukkitLoginSession(playerName, false);
startLoginTaskIfReady(player, playerSession);
}
} catch (Exception ex) {
plugin.getLog().error("Failed to query isRegistered for player: {}", player, ex);
}
});
}
private void startLoginTaskIfReady(Player player, BukkitLoginSession 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);
plugin.getLog().info("Delaying force login until join event fired?: {}", result);
if (result) {
Runnable forceLoginTask = new ForceLoginTask(plugin.getCore(), player, session);
Bukkit.getScheduler().runTaskAsynchronously(plugin, forceLoginTask);
}
}
}

View File

@@ -1,121 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.bukkit.listener;
import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import com.github.games647.fastlogin.bukkit.task.FloodgateAuthTask;
import com.github.games647.fastlogin.bukkit.task.ForceLoginTask;
import com.github.games647.fastlogin.core.hooks.bedrock.FloodgateService;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerLoginEvent;
import org.bukkit.event.player.PlayerLoginEvent.Result;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.metadata.Metadatable;
import org.geysermc.floodgate.api.player.FloodgatePlayer;
/**
* This listener tells authentication plugins weather the player has a premium account. So the
* plugin can skip authentication.
*/
public class ConnectionListener implements Listener {
private static final long DELAY_LOGIN = 20L / 2;
private final FastLoginBukkit plugin;
public ConnectionListener(FastLoginBukkit plugin) {
this.plugin = plugin;
}
@EventHandler(priority = EventPriority.LOWEST)
public void onPlayerLogin(PlayerLoginEvent loginEvent) {
removeBlockedStatus(loginEvent.getPlayer());
if (loginEvent.getResult() == Result.ALLOWED && !plugin.isServerFullyStarted()) {
loginEvent.disallow(Result.KICK_OTHER, plugin.getCore().getMessage("not-started"));
}
}
@EventHandler(ignoreCancelled = true)
public void onPlayerJoin(PlayerJoinEvent joinEvent) {
Player player = joinEvent.getPlayer();
Bukkit.getScheduler().runTaskLater(plugin, () -> {
delayForceLogin(player);
// delay the login process to let auth plugins initialize the player
// Magic number however as there is no direct event from those plugins
}, DELAY_LOGIN);
}
private void delayForceLogin(Player player) {
// session exists so the player is ready for force login
// cases: Paper (firing BungeeCord message before PlayerJoinEvent) or not running BungeeCord and already
// having the login session from the login process
BukkitLoginSession session = plugin.getSession(player.getAddress());
if (session == null) {
// Floodgate players usually don't have a session at this point
// exception: if force login by bungee message had been delayed
FloodgateService floodgateService = plugin.getFloodgateService();
if (floodgateService != null) {
FloodgatePlayer floodgatePlayer = floodgateService.getBedrockPlayer(player.getUniqueId());
if (floodgatePlayer != null) {
Runnable floodgateAuthTask = new FloodgateAuthTask(plugin.getCore(), player, floodgatePlayer);
Bukkit.getScheduler().runTaskAsynchronously(plugin, floodgateAuthTask);
return;
}
}
String sessionId = plugin.getSessionId(player.getAddress());
plugin.getLog().info("No on-going login session for player: {} with ID {}", player, sessionId);
} else {
Runnable forceLoginTask = new ForceLoginTask(plugin.getCore(), player, session);
Bukkit.getScheduler().runTaskAsynchronously(plugin, forceLoginTask);
}
plugin.getBungeeManager().markJoinEventFired(player);
}
@EventHandler
public void onPlayerQuit(PlayerQuitEvent quitEvent) {
Player player = quitEvent.getPlayer();
removeBlockedStatus(player);
plugin.getCore().getPendingConfirms().remove(player.getUniqueId());
plugin.getPremiumPlayers().remove(player.getUniqueId());
plugin.getBungeeManager().cleanup(player);
}
private void removeBlockedStatus(Metadatable player) {
player.removeMetadata(plugin.getName(), plugin);
}
}

View File

@@ -1,66 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.bukkit.listener;
import com.destroystokyo.paper.profile.ProfileProperty;
import com.github.games647.craftapi.model.skin.Textures;
import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.AsyncPlayerPreLoginEvent;
import org.bukkit.event.player.AsyncPlayerPreLoginEvent.Result;
public class PaperCacheListener implements Listener {
private final FastLoginBukkit plugin;
public PaperCacheListener(final FastLoginBukkit plugin) {
this.plugin = plugin;
}
@EventHandler(priority = EventPriority.HIGHEST)
//if paper is used - player skin must be set at pre login, otherwise user cache is used
// user cache makes premium name change basically impossible
public void onAsyncPlayerPreLogin(AsyncPlayerPreLoginEvent event) {
if (event.getLoginResult() != Result.ALLOWED) {
return;
}
// event gives us only IP, not the port, so we need to loop through all the sessions
for (BukkitLoginSession session : plugin.getLoginSessions().values()) {
if (!event.getName().equals(session.getUsername())) {
continue;
}
session.getSkin().ifPresent(skin -> event.getPlayerProfile().setProperty(new ProfileProperty(Textures.KEY,
skin.getValue(), skin.getSignature())));
break;
}
}
}

View File

@@ -1,157 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.bukkit.listener.protocollib;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
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.util.Random;
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
*/
public class EncryptionUtil {
public static final int VERIFY_TOKEN_LENGTH = 4;
public static final String KEY_PAIR_ALGORITHM = "RSA";
private EncryptionUtil() {
// utility
}
/**
* Generate an RSA key pair
*
* @return The RSA key pair.
*/
public static KeyPair generateKeyPair() {
// KeyPair b()
try {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_PAIR_ALGORITHM);
keyPairGenerator.initialize(1_024);
return keyPairGenerator.generateKeyPair();
} catch (NoSuchAlgorithmException nosuchalgorithmexception) {
// Should be existing in every vm
throw new ExceptionInInitializerError(nosuchalgorithmexception);
}
}
/**
* Generate a random token. This is used to verify that we are communicating with the same player
* in a login session.
*
* @param random random generator
* @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;
}
/**
* Generate the server id based on client and server data.
*
* @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
* @return the server id formatted as a hexadecimal string.
*/
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
* @return shared secret key
* @throws GeneralSecurityException if it fails to decrypt the data
*/
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 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)
throws NoSuchAlgorithmException {
// byte[] a(String var0, PublicKey var1, SecretKey var2)
MessageDigest digest = MessageDigest.getInstance("SHA-1");
// 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 digest.digest();
}
}

View File

@@ -1,85 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.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);
}
}

View File

@@ -1,114 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.bukkit.listener.protocollib;
import com.comphenix.protocol.ProtocolLibrary;
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.core.StoredProfile;
import com.github.games647.fastlogin.core.shared.JoinManagement;
import com.github.games647.fastlogin.core.shared.event.FastLoginPreLoginEvent;
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 {
private final FastLoginBukkit plugin;
private final PacketEvent packetEvent;
private final PublicKey publicKey;
private final Random random;
private final Player player;
private final String username;
public NameCheckTask(FastLoginBukkit plugin, Random random, Player player, PacketEvent packetEvent,
String username, PublicKey publicKey) {
super(plugin.getCore(), plugin.getCore().getAuthPluginHook(), plugin.getBedrockService());
this.plugin = plugin;
this.packetEvent = packetEvent;
this.publicKey = publicKey;
this.random = random;
this.player = player;
this.username = username;
}
@Override
public void run() {
try {
super.onLogin(username, new ProtocolLibLoginSource(player, random, publicKey));
} finally {
ProtocolLibrary.getProtocolManager().getAsynchronousManager().signalPacketTransmission(packetEvent);
}
}
@Override
public FastLoginPreLoginEvent callFastLoginPreLoginEvent(String username, ProtocolLibLoginSource source,
StoredProfile profile) {
BukkitFastLoginPreLoginEvent event = new BukkitFastLoginPreLoginEvent(username, source, profile);
plugin.getServer().getPluginManager().callEvent(event);
return event;
}
//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) {
try {
source.enableOnlinemode();
} catch (Exception ex) {
plugin.getLog().error("Cannot send encryption packet. Falling back to cracked login for: {}", profile, ex);
return;
}
String ip = player.getAddress().getAddress().getHostAddress();
core.getPendingLogin().put(ip + username, new Object());
byte[] verify = source.getVerifyToken();
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()) {
packetEvent.setCancelled(true);
}
}
@Override
public void startCrackedSession(ProtocolLibLoginSource source, StoredProfile profile, String username) {
BukkitLoginSession loginSession = new BukkitLoginSession(username, profile);
plugin.putSession(player.getAddress(), loginSession);
}
}

View File

@@ -1,139 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.bukkit.listener.protocollib;
import com.comphenix.protocol.PacketType;
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.github.games647.fastlogin.bukkit.FastLoginBukkit;
import com.github.games647.fastlogin.core.RateLimiter;
import java.security.KeyPair;
import java.security.SecureRandom;
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 RateLimiter rateLimiter;
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)
.types(START, ENCRYPTION_BEGIN)
.optionAsync());
this.plugin = plugin;
this.rateLimiter = rateLimiter;
}
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, rateLimiter))
.start();
}
@Override
public void onPacketReceiving(PacketEvent packetEvent) {
if (packetEvent.isCancelled()
|| plugin.getCore().getAuthPluginHook() == null
|| !plugin.isServerFullyStarted()) {
return;
}
if (isFastLoginPacket(packetEvent)) {
// this is our own packet
return;
}
Player sender = packetEvent.getPlayer();
PacketType packetType = packetEvent.getPacketType();
if (packetType == START) {
if (!rateLimiter.tryAcquire()) {
plugin.getLog().warn("Simple Anti-Bot join limit - Ignoring {}", sender);
return;
}
onLogin(packetEvent, sender);
} else {
onEncryptionBegin(packetEvent, sender);
}
}
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);
packetEvent.getAsyncMarker().incrementProcessingDelay();
Runnable verifyTask = new VerifyResponseTask(plugin, packetEvent, sender, sharedSecret, keyPair);
plugin.getScheduler().runAsync(verifyTask);
}
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();
String username = packet.getGameProfiles().read(0).getName();
if (packetEvent.getPacket().getMeta("original_name").isPresent()) {
//username has been injected by ManualNameChange.java
username = (String) packetEvent.getPacket().getMeta("original_name").get();
}
plugin.getLog().trace("GameProfile {} with {} connecting", sessionKey, username);
packetEvent.getAsyncMarker().incrementProcessingDelay();
Runnable nameCheckTask = new NameCheckTask(plugin, random, player, packetEvent, username, keyPair.getPublic());
plugin.getScheduler().runAsync(nameCheckTask);
}
}

View File

@@ -1,129 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.bukkit.listener.protocollib;
import com.comphenix.protocol.ProtocolLibrary;
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.core.shared.LoginSource;
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;
class ProtocolLibLoginSource implements LoginSource {
private final Player player;
private final Random random;
private final PublicKey publicKey;
private final String serverId = "";
private byte[] verifyToken;
public ProtocolLibLoginSource(Player player, Random random, PublicKey publicKey) {
this.player = player;
this.random = random;
this.publicKey = publicKey;
}
@Override
public void enableOnlinemode() throws InvocationTargetException {
verifyToken = EncryptionUtil.generateVerifyToken(random);
/*
* Packet Information: https://wiki.vg/Protocol#Encryption_Request
*
* ServerID="" (String) key=public server key verifyToken=random 4 byte array
*/
PacketContainer newPacket = new PacketContainer(ENCRYPTION_BEGIN);
newPacket.getStrings().write(0, serverId);
StructureModifier<PublicKey> keyModifier = newPacket.getSpecificModifier(PublicKey.class);
int verifyField = 0;
if (keyModifier.getFields().isEmpty()) {
// Since 1.16.4 this is now a byte field
newPacket.getByteArrays().write(0, publicKey.getEncoded());
verifyField++;
} else {
keyModifier.write(0, publicKey);
}
newPacket.getByteArrays().write(verifyField, verifyToken);
//serverId is an empty string
ProtocolLibrary.getProtocolManager().sendServerPacket(player, newPacket);
}
@Override
public void kick(String message) throws InvocationTargetException {
ProtocolManager protocolManager = ProtocolLibrary.getProtocolManager();
PacketContainer kickPacket = new PacketContainer(DISCONNECT);
kickPacket.getChatComponents().write(0, WrappedChatComponent.fromText(message));
try {
//send kick packet at login state
//the normal event.getPlayer.kickPlayer(String) method does only work at play state
protocolManager.sendServerPacket(player, kickPacket);
} finally {
//tell the server that we want to close the connection
player.kickPlayer("Disconnect");
}
}
@Override
public InetSocketAddress getAddress() {
return player.getAddress();
}
public String getServerId() {
return serverId;
}
public byte[] getVerifyToken() {
return verifyToken.clone();
}
@Override
public String toString() {
return this.getClass().getSimpleName() + '{' +
"player=" + player +
", random=" + random +
", serverId='" + serverId + '\'' +
", verifyToken=" + Arrays.toString(verifyToken) +
'}';
}
}

View File

@@ -1,92 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.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;
import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
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) {
this.plugin = plugin;
}
@EventHandler(priority = EventPriority.LOW)
//run this on the loginEvent to let skins plugins see the skin like in normal Minecraft behaviour
public void onPlayerLogin(PlayerLoginEvent loginEvent) {
if (loginEvent.getResult() != Result.ALLOWED) {
return;
}
Player player = loginEvent.getPlayer();
//go through every session, because player.getAddress is null
//loginEvent.getAddress is just a InetAddress not InetSocketAddress, so not unique enough
for (BukkitLoginSession session : plugin.getLoginSessions().values()) {
if (session.getUsername().equals(player.getName())) {
session.getSkin().ifPresent(skin -> applySkin(player, skin.getValue(), skin.getSignature()));
break;
}
}
}
private void applySkin(Player player, String skinData, String signature) {
WrappedGameProfile gameProfile = WrappedGameProfile.fromPlayer(player);
WrappedSignedProperty skin = WrappedSignedProperty.fromValues(Textures.KEY, skinData, signature);
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);
}
}
}
}

View File

@@ -1,297 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.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.server.TemporaryPlayerFactory;
import com.comphenix.protocol.reflect.FieldUtils;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.WrappedChatComponent;
import com.comphenix.protocol.wrappers.WrappedGameProfile;
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 java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.security.GeneralSecurityException;
import java.security.Key;
import java.security.KeyPair;
import java.security.PrivateKey;
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 Class<?> ENCRYPTION_CLASS;
static {
ENCRYPTION_CLASS = MinecraftReflection.getMinecraftClass("util." + ENCRYPTION_CLASS_NAME, ENCRYPTION_CLASS_NAME);
}
private final FastLoginBukkit plugin;
private final PacketEvent packetEvent;
private final KeyPair serverKey;
private final Player player;
private final byte[] sharedSecret;
private static Method encryptMethod;
private static Method cipherMethod;
public VerifyResponseTask(FastLoginBukkit plugin, PacketEvent packetEvent, Player player,
byte[] sharedSecret, KeyPair keyPair) {
this.plugin = plugin;
this.packetEvent = packetEvent;
this.player = player;
this.sharedSecret = Arrays.copyOf(sharedSecret, sharedSecret.length);
this.serverKey = keyPair;
}
@Override
public void run() {
try {
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()) {
packetEvent.setCancelled(true);
}
ProtocolLibrary.getProtocolManager().getAsynchronousManager().signalPacketTransmission(packetEvent);
}
}
private void verifyResponse(BukkitLoginSession session) {
PrivateKey privateKey = serverKey.getPrivate();
SecretKey loginKey;
try {
loginKey = EncryptionUtil.decryptSharedKey(privateKey, sharedSecret);
} catch (GeneralSecurityException securityEx) {
disconnect("error-kick", false, "Cannot decrypt received contents", securityEx);
return;
}
try {
if (!checkVerifyToken(session) || !enableEncryption(loginKey)) {
return;
}
} catch (Exception ex) {
disconnect("error-kick", false, "Cannot decrypt received contents", ex);
return;
}
String serverId = EncryptionUtil.getServerIdHashString("", loginKey, serverKey.getPublic());
String requestedUsername = session.getRequestUsername();
InetSocketAddress socketAddress = player.getAddress();
try {
MojangResolver resolver = plugin.getCore().getResolver();
InetAddress address = socketAddress.getAddress();
Optional<Verification> response = resolver.hasJoined(requestedUsername, serverId, address);
if (response.isPresent()) {
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;
}
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", false, "Failed to connect to session server", ioEx);
}
}
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
FieldUtils.writeField(networkManager, "spoofedUUID", premiumUUID, true);
} catch (Exception exc) {
plugin.getLog().error("Error setting premium uuid of {}", player, exc);
}
}
}
private boolean checkVerifyToken(BukkitLoginSession session) throws GeneralSecurityException {
byte[] requestVerify = session.getVerifyToken();
//encrypted verify token
byte[] responseVerify = packetEvent.getPacket().getByteArrays().read(1);
//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.getName());
// Initialize method reflections
if (encryptMethod == null) {
Class<?> networkManagerClass = MinecraftReflection.getNetworkManagerClass();
try {
// Try to get the old (pre MC 1.16.4) encryption method
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);
// Get the needed Cipher helper method (used to generate ciphers from login key)
cipherMethod = FuzzyReflection.fromClass(ENCRYPTION_CLASS)
.getMethodByParameters("a", int.class, Key.class);
}
}
try {
Object networkManager = this.getNetworkManager();
// If cipherMethod is null - use old encryption (pre MC 1.16.4), otherwise use the new cipher one
if (cipherMethod == null) {
// Encrypt/decrypt packet flow, this behaviour is expected by the client
encryptMethod.invoke(networkManager, loginKey);
} else {
// Create ciphers from login key
Object decryptionCipher = cipherMethod.invoke(null, Cipher.DECRYPT_MODE, loginKey);
Object encryptionCipher = cipherMethod.invoke(null, Cipher.ENCRYPT_MODE, loginKey);
// Encrypt/decrypt packet flow, this behaviour is expected by the client
encryptMethod.invoke(networkManager, decryptionCipher, encryptionCipher);
}
} catch (Exception ex) {
disconnect("error-kick", false, "Couldn't enable encryption", ex);
return false;
}
return true;
}
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));
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) {
//see StartPacketListener for packet information
PacketContainer startPacket = new PacketContainer(START);
//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"));
}
}
}

View File

@@ -1,67 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.bukkit.listener.protocolsupport;
import com.github.games647.fastlogin.core.shared.LoginSource;
import java.net.InetSocketAddress;
import protocolsupport.api.events.PlayerLoginStartEvent;
public class ProtocolLoginSource implements LoginSource {
private final PlayerLoginStartEvent loginStartEvent;
public ProtocolLoginSource(PlayerLoginStartEvent loginStartEvent) {
this.loginStartEvent = loginStartEvent;
}
@Override
public void enableOnlinemode() {
loginStartEvent.setOnlineMode(true);
}
@Override
public void kick(String message) {
loginStartEvent.denyLogin(message);
}
@Override
public InetSocketAddress getAddress() {
return loginStartEvent.getAddress();
}
public PlayerLoginStartEvent getLoginStartEvent() {
return loginStartEvent;
}
@Override
public String toString() {
return this.getClass().getSimpleName() + '{' +
"loginStartEvent=" + loginStartEvent +
'}';
}
}

View File

@@ -1,128 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.bukkit.listener.protocolsupport;
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.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 java.net.InetSocketAddress;
import java.util.Optional;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import protocolsupport.api.events.ConnectionCloseEvent;
import protocolsupport.api.events.PlayerLoginStartEvent;
import protocolsupport.api.events.PlayerProfileCompleteEvent;
public class ProtocolSupportListener extends JoinManagement<Player, CommandSender, ProtocolLoginSource>
implements Listener {
private final FastLoginBukkit plugin;
private final RateLimiter rateLimiter;
public ProtocolSupportListener(FastLoginBukkit plugin, RateLimiter rateLimiter) {
super(plugin.getCore(), plugin.getCore().getAuthPluginHook(), plugin.getBedrockService());
this.plugin = plugin;
this.rateLimiter = rateLimiter;
}
@EventHandler
public void onLoginStart(PlayerLoginStartEvent loginStartEvent) {
if (loginStartEvent.isLoginDenied() || plugin.getCore().getAuthPluginHook() == null) {
return;
}
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().getAddress();
plugin.removeSession(address);
}
@EventHandler
public void onPropertiesResolve(PlayerProfileCompleteEvent profileCompleteEvent) {
InetSocketAddress address = profileCompleteEvent.getAddress();
BukkitLoginSession session = plugin.getSession(address);
if (session != null && profileCompleteEvent.getConnection().getProfile().isOnlineMode()) {
session.setVerified(true);
if (!plugin.getConfig().getBoolean("premiumUuid")) {
String username = Optional.ofNullable(profileCompleteEvent.getForcedName())
.orElse(profileCompleteEvent.getConnection().getProfile().getName());
profileCompleteEvent.setForcedUUID(UUIDAdapter.generateOfflineId(username));
}
}
}
@Override
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) {
source.enableOnlinemode();
String ip = source.getAddress().getAddress().getHostAddress();
plugin.getCore().getPendingLogin().put(ip + username, new Object());
BukkitLoginSession playerSession = new BukkitLoginSession(username, registered, profile);
plugin.putSession(source.getAddress(), playerSession);
}
@Override
public void startCrackedSession(ProtocolLoginSource source, StoredProfile profile, String username) {
BukkitLoginSession loginSession = new BukkitLoginSession(username, profile);
plugin.putSession(source.getAddress(), loginSession);
}
}

View File

@@ -1,126 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.bukkit.task;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import com.github.games647.fastlogin.bukkit.hook.AuthMeHook;
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.SodionAuthHook;
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
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;
public DelayedAuthHook(FastLoginBukkit plugin) {
this.plugin = plugin;
}
@Override
public void run() {
boolean hookFound = isHookFound();
if (plugin.getBungeeManager().isEnabled()) {
plugin.getLog().info("BungeeCord setting detected. No auth plugin is required");
} else if (!hookFound) {
plugin.getLog().warn("No auth plugin were found by this plugin "
+ "(other plugins could hook into this after the initialization of this plugin)"
+ "and BungeeCord is deactivated. "
+ "Either one or both of the checks have to pass in order to use this plugin");
}
if (hookFound) {
plugin.markInitialized();
}
}
private boolean isHookFound() {
return plugin.getCore().getAuthPluginHook() != null || registerHooks();
}
private boolean registerHooks() {
AuthPlugin<Player> authPluginHook = getAuthHook();
if (authPluginHook == null) {
//run this check for exceptions (errors) and not found plugins
plugin.getLog().warn("No support offline Auth plugin found. ");
return false;
}
if (authPluginHook instanceof Listener) {
Bukkit.getPluginManager().registerEvents((Listener) authPluginHook, plugin);
}
if (plugin.getCore().getAuthPluginHook() == null) {
plugin.getLog().info("Hooking into auth plugin: {}", authPluginHook.getClass().getSimpleName());
plugin.getCore().setAuthPluginHook(authPluginHook);
}
return true;
}
private AuthPlugin<Player> getAuthHook() {
try {
List<Class<? extends AuthPlugin<Player>>> hooks = Arrays.asList(AuthMeHook.class,
CrazyLoginHook.class, LogItHook.class, LoginSecurityHook.class,
SodionAuthHook.class, UltraAuthHook.class, xAuthHook.class);
for (Class<? extends AuthPlugin<Player>> clazz : hooks) {
String pluginName = clazz.getSimpleName();
pluginName = pluginName.substring(0, pluginName.length() - "Hook".length());
//uses only member classes which uses AuthPlugin interface (skip interfaces)
if (Bukkit.getPluginManager().isPluginEnabled(pluginName)) {
//check only for enabled plugins. A single plugin could be disabled by plugin managers
return newInstance(clazz);
}
}
} catch (ReflectiveOperationException ex) {
plugin.getLog().error("Couldn't load the auth hook class", ex);
}
return null;
}
private AuthPlugin<Player> newInstance(Class<? extends AuthPlugin<Player>> clazz)
throws ReflectiveOperationException {
try {
Constructor<? extends AuthPlugin<Player>> cons = clazz.getDeclaredConstructor(FastLoginBukkit.class);
return cons.newInstance(plugin);
} catch (NoSuchMethodException noMethodEx) {
return clazz.getDeclaredConstructor().newInstance();
}
}
}

View File

@@ -1,72 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.bukkit.task;
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 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) {
super(core, player, floodgatePlayer);
}
@Override
protected void startLogin() {
BukkitLoginSession session = new BukkitLoginSession(player.getName(), isRegistered, profile);
// enable auto login based on the value of 'autoLoginFloodgate' in config.yml
session.setVerified(autoLoginFloodgate.equals("true")
|| (autoLoginFloodgate.equals("linked") && isLinked));
// run login task
Runnable forceLoginTask = new ForceLoginTask(core.getPlugin().getCore(), player, session);
Bukkit.getScheduler().runTaskAsynchronously(core.getPlugin(), forceLoginTask);
}
protected String getName(Player player) {
return player.getName();
}
protected UUID getUUID(Player player) {
return player.getUniqueId();
}
protected InetSocketAddress getAddress(Player player) {
return player.getAddress();
}
}

View File

@@ -1,109 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.bukkit.task;
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 java.util.concurrent.ExecutionException;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.metadata.FixedMetadataValue;
public class ForceLoginTask extends ForceLoginManagement<Player, CommandSender, BukkitLoginSession, FastLoginBukkit> {
public ForceLoginTask(FastLoginCore<Player, CommandSender, FastLoginBukkit> core, Player player,
BukkitLoginSession session) {
super(core, player, session);
}
@Override
public void run() {
// block this target player for BungeeCord ID brute force attacks
FastLoginBukkit plugin = core.getPlugin();
player.setMetadata(core.getPlugin().getName(), new FixedMetadataValue(plugin, true));
if (session != null && !session.getUsername().equals(player.getName())) {
String playerName = player.getName();
plugin.getLog().warn("Player username {} is not matching session {}", playerName, session.getUsername());
return;
}
super.run();
PremiumStatus status = PremiumStatus.CRACKED;
if (isOnlineMode()) {
status = PremiumStatus.PREMIUM;
}
plugin.getPremiumPlayers().put(player.getUniqueId(), status);
}
@Override
public FastLoginAutoLoginEvent callFastLoginAutoLoginEvent(LoginSession session, StoredProfile profile) {
BukkitFastLoginAutoLoginEvent event = new BukkitFastLoginAutoLoginEvent(session, profile);
core.getPlugin().getServer().getPluginManager().callEvent(event);
return event;
}
@Override
public void onForceActionSuccess(LoginSession session) {
if (core.getPlugin().getBungeeManager().isEnabled()) {
core.getPlugin().getBungeeManager().sendPluginMessage(player, new SuccessMessage());
}
}
@Override
public String getName(Player player) {
return player.getName();
}
@Override
public boolean isOnline(Player player) {
try {
//the player-list isn't thread-safe
return Bukkit.getScheduler().callSyncMethod(core.getPlugin(), player::isOnline).get();
} catch (InterruptedException | ExecutionException ex) {
core.getPlugin().getLog().error("Failed to perform thread-safe online check for {}", player, ex);
return false;
}
}
@Override
public boolean isOnlineMode() {
return session.isVerified();
}
}

View File

@@ -1,65 +0,0 @@
# project data for Bukkit in order to register our plugin with all it's components
# ${-} are variables from Maven (pom.xml) which will be replaced after the build
name: ${project.parent.name}
version: ${project.version}-${git.commit.id.abbrev}
main: ${project.groupId}.${project.artifactId}.${project.name}
# meta data for plugin managers
authors: [games647, 'https://github.com/games647/FastLogin/graphs/contributors']
description: |
${project.description}
website: ${project.url}
dev-url: ${project.url}
# This plugin doesn't have to be transformed for compatibility with Minecraft >= 1.13
api-version: '1.13'
softdepend:
# We depend on either ProtocolLib or ProtocolSupport
- ProtocolSupport
- ProtocolLib
# Premium variable
- PlaceholderAPI
# Bedrock Player Bridge
- Geyser-Spigot
- floodgate
# Auth plugins
- AuthMe
- LoginSecurity
- SodionAuth
- xAuth
- LogIt
- UltraAuth
- CrazyLogin
commands:
${project.parent.name}:
description: 'Label the invoker as premium'
aliases: [prem, premium, loginfast]
usage: /<command> [player]
permission: ${project.artifactId}.command.premium
cracked:
description: 'Label the invoker or the player specified as cracked if marked premium before'
aliases: [unpremium]
usage: /<command> [player]
permission: ${project.artifactId}.command.cracked
permissions:
${project.artifactId}.command.premium:
description: 'Label themselves as premium'
default: true
${project.artifactId}.command.premium.other:
description: 'Label others as premium'
children:
${project.artifactId}.command.premium: true
${project.artifactId}.command.cracked:
description: 'Label themselves as cracked'
default: true
${project.artifactId}.command.cracked.other:
description: 'Label others as cracked'
children:
${project.artifactId}.command.cracked: true

View File

@@ -1,46 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.bukkit.listener.protocollib;
import java.security.SecureRandom;
import org.junit.Test;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.MatcherAssert.assertThat;
public class EncryptionUtilTest {
@Test
public void testVerifyToken() {
SecureRandom random = new SecureRandom();
byte[] token = EncryptionUtil.generateVerifyToken(random);
assertThat(token, notNullValue());
assertThat(token.length, is(4));
}
}

View File

@@ -1,218 +0,0 @@
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() {
}
}

View File

@@ -1,29 +0,0 @@
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

View File

@@ -1,14 +0,0 @@
<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>

View File

@@ -1,292 +0,0 @@
# 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

View File

@@ -1,50 +0,0 @@
#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

View File

@@ -1,164 +0,0 @@
# 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: {}

Binary file not shown.

View File

@@ -1,210 +0,0 @@
<!--
SPDX-License-Identifier: MIT
The MIT License (MIT)
Copyright (c) 2015-2021 <Your name and contributors>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.github.games647</groupId>
<artifactId>fastlogin</artifactId>
<version>1.11-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<!--This has to be in lowercase because it's used by plugin.yml-->
<artifactId>fastlogin.bungee</artifactId>
<packaging>jar</packaging>
<!--Represents the main plugin-->
<name>FastLoginBungee</name>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.4</version>
<configuration>
<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>
<shadedPattern>fastlogin.hikari</shadedPattern>
</relocation>
<relocation>
<pattern>org.slf4j</pattern>
<shadedPattern>fastlogin.slf4j</shadedPattern>
</relocation>
</relocations>
<!-- Rename the service file too to let SLF4J api find our own relocated jdk logger -->
<!-- Located in META-INF/services -->
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
</transformers>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>codemc-repo</id>
<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>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<dependencies>
<!--Common plugin component-->
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>fastlogin.core</artifactId>
<version>${project.version}</version>
</dependency>
<!--BungeeCord with also the part outside the API-->
<dependency>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-proxy</artifactId>
<version>1.18-R0.1-SNAPSHOT</version>
<scope>provided</scope>
<!-- Use our own newer api version -->
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--Floodgate for Xbox Live Authentication-->
<dependency>
<groupId>org.geysermc.floodgate</groupId>
<artifactId>api</artifactId>
<version>${floodgate.version}</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>io.netty</groupId>
<artifactId>*</artifactId>
</exclusion>
<exclusion>
<groupId>org.geysermc.cumulus</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Bedrock player bridge -->
<dependency>
<groupId>org.geysermc</groupId>
<artifactId>core</artifactId>
<version>${geyser.version}</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- We need the API, but it was excluded above -->
<dependency>
<groupId>org.geysermc</groupId>
<artifactId>geyser-api</artifactId>
<version>${geyser.version}</version>
<scope>provided</scope>
</dependency>
<!--Login plugin-->
<dependency>
<groupId>me.vik1395</groupId>
<artifactId>BungeeAuth</artifactId>
<version>1.4</version>
<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>

View File

@@ -1,68 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.bungee;
import com.github.games647.fastlogin.core.StoredProfile;
import com.github.games647.fastlogin.core.shared.LoginSession;
public class BungeeLoginSession extends LoginSession {
private boolean alreadySaved;
private boolean alreadyLogged;
public BungeeLoginSession(String username, boolean registered, StoredProfile profile) {
super(username, registered, profile);
}
public synchronized void setRegistered(boolean registered) {
this.registered = registered;
}
public synchronized boolean isAlreadySaved() {
return alreadySaved;
}
public synchronized void setAlreadySaved(boolean alreadySaved) {
this.alreadySaved = alreadySaved;
}
public synchronized boolean isAlreadyLogged() {
return alreadyLogged;
}
public synchronized void setAlreadyLogged(boolean alreadyLogged) {
this.alreadyLogged = alreadyLogged;
}
@Override
public synchronized String toString() {
return this.getClass().getSimpleName() + '{' +
"alreadySaved=" + alreadySaved +
", alreadyLogged=" + alreadyLogged +
", registered=" + registered +
"} " + super.toString();
}
}

View File

@@ -1,79 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.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;
public class BungeeLoginSource implements LoginSource {
private final PendingConnection connection;
private final PreLoginEvent preLoginEvent;
public BungeeLoginSource(PendingConnection connection, PreLoginEvent preLoginEvent) {
this.connection = connection;
this.preLoginEvent = preLoginEvent;
}
@Override
public void enableOnlinemode() {
connection.setOnlineMode(true);
}
@Override
public void kick(String message) {
preLoginEvent.setCancelled(true);
if (message == null) {
preLoginEvent.setCancelReason(new ComponentBuilder("Kicked").color(ChatColor.WHITE).create());
} else {
preLoginEvent.setCancelReason(TextComponent.fromLegacyText(message));
}
}
@Override
public InetSocketAddress getAddress() {
return connection.getAddress();
}
public PendingConnection getConnection() {
return connection;
}
@Override
public String toString() {
return this.getClass().getSimpleName() + '{' +
"connection=" + connection +
'}';
}
}

View File

@@ -1,218 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.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;
import com.github.games647.fastlogin.core.hooks.bedrock.FloodgateService;
import com.github.games647.fastlogin.core.hooks.bedrock.GeyserService;
import com.github.games647.fastlogin.core.message.ChangePremiumMessage;
import com.github.games647.fastlogin.core.message.ChannelMessage;
import com.github.games647.fastlogin.core.message.NamespaceKey;
import com.github.games647.fastlogin.core.message.SuccessMessage;
import com.github.games647.fastlogin.core.shared.FastLoginCore;
import com.github.games647.fastlogin.core.shared.PlatformPlugin;
import com.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.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;
/**
* BungeeCord version of FastLogin. This plugin keeps track on online mode connections.
*/
public class FastLoginBungee extends Plugin implements PlatformPlugin<CommandSender> {
private final ConcurrentMap<PendingConnection, BungeeLoginSession> session = new MapMaker().weakKeys().makeMap();
private FastLoginCore<ProxiedPlayer, CommandSender, FastLoginBungee> core;
private AsyncScheduler scheduler;
private FloodgateService floodgateService;
private GeyserService geyserService;
private Logger logger;
@Override
public void onEnable() {
logger = CommonUtil.initializeLoggerService(getLogger());
scheduler = new AsyncScheduler(logger, getThreadFactory());
core = new FastLoginCore<>(this);
core.load();
if (!core.setupDatabase()) {
return;
}
if (isPluginInstalled("floodgate")) {
floodgateService = new FloodgateService(FloodgateApi.getInstance(), core);
}
if (isPluginInstalled("Geyser-BungeeCord")) {
geyserService = new GeyserService(GeyserImpl.getInstance(), core);
}
//events
PluginManager pluginManager = getProxy().getPluginManager();
ConnectListener connectListener = new ConnectListener(this, core.getRateLimiter());
pluginManager.registerListener(this, connectListener);
pluginManager.registerListener(this, new PluginMessageListener(this));
//this is required to listen to incoming messages from the server
getProxy().registerChannel(NamespaceKey.getCombined(getName(), ChangePremiumMessage.CHANGE_CHANNEL));
getProxy().registerChannel(NamespaceKey.getCombined(getName(), SuccessMessage.SUCCESS_CHANNEL));
registerHook();
}
@Override
public void onDisable() {
if (core != null) {
core.close();
}
}
public FastLoginCore<ProxiedPlayer, CommandSender, FastLoginBungee> getCore() {
return core;
}
public ConcurrentMap<PendingConnection, BungeeLoginSession> getSession() {
return session;
}
private void registerHook() {
try {
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();
pluginName = pluginName.substring(0, pluginName.length() - "Hook".length());
//uses only member classes which uses AuthPlugin interface (skip interfaces)
Plugin plugin = getProxy().getPluginManager().getPlugin(pluginName);
if (plugin != null) {
logger.info("Hooking into auth plugin: {}", pluginName);
core.setAuthPluginHook(
clazz.getDeclaredConstructor(FastLoginBungee.class).newInstance(this));
break;
}
}
} catch (ReflectiveOperationException ex) {
logger.error("Couldn't load the auth hook class", ex);
}
}
public void sendPluginMessage(Server server, ChannelMessage message) {
if (server != null) {
ByteArrayDataOutput dataOutput = ByteStreams.newDataOutput();
message.writeTo(dataOutput);
NamespaceKey channel = new NamespaceKey(getName(), message.getChannelName());
server.sendData(channel.getCombinedName(), dataOutput.toByteArray());
}
}
@Override
public String getName() {
return getDescription().getName();
}
@Override
public Path getPluginFolder() {
return getDataFolder().toPath();
}
@Override
public Logger getLog() {
return logger;
}
@Override
public void sendMessage(CommandSender receiver, String message) {
receiver.sendMessage(TextComponent.fromLegacyText(message));
}
@Override
@SuppressWarnings("deprecation")
public ThreadFactory getThreadFactory() {
return new ThreadFactoryBuilder()
.setNameFormat(getName() + " Pool Thread #%1$d")
//Hikari create daemons by default
.setDaemon(true)
.setThreadFactory(new GroupedThreadFactory(this, getName()))
.build();
}
@Override
public AsyncScheduler getScheduler() {
return scheduler;
}
@Override
public boolean isPluginInstalled(String name) {
return getProxy().getPluginManager().getPlugin(name) != null;
}
public FloodgateService getFloodgateService() {
return floodgateService;
}
public GeyserService getGeyserService() {
return geyserService;
}
@Override
public BedrockService<?> getBedrockService() {
if (floodgateService != null) {
return floodgateService;
}
return geyserService;
}
}

View File

@@ -1,64 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.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 net.md_5.bungee.api.plugin.Cancellable;
import net.md_5.bungee.api.plugin.Event;
public class BungeeFastLoginAutoLoginEvent extends Event implements FastLoginAutoLoginEvent, Cancellable {
private final LoginSession session;
private final StoredProfile profile;
private boolean cancelled;
public BungeeFastLoginAutoLoginEvent(LoginSession session, StoredProfile profile) {
this.session = session;
this.profile = profile;
}
@Override
public LoginSession getSession() {
return session;
}
@Override
public StoredProfile getProfile() {
return profile;
}
@Override
public boolean isCancelled() {
return cancelled;
}
@Override
public void setCancelled(boolean cancelled) {
this.cancelled = cancelled;
}
}

View File

@@ -1,59 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.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 net.md_5.bungee.api.plugin.Event;
public class BungeeFastLoginPreLoginEvent extends Event implements FastLoginPreLoginEvent {
private final String username;
private final LoginSource source;
private final StoredProfile profile;
public BungeeFastLoginPreLoginEvent(String username, LoginSource source, StoredProfile profile) {
this.username = username;
this.source = source;
this.profile = profile;
}
@Override
public String getUsername() {
return username;
}
@Override
public LoginSource getSource() {
return source;
}
@Override
public StoredProfile getProfile() {
return profile;
}
}

View File

@@ -1,52 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.bungee.event;
import com.github.games647.fastlogin.core.StoredProfile;
import com.github.games647.fastlogin.core.shared.event.FastLoginPremiumToggleEvent;
import net.md_5.bungee.api.plugin.Event;
public class BungeeFastLoginPremiumToggleEvent extends Event implements FastLoginPremiumToggleEvent {
private final StoredProfile profile;
private final PremiumToggleReason reason;
public BungeeFastLoginPremiumToggleEvent(StoredProfile profile, PremiumToggleReason reason) {
this.profile = profile;
this.reason = reason;
}
@Override
public StoredProfile getProfile() {
return profile;
}
@Override
public PremiumToggleReason getReason() {
return reason;
}
}

View File

@@ -1,65 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.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: https://github.com/vik1395/BungeeAuth-Minecraft
*
* Project page:
*
* Spigot: https://www.spigotmc.org/resources/bungeeauth.493/
*/
public class BungeeAuthHook implements AuthPlugin<ProxiedPlayer> {
private final RequestHandler requestHandler = new RequestHandler();
public BungeeAuthHook(FastLoginBungee plugin) {
}
@Override
public boolean forceLogin(ProxiedPlayer player) {
String playerName = player.getName();
return Main.plonline.contains(playerName) || requestHandler.forceLogin(playerName);
}
@Override
public boolean isRegistered(String playerName) {
return requestHandler.isRegistered(playerName);
}
@Override
public boolean forceRegister(ProxiedPlayer player, String password) {
return requestHandler.forceRegister(player, password);
}
}

View File

@@ -1,92 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.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;
}
}
}

View File

@@ -1,78 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.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);
}
}

View File

@@ -1,213 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.bungee.listener;
import com.github.games647.craftapi.UUIDAdapter;
import com.github.games647.fastlogin.bungee.BungeeLoginSession;
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.RateLimiter;
import com.github.games647.fastlogin.core.StoredProfile;
import com.github.games647.fastlogin.core.hooks.bedrock.FloodgateService;
import com.github.games647.fastlogin.core.shared.LoginSession;
import com.google.common.base.Throwables;
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;
import net.md_5.bungee.api.event.LoginEvent;
import net.md_5.bungee.api.event.PlayerDisconnectEvent;
import net.md_5.bungee.api.event.PreLoginEvent;
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 org.geysermc.floodgate.api.player.FloodgatePlayer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 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.
*/
public class ConnectListener implements Listener {
private static final String UUID_FIELD_NAME = "uniqueId";
private static final MethodHandle uniqueIdSetter;
static {
MethodHandle setHandle = null;
try {
Lookup lookup = MethodHandles.lookup();
Class.forName("net.md_5.bungee.connection.InitialHandler");
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 initial handler; Disabling premium UUID and skin won't work.",
reflectiveOperationException
);
}
uniqueIdSetter = setHandle;
}
private final FastLoginBungee plugin;
private final RateLimiter rateLimiter;
private final Property[] emptyProperties = {};
public ConnectListener(FastLoginBungee plugin, RateLimiter rateLimiter) {
this.plugin = plugin;
this.rateLimiter = rateLimiter;
}
@EventHandler
public void onPreLogin(PreLoginEvent preLoginEvent) {
PendingConnection connection = preLoginEvent.getConnection();
if (preLoginEvent.isCancelled()) {
return;
}
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());
preLoginEvent.registerIntent(plugin);
Runnable asyncPremiumCheck = new AsyncPremiumCheck(plugin, preLoginEvent, connection, username);
plugin.getScheduler().runAsync(asyncPremiumCheck);
}
@EventHandler(priority = EventPriority.LOWEST)
public void onLogin(LoginEvent loginEvent) {
if (loginEvent.isCancelled()) {
return;
}
//use the login event instead of the post login event in order to send the login success packet to the client
//with the offline uuid this makes it possible to set the skin then
PendingConnection connection = loginEvent.getConnection();
if (connection.isOnlineMode()) {
LoginSession session = plugin.getSession().get(connection);
UUID verifiedUUID = connection.getUniqueId();
String verifiedUsername = connection.getName();
session.setUuid(verifiedUUID);
session.setVerifiedUsername(verifiedUsername);
StoredProfile playerProfile = session.getProfile();
playerProfile.setId(verifiedUUID);
// 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)) {
setOfflineId(initialHandler, verifiedUsername);
}
if (!plugin.getCore().getConfig().get("forwardSkin", true)) {
// this is null on offline mode
LoginResult loginProfile = initialHandler.getLoginProfile();
loginProfile.setProperties(emptyProperties);
}
}
}
}
private void setOfflineId(InitialHandler connection, String username) {
try {
UUID oldPremiumId = connection.getUniqueId();
UUID offlineUUID = UUIDAdapter.generateOfflineId(username);
// BungeeCord only allows setting the UUID in PreLogin events and before requesting online mode
// However if online mode is requested, it will override previous values
// So we have to do it with reflection
uniqueIdSetter.invokeExact(connection, offlineUUID);
String format = "Overridden UUID from {} to {} (based of {}) on {}";
plugin.getLog().info(format, oldPremiumId, offlineUUID, username, connection);
} catch (Exception ex) {
plugin.getLog().error("Failed to set offline uuid of {}", username, ex);
} catch (Throwable throwable) {
// throw remaining exceptions like out of memory that we shouldn't handle ourselves
Throwables.throwIfUnchecked(throwable);
}
}
@EventHandler
public void onServerConnected(ServerConnectedEvent serverConnectedEvent) {
ProxiedPlayer player = serverConnectedEvent.getPlayer();
Server server = serverConnectedEvent.getServer();
FloodgateService floodgateService = plugin.getFloodgateService();
if (floodgateService != null) {
FloodgatePlayer floodgatePlayer = floodgateService.getBedrockPlayer(player.getUniqueId());
if (floodgatePlayer != null) {
Runnable floodgateAuthTask = new FloodgateAuthTask(plugin.getCore(), player, floodgatePlayer, server);
plugin.getScheduler().runAsync(floodgateAuthTask);
return;
}
}
BungeeLoginSession session = plugin.getSession().get(player.getPendingConnection());
if (session == null) {
return;
}
// delay sending force command, because Paper will process the login event asynchronously
// In this case it means that the force command (plugin message) is already received and processed while
// player is still in the login phase and reported to be offline.
Runnable loginTask = new ForceLoginTask(plugin.getCore(), player, server, session);
plugin.getScheduler().runAsync(loginTask);
}
@EventHandler
public void onDisconnect(PlayerDisconnectEvent disconnectEvent) {
ProxiedPlayer player = disconnectEvent.getPlayer();
plugin.getSession().remove(player.getPendingConnection());
plugin.getCore().getPendingConfirms().remove(player.getUniqueId());
}
}

View File

@@ -1,141 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.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.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;
import net.md_5.bungee.api.connection.Server;
import net.md_5.bungee.api.event.PluginMessageEvent;
import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.event.EventHandler;
public class PluginMessageListener implements Listener {
private final FastLoginBungee plugin;
private final String successChannel;
private final String changeChannel;
public PluginMessageListener(FastLoginBungee plugin) {
this.plugin = plugin;
this.successChannel = new NamespaceKey(plugin.getName(), SuccessMessage.SUCCESS_CHANNEL).getCombinedName();
this.changeChannel = new NamespaceKey(plugin.getName(), ChangePremiumMessage.CHANGE_CHANNEL).getCombinedName();
}
@EventHandler
public void onPluginMessage(PluginMessageEvent pluginMessageEvent) {
String channel = pluginMessageEvent.getTag();
if (pluginMessageEvent.isCancelled() || !channel.startsWith(plugin.getName().toLowerCase())) {
return;
}
//the client shouldn't be able to read the messages in order to know something about server internal states
//moreover the client shouldn't be able to fake a running premium check by sending the result message
pluginMessageEvent.setCancelled(true);
if (!(pluginMessageEvent.getSender() instanceof Server)) {
//check if the message is sent from the server
return;
}
//so that we can safely process this in the background
byte[] data = Arrays.copyOf(pluginMessageEvent.getData(), pluginMessageEvent.getData().length);
ProxiedPlayer forPlayer = (ProxiedPlayer) pluginMessageEvent.getReceiver();
plugin.getScheduler().runAsync(() -> readMessage(forPlayer, channel, data));
}
private void readMessage(ProxiedPlayer forPlayer, String channel, byte[] data) {
FastLoginCore<ProxiedPlayer, CommandSender, FastLoginBungee> core = plugin.getCore();
ByteArrayDataInput dataInput = ByteStreams.newDataInput(data);
if (successChannel.equals(channel)) {
onSuccessMessage(forPlayer);
} else if (changeChannel.equals(channel)) {
ChangePremiumMessage changeMessage = new ChangePremiumMessage();
changeMessage.readFrom(dataInput);
String playerName = changeMessage.getPlayerName();
boolean isSourceInvoker = changeMessage.isSourceInvoker();
if (changeMessage.shouldEnable()) {
if (playerName.equals(forPlayer.getName()) && plugin.getCore().getConfig().get("premium-warning", true)
&& !core.getPendingConfirms().contains(forPlayer.getUniqueId())) {
String message = core.getMessage("premium-warning");
forPlayer.sendMessage(TextComponent.fromLegacyText(message));
core.getPendingConfirms().add(forPlayer.getUniqueId());
return;
}
core.getPendingConfirms().remove(forPlayer.getUniqueId());
Runnable task = new AsyncToggleMessage(core, forPlayer, playerName, true, isSourceInvoker);
plugin.getScheduler().runAsync(task);
} else {
Runnable task = new AsyncToggleMessage(core, forPlayer, playerName, false, isSourceInvoker);
plugin.getScheduler().runAsync(task);
}
}
}
private void onSuccessMessage(ProxiedPlayer forPlayer) {
boolean shouldPersist = forPlayer.getPendingConnection().isOnlineMode();
FloodgateService floodgateService = plugin.getFloodgateService();
if (!shouldPersist && floodgateService != null) {
// always save floodgate players to lock this username
shouldPersist = floodgateService.isBedrockPlayer(forPlayer.getUniqueId());
}
if (shouldPersist) {
//bukkit module successfully received and force logged in the user
//update only on success to prevent corrupt data
BungeeLoginSession loginSession = plugin.getSession().get(forPlayer.getPendingConnection());
StoredProfile playerProfile = loginSession.getProfile();
loginSession.setRegistered(true);
if (!loginSession.isAlreadySaved()) {
playerProfile.setPremium(true);
plugin.getCore().getStorage().save(playerProfile);
loginSession.setAlreadySaved(true);
}
}
}
}

View File

@@ -1,92 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.bungee.task;
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 net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.connection.PendingConnection;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.event.PreLoginEvent;
public class AsyncPremiumCheck extends JoinManagement<ProxiedPlayer, CommandSender, BungeeLoginSource>
implements Runnable {
private final FastLoginBungee plugin;
private final PreLoginEvent preLoginEvent;
private final String username;
private final PendingConnection connection;
public AsyncPremiumCheck(FastLoginBungee plugin, PreLoginEvent preLoginEvent, PendingConnection connection,
String username) {
super(plugin.getCore(), plugin.getCore().getAuthPluginHook(), plugin.getBedrockService());
this.plugin = plugin;
this.preLoginEvent = preLoginEvent;
this.connection = connection;
this.username = username;
}
@Override
public void run() {
plugin.getSession().remove(connection);
try {
super.onLogin(username, new BungeeLoginSource(connection, preLoginEvent));
} finally {
preLoginEvent.completeIntent(plugin);
}
}
@Override
public FastLoginPreLoginEvent callFastLoginPreLoginEvent(String username, BungeeLoginSource source,
StoredProfile profile) {
return plugin.getProxy().getPluginManager()
.callEvent(new BungeeFastLoginPreLoginEvent(username, source, profile));
}
@Override
public void requestPremiumLogin(BungeeLoginSource source, StoredProfile profile,
String username, boolean registered) {
source.enableOnlinemode();
plugin.getSession().put(source.getConnection(), new BungeeLoginSession(username, registered, profile));
String ip = source.getAddress().getAddress().getHostAddress();
plugin.getCore().getPendingLogin().put(ip + username, new Object());
}
@Override
public void startCrackedSession(BungeeLoginSource source, StoredProfile profile, String username) {
plugin.getSession().put(source.getConnection(), new BungeeLoginSession(username, false, profile));
}
}

View File

@@ -1,108 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.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 net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.connection.ProxiedPlayer;
public class AsyncToggleMessage implements Runnable {
private final FastLoginCore<ProxiedPlayer, CommandSender, FastLoginBungee> core;
private final ProxiedPlayer sender;
private final String targetPlayer;
private final boolean toPremium;
private final boolean isPlayerSender;
public AsyncToggleMessage(FastLoginCore<ProxiedPlayer, CommandSender, FastLoginBungee> core,
ProxiedPlayer sender, String playerName, boolean toPremium, boolean playerSender) {
this.core = core;
this.sender = sender;
this.targetPlayer = playerName;
this.toPremium = toPremium;
this.isPlayerSender = playerSender;
}
@Override
public void run() {
if (toPremium) {
activatePremium();
} else {
turnOffPremium();
}
}
private void turnOffPremium() {
StoredProfile playerProfile = core.getStorage().loadProfile(targetPlayer);
//existing player is already cracked
if (playerProfile.isSaved() && !playerProfile.isPremium()) {
sendMessage("not-premium");
return;
}
playerProfile.setPremium(false);
playerProfile.setId(null);
core.getStorage().save(playerProfile);
PremiumToggleReason reason = (!isPlayerSender || !sender.getName().equalsIgnoreCase(playerProfile.getName())) ?
PremiumToggleReason.COMMAND_OTHER : PremiumToggleReason.COMMAND_SELF;
core.getPlugin().getProxy().getPluginManager().callEvent(
new BungeeFastLoginPremiumToggleEvent(playerProfile, reason));
sendMessage("remove-premium");
}
private void activatePremium() {
StoredProfile playerProfile = core.getStorage().loadProfile(targetPlayer);
if (playerProfile.isPremium()) {
sendMessage("already-exists");
return;
}
playerProfile.setPremium(true);
core.getStorage().save(playerProfile);
PremiumToggleReason reason = (!isPlayerSender || !sender.getName().equalsIgnoreCase(playerProfile.getName())) ?
PremiumToggleReason.COMMAND_OTHER : PremiumToggleReason.COMMAND_SELF;
core.getPlugin().getProxy().getPluginManager().callEvent(
new BungeeFastLoginPremiumToggleEvent(playerProfile, reason));
sendMessage("add-premium");
}
private void sendMessage(String localeId) {
String message = core.getMessage(localeId);
if (isPlayerSender) {
sender.sendMessage(TextComponent.fromLegacyText(message));
} else {
CommandSender console = ProxyServer.getInstance().getConsole();
console.sendMessage(TextComponent.fromLegacyText(message));
}
}
}

View File

@@ -1,83 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.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;
public class FloodgateAuthTask
extends FloodgateManagement<ProxiedPlayer, CommandSender, BungeeLoginSession, FastLoginBungee> {
private final Server server;
public FloodgateAuthTask(FastLoginCore<ProxiedPlayer, CommandSender, FastLoginBungee> core, ProxiedPlayer player,
FloodgatePlayer floodgatePlayer, Server server) {
super(core, player, floodgatePlayer);
this.server = server;
}
@Override
protected void startLogin() {
BungeeLoginSession session = new BungeeLoginSession(player.getName(), isRegistered, profile);
core.getPlugin().getSession().put(player.getPendingConnection(), session);
// enable auto login based on the value of 'autoLoginFloodgate' in config.yml
boolean forcedOnlineMode = autoLoginFloodgate.equals("true")
|| (autoLoginFloodgate.equals("linked") && isLinked);
// run login task
Runnable forceLoginTask = new ForceLoginTask(core.getPlugin().getCore(), player, server, session,
forcedOnlineMode);
core.getPlugin().getScheduler().runAsync(forceLoginTask);
}
@Override
protected String getName(ProxiedPlayer player) {
return player.getName();
}
@Override
protected UUID getUUID(ProxiedPlayer player) {
return player.getUniqueId();
}
@Override
protected InetSocketAddress getAddress(ProxiedPlayer player) {
return player.getAddress();
}
}

View File

@@ -1,131 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.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;
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 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;
public class ForceLoginTask
extends ForceLoginManagement<ProxiedPlayer, CommandSender, BungeeLoginSession, FastLoginBungee> {
private final Server server;
//treat player as if they had a premium account, even when they don't
//use for Floodgate auto login/register
private final boolean forcedOnlineMode;
public ForceLoginTask(FastLoginCore<ProxiedPlayer, CommandSender, FastLoginBungee> core,
ProxiedPlayer player, Server server, BungeeLoginSession session, boolean forcedOnlineMode) {
super(core, player, session);
this.server = server;
this.forcedOnlineMode = forcedOnlineMode;
}
public ForceLoginTask(FastLoginCore<ProxiedPlayer, CommandSender, FastLoginBungee> core, ProxiedPlayer player,
Server server, BungeeLoginSession session) {
this(core, player, server, session, false);
}
@Override
public void run() {
if (session == null) {
return;
}
super.run();
if (!isOnlineMode()) {
session.setAlreadySaved(true);
}
}
@Override
public boolean forceLogin(ProxiedPlayer player) {
if (session.isAlreadyLogged()) {
return true;
}
session.setAlreadyLogged(true);
return super.forceLogin(player);
}
@Override
public FastLoginAutoLoginEvent callFastLoginAutoLoginEvent(LoginSession session, StoredProfile profile) {
return core.getPlugin().getProxy().getPluginManager()
.callEvent(new BungeeFastLoginAutoLoginEvent(session, profile));
}
@Override
public boolean forceRegister(ProxiedPlayer player) {
return session.isAlreadyLogged() || super.forceRegister(player);
}
@Override
public void onForceActionSuccess(LoginSession session) {
//sub channel name
Type type = Type.LOGIN;
if (session.needsRegistration()) {
type = Type.REGISTER;
}
UUID proxyId = UUID.fromString(ProxyServer.getInstance().getConfig().getUuid());
ChannelMessage loginMessage = new LoginActionMessage(type, player.getName(), proxyId);
core.getPlugin().sendPluginMessage(server, loginMessage);
}
@Override
public String getName(ProxiedPlayer player) {
return player.getName();
}
@Override
public boolean isOnline(ProxiedPlayer player) {
return player.isConnected();
}
@Override
public boolean isOnlineMode() {
return forcedOnlineMode || player.getPendingConnection().isOnlineMode();
}
}

View File

@@ -1,21 +0,0 @@
# project data for BungeeCord
# This file will be prioritised over plugin.yml which can be also used for Bungee
# This make it easy to combine BungeeCord and Bukkit support in one plugin
name: ${project.parent.name}
# ${-} will be automatically replaced by Maven
main: ${project.groupId}.${project.artifactId}.${project.name}
version: ${project.version}-${git.commit.id.abbrev}
author: games647, https://github.com/games647/FastLogin/graphs/contributors
softDepends:
# BungeeCord auth plugins
- BungeeAuth
- BungeeCordAuthenticatorBungee
- SodionAuth
# Bedrock Player Bridge
- Geyser-BungeeCord
- floodgate
description: |
${project.description}

View File

@@ -1,185 +0,0 @@
<!--
SPDX-License-Identifier: MIT
The MIT License (MIT)
Copyright (c) 2015-2021 <Your name and contributors>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.github.games647</groupId>
<artifactId>fastlogin</artifactId>
<version>1.11-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>fastlogin.core</artifactId>
<packaging>jar</packaging>
<name>FastLoginCore</name>
<repositories>
<repository>
<id>luck-repo</id>
<url>https://ci.lucko.me/plugin/repository/everything</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>codemc-repo</id>
<url>https://repo.codemc.io/repository/maven-public/</url>
</repository>
<!-- Floodgate -->
<repository>
<id>opencollab-snapshot</id>
<url>https://repo.opencollab.dev/maven-snapshots/</url>
</repository>
</repositories>
<dependencies>
<!-- Libraries that we shade into the project -->
<!--Database pooling-->
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>4.0.3</version>
<exclusions>
<!-- HikariCP uses an old version of this API that has a typo in the service interface -->
<!-- We will use the api provided by the jdk14 dependency -->
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--Logging framework implements slf4j which is required by hikari-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
<version>2.0.0-alpha6</version>
</dependency>
<!-- 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.16-R0.4</version>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--Floodgate for Xbox Live Authentication-->
<dependency>
<groupId>org.geysermc.floodgate</groupId>
<artifactId>api</artifactId>
<version>${floodgate.version}</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>io.netty</groupId>
<artifactId>*</artifactId>
</exclusion>
<exclusion>
<groupId>org.geysermc.cumulus</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Bedrock player bridge -->
<dependency>
<groupId>org.geysermc</groupId>
<artifactId>core</artifactId>
<version>${geyser.version}</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- We need the API, but it was excluded above -->
<dependency>
<groupId>org.geysermc</groupId>
<artifactId>geyser-api</artifactId>
<version>${geyser.version}</version>
<scope>provided</scope>
</dependency>
<!--Common component for contacting the Mojang API-->
<dependency>
<groupId>com.github.games647</groupId>
<artifactId>craftapi</artifactId>
<version>0.5.1</version>
</dependency>
<!-- APIs we can use because they are available in all platforms (Spigot, Bungee, Velocity) -->
<dependency>
<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>

View File

@@ -1,94 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.core;
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);
}
}

View File

@@ -1,114 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.core;
import com.github.games647.craftapi.cache.SafeCacheBuilder;
import com.google.common.cache.CacheLoader;
import java.lang.reflect.Constructor;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.jul.JDK14LoggerAdapter;
public class CommonUtil {
private static final char COLOR_CHAR = '&';
private static final char TRANSLATED_CHAR = '§';
public static <K, V> ConcurrentMap<K, V> buildCache(int expireAfterWrite, int maxSize) {
SafeCacheBuilder<Object, Object> builder = SafeCacheBuilder.newBuilder();
if (expireAfterWrite > 0) {
builder.expireAfterWrite(expireAfterWrite, TimeUnit.MINUTES);
}
if (maxSize > 0) {
builder.maximumSize(maxSize);
}
return builder.build(CacheLoader.from(() -> {
throw new UnsupportedOperationException();
}));
}
public static String translateColorCodes(String rawMessage) {
char[] chars = rawMessage.toCharArray();
for (int i = 0; i < chars.length - 1; i++) {
if (chars[i] == COLOR_CHAR && "0123456789AaBbCcDdEeFfKkLlMmNnOoRr".indexOf(chars[i + 1]) > -1) {
chars[i] = TRANSLATED_CHAR;
chars[i + 1] = Character.toLowerCase(chars[i + 1]);
}
}
return new String(chars);
}
/**
* This creates a SLF4J logger. In the process it initializes the SLF4J service provider. This method looks
* for the provider in the plugin jar instead of in the server jar when creating a Logger. The provider is only
* initialized once, so this method should be called early.
*
* The provider is bound to the service class `SLF4JServiceProvider`. Relocating this class makes it available
* for exclusive own usage. Other dependencies will use the relocated service too, and therefore will find the
* initialized provider.
*
* @param parent JDK logger
* @return slf4j logger
*/
public static Logger initializeLoggerService(java.util.logging.Logger parent) {
// set the class loader to the plugin one to find our own SLF4J provider in the plugin jar and not in the global
ClassLoader oldLoader = Thread.currentThread().getContextClassLoader();
ClassLoader pluginLoader = CommonUtil.class.getClassLoader();
Thread.currentThread().setContextClassLoader(pluginLoader);
// Trigger provider search
LoggerFactory.getLogger(parent.getName()).info("Initialize logging service");
try {
parent.setLevel(Level.ALL);
Class<JDK14LoggerAdapter> adapterClass = JDK14LoggerAdapter.class;
Constructor<JDK14LoggerAdapter> cons = adapterClass.getDeclaredConstructor(java.util.logging.Logger.class);
cons.setAccessible(true);
return cons.newInstance(parent);
} catch (ReflectiveOperationException reflectEx) {
parent.log(Level.WARNING, "Cannot create slf4j logging adapter", reflectEx);
parent.log(Level.WARNING, "Creating logger instance manually...");
return LoggerFactory.getLogger(parent.getName());
} finally {
// restore previous class loader
Thread.currentThread().setContextClassLoader(oldLoader);
}
}
private CommonUtil() {
//Utility class
}
}

View File

@@ -1,45 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.core;
public enum PremiumStatus {
PREMIUM("Premium"),
CRACKED("Cracked"),
UNKNOWN("Unknown");
private final String readableName;
PremiumStatus(String readableName) {
this.readableName = readableName;
}
public String getReadableName() {
return readableName;
}
}

View File

@@ -1,7 +0,0 @@
package com.github.games647.fastlogin.core;
@FunctionalInterface
public interface RateLimiter {
boolean tryAcquire();
}

View File

@@ -1,139 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.core;
import com.github.games647.craftapi.model.Profile;
import java.time.Instant;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.locks.ReentrantLock;
public class StoredProfile extends Profile {
private long rowId;
private final ReentrantLock saveLock = new ReentrantLock();
private boolean premium;
private String lastIp;
private Instant lastLogin;
public StoredProfile(long rowId, UUID uuid, String playerName, boolean premium, String lastIp, Instant lastLogin) {
super(uuid, playerName);
this.rowId = rowId;
this.premium = premium;
this.lastIp = lastIp;
this.lastLogin = lastLogin;
}
public StoredProfile(UUID uuid, String playerName, boolean premium, String lastIp) {
this(-1, uuid, playerName, premium, lastIp, Instant.now());
}
public ReentrantLock getSaveLock() {
return saveLock;
}
public synchronized boolean isSaved() {
return rowId >= 0;
}
public synchronized void setPlayerName(String playerName) {
this.name = playerName;
}
public synchronized long getRowId() {
return rowId;
}
public synchronized void setRowId(long generatedId) {
this.rowId = generatedId;
}
// can be null
public synchronized UUID getId() {
return id;
}
public synchronized Optional<UUID> getOptId() {
return Optional.ofNullable(id);
}
public synchronized void setId(UUID uniqueId) {
this.id = uniqueId;
}
public synchronized boolean isPremium() {
return premium;
}
public synchronized void setPremium(boolean premium) {
this.premium = premium;
}
public synchronized String getLastIp() {
return lastIp;
}
public synchronized void setLastIp(String lastIp) {
this.lastIp = lastIp;
}
public synchronized Instant getLastLogin() {
return lastLogin;
}
public synchronized void setLastLogin(Instant lastLogin) {
this.lastLogin = lastLogin;
}
@Override
public synchronized boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof StoredProfile)) return false;
if (!super.equals(o)) return false;
StoredProfile that = (StoredProfile) o;
return rowId == that.rowId && premium == that.premium
&& Objects.equals(lastIp, that.lastIp) && lastLogin.equals(that.lastLogin);
}
@Override
public synchronized int hashCode() {
return Objects.hash(super.hashCode(), rowId, premium, lastIp, lastLogin);
}
@Override
public synchronized String toString() {
return this.getClass().getSimpleName() + '{' +
"rowId=" + rowId +
", premium=" + premium +
", lastIp='" + lastIp + '\'' +
", lastLogin=" + lastLogin +
"} " + super.toString();
}
}

View File

@@ -1,77 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.core;
import com.google.common.base.Ticker;
import java.util.Arrays;
/**
* Limit the number of requests with a maximum size. Each requests expire after the specified time making it available
* for another request.
*/
public class TickingRateLimiter implements RateLimiter {
private final Ticker ticker;
private final long[] requests;
private final long expireTime;
private int position;
public TickingRateLimiter(Ticker ticker, int maxLimit, long expireTime) {
this.ticker = ticker;
this.requests = new long[maxLimit];
this.expireTime = expireTime;
// fill the array with expired entry, because nanoTime could overflow and include negative numbers
long nowMilli = ticker.read() / 1_000_000;
long initialVal = nowMilli - expireTime;
Arrays.fill(requests, initialVal);
}
/**
* Ask if access is allowed. If so register the request.
*
* @return true if allowed - false otherwise without any side effects
*/
@Override
public boolean tryAcquire() {
// current time millis is not monotonic - it can jump back depending on user choice or NTP
long nowMilli = ticker.read() / 1_000_000;
synchronized (this) {
// having synchronized will limit the amount of concurrency a lot
long oldest = requests[position];
if (nowMilli - oldest >= expireTime) {
requests[position] = nowMilli;
position = (position + 1) % requests.length;
return true;
}
return false;
}
}
}

View File

@@ -1,87 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.core.hooks;
/**
* Represents a supporting authentication plugin in BungeeCord and Bukkit/Spigot/... servers
*
* @param <P> either {@link org.bukkit.entity.Player} for Bukkit or {@link net.md_5.bungee.api.connection.ProxiedPlayer}
* for BungeeCord
*/
public interface AuthPlugin<P> {
String ALREADY_AUTHENTICATED = "Player {} is already authenticated. Cancelling force login.";
/**
* Login the premium (paid account) player after the player joined successfully the server.
*
* <strong>This operation will be performed async while the player successfully
* joined the server.</strong>
*
* @param player the player that needs to be logged in
* @return if the operation was successful
*/
boolean forceLogin(P player);
/**
* Forces a register in order to protect the paid account.
*
* <strong>This operation will be performed async while the player successfully
* joined the server.</strong>
*
* After a successful registration the player should be logged
* in too.
*
* The method will be called only for premium accounts.
* So it's recommended to set additionally premium property
* if possible.
*
* Background: If we don't register an account, cracked players
* could steal the unregistered account from the paid
* player account
*
* @param player the premium account
* @param password a strong random generated password
* @return if the operation was successful
*/
boolean forceRegister(P player, String password);
/**
* Checks whether an account exists for this player name.
*
* This check should check if a cracked player account exists,
* so we can be sure the premium player doesn't steal the account
* of that player.
*
* This operation will be performed async while the player is
* connecting.
*
* @param playerName player name
* @return if the player has an account
* @throws Exception if an error occurred
*/
boolean isRegistered(String playerName) throws Exception;
}

View File

@@ -1,50 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.core.hooks;
import java.security.SecureRandom;
import java.util.Random;
import java.util.stream.IntStream;
public class DefaultPasswordGenerator<P> implements PasswordGenerator<P> {
private static final int PASSWORD_LENGTH = 8;
private static final char[] PASSWORD_CHARACTERS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
.toCharArray();
private final Random random = new SecureRandom();
@Override
public String getRandomPassword(P player) {
StringBuilder generatedPassword = new StringBuilder(8);
IntStream.rangeClosed(1, PASSWORD_LENGTH)
.map(i -> random.nextInt(PASSWORD_CHARACTERS.length - 1))
.mapToObj(pos -> PASSWORD_CHARACTERS[pos])
.forEach(generatedPassword::append);
return generatedPassword.toString();
}
}

View File

@@ -1,32 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.core.hooks;
@FunctionalInterface
public interface PasswordGenerator<P> {
String getRandomPassword(P player);
}

View File

@@ -1,135 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.core.hooks.bedrock;
import com.github.games647.craftapi.model.Profile;
import com.github.games647.craftapi.resolver.RateLimitException;
import com.github.games647.fastlogin.core.StoredProfile;
import com.github.games647.fastlogin.core.shared.FastLoginCore;
import com.github.games647.fastlogin.core.shared.LoginSource;
import java.io.IOException;
import java.util.Optional;
import java.util.UUID;
/**
* @param <B> is an instance of either FloodgatePlayer or GeyserSession
*/
public abstract class BedrockService<B> {
protected final FastLoginCore<?, ?, ?> core;
protected final String allowConflict;
public BedrockService(FastLoginCore<?, ?, ?> core) {
this.core = core;
this.allowConflict = core.getConfig().get("allowFloodgateNameConflict").toString().toLowerCase();
}
/**
* Perform every packet level check needed on a Bedrock player.
*
* @param username the name of the player
* @param source an instance of LoginSource
* @return true if Java specific checks can be skipped
*/
public abstract boolean performChecks(String username, LoginSource source);
/**
* Check if the player's name conflicts an existing Java player's name, and kick
* them if it does
*
* @param username the name of the player
* @param source an instance of LoginSource
*/
protected void checkNameConflict(String username, LoginSource source) {
// check for conflicting Premium Java name
Optional<Profile> premiumUUID = Optional.empty();
try {
premiumUUID = core.getResolver().findProfile(username);
} catch (IOException ioEx) {
core.getPlugin().getLog().error(
"Could not check whether Bedrock Player {}'s name conflicts a premium Java player's name.",
username);
kickPlayer(source, username, "Could not check if your name conflicts an existing " +
"premium Java account's name. This is usually a serverside error.");
} catch (RateLimitException rateLimitException) {
core.getPlugin().getLog().warn("Mojang API rate limit hit");
kickPlayer(source, username, "Could not check if your name conflicts an existing premium " +
"Java account's name. Try again in a few minutes");
}
if (premiumUUID.isPresent()) {
core.getPlugin().getLog().info("Bedrock Player {}'s name conflicts an existing premium Java account's name",
username);
kickPlayer(source, username, "Your name conflicts an existing premium Java account's name");
}
}
private void kickPlayer(LoginSource source, String username, String message) {
try {
source.kick(message);
} catch (Exception ex) {
core.getPlugin().getLog().error("Could not kick Player {}", username, ex);
}
}
/**
* The Floodgate / Geyser API does not support querying players by name, so this function
* iterates over every online Bedrock Player and checks if the requested
* username can be found
* <br>
* <i>Falls back to non-prefixed name checks, if ProtocolLib is installed</i>
*
* @param prefixedUsername the name of the player with the prefix appended
* @return Bedrock Player if found, null otherwise
*/
public B getBedrockPlayer(String prefixedUsername) {
return null;
}
public B getBedrockPlayer(UUID uuid) {
return null;
}
public boolean isBedrockPlayer(UUID uuid) {
return getBedrockPlayer(uuid) != null;
}
public boolean isBedrockConnection(String username) {
return getBedrockPlayer(username) != null;
}
/**
* Checks if a profile's name starts with the Floodgate prefix, if it's available
* @param profile profile of the connecting player
* @return true if the username is forbidden
*/
public boolean isUsernameForbidden(StoredProfile profile) {
return false;
}
}

View File

@@ -1,136 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.core.hooks.bedrock;
import com.github.games647.fastlogin.core.StoredProfile;
import com.github.games647.fastlogin.core.shared.FastLoginCore;
import com.github.games647.fastlogin.core.shared.LoginSource;
import java.util.Locale;
import java.util.UUID;
import org.geysermc.floodgate.api.FloodgateApi;
import org.geysermc.floodgate.api.player.FloodgatePlayer;
public class FloodgateService extends BedrockService<FloodgatePlayer> {
private final FloodgateApi floodgate;
public FloodgateService(FloodgateApi floodgate, FastLoginCore<?, ?, ?> core) {
super(core);
this.floodgate = floodgate;
}
/**
* Checks if a config entry (related to Floodgate) is valid. <br>
* Writes to Log if the value is invalid.
* <p>
* This should be used for:
* <ul>
* <li>allowFloodgateNameConflict
* <li>autoLoginFloodgate
* <li>autoRegisterFloodgate
* </ul>
* </p>
*
* @param key the key of the entry in config.yml
* @return <b>true</b> if the entry's value is "true", "false", or "linked"
*/
public boolean isValidFloodgateConfigString(String key) {
String value = core.getConfig().get(key).toString().toLowerCase(Locale.ENGLISH);
if (!value.equals("true") && !value.equals("linked") && !value.equals("false") && !value.equals("no-conflict")) {
core.getPlugin().getLog().error("Invalid value detected for {} in FastLogin/config.yml.", key);
return false;
}
return true;
}
@Override
public boolean isUsernameForbidden(StoredProfile profile) {
String playerPrefix = floodgate.getPlayerPrefix();
return profile.getName().startsWith(playerPrefix) && !playerPrefix.isEmpty();
}
@Override
public boolean performChecks(String username, LoginSource source) {
// check if the Bedrock player is linked to a Java account
FloodgatePlayer floodgatePlayer = getBedrockPlayer(username);
boolean isLinked = floodgatePlayer.getLinkedPlayer() != null;
if ("false".equals(allowConflict)
|| "linked".equals(allowConflict) && !isLinked) {
super.checkNameConflict(username, source);
} else {
core.getPlugin().getLog().info("Skipping name conflict checking for player {}", username);
}
//Floodgate users don't need Java specific checks
return true;
}
/**
* The FloodgateApi does not support querying players by name, so this function
* iterates over every online FloodgatePlayer and checks if the requested
* username can be found
* <br>
* <i>Falls back to non-prefixed name checks, if ProtocolLib is installed</i>
*
* @param prefixedUsername the name of the player with the prefix appended
* @return FloodgatePlayer if found, null otherwise
*/
public FloodgatePlayer getBedrockPlayer(String prefixedUsername) {
//prefixes are broken with ProtocolLib, so fall back to name checks without prefixes
//this should be removed if #493 gets fixed
if (core.getPlugin().isPluginInstalled("ProtocolLib")) {
for (FloodgatePlayer floodgatePlayer : floodgate.getPlayers()) {
if (floodgatePlayer.getUsername().equals(prefixedUsername)) {
return floodgatePlayer;
}
}
return null;
}
for (FloodgatePlayer floodgatePlayer : floodgate.getPlayers()) {
if (floodgatePlayer.getCorrectUsername().equals(prefixedUsername)) {
return floodgatePlayer;
}
}
return null;
}
public FloodgatePlayer getBedrockPlayer(UUID uuid) {
return floodgate.getPlayer(uuid);
}
public boolean isBedrockPlayer(UUID uuid) {
return getBedrockPlayer(uuid) != null;
}
public boolean isBedrockConnection(String username) {
return getBedrockPlayer(username) != null;
}
}

View File

@@ -1,75 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.core.hooks.bedrock;
import com.github.games647.fastlogin.core.shared.FastLoginCore;
import com.github.games647.fastlogin.core.shared.LoginSource;
import java.util.UUID;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.auth.AuthType;
public class GeyserService extends BedrockService<GeyserSession> {
private final GeyserImpl geyser;
private final FastLoginCore<?, ?, ?> core;
private final AuthType authType;
public GeyserService(GeyserImpl geyser, FastLoginCore<?, ?, ?> core) {
super(core);
this.geyser = geyser;
this.core = core;
this.authType = GeyserImpl.getInstance().getConfig().getRemote().getAuthType();
}
@Override
public boolean performChecks(String username, LoginSource source) {
// AuthType.FLOODGATE will be handled by FloodgateService
if (authType == AuthType.ONLINE) {
// authenticate everyone, as if they were Java players, since they have signed
// in through Mojang
return false;
}
if ("true".equals(allowConflict)) {
core.getPlugin().getLog().info("Skipping name conflict checking for player {}", username);
} else {
super.checkNameConflict(username, source);
}
return true;
}
@Override
public GeyserSession getBedrockPlayer(String username) {
return geyser.connectionByName(username);
}
@Override
public GeyserSession getBedrockPlayer(UUID uuid) {
return geyser.connectionByUuid(uuid);
}
}

View File

@@ -1,88 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.core.message;
import com.google.common.io.ByteArrayDataInput;
import com.google.common.io.ByteArrayDataOutput;
public class ChangePremiumMessage implements ChannelMessage {
public static final String CHANGE_CHANNEL = "ch-st";
private String playerName;
private boolean willEnable;
private boolean isSourceInvoker;
public ChangePremiumMessage(String playerName, boolean willEnable, boolean isSourceInvoker) {
this.playerName = playerName;
this.willEnable = willEnable;
this.isSourceInvoker = isSourceInvoker;
}
public ChangePremiumMessage() {
//reading from
}
public String getPlayerName() {
return playerName;
}
public boolean shouldEnable() {
return willEnable;
}
public boolean isSourceInvoker() {
return isSourceInvoker;
}
@Override
public String getChannelName() {
return CHANGE_CHANNEL;
}
@Override
public void readFrom(ByteArrayDataInput input) {
willEnable = input.readBoolean();
playerName = input.readUTF();
isSourceInvoker = input.readBoolean();
}
@Override
public void writeTo(ByteArrayDataOutput output) {
output.writeBoolean(willEnable);
output.writeUTF(playerName);
output.writeBoolean(isSourceInvoker);
}
@Override
public String toString() {
return this.getClass().getSimpleName() + '{' +
"playerName='" + playerName + '\'' +
", shouldEnable=" + willEnable +
", isSourceInvoker=" + isSourceInvoker +
'}';
}
}

View File

@@ -1,38 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.core.message;
import com.google.common.io.ByteArrayDataInput;
import com.google.common.io.ByteArrayDataOutput;
public interface ChannelMessage {
String getChannelName();
void readFrom(ByteArrayDataInput input);
void writeTo(ByteArrayDataOutput output);
}

View File

@@ -1,110 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.core.message;
import com.google.common.io.ByteArrayDataInput;
import com.google.common.io.ByteArrayDataOutput;
import java.util.UUID;
public class LoginActionMessage implements ChannelMessage {
public static final String FORCE_CHANNEL = "force";
private Type type;
private String playerName;
private UUID proxyId;
public LoginActionMessage(Type type, String playerName, UUID proxyId) {
this.type = type;
this.playerName = playerName;
this.proxyId = proxyId;
}
public LoginActionMessage() {
//reading mode
}
public Type getType() {
return type;
}
public String getPlayerName() {
return playerName;
}
public UUID getProxyId() {
return proxyId;
}
@Override
public void readFrom(ByteArrayDataInput input) {
this.type = Type.values()[input.readInt()];
this.playerName = input.readUTF();
//bungeecord UUID
long mostSignificantBits = input.readLong();
long leastSignificantBits = input.readLong();
this.proxyId = new UUID(mostSignificantBits, leastSignificantBits);
}
@Override
public void writeTo(ByteArrayDataOutput output) {
output.writeInt(type.ordinal());
//Data is sent through a random player. We have to tell the Bukkit version of this plugin the target
output.writeUTF(playerName);
//proxy identifier to check if it's a acceptable proxy
output.writeLong(proxyId.getMostSignificantBits());
output.writeLong(proxyId.getLeastSignificantBits());
}
@Override
public String getChannelName() {
return FORCE_CHANNEL;
}
@Override
public String toString() {
return this.getClass().getSimpleName() + '{' +
"type='" + type + '\'' +
", playerName='" + playerName + '\'' +
", proxyId=" + proxyId +
'}';
}
public enum Type {
LOGIN,
REGISTER,
CRACKED
}
}

View File

@@ -1,51 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.core.message;
public class NamespaceKey {
private static final char SEPARATOR_CHAR = ':';
private final String namespace;
private final String key;
private final String combined;
public NamespaceKey(String namespace, String key) {
this.namespace = namespace.toLowerCase();
this.key = key.toLowerCase();
this.combined = this.namespace + SEPARATOR_CHAR + this.key;
}
public String getCombinedName() {
return combined;
}
public static String getCombined(String namespace, String key) {
return new NamespaceKey(namespace, key).combined;
}
}

View File

@@ -1,54 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.core.message;
import com.google.common.io.ByteArrayDataInput;
import com.google.common.io.ByteArrayDataOutput;
public class SuccessMessage implements ChannelMessage {
public static final String SUCCESS_CHANNEL = "succ";
@Override
public String getChannelName() {
return SUCCESS_CHANNEL;
}
@Override
public void readFrom(ByteArrayDataInput input) {
//empty
}
@Override
public void writeTo(ByteArrayDataOutput output) {
//empty
}
@Override
public String toString() {
return this.getClass().getSimpleName() + "{}";
}
}

View File

@@ -1,25 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

View File

@@ -1,25 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

View File

@@ -1,317 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.core.shared;
import com.github.games647.craftapi.resolver.MojangResolver;
import com.github.games647.craftapi.resolver.http.RotatingProxySelector;
import com.github.games647.fastlogin.core.CommonUtil;
import com.github.games647.fastlogin.core.RateLimiter;
import com.github.games647.fastlogin.core.TickingRateLimiter;
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
import com.github.games647.fastlogin.core.hooks.DefaultPasswordGenerator;
import com.github.games647.fastlogin.core.hooks.PasswordGenerator;
import com.github.games647.fastlogin.core.storage.MySQLStorage;
import com.github.games647.fastlogin.core.storage.SQLStorage;
import com.github.games647.fastlogin.core.storage.SQLiteStorage;
import com.google.common.base.Ticker;
import com.google.common.net.HostAndPort;
import com.zaxxer.hikari.HikariConfig;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.Proxy.Type;
import java.net.UnknownHostException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import net.md_5.bungee.config.Configuration;
import net.md_5.bungee.config.ConfigurationProvider;
import net.md_5.bungee.config.YamlConfiguration;
import org.slf4j.Logger;
import static java.util.function.Function.identity;
import static java.util.stream.Collectors.toMap;
import static java.util.stream.Collectors.toSet;
/**
* @param <P> GameProfile class
* @param <C> CommandSender
* @param <T> Plugin class
*/
public class FastLoginCore<P extends C, C, T extends PlatformPlugin<C>> {
private static final long MAX_EXPIRE_RATE = 1_000_000;
private final Map<String, String> localeMessages = new ConcurrentHashMap<>();
private final ConcurrentMap<String, Object> pendingLogin = CommonUtil.buildCache(5, -1);
private final Collection<UUID> pendingConfirms = new HashSet<>();
private final T plugin;
private final MojangResolver resolver = new MojangResolver();
private Configuration config;
private SQLStorage storage;
private RateLimiter rateLimiter;
private PasswordGenerator<P> passwordGenerator = new DefaultPasswordGenerator<>();
private AuthPlugin<P> authPlugin;
public FastLoginCore(T plugin) {
this.plugin = plugin;
}
public void load() {
saveDefaultFile("messages.yml");
saveDefaultFile("config.yml");
try {
config = loadFile("config.yml");
Configuration messages = loadFile("messages.yml");
messages.getKeys()
.stream()
.filter(key -> messages.get(key) != null)
.collect(toMap(identity(), messages::get))
.forEach((key, message) -> {
String colored = CommonUtil.translateColorCodes((String) message);
if (!colored.isEmpty()) {
localeMessages.put(key, colored.replace("/newline", "\n"));
}
});
} catch (IOException ioEx) {
plugin.getLog().error("Failed to load yaml files", ioEx);
return;
}
rateLimiter = createRateLimiter(config.getSection("anti-bot"));
Set<Proxy> proxies = config.getStringList("proxies")
.stream()
.map(HostAndPort::fromString)
.map(proxy -> new InetSocketAddress(proxy.getHost(), proxy.getPort()))
.map(sa -> new Proxy(Type.HTTP, sa))
.collect(toSet());
Collection<InetAddress> addresses = new HashSet<>();
for (String localAddress : config.getStringList("ip-addresses")) {
try {
addresses.add(InetAddress.getByName(localAddress.replace('-', '.')));
} catch (UnknownHostException ex) {
plugin.getLog().error("IP-Address is unknown to us", ex);
}
}
resolver.setMaxNameRequests(config.getInt("mojang-request-limit"));
resolver.setProxySelector(new RotatingProxySelector(proxies));
resolver.setOutgoingAddresses(addresses);
}
private RateLimiter createRateLimiter(Configuration botSection) {
boolean enabled = botSection.getBoolean("enabled", true);
if (!enabled) {
// no-op rate limiter
return () -> true;
}
int maxCon = botSection.getInt("anti-bot.connections", 200);
long expireTime = botSection.getLong("anti-bot.expire", 5) * 60 * 1_000L;
if (expireTime > MAX_EXPIRE_RATE) {
expireTime = MAX_EXPIRE_RATE;
}
return new TickingRateLimiter(Ticker.systemTicker(), maxCon, expireTime);
}
private Configuration loadFile(String fileName) throws IOException {
ConfigurationProvider configProvider = ConfigurationProvider.getProvider(YamlConfiguration.class);
Configuration defaults;
try (InputStream defaultStream = getClass().getClassLoader().getResourceAsStream(fileName)) {
defaults = configProvider.load(defaultStream);
}
Path file = plugin.getPluginFolder().resolve(fileName);
Configuration config;
try (Reader reader = Files.newBufferedReader(file)) {
config = configProvider.load(reader, defaults);
}
// explicitly add keys here, because Configuration.getKeys doesn't return the keys from the default configuration
for (String key : defaults.getKeys()) {
config.set(key, config.get(key));
}
return config;
}
public MojangResolver getResolver() {
return resolver;
}
public SQLStorage getStorage() {
return storage;
}
public T getPlugin() {
return plugin;
}
public void sendLocaleMessage(String key, C receiver) {
String message = localeMessages.get(key);
if (message != null) {
plugin.sendMultiLineMessage(receiver, message);
}
}
public String getMessage(String key) {
return localeMessages.get(key);
}
public boolean setupDatabase() {
String driver = config.getString("driver");
if (!checkDriver(driver)) {
return false;
}
HikariConfig databaseConfig = new HikariConfig();
databaseConfig.setDriverClassName(driver);
String database = config.getString("database");
databaseConfig.setConnectionTimeout(config.getInt("timeout", 30) * 1_000L);
databaseConfig.setMaxLifetime(config.getInt("lifetime", 30) * 1_000L);
if (driver.contains("sqlite")) {
storage = new SQLiteStorage(this, database, databaseConfig);
} else {
String host = config.get("host", "");
int port = config.get("port", 3306);
boolean useSSL = config.get("useSSL", false);
if (useSSL) {
databaseConfig.addDataSourceProperty("allowPublicKeyRetrieval", config.getBoolean("allowPublicKeyRetrieval", false));
databaseConfig.addDataSourceProperty("serverRSAPublicKeyFile", config.getString("ServerRSAPublicKeyFile"));
databaseConfig.addDataSourceProperty("sslMode", config.getString("sslMode", "Required"));
}
databaseConfig.setUsername(config.get("username", ""));
databaseConfig.setPassword(config.getString("password"));
storage = new MySQLStorage(this, driver, host, port, database, databaseConfig, useSSL);
}
try {
storage.createTables();
return true;
} catch (Exception ex) {
plugin.getLog().warn("Failed to setup database. Disabling plugin...", ex);
return false;
}
}
private boolean checkDriver(String className) {
try {
Class.forName(className);
return true;
} catch (ClassNotFoundException notFoundEx) {
Logger log = plugin.getLog();
log.warn("This driver {} is not supported on this platform", className);
log.warn("Please choose either MySQL (Spigot, BungeeCord), SQLite (Spigot, Sponge) or " +
"MariaDB (Sponge, Velocity)", notFoundEx);
}
return false;
}
public Configuration getConfig() {
return config;
}
public PasswordGenerator<P> getPasswordGenerator() {
return passwordGenerator;
}
public void setPasswordGenerator(PasswordGenerator<P> passwordGenerator) {
this.passwordGenerator = passwordGenerator;
}
public ConcurrentMap<String, Object> getPendingLogin() {
return pendingLogin;
}
public Collection<UUID> getPendingConfirms() {
return pendingConfirms;
}
public AuthPlugin<P> getAuthPluginHook() {
return authPlugin;
}
public RateLimiter getRateLimiter() {
return rateLimiter;
}
public void setAuthPluginHook(AuthPlugin<P> authPlugin) {
this.authPlugin = authPlugin;
}
public void saveDefaultFile(String fileName) {
Path dataFolder = plugin.getPluginFolder();
try {
Files.createDirectories(dataFolder);
Path configFile = dataFolder.resolve(fileName);
if (Files.notExists(configFile)) {
try (InputStream defaultStream = getClass().getClassLoader().getResourceAsStream(fileName)) {
Files.copy(Objects.requireNonNull(defaultStream), configFile);
}
}
} catch (IOException ioExc) {
plugin.getLog().error("Cannot create plugin folder {}", dataFolder, ioExc);
}
}
public void close() {
plugin.getLog().info("Safely shutting down scheduler. This could take up to one minute.");
plugin.getScheduler().shutdown();
if (storage != null) {
storage.close();
}
}
}

View File

@@ -1,174 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.core.shared;
import com.github.games647.craftapi.model.Profile;
import com.github.games647.craftapi.resolver.RateLimitException;
import com.github.games647.fastlogin.core.StoredProfile;
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.Optional;
import java.util.UUID;
import org.geysermc.floodgate.api.player.FloodgatePlayer;
public abstract class FloodgateManagement<P extends C, C, L extends LoginSession, T extends PlatformPlugin<C>>
implements Runnable {
protected final FastLoginCore<P, C, T> core;
protected final P player;
private final FloodgatePlayer floodgatePlayer;
private final String username;
//config.yml values that might be accessed by multiple methods
protected final String autoLoginFloodgate;
protected final String autoRegisterFloodgate;
protected final String allowNameConflict;
//variables initialized through run() and accesses by subclasss
protected boolean isRegistered;
protected StoredProfile profile;
protected boolean isLinked;
public FloodgateManagement(FastLoginCore<P, C, T> core, P player, FloodgatePlayer floodgatePlayer) {
this.core = core;
this.player = player;
this.floodgatePlayer = floodgatePlayer;
this.username = getName(player);
//load values from config.yml
autoLoginFloodgate = core.getConfig().get("autoLoginFloodgate").toString().toLowerCase();
autoRegisterFloodgate = core.getConfig().get("autoRegisterFloodgate").toString().toLowerCase();
allowNameConflict = core.getConfig().get("allowFloodgateNameConflict").toString().toLowerCase();
}
@Override
public void run() {
core.getPlugin().getLog().info("Player {} is connecting through Geyser Floodgate.", username);
// check if the Bedrock player is linked to a Java account
isLinked = floodgatePlayer.getLinkedPlayer() != null;
//this happens on Bukkit if it's connected to Bungee
//if that's the case, players will be logged in via plugin messages
if (core.getStorage() == null) {
return;
}
profile = core.getStorage().loadProfile(username);
AuthPlugin<P> authPlugin = core.getAuthPluginHook();
try {
//maybe Bungee without auth plugin
if (authPlugin == null) {
if (profile != null) {
isRegistered = profile.isPremium();
} else {
isRegistered = false;
}
} else {
isRegistered = authPlugin.isRegistered(username);
}
} catch (Exception ex) {
core.getPlugin().getLog().error(
"An error has occured while checking if player {} is registered",
username, ex);
return;
}
//decide if checks should be made for conflicting Java player names
if (isNameCheckRequired()) {
// check for conflicting Premium Java name
Optional<Profile> premiumUUID = Optional.empty();
try {
premiumUUID = core.getResolver().findProfile(username);
} catch (IOException | RateLimitException e) {
core.getPlugin().getLog().error(
"Could not check whether Floodgate Player {}'s name conflicts a premium Java account's name.",
username, e);
return;
}
//stop execution if player's name is conflicting
if (premiumUUID.isPresent()) {
return;
}
}
if (!isRegistered && !isAutoRegisterAllowed()) {
return;
}
//logging in from bedrock for a second time threw an error with UUID
if (profile == null) {
profile = new StoredProfile(getUUID(player), username, true, getAddress(player).toString());
}
//start Bukkit/Bungee specific tasks
startLogin();
}
/**
* Decude if the player can be auto registered.
* The config option 'non-conflicting' is ignored by this function.
* @return true if the Player can be registered automatically
*/
private boolean isAutoRegisterAllowed() {
return "true".equals(autoRegisterFloodgate)
|| "no-conflict".equals(autoRegisterFloodgate) // this was checked before
|| ("linked".equals(autoRegisterFloodgate) && isLinked);
}
/**
* Decides wether checks for conflicting Java names should be made
* @return ture if an API call to Mojang is needed
*/
private boolean isNameCheckRequired() {
//linked players have the same name as their Java profile
//OR
//if allowNameConflict is 'false' or 'linked' and the player had a conflicting
//name, than they would have been kicked in FloodgateHook#checkNameConflict
if (isLinked || !"true".equals(allowNameConflict)) {
return false;
}
//autoRegisterFloodgate should only be checked if then player is not yet registered
if (!isRegistered && "no-conflict".equals(autoRegisterFloodgate)) {
return true;
}
return "no-conflict".equals(autoLoginFloodgate);
}
protected abstract void startLogin();
protected abstract String getName(P player);
protected abstract UUID getUUID(P player);
protected abstract InetSocketAddress getAddress(P player);
}

View File

@@ -1,137 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.core.shared;
import com.github.games647.fastlogin.core.storage.SQLStorage;
import com.github.games647.fastlogin.core.StoredProfile;
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
import com.github.games647.fastlogin.core.shared.event.FastLoginAutoLoginEvent;
public abstract class ForceLoginManagement<P extends C, C, L extends LoginSession, T extends PlatformPlugin<C>>
implements Runnable {
protected final FastLoginCore<P, C, T> core;
protected final P player;
protected final L session;
public ForceLoginManagement(FastLoginCore<P, C, T> core, P player, L session) {
this.core = core;
this.player = player;
this.session = session;
}
@Override
public void run() {
if (!isOnline(player)) {
core.getPlugin().getLog().info("Player {} disconnected", player);
return;
}
if (session == null) {
core.getPlugin().getLog().info("No valid session found for {}", player);
return;
}
SQLStorage storage = core.getStorage();
StoredProfile playerProfile = session.getProfile();
try {
if (isOnlineMode()) {
//premium player
AuthPlugin<P> authPlugin = core.getAuthPluginHook();
if (authPlugin == null) {
//maybe only bungeecord plugin
onForceActionSuccess(session);
} else {
boolean success = true;
String playerName = getName(player);
if (core.getConfig().get("autoLogin", true)) {
if (session.needsRegistration()
|| (core.getConfig().get("auto-register-unknown", false)
&& !authPlugin.isRegistered(playerName))) {
success = forceRegister(player);
} else if (!callFastLoginAutoLoginEvent(session, playerProfile).isCancelled()) {
success = forceLogin(player);
}
}
if (success) {
//update only on success to prevent corrupt data
if (playerProfile != null) {
playerProfile.setId(session.getUuid());
playerProfile.setPremium(true);
storage.save(playerProfile);
}
onForceActionSuccess(session);
}
}
} else if (playerProfile != null) {
//cracked player
playerProfile.setId(null);
playerProfile.setPremium(false);
storage.save(playerProfile);
}
} catch (Exception ex) {
core.getPlugin().getLog().warn("ERROR ON FORCE LOGIN of {}", getName(player), ex);
}
}
public boolean forceRegister(P player) {
core.getPlugin().getLog().info("Register player {}", getName(player));
String generatedPassword = core.getPasswordGenerator().getRandomPassword(player);
boolean success = core.getAuthPluginHook().forceRegister(player, generatedPassword);
String message = core.getMessage("auto-register");
if (success && message != null) {
message = message.replace("%password", generatedPassword);
core.getPlugin().sendMessage(player, message);
}
return success;
}
public boolean forceLogin(P player) {
core.getPlugin().getLog().info("Logging player {} in", getName(player));
boolean success = core.getAuthPluginHook().forceLogin(player);
if (success) {
core.sendLocaleMessage("auto-login", player);
}
return success;
}
public abstract FastLoginAutoLoginEvent callFastLoginAutoLoginEvent(LoginSession session, StoredProfile profile);
public abstract void onForceActionSuccess(LoginSession session);
public abstract String getName(P player);
public abstract boolean isOnline(P player);
public abstract boolean isOnlineMode();
}

View File

@@ -1,162 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.core.shared;
import com.github.games647.craftapi.model.Profile;
import com.github.games647.craftapi.resolver.RateLimitException;
import com.github.games647.fastlogin.core.StoredProfile;
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
import com.github.games647.fastlogin.core.hooks.bedrock.BedrockService;
import com.github.games647.fastlogin.core.shared.event.FastLoginPreLoginEvent;
import java.util.Optional;
import net.md_5.bungee.config.Configuration;
public abstract class JoinManagement<P extends C, C, S extends LoginSource> {
protected final FastLoginCore<P, C, ?> core;
protected final AuthPlugin<P> authHook;
private final BedrockService<?> bedrockService;
public JoinManagement(FastLoginCore<P, C, ?> core, AuthPlugin<P> authHook, BedrockService<?> bedrockService) {
this.core = core;
this.authHook = authHook;
this.bedrockService = bedrockService;
}
public void onLogin(String username, S source) {
core.getPlugin().getLog().info("Handling player {}", username);
StoredProfile profile = core.getStorage().loadProfile(username);
if (profile == null) {
return;
}
//check if the player is connecting through Bedrock Edition
if (bedrockService != null && bedrockService.isBedrockConnection(username)) {
//perform Bedrock specific checks and skip Java checks, if they are not needed
if (bedrockService.performChecks(username, source)) {
return;
}
}
callFastLoginPreLoginEvent(username, source, profile);
Configuration config = core.getConfig();
String ip = source.getAddress().getAddress().getHostAddress();
profile.setLastIp(ip);
try {
if (profile.isSaved()) {
if (profile.isPremium()) {
core.getPlugin().getLog().info("Requesting premium login for registered player: {}", username);
requestPremiumLogin(source, profile, username, true);
} else {
if (isValidUsername(source, profile)) {
startCrackedSession(source, profile, username);
}
}
} else {
if (core.getPendingLogin().remove(ip + username) != null && config.get("secondAttemptCracked", false)) {
core.getPlugin().getLog().info("Second attempt login -> cracked {}", username);
//first login request failed so make a cracked session
startCrackedSession(source, profile, username);
return;
}
Optional<Profile> premiumUUID = Optional.empty();
if (config.get("nameChangeCheck", false) || config.get("autoRegister", false)) {
premiumUUID = core.getResolver().findProfile(username);
}
if (!premiumUUID.isPresent()
|| (!checkNameChange(source, username, premiumUUID.get())
&& !checkPremiumName(source, username, profile))) {
//nothing detected the player as premium -> start a cracked session
if (core.getConfig().get("switchMode", false)) {
source.kick(core.getMessage("switch-kick-message"));
return;
}
startCrackedSession(source, profile, username);
}
}
} catch (RateLimitException rateLimitEx) {
core.getPlugin().getLog().error("Mojang's rate limit reached for {}. The public IPv4 address of this" +
" server issued more than 600 Name -> UUID requests within 10 minutes. After those 10" +
" minutes we can make requests again.", username);
} catch (Exception ex) {
core.getPlugin().getLog().error("Failed to check premium state for {}", username, ex);
core.getPlugin().getLog().error("Failed to check premium state of {}", username, ex);
}
}
protected boolean isValidUsername(LoginSource source, StoredProfile profile) throws Exception {
if (bedrockService != null && bedrockService.isUsernameForbidden(profile)) {
core.getPlugin().getLog().info("Floodgate Prefix detected on cracked player");
source.kick("Your username contains illegal characters");
return false;
}
return true;
}
private boolean checkPremiumName(S source, String username, StoredProfile profile) throws Exception {
core.getPlugin().getLog().info("GameProfile {} uses a premium username", username);
if (core.getConfig().get("autoRegister", false) && (authHook == null || !authHook.isRegistered(username))) {
requestPremiumLogin(source, profile, username, false);
return true;
}
return false;
}
private boolean checkNameChange(S source, String username, Profile profile) {
//user not exists in the db
if (core.getConfig().get("nameChangeCheck", false)) {
StoredProfile storedProfile = core.getStorage().loadProfile(profile.getId());
if (storedProfile != null) {
//uuid exists in the database
core.getPlugin().getLog().info("GameProfile {} changed it's username", profile);
//update the username to the new one in the database
storedProfile.setPlayerName(username);
requestPremiumLogin(source, storedProfile, username, false);
return true;
}
}
return false;
}
public abstract FastLoginPreLoginEvent callFastLoginPreLoginEvent(String username, S source, StoredProfile profile);
public abstract void requestPremiumLogin(S source, StoredProfile profile, String username, boolean registered);
public abstract void startCrackedSession(S source, StoredProfile profile, String username);
}

View File

@@ -1,103 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.core.shared;
import com.github.games647.fastlogin.core.StoredProfile;
import com.google.common.base.MoreObjects;
import com.google.common.base.Objects;
import java.util.UUID;
public abstract class LoginSession {
private final StoredProfile profile;
private String requestUsername;
private String username;
private UUID uuid;
protected boolean registered;
public LoginSession(String requestUsername, boolean registered, StoredProfile profile) {
this.requestUsername = requestUsername;
this.username = requestUsername;
this.registered = registered;
this.profile = profile;
}
public String getRequestUsername() {
return requestUsername;
}
public String getUsername() {
return username;
}
public synchronized void setVerifiedUsername(String username) {
this.username = username;
}
/**
* @return This value is always false if we authenticate the player with a cracked authentication
*/
public synchronized boolean needsRegistration() {
return !registered;
}
public StoredProfile getProfile() {
return profile;
}
/**
* Get the premium UUID of this player
*
* @return the premium UUID or null if not fetched
*/
public synchronized UUID getUuid() {
return uuid;
}
/**
* Set the online UUID if it's fetched
*
* @param uuid premium UUID
*/
public synchronized void setUuid(UUID uuid) {
this.uuid = uuid;
}
@Override
public synchronized String toString() {
return MoreObjects.toStringHelper(this)
.add("profile", profile)
.add("requestUsername", requestUsername)
.add("username", username)
.add("uuid", uuid)
.add("registered", registered)
.toString();
}
}

View File

@@ -1,37 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.core.shared;
import java.net.InetSocketAddress;
public interface LoginSource {
void enableOnlinemode() throws Exception;
void kick(String message) throws Exception;
InetSocketAddress getAddress();
}

View File

@@ -1,67 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.core.shared;
import com.github.games647.fastlogin.core.AsyncScheduler;
import com.github.games647.fastlogin.core.hooks.bedrock.BedrockService;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.nio.file.Path;
import java.util.concurrent.ThreadFactory;
import org.slf4j.Logger;
public interface PlatformPlugin<C> {
String getName();
Path getPluginFolder();
Logger getLog();
void sendMessage(C receiver, String message);
AsyncScheduler getScheduler();
boolean isPluginInstalled(String name);
default void sendMultiLineMessage(C receiver, String message) {
for (String line : message.split("%nl%")) {
sendMessage(receiver, line);
}
}
BedrockService<?> getBedrockService();
default ThreadFactory getThreadFactory() {
return new ThreadFactoryBuilder()
.setNameFormat(getName() + " Pool Thread #%1$d")
// Hikari create daemons by default and we could daemon threads for our own scheduler too
// because we safely shutdown
.setDaemon(true)
.build();
}
}

View File

@@ -1,34 +0,0 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2021 <Your name and contributors>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.core.shared.event;
import com.github.games647.fastlogin.core.StoredProfile;
import com.github.games647.fastlogin.core.shared.LoginSession;
public interface FastLoginAutoLoginEvent extends FastLoginCancellableEvent {
LoginSession getSession();
StoredProfile getProfile();
}

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