mirror of
https://github.com/TuxCoding/FastLogin.git
synced 2025-12-24 15:48:07 +01:00
Compare commits
50 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
484855724b | ||
|
|
4ea7968366 | ||
|
|
44a47bc97f | ||
|
|
82cb25f809 | ||
|
|
551441cdc4 | ||
|
|
22a56862b0 | ||
|
|
edf5933e07 | ||
|
|
c6da04de70 | ||
|
|
0459b0a5a1 | ||
|
|
033333e35c | ||
|
|
6595dc6ac0 | ||
|
|
ea44002e91 | ||
|
|
131de8404c | ||
|
|
fbdd8ffc35 | ||
|
|
7db8c78975 | ||
|
|
b102f06f8e | ||
|
|
a79e18445a | ||
|
|
cf1a0c1bef | ||
|
|
059c3f346e | ||
|
|
47db2c7858 | ||
|
|
5bb8640d78 | ||
|
|
881b2ec7bc | ||
|
|
194c67cd6f | ||
|
|
863607c9a4 | ||
|
|
f37cc0a0db | ||
|
|
70a81bfcdf | ||
|
|
b8d029d6da | ||
|
|
c47dd1df80 | ||
|
|
4d5b1787b1 | ||
|
|
8c764220bd | ||
|
|
9af076b4c4 | ||
|
|
22aa9287e9 | ||
|
|
f08daa9b72 | ||
|
|
bc53743c6b | ||
|
|
a430a079c9 | ||
|
|
f3ac6090f1 | ||
|
|
5ca9b9c59a | ||
|
|
b886d1501f | ||
|
|
0082cc6536 | ||
|
|
7f96d55084 | ||
|
|
3851d539f8 | ||
|
|
a25d97879f | ||
|
|
41abffdb08 | ||
|
|
e69eb70377 | ||
|
|
e924b7a2fa | ||
|
|
157ca04691 | ||
|
|
ae3e03405d | ||
|
|
bebb04bdea | ||
|
|
91f41c55de | ||
|
|
1acc825f81 |
@@ -1,6 +1,6 @@
|
||||
# Use https://travis-ci.org/ for automatic tests
|
||||
|
||||
# speed up testing http://blog.travis-ci.com/2014-12-17-faster-builds-with-container-based-infrastructure/
|
||||
# speed up testing https://blog.travis-ci.com/2014-12-17-faster-builds-with-container-based-infrastructure/
|
||||
sudo: false
|
||||
|
||||
# This is a java project
|
||||
|
||||
82
CHANGELOG.md
82
CHANGELOG.md
@@ -1,4 +1,22 @@
|
||||
######1.9
|
||||
### 1.10
|
||||
|
||||
* 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 set a value to the API column
|
||||
* 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)
|
||||
@@ -17,7 +35,7 @@
|
||||
* 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
|
||||
### 1.8
|
||||
|
||||
* Added autoIn importer
|
||||
* Added BFA importer
|
||||
@@ -26,12 +44,12 @@
|
||||
* Fix ProtocolSupport BungeeCord
|
||||
* Fix duplicate logins for BungeeAuth users
|
||||
|
||||
######1.7.1
|
||||
### 1.7.1
|
||||
|
||||
* Fix BungeeCord autoRegister (Fixes #46)
|
||||
* Fix protocollsupport autoregister
|
||||
|
||||
######1.7
|
||||
### 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
|
||||
@@ -44,15 +62,15 @@
|
||||
* Fix setting skin on Cauldron
|
||||
* Fix saving on name change
|
||||
|
||||
######1.6.2
|
||||
### 1.6.2
|
||||
|
||||
* Fixed support for new LoginSecurity version
|
||||
|
||||
######1.6.1
|
||||
### 1.6.1
|
||||
|
||||
* Fix message typo in BungeeCord which created a NPE if premium-warning is activated
|
||||
|
||||
######1.6
|
||||
### 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
|
||||
@@ -63,34 +81,34 @@
|
||||
* Fixed cracked command not working on BungeeCord
|
||||
* Fix error if forward skins is disabled
|
||||
|
||||
######1.5.2
|
||||
### 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
|
||||
### 1.5.1
|
||||
|
||||
* Fixed BungeeCord support by correctly saving the proxy ids
|
||||
|
||||
######1.5
|
||||
### 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
|
||||
### 1.4
|
||||
|
||||
* Added Bungee setAuthPlugin method
|
||||
* Added nameChangeCheck
|
||||
* Multiple BungeeCord support
|
||||
|
||||
######1.3.1
|
||||
### 1.3.1
|
||||
|
||||
* Prevent thread create violation in BungeeCord
|
||||
|
||||
######1.3
|
||||
### 1.3
|
||||
|
||||
* Added support for AuthMe 3.X
|
||||
* Fixed premium logins if the server is not fully started
|
||||
@@ -99,11 +117,11 @@
|
||||
* Fixed 1.7 Minecraft support by removing guava 11+ only features -> Cauldron support
|
||||
* Fixed BungeeCord support in Cauldron
|
||||
|
||||
######1.2.1
|
||||
### 1.2.1
|
||||
|
||||
* Fix premium status change notification message on BungeeCord
|
||||
|
||||
######1.2
|
||||
### 1.2
|
||||
|
||||
* Fix race condition in BungeeCord
|
||||
* Fix dead lock in xAuth
|
||||
@@ -111,20 +129,20 @@
|
||||
* Added API methods for plugins to set their own auth plugin hook
|
||||
=> Added support for AdvancedLogin
|
||||
|
||||
######1.1
|
||||
### 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
|
||||
### 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
|
||||
### 0.8
|
||||
|
||||
* Fixed BungeeCord support for the Bukkit module
|
||||
* Added database storage to save the premium state
|
||||
@@ -132,7 +150,7 @@
|
||||
* Fixed issues with host lookup from hosts file (Thanks to @NorbiPeti)
|
||||
* Remove handshake listener because it creates errors on some systems
|
||||
|
||||
######0.7
|
||||
### 0.7
|
||||
|
||||
* Added BungeeAuth support
|
||||
* Added /premium [player] command with optional player parameter
|
||||
@@ -145,14 +163,14 @@
|
||||
* Fixed BungeeCord support
|
||||
* Changed config option autologin to autoregister to clarify the usage
|
||||
|
||||
######0.6
|
||||
### 0.6
|
||||
|
||||
* Fixed 1.9 bugs
|
||||
* Added UltraAuth support
|
||||
|
||||
######0.5
|
||||
### 0.5
|
||||
|
||||
* Added unpremium command
|
||||
* Added cracked command
|
||||
* Added autologin - See config
|
||||
* Added config
|
||||
* Added isRegistered API method
|
||||
@@ -162,34 +180,34 @@
|
||||
* Fixed premium name check to protocolsupport
|
||||
* Improved permissions management
|
||||
|
||||
######0.4
|
||||
### 0.4
|
||||
|
||||
* Added forward premium skin
|
||||
* Added plugin support for protocolsupport
|
||||
|
||||
######0.3.2
|
||||
### 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
|
||||
### 0.3.1
|
||||
|
||||
* Improved BungeeCord security
|
||||
|
||||
#####0.3
|
||||
### 0.3
|
||||
|
||||
* Added BungeeCord support
|
||||
* Decrease timeout checks in order to fail faster on connection problems
|
||||
* Code style improvements
|
||||
|
||||
######0.2.4
|
||||
### 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
|
||||
### 0.2.3
|
||||
|
||||
* Remove useless AuthMe forcelogin code
|
||||
* Send a kick message to the client instead of just "Disconnect"
|
||||
@@ -197,15 +215,15 @@
|
||||
* Fix thread safety for fake start packets (Bukkit.getOfflinePlayer doesn't look like to be thread-safe)
|
||||
* Added more documentation
|
||||
|
||||
######0.2.2
|
||||
### 0.2.2
|
||||
|
||||
* Compile project with Java 7 :(
|
||||
|
||||
######0.2.1
|
||||
### 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
|
||||
### 0.2
|
||||
|
||||
* Added support for CrazyLogin and LoginSecurity
|
||||
* Now minecraft version independent
|
||||
@@ -214,5 +232,5 @@
|
||||
* More state validation
|
||||
* Added better error handling
|
||||
|
||||
#####0.1
|
||||
### 0.1
|
||||
* First release
|
||||
|
||||
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015
|
||||
Copyright (c) 2015-17
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
80
README.md
80
README.md
@@ -1,12 +1,9 @@
|
||||
# FastLogin
|
||||
|
||||
[](https://travis-ci.org/games647/FastLogin)
|
||||
[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=8ZBULMAPN7MZC)
|
||||
|
||||
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).
|
||||
|
||||
###Features:
|
||||
### Features:
|
||||
|
||||
* Detect paid accounts from others
|
||||
* Automatically login paid accounts (premium)
|
||||
@@ -28,55 +25,55 @@ So they don't need to enter passwords. This is also called auto login (auto-logi
|
||||
|
||||
***
|
||||
|
||||
###Commands:
|
||||
### Commands:
|
||||
* /premium [player] Label the invoker or the argument as paid account
|
||||
* /cracked [player] Label the invoker or the argument as cracked account
|
||||
* /importdb <autoIn/bpa/eldzi> <mysql/sqlite> [host:port] [database] [username] [password] - Imports the database from another plugin
|
||||
|
||||
###Permissions:
|
||||
### Permissions:
|
||||
* fastlogin.bukkit.command.premium
|
||||
* fastlogin.bukkit.command.cracked
|
||||
* fastlogin.command.premium.other
|
||||
* fastlogin.command.cracked.other
|
||||
* fastlogin.command.import
|
||||
|
||||
###Requirements:
|
||||
* Plugin: [ProtocolLib](http://www.spigotmc.org/resources/protocollib.1997/) or [ProtocolSupport](http://www.spigotmc.org/resources/protocolsupport.7201/)
|
||||
### Requirements:
|
||||
* Plugin: [ProtocolLib](https://www.spigotmc.org/resources/protocollib.1997/) or [ProtocolSupport](https://www.spigotmc.org/resources/protocolsupport.7201/)
|
||||
* Tested Bukkit/[Spigot](https://www.spigotmc.org) 1.9 (could also work with other versions)
|
||||
* Java 7+
|
||||
* Run Spigot and/or BungeeCord/Waterfall in offline mode (see server.properties or config.yml)
|
||||
* An auth plugin. Supported plugins
|
||||
|
||||
####Bukkit/Spigot/PaperSpigot
|
||||
#### Bukkit/Spigot/Paper
|
||||
|
||||
* [AuthMe (both 5.X and 3.X)](http://dev.bukkit.org/bukkit-plugins/authme-reloaded/)
|
||||
* [xAuth](http://dev.bukkit.org/bukkit-plugins/xauth/)
|
||||
* [AuthMe (both 5.X and 3.X)](https://dev.bukkit.org/bukkit-plugins/authme-reloaded/)
|
||||
* [xAuth](https://dev.bukkit.org/bukkit-plugins/xauth/)
|
||||
* [LogIt](https://github.com/XziomekX/LogIt)
|
||||
* [AdvancedLogin (Paid)](https://www.spigotmc.org/resources/advancedlogin.10510/)
|
||||
* [CrazyLogin](http://dev.bukkit.org/bukkit-plugins/crazylogin/)
|
||||
* [LoginSecurity](http://dev.bukkit.org/bukkit-plugins/loginsecurity/)
|
||||
* [RoyalAuth](http://dev.bukkit.org/bukkit-plugins/royalauth/)
|
||||
* [UltraAuth](http://dev.bukkit.org/bukkit-plugins/ultraauth-aa/)
|
||||
* [CrazyLogin](https://dev.bukkit.org/bukkit-plugins/crazylogin/)
|
||||
* [LoginSecurity](https://dev.bukkit.org/bukkit-plugins/loginsecurity/)
|
||||
* [RoyalAuth](https://dev.bukkit.org/bukkit-plugins/royalauth/)
|
||||
* [UltraAuth](https://dev.bukkit.org/bukkit-plugins/ultraauth-aa/)
|
||||
|
||||
####BungeeCord/Waterfall
|
||||
#### BungeeCord/Waterfall
|
||||
|
||||
* [BungeeAuth](https://www.spigotmc.org/resources/bungeeauth.493/)
|
||||
|
||||
###Downloads
|
||||
### Downloads
|
||||
|
||||
https://www.spigotmc.org/resources/fastlogin.14153/history
|
||||
|
||||
***
|
||||
|
||||
###How to install
|
||||
### How to install
|
||||
|
||||
####Bukkit/Spigot/PaperSpigot
|
||||
#### Bukkit/Spigot/Paper
|
||||
|
||||
1. Download and install ProtocolLib
|
||||
2. Download and install FastLogin
|
||||
3. Set your server in offline mode by setting the value onlinemode in your server.properties to false
|
||||
|
||||
####BungeeCord/Waterfall
|
||||
#### BungeeCord/Waterfall
|
||||
|
||||
1. Activate BungeeCord in the Spigot configuration
|
||||
2. Restart your server
|
||||
@@ -86,13 +83,14 @@ Put your stats id from the BungeeCord config into this file
|
||||
5. Download and Install FastLogin on BungeeCord AND Spigot
|
||||
6. Check your database settings in the config of FastLogin on BungeeCord
|
||||
7. Set your proxy (BungeeCord) in offline mode by setting the value onlinemode in your config.yml to false
|
||||
8. (BungeeCord doesn't support SQLite per default, so you should change the configuration to MySQL or MariaDB)
|
||||
8. You should *always* firewall your spigot server that it's only accessible through BungeeCord https://www.spigotmc.org/wiki/bungeecord-installation/#post-installation
|
||||
9. (BungeeCord doesn't support SQLite per default, so you should change the configuration to MySQL or MariaDB)
|
||||
|
||||
***
|
||||
|
||||
###FAQ
|
||||
### FAQ
|
||||
|
||||
####Index
|
||||
#### Index
|
||||
1. [How does Minecraft logins work?](#how-does-minecraft-logins-work)
|
||||
2. [How does this plugin work?](#how-does-this-plugin-work)
|
||||
3. [Why does the plugin require offline mode?](#why-does-the-plugin-require-offline-mode)
|
||||
@@ -103,26 +101,26 @@ Put your stats id from the BungeeCord config into this file
|
||||
8. [Could premium players have a premium UUID and Skin?](#could-premium-players-have-a-premium-uuid-and-skin)
|
||||
9. [Is this plugin compatible with Cauldron?](#is-this-plugin-compatible-with-cauldron)
|
||||
|
||||
####How does minecraft logins work?
|
||||
######Online Mode
|
||||
#### How does minecraft logins work?
|
||||
###### Online Mode
|
||||
1. Client -> Server: I want to login, here is my username
|
||||
2. Server -> Client: Okay. I'm in online mode so here is my public key for encryption and my serverid
|
||||
3. Client -> Mojang: I'm player "xyz". I want to join a server with that serverid
|
||||
2. Server -> Client: Okay. I'm in online mode so here is my public key for encryption and my server id
|
||||
3. Client -> Mojang: I'm player "xyz". I want to join a server with that server id
|
||||
4. Mojang -> Client: Session data checked. You can continue
|
||||
5. Client -> Server: I received a successful response from Mojang. Heres our shared secret key
|
||||
5. Client -> Server: I received a successful response from Mojang. Here our shared secret key
|
||||
6. Server -> Mojang: Does the player "xyz" with this shared secret key has a valid account to join me?
|
||||
7. Mojang -> Server: Yes, the player has the following additionally properties (UUID, Skin)
|
||||
8. Client and Server: encrypt all following communication packet
|
||||
9. Server -> Client: Everything checked you can play now
|
||||
|
||||
|
||||
######Offline Mode
|
||||
###### Offline Mode
|
||||
In offline mode step 2-7 is skipped. So a login request is directly followed by 8.
|
||||
|
||||
######More details
|
||||
###### More details
|
||||
http://wiki.vg/Protocol#Login
|
||||
|
||||
####How does this plugin work?
|
||||
#### How does this plugin work?
|
||||
By using ProtocolLib, this plugin works as a proxy between the client and server. This plugin will fake that the server
|
||||
runs in online mode. It does everything an online mode server would do. This will be for example, generating keys or
|
||||
checking for valid sessions. Because everything is the same compared to an offline mode login after an encrypted
|
||||
@@ -137,7 +135,7 @@ connection, we will intercept only **login** packets of **premium** players.
|
||||
6. Encrypt the connection
|
||||
7. On success intercept all related login packets and fake a new login packet as a normal offline login
|
||||
|
||||
####Why does the plugin require offline mode?
|
||||
#### Why does the plugin require offline mode?
|
||||
1. As you can see in the question "how does minecraft login works", offline mode is equivalent to online mode except of
|
||||
the encryption and session checks on login. So we can intercept and cancel the first packets for premium players and
|
||||
enable an encrypted connection. Then we send a new fake packet in order to pretend that this a new login request from
|
||||
@@ -147,38 +145,38 @@ incorrectly. For example, a plugin tries to fetch the UUID from Mojang, but the
|
||||
a paid account.
|
||||
3. Servers, who allow cracked players and just speed up logins for premium players, are **already** in offline mode.
|
||||
|
||||
####Can cracked player join with premium usernames?
|
||||
#### Can cracked player join with premium usernames?
|
||||
Yes, indeed. Therefore the command for toggling the fast login method exists.
|
||||
|
||||
####Why do players have to invoke a command?
|
||||
#### Why do players have to invoke a command?
|
||||
1. It's a secure way to make sure a person with a paid account cannot steal the account
|
||||
of a cracked player that has the same username. The player have to proof first that it's his own account.
|
||||
2. We only receive the username from the player on login. We could check if that username is associated
|
||||
to a paid account but if we request a online mode login from a cracked player (who uses a username from
|
||||
a paid account), the player will disconnect with the reason "bad login" or "Invalid session". There is no way to change
|
||||
that message on the server side (without client modifications), because it's a connection between the Client and the
|
||||
Sessionserver.
|
||||
session-server.
|
||||
3. If a premium player would skip registration too, a player of a cracked account could later still register the
|
||||
account and would claim and steal the account from the premium player. Because commands cannot be invoked unless the
|
||||
player has a account or is logged in, protects this method also premium players
|
||||
|
||||
###What happens if a paid account joins with a used username?
|
||||
### What happens if a paid account joins with a used username?
|
||||
The player on the server have to activate the feature of this plugin by command. If a person buys the username
|
||||
of his own account, it's still secured. A normal offline mode login makes sure he's the owner of the server account
|
||||
and Mojang account. Then the command can be executed. So someone different cannot steal the account of cracked player
|
||||
by buying the username.
|
||||
|
||||
####Does the plugin have BungeeCord support?
|
||||
#### Does the plugin have BungeeCord support?
|
||||
Yes it has. See the how to install above.
|
||||
|
||||
####Could premium players have a premium UUID and Skin?
|
||||
#### Could premium players have a premium UUID and Skin?
|
||||
Since 0.7 both features are implemented. You can check the config.yml in order to activate it.
|
||||
|
||||
####Is this plugin compatible with Cauldron?
|
||||
#### Is this plugin compatible with Cauldron?
|
||||
It's not tested yet, but all needed methods also exists in Cauldron so it could work together.
|
||||
|
||||
***
|
||||
|
||||
###Useful Links:
|
||||
### Useful Links:
|
||||
* [Login Protocol](http://wiki.vg/Protocol#Login)
|
||||
* [Protocol Encryption](http://wiki.vg/Protocol_Encryption)
|
||||
* [Protocol Encryption](http://wiki.vg/Protocol_Encryption)
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>com.github.games647</groupId>
|
||||
<artifactId>fastlogin</artifactId>
|
||||
<version>1.9</version>
|
||||
<version>1.10</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -34,10 +34,10 @@
|
||||
<url>http://repo.dmulloy2.net/content/groups/public/</url>
|
||||
</repository>
|
||||
|
||||
<!--Authme Reloaded-->
|
||||
<!--AuthMe Reloaded-->
|
||||
<repository>
|
||||
<id>xephi-repo</id>
|
||||
<url>http://ci.xephi.fr/plugin/repository/everything/</url>
|
||||
<url>https://ci.xephi.fr/plugin/repository/everything/</url>
|
||||
</repository>
|
||||
|
||||
<!--xAuth-->
|
||||
@@ -51,6 +51,12 @@
|
||||
<id>jitpack.io</id>
|
||||
<url>https://jitpack.io</url>
|
||||
</repository>
|
||||
|
||||
<!--PlaceholderAPI -->
|
||||
<repository>
|
||||
<id>placeholderapi</id>
|
||||
<url>http://repo.extendedclip.com/content/repositories/placeholderapi/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
@@ -65,7 +71,7 @@
|
||||
<dependency>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
<artifactId>spigot-api</artifactId>
|
||||
<version>1.10-R0.1-SNAPSHOT</version>
|
||||
<version>1.12-pre2-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -73,21 +79,35 @@
|
||||
<dependency>
|
||||
<groupId>com.comphenix.protocol</groupId>
|
||||
<artifactId>ProtocolLib</artifactId>
|
||||
<version>3.6.5</version>
|
||||
<version>4.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.ProtocolSupport</groupId>
|
||||
<artifactId>ProtocolSupport</artifactId>
|
||||
<dependency>
|
||||
<groupId>com.github.ProtocolSupport</groupId>
|
||||
<artifactId>ProtocolSupport</artifactId>
|
||||
<!--4.25.dev-->
|
||||
<version>5554413b51</version>
|
||||
</dependency>
|
||||
<version>a4f060dc46</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>me.clip</groupId>
|
||||
<artifactId>placeholderapi</artifactId>
|
||||
<version>2.8.1</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.2-SNAPSHOT</version>
|
||||
<version>5.3.1</version>
|
||||
<optional>true</optional>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
@@ -100,7 +120,7 @@
|
||||
<dependency>
|
||||
<groupId>com.lenis0012.bukkit</groupId>
|
||||
<artifactId>loginsecurity</artifactId>
|
||||
<version>2.1.3-SNAPSHOT</version>
|
||||
<version>2.1.6</version>
|
||||
<optional>true</optional>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
@@ -110,10 +130,10 @@
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.games647</groupId>
|
||||
<artifactId>LogIt</artifactId>
|
||||
<version>9e3581db27</version>
|
||||
<dependency>
|
||||
<groupId>com.github.games647</groupId>
|
||||
<artifactId>LogIt</artifactId>
|
||||
<version>9e3581db27</version>
|
||||
<optional>true</optional>
|
||||
<scope>provided</scope>
|
||||
<exclusions>
|
||||
@@ -122,7 +142,7 @@
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.RoyalDev</groupId>
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
package com.github.games647.fastlogin.bukkit;
|
||||
|
||||
import com.github.games647.fastlogin.core.shared.FastLoginCore;
|
||||
import com.github.games647.fastlogin.core.shared.MojangApiConnector;
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class BukkitCore extends FastLoginCore<Player> {
|
||||
|
||||
private final FastLoginBukkit plugin;
|
||||
|
||||
public BukkitCore(FastLoginBukkit plugin) {
|
||||
super(plugin.getConfig().getValues(false));
|
||||
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getDataFolder() {
|
||||
return plugin.getDataFolder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Logger getLogger() {
|
||||
return plugin.getLogger();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ThreadFactory getThreadFactory() {
|
||||
String pluginName = plugin.getName();
|
||||
return new ThreadFactoryBuilder()
|
||||
.setNameFormat(pluginName + " Database Pool Thread #%1$d")
|
||||
//Hikari create daemons by default
|
||||
.setDaemon(true)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadMessages() {
|
||||
plugin.saveResource("messages.yml", false);
|
||||
|
||||
File messageFile = new File(plugin.getDataFolder(), "messages.yml");
|
||||
YamlConfiguration messageConfig = YamlConfiguration.loadConfiguration(messageFile);
|
||||
|
||||
InputStreamReader defaultReader = new InputStreamReader(plugin.getResource("messages.yml"), Charsets.UTF_8);
|
||||
YamlConfiguration defaults = YamlConfiguration.loadConfiguration(defaultReader);
|
||||
|
||||
messageConfig.setDefaults(defaults);
|
||||
|
||||
messageConfig.getKeys(false).forEach((key) -> {
|
||||
String message = ChatColor.translateAlternateColorCodes('&', messageConfig.getString(key));
|
||||
if (!message.isEmpty()) {
|
||||
localeMessages.put(key, message);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public MojangApiConnector makeApiConnector(Logger logger, List<String> addresses, int requests) {
|
||||
return new MojangApiBukkit(logger, addresses, requests);
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,7 @@ public class BukkitLoginSession extends LoginSession {
|
||||
this.verifyToken = ArrayUtils.clone(verifyToken);
|
||||
}
|
||||
|
||||
//available for bungeecord
|
||||
//available for BungeeCord
|
||||
public BukkitLoginSession(String username, boolean registered) {
|
||||
this(username, "", ArrayUtils.EMPTY_BYTE_ARRAY, registered, null);
|
||||
}
|
||||
@@ -41,7 +41,7 @@ public class BukkitLoginSession extends LoginSession {
|
||||
/**
|
||||
* Gets the random generated server id. This makes sure the request sent from the client is just for this server.
|
||||
*
|
||||
* See this for details http://www.sk89q.com/2011/09/minecraft-name-spoofing-exploit/
|
||||
* See this for details http://www.sk89q.com/2011/09/Minecraft-name-spoofing-exploit/
|
||||
*
|
||||
* Empty if it's a BungeeCord connection
|
||||
*
|
||||
|
||||
@@ -8,8 +8,6 @@ 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.stream.Stream;
|
||||
|
||||
import javax.crypto.BadPaddingException;
|
||||
@@ -41,14 +39,14 @@ public class EncryptionUtil {
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] getServerIdHash(String serverId, PublicKey publicKey, SecretKey secretKey) {
|
||||
public static byte[] getServerIdHash(String serverId, Key publicKey, Key secretKey) {
|
||||
return digestOperation("SHA-1"
|
||||
, new byte[][]{serverId.getBytes(Charsets.ISO_8859_1), secretKey.getEncoded(), publicKey.getEncoded()});
|
||||
, serverId.getBytes(Charsets.ISO_8859_1), secretKey.getEncoded(), publicKey.getEncoded());
|
||||
}
|
||||
|
||||
private static byte[] digestOperation(String algo, byte[]... content) {
|
||||
private static byte[] digestOperation(String algorithm, byte[]... content) {
|
||||
try {
|
||||
MessageDigest messagedigest = MessageDigest.getInstance(algo);
|
||||
MessageDigest messagedigest = MessageDigest.getInstance(algorithm);
|
||||
Stream.of(content).forEach(messagedigest::update);
|
||||
|
||||
return messagedigest.digest();
|
||||
@@ -72,7 +70,7 @@ public class EncryptionUtil {
|
||||
// return null;
|
||||
// }
|
||||
|
||||
public static SecretKey decryptSharedKey(PrivateKey privateKey, byte[] encryptedSharedKey) {
|
||||
public static SecretKey decryptSharedKey(Key privateKey, byte[] encryptedSharedKey) {
|
||||
return new SecretKeySpec(decryptData(privateKey, encryptedSharedKey), "AES");
|
||||
}
|
||||
|
||||
@@ -83,8 +81,8 @@ public class EncryptionUtil {
|
||||
private static byte[] cipherOperation(int operationMode, Key key, byte[] data) {
|
||||
try {
|
||||
return createCipherInstance(operationMode, key.getAlgorithm(), key).doFinal(data);
|
||||
} catch (IllegalBlockSizeException | BadPaddingException illegalblocksizeexception) {
|
||||
illegalblocksizeexception.printStackTrace();
|
||||
} catch (IllegalBlockSizeException | BadPaddingException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
|
||||
System.err.println("Cipher data failed!");
|
||||
@@ -97,8 +95,8 @@ public class EncryptionUtil {
|
||||
|
||||
cipher.init(operationMode, key);
|
||||
return cipher;
|
||||
} catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException invalidkeyexception) {
|
||||
invalidkeyexception.printStackTrace();
|
||||
} catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
|
||||
System.err.println("Cipher creation failed!");
|
||||
|
||||
@@ -1,66 +1,65 @@
|
||||
package com.github.games647.fastlogin.bukkit;
|
||||
|
||||
import com.avaje.ebeaninternal.api.ClassUtil;
|
||||
import com.comphenix.protocol.AsynchronousManager;
|
||||
import com.comphenix.protocol.ProtocolLibrary;
|
||||
import com.github.games647.fastlogin.bukkit.commands.CrackedCommand;
|
||||
import com.github.games647.fastlogin.bukkit.commands.ImportCommand;
|
||||
import com.github.games647.fastlogin.bukkit.commands.PremiumCommand;
|
||||
import com.github.games647.fastlogin.bukkit.hooks.BukkitAuthPlugin;
|
||||
import com.github.games647.fastlogin.bukkit.listener.BukkitJoinListener;
|
||||
import com.github.games647.fastlogin.bukkit.listener.BungeeCordListener;
|
||||
import com.github.games647.fastlogin.bukkit.listener.protocollib.EncryptionPacketListener;
|
||||
import com.github.games647.fastlogin.bukkit.listener.protocollib.LoginSkinApplyListener;
|
||||
import com.github.games647.fastlogin.bukkit.listener.protocollib.StartPacketListener;
|
||||
import com.github.games647.fastlogin.bukkit.listener.protocollib.ProtocolLibListener;
|
||||
import com.github.games647.fastlogin.bukkit.listener.protocolsupport.ProtocolSupportListener;
|
||||
import com.github.games647.fastlogin.bukkit.tasks.DelayedAuthHook;
|
||||
import com.github.games647.fastlogin.core.shared.FastLoginCore;
|
||||
import com.github.games647.fastlogin.core.shared.MojangApiConnector;
|
||||
import com.github.games647.fastlogin.core.shared.PlatformPlugin;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.io.ByteArrayDataOutput;
|
||||
import com.google.common.io.ByteStreams;
|
||||
|
||||
import java.io.Reader;
|
||||
import java.security.KeyPair;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.bukkit.plugin.messaging.PluginMessageRecipient;
|
||||
|
||||
/**
|
||||
* This plugin checks if a player has a paid account and if so tries to skip offline mode authentication.
|
||||
*/
|
||||
public class FastLoginBukkit extends JavaPlugin {
|
||||
|
||||
private static final int WORKER_THREADS = 3;
|
||||
public class FastLoginBukkit extends JavaPlugin implements PlatformPlugin<CommandSender> {
|
||||
|
||||
//provide a immutable key pair to be thread safe | used for encrypting and decrypting traffic
|
||||
private final KeyPair keyPair = EncryptionUtil.generateKeyPair();
|
||||
|
||||
private boolean bungeeCord;
|
||||
private BukkitCore core;
|
||||
private FastLoginCore<Player, CommandSender, FastLoginBukkit> core;
|
||||
private boolean serverStarted;
|
||||
|
||||
//1 minutes should be enough as a timeout for bad internet connection (Server, Client and Mojang)
|
||||
private final ConcurrentMap<String, BukkitLoginSession> session = FastLoginCore.buildCache(1, -1);
|
||||
private final ConcurrentMap<String, BukkitLoginSession> loginSession = FastLoginCore.buildCache(1, -1);
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
saveDefaultConfig();
|
||||
core = new BukkitCore(this);
|
||||
|
||||
core.loadMessages();
|
||||
core.setApiConnector();
|
||||
core = new FastLoginCore<>(this);
|
||||
core.load();
|
||||
try {
|
||||
if (ClassUtil.isPresent("org.spigotmc.SpigotConfig")) {
|
||||
bungeeCord = Class.forName("org.spigotmc.SpigotConfig").getDeclaredField("bungee").getBoolean(null);
|
||||
}
|
||||
bungeeCord = Class.forName("org.spigotmc.SpigotConfig").getDeclaredField("bungee").getBoolean(null);
|
||||
} catch (ClassNotFoundException notFoundEx) {
|
||||
//ignore server has no bungee support
|
||||
} catch (Exception ex) {
|
||||
getLogger().log(Level.WARNING, "Cannot check bungeecord support. You use a non-spigot build", ex);
|
||||
}
|
||||
|
||||
if (getServer().getOnlineMode()) {
|
||||
//we need to require offline to prevent a session request for a offline player
|
||||
//we need to require offline to prevent a loginSession request for a offline player
|
||||
getLogger().severe("Server have to be in offline mode");
|
||||
setEnabled(false);
|
||||
return;
|
||||
@@ -82,14 +81,11 @@ public class FastLoginBukkit extends JavaPlugin {
|
||||
if (getServer().getPluginManager().isPluginEnabled("ProtocolSupport")) {
|
||||
getServer().getPluginManager().registerEvents(new ProtocolSupportListener(this), this);
|
||||
} else if (getServer().getPluginManager().isPluginEnabled("ProtocolLib")) {
|
||||
//we are performing HTTP request on these so run it async (seperate from the Netty IO threads)
|
||||
AsynchronousManager asynchronousManager = ProtocolLibrary.getProtocolManager().getAsynchronousManager();
|
||||
//they will be created with a static builder, because otherwise it will throw a
|
||||
//java.lang.NoClassDefFoundError: com/comphenix/protocol/events/PacketListener if ProtocolSupport was
|
||||
//only found
|
||||
ProtocolLibListener.register(this);
|
||||
|
||||
StartPacketListener startPacketListener = new StartPacketListener(this);
|
||||
EncryptionPacketListener encryptionPacketListener = new EncryptionPacketListener(this);
|
||||
|
||||
asynchronousManager.registerAsyncHandler(startPacketListener).start(WORKER_THREADS);
|
||||
asynchronousManager.registerAsyncHandler(encryptionPacketListener).start(WORKER_THREADS);
|
||||
getServer().getPluginManager().registerEvents(new LoginSkinApplyListener(this), this);
|
||||
} else {
|
||||
getLogger().warning("Either ProtocolLib or ProtocolSupport have to be installed "
|
||||
@@ -98,19 +94,23 @@ public class FastLoginBukkit extends JavaPlugin {
|
||||
}
|
||||
|
||||
//delay dependency setup because we load the plugin very early where plugins are initialized yet
|
||||
getServer().getScheduler().runTask(this, new DelayedAuthHook(this));
|
||||
getServer().getScheduler().runTaskLater(this, new DelayedAuthHook(this), 5L);
|
||||
|
||||
getServer().getPluginManager().registerEvents(new BukkitJoinListener(this), this);
|
||||
|
||||
//register commands using a unique name
|
||||
getCommand("premium").setExecutor(new PremiumCommand(this));
|
||||
getCommand("cracked").setExecutor(new CrackedCommand(this));
|
||||
getCommand("import-auth").setExecutor(new ImportCommand(core));
|
||||
|
||||
if (getServer().getPluginManager().isPluginEnabled("PlaceholderAPI")) {
|
||||
//prevents NoClassDef errors if it's not available
|
||||
PremiumPlaceholder.register(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
session.clear();
|
||||
loginSession.clear();
|
||||
|
||||
if (core != null) {
|
||||
core.close();
|
||||
@@ -120,13 +120,13 @@ public class FastLoginBukkit extends JavaPlugin {
|
||||
getServer().getOnlinePlayers().forEach(player -> player.removeMetadata(getName(), this));
|
||||
}
|
||||
|
||||
public BukkitCore getCore() {
|
||||
public FastLoginCore<Player, CommandSender, FastLoginBukkit> getCore() {
|
||||
return core;
|
||||
}
|
||||
|
||||
public void sendBungeeActivateMessage(CommandSender sender, String target, boolean activate) {
|
||||
if (sender instanceof Player) {
|
||||
notifiyBungeeCord((Player) sender, target, activate);
|
||||
notifyBungeeCord((Player) sender, target, activate, sender instanceof Player);
|
||||
} else {
|
||||
Player firstPlayer = Iterables.getFirst(getServer().getOnlinePlayers(), null);
|
||||
if (firstPlayer == null) {
|
||||
@@ -134,23 +134,18 @@ public class FastLoginBukkit extends JavaPlugin {
|
||||
return;
|
||||
}
|
||||
|
||||
notifiyBungeeCord(firstPlayer, target, activate);
|
||||
notifyBungeeCord(firstPlayer, target, activate, sender instanceof Player);
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void setPasswordGenerator(PasswordGenerator passwordGenerator) {
|
||||
core.setPasswordGenerator(passwordGenerator);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 session map
|
||||
* @return a thread-safe loginSession map
|
||||
*/
|
||||
public ConcurrentMap<String, BukkitLoginSession> getSessions() {
|
||||
return session;
|
||||
public ConcurrentMap<String, BukkitLoginSession> getLoginSessions() {
|
||||
return loginSession;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -162,22 +157,6 @@ public class FastLoginBukkit extends JavaPlugin {
|
||||
return keyPair;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the auth plugin hook in order to interact with the plugins. This can be null if no supporting auth plugin
|
||||
* was found.
|
||||
*
|
||||
* @return interface to any supported auth plugin
|
||||
*/
|
||||
@Deprecated
|
||||
public BukkitAuthPlugin getAuthPlugin() {
|
||||
return (BukkitAuthPlugin) core.getAuthPluginHook();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void setAuthPluginHook(BukkitAuthPlugin authPlugin) {
|
||||
core.setAuthPluginHook(authPlugin);
|
||||
}
|
||||
|
||||
public boolean isBungeeCord() {
|
||||
return bungeeCord;
|
||||
}
|
||||
@@ -198,7 +177,7 @@ public class FastLoginBukkit extends JavaPlugin {
|
||||
}
|
||||
}
|
||||
|
||||
private void notifiyBungeeCord(Player sender, String target, boolean activate) {
|
||||
private void notifyBungeeCord(PluginMessageRecipient sender, String target, boolean activate, boolean isPlayer) {
|
||||
ByteArrayDataOutput dataOutput = ByteStreams.newDataOutput();
|
||||
if (activate) {
|
||||
dataOutput.writeUTF("ON");
|
||||
@@ -207,6 +186,35 @@ public class FastLoginBukkit extends JavaPlugin {
|
||||
}
|
||||
|
||||
dataOutput.writeUTF(target);
|
||||
dataOutput.writeBoolean(isPlayer);
|
||||
sender.sendPluginMessage(this, getName(), dataOutput.toByteArray());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> loadYamlFile(Reader reader) {
|
||||
YamlConfiguration config = YamlConfiguration.loadConfiguration(reader);
|
||||
return config.getValues(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(CommandSender receiver, String message) {
|
||||
receiver.sendMessage(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String translateColorCodes(char colorChar, String rawMessage) {
|
||||
return ChatColor.translateAlternateColorCodes(colorChar, rawMessage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ThreadFactory getThreadFactory() {
|
||||
//not required here to make a custom thread factory
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MojangApiConnector makeApiConnector(Logger logger, List<String> addresses, int requests
|
||||
, Map<String, Integer> proxies) {
|
||||
return new MojangApiBukkit(logger, addresses, requests, proxies);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,10 @@ import com.github.games647.fastlogin.core.shared.MojangApiConnector;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
@@ -18,22 +21,27 @@ import org.json.simple.JSONValue;
|
||||
public class MojangApiBukkit extends MojangApiConnector {
|
||||
|
||||
//mojang api check to prove a player is logged in minecraft and made a join server request
|
||||
private static final String HAS_JOINED_URL = "https://sessionserver.mojang.com/session/minecraft/hasJoined?";
|
||||
private static final String HAS_JOINED_URL = "https://sessionserver.mojang.com/session/minecraft/hasJoined?" +
|
||||
"username=%s&serverId=%s";
|
||||
|
||||
public MojangApiBukkit(Logger logger, List<String> localAddresses, int rateLimit) {
|
||||
super(logger, localAddresses, rateLimit);
|
||||
public MojangApiBukkit(Logger logger, List<String> localAddresses, int rateLimit, Map<String, Integer> proxies) {
|
||||
super(logger, localAddresses, rateLimit, proxies);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasJoinedServer(LoginSession session, String serverId) {
|
||||
public boolean hasJoinedServer(LoginSession session, String serverId, InetSocketAddress ip) {
|
||||
BukkitLoginSession playerSession = (BukkitLoginSession) session;
|
||||
try {
|
||||
String url = HAS_JOINED_URL + "username=" + playerSession.getUsername() + "&serverId=" + serverId;
|
||||
String url = String.format(HAS_JOINED_URL, playerSession.getUsername(), serverId);
|
||||
if (ip != null) {
|
||||
url += "&ip=" + URLEncoder.encode(ip.getAddress().getHostAddress(), "UTF-8");
|
||||
}
|
||||
|
||||
HttpURLConnection conn = getConnection(url);
|
||||
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
|
||||
String line = reader.readLine();
|
||||
if (line != null && !line.equals("null")) {
|
||||
if (line != null && !"null".equals(line)) {
|
||||
//validate parsing
|
||||
//http://wiki.vg/Protocol_Encryption#Server
|
||||
JSONObject userData = (JSONObject) JSONValue.parseWithException(line);
|
||||
@@ -44,7 +52,7 @@ public class MojangApiBukkit extends MojangApiConnector {
|
||||
JSONObject skinProperty = (JSONObject) properties.get(0);
|
||||
|
||||
String propertyName = (String) skinProperty.get("name");
|
||||
if (propertyName.equals("textures")) {
|
||||
if ("textures".equals(propertyName)) {
|
||||
String skinValue = (String) skinProperty.get("value");
|
||||
String signature = (String) skinProperty.get("signature");
|
||||
playerSession.setSkin(skinValue, signature);
|
||||
@@ -53,7 +61,7 @@ public class MojangApiBukkit extends MojangApiConnector {
|
||||
return true;
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
//catch not only ioexceptions also parse and NPE on unexpected json format
|
||||
//catch not only io-exceptions also parse and NPE on unexpected json format
|
||||
logger.log(Level.WARNING, "Failed to verify session", ex);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
package com.github.games647.fastlogin.bukkit;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
/**
|
||||
*
|
||||
* @deprecated please use com.github.games647.fastlogin.core.hooks.PasswordGenerator<org.bukkit.entity.Player>
|
||||
*/
|
||||
@Deprecated
|
||||
public interface PasswordGenerator extends com.github.games647.fastlogin.core.hooks.PasswordGenerator<Player> {
|
||||
|
||||
@Override
|
||||
String getRandomPassword(Player player);
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.github.games647.fastlogin.bukkit;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import me.clip.placeholderapi.PlaceholderAPI;
|
||||
import me.clip.placeholderapi.PlaceholderHook;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.metadata.MetadataValue;
|
||||
|
||||
public class PremiumPlaceholder extends PlaceholderHook {
|
||||
|
||||
private final FastLoginBukkit plugin;
|
||||
|
||||
public PremiumPlaceholder(FastLoginBukkit plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String onPlaceholderRequest(Player player, String variable) {
|
||||
if (player != null && "fastlogin_status".contains(variable)) {
|
||||
List<MetadataValue> metadata = player.getMetadata(plugin.getName());
|
||||
if (metadata == null) {
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
if (!metadata.isEmpty()) {
|
||||
return "premium";
|
||||
} else {
|
||||
return "cracked";
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static boolean register(FastLoginBukkit plugin) {
|
||||
return PlaceholderAPI.registerPlaceholderHook(plugin, new PremiumPlaceholder(plugin));
|
||||
}
|
||||
}
|
||||
@@ -28,15 +28,12 @@ public class CrackedCommand implements CommandExecutor {
|
||||
|
||||
if (plugin.isBungeeCord()) {
|
||||
plugin.sendBungeeActivateMessage(sender, sender.getName(), false);
|
||||
String message = plugin.getCore().getMessage("wait-on-proxy");
|
||||
if (message != null) {
|
||||
sender.sendMessage(message);
|
||||
}
|
||||
plugin.getCore().sendLocaleMessage("wait-on-proxy", sender);
|
||||
} else {
|
||||
//todo: load async if
|
||||
PlayerProfile profile = plugin.getCore().getStorage().loadProfile(sender.getName());
|
||||
if (profile.isPremium()) {
|
||||
sender.sendMessage(plugin.getCore().getMessage("remove-premium"));
|
||||
plugin.getCore().sendLocaleMessage("remove-premium", sender);
|
||||
|
||||
profile.setPremium(false);
|
||||
profile.setUuid(null);
|
||||
@@ -44,7 +41,7 @@ public class CrackedCommand implements CommandExecutor {
|
||||
plugin.getCore().getStorage().save(profile);
|
||||
});
|
||||
} else {
|
||||
sender.sendMessage(plugin.getCore().getMessage("not-premium"));
|
||||
plugin.getCore().sendLocaleMessage("not-premium", sender);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,29 +55,27 @@ public class CrackedCommand implements CommandExecutor {
|
||||
|
||||
private void onCrackedOther(CommandSender sender, Command command, String[] args) {
|
||||
if (!sender.hasPermission(command.getPermission() + ".other")) {
|
||||
sender.sendMessage(plugin.getCore().getMessage("no-permission"));
|
||||
plugin.getCore().sendLocaleMessage("no-permission", sender);
|
||||
return;
|
||||
}
|
||||
|
||||
if (plugin.isBungeeCord()) {
|
||||
plugin.sendBungeeActivateMessage(sender, args[0], false);
|
||||
String message = plugin.getCore().getMessage("wait-on-proxy");
|
||||
if (message != null) {
|
||||
sender.sendMessage(message);
|
||||
}
|
||||
plugin.getCore().sendLocaleMessage("wait-on-proxy", sender);
|
||||
} else {
|
||||
//todo: load async
|
||||
PlayerProfile profile = plugin.getCore().getStorage().loadProfile(args[0]);
|
||||
if (profile == null) {
|
||||
sender.sendMessage("Error occured");
|
||||
sender.sendMessage("Error occurred");
|
||||
return;
|
||||
}
|
||||
|
||||
//existing player is already cracked
|
||||
if (profile.getUserId() != -1 && !profile.isPremium()) {
|
||||
sender.sendMessage(plugin.getCore().getMessage("not-premium-other"));
|
||||
plugin.getCore().sendLocaleMessage("not-premium-other", sender);
|
||||
} else {
|
||||
sender.sendMessage(plugin.getCore().getMessage("remove-premium"));
|
||||
plugin.getCore().sendLocaleMessage("remove-premium", sender);
|
||||
|
||||
profile.setPremium(false);
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||
plugin.getCore().getStorage().save(profile);
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
package com.github.games647.fastlogin.bukkit.commands;
|
||||
|
||||
import com.github.games647.fastlogin.bukkit.BukkitCore;
|
||||
import com.github.games647.fastlogin.core.AuthStorage;
|
||||
import com.github.games647.fastlogin.core.importer.ImportPlugin;
|
||||
import org.bukkit.ChatColor;
|
||||
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
public class ImportCommand implements CommandExecutor {
|
||||
|
||||
private final BukkitCore core;
|
||||
|
||||
public ImportCommand(BukkitCore core) {
|
||||
this.core = core;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
if (args.length < 2) {
|
||||
sender.sendMessage(ChatColor.DARK_RED + "You need to specify the import plugin and database type");
|
||||
return true;
|
||||
}
|
||||
|
||||
ImportPlugin importPlugin;
|
||||
switch (args[0].toLowerCase()) {
|
||||
case "autoin":
|
||||
importPlugin = ImportPlugin.AUTO_IN;
|
||||
break;
|
||||
case "bpa":
|
||||
importPlugin = ImportPlugin.BPA;
|
||||
break;
|
||||
case "eldzi":
|
||||
importPlugin = ImportPlugin.ELDZI;
|
||||
break;
|
||||
default:
|
||||
sender.sendMessage(ChatColor.DARK_RED + "Unknown auto login plugin");
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean sqlite;
|
||||
switch (args[1].toLowerCase()) {
|
||||
case "sqlite":
|
||||
sqlite = true;
|
||||
break;
|
||||
case "mysql":
|
||||
sqlite = false;
|
||||
break;
|
||||
default:
|
||||
sender.sendMessage(ChatColor.DARK_RED + "Unknown storage type to import from. Either SQLite or MySQL");
|
||||
return true;
|
||||
}
|
||||
|
||||
String host = "";
|
||||
String database = "";
|
||||
String username = "";
|
||||
String password = "";
|
||||
if (!sqlite) {
|
||||
if (args.length <= 5) {
|
||||
sender.sendMessage(ChatColor.DARK_RED + "If importing from MySQL, you need to specify host database "
|
||||
+ "and username passowrd too");
|
||||
return true;
|
||||
}
|
||||
|
||||
host = args[2];
|
||||
database = args[3];
|
||||
username = args[4];
|
||||
password = args[5];
|
||||
}
|
||||
|
||||
AuthStorage storage = core.getStorage();
|
||||
boolean success = core.importDatabase(importPlugin, true, storage, host, database, username, password);
|
||||
if (success) {
|
||||
sender.sendMessage(ChatColor.DARK_GREEN + "Successful imported the data");
|
||||
} else {
|
||||
sender.sendMessage(ChatColor.DARK_RED + "Failed to import the data. Check out the logs");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -29,16 +29,13 @@ public class PremiumCommand implements CommandExecutor {
|
||||
if (args.length == 0) {
|
||||
if (!(sender instanceof Player)) {
|
||||
//console or command block
|
||||
sender.sendMessage(plugin.getCore().getMessage("no-console"));
|
||||
plugin.getCore().sendLocaleMessage("no-console", sender);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (plugin.isBungeeCord()) {
|
||||
plugin.sendBungeeActivateMessage(sender, sender.getName(), true);
|
||||
String message = plugin.getCore().getMessage("wait-on-proxy");
|
||||
if (message != null) {
|
||||
sender.sendMessage(message);
|
||||
}
|
||||
plugin.getCore().sendLocaleMessage("wait-on-proxy", sender);
|
||||
} else {
|
||||
UUID id = ((Player) sender).getUniqueId();
|
||||
if (plugin.getConfig().getBoolean("premium-warning")
|
||||
@@ -52,7 +49,7 @@ public class PremiumCommand implements CommandExecutor {
|
||||
//todo: load async
|
||||
PlayerProfile profile = plugin.getCore().getStorage().loadProfile(sender.getName());
|
||||
if (profile.isPremium()) {
|
||||
sender.sendMessage(plugin.getCore().getMessage("already-exists"));
|
||||
plugin.getCore().sendLocaleMessage("already-exists", sender);
|
||||
} else {
|
||||
//todo: resolve uuid
|
||||
profile.setPremium(true);
|
||||
@@ -60,7 +57,7 @@ public class PremiumCommand implements CommandExecutor {
|
||||
plugin.getCore().getStorage().save(profile);
|
||||
});
|
||||
|
||||
sender.sendMessage(plugin.getCore().getMessage("add-premium"));
|
||||
plugin.getCore().sendLocaleMessage("add-premium", sender);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,26 +71,23 @@ public class PremiumCommand implements CommandExecutor {
|
||||
|
||||
private void onPremiumOther(CommandSender sender, Command command, String[] args) {
|
||||
if (!sender.hasPermission(command.getPermission() + ".other")) {
|
||||
sender.sendMessage(plugin.getCore().getMessage("no-permission"));
|
||||
plugin.getCore().sendLocaleMessage("no-permission", sender);
|
||||
return ;
|
||||
}
|
||||
|
||||
if (plugin.isBungeeCord()) {
|
||||
plugin.sendBungeeActivateMessage(sender, args[0], true);
|
||||
String message = plugin.getCore().getMessage("wait-on-proxy");
|
||||
if (message != null) {
|
||||
sender.sendMessage(message);
|
||||
}
|
||||
plugin.getCore().sendLocaleMessage("wait-on-proxy", sender);
|
||||
} else {
|
||||
//todo: load async
|
||||
PlayerProfile profile = plugin.getCore().getStorage().loadProfile(args[0]);
|
||||
if (profile == null) {
|
||||
sender.sendMessage(plugin.getCore().getMessage("player-unknown"));
|
||||
plugin.getCore().sendLocaleMessage("player-unknown", sender);
|
||||
return;
|
||||
}
|
||||
|
||||
if (profile.isPremium()) {
|
||||
sender.sendMessage(plugin.getCore().getMessage("already-exists-other"));
|
||||
plugin.getCore().sendLocaleMessage("already-exists-other", sender);
|
||||
} else {
|
||||
//todo: resolve uuid
|
||||
profile.setPremium(true);
|
||||
@@ -101,7 +95,7 @@ public class PremiumCommand implements CommandExecutor {
|
||||
plugin.getCore().getStorage().save(profile);
|
||||
});
|
||||
|
||||
sender.sendMessage(plugin.getCore().getMessage("add-premium-other"));
|
||||
plugin.getCore().sendLocaleMessage("add-premium-other", sender);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,59 +1,72 @@
|
||||
package com.github.games647.fastlogin.bukkit.hooks;
|
||||
|
||||
import com.avaje.ebeaninternal.api.ClassUtil;
|
||||
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
|
||||
|
||||
import fr.xephi.authme.api.API;
|
||||
import fr.xephi.authme.api.NewAPI;
|
||||
|
||||
import fr.xephi.authme.api.v3.AuthMeApi;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
/**
|
||||
* Github: https://github.com/Xephi/AuthMeReloaded/
|
||||
*
|
||||
* Project page:
|
||||
*
|
||||
* Bukkit: http://dev.bukkit.org/bukkit-plugins/authme-reloaded/
|
||||
*
|
||||
* Spigot: https://www.spigotmc.org/resources/authme-reloaded.6269/
|
||||
*/
|
||||
public class AuthMeHook implements AuthPlugin<Player> {
|
||||
|
||||
private final boolean isNewAPIAvailable;
|
||||
private final boolean v3APIAvailable;
|
||||
|
||||
public AuthMeHook() {
|
||||
this.isNewAPIAvailable = ClassUtil.isPresent("fr.xephi.authme.api.NewAPI");
|
||||
boolean apiAvailable = true;
|
||||
try {
|
||||
Class.forName("fr.xephi.authme.api.v3.AuthMeApi");
|
||||
} catch (ClassNotFoundException classNotFoundEx) {
|
||||
apiAvailable = false;
|
||||
}
|
||||
|
||||
this.v3APIAvailable = apiAvailable;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
public boolean forceLogin(Player player) {
|
||||
//skips registration and login
|
||||
if (isNewAPIAvailable) {
|
||||
NewAPI.getInstance().forceLogin(player);
|
||||
if (v3APIAvailable) {
|
||||
//skips registration and login
|
||||
if (AuthMeApi.getInstance().isAuthenticated(player)) {
|
||||
return false;
|
||||
} else {
|
||||
AuthMeApi.getInstance().forceLogin(player);
|
||||
}
|
||||
} else {
|
||||
API.forceLogin(player);
|
||||
//skips registration and login
|
||||
if (NewAPI.getInstance().isAuthenticated(player)) {
|
||||
return false;
|
||||
} else {
|
||||
NewAPI.getInstance().forceLogin(player);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
public boolean isRegistered(String playerName) throws Exception {
|
||||
if (isNewAPIAvailable) {
|
||||
return NewAPI.getInstance().isRegistered(playerName);
|
||||
} else {
|
||||
return API.isRegistered(playerName);
|
||||
if (v3APIAvailable) {
|
||||
return AuthMeApi.getInstance().isRegistered(playerName);
|
||||
}
|
||||
|
||||
return NewAPI.getInstance().isRegistered(playerName);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
public boolean forceRegister(Player player, String password) {
|
||||
if (isNewAPIAvailable) {
|
||||
NewAPI.getInstance().forceRegister(player, password);
|
||||
//this automatically registers the player too
|
||||
if (v3APIAvailable) {
|
||||
AuthMeApi.getInstance().forceRegister(player, password);
|
||||
} else {
|
||||
API.registerPlayer(player.getName(), password);
|
||||
forceLogin(player);
|
||||
NewAPI.getInstance().forceRegister(player, password);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
package com.github.games647.fastlogin.bukkit.hooks;
|
||||
|
||||
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
/**
|
||||
* @deprecated please use com.github.games647.fastlogin.core.hooks.AuthPlugin<org.bukkit.entity.Player>
|
||||
*/
|
||||
@Deprecated
|
||||
public interface BukkitAuthPlugin extends AuthPlugin<Player> {
|
||||
|
||||
@Override
|
||||
boolean forceLogin(Player player);
|
||||
|
||||
@Override
|
||||
boolean isRegistered(String playerName) throws Exception;
|
||||
|
||||
@Override
|
||||
boolean forceRegister(Player player, String password);
|
||||
}
|
||||
@@ -38,7 +38,7 @@ public class CrazyLoginHook implements AuthPlugin<Player> {
|
||||
playerData.setLoggedIn(true);
|
||||
|
||||
String ip = player.getAddress().getAddress().getHostAddress();
|
||||
//this should be done after login to restore the inventory, unhide players, prevent potential memory leaks...
|
||||
//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);
|
||||
|
||||
@@ -5,7 +5,7 @@ import com.github.games647.fastlogin.core.hooks.AuthPlugin;
|
||||
import io.github.lucaseasedup.logit.CancelledState;
|
||||
import io.github.lucaseasedup.logit.LogItCore;
|
||||
import io.github.lucaseasedup.logit.account.Account;
|
||||
|
||||
import io.github.lucaseasedup.logit.session.SessionManager;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
/**
|
||||
@@ -19,7 +19,12 @@ public class LogItHook implements AuthPlugin<Player> {
|
||||
|
||||
@Override
|
||||
public boolean forceLogin(Player player) {
|
||||
return LogItCore.getInstance().getSessionManager().startSession(player) == CancelledState.NOT_CANCELLED;
|
||||
SessionManager sessionManager = LogItCore.getInstance().getSessionManager();
|
||||
if (sessionManager.isSessionAlive(player)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return sessionManager.startSession(player) == CancelledState.NOT_CANCELLED;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -24,6 +24,10 @@ public class LoginSecurityHook implements AuthPlugin<Player> {
|
||||
@Override
|
||||
public boolean forceLogin(Player player) {
|
||||
PlayerSession session = LoginSecurity.getSessionManager().getPlayerSession(player);
|
||||
if (session.isAuthorized()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return session.performAction(new LoginAction(AuthService.PLUGIN, plugin)).isSuccess();
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import java.util.logging.Level;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.royaldev.royalauth.AuthPlayer;
|
||||
import org.royaldev.royalauth.Config;
|
||||
import org.royaldev.royalauth.RoyalAuth;
|
||||
@@ -21,13 +22,17 @@ import org.royaldev.royalauth.RoyalAuth;
|
||||
*/
|
||||
public class RoyalAuthHook implements AuthPlugin<Player> {
|
||||
|
||||
private final RoyalAuth royalAuthPlugin = (RoyalAuth) Bukkit.getPluginManager().getPlugin("RoyalAuth");
|
||||
private final Plugin royalAuthPlugin = (RoyalAuth) Bukkit.getPluginManager().getPlugin("RoyalAuth");
|
||||
|
||||
@Override
|
||||
public boolean forceLogin(Player player) {
|
||||
AuthPlayer authPlayer = AuthPlayer.getAuthPlayer(player);
|
||||
|
||||
Future<Boolean> future = Bukkit.getScheduler().callSyncMethod(royalAuthPlugin, () -> {
|
||||
if (authPlayer.isLoggedIn()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
//https://github.com/RoyalDev/RoyalAuth/blob/master/src/main/java/org/royaldev/royalauth/commands/CmdLogin.java#L62
|
||||
//not thread-safe
|
||||
authPlayer.login();
|
||||
|
||||
@@ -9,7 +9,6 @@ import java.util.logging.Level;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import ultraauth.api.UltraAuthAPI;
|
||||
import ultraauth.main.Main;
|
||||
import ultraauth.managers.PlayerManager;
|
||||
@@ -18,6 +17,7 @@ import ultraauth.managers.PlayerManager;
|
||||
* Project page:
|
||||
*
|
||||
* Bukkit: http://dev.bukkit.org/bukkit-plugins/ultraauth-aa/
|
||||
*
|
||||
* Spigot: https://www.spigotmc.org/resources/ultraauth.17044/
|
||||
*/
|
||||
public class UltraAuthHook implements AuthPlugin<Player> {
|
||||
@@ -28,6 +28,10 @@ public class UltraAuthHook implements AuthPlugin<Player> {
|
||||
public boolean forceLogin(Player player) {
|
||||
//not thread-safe
|
||||
Future<Boolean> future = Bukkit.getScheduler().callSyncMethod(ultraAuthPlugin, () -> {
|
||||
if (UltraAuthAPI.isAuthenticated(player)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
UltraAuthAPI.authenticatedPlayer(player);
|
||||
return UltraAuthAPI.isAuthenticated(player);
|
||||
});
|
||||
@@ -49,7 +53,7 @@ public class UltraAuthHook implements AuthPlugin<Player> {
|
||||
public boolean forceRegister(Player player, String password) {
|
||||
UltraAuthAPI.setPlayerPasswordOnline(player, password);
|
||||
if (PlayerManager.getInstance().checkPlayerPassword(player, password)) {
|
||||
//the register method silents any excpetion so check if our entry was saved
|
||||
//the register method silents any exception so check if our entry was saved
|
||||
return forceLogin(player);
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@ import java.util.concurrent.Future;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
/**
|
||||
@@ -30,6 +29,10 @@ public class xAuthHook implements AuthPlugin<Player> {
|
||||
Future<Boolean> future = Bukkit.getScheduler().callSyncMethod(xAuthPlugin, () -> {
|
||||
xAuthPlayer xAuthPlayer = xAuthPlugin.getPlayerManager().getPlayer(player);
|
||||
if (xAuthPlayer != null) {
|
||||
if (xAuthPlayer.isAuthenticated()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
//we checked that the player is premium (paid account)
|
||||
xAuthPlayer.setPremium(true);
|
||||
|
||||
@@ -63,10 +66,9 @@ public class xAuthHook implements AuthPlugin<Player> {
|
||||
if (xAuthPlayer != null) {
|
||||
//this should run async because the plugin executes a sql query, but the method
|
||||
//accesses non thread-safe collections :(
|
||||
boolean registerSuccess = xAuthPlugin.getAuthClass(xAuthPlayer)
|
||||
.adminRegister(player.getName(), password, null);
|
||||
|
||||
return registerSuccess;
|
||||
return xAuthPlugin.getAuthClass(xAuthPlayer)
|
||||
.adminRegister(player.getName(), password, null);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
@@ -40,7 +40,8 @@ public class BukkitJoinListener implements Listener {
|
||||
|
||||
if (!plugin.isBungeeCord()) {
|
||||
//Wait before auth plugin and we received a message from BungeeCord initializes the player
|
||||
Bukkit.getScheduler().runTaskLaterAsynchronously(plugin, new ForceLoginTask(plugin, player), DELAY_LOGIN);
|
||||
Runnable forceLoginTask = new ForceLoginTask(plugin.getCore(), player);
|
||||
Bukkit.getScheduler().runTaskLaterAsynchronously(plugin, forceLoginTask, DELAY_LOGIN);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,11 +7,10 @@ import com.github.games647.fastlogin.core.hooks.AuthPlugin;
|
||||
import com.google.common.io.ByteArrayDataInput;
|
||||
import com.google.common.io.ByteStreams;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.file.Files;
|
||||
import java.util.List;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
@@ -19,7 +18,6 @@ import java.util.stream.Collectors;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.metadata.FixedMetadataValue;
|
||||
import org.bukkit.plugin.messaging.PluginMessageListener;
|
||||
|
||||
/**
|
||||
@@ -48,19 +46,16 @@ public class BungeeCordListener implements PluginMessageListener {
|
||||
}
|
||||
|
||||
ByteArrayDataInput dataInput = ByteStreams.newDataInput(message);
|
||||
String subchannel = dataInput.readUTF();
|
||||
plugin.getLogger().log(Level.FINEST, "Received plugin message for subchannel {0} from {1}"
|
||||
, new Object[]{subchannel, player});
|
||||
String subChannel = dataInput.readUTF();
|
||||
plugin.getLogger().log(Level.FINEST, "Received plugin message for sub channel {0} from {1}"
|
||||
, new Object[]{subChannel, player});
|
||||
|
||||
String playerName = dataInput.readUTF();
|
||||
|
||||
//check if the player is still online or disconnected
|
||||
Player checkedPlayer = plugin.getServer().getPlayerExact(playerName);
|
||||
//fail if target player is blacklisted because already authed or wrong bungeecord id
|
||||
//fail if target player is blacklisted because already authenticated or wrong bungeecord id
|
||||
if (checkedPlayer != null && !checkedPlayer.hasMetadata(plugin.getName())) {
|
||||
//blacklist this target player for BungeeCord Id brute force attacks
|
||||
player.setMetadata(plugin.getName(), new FixedMetadataValue(plugin, true));
|
||||
|
||||
//bungeecord UUID
|
||||
long mostSignificantBits = dataInput.readLong();
|
||||
long leastSignificantBits = dataInput.readLong();
|
||||
@@ -69,7 +64,7 @@ public class BungeeCordListener implements PluginMessageListener {
|
||||
|
||||
//fail if BungeeCord support is disabled (id = null)
|
||||
if (proxyIds.contains(sourceId)) {
|
||||
readMessage(checkedPlayer, subchannel, playerName, player);
|
||||
readMessage(checkedPlayer, subChannel, playerName, player);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -80,8 +75,8 @@ public class BungeeCordListener implements PluginMessageListener {
|
||||
if ("AUTO_LOGIN".equalsIgnoreCase(subchannel)) {
|
||||
BukkitLoginSession playerSession = new BukkitLoginSession(playerName, true);
|
||||
playerSession.setVerified(true);
|
||||
plugin.getSessions().put(id, playerSession);
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, new ForceLoginTask(plugin, player));
|
||||
plugin.getLoginSessions().put(id, playerSession);
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, new ForceLoginTask(plugin.getCore(), player));
|
||||
} else if ("AUTO_REGISTER".equalsIgnoreCase(subchannel)) {
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
|
||||
AuthPlugin<Player> authPlugin = plugin.getCore().getAuthPluginHook();
|
||||
@@ -90,8 +85,8 @@ public class BungeeCordListener implements PluginMessageListener {
|
||||
if (authPlugin == null || !authPlugin.isRegistered(playerName)) {
|
||||
BukkitLoginSession playerSession = new BukkitLoginSession(playerName, false);
|
||||
playerSession.setVerified(true);
|
||||
plugin.getSessions().put(id, playerSession);
|
||||
new ForceLoginTask(plugin, player).run();
|
||||
plugin.getLoginSessions().put(id, playerSession);
|
||||
new ForceLoginTask(plugin.getCore(), player).run();
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
plugin.getLogger().log(Level.SEVERE, "Failed to query isRegistered", ex);
|
||||
@@ -101,14 +96,16 @@ public class BungeeCordListener implements PluginMessageListener {
|
||||
}
|
||||
|
||||
public Set<UUID> loadBungeeCordIds() {
|
||||
File whitelistFile = new File(plugin.getDataFolder(), FILE_NAME);
|
||||
Path whitelistFile = plugin.getDataFolder().toPath().resolve(FILE_NAME);
|
||||
try {
|
||||
if (!whitelistFile.exists()) {
|
||||
whitelistFile.createNewFile();
|
||||
if (Files.notExists(whitelistFile)) {
|
||||
Files.createFile(whitelistFile);
|
||||
}
|
||||
|
||||
List<String> lines = Files.readAllLines(whitelistFile.toPath());
|
||||
return lines.stream().map(String::trim).map(UUID::fromString).collect(Collectors.toSet());
|
||||
return Files.lines(whitelistFile)
|
||||
.map(String::trim)
|
||||
.map(UUID::fromString)
|
||||
.collect(Collectors.toSet());
|
||||
} catch (IOException ex) {
|
||||
plugin.getLogger().log(Level.SEVERE, "Failed to create file for Proxy whitelist", ex);
|
||||
} catch (Exception ex) {
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
package com.github.games647.fastlogin.bukkit.listener.protocollib;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.events.PacketAdapter;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
|
||||
/**
|
||||
* Handles incoming encryption responses from connecting clients.
|
||||
* It prevents them from reaching the server because that cannot handle
|
||||
* it in offline mode.
|
||||
*
|
||||
* Moreover this manages a started premium check from
|
||||
* this plugin. So check if all data is correct and we can prove him as a
|
||||
* owner of a paid minecraft account.
|
||||
*
|
||||
* Receiving packet information:
|
||||
* http://wiki.vg/Protocol#Encryption_Response
|
||||
*
|
||||
* sharedSecret=encrypted byte array
|
||||
* verify token=encrypted byte array
|
||||
*/
|
||||
public class EncryptionPacketListener extends PacketAdapter {
|
||||
|
||||
//hides the inherit Plugin plugin field, but we need this type
|
||||
private final FastLoginBukkit plugin;
|
||||
|
||||
public EncryptionPacketListener(FastLoginBukkit plugin) {
|
||||
//run async in order to not block the server, because we make api calls to Mojang
|
||||
super(params(plugin, PacketType.Login.Client.ENCRYPTION_BEGIN).optionAsync());
|
||||
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* C->S : Handshake State=2
|
||||
* C->S : Login Start
|
||||
* S->C : Encryption Key Request
|
||||
* (Client Auth)
|
||||
* C->S : Encryption Key Response
|
||||
* (Server Auth, Both enable encryption)
|
||||
* S->C : Login Success (*)
|
||||
*
|
||||
* On offline logins is Login Start followed by Login Success
|
||||
*
|
||||
* Minecraft Server implementation
|
||||
* https://github.com/bergerkiller/CraftSource/blob/master/net.minecraft.server/LoginListener.java#L180
|
||||
*/
|
||||
@Override
|
||||
public void onPacketReceiving(PacketEvent packetEvent) {
|
||||
if (packetEvent.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Player sender = packetEvent.getPlayer();
|
||||
byte[] sharedSecret = packetEvent.getPacket().getByteArrays().read(0);
|
||||
|
||||
packetEvent.getAsyncMarker().incrementProcessingDelay();
|
||||
VerifyResponseTask verifyTask = new VerifyResponseTask(plugin, packetEvent, sender, sharedSecret);
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, verifyTask);
|
||||
}
|
||||
}
|
||||
@@ -42,8 +42,8 @@ public class LoginSkinApplyListener implements Listener {
|
||||
|
||||
if (plugin.getConfig().getBoolean("forwardSkin")) {
|
||||
//go through every session, because player.getAddress is null
|
||||
//loginEvent.getAddress is just a InetAddress not InetSocketAddres, so not unique enough
|
||||
for (BukkitLoginSession session : plugin.getSessions().values()) {
|
||||
//loginEvent.getAddress is just a InetAddress not InetSocketAddress, so not unique enough
|
||||
for (BukkitLoginSession session : plugin.getLoginSessions().values()) {
|
||||
if (session.getUsername().equals(player.getName())) {
|
||||
String signature = session.getSkinSignature();
|
||||
String skinData = session.getEncodedSkinData();
|
||||
|
||||
@@ -10,9 +10,11 @@ import com.github.games647.fastlogin.core.shared.JoinManagement;
|
||||
import java.util.Random;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class NameCheckTask extends JoinManagement<Player, ProtocolLibLoginSource> implements Runnable {
|
||||
public class NameCheckTask extends JoinManagement<Player, CommandSender, ProtocolLibLoginSource>
|
||||
implements Runnable {
|
||||
|
||||
private final FastLoginBukkit plugin;
|
||||
private final PacketEvent packetEvent;
|
||||
@@ -53,13 +55,13 @@ public class NameCheckTask extends JoinManagement<Player, ProtocolLibLoginSource
|
||||
}
|
||||
|
||||
String ip = player.getAddress().getAddress().getHostAddress();
|
||||
core.getPendingLogins().put(ip + username, new Object());
|
||||
core.getPendingLogin().put(ip + username, new Object());
|
||||
|
||||
String serverId = source.getServerId();
|
||||
byte[] verify = source.getVerifyToken();
|
||||
|
||||
BukkitLoginSession playerSession = new BukkitLoginSession(username, serverId, verify, registered, profile);
|
||||
plugin.getSessions().put(player.getAddress().toString(), playerSession);
|
||||
plugin.getLoginSessions().put(player.getAddress().toString(), playerSession);
|
||||
//cancel only if the player has a paid account otherwise login as normal offline player
|
||||
synchronized (packetEvent.getAsyncMarker().getProcessingLock()) {
|
||||
packetEvent.setCancelled(true);
|
||||
@@ -69,6 +71,6 @@ public class NameCheckTask extends JoinManagement<Player, ProtocolLibLoginSource
|
||||
@Override
|
||||
public void startCrackedSession(ProtocolLibLoginSource source, PlayerProfile profile, String username) {
|
||||
BukkitLoginSession loginSession = new BukkitLoginSession(username, profile);
|
||||
plugin.getSessions().put(player.getAddress().toString(), loginSession);
|
||||
plugin.getLoginSessions().put(player.getAddress().toString(), loginSession);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
package com.github.games647.fastlogin.bukkit.listener.protocollib;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
import com.comphenix.protocol.PacketType.Login.Client;
|
||||
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 java.util.Random;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class ProtocolLibListener extends PacketAdapter {
|
||||
|
||||
private static final int WORKER_THREADS = 3;
|
||||
|
||||
private final FastLoginBukkit plugin;
|
||||
//just create a new once on plugin enable. This used for verify token generation
|
||||
private final Random random = new Random();
|
||||
|
||||
public ProtocolLibListener(FastLoginBukkit plugin) {
|
||||
//run async in order to not block the server, because we are making api calls to Mojang
|
||||
super(params().plugin(plugin)
|
||||
.types(PacketType.Login.Client.START, PacketType.Login.Client.ENCRYPTION_BEGIN)
|
||||
.optionAsync());
|
||||
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
public static void register(FastLoginBukkit plugin) {
|
||||
ProtocolLibrary.getProtocolManager()
|
||||
.getAsynchronousManager()
|
||||
.registerAsyncHandler(new ProtocolLibListener(plugin))
|
||||
.start(WORKER_THREADS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketReceiving(PacketEvent packetEvent) {
|
||||
if (packetEvent.isCancelled()
|
||||
|| plugin.getCore().getAuthPluginHook()== null
|
||||
|| !plugin.isServerFullyStarted()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Player sender = packetEvent.getPlayer();
|
||||
PacketType packetType = packetEvent.getPacketType();
|
||||
if (packetType == Client.START) {
|
||||
onLogin(packetEvent, sender);
|
||||
} else {
|
||||
onEncryptionBegin(packetEvent, sender);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, 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.getLoginSessions().remove(sessionKey);
|
||||
|
||||
//player.getName() won't work at this state
|
||||
PacketContainer packet = packetEvent.getPacket();
|
||||
|
||||
String username = packet.getGameProfiles().read(0).getName();
|
||||
plugin.getLogger().log(Level.FINER, "Player {0} with {1} connecting", new Object[]{sessionKey, username});
|
||||
|
||||
packetEvent.getAsyncMarker().incrementProcessingDelay();
|
||||
Runnable nameCheckTask = new NameCheckTask(plugin, packetEvent, random, player, username);
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, nameCheckTask);
|
||||
}
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
package com.github.games647.fastlogin.bukkit.listener.protocollib;
|
||||
|
||||
import com.comphenix.protocol.PacketType;
|
||||
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 java.util.Random;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
/**
|
||||
* Handles incoming start packets from connecting clients. It
|
||||
* checks if we can start checking if the player is premium and
|
||||
* start a request to the client that it should start online mode
|
||||
* login.
|
||||
*
|
||||
* Receiving packet information:
|
||||
* http://wiki.vg/Protocol#Login_Start
|
||||
*
|
||||
* String=Username
|
||||
*/
|
||||
public class StartPacketListener extends PacketAdapter {
|
||||
|
||||
//hides the inherit Plugin plugin field, but we need a more detailed type than just Plugin
|
||||
private final FastLoginBukkit plugin;
|
||||
|
||||
//just create a new once on plugin enable. This used for verify token generation
|
||||
private final Random random = new Random();
|
||||
|
||||
public StartPacketListener(FastLoginBukkit plugin) {
|
||||
//run async in order to not block the server, because we are making api calls to Mojang
|
||||
super(params(plugin, PacketType.Login.Client.START).optionAsync());
|
||||
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* C->S : Handshake State=2
|
||||
* C->S : Login Start
|
||||
* S->C : Encryption Key Request
|
||||
* (Client Auth)
|
||||
* C->S : Encryption Key Response
|
||||
* (Server Auth, Both enable encryption)
|
||||
* S->C : Login Success (*)
|
||||
*
|
||||
* On offline logins is Login Start followed by Login Success
|
||||
*/
|
||||
@Override
|
||||
public void onPacketReceiving(PacketEvent packetEvent) {
|
||||
if (packetEvent.isCancelled()
|
||||
|| plugin.getCore().getAuthPluginHook()== null || !plugin.isServerFullyStarted()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Player player = packetEvent.getPlayer();
|
||||
|
||||
//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.getSessions().remove(sessionKey);
|
||||
|
||||
//player.getName() won't work at this state
|
||||
PacketContainer packet = packetEvent.getPacket();
|
||||
|
||||
String username = packet.getGameProfiles().read(0).getName();
|
||||
plugin.getLogger().log(Level.FINER, "Player {0} with {1} connecting", new Object[]{sessionKey, username});
|
||||
|
||||
packetEvent.getAsyncMarker().incrementProcessingDelay();
|
||||
NameCheckTask nameCheckTask = new NameCheckTask(plugin, packetEvent, random, player, username);
|
||||
Bukkit.getScheduler().runTaskAsynchronously(plugin, nameCheckTask);
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,6 @@ import com.comphenix.protocol.ProtocolLibrary;
|
||||
import com.comphenix.protocol.ProtocolManager;
|
||||
import com.comphenix.protocol.events.PacketContainer;
|
||||
import com.comphenix.protocol.events.PacketEvent;
|
||||
import com.comphenix.protocol.injector.netty.Injector;
|
||||
import com.comphenix.protocol.injector.server.TemporaryPlayerFactory;
|
||||
import com.comphenix.protocol.reflect.FieldUtils;
|
||||
import com.comphenix.protocol.reflect.FuzzyReflection;
|
||||
@@ -18,6 +17,7 @@ import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.math.BigInteger;
|
||||
import java.security.Key;
|
||||
import java.security.PrivateKey;
|
||||
import java.util.Arrays;
|
||||
import java.util.UUID;
|
||||
@@ -46,14 +46,11 @@ public class VerifyResponseTask implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
BukkitLoginSession session = plugin.getSessions().get(fromPlayer.getAddress().toString());
|
||||
BukkitLoginSession session = plugin.getLoginSessions().get(fromPlayer.getAddress().toString());
|
||||
if (session == null) {
|
||||
disconnect(plugin.getCore().getMessage("invalid-requst"), true
|
||||
disconnect(plugin.getCore().getMessage("invalid-request"), true
|
||||
, "Player {0} tried to send encryption response at invalid state", fromPlayer.getAddress());
|
||||
} else {
|
||||
String ip = fromPlayer.getAddress().getAddress().getHostAddress();
|
||||
plugin.getCore().getPendingLogins().remove(ip + session.getUsername());
|
||||
|
||||
verifyResponse(session);
|
||||
}
|
||||
} finally {
|
||||
@@ -84,8 +81,8 @@ public class VerifyResponseTask implements Runnable {
|
||||
String serverId = (new BigInteger(serverIdHash)).toString(16);
|
||||
|
||||
String username = session.getUsername();
|
||||
if (plugin.getCore().getApiConnector().hasJoinedServer(session, serverId)) {
|
||||
plugin.getLogger().log(Level.FINE, "Player {0} has a verified premium account", username);
|
||||
if (plugin.getCore().getApiConnector().hasJoinedServer(session, serverId, fromPlayer.getAddress())) {
|
||||
plugin.getLogger().log(Level.INFO, "Player {0} has a verified premium account", username);
|
||||
|
||||
session.setVerified(true);
|
||||
setPremiumUUID(session.getUuid());
|
||||
@@ -110,7 +107,7 @@ public class VerifyResponseTask implements Runnable {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean checkVerifyToken(BukkitLoginSession session, PrivateKey privateKey) {
|
||||
private boolean checkVerifyToken(BukkitLoginSession session, Key privateKey) {
|
||||
byte[] requestVerify = session.getVerifyToken();
|
||||
//encrypted verify token
|
||||
byte[] responseVerify = packetEvent.getPacket().getByteArrays().read(1);
|
||||
@@ -128,11 +125,12 @@ public class VerifyResponseTask implements Runnable {
|
||||
}
|
||||
|
||||
//try to get the networkManager from ProtocolLib
|
||||
private Object getNetworkManager() throws IllegalAccessException, NoSuchFieldException {
|
||||
private Object getNetworkManager() throws IllegalAccessException, NoSuchFieldException, ClassNotFoundException {
|
||||
Object injectorContainer = TemporaryPlayerFactory.getInjectorFromPlayer(fromPlayer);
|
||||
|
||||
//ChannelInjector
|
||||
Injector rawInjector = FuzzyReflection.getFieldValue(injectorContainer, Injector.class, true);
|
||||
Class<?> injectorClass = Class.forName("com.comphenix.protocol.injector.netty.Injector");
|
||||
Object rawInjector = FuzzyReflection.getFieldValue(injectorContainer, injectorClass, true);
|
||||
return FieldUtils.readField(rawInjector, "networkManager", true);
|
||||
}
|
||||
|
||||
@@ -172,7 +170,6 @@ public class VerifyResponseTask implements Runnable {
|
||||
|
||||
PacketContainer kickPacket = protocolManager.createPacket(PacketType.Login.Server.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
|
||||
@@ -180,7 +177,7 @@ public class VerifyResponseTask implements Runnable {
|
||||
//tell the server that we want to close the connection
|
||||
player.kickPlayer("Disconnect");
|
||||
} catch (InvocationTargetException ex) {
|
||||
plugin.getLogger().log(Level.SEVERE, "Error sending kickpacket", ex);
|
||||
plugin.getLogger().log(Level.SEVERE, "Error sending kick packet", ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,19 +2,20 @@ package com.github.games647.fastlogin.bukkit.listener.protocolsupport;
|
||||
|
||||
import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
|
||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||
import com.github.games647.fastlogin.core.shared.JoinManagement;
|
||||
import com.github.games647.fastlogin.core.PlayerProfile;
|
||||
import com.github.games647.fastlogin.core.shared.JoinManagement;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
|
||||
import protocolsupport.api.events.PlayerLoginStartEvent;
|
||||
import protocolsupport.api.events.PlayerPropertiesResolveEvent;
|
||||
|
||||
public class ProtocolSupportListener extends JoinManagement<Player, ProtocolLoginSource> implements Listener {
|
||||
public class ProtocolSupportListener extends JoinManagement<Player, CommandSender, ProtocolLoginSource>
|
||||
implements Listener {
|
||||
|
||||
private final FastLoginBukkit plugin;
|
||||
|
||||
@@ -26,7 +27,6 @@ public class ProtocolSupportListener extends JoinManagement<Player, ProtocolLogi
|
||||
|
||||
@EventHandler
|
||||
public void onLoginStart(PlayerLoginStartEvent loginStartEvent) {
|
||||
plugin.setServerStarted();
|
||||
if (loginStartEvent.isLoginDenied() || plugin.getCore().getAuthPluginHook() == null) {
|
||||
return;
|
||||
}
|
||||
@@ -35,7 +35,7 @@ public class ProtocolSupportListener extends JoinManagement<Player, ProtocolLogi
|
||||
InetSocketAddress address = loginStartEvent.getAddress();
|
||||
|
||||
//remove old data every time on a new login in order to keep the session only for one person
|
||||
plugin.getSessions().remove(address.toString());
|
||||
plugin.getLoginSessions().remove(address.toString());
|
||||
|
||||
super.onLogin(username, new ProtocolLoginSource(loginStartEvent));
|
||||
}
|
||||
@@ -43,13 +43,10 @@ public class ProtocolSupportListener extends JoinManagement<Player, ProtocolLogi
|
||||
@EventHandler
|
||||
public void onPropertiesResolve(PlayerPropertiesResolveEvent propertiesResolveEvent) {
|
||||
InetSocketAddress address = propertiesResolveEvent.getAddress();
|
||||
BukkitLoginSession session = plugin.getSessions().get(address.toString());
|
||||
BukkitLoginSession session = plugin.getLoginSessions().get(address.toString());
|
||||
|
||||
//skin was resolved -> premium player
|
||||
if (propertiesResolveEvent.hasProperty("textures") && session != null) {
|
||||
String ip = address.getAddress().getHostAddress();
|
||||
plugin.getCore().getPendingLogins().remove(ip + session.getUsername());
|
||||
|
||||
session.setVerified(true);
|
||||
}
|
||||
}
|
||||
@@ -59,10 +56,10 @@ public class ProtocolSupportListener extends JoinManagement<Player, ProtocolLogi
|
||||
source.setOnlineMode();
|
||||
|
||||
String ip = source.getAddress().getAddress().getHostAddress();
|
||||
plugin.getCore().getPendingLogins().put(ip + username, new Object());
|
||||
plugin.getCore().getPendingLogin().put(ip + username, new Object());
|
||||
|
||||
BukkitLoginSession playerSession = new BukkitLoginSession(username, null, null, registered, profile);
|
||||
plugin.getSessions().put(source.getAddress().toString(), playerSession);
|
||||
plugin.getLoginSessions().put(source.getAddress().toString(), playerSession);
|
||||
if (plugin.getConfig().getBoolean("premiumUuid")) {
|
||||
source.getLoginStartEvent().setUseOnlineModeUUID(true);
|
||||
}
|
||||
@@ -71,6 +68,6 @@ public class ProtocolSupportListener extends JoinManagement<Player, ProtocolLogi
|
||||
@Override
|
||||
public void startCrackedSession(ProtocolLoginSource source, PlayerProfile profile, String username) {
|
||||
BukkitLoginSession loginSession = new BukkitLoginSession(username, profile);
|
||||
plugin.getSessions().put(source.getAddress().toString(), loginSession);
|
||||
plugin.getLoginSessions().put(source.getAddress().toString(), loginSession);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,19 +26,24 @@ public class DelayedAuthHook implements Runnable {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
boolean hookFound = registerHooks();
|
||||
boolean hookFound = plugin.getCore().getAuthPluginHook() != null || registerHooks();
|
||||
if (plugin.isBungeeCord()) {
|
||||
plugin.getLogger().info("BungeeCord setting detected. No auth plugin is required");
|
||||
} else if (!hookFound) {
|
||||
plugin.getLogger().warning("No auth plugin were found by this plugin "
|
||||
+ "(other plugins could hook into this after the intialization of this plugin)"
|
||||
+ "and bungeecord is deactivated. "
|
||||
+ "(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.setServerStarted();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean registerHooks() {
|
||||
AuthPlugin<Player> authPluginHook = null;
|
||||
|
||||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Class<? extends AuthPlugin<Player>>> supportedHooks = Lists.newArrayList(AuthMeHook.class
|
||||
@@ -67,9 +72,8 @@ public class DelayedAuthHook implements Runnable {
|
||||
|
||||
if (plugin.getCore().getAuthPluginHook() == null) {
|
||||
plugin.getCore().setAuthPluginHook(authPluginHook);
|
||||
plugin.setServerStarted();
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,132 +2,67 @@ package com.github.games647.fastlogin.bukkit.tasks;
|
||||
|
||||
import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
|
||||
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
|
||||
import com.github.games647.fastlogin.core.AuthStorage;
|
||||
import com.github.games647.fastlogin.core.PlayerProfile;
|
||||
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
|
||||
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.google.common.io.ByteArrayDataOutput;
|
||||
import com.google.common.io.ByteStreams;
|
||||
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.metadata.FixedMetadataValue;
|
||||
|
||||
public class ForceLoginTask implements Runnable {
|
||||
public class ForceLoginTask extends ForceLoginManagement<Player, CommandSender, BukkitLoginSession, FastLoginBukkit> {
|
||||
|
||||
private final FastLoginBukkit plugin;
|
||||
private final Player player;
|
||||
|
||||
public ForceLoginTask(FastLoginBukkit plugin, Player player) {
|
||||
this.plugin = plugin;
|
||||
this.player = player;
|
||||
public ForceLoginTask(FastLoginCore<Player, CommandSender, FastLoginBukkit> core, Player player) {
|
||||
super(core, player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (!isOnlineThreadSafe()) {
|
||||
return;
|
||||
}
|
||||
|
||||
//remove the bungeecord identifier if there is ones
|
||||
String id = '/' + player.getAddress().getAddress().getHostAddress() + ':' + player.getAddress().getPort();
|
||||
BukkitLoginSession session = plugin.getSessions().remove(id);
|
||||
if (session == null) {
|
||||
return;
|
||||
}
|
||||
session = core.getPlugin().getLoginSessions().remove(id);
|
||||
|
||||
AuthStorage storage = plugin.getCore().getStorage();
|
||||
PlayerProfile playerProfile = session.getProfile();
|
||||
//blacklist this target player for BungeeCord Id brute force attacks
|
||||
FastLoginBukkit plugin = core.getPlugin();
|
||||
player.setMetadata(core.getPlugin().getName(), new FixedMetadataValue(plugin, true));
|
||||
|
||||
//check if it's the same player as we checked before
|
||||
if (session.isVerified() && player.getName().equals(session.getUsername())) {
|
||||
//premium player
|
||||
AuthPlugin<Player> authPlugin = plugin.getCore().getAuthPluginHook();
|
||||
if (authPlugin == null) {
|
||||
//maybe only bungeecord plugin
|
||||
sendSuccessNotification();
|
||||
} else {
|
||||
boolean success = false;
|
||||
if (isOnlineThreadSafe()) {
|
||||
if (plugin.getConfig().getBoolean("autoLogin")) {
|
||||
if (session.needsRegistration()) {
|
||||
success = forceRegister(authPlugin, player);
|
||||
} else {
|
||||
success = forceLogin(authPlugin, player);
|
||||
}
|
||||
} else {
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (success) {
|
||||
//update only on success to prevent corrupt data
|
||||
if (playerProfile != null) {
|
||||
playerProfile.setUuid(session.getUuid());
|
||||
//save cracked players too
|
||||
playerProfile.setPremium(true);
|
||||
storage.save(playerProfile);
|
||||
}
|
||||
|
||||
sendSuccessNotification();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//cracked player
|
||||
if (playerProfile != null) {
|
||||
playerProfile.setUuid(null);
|
||||
playerProfile.setPremium(false);
|
||||
storage.save(playerProfile);
|
||||
}
|
||||
}
|
||||
super.run();
|
||||
}
|
||||
|
||||
private boolean forceRegister(AuthPlugin<Player> authPlugin, Player player) {
|
||||
plugin.getLogger().log(Level.FINE, "Register player {0}", player.getName());
|
||||
|
||||
String generatedPassword = plugin.getCore().getPasswordGenerator().getRandomPassword(player);
|
||||
boolean success = authPlugin.forceRegister(player, generatedPassword);
|
||||
String message = plugin.getCore().getMessage("auto-register");
|
||||
if (success && message != null) {
|
||||
message = message.replace("%password", generatedPassword);
|
||||
player.sendMessage(message);
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
private boolean forceLogin(AuthPlugin<Player> authPlugin, Player player) {
|
||||
plugin.getLogger().log(Level.FINE, "Logging player {0} in", player.getName());
|
||||
boolean success = authPlugin.forceLogin(player);
|
||||
|
||||
String message = plugin.getCore().getMessage("auto-login");
|
||||
if (success && message != null) {
|
||||
player.sendMessage(message);
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
private void sendSuccessNotification() {
|
||||
if (plugin.isBungeeCord()) {
|
||||
@Override
|
||||
public void onForceActionSuccess(LoginSession session) {
|
||||
if (core.getPlugin().isBungeeCord()) {
|
||||
ByteArrayDataOutput dataOutput = ByteStreams.newDataOutput();
|
||||
dataOutput.writeUTF("SUCCESS");
|
||||
|
||||
player.sendPluginMessage(plugin, plugin.getName(), dataOutput.toByteArray());
|
||||
player.sendPluginMessage(core.getPlugin(), core.getPlugin().getName(), dataOutput.toByteArray());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isOnlineThreadSafe() {
|
||||
//the playerlist isn't thread-safe
|
||||
Future<Boolean> onlineFuture = Bukkit.getScheduler().callSyncMethod(plugin, player::isOnline);
|
||||
@Override
|
||||
public String getName(Player player) {
|
||||
return player.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOnline(Player player) {
|
||||
try {
|
||||
return onlineFuture.get();
|
||||
//the playerlist isn't thread-safe
|
||||
return Bukkit.getScheduler().callSyncMethod(core.getPlugin(), player::isOnline).get();
|
||||
} catch (InterruptedException | ExecutionException ex) {
|
||||
plugin.getLogger().log(Level.SEVERE, "Failed to perform thread-safe online check", ex);
|
||||
core.getPlugin().getLogger().log(Level.SEVERE, "Failed to perform thread-safe online check", ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOnlineMode() {
|
||||
return session.isVerified() && player.getName().equals(session.getUsername());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# project informations for Bukkit in order to register our plugin with all it components
|
||||
# project data for Bukkit in order to register our plugin with all it components
|
||||
# ${-} are variables from Maven (pom.xml) which will be replaced after the build
|
||||
name: ${project.parent.name}
|
||||
version: ${project.version}
|
||||
version: ${project.version}-git${git.commit.id}
|
||||
main: ${project.groupId}.${project.artifactId}.${project.name}
|
||||
|
||||
# meta informations for plugin managers
|
||||
# meta data for plugin managers
|
||||
authors: [games647, 'https://github.com/games647/FastLogin/graphs/contributors']
|
||||
description: |
|
||||
${project.description}
|
||||
@@ -18,6 +18,7 @@ softdepend:
|
||||
# We depend either ProtocolLib or ProtocolSupport
|
||||
- ProtocolSupport
|
||||
- ProtocolLib
|
||||
- PlaceholderAPI
|
||||
|
||||
commands:
|
||||
${project.parent.name}:
|
||||
@@ -32,11 +33,6 @@ commands:
|
||||
usage: /<command> [player]
|
||||
permission: ${project.artifactId}.command.cracked
|
||||
|
||||
import-auth:
|
||||
description: 'Imports the auth data from another auto login'
|
||||
usage: /<command> [player]
|
||||
permission: ${project.artifactId}.command.import
|
||||
|
||||
permissions:
|
||||
${project.artifactId}.command.premium:
|
||||
description: 'Label themselves as premium'
|
||||
@@ -54,4 +50,4 @@ permissions:
|
||||
${project.artifactId}.command.cracked.other:
|
||||
description: 'Label others as cracked'
|
||||
children:
|
||||
${project.artifactId}.command.cracked: true
|
||||
${project.artifactId}.command.cracked: true
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>com.github.games647</groupId>
|
||||
<artifactId>fastlogin</artifactId>
|
||||
<version>1.9</version>
|
||||
<version>1.10</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
<repositories>
|
||||
<!--BungeeCord with also the part outside the API-->
|
||||
<repository>
|
||||
<id>myplayplanet-REPO</id>
|
||||
<url>http://maven.myplayplanet.net/</url>
|
||||
<id>luck-repo</id>
|
||||
<url>https://ci.lucko.me/plugin/repository/everything</url>
|
||||
</repository>
|
||||
|
||||
<repository>
|
||||
@@ -40,14 +40,20 @@
|
||||
<dependency>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-proxy</artifactId>
|
||||
<version>1.8-SNAPSHOT</version>
|
||||
<version>1.12-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>me.vik1395</groupId>
|
||||
<artifactId>BungeeAuth</artifactId>
|
||||
<version>1.3.1</version>
|
||||
<version>1.4</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>*</groupId>
|
||||
<artifactId>*</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
package com.github.games647.fastlogin.bungee;
|
||||
|
||||
import com.github.games647.fastlogin.core.shared.FastLoginCore;
|
||||
import com.github.games647.fastlogin.core.shared.MojangApiConnector;
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.api.scheduler.GroupedThreadFactory;
|
||||
import net.md_5.bungee.config.Configuration;
|
||||
import net.md_5.bungee.config.ConfigurationProvider;
|
||||
import net.md_5.bungee.config.YamlConfiguration;
|
||||
|
||||
public class BungeeCore extends FastLoginCore<ProxiedPlayer> {
|
||||
|
||||
private static Map<String, Object> generateConfigMap(Configuration config) {
|
||||
return config.getKeys().stream()
|
||||
.filter(key -> config.get(key) != null)
|
||||
.collect(Collectors.toMap(key -> key, config::get));
|
||||
}
|
||||
|
||||
private final FastLoginBungee plugin;
|
||||
|
||||
public BungeeCore(FastLoginBungee plugin, Configuration config) {
|
||||
super(generateConfigMap(config));
|
||||
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getDataFolder() {
|
||||
return plugin.getDataFolder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Logger getLogger() {
|
||||
return plugin.getLogger();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
public ThreadFactory getThreadFactory() {
|
||||
String pluginName = plugin.getDescription().getName();
|
||||
return new ThreadFactoryBuilder()
|
||||
.setNameFormat(pluginName + " Database Pool Thread #%1$d")
|
||||
//Hikari create daemons by default
|
||||
.setDaemon(true)
|
||||
.setThreadFactory(new GroupedThreadFactory(plugin, pluginName))
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadMessages() {
|
||||
try {
|
||||
plugin.saveDefaultFile("messages.yml");
|
||||
ConfigurationProvider configProvider = ConfigurationProvider.getProvider(YamlConfiguration.class);
|
||||
|
||||
Configuration defaults = configProvider.load(getClass().getResourceAsStream("/messages.yml"));
|
||||
|
||||
File messageFile = new File(getDataFolder(), "messages.yml");
|
||||
Configuration messageConfig = configProvider.load(messageFile, defaults);
|
||||
|
||||
messageConfig.getKeys().forEach(key -> {
|
||||
String message = ChatColor.translateAlternateColorCodes('&', messageConfig.getString(key));
|
||||
if (!message.isEmpty()) {
|
||||
localeMessages.put(key, message);
|
||||
}
|
||||
});
|
||||
} catch (IOException ex) {
|
||||
getLogger().log(Level.SEVERE, "Failed to load messages", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public MojangApiConnector makeApiConnector(Logger logger, List<String> addresses, int requests) {
|
||||
return new MojangApiBungee(logger, addresses, requests);
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,29 @@
|
||||
package com.github.games647.fastlogin.bungee;
|
||||
|
||||
import com.github.games647.fastlogin.bungee.hooks.BungeeAuthHook;
|
||||
import com.github.games647.fastlogin.bungee.hooks.BungeeAuthPlugin;
|
||||
import com.github.games647.fastlogin.bungee.listener.PlayerConnectionListener;
|
||||
import com.github.games647.fastlogin.bungee.listener.PluginMessageListener;
|
||||
import com.github.games647.fastlogin.core.shared.FastLoginCore;
|
||||
import com.github.games647.fastlogin.core.shared.MojangApiConnector;
|
||||
import com.github.games647.fastlogin.core.shared.PlatformPlugin;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.io.Reader;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.logging.Level;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.function.Function;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
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.plugin.Plugin;
|
||||
import net.md_5.bungee.api.scheduler.GroupedThreadFactory;
|
||||
import net.md_5.bungee.config.Configuration;
|
||||
import net.md_5.bungee.config.ConfigurationProvider;
|
||||
import net.md_5.bungee.config.YamlConfiguration;
|
||||
@@ -22,42 +31,24 @@ import net.md_5.bungee.config.YamlConfiguration;
|
||||
/**
|
||||
* BungeeCord version of FastLogin. This plugin keeps track on online mode connections.
|
||||
*/
|
||||
public class FastLoginBungee extends Plugin {
|
||||
public class FastLoginBungee extends Plugin implements PlatformPlugin<CommandSender> {
|
||||
|
||||
private final ConcurrentMap<PendingConnection, BungeeLoginSession> session = Maps.newConcurrentMap();
|
||||
|
||||
private BungeeCore core;
|
||||
private Configuration config;
|
||||
private FastLoginCore<ProxiedPlayer, CommandSender, FastLoginBungee> core;
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
saveDefaultFile("config.yml");
|
||||
|
||||
try {
|
||||
File configFile = new File(getDataFolder(), "config.yml");
|
||||
ConfigurationProvider provider = ConfigurationProvider.getProvider(YamlConfiguration.class);
|
||||
Configuration defaults = provider.load(getResourceAsStream("config.yml"));
|
||||
config = provider.load(configFile, defaults);
|
||||
|
||||
core = new BungeeCore(this, config);
|
||||
if (!core.setupDatabase()) {
|
||||
return;
|
||||
}
|
||||
} catch (IOException ioExc) {
|
||||
getLogger().log(Level.SEVERE, "Error loading config. Disabling plugin...", ioExc);
|
||||
core = new FastLoginCore<>(this);
|
||||
core.load();
|
||||
if (!core.setupDatabase()) {
|
||||
return;
|
||||
}
|
||||
|
||||
core.setApiConnector();
|
||||
core.loadMessages();
|
||||
|
||||
//events
|
||||
getProxy().getPluginManager().registerListener(this, new PlayerConnectionListener(this));
|
||||
getProxy().getPluginManager().registerListener(this, new PluginMessageListener(this));
|
||||
|
||||
//bungee only commands
|
||||
getProxy().getPluginManager().registerCommand(this, new ImportCommand(this));
|
||||
|
||||
//this is required to listen to messages from the server
|
||||
getProxy().registerChannel(getDescription().getName());
|
||||
|
||||
@@ -71,55 +62,14 @@ public class FastLoginBungee extends Plugin {
|
||||
}
|
||||
}
|
||||
|
||||
public void saveDefaultFile(String fileName) {
|
||||
if (!getDataFolder().exists()) {
|
||||
getDataFolder().mkdir();
|
||||
}
|
||||
|
||||
File configFile = new File(getDataFolder(), fileName);
|
||||
if (!configFile.exists()) {
|
||||
InputStream in = getResourceAsStream(fileName);
|
||||
try {
|
||||
Files.copy(in, configFile.toPath());
|
||||
} catch (IOException ioExc) {
|
||||
getLogger().log(Level.SEVERE, "Error saving default " + fileName, ioExc);
|
||||
} finally {
|
||||
try {
|
||||
in.close();
|
||||
} catch (IOException ex) {
|
||||
getLogger().log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public BungeeCore getCore() {
|
||||
public FastLoginCore<ProxiedPlayer, CommandSender, FastLoginBungee> getCore() {
|
||||
return core;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void setAuthPluginHook(BungeeAuthPlugin authPlugin) {
|
||||
core.setAuthPluginHook(authPlugin);
|
||||
}
|
||||
|
||||
public Configuration getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
public ConcurrentMap<PendingConnection, BungeeLoginSession> getSession() {
|
||||
return session;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the auth plugin hook for BungeeCord
|
||||
*
|
||||
* @return the auth hook for BungeeCord. null if none found
|
||||
*/
|
||||
@Deprecated
|
||||
public BungeeAuthPlugin getBungeeAuthPlugin() {
|
||||
return (BungeeAuthPlugin) core.getAuthPluginHook();
|
||||
}
|
||||
|
||||
private void registerHook() {
|
||||
Plugin plugin = getProxy().getPluginManager().getPlugin("BungeeAuth");
|
||||
if (plugin != null) {
|
||||
@@ -127,4 +77,40 @@ public class FastLoginBungee extends Plugin {
|
||||
getLogger().info("Hooked into BungeeAuth");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return getDescription().getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> loadYamlFile(Reader reader) {
|
||||
ConfigurationProvider configProvider = ConfigurationProvider.getProvider(YamlConfiguration.class);
|
||||
Configuration config = configProvider.load(reader);
|
||||
return config.getKeys().stream()
|
||||
.filter(key -> config.get(key) != null)
|
||||
.collect(Collectors.toMap(Function.identity(), config::get));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(CommandSender receiver, String message) {
|
||||
receiver.sendMessage(TextComponent.fromLegacyText(message));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String translateColorCodes(char colorChar, String rawMessage) {
|
||||
return ChatColor.translateAlternateColorCodes(colorChar, rawMessage);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
public ThreadFactory getThreadFactory() {
|
||||
return new GroupedThreadFactory(this, getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public MojangApiConnector makeApiConnector(Logger logger, List<String> addresses, int requests
|
||||
, Map<String, Integer> proxies) {
|
||||
return new MojangApiBungee(logger, addresses, requests, proxies);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,91 +0,0 @@
|
||||
package com.github.games647.fastlogin.bungee;
|
||||
|
||||
import com.github.games647.fastlogin.core.AuthStorage;
|
||||
import com.github.games647.fastlogin.core.importer.ImportPlugin;
|
||||
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
import net.md_5.bungee.api.CommandSender;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import net.md_5.bungee.api.plugin.Command;
|
||||
|
||||
public class ImportCommand extends Command {
|
||||
|
||||
private final BungeeCore core;
|
||||
|
||||
public ImportCommand(FastLoginBungee plugin) {
|
||||
super("import-db", plugin.getDescription().getName().toLowerCase() + ".import");
|
||||
|
||||
this.core = plugin.getCore();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(CommandSender sender, String[] args) {
|
||||
if (args.length < 2) {
|
||||
String message = ChatColor.DARK_RED + "You need to specify the import plugin and database type";
|
||||
sender.sendMessage(convertFromLegacy(message));
|
||||
return;
|
||||
}
|
||||
|
||||
ImportPlugin importPlugin;
|
||||
switch (args[0].toLowerCase()) {
|
||||
case "autoin":
|
||||
importPlugin = ImportPlugin.AUTO_IN;
|
||||
break;
|
||||
case "bpa":
|
||||
importPlugin = ImportPlugin.BPA;
|
||||
break;
|
||||
case "eldzi":
|
||||
importPlugin = ImportPlugin.ELDZI;
|
||||
break;
|
||||
default:
|
||||
String message = ChatColor.DARK_RED + "Unknown auto login plugin";
|
||||
sender.sendMessage(convertFromLegacy(message));
|
||||
return;
|
||||
}
|
||||
|
||||
boolean sqlite;
|
||||
switch (args[1].toLowerCase()) {
|
||||
case "sqlite":
|
||||
sqlite = true;
|
||||
break;
|
||||
case "mysql":
|
||||
sqlite = false;
|
||||
break;
|
||||
default:
|
||||
String message = ChatColor.DARK_RED + "Unknown storage type to import from. Either SQLite or MySQL";
|
||||
sender.sendMessage(convertFromLegacy(message));
|
||||
return;
|
||||
}
|
||||
|
||||
String host = "";
|
||||
String database = "";
|
||||
String username = "";
|
||||
String password = "";
|
||||
if (!sqlite) {
|
||||
if (args.length <= 5) {
|
||||
String message = ChatColor.DARK_RED + "If importing from MySQL, you need to specify host database "
|
||||
+ "and username passowrd too";
|
||||
sender.sendMessage(convertFromLegacy(message));
|
||||
return;
|
||||
}
|
||||
|
||||
host = args[2];
|
||||
database = args[3];
|
||||
username = args[4];
|
||||
password = args[5];
|
||||
}
|
||||
|
||||
AuthStorage storage = core.getStorage();
|
||||
boolean success = core.importDatabase(importPlugin, true, storage, host, database, username, password);
|
||||
if (success) {
|
||||
sender.sendMessage(convertFromLegacy(ChatColor.DARK_GREEN + "Successful imported the data"));
|
||||
} else {
|
||||
sender.sendMessage(convertFromLegacy(ChatColor.DARK_RED + "Failed to import the data. Check out the logs"));
|
||||
}
|
||||
}
|
||||
|
||||
private BaseComponent[] convertFromLegacy(String message) {
|
||||
return TextComponent.fromLegacyText(message);
|
||||
}
|
||||
}
|
||||
@@ -3,15 +3,17 @@ package com.github.games647.fastlogin.bungee;
|
||||
import com.github.games647.fastlogin.core.shared.LoginSession;
|
||||
import com.github.games647.fastlogin.core.shared.MojangApiConnector;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import net.md_5.bungee.BungeeCord;
|
||||
|
||||
public class MojangApiBungee extends MojangApiConnector {
|
||||
|
||||
public MojangApiBungee(Logger logger, List<String> localAddresses, int rateLimit) {
|
||||
super(logger, localAddresses, rateLimit);
|
||||
public MojangApiBungee(Logger logger, List<String> localAddresses, int rateLimit, Map<String, Integer> proxies) {
|
||||
super(logger, localAddresses, rateLimit, proxies);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -29,12 +31,12 @@ public class MojangApiBungee extends MojangApiConnector {
|
||||
if ("null".equals(id)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasJoinedServer(LoginSession session, String serverId) {
|
||||
public boolean hasJoinedServer(LoginSession session, String serverId, InetSocketAddress ip) {
|
||||
//this is not needed in Bungee
|
||||
throw new UnsupportedOperationException("Not supported");
|
||||
}
|
||||
|
||||
@@ -2,21 +2,13 @@ package com.github.games647.fastlogin.bungee.hooks;
|
||||
|
||||
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Random;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import me.vik1395.BungeeAuth.ListenerClass;
|
||||
import me.vik1395.BungeeAuth.Main;
|
||||
import me.vik1395.BungeeAuth.Password.PasswordHandler;
|
||||
import me.vik1395.BungeeAuth.Tables;
|
||||
import me.vik1395.BungeeAuthAPI.RequestHandler;
|
||||
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
|
||||
/**
|
||||
* Github: https://github.com/MatteCarra/BungeeAuth
|
||||
* Github: https://github.com/vik1395/BungeeAuth-Minecraft
|
||||
*
|
||||
* Project page:
|
||||
*
|
||||
@@ -24,88 +16,25 @@ import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
*/
|
||||
public class BungeeAuthHook implements AuthPlugin<ProxiedPlayer> {
|
||||
|
||||
//https://github.com/MatteCarra/BungeeAuth/blob/master/src/me/vik1395/BungeeAuth/Login.java#L32
|
||||
private final Tables databaseConnection = new Tables();
|
||||
private final RequestHandler requestHandler = new RequestHandler();
|
||||
|
||||
@Override
|
||||
public boolean forceLogin(ProxiedPlayer player) {
|
||||
String playerName = player.getName();
|
||||
//https://github.com/MatteCarra/BungeeAuth/blob/master/src/me/vik1395/BungeeAuth/Login.java#L92-95
|
||||
if (Main.plonline.contains(playerName)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Main.plonline.add(playerName);
|
||||
|
||||
//renamed from ct to databaseConnection
|
||||
// databaseConnection.setStatus(player.getName(), "online");
|
||||
Class<?>[] parameterTypes = new Class<?>[]{String.class, String.class};
|
||||
Object[] arguments = new Object[]{playerName, "online"};
|
||||
|
||||
try {
|
||||
callProtected("setStatus", parameterTypes, arguments);
|
||||
ListenerClass.movePlayer(player, false);
|
||||
|
||||
//proparly not thread-safe
|
||||
ListenerClass.prelogin.get(playerName).cancel();
|
||||
} catch (Exception ex) {
|
||||
Main.plugin.getLogger().log(Level.SEVERE, "Error force loging in player", ex);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return requestHandler.forceLogin(playerName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRegistered(String playerName) throws Exception {
|
||||
//https://github.com/MatteCarra/BungeeAuth/blob/master/src/me/vik1395/BungeeAuth/Register.java#L46
|
||||
//renamed t to databaseConnection
|
||||
return databaseConnection.checkPlayerEntry(playerName);
|
||||
return requestHandler.isRegistered(playerName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean forceRegister(ProxiedPlayer player, String password) {
|
||||
//https://github.com/MatteCarra/BungeeAuth/blob/master/src/me/vik1395/BungeeAuth/Register.java#L102
|
||||
PasswordHandler ph = new PasswordHandler();
|
||||
Random rand = new Random();
|
||||
int maxp = 7; //Total Password Hashing methods.
|
||||
Date dNow = new Date();
|
||||
SimpleDateFormat ft = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss");
|
||||
|
||||
String Pw = password;
|
||||
String pType = "" + rand.nextInt(maxp + 1);
|
||||
String regdate = ft.format(dNow);
|
||||
//https://github.com/MatteCarra/BungeeAuth/blob/master/src/me/vik1395/BungeeAuth/Register.java#L60
|
||||
String lastip = player.getAddress().getAddress().getHostAddress();
|
||||
String lastseen = regdate;
|
||||
String hash = ph.newHash(Pw, pType);
|
||||
|
||||
//creates a new SQL entry with the player's details.
|
||||
Class<?>[] parameterTypes = new Class<?>[] {String.class, String.class, String.class, String.class
|
||||
, String.class, String.class, String.class, String.class};
|
||||
Object[] arguments = new Object[] {player.getName(), hash, pType, "", lastip, regdate, lastip, lastseen};
|
||||
|
||||
try {
|
||||
|
||||
callProtected("newPlayerEntry", parameterTypes, arguments);
|
||||
//proparly not thread-safe
|
||||
forceLogin(player);
|
||||
} catch (Exception ex) {
|
||||
Main.plugin.getLogger().log(Level.SEVERE, "[BungeeAuth] Error when creating a new player in the Database", ex);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//pail ;(
|
||||
private void callProtected(String methodName, Class<?>[] parameterTypes, Object[] arguments) throws Exception {
|
||||
Class<Tables> tableClass = Tables.class;
|
||||
|
||||
Method method = tableClass.getDeclaredMethod(methodName, parameterTypes);
|
||||
method.setAccessible(true);
|
||||
//renamed t to databaseConnection
|
||||
//databaseConnection.newPlayerEntry(player.getName(), hash, pType, "", lastip, regdate, lastip, lastseen);
|
||||
method.invoke(databaseConnection, arguments);
|
||||
return requestHandler.forceRegister(player, password);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
package com.github.games647.fastlogin.bungee.hooks;
|
||||
|
||||
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
|
||||
/**
|
||||
* @deprecated please use com.github.games647.fastlogin.core.hooks.AuthPlugin<net.md_5.bungee.api.connection.ProxiedPlayer>
|
||||
*/
|
||||
@Deprecated
|
||||
public interface BungeeAuthPlugin extends AuthPlugin<ProxiedPlayer> {
|
||||
|
||||
@Override
|
||||
boolean forceLogin(ProxiedPlayer player);
|
||||
|
||||
@Override
|
||||
boolean isRegistered(String playerName) throws Exception;
|
||||
|
||||
@Override
|
||||
boolean forceRegister(ProxiedPlayer player, String password);
|
||||
}
|
||||
@@ -14,6 +14,7 @@ import java.util.logging.Level;
|
||||
import net.md_5.bungee.api.ProxyServer;
|
||||
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;
|
||||
@@ -47,24 +48,23 @@ public class PlayerConnectionListener implements Listener {
|
||||
preLoginEvent.registerIntent(plugin);
|
||||
|
||||
PendingConnection connection = preLoginEvent.getConnection();
|
||||
AsyncPremiumCheck asyncPremiumCheck = new AsyncPremiumCheck(plugin, preLoginEvent, connection);
|
||||
Runnable asyncPremiumCheck = new AsyncPremiumCheck(plugin, preLoginEvent, connection);
|
||||
ProxyServer.getInstance().getScheduler().runAsync(plugin, asyncPremiumCheck);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOW)
|
||||
@EventHandler(priority = EventPriority.LOWEST)
|
||||
public void onLogin(LoginEvent loginEvent) {
|
||||
if (loginEvent.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
//use the loginevent instead of the postlogin event in order to send the loginsuccess packet to the client
|
||||
//use the login event instead of the postlogin event in order to send the loginsuccess packet to the client
|
||||
//with the offline uuid this makes it possible to set the skin then
|
||||
PendingConnection connection = loginEvent.getConnection();
|
||||
String username = connection.getName();
|
||||
if (connection.isOnlineMode()) {
|
||||
String ip = connection.getAddress().getAddress().getHostAddress();
|
||||
plugin.getCore().getPendingLogins().remove(ip + username);
|
||||
InitialHandler initialHandler = (InitialHandler) connection;
|
||||
|
||||
String username = initialHandler.getLoginRequest().getData();
|
||||
if (connection.isOnlineMode()) {
|
||||
LoginSession session = plugin.getSession().get(connection);
|
||||
session.setUuid(connection.getUniqueId());
|
||||
|
||||
@@ -72,8 +72,7 @@ public class PlayerConnectionListener implements Listener {
|
||||
playerProfile.setUuid(connection.getUniqueId());
|
||||
|
||||
//bungeecord will do this automatically so override it on disabled option
|
||||
InitialHandler initialHandler = (InitialHandler) connection;
|
||||
if (!plugin.getConfig().getBoolean("premiumUuid")) {
|
||||
if (!plugin.getCore().getConfig().get("premiumUuid", true)) {
|
||||
try {
|
||||
UUID offlineUUID = UUID.nameUUIDFromBytes(("OfflinePlayer:" + username).getBytes(Charsets.UTF_8));
|
||||
|
||||
@@ -87,7 +86,7 @@ public class PlayerConnectionListener implements Listener {
|
||||
}
|
||||
}
|
||||
|
||||
if (!plugin.getConfig().getBoolean("forwardSkin")) {
|
||||
if (!plugin.getCore().getConfig().get("forwardSkin", true)) {
|
||||
//this is null on offline mode
|
||||
LoginResult loginProfile = initialHandler.getLoginProfile();
|
||||
if (loginProfile != null) {
|
||||
@@ -100,7 +99,9 @@ public class PlayerConnectionListener implements Listener {
|
||||
@EventHandler
|
||||
public void onServerConnected(ServerConnectedEvent serverConnectedEvent) {
|
||||
ProxiedPlayer player = serverConnectedEvent.getPlayer();
|
||||
ForceLoginTask loginTask = new ForceLoginTask(plugin, player, serverConnectedEvent.getServer());
|
||||
Server server = serverConnectedEvent.getServer();
|
||||
|
||||
Runnable loginTask = new ForceLoginTask(plugin.getCore(), player, server);
|
||||
ProxyServer.getInstance().getScheduler().runAsync(plugin, loginTask);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,11 +4,13 @@ import com.github.games647.fastlogin.bungee.BungeeLoginSession;
|
||||
import com.github.games647.fastlogin.bungee.FastLoginBungee;
|
||||
import com.github.games647.fastlogin.bungee.tasks.AsyncToggleMessage;
|
||||
import com.github.games647.fastlogin.core.PlayerProfile;
|
||||
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.ProxyServer;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
@@ -47,30 +49,34 @@ public class PluginMessageListener implements Listener {
|
||||
}
|
||||
|
||||
private void readMessage(ProxiedPlayer forPlayer, byte[] data) {
|
||||
FastLoginCore<ProxiedPlayer, CommandSender, FastLoginBungee> core = plugin.getCore();
|
||||
|
||||
ByteArrayDataInput dataInput = ByteStreams.newDataInput(data);
|
||||
String subchannel = dataInput.readUTF();
|
||||
if ("ON".equals(subchannel)) {
|
||||
if ("SUCCESS".equals(subchannel)) {
|
||||
onSuccessMessage(forPlayer);
|
||||
} else if ("ON".equals(subchannel)) {
|
||||
String playerName = dataInput.readUTF();
|
||||
boolean isPlayerSender = dataInput.readBoolean();
|
||||
|
||||
if (playerName.equals(forPlayer.getName()) && plugin.getConfig().getBoolean("premium-warning")
|
||||
&& !plugin.getCore().getPendingConfirms().contains(forPlayer.getUniqueId())) {
|
||||
String message = plugin.getCore().getMessage("premium-warning");
|
||||
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));
|
||||
plugin.getCore().getPendingConfirms().add(forPlayer.getUniqueId());
|
||||
core.getPendingConfirms().add(forPlayer.getUniqueId());
|
||||
return;
|
||||
}
|
||||
|
||||
plugin.getCore().getPendingConfirms().remove(forPlayer.getUniqueId());
|
||||
AsyncToggleMessage task = new AsyncToggleMessage(plugin.getCore(), forPlayer, playerName, true);
|
||||
core.getPendingConfirms().remove(forPlayer.getUniqueId());
|
||||
Runnable task = new AsyncToggleMessage(core, forPlayer, playerName, true, isPlayerSender);
|
||||
ProxyServer.getInstance().getScheduler().runAsync(plugin, task);
|
||||
} else if ("OFF".equals(subchannel)) {
|
||||
String playerName = dataInput.readUTF();
|
||||
boolean isPlayerSender = dataInput.readBoolean();
|
||||
|
||||
AsyncToggleMessage task = new AsyncToggleMessage(plugin.getCore(), forPlayer, playerName, false);
|
||||
Runnable task = new AsyncToggleMessage(core, forPlayer, playerName, false, isPlayerSender);
|
||||
ProxyServer.getInstance().getScheduler().runAsync(plugin, task);
|
||||
} else if ("SUCCESS".equals(subchannel)) {
|
||||
onSuccessMessage(forPlayer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void onSuccessMessage(ProxiedPlayer forPlayer) {
|
||||
|
||||
@@ -6,11 +6,14 @@ import com.github.games647.fastlogin.bungee.FastLoginBungee;
|
||||
import com.github.games647.fastlogin.core.PlayerProfile;
|
||||
import com.github.games647.fastlogin.core.shared.JoinManagement;
|
||||
|
||||
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.AsyncEvent;
|
||||
import net.md_5.bungee.connection.InitialHandler;
|
||||
|
||||
public class AsyncPremiumCheck extends JoinManagement<ProxiedPlayer, BungeeLoginSource> implements Runnable {
|
||||
public class AsyncPremiumCheck extends JoinManagement<ProxiedPlayer, CommandSender, BungeeLoginSource>
|
||||
implements Runnable {
|
||||
|
||||
private final FastLoginBungee plugin;
|
||||
private final AsyncEvent<?> preLoginEvent;
|
||||
@@ -29,7 +32,8 @@ public class AsyncPremiumCheck extends JoinManagement<ProxiedPlayer, BungeeLogin
|
||||
public void run() {
|
||||
plugin.getSession().remove(connection);
|
||||
|
||||
String username = connection.getName();
|
||||
InitialHandler initialHandler = (InitialHandler) connection;
|
||||
String username = initialHandler.getLoginRequest().getData();
|
||||
try {
|
||||
super.onLogin(username, new BungeeLoginSource(connection));
|
||||
} finally {
|
||||
@@ -43,7 +47,7 @@ public class AsyncPremiumCheck extends JoinManagement<ProxiedPlayer, BungeeLogin
|
||||
plugin.getSession().put(source.getConnection(), new BungeeLoginSession(username, registered, profile));
|
||||
|
||||
String ip = source.getAddress().getAddress().getHostAddress();
|
||||
plugin.getCore().getPendingLogins().put(ip + username, new Object());
|
||||
plugin.getCore().getPendingLogin().put(ip + username, new Object());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,23 +1,29 @@
|
||||
package com.github.games647.fastlogin.bungee.tasks;
|
||||
|
||||
import com.github.games647.fastlogin.bungee.BungeeCore;
|
||||
import com.github.games647.fastlogin.bungee.FastLoginBungee;
|
||||
import com.github.games647.fastlogin.core.PlayerProfile;
|
||||
import com.github.games647.fastlogin.core.shared.FastLoginCore;
|
||||
|
||||
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 BungeeCore core;
|
||||
private final ProxiedPlayer fromPlayer;
|
||||
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(BungeeCore core, ProxiedPlayer fromPlayer, String targetPlayer, boolean toPremium) {
|
||||
public AsyncToggleMessage(FastLoginCore<ProxiedPlayer, CommandSender, FastLoginBungee> core,
|
||||
ProxiedPlayer sender, String playerName, boolean toPremium, boolean playerSender) {
|
||||
this.core = core;
|
||||
this.fromPlayer = fromPlayer;
|
||||
this.targetPlayer = targetPlayer;
|
||||
this.sender = sender;
|
||||
this.targetPlayer = playerName;
|
||||
this.toPremium = toPremium;
|
||||
this.isPlayerSender = playerSender;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -33,25 +39,35 @@ public class AsyncToggleMessage implements Runnable {
|
||||
PlayerProfile playerProfile = core.getStorage().loadProfile(targetPlayer);
|
||||
//existing player is already cracked
|
||||
if (playerProfile.getUserId() != -1 && !playerProfile.isPremium()) {
|
||||
fromPlayer.sendMessage(TextComponent.fromLegacyText(core.getMessage("not-premium")));
|
||||
sendMessage("not-premium");
|
||||
return;
|
||||
}
|
||||
|
||||
playerProfile.setPremium(false);
|
||||
playerProfile.setUuid(null);
|
||||
core.getStorage().save(playerProfile);
|
||||
fromPlayer.sendMessage(TextComponent.fromLegacyText(core.getMessage("remove-premium")));
|
||||
sendMessage("remove-premium");
|
||||
}
|
||||
|
||||
private void activatePremium() {
|
||||
PlayerProfile playerProfile = core.getStorage().loadProfile(targetPlayer);
|
||||
if (playerProfile.isPremium()) {
|
||||
fromPlayer.sendMessage(TextComponent.fromLegacyText(core.getMessage("already-exists")));
|
||||
sendMessage("already-exists");
|
||||
return;
|
||||
}
|
||||
|
||||
playerProfile.setPremium(true);
|
||||
core.getStorage().save(playerProfile);
|
||||
fromPlayer.sendMessage(TextComponent.fromLegacyText(core.getMessage("add-premium")));
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,119 +2,70 @@ package com.github.games647.fastlogin.bungee.tasks;
|
||||
|
||||
import com.github.games647.fastlogin.bungee.BungeeLoginSession;
|
||||
import com.github.games647.fastlogin.bungee.FastLoginBungee;
|
||||
import com.github.games647.fastlogin.core.PlayerProfile;
|
||||
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
|
||||
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.google.common.io.ByteArrayDataOutput;
|
||||
import com.google.common.io.ByteStreams;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
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.connection.Server;
|
||||
|
||||
public class ForceLoginTask implements Runnable {
|
||||
public class ForceLoginTask extends ForceLoginManagement<ProxiedPlayer, CommandSender, BungeeLoginSession, FastLoginBungee> {
|
||||
|
||||
private final FastLoginBungee plugin;
|
||||
private final ProxiedPlayer player;
|
||||
private final Server server;
|
||||
|
||||
public ForceLoginTask(FastLoginBungee plugin, ProxiedPlayer player, Server server) {
|
||||
this.plugin = plugin;
|
||||
this.player = player;
|
||||
public ForceLoginTask(FastLoginCore<ProxiedPlayer, CommandSender, FastLoginBungee> core,
|
||||
ProxiedPlayer player, Server server) {
|
||||
super(core, player);
|
||||
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
PendingConnection pendingConnection = player.getPendingConnection();
|
||||
BungeeLoginSession session = plugin.getSession().get(pendingConnection);
|
||||
PendingConnection pendingConnection = player.getPendingConnection();
|
||||
session = core.getPlugin().getSession().get(pendingConnection);
|
||||
|
||||
if (session == null || !player.isConnected()) {
|
||||
plugin.getLogger().log(Level.FINE, "Invalid session player {0} propaly left the server", player);
|
||||
return;
|
||||
}
|
||||
|
||||
PlayerProfile playerProfile = session.getProfile();
|
||||
|
||||
//force login only on success
|
||||
if (pendingConnection.isOnlineMode()) {
|
||||
boolean autoRegister = session.needsRegistration();
|
||||
|
||||
//2fa authentication - no need to send bukkit force login notification and so we also don't need
|
||||
// to wait for a response -> save immediatly
|
||||
if (!plugin.getConfig().getBoolean("autoLogin")) {
|
||||
playerProfile.setPremium(true);
|
||||
plugin.getCore().getStorage().save(playerProfile);
|
||||
session.setAlreadySaved(true);
|
||||
}
|
||||
|
||||
AuthPlugin<ProxiedPlayer> authPlugin = plugin.getCore().getAuthPluginHook();
|
||||
if (authPlugin == null) {
|
||||
//save will happen on success message from bukkit
|
||||
sendBukkitLoginNotification(autoRegister);
|
||||
} else if (session.needsRegistration()) {
|
||||
forceRegister(session, authPlugin);
|
||||
} else if (authPlugin.forceLogin(player)) {
|
||||
forceLogin(session, authPlugin);
|
||||
}
|
||||
} else {
|
||||
//cracked player
|
||||
if (!session.isAlreadySaved()) {
|
||||
playerProfile.setPremium(false);
|
||||
plugin.getCore().getStorage().save(playerProfile);
|
||||
session.setAlreadySaved(true);
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
plugin.getLogger().log(Level.INFO, "ERROR ON FORCE LOGIN", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void forceRegister(BungeeLoginSession session, AuthPlugin<ProxiedPlayer> authPlugin) {
|
||||
if (session.isAlreadyLogged()) {
|
||||
sendBukkitLoginNotification(true);
|
||||
if (session == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
session.setAlreadyLogged(true);
|
||||
super.run();
|
||||
|
||||
String password = plugin.getCore().getPasswordGenerator().getRandomPassword(player);
|
||||
if (authPlugin.forceRegister(player, password)) {
|
||||
//save will happen on success message from bukkit
|
||||
sendBukkitLoginNotification(true);
|
||||
String message = plugin.getCore().getMessage("auto-register");
|
||||
if (message != null) {
|
||||
message = message.replace("%password", password);
|
||||
player.sendMessage(TextComponent.fromLegacyText(message));
|
||||
}
|
||||
if (!isOnlineMode()) {
|
||||
session.setAlreadySaved(true);
|
||||
}
|
||||
}
|
||||
|
||||
private void forceLogin(BungeeLoginSession session, AuthPlugin<ProxiedPlayer> authPlugin) {
|
||||
@Override
|
||||
public boolean forceLogin(ProxiedPlayer player) {
|
||||
if (session.isAlreadyLogged()) {
|
||||
sendBukkitLoginNotification(false);
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
session.setAlreadyLogged(true);
|
||||
if (authPlugin.forceLogin(player)) {
|
||||
//save will happen on success message from bukkit
|
||||
sendBukkitLoginNotification(false);
|
||||
String message = plugin.getCore().getMessage("auto-login");
|
||||
if (message != null) {
|
||||
player.sendMessage(TextComponent.fromLegacyText(message));
|
||||
}
|
||||
}
|
||||
return super.forceLogin(player);
|
||||
}
|
||||
|
||||
private void sendBukkitLoginNotification(boolean autoRegister) {
|
||||
@Override
|
||||
public boolean forceRegister(ProxiedPlayer player) {
|
||||
if (session.isAlreadyLogged()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.forceRegister(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onForceActionSuccess(LoginSession session) {
|
||||
ByteArrayDataOutput dataOutput = ByteStreams.newDataOutput();
|
||||
//subchannel name
|
||||
if (autoRegister) {
|
||||
//sub channel name
|
||||
if (session.needsRegistration()) {
|
||||
dataOutput.writeUTF("AUTO_REGISTER");
|
||||
} else {
|
||||
dataOutput.writeUTF("AUTO_LOGIN");
|
||||
@@ -124,12 +75,27 @@ public class ForceLoginTask implements Runnable {
|
||||
dataOutput.writeUTF(player.getName());
|
||||
|
||||
//proxy identifier to check if it's a acceptable proxy
|
||||
UUID proxyId = UUID.fromString(plugin.getProxy().getConfig().getUuid());
|
||||
UUID proxyId = UUID.fromString(core.getPlugin().getProxy().getConfig().getUuid());
|
||||
dataOutput.writeLong(proxyId.getMostSignificantBits());
|
||||
dataOutput.writeLong(proxyId.getLeastSignificantBits());
|
||||
|
||||
if (server != null) {
|
||||
server.sendData(plugin.getDescription().getName(), dataOutput.toByteArray());
|
||||
server.sendData(core.getPlugin().getName(), dataOutput.toByteArray());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName(ProxiedPlayer player) {
|
||||
return player.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOnline(ProxiedPlayer player) {
|
||||
return player.isConnected();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOnlineMode() {
|
||||
return player.getPendingConnection().isOnlineMode();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
# project informations for BungeeCord
|
||||
# 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}
|
||||
version: ${project.version}-git${git.commit.id}
|
||||
author: games647, http://github.com/games647/FastLogin/graphs/contributors
|
||||
|
||||
softdepend:
|
||||
softDepends:
|
||||
# BungeeCord auth plugins
|
||||
- BungeeAuth
|
||||
|
||||
description: |
|
||||
${project.description}
|
||||
${project.description}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>com.github.games647</groupId>
|
||||
<artifactId>fastlogin</artifactId>
|
||||
<version>1.9</version>
|
||||
<version>1.10</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -19,16 +19,16 @@
|
||||
<dependency>
|
||||
<groupId>com.zaxxer</groupId>
|
||||
<artifactId>HikariCP</artifactId>
|
||||
<version>2.5.0</version>
|
||||
<version>2.6.3</version>
|
||||
</dependency>
|
||||
|
||||
<!--Logging framework implements slf4j which is required by hikari-->
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-jdk14</artifactId>
|
||||
<version>1.7.21</version>
|
||||
<version>1.7.22</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.github.games647.fastlogin.core;
|
||||
|
||||
import com.github.games647.fastlogin.core.shared.FastLoginCore;
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
import com.zaxxer.hikari.HikariConfig;
|
||||
import com.zaxxer.hikari.HikariDataSource;
|
||||
|
||||
@@ -9,46 +10,60 @@ import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.Calendar;
|
||||
import java.util.Locale;
|
||||
import java.util.Properties;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
public class AuthStorage {
|
||||
|
||||
private static final String PREMIUM_TABLE = "premium";
|
||||
|
||||
private final FastLoginCore<?> core;
|
||||
private final FastLoginCore<?, ?, ?> core;
|
||||
private final HikariDataSource dataSource;
|
||||
|
||||
//a try to fix https://www.spigotmc.org/threads/fastlogin.101192/page-26#post-1874647
|
||||
private final Calendar calendar = Calendar.getInstance(Locale.US);
|
||||
|
||||
public AuthStorage(FastLoginCore<?> core, String driver, String host, int port, String databasePath
|
||||
, String user, String pass) {
|
||||
public AuthStorage(FastLoginCore<?, ?, ?> core, String driver, String host, int port, String databasePath
|
||||
, String user, String pass, boolean useSSL) {
|
||||
this.core = core;
|
||||
|
||||
HikariConfig databaseConfig = new HikariConfig();
|
||||
databaseConfig.setUsername(user);
|
||||
databaseConfig.setPassword(pass);
|
||||
databaseConfig.setDriverClassName(driver);
|
||||
databaseConfig.setThreadFactory(core.getThreadFactory());
|
||||
HikariConfig config = new HikariConfig();
|
||||
config.setUsername(user);
|
||||
config.setPassword(pass);
|
||||
config.setDriverClassName(driver);
|
||||
|
||||
databasePath = databasePath.replace("{pluginDir}", core.getDataFolder().getAbsolutePath());
|
||||
//a try to fix https://www.spigotmc.org/threads/fastlogin.101192/page-26#post-1874647
|
||||
Properties properties = new Properties();
|
||||
properties.setProperty("date_string_format", "yyyy-MM-dd HH:mm:ss");
|
||||
properties.setProperty("useSSL", String.valueOf(useSSL));
|
||||
config.setDataSourceProperties(properties);
|
||||
|
||||
ThreadFactory platformThreadFactory = core.getPlugin().getThreadFactory();
|
||||
if (platformThreadFactory != null) {
|
||||
config.setThreadFactory(new ThreadFactoryBuilder()
|
||||
.setNameFormat(core.getPlugin().getName() + " Database Pool Thread #%1$d")
|
||||
//Hikari create daemons by default
|
||||
.setDaemon(true)
|
||||
.setThreadFactory(platformThreadFactory)
|
||||
.build());
|
||||
}
|
||||
|
||||
databasePath = databasePath.replace("{pluginDir}", core.getPlugin().getDataFolder().getAbsolutePath());
|
||||
|
||||
String jdbcUrl = "jdbc:";
|
||||
if (driver.contains("sqlite")) {
|
||||
jdbcUrl += "sqlite" + "://" + databasePath;
|
||||
databaseConfig.setConnectionTestQuery("SELECT 1");
|
||||
config.setConnectionTestQuery("SELECT 1");
|
||||
} else {
|
||||
jdbcUrl += "mysql" + "://" + host + ':' + port + '/' + databasePath;
|
||||
}
|
||||
|
||||
databaseConfig.setJdbcUrl(jdbcUrl);
|
||||
this.dataSource = new HikariDataSource(databaseConfig);
|
||||
config.setJdbcUrl(jdbcUrl);
|
||||
this.dataSource = new HikariDataSource(config);
|
||||
}
|
||||
|
||||
public HikariDataSource getDataSource() {
|
||||
public DataSource getDataSource() {
|
||||
return dataSource;
|
||||
}
|
||||
|
||||
@@ -68,7 +83,7 @@ public class AuthStorage {
|
||||
+ "LastLogin TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "
|
||||
//the premium shouldn't steal the cracked account by changing the name
|
||||
+ "UNIQUE (Name) "
|
||||
+ ")";
|
||||
+ ')';
|
||||
|
||||
if (dataSource.getJdbcUrl().contains("sqlite")) {
|
||||
createDataStmt = createDataStmt.replace("AUTO_INCREMENT", "AUTOINCREMENT");
|
||||
@@ -126,15 +141,13 @@ public class AuthStorage {
|
||||
|
||||
boolean premium = resultSet.getBoolean(4);
|
||||
String lastIp = resultSet.getString(5);
|
||||
long lastLogin = resultSet.getTimestamp(6, calendar).getTime();
|
||||
PlayerProfile playerProfile = new PlayerProfile(userId, uuid, name, premium, lastIp, lastLogin);
|
||||
return playerProfile;
|
||||
long lastLogin = resultSet.getTimestamp(6).getTime();
|
||||
return new PlayerProfile(userId, uuid, name, premium, lastIp, lastLogin);
|
||||
} else {
|
||||
PlayerProfile crackedProfile = new PlayerProfile(null, name, false, "");
|
||||
return crackedProfile;
|
||||
return new PlayerProfile(null, name, false, "");
|
||||
}
|
||||
} catch (SQLException sqlEx) {
|
||||
core.getLogger().log(Level.SEVERE, "Failed to query profile", sqlEx);
|
||||
core.getPlugin().getLogger().log(Level.SEVERE, "Failed to query profile", sqlEx);
|
||||
} finally {
|
||||
closeQuietly(con);
|
||||
closeQuietly(loadStmt);
|
||||
@@ -160,12 +173,11 @@ public class AuthStorage {
|
||||
String name = resultSet.getString(3);
|
||||
boolean premium = resultSet.getBoolean(4);
|
||||
String lastIp = resultSet.getString(5);
|
||||
long lastLogin = resultSet.getTimestamp(6, calendar).getTime();
|
||||
PlayerProfile playerProfile = new PlayerProfile(userId, uuid, name, premium, lastIp, lastLogin);
|
||||
return playerProfile;
|
||||
long lastLogin = resultSet.getTimestamp(6).getTime();
|
||||
return new PlayerProfile(userId, uuid, name, premium, lastIp, lastLogin);
|
||||
}
|
||||
} catch (SQLException sqlEx) {
|
||||
core.getLogger().log(Level.SEVERE, "Failed to query profile", sqlEx);
|
||||
core.getPlugin().getLogger().log(Level.SEVERE, "Failed to query profile", sqlEx);
|
||||
} finally {
|
||||
closeQuietly(con);
|
||||
closeQuietly(loadStmt);
|
||||
@@ -226,7 +238,7 @@ public class AuthStorage {
|
||||
|
||||
return true;
|
||||
} catch (SQLException ex) {
|
||||
core.getLogger().log(Level.SEVERE, "Failed to save playerProfile", ex);
|
||||
core.getPlugin().getLogger().log(Level.SEVERE, "Failed to save playerProfile", ex);
|
||||
} finally {
|
||||
closeQuietly(con);
|
||||
closeQuietly(updateStmt);
|
||||
@@ -246,7 +258,7 @@ public class AuthStorage {
|
||||
try {
|
||||
closeable.close();
|
||||
} catch (Exception closeEx) {
|
||||
core.getLogger().log(Level.SEVERE, "Failed to close connection", closeEx);
|
||||
core.getPlugin().getLogger().log(Level.SEVERE, "Failed to close connection", closeEx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import com.google.common.collect.ImmutableList;
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.Socket;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
@@ -36,18 +35,18 @@ public class BalancedSSLFactory extends SSLSocketFactory {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(Socket socket, String host, int port, boolean autoclose) throws IOException {
|
||||
public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException {
|
||||
return oldFactory.createSocket(host, port, getNextLocalAddress(), 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
|
||||
public Socket createSocket(String host, int port) throws IOException {
|
||||
return oldFactory.createSocket(host, port, getNextLocalAddress(), 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(String host, int port, InetAddress localAddress, int localPort)
|
||||
throws IOException, UnknownHostException {
|
||||
throws IOException {
|
||||
//default
|
||||
return oldFactory.createSocket(host, port, localAddress, localPort);
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
package com.github.games647.fastlogin.core;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import com.google.common.base.Ticker;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.RemovalListener;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Represents a Guava CacheBuilder that is compatible with both Guava 10 (Minecraft 1.7.X) and 13
|
||||
*/
|
||||
@@ -54,7 +54,7 @@ public class CompatibleCacheBuilder<K, V> {
|
||||
* @param concurrencyLevel New concurrency level
|
||||
* @return This for chaining
|
||||
*
|
||||
* @throws IllegalArgumentException if {@code concurrencyLevel} is nonpositive
|
||||
* @throws IllegalArgumentException if {@code concurrencyLevel} is non-positive
|
||||
* @throws IllegalStateException if a concurrency level was already set
|
||||
*/
|
||||
public CompatibleCacheBuilder<K, V> concurrencyLevel(int concurrencyLevel) {
|
||||
@@ -76,7 +76,7 @@ public class CompatibleCacheBuilder<K, V> {
|
||||
* <p>
|
||||
* Expired entries may be counted by {@link com.google.common.cache.Cache#size Cache.size()}, but will never be
|
||||
* visible to read or write operations. Expired entries are currently cleaned up during write operations, or during
|
||||
* occasional read operations in the absense of writes; though this behavior may change in the future.
|
||||
* occasional read operations in the absence of writes; though this behavior may change in the future.
|
||||
*
|
||||
* @param duration the length of time after an entry is last accessed that it should be automatically removed
|
||||
* @param unit the unit that {@code duration} is expressed in
|
||||
@@ -102,7 +102,7 @@ public class CompatibleCacheBuilder<K, V> {
|
||||
* <p>
|
||||
* Expired entries may be counted by {@link com.google.common.cache.Cache#size Cache.size()}, but will never be
|
||||
* visible to read or write operations. Expired entries are currently cleaned up during write operations, or during
|
||||
* occasional read operations in the absense of writes; though this behavior may change in the future.
|
||||
* occasional read operations in the absence of writes; though this behavior may change in the future.
|
||||
*
|
||||
* @param duration the length of time after an entry is created that it should be automatically removed
|
||||
* @param unit the unit that {@code duration} is expressed in
|
||||
@@ -284,7 +284,7 @@ public class CompatibleCacheBuilder<K, V> {
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <K1 extends K, V1 extends V> ConcurrentMap<K1, V1> build(CacheLoader<? super K1, V1> loader) {
|
||||
Object cache = null;
|
||||
Object cache;
|
||||
|
||||
if (BUILD_METHOD == null) {
|
||||
try {
|
||||
|
||||
@@ -13,6 +13,11 @@ public class SharedConfig {
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T get(String path, T def) {
|
||||
Object val = configValues.get(path);
|
||||
|
||||
if (def instanceof String) {
|
||||
return (T) String.valueOf(val);
|
||||
}
|
||||
|
||||
return ( val != null ) ? (T) val : def;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.github.games647.fastlogin.core.hooks;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface PasswordGenerator<P> {
|
||||
|
||||
String getRandomPassword(P player);
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
package com.github.games647.fastlogin.core.importer;
|
||||
|
||||
import com.github.games647.fastlogin.core.AuthStorage;
|
||||
import com.github.games647.fastlogin.core.PlayerProfile;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
public class AutoInImporter extends Importer {
|
||||
|
||||
private static final String PLUGIN_NAME = "AutoIn";
|
||||
|
||||
private static final String SQLITE_FILE = "plugins/" + PLUGIN_NAME + "/AutoIn_PlayerOptions.db";
|
||||
|
||||
private static final String USER_TABLE = "nicknames";
|
||||
private static final String UUID_TABLE = "uuids";
|
||||
private static final String SESSION_TABLE = "sessions";
|
||||
|
||||
public static String getSQLitePath() {
|
||||
return SQLITE_FILE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int importData(Connection source, DataSource target, AuthStorage storage) throws SQLException {
|
||||
Statement stmt = null;
|
||||
ResultSet resultSet = null;
|
||||
try {
|
||||
stmt = source.createStatement();
|
||||
resultSet = stmt.executeQuery("SELECT name, protection, premium, puuid FROM " + USER_TABLE
|
||||
+ " LEFT JOIN " + " ("
|
||||
/* Prevent duplicates */
|
||||
+ "SELECT * FROM " + UUID_TABLE + " GROUP BY nickname_id"
|
||||
+ ") uuids"
|
||||
+ " ON " + USER_TABLE + ".id = uuids.nickname_id");
|
||||
|
||||
int rows = 0;
|
||||
while (resultSet.next()) {
|
||||
String name = resultSet.getString(1);
|
||||
boolean protection = resultSet.getBoolean(2);
|
||||
/* Enable premium authentication only for those who want to be auto logged in,
|
||||
so they have their cracked protection disabled */
|
||||
boolean premium = !protection && resultSet.getBoolean(3);
|
||||
String puuid = resultSet.getString(4);
|
||||
|
||||
/* FastLogin will also make lookups on the uuid column for name changes
|
||||
the old 1.6.2 version won't check if those user have premium enabled
|
||||
|
||||
so it could happen that a premium could steal the account if we don't do this
|
||||
|
||||
It seems the uuid is saved on autoin too if the player is cracked */
|
||||
PlayerProfile profile;
|
||||
if (premium) {
|
||||
profile = new PlayerProfile(UUID.fromString(puuid), name, premium, "");
|
||||
} else {
|
||||
profile = new PlayerProfile(null, name, premium, "");
|
||||
}
|
||||
|
||||
storage.save(profile);
|
||||
rows++;
|
||||
}
|
||||
|
||||
return rows;
|
||||
} finally {
|
||||
closeQuietly(stmt);
|
||||
closeQuietly(resultSet);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
package com.github.games647.fastlogin.core.importer;
|
||||
|
||||
import com.github.games647.fastlogin.core.AuthStorage;
|
||||
import com.github.games647.fastlogin.core.PlayerProfile;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.sql.Timestamp;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
public class BPAImporter extends Importer {
|
||||
|
||||
private static final String DEFAULT_TABLE_NAME = "users";
|
||||
|
||||
@Override
|
||||
public int importData(Connection source, DataSource target, AuthStorage storage) throws SQLException {
|
||||
Statement stmt = null;
|
||||
ResultSet resultSet = null;
|
||||
try {
|
||||
stmt = source.createStatement();
|
||||
resultSet = stmt.executeQuery("SELECT "
|
||||
+ "nick, "
|
||||
+ "checked, "
|
||||
+ "lastIP, "
|
||||
+ "FROM_UNIXTIME(lastJoined * 0.001) AS LastLogin "
|
||||
+ "FROM " + DEFAULT_TABLE_NAME);
|
||||
|
||||
int rows = 0;
|
||||
while (resultSet.next()) {
|
||||
String name = resultSet.getString(1);
|
||||
boolean premium = resultSet.getBoolean(2);
|
||||
String lastIP = resultSet.getString(3);
|
||||
Timestamp lastLogin = resultSet.getTimestamp(4);
|
||||
|
||||
//uuid doesn't exist here
|
||||
PlayerProfile profile = new PlayerProfile(null, name, premium, lastIP);
|
||||
storage.save(profile);
|
||||
rows++;
|
||||
}
|
||||
|
||||
return rows;
|
||||
} finally {
|
||||
closeQuietly(stmt);
|
||||
closeQuietly(resultSet);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
package com.github.games647.fastlogin.core.importer;
|
||||
|
||||
import com.github.games647.fastlogin.core.AuthStorage;
|
||||
import com.github.games647.fastlogin.core.PlayerProfile;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
public class ElDziAuthImporter extends Importer {
|
||||
|
||||
private static final String TABLE_NAME = "accounts";
|
||||
|
||||
@Override
|
||||
public int importData(Connection source, DataSource target, AuthStorage storage) throws SQLException {
|
||||
Statement stmt = null;
|
||||
ResultSet resultSet = null;
|
||||
try {
|
||||
stmt = source.createStatement();
|
||||
resultSet = stmt.executeQuery("SELECT "
|
||||
+ "nick, "
|
||||
+ "premium, "
|
||||
+ "lastIP, "
|
||||
+ "FROM_UNIXTIME(lastPlayed * 0.001) AS LastLogin "
|
||||
+ "FROM " + TABLE_NAME);
|
||||
|
||||
int rows = 0;
|
||||
while (resultSet.next()) {
|
||||
String name = resultSet.getString(1);
|
||||
boolean premium = resultSet.getBoolean(2);
|
||||
String lastIP = resultSet.getString(3);
|
||||
Timestamp lastLogin = resultSet.getTimestamp(4);
|
||||
|
||||
String uuid = resultSet.getString(5);
|
||||
|
||||
PlayerProfile profile;
|
||||
if (premium) {
|
||||
profile = new PlayerProfile(UUID.fromString(uuid), name, premium, lastIP);
|
||||
} else {
|
||||
profile = new PlayerProfile(null, name, premium, "");
|
||||
}
|
||||
|
||||
storage.save(profile);
|
||||
rows++;
|
||||
}
|
||||
|
||||
return rows;
|
||||
} finally {
|
||||
closeQuietly(stmt);
|
||||
closeQuietly(resultSet);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package com.github.games647.fastlogin.core.importer;
|
||||
|
||||
public enum ImportPlugin {
|
||||
|
||||
AUTO_IN(AutoInImporter.class),
|
||||
|
||||
BPA(BPAImporter.class),
|
||||
|
||||
ELDZI(ElDziAuthImporter.class);
|
||||
|
||||
private final Class<? extends Importer> importerClass;
|
||||
|
||||
ImportPlugin(Class<? extends Importer> importer) {
|
||||
this.importerClass = importer;
|
||||
}
|
||||
|
||||
public Class<? extends Importer> getImporter() {
|
||||
return importerClass;
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
package com.github.games647.fastlogin.core.importer;
|
||||
|
||||
import com.github.games647.fastlogin.core.AuthStorage;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import javax.sql.DataSource;
|
||||
|
||||
public abstract class Importer {
|
||||
|
||||
public abstract int importData(Connection source, DataSource target, AuthStorage storage) throws SQLException;
|
||||
|
||||
protected void closeQuietly(AutoCloseable closeable) {
|
||||
if (closeable != null) {
|
||||
try {
|
||||
closeable.close();
|
||||
} catch (Exception ignore) {
|
||||
//ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,31 +6,34 @@ import com.github.games647.fastlogin.core.SharedConfig;
|
||||
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.importer.AutoInImporter;
|
||||
import com.github.games647.fastlogin.core.importer.ImportPlugin;
|
||||
import com.github.games647.fastlogin.core.importer.Importer;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import java.io.File;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.SQLException;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @param <P> Player class
|
||||
* @param <C> CommandSender
|
||||
* @param <T> Plugin class
|
||||
*/
|
||||
public abstract class FastLoginCore<P> {
|
||||
public class FastLoginCore<P extends C, C, T extends PlatformPlugin<C>> {
|
||||
|
||||
public static <K, V> ConcurrentMap<K, V> buildCache(int expireAfterWrite, int maxSize) {
|
||||
CompatibleCacheBuilder<Object, Object> builder = CompatibleCacheBuilder.newBuilder();
|
||||
@@ -54,31 +57,77 @@ public abstract class FastLoginCore<P> {
|
||||
}
|
||||
|
||||
return UUID.fromString(withoutDashes.substring(0, 8)
|
||||
+ "-" + withoutDashes.substring(8, 12)
|
||||
+ "-" + withoutDashes.substring(12, 16)
|
||||
+ "-" + withoutDashes.substring(16, 20)
|
||||
+ "-" + withoutDashes.substring(20, 32));
|
||||
+ '-' + withoutDashes.substring(8, 12)
|
||||
+ '-' + withoutDashes.substring(12, 16)
|
||||
+ '-' + withoutDashes.substring(16, 20)
|
||||
+ '-' + withoutDashes.substring(20, 32));
|
||||
}
|
||||
|
||||
protected final Map<String, String> localeMessages = new ConcurrentHashMap<>();
|
||||
|
||||
private final ConcurrentMap<String, Object> pendingLogins = FastLoginCore.buildCache(5, 0);
|
||||
private final ConcurrentMap<String, Object> pendingLogin = FastLoginCore.buildCache(5, -1);
|
||||
private final Set<UUID> pendingConfirms = Sets.newHashSet();
|
||||
private final SharedConfig sharedConfig;
|
||||
private final T plugin;
|
||||
|
||||
private SharedConfig sharedConfig;
|
||||
private MojangApiConnector apiConnector;
|
||||
private AuthStorage storage;
|
||||
private PasswordGenerator<P> passwordGenerator = new DefaultPasswordGenerator<>();
|
||||
private AuthPlugin<P> authPlugin;
|
||||
|
||||
public FastLoginCore(Map<String, Object> config) {
|
||||
this.sharedConfig = new SharedConfig(config);
|
||||
public FastLoginCore(T plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
public void setApiConnector() {
|
||||
|
||||
public void load() {
|
||||
saveDefaultFile("messages.yml");
|
||||
saveDefaultFile("config.yml");
|
||||
|
||||
BufferedReader reader = null;
|
||||
try {
|
||||
reader = new BufferedReader(new InputStreamReader(getClass().getClassLoader().getResourceAsStream("config.yml")));
|
||||
sharedConfig = new SharedConfig(plugin.loadYamlFile(reader));
|
||||
reader.close();
|
||||
|
||||
reader = Files.newBufferedReader(plugin.getDataFolder().toPath().resolve("config.yml"));
|
||||
sharedConfig.getConfigValues().putAll(plugin.loadYamlFile(reader));
|
||||
reader.close();
|
||||
|
||||
reader = new BufferedReader(new InputStreamReader(getClass().getClassLoader().getResourceAsStream("messages.yml")));
|
||||
reader = Files.newBufferedReader(plugin.getDataFolder().toPath().resolve("messages.yml"));
|
||||
Map<String, Object> messageConfig = plugin.loadYamlFile(reader);
|
||||
reader.close();
|
||||
|
||||
reader = Files.newBufferedReader(plugin.getDataFolder().toPath().resolve("messages.yml"));
|
||||
messageConfig.putAll(plugin.loadYamlFile(reader));
|
||||
for (Entry<String, Object> entry : messageConfig.entrySet()) {
|
||||
String message = plugin.translateColorCodes('&', (String) entry.getValue());
|
||||
if (!message.isEmpty()) {
|
||||
localeMessages.put(entry.getKey(), message);
|
||||
}
|
||||
}
|
||||
|
||||
reader.close();
|
||||
} catch (IOException ioEx) {
|
||||
plugin.getLogger().log(Level.INFO, "Failed to load yaml files", ioEx);
|
||||
} finally {
|
||||
if (reader != null) {
|
||||
try {
|
||||
reader.close();
|
||||
} catch (IOException ex) {
|
||||
plugin.getLogger().log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<String> ipAddresses = sharedConfig.get("ip-addresses");
|
||||
int requestLimit = sharedConfig.get("mojang-request-limit");
|
||||
this.apiConnector = makeApiConnector(getLogger(), ipAddresses, requestLimit);
|
||||
List<String> proxyList = sharedConfig.get("proxies", Lists.newArrayList());
|
||||
Map<String, Integer> proxies = proxyList.stream()
|
||||
.collect(Collectors
|
||||
.toMap(line -> line.split(":")[0], line -> Integer.parseInt(line.split(":")[1])));
|
||||
|
||||
this.apiConnector = plugin.makeApiConnector(plugin.getLogger(), ipAddresses, requestLimit, proxies);
|
||||
}
|
||||
|
||||
public MojangApiConnector getApiConnector() {
|
||||
@@ -89,20 +138,21 @@ public abstract class FastLoginCore<P> {
|
||||
return storage;
|
||||
}
|
||||
|
||||
public abstract File getDataFolder();
|
||||
public T getPlugin() {
|
||||
return plugin;
|
||||
}
|
||||
|
||||
public abstract Logger getLogger();
|
||||
|
||||
public abstract ThreadFactory getThreadFactory();
|
||||
public void sendLocaleMessage(String key, C receiver) {
|
||||
String message = localeMessages.get(key);
|
||||
if (message != null) {
|
||||
plugin.sendMessage(receiver, message);
|
||||
}
|
||||
}
|
||||
|
||||
public String getMessage(String key) {
|
||||
return localeMessages.get(key);
|
||||
}
|
||||
|
||||
public abstract void loadMessages();
|
||||
|
||||
public abstract MojangApiConnector makeApiConnector(Logger logger, List<String> addresses, int requests);
|
||||
|
||||
public boolean setupDatabase() {
|
||||
String driver = sharedConfig.get("driver");
|
||||
String host = sharedConfig.get("host", "");
|
||||
@@ -112,56 +162,18 @@ public abstract class FastLoginCore<P> {
|
||||
String user = sharedConfig.get("username", "");
|
||||
String password = sharedConfig.get("password", "");
|
||||
|
||||
storage = new AuthStorage(this, driver, host, port, database, user, password);
|
||||
boolean useSSL = sharedConfig.get("useSSL", false);
|
||||
|
||||
storage = new AuthStorage(this, driver, host, port, database, user, password, useSSL);
|
||||
try {
|
||||
storage.createTables();
|
||||
return true;
|
||||
} catch (Exception ex) {
|
||||
getLogger().log(Level.SEVERE, "Failed to setup database. Disabling plugin...", ex);
|
||||
plugin.getLogger().log(Level.SEVERE, "Failed to setup database. Disabling plugin...", ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean importDatabase(ImportPlugin plugin, boolean sqlite, AuthStorage storage, String host, String database
|
||||
, String username, String pass) {
|
||||
if (sqlite && (plugin == ImportPlugin.BPA || plugin == ImportPlugin.ELDZI)) {
|
||||
throw new IllegalArgumentException("These plugins doesn't support flat file databases");
|
||||
}
|
||||
|
||||
Importer importer;
|
||||
try {
|
||||
importer = plugin.getImporter().newInstance();
|
||||
} catch (Exception ex) {
|
||||
getLogger().log(Level.SEVERE, "Couldn't not setup importer class", ex);
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
if (sqlite && plugin == ImportPlugin.AUTO_IN) {
|
||||
//load sqlite driver
|
||||
Class.forName("org.sqlite.JDBC");
|
||||
|
||||
String jdbcUrl = "jdbc:sqlite:" + AutoInImporter.getSQLitePath();
|
||||
Connection con = DriverManager.getConnection(jdbcUrl);
|
||||
importer.importData(con, storage.getDataSource(), storage);
|
||||
return true;
|
||||
} else {
|
||||
Class.forName("com.mysql.jdbc.Driver");
|
||||
|
||||
String jdbcUrl = "jdbc:mysql://" + host + "/" + database;
|
||||
Connection con = DriverManager.getConnection(jdbcUrl, username, pass);
|
||||
importer.importData(con, storage.getDataSource(), storage);
|
||||
return true;
|
||||
}
|
||||
} catch (ClassNotFoundException ex) {
|
||||
getLogger().log(Level.SEVERE, "Cannot find SQL driver. Do you removed it?", ex);
|
||||
} catch (SQLException ex) {
|
||||
getLogger().log(Level.SEVERE, "Couldn't import data. Aborting...", ex);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public SharedConfig getConfig() {
|
||||
return sharedConfig;
|
||||
}
|
||||
@@ -174,11 +186,11 @@ public abstract class FastLoginCore<P> {
|
||||
this.passwordGenerator = passwordGenerator;
|
||||
}
|
||||
|
||||
public ConcurrentMap<String, Object> getPendingLogins() {
|
||||
return pendingLogins;
|
||||
public ConcurrentMap<String, Object> getPendingLogin() {
|
||||
return pendingLogin;
|
||||
}
|
||||
|
||||
public Set<UUID> getPendingConfirms() {
|
||||
public Collection<UUID> getPendingConfirms() {
|
||||
return pendingConfirms;
|
||||
}
|
||||
|
||||
@@ -190,6 +202,28 @@ public abstract class FastLoginCore<P> {
|
||||
this.authPlugin = authPlugin;
|
||||
}
|
||||
|
||||
public void saveDefaultFile(String fileName) {
|
||||
if (!plugin.getDataFolder().exists()) {
|
||||
plugin.getDataFolder().mkdir();
|
||||
}
|
||||
|
||||
Path configFile = plugin.getDataFolder().toPath().resolve(fileName);
|
||||
if (Files.notExists(configFile)) {
|
||||
InputStream in = getClass().getClassLoader().getResourceAsStream(fileName);
|
||||
try {
|
||||
Files.copy(in, configFile);
|
||||
} catch (IOException ioExc) {
|
||||
plugin.getLogger().log(Level.SEVERE, "Error saving default " + fileName, ioExc);
|
||||
} finally {
|
||||
try {
|
||||
in.close();
|
||||
} catch (IOException ex) {
|
||||
plugin.getLogger().log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void close() {
|
||||
if (storage != null) {
|
||||
storage.close();
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
package com.github.games647.fastlogin.core.shared;
|
||||
|
||||
import com.github.games647.fastlogin.core.AuthStorage;
|
||||
import com.github.games647.fastlogin.core.PlayerProfile;
|
||||
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
|
||||
|
||||
import java.util.logging.Level;
|
||||
|
||||
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 L session;
|
||||
|
||||
public ForceLoginManagement(FastLoginCore<P, C, T> core, P player) {
|
||||
this.core = core;
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (!isOnline(player) || session == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
AuthStorage storage = core.getStorage();
|
||||
|
||||
PlayerProfile 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 {
|
||||
success = forceLogin(player);
|
||||
}
|
||||
}
|
||||
|
||||
if (success) {
|
||||
//update only on success to prevent corrupt data
|
||||
if (playerProfile != null) {
|
||||
playerProfile.setUuid(session.getUuid());
|
||||
playerProfile.setPremium(true);
|
||||
storage.save(playerProfile);
|
||||
}
|
||||
|
||||
onForceActionSuccess(session);
|
||||
}
|
||||
}
|
||||
} else if (playerProfile != null) {
|
||||
//cracked player
|
||||
playerProfile.setUuid(null);
|
||||
playerProfile.setPremium(false);
|
||||
storage.save(playerProfile);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
core.getPlugin().getLogger().log(Level.WARNING, "ERROR ON FORCE LOGIN", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean forceRegister(P player) {
|
||||
core.getPlugin().getLogger().log(Level.INFO, "Register player {0}", 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().getLogger().log(Level.INFO, "Logging player {0} in", getName(player));
|
||||
boolean success = core.getAuthPluginHook().forceLogin(player);
|
||||
|
||||
if (success) {
|
||||
core.sendLocaleMessage("auto-login", player);
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
public abstract void onForceActionSuccess(LoginSession session);
|
||||
|
||||
public abstract String getName(P player);
|
||||
|
||||
public abstract boolean isOnline(P player);
|
||||
|
||||
public abstract boolean isOnlineMode();
|
||||
}
|
||||
@@ -7,12 +7,12 @@ import com.github.games647.fastlogin.core.hooks.AuthPlugin;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public abstract class JoinManagement<T, S extends LoginSource> {
|
||||
public abstract class JoinManagement<P extends C, C, S extends LoginSource> {
|
||||
|
||||
protected final FastLoginCore<T> core;
|
||||
protected final AuthPlugin<T> authHook;
|
||||
protected final FastLoginCore<P, C, ?> core;
|
||||
protected final AuthPlugin<P> authHook;
|
||||
|
||||
public JoinManagement(FastLoginCore<T> core, AuthPlugin<T> authHook) {
|
||||
public JoinManagement(FastLoginCore<P, C, ?> core, AuthPlugin<P> authHook) {
|
||||
this.core = core;
|
||||
this.authHook = authHook;
|
||||
}
|
||||
@@ -26,10 +26,11 @@ public abstract class JoinManagement<T, S extends LoginSource> {
|
||||
SharedConfig config = core.getConfig();
|
||||
|
||||
String ip = source.getAddress().getAddress().getHostAddress();
|
||||
profile.setLastIp(ip);
|
||||
try {
|
||||
if (profile.getUserId() == -1) {
|
||||
if (core.getPendingLogins().containsKey(ip + username) && config.get("secondAttemptCracked", false)) {
|
||||
core.getLogger().log(Level.INFO, "Second attempt login -> cracked {0}", username);
|
||||
if (core.getPendingLogin().remove(ip + username) != null && config.get("secondAttemptCracked", false)) {
|
||||
core.getPlugin().getLogger().log(Level.INFO, "Second attempt login -> cracked {0}", username);
|
||||
|
||||
//first login request failed so make a cracked session
|
||||
startCrackedSession(source, profile, username);
|
||||
@@ -38,7 +39,6 @@ public abstract class JoinManagement<T, S extends LoginSource> {
|
||||
|
||||
UUID premiumUUID = null;
|
||||
if (config.get("nameChangeCheck", false) || config.get("autoRegister", false)) {
|
||||
core.getLogger().log(Level.FINER, "Player {0} uses a premium username", username);
|
||||
premiumUUID = core.getApiConnector().getPremiumUUID(username);
|
||||
}
|
||||
|
||||
@@ -56,15 +56,15 @@ public abstract class JoinManagement<T, S extends LoginSource> {
|
||||
} else if (profile.isPremium()) {
|
||||
requestPremiumLogin(source, profile, username, true);
|
||||
} else {
|
||||
|
||||
startCrackedSession(source, profile, username);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
core.getLogger().log(Level.SEVERE, "Failed to check premium state", ex);
|
||||
core.getPlugin().getLogger().log(Level.SEVERE, "Failed to check premium state", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean checkPremiumName(S source, String username, PlayerProfile profile) throws Exception {
|
||||
core.getPlugin().getLogger().log(Level.FINER, "Player {0} uses a premium username", username);
|
||||
if (core.getConfig().get("autoRegister", false) && (authHook == null || !authHook.isRegistered(username))) {
|
||||
requestPremiumLogin(source, profile, username, false);
|
||||
return true;
|
||||
@@ -79,7 +79,7 @@ public abstract class JoinManagement<T, S extends LoginSource> {
|
||||
PlayerProfile profile = core.getStorage().loadProfile(premiumUUID);
|
||||
if (profile != null) {
|
||||
//uuid exists in the database
|
||||
core.getLogger().log(Level.FINER, "Player {0} changed it's username", premiumUUID);
|
||||
core.getPlugin().getLogger().log(Level.FINER, "Player {0} changed it's username", premiumUUID);
|
||||
|
||||
//update the username to the new one in the database
|
||||
profile.setPlayerName(username);
|
||||
|
||||
@@ -1,18 +1,25 @@
|
||||
package com.github.games647.fastlogin.core.shared;
|
||||
|
||||
import com.github.games647.fastlogin.core.BalancedSSLFactory;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.common.io.CharStreams;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Proxy;
|
||||
import java.net.Proxy.Type;
|
||||
import java.net.URL;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
@@ -28,8 +35,6 @@ public abstract class MojangApiConnector {
|
||||
private static final int TIMEOUT = 3 * 1_000;
|
||||
private static final String USER_AGENT = "Premium-Checker";
|
||||
|
||||
private static final String MCAPI_UUID_URL = "https://mcapi.ca/uuid/player/";
|
||||
|
||||
//only premium (paid account) users have a uuid from here
|
||||
private static final String UUID_LINK = "https://api.mojang.com/users/profiles/minecraft/";
|
||||
//this includes a-zA-Z1-9_
|
||||
@@ -37,9 +42,10 @@ public abstract class MojangApiConnector {
|
||||
|
||||
private static final int RATE_LIMIT_CODE = 429;
|
||||
|
||||
//compile the pattern only on plugin enable -> and this have to be threadsafe
|
||||
private final Pattern playernameMatcher = Pattern.compile(VALID_PLAYERNAME);
|
||||
//compile the pattern only on plugin enable -> and this have to be thread-safe
|
||||
private final Pattern nameMatcher = Pattern.compile(VALID_PLAYERNAME);
|
||||
|
||||
private final Iterator<Proxy> proxies;
|
||||
private final ConcurrentMap<Object, Object> requests = FastLoginCore.buildCache(10, -1);
|
||||
private final BalancedSSLFactory sslFactory;
|
||||
private final int rateLimit;
|
||||
@@ -47,17 +53,92 @@ public abstract class MojangApiConnector {
|
||||
|
||||
protected final Logger logger;
|
||||
|
||||
public MojangApiConnector(Logger logger, List<String> localAddresses, int rateLimit) {
|
||||
public MojangApiConnector(Logger logger, Collection<String> localAddresses, int rateLimit
|
||||
, Map<String, Integer> proxies) {
|
||||
this.logger = logger;
|
||||
|
||||
if (rateLimit > 600) {
|
||||
this.rateLimit = 600;
|
||||
} else {
|
||||
this.rateLimit = rateLimit;
|
||||
this.rateLimit = Math.max(rateLimit, 600);
|
||||
this.sslFactory = buildAddresses(logger, localAddresses);
|
||||
|
||||
List<Proxy> proxyBuilder = Lists.newArrayList();
|
||||
for (Entry<String, Integer> proxy : proxies.entrySet()) {
|
||||
proxyBuilder.add(new Proxy(Type.HTTP, new InetSocketAddress(proxy.getKey(), proxy.getValue())));
|
||||
}
|
||||
|
||||
this.proxies = Iterables.cycle(proxyBuilder).iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null on non-premium
|
||||
*/
|
||||
public UUID getPremiumUUID(String playerName) {
|
||||
if (!nameMatcher.matcher(playerName).matches()) {
|
||||
//check if it's a valid player name
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
HttpsURLConnection connection;
|
||||
if (requests.size() >= rateLimit || System.currentTimeMillis() - lastRateLimit < 1_000 * 60 * 10) {
|
||||
synchronized (proxies) {
|
||||
if (proxies.hasNext()) {
|
||||
connection = getConnection(UUID_LINK + playerName, proxies.next());
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
requests.put(new Object(), new Object());
|
||||
connection = getConnection(UUID_LINK + playerName);
|
||||
}
|
||||
|
||||
if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
|
||||
String line = reader.readLine();
|
||||
if (!"null".equals(line)) {
|
||||
return FastLoginCore.parseId(getUUIDFromJson(line));
|
||||
}
|
||||
} else if (connection.getResponseCode() == RATE_LIMIT_CODE) {
|
||||
logger.info("RATE_LIMIT REACHED");
|
||||
lastRateLimit = System.currentTimeMillis();
|
||||
if (!connection.usingProxy()) {
|
||||
return getPremiumUUID(playerName);
|
||||
}
|
||||
}
|
||||
//204 - no content for not found
|
||||
} catch (Exception ex) {
|
||||
logger.log(Level.SEVERE, "Failed to check if player has a paid account", ex);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public abstract boolean hasJoinedServer(LoginSession session, String serverId, InetSocketAddress ip);
|
||||
|
||||
protected abstract String getUUIDFromJson(String json);
|
||||
|
||||
protected HttpsURLConnection getConnection(String url, Proxy proxy) throws IOException {
|
||||
HttpsURLConnection connection = (HttpsURLConnection) new URL(url).openConnection(proxy);
|
||||
connection.setConnectTimeout(TIMEOUT);
|
||||
connection.setReadTimeout(2 * TIMEOUT);
|
||||
//the new Mojang API just uses json as response
|
||||
connection.setRequestProperty("Content-Type", "application/json");
|
||||
connection.setRequestProperty("User-Agent", USER_AGENT);
|
||||
|
||||
//this connection doesn't need to be closed. So can make use of keep alive in java
|
||||
if (sslFactory != null) {
|
||||
connection.setSSLSocketFactory(sslFactory);
|
||||
}
|
||||
|
||||
return connection;
|
||||
}
|
||||
|
||||
protected HttpsURLConnection getConnection(String url) throws IOException {
|
||||
return getConnection(url, Proxy.NO_PROXY);
|
||||
}
|
||||
|
||||
private BalancedSSLFactory buildAddresses(Logger logger, Collection<String> localAddresses) {
|
||||
if (localAddresses.isEmpty()) {
|
||||
this.sslFactory = null;
|
||||
return null;
|
||||
} else {
|
||||
Set<InetAddress> addresses = Sets.newHashSet();
|
||||
for (String localAddress : localAddresses) {
|
||||
@@ -74,85 +155,7 @@ public abstract class MojangApiConnector {
|
||||
}
|
||||
}
|
||||
|
||||
this.sslFactory = new BalancedSSLFactory(HttpsURLConnection.getDefaultSSLSocketFactory(), addresses);
|
||||
return new BalancedSSLFactory(HttpsURLConnection.getDefaultSSLSocketFactory(), addresses);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param playerName
|
||||
* @return null on non-premium
|
||||
*/
|
||||
public UUID getPremiumUUID(String playerName) {
|
||||
//check if it's a valid playername
|
||||
if (playernameMatcher.matcher(playerName).matches()) {
|
||||
// only make a API call if the name is valid existing mojang account
|
||||
|
||||
if (requests.size() >= rateLimit || System.currentTimeMillis() - lastRateLimit < 1_000 * 60 * 10) {
|
||||
// plugin.getLogger().fine("STILL WAITING FOR RATE_LIMIT - TRYING Third-party API");
|
||||
return getUUIDFromAPI(playerName);
|
||||
}
|
||||
|
||||
requests.put(new Object(), new Object());
|
||||
|
||||
try {
|
||||
HttpsURLConnection connection = getConnection(UUID_LINK + playerName);
|
||||
if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
|
||||
String line = reader.readLine();
|
||||
if (!line.equals("null")) {
|
||||
return FastLoginCore.parseId(getUUIDFromJson(line));
|
||||
}
|
||||
} else if (connection.getResponseCode() == RATE_LIMIT_CODE) {
|
||||
logger.info("RATE_LIMIT REACHED - TRYING THIRD-PARTY API");
|
||||
lastRateLimit = System.currentTimeMillis();
|
||||
return getUUIDFromAPI(playerName);
|
||||
}
|
||||
//204 - no content for not found
|
||||
} catch (Exception ex) {
|
||||
logger.log(Level.SEVERE, "Failed to check if player has a paid account", ex);
|
||||
}
|
||||
//this connection doesn't need to be closed. So can make use of keep alive in java
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public UUID getUUIDFromAPI(String playerName) {
|
||||
try {
|
||||
HttpURLConnection httpConnection = getConnection(MCAPI_UUID_URL + playerName);
|
||||
|
||||
if (httpConnection.getResponseCode() == HttpURLConnection.HTTP_NOT_FOUND) {
|
||||
//cracked
|
||||
return null;
|
||||
}
|
||||
|
||||
Reader reader = new InputStreamReader(httpConnection.getInputStream());
|
||||
String json = CharStreams.toString(reader);
|
||||
return FastLoginCore.parseId(getUUIDFromJson(json));
|
||||
} catch (IOException iOException) {
|
||||
logger.log(Level.SEVERE, "Tried converting name->uuid from third-party api", iOException);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public abstract boolean hasJoinedServer(LoginSession session, String serverId);
|
||||
|
||||
protected abstract String getUUIDFromJson(String json);
|
||||
|
||||
protected HttpsURLConnection getConnection(String url) throws IOException {
|
||||
HttpsURLConnection connection = (HttpsURLConnection) new URL(url).openConnection();
|
||||
connection.setConnectTimeout(TIMEOUT);
|
||||
connection.setReadTimeout(2 * TIMEOUT);
|
||||
//the new Mojang API just uses json as response
|
||||
connection.setRequestProperty("Content-Type", "application/json");
|
||||
connection.setRequestProperty("User-Agent", USER_AGENT);
|
||||
|
||||
if (sslFactory != null) {
|
||||
connection.setSSLSocketFactory(sslFactory);
|
||||
}
|
||||
|
||||
return connection;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.github.games647.fastlogin.core.shared;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.Reader;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public interface PlatformPlugin<C> {
|
||||
|
||||
String getName();
|
||||
|
||||
File getDataFolder();
|
||||
|
||||
Logger getLogger();
|
||||
|
||||
Map<String, Object> loadYamlFile(Reader reader);
|
||||
|
||||
void sendMessage(C receiver, String message);
|
||||
|
||||
ThreadFactory getThreadFactory();
|
||||
|
||||
String translateColorCodes(char colorChar, String rawMessage);
|
||||
|
||||
MojangApiConnector makeApiConnector(Logger logger, List<String> addresses, int requests
|
||||
, Map<String, Integer> proxies);
|
||||
}
|
||||
@@ -37,8 +37,8 @@ secondAttemptCracked: false
|
||||
switchMode: false
|
||||
|
||||
# If this plugin detected that a player has a premium, it can also set the associated
|
||||
# uuid from that account. So if the players changes their usernames, they will still have
|
||||
# the same playerdata (inventory, permissions, ...)
|
||||
# uuid from that account. So if the player changes the username, they will still have
|
||||
# the same player data (inventory, permissions, ...)
|
||||
#
|
||||
# Warning: This also means that the UUID will be different if the player is connecting
|
||||
# through a offline mode connection. This **could** cause plugin compatibility issues.
|
||||
@@ -49,14 +49,14 @@ switchMode: false
|
||||
# players could still join the server, because they have different UUID.
|
||||
#
|
||||
# Moreover you may want to convert the offline UUID to a premium UUID. This will ensure that the player
|
||||
# will have the same inventory, permissions, ... if they switched to premium authentification from offline/cracked
|
||||
# authentification.
|
||||
# will have the same inventory, permissions, ... if they switched to premium authentication from offline/cracked
|
||||
# authentication.
|
||||
#
|
||||
# This feature requires Cauldron, Spigot or a fork of Spigot (PaperSpigot, TacoSpigot)
|
||||
# This feature requires Cauldron, Spigot or a fork of Spigot (Paper)
|
||||
premiumUuid: false
|
||||
|
||||
# This will make an additional check (only for player names which are not in the database) against the mojang servers
|
||||
# in order to get the premium UUID. If that premium UUID is in the database, we can assume on sucessful login that the
|
||||
# in order to get the premium UUID. If that premium UUID is in the database, we can assume on successful login that the
|
||||
# player changed it's username and we just update the name in the database.
|
||||
# Examples:
|
||||
# #### Case 1
|
||||
@@ -104,7 +104,7 @@ nameChangeCheck: false
|
||||
#
|
||||
# Keep in mind that this will only works if the player:
|
||||
# * is the owner of the premium account
|
||||
# * the serverconnection is established through a premium connection (paid account authentification)
|
||||
# * the server connection is established through a premium connection (paid account authentication)
|
||||
# * has a skin
|
||||
#
|
||||
# This means this plugin doesn't need to create a new connection to the Mojang servers, because
|
||||
@@ -112,7 +112,7 @@ nameChangeCheck: false
|
||||
# players like cracked player, you have to use other plugins.
|
||||
#
|
||||
# If you want to use skins for your cracked player, you need an additional plugin like
|
||||
# ChangeSkin, SkinRestoer, ...
|
||||
# ChangeSkin, SkinRestorer, ...
|
||||
forwardSkin: true
|
||||
|
||||
# Displays a warning message that this message SHOULD only be invoked by
|
||||
@@ -128,8 +128,8 @@ premium-warning: true
|
||||
# Once the limit is reached, new players are always logged in as cracked until the rate-limit is expired.
|
||||
# (to the next ten minutes)
|
||||
#
|
||||
# The limit is IP-wide. If you have multiple IPv4-Addreses you specify them here. FastLogin will then use it in rotating
|
||||
# order --> 5 different IP-addreses 5 * 600 per 10 minutes
|
||||
# The limit is IP-wide. If you have multiple IPv4-addresses you specify them here. FastLogin will then use it in rotating
|
||||
# order --> 5 different IP-addresses 5 * 600 per 10 minutes
|
||||
# If this list is empty only the default one will be used
|
||||
#
|
||||
# Lists are created like this:
|
||||
@@ -137,13 +137,19 @@ premium-warning: true
|
||||
# - 192-168-0-2
|
||||
ip-addresses: []
|
||||
|
||||
# How many requests should be established until the plugin uses the third-party API https://mcapi.ca/
|
||||
# Once this number is reached in a range of ten minutes it will start connecting to https://mcapi.ca/ for the next ten minutes
|
||||
# This option exists in order to workaround the rate-limiting. Name -> UUID are fetched in the same way like heads
|
||||
# How many requests should be established to the Mojang API for Name -> UUID requests. Some other plugins as well
|
||||
# as the head minecraft block make such requests as well. Using this option you can limit the amount requests this
|
||||
# plugin should make.
|
||||
#
|
||||
# If you want to join the discussion visit this: https://github.com/games647/FastLogin/issues/27#issuecomment-226954350
|
||||
# If you lower this value, other plugins could still make requests while FastLogin cannot.
|
||||
# Mojang limits the amount of request to 600 per 10 minutes per IPv4-address.
|
||||
mojang-request-limit: 600
|
||||
|
||||
# This option automatically registers players which are in the FastLogin database, but not in the auth plugin database.
|
||||
# This can happen if you switch your auth plugin or cleared the database of the auth plugin.
|
||||
# https://github.com/games647/FastLogin/issues/85
|
||||
auto-register-unknown: false
|
||||
|
||||
# This disables the auto login from fastlogin. So a premium (like a paid account) authentication is requested, but
|
||||
# the player won't be auto logged into the account.
|
||||
#
|
||||
@@ -152,17 +158,30 @@ mojang-request-limit: 600
|
||||
autoLogin: true
|
||||
|
||||
# Database configuration
|
||||
# Recommened is the use of MariaDB (a better version of MySQL)
|
||||
# Recommended is the use of MariaDB (a better version of MySQL)
|
||||
|
||||
# Single file SQLite database
|
||||
driver: org.sqlite.JDBC
|
||||
# File location
|
||||
database: '{pluginDir}/FastLogin.db'
|
||||
|
||||
# MySQL
|
||||
# MySQL/MariaDB
|
||||
#driver: com.mysql.jdbc.Driver
|
||||
#host: localhost
|
||||
#port: 3306
|
||||
#database: fastlogin
|
||||
#username: myUser
|
||||
#password: myPassword
|
||||
#password: myPassword
|
||||
|
||||
# It's strongly recommended to enable SSL and setup a SSL certificate if the MySQL server isn't running on the same
|
||||
# machine
|
||||
#useSSL: false
|
||||
|
||||
# HTTP proxies for connecting to the Mojang servers in order to check if the username of a player is premium.
|
||||
# This is a workaround to prevent rate-limiting by Mojang. These proxies will only be used once your server hit
|
||||
# the rate-limit or the custom value above.
|
||||
# Please make sure you use reliable proxies.
|
||||
proxies:
|
||||
# 'IP:Port' or 'Domain:Port'
|
||||
# - 'xyz.com:1337'
|
||||
# - 'test.com:5131'
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
# You want to have language template? Visit the Github Wiki here:
|
||||
# https://github.com/games647/FastLogin/wiki/English
|
||||
|
||||
# In order to split a message into seperate lines you could just make a new line, but keep the '
|
||||
# In order to split a message into separate lines you could just make a new line, but keep the '
|
||||
# Example:
|
||||
# bla: '&aFirst line
|
||||
# Second line
|
||||
@@ -24,10 +24,10 @@
|
||||
# Switch mode is activated and a new (non-whitelist) cracked player tries to join
|
||||
switch-kick-message: '&4Only paid minecraft whitelisted accounts are allowed to join this server'
|
||||
|
||||
# Player activated premium logins in order to skip offline authentication
|
||||
# Player activated premium login in order to skip offline authentication
|
||||
add-premium: '&2Added to the list of premium players'
|
||||
|
||||
# Player activated premium logins in order to skip offline authentication
|
||||
# Player activated premium login in order to skip offline authentication
|
||||
add-premium-other: '&2Player has been added to the premium list'
|
||||
|
||||
# Player is already set be a paid account
|
||||
@@ -48,7 +48,7 @@ not-premium-other: '&4Player is not in the premium list'
|
||||
# Admin wanted to change the premium of a user that isn't known to the plugin
|
||||
player-unknown: '&4Player not in the database'
|
||||
|
||||
# ========= Bukkit/Spigot/PaperSpigot/TacoSpigot only ================================
|
||||
# ========= Bukkit/Spigot ================
|
||||
|
||||
# The user skipped the authentication, because it was a premium player
|
||||
auto-login: '&2Auto logged in'
|
||||
@@ -73,17 +73,17 @@ wait-on-proxy: '&6Sending request...'
|
||||
# authentication. In this state the client expects a success packet with a encrypted connection or disconnect packet.
|
||||
# So we kick the player, if we cannot encrypt the connection. In other situation (example: premium name check),
|
||||
# the player will be just authenticated as cracked
|
||||
error-kick: '&4Error occured'
|
||||
error-kick: '&4Error occurred'
|
||||
|
||||
# The server sents a verify token within the premium authentication reqest. If this doesn't match on response,
|
||||
# The server sends a verify token within the premium authentication request. If this doesn't match on response,
|
||||
# it could be another client sending malicious packets
|
||||
invalid-verify-token: '&4Invalid token'
|
||||
|
||||
# The client sent no request join server request to the mojang servers which would proof that it's owner of that
|
||||
# acciunt. Only modified clients would do this.
|
||||
# account. Only modified clients would do this.
|
||||
invalid-session: '&4Invalid session'
|
||||
|
||||
# The client sent a malicous packet without a login request packet
|
||||
# The client sent a malicious packet without a login request packet
|
||||
invalid-requst: '&4Invalid request'
|
||||
|
||||
# Message if the bukkit isn't fully started to inject the packets
|
||||
|
||||
44
pom.xml
44
pom.xml
@@ -8,7 +8,7 @@
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<name>FastLogin</name>
|
||||
<version>1.9</version>
|
||||
<version>1.10</version>
|
||||
<inceptionYear>2015</inceptionYear>
|
||||
<url>https://www.spigotmc.org/resources/fastlogin.14153/</url>
|
||||
<description>
|
||||
@@ -19,6 +19,7 @@
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<!--Possibility to deploy directly to the plugins folder-->
|
||||
<outputDir>${basedir}/target</outputDir>
|
||||
<git.commit.id>Unknown</git.commit.id>
|
||||
</properties>
|
||||
|
||||
<modules>
|
||||
@@ -28,17 +29,6 @@
|
||||
<module>universal</module>
|
||||
</modules>
|
||||
|
||||
<issueManagement>
|
||||
<system>GitHub</system>
|
||||
<url>https://github.com/games647/FastLogin/issues</url>
|
||||
</issueManagement>
|
||||
|
||||
<scm>
|
||||
<url>https://github.com/games647/FastLogin</url>
|
||||
<connection>scm:git:git://github.com/games647/FastLogin.git</connection>
|
||||
<developerConnection>scm:git:ssh://git@github.com:games647/FastLogin.git</developerConnection>
|
||||
</scm>
|
||||
|
||||
<build>
|
||||
<defaultGoal>install</defaultGoal>
|
||||
<!--Just use the project name to replace an old version of the plugin if the user does only copy-paste-->
|
||||
@@ -48,14 +38,29 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.5.1</version>
|
||||
<version>3.6.1</version>
|
||||
<configuration>
|
||||
<source>1.8</source>
|
||||
<target>1.8</target>
|
||||
<showWarnings>true</showWarnings>
|
||||
<showDeprecation>true</showDeprecation>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>pl.project13.maven</groupId>
|
||||
<artifactId>git-commit-id-plugin</artifactId>
|
||||
<version>2.2.2</version>
|
||||
<configuration>
|
||||
<failOnNoGitDirectory>false</failOnNoGitDirectory>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>get-the-git-infos</id>
|
||||
<goals>
|
||||
<goal>revision</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
|
||||
<resources>
|
||||
@@ -64,15 +69,6 @@
|
||||
<!--Replace variables-->
|
||||
<filtering>true</filtering>
|
||||
</resource>
|
||||
|
||||
<!--Add the license to jar in order to see it in the final jar-->
|
||||
<resource>
|
||||
<!--Parent folder-->
|
||||
<directory>${basedir}/..</directory>
|
||||
<includes>
|
||||
<include>LICENSE</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</resources>
|
||||
</build>
|
||||
</project>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>com.github.games647</groupId>
|
||||
<artifactId>fastlogin</artifactId>
|
||||
<version>1.9</version>
|
||||
<version>1.10</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>2.4.3</version>
|
||||
<version>3.0.0</version>
|
||||
<configuration>
|
||||
<createDependencyReducedPom>false</createDependencyReducedPom>
|
||||
<shadedArtifactAttached>false</shadedArtifactAttached>
|
||||
@@ -43,15 +43,6 @@
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>3.0.2</version>
|
||||
<configuration>
|
||||
<outputDirectory>${outputDir}</outputDirectory>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user