Compare commits

...

708 Commits
1.1 ... 1.11

Author SHA1 Message Date
a9e4c90970 Merge pull request #799 from games647/dependabot/maven/org.mariadb.jdbc-mariadb-java-client-3.0.5
Bump mariadb-java-client from 3.0.4 to 3.0.5
2022-06-15 13:15:15 +02:00
0af22d9927 Merge pull request #811 from Enginecrafter77/main
Support for transparent proxies / DNAT
2022-06-11 08:40:48 +02:00
5abd864fa5 fix: Typo in config.yml comment about useProxyAgnosticResolver 2022-06-10 11:55:31 +02:00
d7d0caf6e7 style: Re-phrasing of config.yml comment for useProxyAgnosticResolver
The comment for useProxyAgnosticResolver in config.yml was re-phrased and supplied with additional information regarding its effective context and its relation to the prevent-proxy setting in server.properties.
2022-06-10 11:40:21 +02:00
edd82f7978 style: ProxyAgnosticMojangResolver: Extracted the URL into a constant
The URL of the mojang session server call in ProxyAgnosticMojangResolver was extracted into a constant in order to mitigate the "magic constants" problem.

Additionally, the 204 in the response code check was replaced by a constant from HttpURLConnection, again, in order to not use any magic numbers.
2022-06-10 11:38:22 +02:00
6413ca4d10 fix: Reverted the invalid session log entry modification
The initial debug-entry modification regarding the invalid session message was reverted. Now the logging and parameter expansion is done solely by the SLF4J library.
2022-06-10 11:14:10 +02:00
5f494e5a04 feat: Config option to enable ProxyAgnosticMojangResolver
A config option was added to allow users to use the ProxyAgnosticMojangResolver if they feel the need to do so. This setting won't affect existing users since it's disabled by default.
2022-06-09 19:24:53 +02:00
5d4483224d refactor: ProxyAgnosticMojangResolver
ProxyAgnosticMojangResolver serves as a cleaner implementation of the hasJoined static method from previous commit. All the reflection involved was removed due to no longer being necessary. This way, a lot cleaner code could be made. All the inner workings remained the same. For more information on how it works, check out ProxyAgnosticMojangResolver.java.
2022-06-09 19:04:49 +02:00
61b86fba52 fix: Initial addressing of #810
This commit contains a crude but functional fix for problem described by issue #810. It works by reimplementing the MojangResolver#hasJoined method using reflection to access the inaccessible fields. The significant difference is that unlike in the CraftAPI implementation, which sends the "ip" parameter when the hostIp parameter is an IPv4 address, but skips it for IPv6, this implementation ommits the "ip" parameter also for IPv4, effectively enabling transparent proxies to work.

WARNING: This commit contains only a proof-of-concept code with crude hacks to make it work with the least amount of code edits. Improvements upon the code will be included in following commits. The code is at the moment really ineffective, and therefore SHOULD NOT BE USED IN PRODUCTION! You have been warned.
2022-06-09 18:41:03 +02:00
6665c0359b Note latest proxy version 2022-06-08 10:20:15 +02:00
896291be53 Bump BungeeCord required version
Fixes #808
2022-06-08 10:08:15 +02:00
5828e1abde Remove last sodion reference 2022-06-07 18:51:22 +02:00
8d50a14371 Drop SodionAuth plugin, because it's no longer reachable in Maven 2022-06-07 18:26:24 +02:00
0ae18f3ac6 Always log invalid state messages
Related #803
Related #806
2022-06-07 18:24:27 +02:00
82d8b4d2be Fix typo in messages for invalid-request 2022-06-07 18:18:28 +02:00
41890679ed Bump mariadb-java-client from 3.0.4 to 3.0.5
Bumps [mariadb-java-client](https://github.com/mariadb-corporation/mariadb-connector-j) from 3.0.4 to 3.0.5.
- [Release notes](https://github.com/mariadb-corporation/mariadb-connector-j/releases)
- [Changelog](https://github.com/mariadb-corporation/mariadb-connector-j/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mariadb-corporation/mariadb-connector-j/compare/3.0.4...3.0.5)

---
updated-dependencies:
- dependency-name: org.mariadb.jdbc:mariadb-java-client
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-05-30 07:03:27 +00:00
fc50c997ba Dump craftapi for updated GSON version and allow . in legit usernames 2022-05-21 14:02:37 +02:00
b04b8f5806 Merge pull request #790 from games647/dependabot/github_actions/github/codeql-action-2
Bump github/codeql-action from 1 to 2
2022-05-20 12:34:03 +02:00
16f8a49b4c Inform users about the delayed session start for proxies 2022-05-20 12:32:58 +02:00
830d6b1e35 Bump github/codeql-action from 1 to 2
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 1 to 2.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/v1...v2)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-05-01 07:04:13 +00:00
a32791687c Merge pull request #774 from games647/dependabot/github_actions/actions/checkout-3
Bump actions/checkout from 2 to 3
2022-04-01 13:21:52 +02:00
5d63d304e6 Merge pull request #775 from games647/dependabot/github_actions/actions/cache-3.0.1
Bump actions/cache from 2.1.4 to 3.0.1
2022-04-01 13:21:29 +02:00
f25d8b3a99 Bump actions/cache from 2.1.4 to 3.0.1
Bumps [actions/cache](https://github.com/actions/cache) from 2.1.4 to 3.0.1.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v2.1.4...v3.0.1)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-04-01 07:02:15 +00:00
cac96408a0 Bump actions/checkout from 2 to 3
Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 3.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v2...v3)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-04-01 07:02:13 +00:00
0bdcc935ad Merge pull request #771 from games647/dependabot/maven/org.apache.maven.plugins-maven-shade-plugin-3.3.0
Bump maven-shade-plugin from 3.2.4 to 3.3.0
2022-03-30 17:34:12 +02:00
07dea4ac0f Bump maven-shade-plugin from 3.2.4 to 3.3.0
Bumps [maven-shade-plugin](https://github.com/apache/maven-shade-plugin) from 3.2.4 to 3.3.0.
- [Release notes](https://github.com/apache/maven-shade-plugin/releases)
- [Commits](https://github.com/apache/maven-shade-plugin/compare/maven-shade-plugin-3.2.4...maven-shade-plugin-3.3.0)

---
updated-dependencies:
- dependency-name: org.apache.maven.plugins:maven-shade-plugin
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-30 14:04:11 +00:00
f4a3fed017 Highlight build definition 2022-03-30 16:02:52 +02:00
0105c29710 Reduce memory consumption of anti bot feature 2022-03-30 16:02:52 +02:00
d11cd4f9a1 Ignore META-Info in shading 2022-03-30 16:02:52 +02:00
6c0baea278 Make questions section more specific 2022-03-30 16:02:52 +02:00
55270702a5 Document event behavior that is platform specific 2022-03-30 16:02:52 +02:00
9d2c346235 Use platform scheduler to reuse threads between plugins
The platforms usually use a caching thread executor. It makes to use
this executor to reuse threads as they are expensive to create.
2022-03-30 16:02:51 +02:00
1d3fcb9929 Use custom repository settings for faster fetching
Order of repository seems to indicate maven priority fetching
2022-03-30 16:02:51 +02:00
35b044651c Update dependencies
diff --git a/bukkit/pom.xml b/bukkit/pom.xml
index e57b9d2..939697a 100644
--- a/bukkit/pom.xml
+++ b/bukkit/pom.xml
@@ -272,7 +272,7 @@
         <dependency>
             <groupId>com.lenis0012.bukkit</groupId>
             <artifactId>loginsecurity</artifactId>
-            <version>3.0.2</version>
+            <version>3.1</version>
             <scope>provided</scope>
             <optional>true</optional>
             <exclusions>
diff --git a/core/pom.xml b/core/pom.xml
index 15a1eb7..3738e9d 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -179,7 +179,7 @@
         <dependency>
             <groupId>com.google.code.gson</groupId>
             <artifactId>gson</artifactId>
-            <version>2.8.9</version>
+            <version>2.9.0</version>
         </dependency>
     </dependencies>
 </project>
2022-03-30 16:02:51 +02:00
666fabd76c Bump mariadb-java-client from 3.0.3 to 3.0.4
Bumps [mariadb-java-client](https://github.com/mariadb-corporation/mariadb-connector-j) from 3.0.3 to 3.0.4.
- [Release notes](https://github.com/mariadb-corporation/mariadb-connector-j/releases)
- [Changelog](https://github.com/mariadb-corporation/mariadb-connector-j/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mariadb-corporation/mariadb-connector-j/compare/3.0.3...3.0.4)

---
updated-dependencies:
- dependency-name: org.mariadb.jdbc:mariadb-java-client
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-28 13:17:13 +02:00
5d49c497b5 Bump slf4j-jdk14 from 2.0.0-alpha6 to 2.0.0-alpha7 (#756)
Bumps [slf4j-jdk14](https://github.com/qos-ch/slf4j) from 2.0.0-alpha6 to 2.0.0-alpha7.
- [Release notes](https://github.com/qos-ch/slf4j/releases)
- [Commits](https://github.com/qos-ch/slf4j/compare/v_2.0.0-alpha6...v_2.0.0-alpha7)

---
updated-dependencies:
- dependency-name: org.slf4j:slf4j-jdk14
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-03-28 13:16:30 +02:00
812cf21ad0 Merge pull request #750 from Smart123s/license/autoheader
Fix License header
2022-03-14 14:54:17 +01:00
e7a915a6cd Check license headers before building
If a header is missing, then the build should fail. If someone commits
a new file, WITHOUT building it beforehnd, than that header won't be
added. The CI will build the plugin, but changes made by the CI are not
retained in the source tree.
2022-03-13 10:41:33 +01:00
a856356c49 Update license headers to 02db104
Done using `mvn license:format`
2022-03-13 10:08:57 +01:00
e9ea930be7 Update copyright year in LICENSE 2022-03-13 10:06:17 +01:00
59eb9ced8b Merge pull request #741 from games647/dependabot/github_actions/actions/setup-java-3
Bump actions/setup-java from 2.3.0 to 3
2022-03-01 08:32:54 +01:00
60d414eacb Bump actions/setup-java from 2.3.0 to 3
Bumps [actions/setup-java](https://github.com/actions/setup-java) from 2.3.0 to 3.
- [Release notes](https://github.com/actions/setup-java/releases)
- [Commits](https://github.com/actions/setup-java/compare/v2.3.0...v3)

---
updated-dependencies:
- dependency-name: actions/setup-java
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-01 07:02:58 +00:00
ae93b3215a Merge pull request #737 from Smart123s/fix/736
Fix 'no-conflict' option of 'autoLoginFloodgate' in config
2022-02-26 17:37:37 +01:00
7e9bd1639b Move similar config checks to a common function
'autoRegisterFloodgate' and 'autoLoginFloodgate' have the same possible
options, and they are checked against the player in the exact same way.
For example, if either one of them is set to isLinked, linked status is
checked in the same way, regardless of wether it's checked for login or
for registering.
2022-02-25 18:14:25 +01:00
817eedd4ac Move autoLoginFloodgate check to a common function
Reduces duplicate code.
Added missing check for "no-conflict".
2022-02-25 18:13:44 +01:00
033fdd55ae [ci-skip] Update proxy setup guide 2022-02-15 12:01:13 +01:00
d581b34005 Use mariadb protocol if using its connector
MariaDB enforces this to ensure the selection of the correct driver.

Fixes #724
2022-02-09 12:57:14 +01:00
3665e15920 Add velocity driver hint
Related #723
2022-02-09 12:57:14 +01:00
6f16700cdf Merge pull request #717 from games647/dependabot/maven/org.mariadb.jdbc-mariadb-java-client-3.0.3
Bump mariadb-java-client from 2.7.5 to 3.0.3
2022-02-07 18:07:34 +01:00
8e6221d846 Fix delayed force login for Floodgate players
Login checks are done by bungee, so Bukkit doesn't have to do anything.
The session is set for them by the plugin messages, however, force login
may be delayed. In that case, the player should be logged in at
the onPlayerJoin event.
However, FloodgateAuthTask was run at onPlayerJoin, even if the player
allready had a valid login session. And FloodgateAuthTask always deffers
force login if bungee is present.
As a result, the Floodgate player will never get logged in, if the force
login was delayed by the plugin message.

Co-authored-by: BOT-Neil <neilbooth125@gmail.com>
2022-02-07 17:30:51 +01:00
7a049b98a6 Update distribution 2022-02-07 17:18:10 +01:00
8de1546e7b Update craftapi to fix broken prevent-proxy api
Related #718
2022-02-04 16:37:13 +01:00
7cce0f6e4a Bump mariadb-java-client from 2.7.5 to 3.0.3
Bumps [mariadb-java-client](https://github.com/mariadb-corporation/mariadb-connector-j) from 2.7.5 to 3.0.3.
- [Release notes](https://github.com/mariadb-corporation/mariadb-connector-j/releases)
- [Changelog](https://github.com/mariadb-corporation/mariadb-connector-j/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mariadb-corporation/mariadb-connector-j/compare/2.7.5...3.0.3)

---
updated-dependencies:
- dependency-name: org.mariadb.jdbc:mariadb-java-client
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-31 07:02:36 +00:00
999738ef3e Merge pull request #715 from Smart123s/fix/bukkit/noplibload
Fix plugin start without ProtocolLib installed
2022-01-29 11:55:55 +01:00
7951c4c893 Fix plugin startup without ProtocolLib installed
Referencing `ProtocolLibrary` in FastLoginBukkit (even in an unreachable
code block) causes Bukkit servers to throw a NoClassDefFoundError on
startup.
Fix based on commit 0082cc6536
2022-01-29 10:48:27 +01:00
9b04ea5c89 Merge pull request #707 from games647/dependabot/maven/org.mariadb.jdbc-mariadb-java-client-2.7.5
Bump mariadb-java-client from 2.7.4 to 2.7.5
2022-01-25 20:27:13 +01:00
ac66cefd33 Bump mariadb-java-client from 2.7.4 to 2.7.5
Bumps [mariadb-java-client](https://github.com/mariadb-corporation/mariadb-connector-j) from 2.7.4 to 2.7.5.
- [Release notes](https://github.com/mariadb-corporation/mariadb-connector-j/releases)
- [Changelog](https://github.com/mariadb-corporation/mariadb-connector-j/blob/2.7.5/CHANGELOG.md)
- [Commits](https://github.com/mariadb-corporation/mariadb-connector-j/compare/2.7.4...2.7.5)

---
updated-dependencies:
- dependency-name: org.mariadb.jdbc:mariadb-java-client
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-20 10:51:24 +00:00
8a01ddc231 Do not shade the MultiMap class
This class is used by ProtocolLib. Calling it, means we use its signature. With relocating this would also update the method
call signature to `.WrappedGameProfile.getProperties()Lfastlogin/guava/collect/Multimap;`, which obviously not present.
2022-01-20 11:50:32 +01:00
3bcc6c2e94 Differentiate between rate limit and IOExceptions
(Related #698)
2022-01-14 14:22:11 +01:00
b351338e0b Allow disabling anti bot completely 2022-01-14 14:13:43 +01:00
36c9ae2465 Fix rate limiter
Time reported by nanoTime is arbitrarily and could include negative numbers
2022-01-14 14:03:14 +01:00
e0f823cbe4 Fix rate limiter blocking the first requests
If the server just started, expireTime can become negative. Therefore, the first uninitialized values will not be made available.
2022-01-14 13:20:44 +01:00
17234a791b Update logging dependency 2022-01-14 12:56:22 +01:00
0e935e3ad0 Fail safe if command is not specified in plugin.yml 2022-01-14 12:53:51 +01:00
52d778afb1 Clean up 2022-01-14 12:52:46 +01:00
e6eb4939b4 Document generics 2022-01-14 12:50:53 +01:00
36337f7feb Fix potential thread-safety issues in storing the profile 2022-01-14 12:50:07 +01:00
aa51e98fe2 Declare nullable variants using jetbrains annotations 2022-01-14 12:16:30 +01:00
a5c7e7371d Try out to minimize unnecessary classes from big dependencies 2022-01-14 12:14:22 +01:00
35b493a708 Typo fixes 2022-01-14 12:12:47 +01:00
b02a1a54d9 Add velocity platform to support tickets 2022-01-14 12:02:19 +01:00
253da03f9c Add missing descriptions 2022-01-14 12:01:25 +01:00
4a5516c9f9 Merge pull request #566 from Smart123s/fg-plib-fix
Workaround for Floodgate prefixes with ProtocolLlib
2021-12-23 12:28:15 +01:00
3ca8ae694d Merge pull request #682 from games647/dependabot/maven/me.clip-placeholderapi-2.11.0
Bump placeholderapi from 2.10.10 to 2.11.0
2021-12-21 19:19:47 +01:00
1d7c2aed61 Bump placeholderapi from 2.10.10 to 2.11.0
Bumps placeholderapi from 2.10.10 to 2.11.0.

---
updated-dependencies:
- dependency-name: me.clip:placeholderapi
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-12-20 07:01:45 +00:00
03850ae4f2 Only add Floodgate prefixes if they are needed
Without this patch, Java players would also get a prefix.
2021-12-09 19:40:46 +01:00
b92911bf26 Made floodgatePrefixWorkaround configurable 2021-12-09 18:03:29 +01:00
8859ebb454 Manually append Floodgate Prefixes
This can be used as a workaround for #493
This will leave
821be02bdb/spigot/src/main/java/org/geysermc/floodgate/addon/data/SpigotDataHandler.java
in a limbo state, but it shouldn't have a noticable impact on neither
performance nor stability.
This commit will try append prefixes to every player, even if it's not
needed of if Floodgate isn't installed.
2021-12-09 18:01:58 +01:00
06a8d6c625 Merge pull request #674 from Smart123s/fix/geyser/mc-1.18
Update to Geyser 2.0 API (fix 1.18)
2021-12-07 19:24:24 +01:00
8c33813e45 Update to Geyser 2.0 API 2021-12-06 19:22:54 +01:00
95251b611a Initialize logger factory by doing a first call to it
Related #670
2021-12-03 10:22:33 +01:00
6c47abc76d Update dependencies 2021-12-03 09:46:26 +01:00
9c2068032f Remove duplicate dependency 2021-12-03 09:46:26 +01:00
2110e93bd6 Override slf4j transitive dependency from paper
Fixes #670
2021-12-03 09:46:25 +01:00
7439a95e16 Reduce the amount of necessary dependencies by dropping transitive ones 2021-12-02 14:40:29 +01:00
e1c1da199e Search SL4J JDK provider in our own classpath
Using the previous behavior it would look for the service file and provider
in the server jar. First it requires relocating the service file to our JDK
provider and let the service only be queryable from our plugin jar.

Fixes #668

Search SL4J JDK provider in our own classpath

Using the previous behavior it would look for the service file and provider
in the server jar. First it requires relocating the service file to our JDK
provider and let the service only be queryable from our plugin jar.

Fixes #668
2021-12-02 14:40:26 +01:00
829ba79400 Merge pull request #659 from AlbeMiglio/main
Added latest Guava and SLF4J compatibility
2021-12-01 16:34:14 +01:00
1dd27ff529 Restore 1.8 compatibility by shading guava in Spigot versions 2021-12-01 16:29:37 +01:00
2cdfdcb5c5 Added latest Guava and SLF4J compatibility
(1.18+ breaks support to older guava methods)
2021-12-01 01:12:38 +01:00
15fee92937 Detect enabled Velocity support in server 2021-11-29 13:12:19 +01:00
cb29c5e226 Merge pull request #655 from games647/dependabot/maven/com.velocitypowered-velocity-api-3.1.0
Bump velocity-api from 3.0.1 to 3.1.0
2021-11-28 14:04:50 +01:00
d8cd39a974 Merge pull request #654 from games647/dependabot/maven/io.papermc-paperlib-1.0.7
Bump paperlib from 1.0.6 to 1.0.7
2021-11-28 14:04:44 +01:00
9a2bc14b72 Bump velocity-api from 3.0.1 to 3.1.0
Bumps velocity-api from 3.0.1 to 3.1.0.

---
updated-dependencies:
- dependency-name: com.velocitypowered:velocity-api
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-11-28 12:48:34 +00:00
f5a60ca0b3 Bump paperlib from 1.0.6 to 1.0.7
Bumps [paperlib](https://github.com/PaperMC/PaperLib) from 1.0.6 to 1.0.7.
- [Release notes](https://github.com/PaperMC/PaperLib/releases)
- [Changelog](https://github.com/PaperMC/PaperLib/blob/master/CHANGELOG.md)
- [Commits](https://github.com/PaperMC/PaperLib/compare/1.0.6...v1.0.7)

---
updated-dependencies:
- dependency-name: io.papermc:paperlib
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-11-28 12:46:15 +00:00
10a7b01bc7 Merge branch 'Smart123s-feature/geyser/allowOfflineNameConflict' 2021-11-28 13:45:33 +01:00
909f263189 Update GH Actions to build with Java 16 2021-11-28 13:45:25 +01:00
2f61a8f8ad Add packet level checks for Geyser
If AuthType == ONLINE, players will be treated as if they were Java
players
If AuthType == OFFLINE, name conflicts will be checked the same way it's
done with Floodgate
Updated config.yml to infrom about the changes.
2021-11-28 13:45:23 +01:00
d0a7832929 Remove unused Floodgate 1.0 dependency 2021-11-28 13:44:57 +01:00
2ac638f3f9 Merge bedrock packet checks into a single function
Floodgate and Geyser specific checks can now be modified without
changing JoinManagement.
Added the ability to resume Java specific checks for Bedrock players.
This will be neccessary for Geyser `auth-type=online` players
2021-11-28 13:44:57 +01:00
af0bc34255 Use local Floodgate instance
Shortens code and fixes an unused warning
2021-11-28 13:44:57 +01:00
fcd2aa95f0 Use BedrockService in JoinManagement
Since the code only needs to interact with Geyser, if Floodgate is not
installed, and perform similar things with both, it's reasonable, to
merge their code.
This commit breaks premium checking with `auth-type=online` in Geyser
2021-11-28 13:44:57 +01:00
f76c7bd62f Create generalized BedrockService class 2021-11-28 13:44:57 +01:00
f570474fa3 Detect Geyser connections at packet level
It is possible to use Geyser without Floodgate by configuring Geyser to
use auth-type= 'online' or 'offline'. In that scenario, floodgateService
will be either unavailable or empty.
2021-11-28 13:44:57 +01:00
3ee6cb2ada Create stub GeyserService
The FloodgateService and GeyserService classes are not merged,
because Geyser can work without Floodgate.
Added Geyser as 'softdepends' in plugin.yml and bungee.yml
to make it load before FastLogin.
Also made Floodgate a soft dependency in bungee.yml.
2021-11-28 13:44:56 +01:00
5612ca744b Fix relocating mariadb driver in Velocity
Fixes #643
2021-11-04 10:29:57 +01:00
d2c94af4a7 Merge pull request #636 from Smart123s/fix/floodgate/pluginmessage
Fix "No Session" in plugin messaging
2021-10-24 20:23:53 +02:00
4c514c269b Fix "No Session" in plugin messaging
After Bungee recieved a plugin message from Bukkit, that it has
completed login/register for a Floodgate player, bungee would throw a
NullPointerException: Cannot invoke "BungeeLoginSession.getProfile()"
because "loginSession" is null
2021-10-24 18:24:50 +02:00
40b4eae756 Merge pull request #635 from Smart123s/fix/floodgate/prefix-detect
Fix Floodgate player detection (caused by prefixes)
2021-10-23 17:38:14 +02:00
9978fe69d5 Fix Floodgate detection for buggy ProtocolLib 2021-10-23 15:56:16 +02:00
94299d2547 Prefixed names for packet level Floodgate checks 2021-10-23 13:24:40 +02:00
ef6f0fc436 Fix detecting floodgate on plugin name
Floodgate's name is lowercase
Related #630
2021-10-11 16:09:24 +02:00
1f83a656cc Merge pull request #629 from Krakenied/main
Fix #627 - skip Floodgate config validation if there's no Floodgate installed
2021-10-10 13:38:29 +02:00
d7e0a4469f Fix #627 - skip Floodgate config validation if there's no Floodgate installed
Fix BungeeManager NPE
2021-10-09 19:52:26 +02:00
28480a0f01 Add floodgate service to Bungee 2021-10-05 15:42:40 +02:00
a3bf875976 Encapsulate floodgate hooks
Related #619
Related #620
2021-10-05 15:35:43 +02:00
11c91e6428 Merge pull request #622 from BOT-Neil/main
Update JoinManagement.java Dont Check empty for empty prefix
2021-10-05 14:39:36 +02:00
8490ff628c Update JoinManagement.java 2021-10-01 17:15:05 +02:00
0d7b8e237d Update JoinManagement.java
dont check for empty prefixes
2021-10-01 13:29:02 +02:00
8b4d4586a7 Fix compiling 2021-09-23 14:56:30 +02:00
cd55441e4e Allow configuring SSL requirements for MySQL instances
Fixes #433
2021-09-23 14:55:23 +02:00
17ecb186a5 Require full verification if SSL is enabled in MySQL 2021-09-23 14:43:24 +02:00
aaff7710e0 Merge pull request #612 from juanmuscaria/main
Velocity support
2021-09-22 12:47:21 +02:00
37ac04c8ed Reformat 2021-09-22 12:13:33 +02:00
c2ec8c93b0 Use the MariaDB driver for a smaller footprint 2021-09-22 12:03:44 +02:00
665881d19a Exclude gson already present in velocity 2021-09-22 12:01:08 +02:00
5192b98d78 Use java runner for cache 2021-09-22 11:48:59 +02:00
709edc6c0a Added comment to the config to notify about the different driver name 2021-09-17 10:04:05 -03:00
c458bd383a Only remove the texture property from the player profile if forwardSkin is disabled 2021-09-17 10:00:45 -03:00
64fbbf759f Actually handle proxy shutdown this time 2021-09-17 09:45:09 -03:00
20379d13b2 Commented why org.slf4j is excluded from the final jar 2021-09-17 09:40:12 -03:00
310ef4068c Make PROXY_ID_fILE static 2021-09-17 09:37:50 -03:00
bc4d9857b4 Ops, printing exception directly 2021-09-17 09:36:51 -03:00
d0491d44ec Remove left over assertion 2021-09-17 09:35:07 -03:00
7ade127888 Relocate the MySql Driver 2021-09-16 13:42:36 -03:00
68a783bd40 Use path directly for loading the proxy uuid 2021-09-16 13:25:35 -03:00
352702eae4 Some code formatting and cleanup 2021-09-15 20:08:03 -03:00
b6dfa4802a Removed old FIXME and added a new TODO 2021-09-15 19:58:29 -03:00
fc226e1010 Moved LoginEvent logic to GameProfileRequestEvent. Allow overwriting online mode uuid and support for forwardSkin. Added one second delay for login command. 2021-09-15 19:58:29 -03:00
4befb35af9 Added a way to generate or use a predefined proxy uuid. 2021-09-15 19:58:29 -03:00
01632ec125 Cleanup unused imports 2021-09-15 15:44:51 -03:00
268c70bc51 Use org.slf4j.Logger directly 2021-09-15 13:54:28 -03:00
ec7c421f83 [Security] Check if cracked user have the Floodgate prefix (#614) 2021-09-15 16:50:55 +02:00
f82c85d3eb Ops, seems like velocity repo already mirrors the latest driver. 2021-09-14 12:29:49 -03:00
6e318ba9bb Use logger for exceptions 2021-09-14 12:26:52 -03:00
89d03bcc6b I missed that there was an alternative for bungee in the documentation 2021-09-14 12:22:22 -03:00
502b16a0e7 Fill all plugin metadata properly. 2021-09-14 12:12:04 -03:00
de0655cba2 Changed the database driver to mysql 2021-09-14 11:59:22 -03:00
0e7d8a595d Added missing license header for some classes. 2021-09-14 11:36:26 -03:00
7178ea4587 Seems like this is generated automatically. 2021-09-13 21:29:56 -03:00
e639e29dee Partial velocity support. 2021-09-13 21:24:46 -03:00
d8021931b6 Merge pull request #606 from Smart123s/fg-nostorage-fix
Don't crash if no storage is loaded
2021-08-29 20:48:47 +02:00
47ee2cf458 [ci skip] Merge pull request #609 from cmod31/patch-1
BungeeCord/Waterfall section
2021-08-29 10:56:32 +02:00
d564d74443 BungeeCord/Waterfall section
Added some detail.
issue #607
2021-08-29 10:47:05 +02:00
586b357be8 Don't crash if no storage is loaded
If the pluign is running on Bukkit, and it's connected to Bungee
then core.getStorage() will be null.
If that's the case, players will be logged in via a plugin messages.
2021-08-26 13:40:41 +02:00
172efafc2b Merge pull request #602 from Smart123s/fg-bungee-handshake
Fix plugin messages for Floodgate players
2021-08-23 17:17:56 +02:00
056b8a7af7 Recieve success message for Floodgate
Floodgate players are offline players, so a special check needs to be
done to save Floodgate players as if they were online players.
2021-08-22 13:33:20 +02:00
198e8f9ea2 Fix Floodgate without auth plugin
If no auth plugin is installed, rely on StoredProfile
This fixes #594
2021-08-22 13:33:16 +02:00
f39c3a1ea6 Update Floodgate repository 2021-08-20 11:49:54 +02:00
7dd0aa5bca Limit parallelism to one thread for SQLite
Fixes #596
2021-08-14 17:54:57 +02:00
f8fe3d7d71 Do not silence exceptions for Floodgate 2021-08-12 20:39:14 +02:00
4d4ecf3da7 Merge pull request #583 from games647/dependabot/maven/org.slf4j-slf4j-jdk14-1.7.32
Bump slf4j-jdk14 from 1.7.31 to 1.7.32
2021-07-26 10:06:59 +02:00
f80059987b Bump slf4j-jdk14 from 1.7.31 to 1.7.32
Bumps [slf4j-jdk14](https://github.com/qos-ch/slf4j) from 1.7.31 to 1.7.32.
- [Release notes](https://github.com/qos-ch/slf4j/releases)
- [Commits](https://github.com/qos-ch/slf4j/commits)

---
updated-dependencies:
- dependency-name: org.slf4j:slf4j-jdk14
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-07-26 07:02:17 +00:00
6e0272d92c Fix receiving login packet twice with ProtocolLib
Fixes #578
Commit that introduces the bug:
1f4757669c
2021-07-16 14:24:17 +02:00
758ccb9bf2 Clean up 2021-07-14 16:33:31 +02:00
0515ac193d Extract protocollib method detection 2021-07-14 16:33:18 +02:00
864e551a88 Disable verbose verification output
NameChange fix is resolved
2021-07-14 16:32:32 +02:00
0a24d36ef8 [ci skip] Rephrase development build section
Fixes #576
2021-07-12 17:25:52 +02:00
8a535e1ad9 [ci skip] Merge pull request #574 from MAX-XiaoKui/patch-1
Update plugin SodionAuth's link. Its URL has changed.
2021-07-10 17:18:08 +02:00
ae66f1ab35 Update plugin SodionAuth's link 2021-07-10 22:04:54 +08:00
ed725d991e [ci skip] Show user clickable links 2021-07-06 11:25:52 +02:00
e45d4b3482 [ci skip] Require what happened form 2021-07-06 11:20:36 +02:00
b5b5faaefb [ci skip] Add feedback message to forms 2021-06-30 17:23:54 +02:00
51a28ae93f [CI-SKIP] Fix issue template syntax 2021-06-30 17:04:16 +02:00
d2fcdade0b [CI-SKIP] Try out issue forms 2021-06-30 17:02:19 +02:00
cde045d6fd Only build for main branches to fix duplicate actions with dependabot 2021-06-29 12:55:37 +02:00
ccdc455ce9 Clarify naming and structure 2021-06-22 14:07:47 +02:00
4e1f5e9401 Add icons to issue templates 2021-06-22 13:25:57 +02:00
2e64b52261 Migrate questions to discussions 2021-06-22 13:25:41 +02:00
1f4757669c Allow packet modifications by other plugins
If we disable filters, other plugins are advised to only monitor
packets. Potential modifications can be allowed by enabling the filtering again, but adding our meta data to prevent duplicate
processing.
2021-06-22 11:15:21 +02:00
496817afca Increase the rate limit for bigger servers
Related #406
2021-06-21 18:00:45 +02:00
daf6f06a00 Name anti-bot feature explicitly for own rate limiting 2021-06-21 17:33:38 +02:00
4be0e1333d [CI-SKIP] Add issue template descriptions 2021-06-21 17:08:20 +02:00
58cee1e26e Exclude more transitive dependencies
This could reduce the number of required
downloads as well as to prevent download
from legacy HTTP non secure (not HTTPS)
sources.
2021-06-21 17:08:20 +02:00
9f89756820 Disable plugin if dependencies not available
Related #554
2021-06-21 17:08:20 +02:00
d1daec436c Merge pull request #559 from Smart123s/fg2-ln-autoreg
Add 'linked' option to 'autoRegisterFloodgate'
2021-06-21 16:31:25 +02:00
2626c21bc8 Add 'linked' option to 'autoRegisterFloodgate'
This makes sense with Floodgate 2.0's Global Link API
2021-06-21 15:15:18 +02:00
5607a19312 Merge pull request #560 from Smart123s/fg-player-to-account
Update messages
2021-06-21 14:30:46 +02:00
1f950397a4 Merge pull request #561 from games647/dependabot/maven/org.slf4j-slf4j-jdk14-1.7.31
Bump slf4j-jdk14 from 1.7.30 to 1.7.31
2021-06-21 12:07:45 +02:00
08e26dd60f Bump slf4j-jdk14 from 1.7.30 to 1.7.31
Bumps [slf4j-jdk14](https://github.com/qos-ch/slf4j) from 1.7.30 to 1.7.31.
- [Release notes](https://github.com/qos-ch/slf4j/releases)
- [Commits](https://github.com/qos-ch/slf4j/commits)

---
updated-dependencies:
- dependency-name: org.slf4j:slf4j-jdk14
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-06-21 07:03:13 +00:00
2ffe7e0daf Replace word 'Player' with 'Account' in messages.
Technically speaking, one player can own both the Java and the Bedrock
account with the same name, so it's more appropriate to say account
instead of player.
2021-06-20 17:44:46 +02:00
33261b9ea6 Floodgate 2.0 is no longer in a seperate branch 2021-06-20 17:37:18 +02:00
5442f773ae Merge pull request #558 from Smart123s/fg-sharedtask
Add Floodgate config options for BungeeCord
2021-06-20 14:03:45 +02:00
fd3da28bec Use Override for non core code in FloodgateAuthTas 2021-06-20 13:35:31 +02:00
9350f52916 Create a seperate function for isNameCheckRequired 2021-06-20 13:23:51 +02:00
7c2bb9c9a4 Don't spam log when with unregistered floodgate 2021-06-20 13:05:04 +02:00
0b307a95a3 Add further documentation to config.yml
Floodgate options are no longer Bukkit only
autoRegister/LoginFloodgate needs normal (Java) autoRegister/Login
to be set to ture
2021-06-20 11:26:15 +02:00
ed627ce438 Add autoLogin and register for Floodgate Bungee 2021-06-20 11:26:14 +02:00
af83604c94 Remove existing Floodgate integration from Bungee 2021-06-20 11:26:13 +02:00
8f43cc0978 Partially move FloodgateAuthTask to Core
The moved code can be used in a BungeeCord implementation
2021-06-20 11:26:13 +02:00
578028719f Merge pull request #556 from Smart123s/patch-5
FloodgateNameConflict no longer needs autoLogin
2021-06-19 10:43:09 +02:00
b87b59be12 FloodgateNameConflict no longer needs autoLogin
The removed line is not valid after 1f3cd5fa5b
2021-06-18 23:25:46 +02:00
f17f9d983a Merge pull request #551 from games647/codeql
CodeQL Test
2021-06-17 14:52:09 +02:00
6cbd64b06d Cache autobuild process 2021-06-17 14:23:18 +02:00
8d588ff7db Document codeql workflow file 2021-06-17 14:23:17 +02:00
92e05c460f Create CodeQL scanning workflow 2021-06-17 14:23:17 +02:00
b9011b6141 Add support for 1.17
Fixes #552
2021-06-15 20:46:25 +02:00
7fdbedb8c1 Merge pull request #533 from Smart123s/fg-bk-nonconfict
Add 'no-conflict' option to some Floodgate config entries
2021-06-15 11:30:06 +02:00
baede33abc Make Bedrock check nullsafe
If the UUID was not set by FloodGate or any other plugin, it's null
by in BungeeCord

Fixes #548
2021-06-15 10:31:53 +02:00
2e83eaaa81 Remove unused import 2021-06-14 19:42:56 +02:00
fb8780d006 Prevent duplicate name check API call
Previously: If allowFloodgateNameConflict = false or linked and either autoLoginFloodgate or
autoRegisterFloodgate is set to no-conflict and a player with a non-conflicting name
connects then there will be a double check.
2021-06-14 19:16:40 +02:00
4c0797d5ea No longer treat linked Floodgate players as conflicting names
Since linked players inherit the name of the Java player, it'll always conflict a Java player 's name
2021-06-14 19:16:04 +02:00
235d7f8d80 Add 'no-conflict' as an option to config.yml value checking 2021-06-14 19:16:04 +02:00
dbf10f4767 Remove unnecessary whitespaces 2021-06-14 19:16:02 +02:00
fed1e91b28 Document 'no-conflict' in config.yml 2021-06-14 19:09:54 +02:00
dc8dd13d5b Expand possible values in config.yml 2021-06-14 19:09:54 +02:00
165e5fe856 Add 'no-conflict' option to some Floodgate config entries 2021-06-14 19:09:43 +02:00
01c9b55d80 Merge pull request #545 from games647/dependabot/github_actions/actions/setup-java-2.1.0
Bump actions/setup-java from 1.4.3 to 2.1.0
2021-06-14 10:37:40 +02:00
4cf660559e Merge pull request #532 from Smart123s/fg-bk-ncshare
Share Floodgate name conflict check between Protocol Plugins
2021-06-14 10:37:11 +02:00
11c38bdb15 Set required distribution name 2021-06-14 10:35:10 +02:00
0d9e30be8d Bump actions/setup-java from 1.4.3 to 2.1.0
Bumps [actions/setup-java](https://github.com/actions/setup-java) from 1.4.3 to 2.1.0.
- [Release notes](https://github.com/actions/setup-java/releases)
- [Commits](https://github.com/actions/setup-java/compare/v1.4.3...v2.1.0)

---
updated-dependencies:
- dependency-name: actions/setup-java
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-06-13 18:08:09 +00:00
edb4179ba6 Ignore provided guava dependency 2021-06-13 20:06:44 +02:00
e0ddfec921 Drop not needed specific time and number limits 2021-06-13 20:06:44 +02:00
b2fef5e6d6 Update GitHub actions with dependabot too 2021-06-13 20:06:44 +02:00
02db1041e2 Be explicit about the license name 2021-06-13 20:06:43 +02:00
04428fe146 Merge pull request #543 from Smart123s/fix-warnings
Get rid of several warning messages
2021-06-13 19:42:20 +02:00
9656aadb35 Remove unused imports 2021-06-13 16:50:36 +02:00
c0d21fdca2 Remove 'handlerFound' from ConnectListener 2021-06-13 16:48:01 +02:00
c1cf80fe28 Remove 'initialHandlerClazzFound' from ConnectL... 2021-06-13 16:44:57 +02:00
9acab1e453 Don't store 'floodgateVersion' in ConnectListener 2021-06-13 16:43:38 +02:00
d51335c6de Don't store 'ultraAuthPlugin' 2021-06-13 16:36:04 +02:00
2fdcc34575 Don't store 'plugin' in BukkitScheduler 2021-06-13 16:33:47 +02:00
66a51a8e13 Remove unused 'serverId' from BukkitLoginSession 2021-06-13 16:31:39 +02:00
411148b560 No longer reference 'Floodgate' in JoinManagement
Referencing 'FloodgatePlayer' in JoinManagement.java and it's subclasses
has cause ProtocolLib to fail to register an event when Floodgate was not
installed.
2021-06-13 14:24:14 +02:00
d1109ac75e Merge pull request #542 from Smart123s/patch-4
Add license header to FloodgateAuthTask.java
2021-06-12 20:08:30 +02:00
af0ef2aed9 Stop ProtocolSupport from crying
If I ever tried to either cast or use FloodgatePlayer as a return type
when Floodgate was not installed in the server, I got this error:

[19:37:46 ERROR]: [FastLogin] Plugin FastLogin v1.11-SNAPSHOT-744264d has failed to
register events for class
com.github.games647.fastlogin.bukkit.listener.protocolsupport.ProtocolSupportListener
because org/geysermc/floodgate/api/player/FloodgatePlayer does not exist.

ProtocolLib doen't have this problem.
2021-06-12 19:57:32 +02:00
ee2b3a37f8 Move Floodgate name conflict check to Core 2021-06-12 19:57:31 +02:00
0e8ad6e318 Add license header to FloodgateHook.java 2021-06-12 19:56:36 +02:00
bc8fffe063 Add license header to FloodgateAuthTask.java 2021-06-12 19:51:15 +02:00
757d0ef991 Fix & Move allowFloodgateNameConflict=linked 2021-06-05 18:43:23 +02:00
2d5a53a2f9 Kick player if Floodgate name conflict checking fails
Rebased on  Sat May 22 13:48:29 2021 +0200
Fixed typos in strings
2021-06-05 18:43:23 +02:00
9a6112ebd4 Made ProtocolSupport check for Floodgate name conflicts
Rebased on Sat May 22 11:42:34 2021 +0200
Initialize FloodgateHook in constructor
This way, it won't have to be initialized whenever a player joins
2021-06-05 18:42:30 +02:00
1f3cd5fa5b Share Floodgate name conflict check between Protocol Plugins
Added a shared class for Floodgate name conflict checking that can be used by both ProtocolLib and ProtocolSupport

Rebased on Sat May 22 11:42:08 2021 +0200
Added access modifier to "FastLoginBukkit plugin;" in FloodgateHook.java

Rebased on Sat May 22 11:42:08 2021 +0200
Initialize FloogateHook in ProtocolLib's class
2021-06-05 18:40:15 +02:00
e8bb9fd30a Merge pull request #534 from Smart123s/patch-3
Warn in config: linked players conflict their Java name
2021-05-23 13:53:20 +02:00
0d5413d402 Warn in config: linked players conflict their Java name 2021-05-22 21:17:11 +02:00
119b9cb000 Merge pull request #494 from Smart123s/main
Floodgate support for Bukkit
2021-05-16 20:50:53 +02:00
d44ab4e634 Warn when unsupported plugins are detected
Floodgate 1.0 does not work with the current Bukkit implementation.
ProtocolLib and Floodgate don' play along nicely when used with FastLogin
Related issue: #493
2021-05-16 17:54:50 +02:00
b2b61539e1 Revert "Move Floodgate conflict chechking to core"
This reverts commit b0ef1a59ac.
2021-05-16 17:54:50 +02:00
b9dd921885 Change plugin.yml to softepend on Floodgate 2.0
Rebased to remove Bungee related changes
2021-05-16 17:53:22 +02:00
6b20b71403 Merge pull request #530 from Smart123s/patch-2
Change dependency Floodgate Bungee to API
2021-05-16 16:08:14 +02:00
40b9405c1d Change dependency Floodgate Bungee to API
Same functionality, less code to be downloaded.
2021-05-15 11:58:43 +02:00
1385f0c730 Merge pull request #528 from Smart123s/patch-1
Actually assign the queried version to a variable
2021-05-13 17:21:47 +02:00
a8c1570dd3 Actually assign the queried version to a variable
The Floodgate version was queried, but it's return value wasn't actually assigned to a variable.

Edited this from mobile GitHub website. I have no code check here.
2021-05-13 15:12:13 +02:00
3d65a33e0c Merge pull request #527 from TechnicallyCoded/feature/floodgate-v2.0
Implement floodgate api version 2.0
2021-05-13 15:07:21 +02:00
2700b3ee0e Implement floodgate api version 2.0 2021-05-12 11:49:15 +02:00
5858bfb443 Add Floodgate name conflict check to ProtocolSupport 2021-05-09 11:10:10 +02:00
b0ef1a59ac Move Floodgate conflict chechking to core
The FloodgateApi is the same for Bukkit and Bungee, so the Floodgate
related code could be used in a future Bungee implementation too.

Currently, Bungee will report Floodgate disabled to core, so the
upstream Floodgate implementation will be used there.
If enough code will be moved to core, I might consider enabling these
features to BungeeCord too.
2021-05-09 11:01:36 +02:00
25254b2393 Add vscode to .gitignore 2021-05-09 09:09:52 +02:00
08b864220d Merge branch 'main' of https://github.com/games647/FastLogin 2021-05-09 09:06:59 +02:00
dbe9ac2903 Merge pull request #525 from Smart123s/fg2-bc
Fix Floodgate 2.0 on BungeeCord
2021-05-08 19:18:24 +02:00
1b7b2ff2b5 Add support for Floodgate 2.0 2021-05-08 16:26:58 +02:00
d01c368cdb Fix identation in bukkit/pom.xml 2021-05-08 14:46:00 +02:00
75750f8417 Apostrophes are no longer necessary in config.yml 2021-05-08 14:20:56 +02:00
85a1abfaac Describe how UUIDs work for 'allowFloodgateNameConflict' in config.yml 2021-05-08 13:38:38 +02:00
5a263956fe Fixed Java auto login/register 2021-05-06 11:56:24 +02:00
c1d3f278f7 Merge pull request #520 from games647/dependabot/add-v2-config-file
Upgrade to GitHub-native Dependabot
2021-04-30 13:32:08 +02:00
7fe6d0ae2b Upgrade to GitHub-native Dependabot 2021-04-29 15:33:48 +00:00
d3be664c3d Merge pull request #517 from games647/dependabot/maven/com.mycila-license-maven-plugin-4.1 2021-04-28 07:49:29 +00:00
1edfc83e65 Merge pull request #518 from games647/dependabot/maven/junit-junit-4.13.2 2021-04-28 07:44:10 +00:00
727a16a9c0 Bump junit from 4.13.1 to 4.13.2
Bumps [junit](https://github.com/junit-team/junit4) from 4.13.1 to 4.13.2.
- [Release notes](https://github.com/junit-team/junit4/releases)
- [Changelog](https://github.com/junit-team/junit4/blob/main/doc/ReleaseNotes4.13.1.md)
- [Commits](https://github.com/junit-team/junit4/compare/r4.13.1...r4.13.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-04-27 21:26:42 +00:00
12dccaebe8 Bump license-maven-plugin from 4.0 to 4.1
Bumps license-maven-plugin from 4.0 to 4.1.

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-04-27 21:26:22 +00:00
0d598ad390 Moved config value checking
Previously the value for "autoLoginFloodgate" and "autoRegisterFloodgate" was checked every time a player joined.
Now they are checked at startup.
2021-04-11 11:47:56 +02:00
a078bb8214 Add missing return 2021-04-11 10:13:43 +02:00
7192dcdf07 Merge pull request #504 from InkerBot/main
Add SodionAuthBungee support
2021-04-07 10:48:56 +02:00
2c3fd4a575 Return when first auth plugin found 2021-04-07 00:30:11 +08:00
ab1d3ded63 Fix errors 2021-04-07 00:12:58 +08:00
83a6cb4bb4 Add log info and fix errors 2021-04-07 00:11:22 +08:00
fd045e9ed7 Add SodionAuth softDepend 2021-04-06 22:16:27 +08:00
fb98dd8393 Void "Hook" in plugin name 2021-04-06 22:07:26 +08:00
f766e6213e Use a loop to hook AuthPlugin 2021-04-06 21:58:53 +08:00
68d23e127a Bump SodionAuth version and fix errors. 2021-04-06 21:28:36 +08:00
8837a1b70e Bump SodionAuth version and fix errors. 2021-04-06 21:27:25 +08:00
9568556ceb Merge pull request #503 from games647/dependabot/maven/pl.project13.maven-git-commit-id-plugin-4.0.4 2021-04-06 13:02:28 +00:00
ea894b3a7b Add SodionAuthBungee hook 2021-04-06 00:31:17 +08:00
0c45c30d2f Bump SodionAuth version 2021-04-06 00:30:18 +08:00
17e16b68b2 Bump git-commit-id-plugin from 4.0.3 to 4.0.4
Bumps git-commit-id-plugin from 4.0.3 to 4.0.4.

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-04-05 16:17:24 +00:00
d6a02aa538 Apply license headers to every file
(Related #494)
2021-04-05 11:04:42 +02:00
9e8a9508d7 Re-use FloodgatePlayer in FloodgateAuthTask 2021-03-30 20:03:09 +02:00
600f484963 Added function isValidConfigValue() 2021-03-30 20:00:21 +02:00
94fdc70164 Update config.yml
Added Floodgate 2.0 as a requirement
Removed warning about linking accounts with "allowFloodgateNameConflict:
'false'" as it's no longer a problem with Global Linking API enabled
2021-03-29 17:15:45 +02:00
c3c0827cfc Drop duplicate package goal 2021-03-29 15:19:53 +02:00
870d1ee281 Migrate to Floodgate v2.0
This removes support for Floodgate 1.x
2021-03-29 14:49:33 +02:00
e0790965ec Catch NoSuchMethodError
Fixes #499
2021-03-29 11:56:02 +02:00
4bc328c37c Fail safely if BungeeCord implementation specific classes are not found 2021-03-29 11:56:02 +02:00
357430b5ad Put configuration values into extra lines to highlight the environment
They are not configuration options

Related #488
2021-03-29 11:56:02 +02:00
f7fd94e983 Update config comments 2021-03-22 21:25:27 +01:00
d8b9822c11 Fix "Asynchronous player kick" 2021-03-22 20:46:49 +01:00
e2e4e76fd9 Moved getGeyserPlayer() to the only class it's used in 2021-03-22 20:35:22 +01:00
f9992f1447 Fixed 'allowFloodgateNameConflict' for 'false' value. 2021-03-22 19:57:13 +01:00
5d94e610ff Create a profile for Bedrock players when registering 2021-03-22 19:51:46 +01:00
9abc99ebc2 Use UUID instead of name when checking for Geyser player 2021-03-22 18:37:12 +01:00
07da8fc76a autoRegisterFloodgate will no longer try to log in unregistered BE users
previously if autoRegisterFloodgate was set to false and the player was
not registered, the plugin tried to auto-login the user, which has led
to misleading "Failed to login" messages
2021-03-22 17:22:51 +01:00
305700497e Re-added an empty line (deleted by mistake) 2021-03-22 11:43:30 +01:00
8c8ed0b639 Made FloodgateAuth async 2021-03-22 10:16:23 +01:00
ee2ae7f9fd Use authPlugin.isRegistered() instead of profile.isSaved() 2021-03-21 19:21:49 +01:00
3e9e433736 Move dependencies 2021-03-21 11:07:18 +01:00
c41896e5f2 Use isPluginEnabled() in NameCheckTask.java 2021-03-21 11:02:29 +01:00
78f897a490 Use isPluginEnabled() in ConnectionListener.java
Co-authored-by: games647 <games647@users.noreply.github.com>
2021-03-21 10:59:58 +01:00
e1af75191e Implement autoLoginFloodgate & autoRegisterFloodgate config options
Knwon Bug: Profile.isSaved() is 'false' when logging in from Bedrock
after auto registering through Bedrock so FastLogin will try to register
for a second time, insted of logging in
2021-03-20 18:28:32 +01:00
a23f846146 Implement allowFloodgateNameConflict
Check config.yml for details.
2021-03-20 17:16:55 +01:00
0206a6c5a4 Added new config options (not yet implemented) 2021-03-20 15:37:37 +01:00
6544e007f6 Code cleanup 2021-03-20 14:41:21 +01:00
0cbf2a4e20 Check if Geyser and Floodgate are installed before accessing them 2021-03-20 13:38:41 +01:00
57e797f1be Implement auto login for Floodgate players
This is buggy in most cases.
2021-03-20 12:22:41 +01:00
e03e67b8fa Detect if a player is connecting through Floodgate
The Floodgate API requires UUID which is inaccessible at the level
FastLogion operates on.
A workaround for this is to check if the currently connecting player is
also a part of the Geyser server's online players list.
*TODO: Check for Java and Bedrock name conflicts with multiple
configurations.*
2021-03-19 08:58:29 +01:00
176781d55a Merge pull request #474 from games647/dependabot/maven/de.xxschrandxx.bca-BungeeCordAuthenticator-0.0.2 2021-03-17 15:41:41 +00:00
d9f254fbff Merge pull request #473 from games647/dependabot/maven/com.comphenix.protocol-ProtocolLib-4.6.0
Bump ProtocolLib from 4.5.1 to 4.6.0
2021-03-09 20:57:41 +01:00
6b617d993f Bump BungeeCordAuthenticator from 0.0.2-SNAPSHOT to 0.0.2
Bumps BungeeCordAuthenticator from 0.0.2-SNAPSHOT to 0.0.2.

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-02-22 16:16:07 +00:00
09ab7288da Bump ProtocolLib from 4.5.1 to 4.6.0
Bumps [ProtocolLib](https://github.com/dmulloy2/ProtocolLib) from 4.5.1 to 4.6.0.
- [Release notes](https://github.com/dmulloy2/ProtocolLib/releases)
- [Commits](https://github.com/dmulloy2/ProtocolLib/compare/4.5.1...4.6.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-02-22 16:15:32 +00:00
ff2e5c0435 Drop package goal for the core module 2021-02-18 18:25:37 +01:00
30c233c953 Fix workflow version 2021-02-18 18:21:32 +01:00
6fd81e8e29 Sort supported list alphabetically 2021-02-18 18:17:52 +01:00
4765372b0a Add BungeeAuthenticator and UserLogin 2021-02-18 18:16:06 +01:00
303c064416 Check for valid file access first 2021-02-14 17:06:44 +01:00
a15de80c7d Revert "Ignore the correct channel"
This reverts commit 465519ee
Namespace key class did the lowercase operation
2021-02-11 10:22:50 +01:00
25356db175 Clean up CI 2021-02-09 16:33:37 +01:00
465519eece Ignore the correct channel 2021-02-09 16:33:37 +01:00
16f131ee2d Limit expire timer 2021-02-09 16:33:36 +01:00
43046bea97 Fix branch references 2021-02-09 16:33:36 +01:00
aa734853c6 Implement BungeeCordAuthenticator (#454)
* Create BungeeCordAuthenticatorHook

* Update bungee.yml

* Update FastLoginBungee.java

* Some small changes

* Test Hook

* Update pom.xml

* Update pom.xml

* Rebuild with new spigotplugins-repo

* Disable checksum for spigotplugins

* Update pom.xml

* Update pom.xml

* Update pom.xml

* Update pom.xml

* Update pom.xml

* Removed spigotplugins-repo

* Update pom.xml

* Update pom.xml

* Update pom.xml

* Update pom.xml

* Update pom.xml

* Update pom.xml

* Update pom.xml

New url

* Update pom.xml

* Update pom.xml

* Update FastLoginBungee.java

* requested changes

* Delete BungeeCordAuthenticator-0.0.2-SNAPSHOT.jar
2021-02-09 14:41:21 +01:00
28fb40d063 Merge pull request #453 from games647/dependabot/maven/com.zaxxer-HikariCP-4.0.1 2021-02-09 12:36:45 +00:00
29c633f68a Merge pull request #459 from Kamilkime/main
Fix name change detection, allow FastLogin to respect AuthMe registration limits
2021-02-09 10:59:33 +01:00
c079653d09 Always return true when registering with AuthMe 2021-02-09 10:42:10 +01:00
7fbfa8240c Allow FastLogin to respect AuthMe pre IP register limit, fixes #458 2021-02-09 01:28:12 +01:00
1ab346d067 Correct name check on PaperSpigot, fixes #423, fixes #437, fixes #457 2021-02-09 00:28:05 +01:00
50a8fd2f79 Bump HikariCP from 3.4.5 to 4.0.1
Bumps [HikariCP](https://github.com/brettwooldridge/HikariCP) from 3.4.5 to 4.0.1.
- [Release notes](https://github.com/brettwooldridge/HikariCP/releases)
- [Changelog](https://github.com/brettwooldridge/HikariCP/blob/dev/CHANGES)
- [Commits](https://github.com/brettwooldridge/HikariCP/compare/HikariCP-3.4.5...HikariCP-4.0.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2021-02-01 16:25:09 +00:00
9ff404a424 Update CI workflow 2020-11-10 12:33:31 +01:00
5029701f2a Merge pull request #418 from Kamilkime/master
Update encryption to handle MC 1.16.4, fixes #412
2020-11-09 14:38:31 +01:00
1f63f91f17 Add comments, make cipher variable names clearer 2020-11-09 13:25:16 +01:00
71d84d1748 Make encryption method reflections static 2020-11-08 17:15:26 +01:00
64072d84d8 Update encryption to handle MC 1.16.4, fixes #412 2020-11-08 03:52:39 +01:00
f2441ecc15 Merge pull request #408 from games647/dependabot/maven/junit-junit-4.13.1 2020-11-04 11:18:44 +00:00
fe1499f1fb Add support for 1.16.4
Fixes #412
Fixes #411
2020-11-04 11:05:34 +01:00
e74288e676 Bump junit from 4.13 to 4.13.1
Bumps [junit](https://github.com/junit-team/junit4) from 4.13 to 4.13.1.
- [Release notes](https://github.com/junit-team/junit4/releases)
- [Changelog](https://github.com/junit-team/junit4/blob/main/doc/ReleaseNotes4.13.1.md)
- [Commits](https://github.com/junit-team/junit4/compare/r4.13...r4.13.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-10-12 16:20:06 +00:00
d034e71331 Do not move and create a new proxy file
Fixes #389
2020-08-06 10:10:00 +02:00
7e43b99f91 [CI-SKIP] Dump placeholderapi to prevent deprecates on register 2020-08-03 21:06:22 +02:00
496a036fb4 Merge pull request #385 from ishlandbukkit/master
Add SodionAuthHook
2020-08-02 17:17:58 +02:00
fa1f85698a Fix NoSuchMethodError for PlaceholderAPI after breaking change
Fixes #386
2020-07-31 17:02:51 +02:00
25b6ca7653 Convert proxy file to the new name 2020-07-31 17:02:51 +02:00
b30eb0de29 Debug verified username usage in ProtocolLib 2020-07-31 17:02:51 +02:00
c96d64597a Clear proxies file name 2020-07-31 17:02:51 +02:00
0ef2000f2a Rename rate limit to join limit 2020-07-31 17:02:51 +02:00
a36ac7f9e3 Remove unneeded SodionAuth-Libs 2020-07-29 16:11:41 +08:00
2e38550db0 I figured it out FINALLY 2020-07-29 16:07:24 +08:00
4e2b6c7810 Use JitPack and update API reference 2020-07-29 10:58:04 +08:00
4acbcbfd19 Squash previous commits
* Add SodionAuth

* Add Comment

* add SodionAuth

Co-authored-by: logos <1102280066@qq.com>
2020-07-29 09:22:17 +08:00
cae2f8f58d Implement FastLoginPremiumToggleEvent 2020-06-27 13:58:38 +02:00
de1487b6aa Fix driver warning message not displaying 2020-06-06 18:38:51 +02:00
96cdac1332 Ignore proxied bedrock players (Fixes #328) 2020-06-06 18:32:08 +02:00
2601fea84f Drop no longer needed uuid getter 2020-06-06 16:29:52 +02:00
2bd339b0bf Use username from Mojang for offline IDs
Affected systems: BungeeCord after name change
Effects: Carry on items, permissions, etc. from the old user account without access to the new one

After a name change it could happen that the client still only
knows the old username and will send it to the server. Mojang will provide us with an up-to-date username that we should use instead.

The username is also sent to Mojang, so that they could verify the use. Therefore exploiting this behavior extensively for arbitrary usernames is not possible.

Related #344
2020-05-25 13:41:41 +02:00
286b755ee3 Log player handling
Related #354
2020-05-23 21:40:10 +02:00
5c5b7384a4 Better explain anti bot configuration values 2020-05-21 10:25:01 +02:00
7c125dc0b6 Add more debugging messages for delayed proxy logins
Related #352
2020-05-21 10:03:56 +02:00
103a8320ec Log requested premium logins 2020-05-20 19:50:22 +02:00
49df461643 Reduce work threads, because processing is async 2020-05-19 11:47:08 +02:00
3b69904257 Extract login decryption int utilities 2020-05-19 11:46:10 +02:00
b41d56f835 Fix reflection access 2020-05-19 09:57:39 +02:00
b1797c84d9 Prevent duplicate save requests 2020-05-18 16:10:39 +02:00
9941a69c6d Use invokeExact for better performance 2020-05-18 13:21:07 +02:00
0ee269785a Set UUIDs using MethodHandles 2020-05-18 12:12:51 +02:00
c74c8f5fee Throw exception on unexpected UUIDs 2020-05-15 15:14:14 +02:00
0ad3a853b5 Guard against any potential modifications for UUID changes
Related #344
2020-05-15 14:49:49 +02:00
42637813e8 Add missing cases from last commit 2020-05-15 14:44:31 +02:00
feee64309e Warn if FastLogin doesn't login authenticated players
Related #351
2020-05-15 14:38:22 +02:00
b9cf8f0498 Log setting the offline UUID on Bungee
Related #344
2020-05-15 14:12:18 +02:00
30a763dbc4 Add introduction about closing issues 2020-05-14 15:11:01 +02:00
42790b27f8 Unregister variable on disable 2020-05-14 13:45:23 +02:00
c58eda983a Fix parsing of placeholder status
Fixes #341
2020-05-14 13:41:32 +02:00
477d3df1dd Favor nano time, because it's monotonic 2020-05-13 22:40:26 +02:00
c1dc69845e [Experimental] Enable paranoid to hide MySQL details 2020-05-13 21:27:53 +02:00
c3f6881e4b Add missing default values for the rate limiter 2020-05-13 19:51:05 +02:00
25b380f74a Add a basic rate limiter for incoming connections
Related #347
2020-05-13 19:47:02 +02:00
a5777869c8 Re-order driver specific properties 2020-05-13 19:46:02 +02:00
cf3e8434a2 Prefer more MySQL performance optimizations 2020-05-13 19:46:02 +02:00
5881be9b78 Merge pull request #348 from games647/dependabot/maven/com.zaxxer-HikariCP-3.4.5 2020-05-12 14:22:39 +00:00
58c42f9f7f Bump HikariCP from 3.4.3 to 3.4.5
Bumps [HikariCP](https://github.com/brettwooldridge/HikariCP) from 3.4.3 to 3.4.5.
- [Release notes](https://github.com/brettwooldridge/HikariCP/releases)
- [Changelog](https://github.com/brettwooldridge/HikariCP/blob/dev/CHANGES)
- [Commits](https://github.com/brettwooldridge/HikariCP/compare/HikariCP-3.4.3...HikariCP-3.4.5)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-05-11 16:26:14 +00:00
f40e787b55 Delay force action until PlayerJoinEvent is fired
Fixes Paper async chunk loading -> delayed PlayerJoinEvent
(Related #331)
2020-05-09 18:49:43 +02:00
b6a95bb153 Delay force login command for async logins on Paper from Bungee
Related #331
2020-05-07 16:25:10 +02:00
9bae5c3f79 Merge pull request #339 from games647/dependabot/maven/me.clip-placeholderapi-2.10.6 2020-05-05 11:46:47 +00:00
b5ccc1df2e Bump placeholderapi from 2.10.5 to 2.10.6
Bumps placeholderapi from 2.10.5 to 2.10.6.

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-05-04 16:26:59 +00:00
d829c71438 Merge pull request #334 from games647/dependabot/maven/com.zaxxer-HikariCP-3.4.3 2020-04-29 10:36:11 +00:00
9cf5431d1a Merge pull request #335 from games647/dependabot/maven/com.lenis0012.bukkit-loginsecurity-3.0.2 2020-04-28 08:36:50 +00:00
e0a7f207c5 Bump loginsecurity from 3.0.1 to 3.0.2
Bumps loginsecurity from 3.0.1 to 3.0.2.

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-04-27 16:21:02 +00:00
efb4c34b50 Bump HikariCP from 3.4.2 to 3.4.3
Bumps [HikariCP](https://github.com/brettwooldridge/HikariCP) from 3.4.2 to 3.4.3.
- [Release notes](https://github.com/brettwooldridge/HikariCP/releases)
- [Changelog](https://github.com/brettwooldridge/HikariCP/blob/dev/CHANGES)
- [Commits](https://github.com/brettwooldridge/HikariCP/compare/HikariCP-3.4.2...HikariCP-3.4.3)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-04-27 16:20:09 +00:00
68d35c75c2 Bump maven-shade-plugin from 3.2.2 to 3.2.3
Bumps [maven-shade-plugin](https://github.com/apache/maven-shade-plugin) from 3.2.2 to 3.2.3.
- [Release notes](https://github.com/apache/maven-shade-plugin/releases)
- [Commits](https://github.com/apache/maven-shade-plugin/compare/maven-shade-plugin-3.2.2...maven-shade-plugin-3.2.3)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-04-26 11:49:33 +02:00
0d93a2c371 Bump placeholderapi from 2.10.4 to 2.10.5
Bumps placeholderapi from 2.10.4 to 2.10.5.

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-04-26 11:49:33 +02:00
6091a228ab Don't rely on toString to build unique session keys
Fix IPv6 compatibility which contains brackets

Related #331
2020-04-25 17:05:08 +02:00
6f99b6e9b4 Add explicit Junit dependency 2020-03-20 16:13:45 +01:00
109e19e6da Add missing synchronized modifiers 2020-03-20 16:13:45 +01:00
65469ed579 Make scheduler platform dependent 2020-03-20 13:44:58 +01:00
8ecb5657d3 Drop nullable annotation usage to compile
Thanks CI
2020-03-14 19:06:05 +01:00
b0cf6e39c7 Drop forced dependency 2020-03-14 19:01:09 +01:00
88a526b5bf Drop support for Minecraft 1.7 2020-03-14 18:51:27 +01:00
57b84509da Limit the total number of running threads
(Related #304)
2020-03-14 18:20:34 +01:00
e0bc7d914c Load the after auth plugins
Fixes #306

There seems to be some capability problems
that it doesn't correctly load ProtocolLib before
this plugin. Furthermore if we use
events for our hooks they print out a warning.
2020-03-10 09:59:31 +01:00
f1933f735a Merge pull request #305 from games647/dependabot/maven/pl.project13.maven-git-commit-id-plugin-4.0.0
Bump git-commit-id-plugin from 3.0.0 to 4.0.0
2020-03-09 19:18:30 +01:00
f69154418e Bump git-commit-id-plugin from 3.0.0 to 4.0.0
Bumps [git-commit-id-plugin](https://github.com/git-commit-id/maven-git-commit-id-plugin) from 3.0.0 to 4.0.0.
- [Release notes](https://github.com/git-commit-id/maven-git-commit-id-plugin/releases)
- [Commits](https://github.com/git-commit-id/maven-git-commit-id-plugin/compare/v3.0.0...v4.0.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-03-09 16:17:00 +00:00
74b13231f8 bump craftapi to 0.3 (#303) 2020-03-08 12:15:37 +01:00
79627e3b60 Bump slf4j-jdk14 from 1.7.26 to 1.7.30 (#301)
Bumps [slf4j-jdk14](https://github.com/qos-ch/slf4j) from 1.7.26 to 1.7.30.
- [Release notes](https://github.com/qos-ch/slf4j/releases)
- [Commits](https://github.com/qos-ch/slf4j/compare/v_1.7.26...v_1.7.30)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-03-06 19:28:02 +01:00
1722ab3267 Bump maven-shade-plugin from 3.2.0 to 3.2.2 (#302)
Bumps [maven-shade-plugin](https://github.com/apache/maven-shade-plugin) from 3.2.0 to 3.2.2.
- [Release notes](https://github.com/apache/maven-shade-plugin/releases)
- [Commits](https://github.com/apache/maven-shade-plugin/compare/maven-shade-plugin-3.2.0...maven-shade-plugin-3.2.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-03-06 19:27:19 +01:00
fdc0485f05 [CI-SKIP] Use HTTPS and newer URLs in Maven 2020-03-03 10:50:48 +01:00
1068ddbadd Enable Maven cache for runner 2020-03-03 10:47:55 +01:00
b0d5bd606c Rework issue templates 2020-03-03 10:47:37 +01:00
1e59101ada Enable MySQL storage caching 2020-03-03 10:46:43 +01:00
46ac3c74e4 Fix Java setup action version 2020-02-17 15:28:58 +01:00
d22af0f42e Fix unique name 2020-02-17 15:24:45 +01:00
2cc078773b Migrate to GitHub actions 2020-02-17 15:18:58 +01:00
c1b8b60bf7 Use HTTPS for code comments too 2020-02-12 10:59:11 +01:00
c54d6eecfc Merge branch 'JLLeitschuh/fix/JLL/use_https_to_resolve_dependencies' 2020-02-12 10:57:30 +01:00
0cf6c4c188 Use HTTPS instead of HTTP to resolve dependencies
This fixes a security vulnerability in this project where the `pom.xml`
files were configuring Maven to resolve dependencies over HTTP instead of
HTTPS.

Signed-off-by: Jonathan Leitschuh <Jonathan.Leitschuh@gmail.com>
2020-02-12 10:55:08 +01:00
282467d21b Set a default kick reason if nothing is specified 2020-01-08 10:44:10 +01:00
e8a5dc7433 Use BungeeCord event methods for kicking the player 2020-01-07 20:35:16 +01:00
4b45932d6a Migrate to asynchronous calls to LoginSecurity
(Fixes #290)
2020-01-06 10:04:44 +01:00
1375cf3997 Fix ProtocolLib version 2019-12-29 13:35:33 +01:00
5a5cf016d9 Remove on blacklist status on join too 2019-12-29 13:33:18 +01:00
2c7e569653 Fix force login events being sync instead of async (Fixes #278) 2019-11-09 13:57:12 +01:00
94c5fe302e Merge pull request #277 from FearGames/master
Fix build and add events
2019-11-02 16:41:43 +01:00
c80012ddb1 Allow multi-line messages 2019-10-25 11:54:26 +02:00
17361b1b54 Implement PreLogin and AutoLogin events 2019-10-23 14:45:51 +02:00
426b458a58 Fix BungeeAuth dependency 2019-10-23 12:50:40 +02:00
62a8b939cc Use 'TEXT' for strings in the configuration file 2019-08-16 14:59:14 +02:00
327c8c4c9d Dump dependencies 2019-07-09 12:56:14 +02:00
fbbe7a735a Initialize the decryption cipher only once
(Related #259)
2019-07-09 12:52:54 +02:00
4110ce2fa2 Fix creating logging session in ProtocolSupport environments
Fixes #251
2019-05-11 21:16:38 +02:00
28743d23bf Remove duplicate repository 2019-05-06 17:39:21 +02:00
145bd95679 Drop usage of deprecated apache lang library 2019-05-06 17:37:13 +02:00
3fe17cf8d9 Merge pull request #250 from lenis0012/patch-2
Update LoginSecurity maven repository
2019-05-06 17:36:15 +02:00
3446d4c443 Update LoginSecurity maven repository 2019-05-06 11:35:39 +02:00
c528433079 Allow username only database logins
Related #247
2019-04-15 20:35:56 +02:00
25858ea11f Enable travis caching 2019-04-14 10:16:53 +02:00
204ffbb2ee Document network requests 2019-04-14 10:16:33 +02:00
9c1ba81cbe Fix running force actions in LoginSecurity thread-safe 2019-04-14 10:16:19 +02:00
15c5857c4f Correctly set the offline UUID in ProtocolSupport (Thanks to @Shevchik) 2018-08-30 12:32:42 +02:00
4b0ad3b186 Update ProtocolSupport hook 2018-08-29 19:49:10 +02:00
101f7207a9 Limit plugin channels to 16 characters for 1.7 support
Fixes #223
2018-08-07 13:41:30 +02:00
64175fe9e8 Update to CraftApi 0.2 2018-07-29 18:47:07 +02:00
f7a10d86eb Don't transform because it's already compatible with 1.13 2018-07-25 16:55:52 +02:00
542aabad73 Lowercase the namespace correctly 2018-07-24 14:33:33 +02:00
260b93a565 Channel names should be lowercase according to the spec
Related #217, #218
2018-07-24 10:17:21 +02:00
6604cca8bd Use Plugin:Subchannel for channel messages
This is required to follow 1.13 spec.
(Related #216, #215)
2018-07-23 14:11:40 +02:00
c172b1ec84 Prevent duplicate message fetching for kick in PLib (Fixes #212) 2018-06-27 18:59:40 +02:00
9b0b8f5fcb Fix repository link of ProtocolLib 2018-06-06 20:17:24 +02:00
20104b2b00 Update ProtocolLib to fix building 2018-06-06 09:33:05 +02:00
fd76d2448e Add advanced options for the connection pool (Fixes #210) 2018-06-04 21:28:19 +02:00
53a1821a9d Fix ProtocolLib repository 2018-06-04 21:27:23 +02:00
1c6f4e82e0 Fix NPE for cracked players on non-bungee environments 2018-05-23 19:14:20 +02:00
084afef899 Update premium UUID on verification (Related #208) 2018-05-04 19:45:43 +02:00
8a9eed3a74 Add /newline variable 2018-05-04 18:54:26 +02:00
1ea6d929b1 Clarify how to configure MariaDB/MySQL correctly 2018-04-26 10:09:40 +02:00
ddc3aa9279 Replace deprecated PropertiesResolveEvent with LoginFinishEvent
affects only ProtocolSupport
2018-04-26 10:08:56 +02:00
2a79a9511b Fix auto register type in BungeeCord not being sent 2018-04-07 16:14:46 +02:00
791df26702 Fix defaults overriding config 2018-04-05 18:42:50 +02:00
cdf1988f2f Fix comping after craftapi update 2018-04-05 17:38:16 +02:00
f476c091bb Fix default message loading, because default values are ignored by .getKeys() 2018-04-05 17:33:32 +02:00
352c72df64 Add note about developments builds 2018-04-02 14:43:43 +02:00
2cd0b194aa We are SNAPSHOT build not a release candidate
Maven versions plugin is great for multi-modules.
Run mvn version:set -DnewVersion=... and that's it.
2018-03-31 10:38:57 +02:00
f2e42019d6 Mention the new FastLogin module names in the setup guide 2018-03-31 10:34:26 +02:00
82ec71e8d0 Update premium status for non-bungeecord setups (Related #200) 2018-03-27 20:43:53 +02:00
6d207d62ba Fix BungeeCord blacklist condition checking 2018-03-25 15:44:51 +02:00
889dab3152 Migrate to PlaceholderExpansion from PlaceholderAPI 2018-03-21 11:02:55 +01:00
71c1f4f12e Remove session in ProtocolSupport directly without expiring 2018-03-18 16:22:03 +01:00
3651c4873c Dump craftapi version 2018-03-17 16:57:05 +01:00
8b613a48cc Forward skin from ProtocolLib verification response 2018-03-16 16:40:12 +01:00
78f8fa1f05 Fix NPE on nullable uuid column 2018-03-16 16:34:31 +01:00
ac5820bb75 Encode enums as integers 2018-03-16 15:15:54 +01:00
c1cb28c996 Add basic API (Fixes #200) 2018-03-16 15:15:03 +01:00
b534765ff8 Always forward premium status to spigot 2018-03-16 15:14:35 +01:00
5bcfdfeb32 Cancel restore session events if it's a premium player (Related #201) 2018-03-16 14:44:35 +01:00
b7c0fd549c Add an explicit warning about the BungeeCord setup guide 2018-03-11 12:27:43 +01:00
61c1364506 Simplify command handling 2018-03-11 11:47:02 +01:00
a29dd849f9 Move shared Mojang client into independant project 2018-03-09 14:39:02 +01:00
3f9eba69ba Generate a public key only for ProtocolLib listener 2018-03-09 13:57:51 +01:00
f250f8071f Optional migration 2018-03-05 21:27:48 +01:00
8272aeac69 Switch to the codemc repo for BungeeCord 2018-03-05 17:37:40 +01:00
4d470be712 Dump AuthMe version 2018-03-05 17:35:33 +01:00
e2c04f2c26 Add isSaved helper 2018-03-02 19:56:17 +01:00
86694982c7 Minor refactoring 2018-03-02 18:29:38 +01:00
04b00f4f22 Add driver available check for more readable error messages 2018-02-24 20:45:22 +01:00
48c2355745 Remove copy-paste misleading package name 2018-02-24 20:45:22 +01:00
cff25c958d Extract BungeeCord message in dedicated classes 2018-02-24 20:45:20 +01:00
06bb4b80dd Add toString methods to all relevant classes 2018-02-24 20:43:18 +01:00
2bdd051a41 Remove universal jar building for a smaller jar footprint and less conflicts with provided dependencies 2018-02-24 20:43:18 +01:00
526a8a9d51 Log invalid proxy id messages 2018-02-24 10:45:52 +01:00
8cbdb66625 Relocate HikariCP and slf4j too to prevent conflicts 2018-02-08 13:24:42 +01:00
e5e815a885 Cancel autologin for AuthMe sessions (Fixes #189, #148, #103) 2018-02-05 15:01:28 +01:00
d0d5bd300b Use static imports for Colectors.* 2018-02-05 12:54:35 +01:00
0c550edb05 Shade the gson dependency to fix compatibility with Minecraft 1.7.10
(Fixes #190)
2018-01-30 13:15:48 +01:00
181ea71222 Readd SSLFactory for rate-limit load balance because direct proxies doesn't work at all 2018-01-28 13:25:10 +01:00
c38692e237 Use ChangeSkins rate-limit message here too 2018-01-28 12:25:25 +01:00
dcef62fa57 Fix FileAlreadyExistsException for sym linked folders 2018-01-27 21:49:32 +01:00
856613a8c7 Update Hikari dependency 2018-01-27 18:47:19 +01:00
3beb8beaeb Migrate tests to assertThat 2018-01-27 18:47:01 +01:00
f3ea7ecbbe Update development builds link 2018-01-27 18:46:43 +01:00
25c725f237 [ci skip] Update LoginSecurity to 2.1.7 to fix compiling 2017-12-01 09:35:37 +01:00
ffe4eb7364 Clarify BungeeCord plugin installation on Spigot 2017-11-25 09:34:38 +01:00
82a258097d Use SecureRandom for passwords 2017-10-30 17:57:01 +01:00
57eff4b3ec Fix NPE for skin apply in ProtocolLib mode (Related #182) 2017-10-15 17:57:24 +02:00
4858049c2a Use direct proxies instead of ssl factories for multiple IP-addresses 2017-10-14 18:25:12 +02:00
bb2cc1b42a Remove local address check (Related #181) 2017-10-12 09:59:16 +02:00
2512c5cf67 Convert local IP addr '-' to . (Related #179) 2017-10-09 10:33:38 +02:00
c7c0782071 Fix address rotating for contacting the Mojang API 2017-10-07 19:48:29 +02:00
df945146b8 Fix debug logging 2017-10-07 19:19:45 +02:00
e32b0232e9 Fix logger init (Fixes #178) 2017-10-04 09:22:21 +02:00
6daa654af8 Fix NPE for Mojang API connector 2017-10-03 15:14:37 +02:00
0f01002564 Optimize issue template 2017-10-03 14:19:34 +02:00
28a20a46fa Fix NPE parsing Mojang uuid 2017-10-03 14:19:02 +02:00
105e00b7e8 Use Instant for timestamps 2017-10-01 17:11:06 +02:00
dce44295d0 Migrate SLF4J logging (Fixes #177) 2017-09-29 16:54:29 +02:00
1f917f3a8d Use Optionals for nullable values 2017-09-24 19:50:42 +02:00
e6c23a4bb5 Use Gson's TypeAdapter for more type safety 2017-09-23 13:56:28 +02:00
66b808c999 Fix compile 2017-09-22 21:41:24 +02:00
2932de5588 Add support for IPv6 proxies 2017-09-22 21:08:24 +02:00
16f7461568 Fix message loading was interacting with the normal config 2017-09-22 20:11:58 +02:00
2f0eb81735 Shade the Bungee-Config implementation because it's platform independent 2017-09-22 20:07:04 +02:00
bb80521ab6 Thermos supports GSON so we could share the json parsing 2017-09-22 18:17:35 +02:00
109508dae6 Clean up using IDE inspections 2017-09-21 15:00:39 +02:00
5bf9b05d30 Fix BungeeAuth Maven repository 2017-09-13 12:34:56 +02:00
7839804a4c Drop support for deprecated AuthMe API 2017-09-12 17:05:18 +02:00
ca58c55eca Remove legacy database migration code 2017-09-08 11:33:14 +02:00
10453fd637 Drop support for RoyalAuth, because it doesn't seem to be supported anymore 2017-09-08 11:30:24 +02:00
d18b734550 Update dependencies 2017-09-08 11:17:05 +02:00
7f51659cc7 Version dump 2017-09-03 20:06:00 +02:00
bb240d3aa0 Refactor encryption implementation
* Simplify utility class and make it more independent from the vendor code
* Create only one cipher object for verification
2017-08-28 12:17:47 +02:00
484855724b Add ip parameter to verify a player doesn't use an authentication proxy.
This doesn't prevent proxy connections in general, but it verifies that
the same IP that is used for connecting to the Minecraft server is also
used for authenticating against the Mojang servers.

This happens if someone uses McLeaks. They use an authentication proxy
in order to hide and control the credentials behind those leaked or
donated accounts. So a user of that service joins the server using
a direct connection, but asks the McLeaks servers to send a relevant
request to the Mojang session-servers in order to pass the premium
verification process.
2017-08-25 13:20:55 +02:00
4ea7968366 Remove Importer to prepare for code refactor 2017-08-24 18:50:37 +02:00
44a47bc97f Set default value for proxies 2017-08-20 21:40:37 +02:00
82cb25f809 Output more informational messages by default 2017-08-19 21:53:07 +02:00
551441cdc4 Add HTTP-proxies support 2017-08-18 16:09:59 +02:00
22a56862b0 Remove mcapi.ca section and fix config typos 2017-08-16 17:18:58 +02:00
edf5933e07 Set the fake offline UUID on lowest priority (-> as soon as possible)
Then every plugin listening on priority level higher than lowest can see that fake UUID

This also fixes race conditions for plugins listening on the same priority as FastLogin before (->low)
(Fixes #167)
2017-08-01 10:29:58 +02:00
c6da04de70 Fix listening for login start packets if ProtocolLib is installed
Another call on ProtocolLib's types removes all previous listening types

Fixes #163
2017-07-25 13:18:08 +02:00
0459b0a5a1 Remove bungee chatcolor for Bukkit to support KCauldron 2017-07-22 08:35:32 +02:00
033333e35c Minor cleanup using inspections + Https
* Use https for maven repositories if possible
* Fix typos
* Merge ProtocolLib listeners into one class
* Upgrade maven plugins and dependencies
2017-07-22 08:27:55 +02:00
6595dc6ac0 Increase hook delay to let ProtocolLib inject the listener 2017-06-30 17:37:57 +02:00
ea44002e91 Update dependencies and format imports 2017-06-30 17:23:46 +02:00
131de8404c Add support for new authme API 2017-06-12 17:26:46 +02:00
fbdd8ffc35 Choose player name casing based on client request.
Since BungeeCord commit 5bc189fbb7e8ca19984544af78a083024404fb2a the name casing is based on
the exact name saved at Mojang. This means it could have breaking effects on FastLogin, because
it performs case-sensitive checks against the database. To provide backwards compatibility with
old data we restore the old implementation access for FastLogin.

Thanks to @Maxetto for pointing this out. This commit basically reverts:
059c3f346e
2017-06-07 21:09:00 +02:00
7db8c78975 Drop support for old authme API 2017-06-04 15:52:01 +02:00
b102f06f8e Update ProtocolLib to fix building 2017-05-27 11:24:43 +02:00
a79e18445a Fix building because the bungee proxy repo is down [ci skip] 2017-05-19 12:01:02 +02:00
cf1a0c1bef Remove ebean util usage to make it compatible with 1.12 2017-05-14 17:11:10 +02:00
059c3f346e Lowercase name inside pendingconnection for comparisons against the database 2017-05-10 17:06:25 +02:00
47db2c7858 Fixed AuthHook (#144)
* Fixed AuthHook

The setServerStarted()-Method is now also called if an extern AuthHook
hooks into FastLogin via the API

* Simplified if-Statement
2017-04-19 14:39:27 +02:00
5bb8640d78 Do not try to hook into a plugin if auth plugin hook is already set using the FastLogin API 2017-04-17 15:22:09 +02:00
881b2ec7bc Fix changelog markdown syntax 2017-04-15 09:42:17 +02:00
194c67cd6f Fix markdown syntax 2017-04-05 09:24:41 +02:00
863607c9a4 Add optional useSSL config option 2017-02-23 09:16:11 +01:00
f37cc0a0db Add commit id to the version 2017-02-14 14:01:57 +01:00
70a81bfcdf Correctly wait for BungeeAuth loading by using the correct depend tag (Fixes #119) 2017-02-10 19:06:57 +01:00
b8d029d6da Remove third party API 2017-02-04 14:09:38 +01:00
c47dd1df80 Fix FileNotFoundEx if the bungee config doesn't exist 2017-01-28 16:38:48 +01:00
4d5b1787b1 Migrate to Java 7 NIO files 2017-01-26 09:52:45 +01:00
8c764220bd Fix duplicate premium username message 2017-01-21 18:02:45 +01:00
9af076b4c4 Fix premium username logging message at the wrong place 2017-01-09 17:57:50 +01:00
22aa9287e9 Fix NoClassDef errors if the optional PlaceholderAPI is not available (Fixes #108) 2017-01-07 18:42:10 +01:00
f08daa9b72 Update bungee-proxy maven repository 2017-01-06 13:00:17 +01:00
bc53743c6b Add placeholder variables 2017-01-06 12:54:02 +01:00
a430a079c9 Do no print auto login message on authme session reuse (Related #101) 2016-12-23 22:12:55 +01:00
f3ac6090f1 Fix bungee online check (Fixes #101) 2016-12-23 10:01:38 +01:00
5ca9b9c59a Add note about firewalling your spigot server if you use BungeeCord 2016-12-22 09:13:58 +01:00
b886d1501f Update LoginSecurity to make it buildable 2016-12-16 15:56:30 +01:00
0082cc6536 Use static builder to make it independent from ProtocolLib without throwing NoClassDefFoundError 2016-12-16 15:49:40 +01:00
7f96d55084 Convert config values to string if casting fails 2016-11-26 13:27:39 +01:00
3851d539f8 Workaround injector class is package private in older versions of ProtocolLib (Fixes #94) 2016-11-26 11:33:15 +01:00
a25d97879f Fail safetly if there session was started (prevents duplicate errors) 2016-11-26 10:06:27 +01:00
41abffdb08 Fix Spigot console command invocation sends result to ingame players 2016-10-20 14:06:18 +02:00
e69eb70377 Update BungeeAuth dependency and use the new API 2016-10-05 10:06:02 +02:00
e924b7a2fa Automatically register players who are not known to the auth plugin
Fixes #85
2016-10-03 13:46:33 +02:00
157ca04691 Fix timestamp parsing in newer versions of SQLite 2016-09-23 12:26:18 +02:00
ae3e03405d No duplicate login's like auth plugins auto logins if it's the same ip 2016-09-23 10:42:25 +02:00
bebb04bdea Share the same force login mangement for less duplicate code 2016-09-22 10:56:31 +02:00
91f41c55de Finally set a value to the API column 2016-09-21 13:24:26 +02:00
1acc825f81 Remove deprecated API methods 2016-09-21 13:22:48 +02:00
87ca00d75d [SwitchMode] Kick the player only if the player is unknown to us 2016-09-21 09:16:19 +02:00
62ffb1a904 [Bukkit] Fix adding to premium whitelist 2016-09-20 13:55:03 +02:00
5075a71843 A few code styling things 2016-09-20 13:32:06 +02:00
da266c7e91 Fix loading of settings 2016-09-19 17:59:45 +02:00
acab4766b1 Remove database migration logging 2016-09-19 15:59:57 +02:00
bef90d11cd Deploy only the universal jar to the target folder 2016-09-18 11:40:21 +02:00
a02acd2d63 Remove the nasty UltraAuth fakeplayer workaround 2016-09-18 10:38:05 +02:00
ca42a7c19e Refactor more code for more Java 8 and Guava usage 2016-09-17 15:19:07 +02:00
b533197f05 Fix config loading in BungeeCord 2016-09-17 15:19:07 +02:00
c94711f315 Fix verb (#79) 2016-09-17 08:02:33 +02:00
ee7af80bf0 Fix travis 2016-09-16 17:41:23 +02:00
17c2099bf1 Make use of the awesome Java 8 features 2016-09-16 17:40:42 +02:00
31d6b67381 Try to upgrade to Java 8. I hope enough people are using it. 2016-09-16 16:31:47 +02:00
4b423c9ccb Update ProtocolSupport and use a maven repository for it now 2016-09-16 10:45:05 +02:00
4292e9aaa0 Less deprecated warnings + Clean up 2016-09-15 11:10:52 +02:00
07d0aededa Fix loading with unloaded configuration values 2016-09-15 10:33:17 +02:00
218bc50c96 Drop support for LoginSecurity 1.X since 2.X seems to be stable 2016-09-14 17:44:32 +02:00
a3b2e33aad Switch to vik1395 repository for BungeeAuth 2016-09-13 09:53:46 +02:00
76f5ba7ed1 Refactor a lot of code + Add Guava v10 as shared library 2016-09-11 21:26:03 +02:00
2cd50d23ad Revert converting auth hooks to the new format
-> backwards compatibility
2016-09-11 20:07:21 +02:00
9f5f61f1c2 Do the same for the password generator 2016-09-11 19:57:27 +02:00
3e9c8e3a7e More shared project code for less errors and less duplication 2016-09-11 18:59:42 +02:00
8e5da01be0 Added configuration to disable auto logins for 2Factor authentication
(Fixes #65)
2016-09-09 16:52:44 +02:00
5022c9aa7b Add cracked whitelist (Fixes #42)
(switch-mode -> switching to online-mode from offlinemode)
2016-09-09 16:40:24 +02:00
ad1ab22586 Test another locale sqlite fix 2016-09-08 11:48:13 +02:00
99ef5ce726 Fix correct cracked permission for bukkit 2016-09-08 10:06:43 +02:00
9b7634a9f3 Fix LogIt repository 2016-09-04 16:35:57 +02:00
115fc2e7ba A try to fix SQLite timestamp parsing 2016-09-04 12:14:28 +02:00
b660951e1e Fix compatibility with older ProtocolLib versions (for 1.7)
because of the missing getMethodAcccessorOrNull method
2016-09-03 10:21:42 +02:00
e495f70ccd Fix logging exceptions on encryption enabling 2016-09-01 20:09:34 +02:00
b35d67b5c0 Add missing add-premium-other message 2016-09-01 19:41:58 +02:00
58ac73a5a9 Fix update username in FastLogin database after nameChange (Fixes #67) 2016-08-31 13:29:06 +02:00
ebe768f7a2 Fix ProtocolSupport autoRegister 2016-08-30 12:27:02 +02:00
d20db79f46 Add second attemp login -> cracked (Fixes #51) 2016-08-29 17:38:46 +02:00
c28d889c1b Remove complex nameChange convert since we now allow duplicate uuids 2016-08-26 13:32:28 +02:00
ad60397851 Added auto login importers 2016-08-25 17:45:09 +02:00
88fdeff3f1 Update documentation 2016-08-25 17:44:51 +02:00
558ee1c92c Fix console support for cracked/premium commands (Fixes# 62) 2016-08-20 17:38:47 +02:00
3e84ebd787 [BungeeAuth] Do not login the player if it's already logged in using
sessions
2016-08-19 22:00:07 +02:00
36d7564c3a Fix race condition when waiting for bukkit message while
bungee redirects player
2016-08-19 21:07:29 +02:00
596caa0573 Invoke forcelogin in BungeeCord only once 2016-08-19 20:52:51 +02:00
fe4331298f Send a message on BungeeCord if there is only an auth plugin 2016-08-18 20:22:04 +02:00
a67d84ef3f Fix race condition in bungee<->bukkit 2016-08-18 20:15:43 +02:00
71362dfd7d Log bungeeauth exceptions 2016-08-18 09:16:06 +02:00
fcd98fce43 Fix Color Code (#54)
Got it wrong the first time.
2016-08-09 19:17:48 +02:00
6c1c4e7286 Fix third-party not premium player detection 2016-08-09 14:24:57 +02:00
164fb735d6 Fix ProtocolSupport BungeeCord 2016-08-07 11:41:03 +02:00
fa1b0970a5 Dump to 1.7.1 2016-08-01 12:59:04 +02:00
974bf498fc Fix protocollsupport autoregister 2016-07-31 11:56:20 +02:00
27c04ff08f Fix BungeeCord autoRegister (Fixes #46) 2016-07-31 09:57:50 +02:00
fb357424e6 Run the plugin message reader async to prevent the timeout event
warning from BungeeCord
2016-07-26 14:30:11 +02:00
c73bb70256 FIx autoregister bug 2016-07-25 19:29:22 +02:00
dc395cdc3f Remove debug code 2016-07-20 20:03:17 +02:00
f7626ab969 Read the fully input from mcapi.ca instead of just one line 2016-07-20 11:54:00 +02:00
5f9802d589 Fix third party profile parsing 2016-07-19 10:47:47 +02:00
642c1621ad Fine tune timeout length 2016-07-13 13:00:25 +02:00
eb965d5a48 Fix importing 2016-07-13 10:05:22 +02:00
457bc9cf47 Fix SQLite drop index 2016-07-12 13:06:27 +02:00
2ab3c6b77c Update AutoIn importer 2016-07-12 12:39:06 +02:00
f27bad02d3 Remove the uuid index for name change conflicts 2016-07-12 12:23:32 +02:00
9334296beb Fix saving on name change 2016-07-10 19:25:37 +02:00
fd9940e6f0 Fix setting skin on Cauldron (Fixes #36) 2016-07-10 13:34:08 +02:00
0745957e79 Fix BungeeCord not setting an premium uuid (Fixes #35) 2016-07-09 13:30:43 +02:00
bb2e60f6e1 State why choose the loginEvent and fix it 2016-07-08 16:47:50 +02:00
d15861b8e5 Use the correct message key 2016-07-08 16:47:50 +02:00
b84b340a77 Consistency language update (#33)
Messages that aren't targeted to the player invoking the command shouldn't start with "You are", that could be confusing.

Changed Warning message to be more eye-catching to the player. Assuming also that next line doesn't take the color code from previous line (otherwise change "&r" with "&6").
2016-07-07 14:51:14 +02:00
c50249edea Switch to mcapi.ca and add configurable number of requests 2016-07-07 12:20:39 +02:00
757ddb905a Make it buildable again 2016-07-04 21:40:42 +02:00
9914b7f358 Fix player entry is not saved if namechangecheck is enabled 2016-07-04 21:26:03 +02:00
bba4eb4eec Ignore all canceled events 2016-07-03 21:28:44 +02:00
2b16f3341f Use the loginevent to send the client the offline uuid
-> skin applies on deactivated premium uuid
2016-07-03 16:58:23 +02:00
167ce66057 Added note about skin forwarding if premium uuids are disabled 2016-07-03 14:18:27 +02:00
8d1021e44c Update fake player methods 2016-06-29 19:04:12 +02:00
a811a741f5 Change to lenis repository 2016-06-29 19:03:52 +02:00
a6348766b3 Reduce the number of lookups if a cracked player already exists 2016-06-20 16:29:39 +02:00
22dcc50950 I'm stupid (Related #27) 2016-06-20 16:16:31 +02:00
bd3494eed0 Fix recursive method invocation (Related #27) 2016-06-20 15:27:43 +02:00
1aba9a0f3b Added us.mcapi.com as third-party APIs to workaround rate-limits
(Fixes #27)
2016-06-20 14:12:29 +02:00
6faf00e1bf Support for making requests to Mojang from different IPv4 addresses
(Related #27)
2016-06-20 13:52:37 +02:00
0d89614f3c Add support for the new LoginSecurity version 2016-06-18 14:39:47 +02:00
b009658eea Fix typo in BungeeCord message key 2016-06-16 15:10:25 +02:00
2881689f09 Fixed default message copying 2016-06-15 17:35:10 +02:00
6d1a97fd32 Added premium command warning 2016-06-15 13:55:57 +02:00
b74faa2fd5 Fix missing translation 2016-06-15 13:26:20 +02:00
4800a88886 Perform protocollib checks async/non-blocking 2016-06-14 19:36:34 +02:00
92c9ab5b76 Use ProtocolLib as a soft dependency 2016-06-14 17:21:46 +02:00
d90e3fdb44 Load the embed message as default 2016-06-14 17:02:12 +02:00
8abbb8f07c Fix bungeecord support (Fixes #26) 2016-06-13 08:51:47 +02:00
f04a44b1d2 Applies skin earlier to make it visible for other
plugins listening on login events
2016-06-11 17:16:03 +02:00
1a66121977 Do not save players multiple times on server switch 2016-06-11 15:08:44 +02:00
413a0325f8 Fixed BungeeCord force logins if there is a lobby server 2016-06-11 13:24:35 +02:00
9fc7e0bf43 Fixed BungeeCord support by correctly saving the proxy ids (Fixes #22) 2016-06-11 10:32:53 +02:00
ac8bcb1758 Fix default config deploy 2016-06-11 09:29:55 +02:00
bebcb3e9de Make locale messages thread-safe 2016-06-10 10:37:04 +02:00
0b899f61a8 Fixed message removal 2016-06-10 09:23:15 +02:00
7733135ce4 Fixed NPE in BungeeCord on cracked login for existing players (#22) 2016-06-10 08:57:08 +02:00
be89eec23b Fix NPE on premium name check if it's pure cracked player
(Fixes #21)
2016-06-10 08:53:06 +02:00
679060d4e9 Fixed support for empty messages 2016-06-09 14:33:14 +02:00
f6aa064835 Added localization messages (Fixes #20) 2016-06-09 12:43:04 +02:00
de4b73c3bd Upgrade maven plugin version 2016-06-09 10:30:42 +02:00
ac15829dcc Fixes insert (new player) for cracked players (Fixes #18) 2016-06-07 17:32:45 +02:00
0b709997a4 Fix duplicate premium uuid check in BungeeCord 2016-06-07 16:46:18 +02:00
8809875ca4 Fixed setting auth hook 2016-06-04 14:56:04 +02:00
aa30c070b9 Add database importers (planned) 2016-06-02 15:02:15 +02:00
51d0aefbf3 Added support of detecting name changes (Fixes #18) 2016-06-01 14:42:48 +02:00
cb876a52bd Add support for multiple bungeecords (Fixes #19) 2016-05-28 17:21:08 +02:00
3e844be65d Clean up project structure 2016-05-26 11:04:13 +02:00
dce95cf0d0 Prevent thread create violation in BungeeCord 2016-05-25 09:40:43 +02:00
81eeaeae83 Fix recursive call for bungeecord 2016-05-23 21:11:34 +02:00
6b1542de88 Now bungeeCord detection should work for all server versions 2016-05-23 17:02:32 +02:00
99b7367366 Fixed bungeecord support in Cauldron (Related to #11) 2016-05-23 12:02:44 +02:00
961b144efb Load the plugin before worlds loading and auth plugins (Related to #12)
to display the message not fully started more less
2016-05-23 10:27:01 +02:00
dcd06ad613 Fix server not fully started message on ProtocolSupport or Bungee
(Fixes #15)
2016-05-23 08:46:18 +02:00
c4c043e1c5 Fix AuthMe 3.X forceLogin on autoRegister (Fixes #14) 2016-05-22 20:00:07 +02:00
87aa9dd668 Fixed CrazyLogin hook 2016-05-22 18:34:21 +02:00
2838c06ab3 Replacing guava's class search with an explicit list (Fixes #11)
-> Fixed 1.7 Minecraft support 
-> Fixed Cauldron support
2016-05-22 18:31:34 +02:00
ae58e0539a Added support for LogIt 2016-05-22 13:59:41 +02:00
624745728f Added other command argument to /premium and /cracked (Fixes #13) 2016-05-21 13:32:48 +02:00
d0287ec2b4 Fixed premium logins if the server is not fully started (Fixes #12) 2016-05-18 18:41:24 +02:00
e6a4af92cc Add support for AuthMe 3.X 2016-05-18 15:47:51 +02:00
8f3920fa99 Fix message order 2016-05-15 17:33:21 +02:00
a723b2ddd3 Added BungeeCord setup description 2016-05-14 14:00:43 +02:00
5cf67127c7 Fix dead lock in xAuth 2016-05-14 13:30:32 +02:00
e5309b9fa1 Remove the check for auth plugins in order to allow auth plugins to
register their hook after the initialization of FastLogin
2016-05-14 13:25:12 +02:00
e439126294 Added API methods for auth plugins to set their own hook 2016-05-14 12:27:03 +02:00
152 changed files with 11534 additions and 5096 deletions

68
.github/ISSUE_TEMPLATE/bug_report.yaml vendored Normal file
View File

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

12
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@ -0,0 +1,12 @@
# General configuration for issue templates
# Allow issues without a template
#blank_issues_enabled: false
# Extra section on creating issues to redirect to another site
contact_links:
- name: 📌 Questions
url: https://github.com/games647/FastLogin/discussions
about:
You want to ask something - general questions. Example includes how to set it up or how it is working internally

View File

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

35
.github/dependabot.yml vendored Normal file
View File

@ -0,0 +1,35 @@
version: 2
updates:
# Updates for workflow files
- package-ecosystem: "github-actions"
# Workflow files stored in the
# default location of `.github/workflows`
directory: "/"
schedule:
interval: "monthly"
# Maven project
- package-ecosystem: maven
directory: "/"
schedule:
interval: weekly
ignore:
- dependency-name: com.google.code.gson:gson
versions:
- "> 2.2.4"
- dependency-name: com.google.guava
- dependency-name: me.clip:placeholderapi
versions:
- "> 2.10.8, < 2.11"
- dependency-name: net.md-5:bungeecord-config
versions:
- "> 1.12-SNAPSHOT"
- dependency-name: de.xxschrandxx.bca:BungeeCordAuthenticator
versions:
- 0.0.3
- dependency-name: com.zaxxer:HikariCP
versions:
- 4.0.0
- 4.0.2
- 4.0.3

8
.github/pull_request_template.md vendored Normal file
View File

@ -0,0 +1,8 @@
[//]: # (Lines in this format are considered as comments and will not be displayed.)
[//]: # (If your work is in progress, please consider making a draft pull request.)
### Summary of your change
[//]: # (Example: motiviation, enhancement)
### Related issue
[//]: # (Reference it using '#NUMBER'. Ex: Fixes/Related #...)

67
.github/workflows/codeql-analysis.yml vendored Normal file
View File

@ -0,0 +1,67 @@
# GitHub automatic code security scanning using CodeQL
# Human-readable name in the actions tab
name: "CodeQL"
on:
# Scan only for push on the primary branch for now
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
# job i
analyze:
# Display name
name: Analyze
# Environment
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
# Languages to scan
language: [ 'java' ]
steps:
- name: Checkout repository
uses: actions/checkout@v3
# Setup Java
- name: Set up JDK
uses: actions/setup-java@v3
with:
distribution: 'adopt'
# Use Java 16, because it's minimum required version by Geyser
java-version: 16
cache: 'maven'
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
# Cache build process too like in the maven config
- uses: actions/cache@v3.0.1
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
restore-keys: |
${{ runner.os }}-maven-
# Auto build attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v2
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2

43
.github/workflows/maven.yml vendored Normal file
View File

@ -0,0 +1,43 @@
# Automatically build, run unit and integration tests to detect errors early (CI provided by GitHub)
# including making pull requests review easier
# Human-readable name in the actions tab
name: Java CI
# Build on every pull request regardless of the branch
# Wiki: https://help.github.com/en/actions/reference/events-that-trigger-workflows
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
# job id
build_and_test:
# Environment image - always use the newest OS
runs-on: ubuntu-latest
# Run steps
steps:
# Pull changes
- uses: actions/checkout@v3
# Setup Java
- name: Set up JDK
uses: actions/setup-java@v3
with:
distribution: 'temurin'
# Use Java 16, because it's minimum required version by Geyser
java-version: 16
cache: 'maven'
# Build and test (included in package)
- name: Build with Maven and test
# Run non-interactive, package (with compile+test),
# ignore snapshot updates, because they are likely to have breaking changes, enforce checksums to validate
# possible errors in dependencies
run: mvn test --batch-mode --no-snapshot-updates --strict-checksums --file pom.xml

63
.gitignore vendored
View File

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

View File

@ -1,15 +0,0 @@
# 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/
sudo: false
# This is a java project
language: java
script: mvn compile test
# We run on 7+
jdk:
- openjdk7
- oraclejdk7
- oraclejdk8

View File

@ -1,17 +1,172 @@
######1.1
### 1.11
* TODO: Replace reflection with methodhandles
* Use direct proxies instead of ssl factories for multiple IP-addresses
* Remove local address check for multiple IP-addresses
* Fix parsing of local IP-addresses
* Fix address rotating for contacting the Mojang API
* Optimize issue template
* Use Instant for timestamps
* Migrate SLF4J logging (Fixes #177)
* Use Gson's TypeAdapter for more type safety
* Add support for IPv6 proxies
* Shared configuration implementation for easier maintained code
* Use Gson for json parsing, because it's supported on all platforms and removes code duplicates
* Clean up project code
* Drop support for deprecated AuthMe API
* Remove legacy database migration code
* Drop support for RoyalAuth, because it doesn't seem to be supported anymore
* Clean up client-server encryption -> use only one cipher per connection, simplify code
### 1.10
* Prevent authentication proxies
* Drop database importer
* More logging by default
* Add support for HTTP proxies
* Set the fake offline UUID on lowest priority (-> as soon as possible)
* Remove bungee chatcolor for Bukkit to support KCauldron
* Minor cleanup using inspections + Https
* Increase hook delay to let ProtocolLib inject the listener
* Drop support for old AuthMe API + Add support for new AuthMe API
* Remove eBean util usage to make it compatible with 1.12
* Do not try to hook into a plugin if auth plugin hook is already set using the FastLogin API
* Automatically register accounts if they are not in the auth plugin database but in the FastLogin database
* Update BungeeAuth dependency and use the new API. Please update your plugin if you still use the old one.
* Remove deprecated API methods from the last version
* Finally update the IP column on every login
* No duplicate session login
* Fix timestamp parsing in newer versions of SQLite
* Fix Spigot console command invocation sends result to in game players
### 1.9
* Added second attempt login -> cracked login
* Added cracked whitelist (switch-mode -> switching to online-mode from offlinemode)
* Added configuration to disable auto logins for 2Factor authentication
* Added missing add-premium-other message
* Upgrade to Java 8 -> Minimize file size
* Refactored/Cleaned up a lot of code
* [API] Deprecated platform specific auth-plugin. Please use AuthPlugin< platform specific player type >
* [API] Deprecated bukkit's password generator. Please use PasswordGenerator< platform specific player type >
* Fix ProtocolSupport autoRegister
* Fix update username in FastLogin database after nameChange
* Fix logging exceptions on encryption enabling
* Fix compatibility with older ProtocolLib versions (for 1.7) because of the missing getMethodAcccessorOrNull method
* Fix correct cracked permission for bukkit
* A try to fix SQLite timestamp parsing
* Drop support for LoginSecurity 1.X since 2.X seems to be stable
* Remove the nasty UltraAuth fakeplayer workaround by using a new api method. You should UltraAuth if you have it
### 1.8
* Added autoIn importer
* Added BFA importer
* Added ElDziAuth importer
* Fix third-party not premium player detection
* Fix ProtocolSupport BungeeCord
* Fix duplicate logins for BungeeAuth users
### 1.7.1
* Fix BungeeCord autoRegister (Fixes #46)
* Fix ProtocolSupport auto-register
### 1.7
* Added support for making requests to Mojang from different IPv4 addresses
* Added us.mcapi.com as third-party APIs to workaround rate-limits
* Fixed NPE in BungeeCord on cracked session
* Fixed skin applies if premium uuid is deactivated
* Fix player entry is not saved if namechangecheck is enabled
* Fix skin applies for third-party plugins
* Switch to mcapi.ca for uuid lookups
* Fix BungeeCord not setting an premium uuid
* Fix setting skin on Cauldron
* Fix saving on name change
### 1.6.2
* Fixed support for new LoginSecurity version
### 1.6.1
* Fix message typo in BungeeCord which created a NPE if premium-warning is activated
### 1.6
* Add a warning message if the user tries to invoke the premium command
* Added missing translation if the server isn't fully started
* Removed ProtocolLib as required dependency. You can use ProtocolSupport or BungeeCord as alternative
* Reduce the number of worker threads from 5 to 3 in ProtocolLib
* Process packets in ProtocolLib async/non-blocking -> better performance
* Fixed missing translation in commands
* Fixed cracked command not working on BungeeCord
* Fix error if forward skins is disabled
### 1.5.2
* Fixed BungeeCord force logins if there is a lobby server
* Removed cache expire in BungeeCord
* Applies skin earlier to make it visible for other plugins listening on login events
### 1.5.1
* Fixed BungeeCord support by correctly saving the proxy ids
### 1.5
* Added localization
* Fixed NPE on premium name check if it's pure cracked player
* Fixed NPE in BungeeCord on cracked login for existing players
* Fixed saving of existing cracked players
### 1.4
* Added Bungee setAuthPlugin method
* Added nameChangeCheck
* Multiple BungeeCord support
### 1.3.1
* Prevent thread create violation in BungeeCord
### 1.3
* Added support for AuthMe 3.X
* Fixed premium logins if the server is not fully started
* Added other command argument to /premium and /cracked
* Added support for LogIt
* Fixed 1.7 Minecraft support by removing guava 11+ only features -> Cauldron support
* Fixed BungeeCord support in Cauldron
### 1.2.1
* Fix premium status change notification message on BungeeCord
### 1.2
* Fix race condition in BungeeCord
* Fix dead lock in xAuth
* Added API methods for plugins to set their own password generator
* Added API methods for plugins to set their own auth plugin hook
=> Added support for AdvancedLogin
### 1.1
* Make the configuration options also work under BungeeCord (premiumUUID, forwardSkin)
* Catch configuration loading exception if it's not spigot build
* Fix config loading for older Spigot builds
######1.0
### 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
@ -19,7 +174,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
@ -30,69 +185,69 @@
* Removes the need of an Bukkit auth plugin if you use a bungeecord one
* Optimize performance and thread-safety
* Fixed BungeeCord support
* Changed config option autologin to autoregister to clarify the usage
* Changed config option auto-login to auto-register to clarify the usage
######0.6
### 0.6
* Fixed 1.9 bugs
* Added UltraAuth support
######0.5
### 0.5
* Added unpremium command
* Added autologin - See config
* Added cracked command
* Added auto-login - See config
* Added config
* Added isRegistered API method
* Added forceRegister API method
* Fixed CrazyLogin player data restore -> Fixes memory leaks with this plugin
* Fixed premium name check to protocolsupport
* Fixed premium name check to ProtocolSupport
* Improved permissions management
######0.4
### 0.4
* Added forward premium skin
* Added plugin support for protocolsupport
* 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
* Remove useless AuthMe force-login code
* Send a kick message to the client instead of just "Disconnect"
* Reformat source code
* Fix thread safety for fake start packets (Bukkit.getOfflinePlayer doesn't look like to be thread-safe)
* Added more documentation
######0.2.2
### 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
@ -101,5 +256,5 @@
* More state validation
* Added better error handling
#####0.1
* First release
### 0.1
* First release

View File

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

216
README.md
View File

@ -1,12 +1,9 @@
# FastLogin
[![Build Status](https://travis-ci.org/games647/FastLogin.svg?branch=master)](https://travis-ci.org/games647/FastLogin)
[![Donate Button](https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif)](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).
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)
@ -14,143 +11,120 @@ So they don't need to enter passwords. This is also called auto login (auto-logi
* Cauldron support
* Forge/Sponge message support
* Premium UUID support
* Forwards Skins
* Forward skins
* Detect username changed and will update the existing database record
* BungeeCord support
* Auto register new premium players
* Plugin: ProtocolSupport is supported and can be used as an alternative to ProtocolLib
* No client modifications needed
* Good performance by using async non blocking operations
* Free
* Open source
* Good performance by using async operations
* Locale messages
* Support for Bedrock players proxied through FloodGate
## Issues
Please use issues for bug reports, suggestions, questions and more. Please check for existing issues. Existing issues
can be voted up by adding up vote to the original post. Closing issues means that they are marked as resolved. Comments
are still allowed and it could be re-opened.
## Development builds
Development builds contain the latest changes from the Source-Code. They are bleeding edge and could introduce new bugs,
but also include features, enhancements and bug fixes that are not yet in a released version. If you click on the left
side on `Changes`, you can see iterative change sets leading to a specific build.
You can download them from here: https://ci.codemc.org/job/Games647/job/FastLogin/
***
###Commands:
* /premium [player] Label the invoker as paid account
* /cracked [player] Label the invoker as cracked account
## Commands
###Permissions:
* fastlogin.bukkit.command.premium
/premium [player] Label the invoker or the argument as paid account
/cracked [player] Label the invoker or the argument as cracked account
###Requirements:
* Plugin: [ProtocolLib](http://www.spigotmc.org/resources/protocollib.1997/)
* 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
## Permissions
####Bukkit/Spigot/PaperSPigot
fastlogin.bukkit.command.premium
fastlogin.bukkit.command.cracked
fastlogin.command.premium.other
fastlogin.command.cracked.other
* [AuthMe](http://dev.bukkit.org/bukkit-plugins/authme-reloaded/)
* [xAuth](http://dev.bukkit.org/bukkit-plugins/xauth/)
* [CrazyLogin](http://dev.bukkit.org/bukkit-plugins/crazylogin/)
* [LoginSecurity](http://dev.bukkit.org/bukkit-plugins/loginsecurity/)
* [RoyalAuth](http://dev.bukkit.org/bukkit-plugins/royalauth/)
* [UltraAuth](http://dev.bukkit.org/bukkit-plugins/ultraauth-aa/)
## Placeholder
####BungeeCord/Waterfall
This plugin supports `PlaceholderAPI` on `Spigot`. It exports the following variable
`%fastlogin_status%`. In BungeeCord environments, the status of a player will be delivered with a delay after the player
already successful joined the server. This takes about a couple of milliseconds. In this case the value
will be `Unknown`.
Possible values: `Premium`, `Cracked`, `Unknown`
## Requirements
* Java 8+
* Server software in offlinemode:
* Spigot (or a fork e.g. Paper) 1.8.8+
* Protocol plugin:
* [ProtocolLib](https://www.spigotmc.org/resources/protocollib.1997/) or
* [ProtocolSupport](https://www.spigotmc.org/resources/protocolsupport.7201/)
* Latest BungeeCord (or a fork e.g. Waterfall)
* An auth plugin.
### Supported auth plugins
#### Spigot/Paper
* [AdvancedLogin (Paid)](https://www.spigotmc.org/resources/advancedlogin.10510/)
* [AuthMe (5.X)](https://dev.bukkit.org/bukkit-plugins/authme-reloaded/)
* [CrazyLogin](https://dev.bukkit.org/bukkit-plugins/crazylogin/)
* [LoginSecurity](https://dev.bukkit.org/bukkit-plugins/loginsecurity/)
* [LogIt](https://github.com/games647/LogIt)
* [SodionAuth (2.0+)](https://github.com/MohistMC/SodionAuth)
* [UltraAuth](https://dev.bukkit.org/bukkit-plugins/ultraauth-aa/)
* [UserLogin](https://www.spigotmc.org/resources/userlogin.80669/)
* [xAuth](https://dev.bukkit.org/bukkit-plugins/xauth/)
#### BungeeCord/Waterfall
* [BungeeAuth](https://www.spigotmc.org/resources/bungeeauth.493/)
* [BungeeAuthenticator](https://www.spigotmc.org/resources/bungeecordauthenticator.87669/)
* [SodionAuth (2.0+)](https://github.com/MohistMC/SodionAuth)
###Downloads
## Network requests
https://www.spigotmc.org/resources/fastlogin.14153/history
This plugin performs network requests to:
* https://api.mojang.com - retrieving uuid data to decide if we should activate premium login
* https://sessionserver.mojang.com - verify if the player is the owner of that account
***
###FAQ
## How to install
####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)
4. [Can cracked player join with premium usernames?](#can-cracked-player-join-with-premium-usernames)
5. [Why do players have to invoke a command?](#why-do-players-have-to-invoke-a-command)
6. [What happens if a paid account joins with a used username?](#what-happens-if-a-paid-account-joins-with-a-used-username)
7. [Does the plugin have BungeeCord support?](#does-the-plugin-have-bungeecord-support)
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)
### Spigot/Paper
####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
4. Mojang -> Client: Session data checked. You can continue
5. Client -> Server: I received a successful response from Mojang. Heres 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
1. Download and install ProtocolLib/ProtocolSupport
2. Download and install FastLogin (or `FastLoginBukkit` for newer versions)
3. Set your server in offline mode by setting the value `onlinemode` in your server.properties to false
### BungeeCord/Waterfall or Velocity
######Offline Mode
In offline mode step 2-7 is skipped. So a login request is directly followed by 8.
Install the plugin on both platforms, that is proxy (BungeeCord or Velocity) and backend server (Spigot).
######More details
http://wiki.vg/Protocol#Login
####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
connection, we will intercept only **login** packets of **premium** players.
1. Player is connecting to the server.
2. Plugin checks if the username we received activated the fast login method (i.e. using command)
3. Run a check if the username is currently used by a paid account.
(We don't know yet if the client connecting is premium)
4. Request an Mojang Session Server authentication
5. On response check if all data is correct
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?
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
a offline mode player. The server will handle the rest.
2. Some plugins check if the server is in online mode. If so, they could process the real offline (cracked) accounts
incorrectly. For example, a plugin tries to fetch the UUID from Mojang, but the name of the player is not associated to
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?
Yes, indeed. Therefore the command for toggling the fast login method exists.
####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.
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?
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?
Yes it has. Just activate ipForward in your BungeeCord config and place the plugin in the plugins folder of
Bukkit/Spigot and BungeeCord. Then you have fill your BungeeCord Id (from the Stats-Option in the BungeeCord config)
into the whitelist file of your Bukkit/Spigot server. For security reasons, don't post this Id on Forums.
This plugin will automatically detect if BungeeCord is running and handle premium checks on BungeeCord.
####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?
It's not tested yet, but all needed methods also exists in Cauldron so it could work together.
***
###Useful Links:
* [Login Protocol](http://wiki.vg/Protocol#Login)
* [Protocol Encryption](http://wiki.vg/Protocol_Encryption)
1. Activate proxy support in the server configuration
* This is often found in `spigot.yml` or `paper.yml`
2. Restart the backend server
3. Now there is `allowed-proxies.txt` file in the FastLogin folder of the restarted server
* BungeeCord: Put your `stats-id` from the BungeeCord config into this file
* Velocity: On plugin startup the plugin generates a `proxyId.txt` inside the plugins folder of the proxy
4. Activate ip forwarding in your proxy config
5. Check your database settings in the config of FastLogin on your proxy
* The proxies only ship with a limited set of drivers where Spigot supports more. Therefore, these are supported:
* BungeeCord: `com.mysql.jdbc.Driver` for MySQL/MariaDB
* Velocity: `fastlogin.mariadb.jdbc.Driver` for MySQL/MariaDB
* Note the embedded file storage SQLite is not available
* MySQL/MariaDB requires an external database server running. Check your server provider if there is one available
or install one.
6. Set proxy and Spigot in offline mode by setting the value `onlinemode` in your `config.yml` to false
7. You should *always* firewall your Spigot server that it's only accessible through your proxy
* This is also the case without this plugin
* https://www.spigotmc.org/wiki/bungeecord-installation/#post-installation

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

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

View File

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

View File

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

View File

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

View File

@ -1,123 +0,0 @@
package com.github.games647.fastlogin.bukkit;
import com.google.common.base.Charsets;
import java.security.InvalidKeyException;
import java.security.Key;
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 javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
/**
* Encryption and decryption minecraft util for connection between servers
* and paid minecraft account clients.
*
* Source: https://github.com/bergerkiller/CraftSource/blob/master/net.minecraft.server/MinecraftEncryption.java
*
* Remapped by: https://github.com/Techcable/MinecraftMappings/tree/master/1.8
*/
public class EncryptionUtil {
public static KeyPair generateKeyPair() {
try {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(1_024);
return keyPairGenerator.generateKeyPair();
} catch (NoSuchAlgorithmException nosuchalgorithmexception) {
//Should be existing in every vm
throw new ExceptionInInitializerError(nosuchalgorithmexception);
}
}
public static byte[] getServerIdHash(String serverId, PublicKey publicKey, SecretKey secretKey) {
return digestOperation("SHA-1"
, new byte[][]{serverId.getBytes(Charsets.ISO_8859_1), secretKey.getEncoded(), publicKey.getEncoded()});
}
private static byte[] digestOperation(String algo, byte[]... content) {
try {
MessageDigest messagedigest = MessageDigest.getInstance(algo);
for (byte[] data : content) {
messagedigest.update(data);
}
return messagedigest.digest();
} catch (NoSuchAlgorithmException nosuchalgorithmexception) {
nosuchalgorithmexception.printStackTrace();
return null;
}
}
// public static PublicKey decodePublicKey(byte[] encodedKey) {
// try {
// KeyFactory keyfactory = KeyFactory.getInstance("RSA");
//
// X509EncodedKeySpec x509encodedkeyspec = new X509EncodedKeySpec(encodedKey);
// return keyfactory.generatePublic(x509encodedkeyspec);
// } catch (NoSuchAlgorithmException | InvalidKeySpecException nosuchalgorithmexception) {
// //ignore
// }
//
// System.err.println("Public key reconstitute failed!");
// return null;
// }
public static SecretKey decryptSharedKey(PrivateKey privateKey, byte[] encryptedSharedKey) {
return new SecretKeySpec(decryptData(privateKey, encryptedSharedKey), "AES");
}
public static byte[] decryptData(Key key, byte[] data) {
return cipherOperation(Cipher.DECRYPT_MODE, key, data);
}
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();
}
System.err.println("Cipher data failed!");
return null;
}
private static Cipher createCipherInstance(int operationMode, String cipherName, Key key) {
try {
Cipher cipher = Cipher.getInstance(cipherName);
cipher.init(operationMode, key);
return cipher;
} catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException invalidkeyexception) {
invalidkeyexception.printStackTrace();
}
System.err.println("Cipher creation failed!");
return null;
}
//
// public static Cipher createBufferedBlockCipher(int operationMode, Key key) {
// try {
// Cipher cipher = Cipher.getInstance("AES/CFB8/NoPadding");
//
// cipher.init(operationMode, key, new IvParameterSpec(key.getEncoded()));
// return cipher;
// } catch (GeneralSecurityException generalsecurityexception) {
// throw new RuntimeException(generalsecurityexception);
// }
// }
private EncryptionUtil() {
//utility
}
}

View File

@ -1,250 +1,333 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2022 games647 and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.bukkit;
import com.comphenix.protocol.AsynchronousManager;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.ProtocolManager;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.utility.SafeCacheBuilder;
import com.github.games647.fastlogin.bukkit.commands.CrackedCommand;
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.EncryptionPacketListener;
import com.github.games647.fastlogin.bukkit.listener.ProtocolSupportListener;
import com.github.games647.fastlogin.bukkit.listener.StartPacketListener;
import com.google.common.cache.CacheLoader;
import com.google.common.reflect.ClassPath;
import com.github.games647.fastlogin.bukkit.command.CrackedCommand;
import com.github.games647.fastlogin.bukkit.command.PremiumCommand;
import com.github.games647.fastlogin.bukkit.listener.ConnectionListener;
import com.github.games647.fastlogin.bukkit.listener.PaperCacheListener;
import com.github.games647.fastlogin.bukkit.listener.protocollib.ManualNameChange;
import com.github.games647.fastlogin.bukkit.listener.protocollib.ProtocolLibListener;
import com.github.games647.fastlogin.bukkit.listener.protocollib.SkinApplyListener;
import com.github.games647.fastlogin.bukkit.listener.protocolsupport.ProtocolSupportListener;
import com.github.games647.fastlogin.bukkit.task.DelayedAuthHook;
import com.github.games647.fastlogin.core.CommonUtil;
import com.github.games647.fastlogin.core.PremiumStatus;
import com.github.games647.fastlogin.core.hooks.bedrock.BedrockService;
import com.github.games647.fastlogin.core.hooks.bedrock.FloodgateService;
import com.github.games647.fastlogin.core.hooks.bedrock.GeyserService;
import com.github.games647.fastlogin.core.shared.FastLoginCore;
import com.github.games647.fastlogin.core.shared.PlatformPlugin;
import java.io.IOException;
import java.lang.reflect.Method;
import java.security.KeyPair;
import java.sql.SQLException;
import io.papermc.lib.PaperLib;
import java.net.InetSocketAddress;
import java.nio.file.Path;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import org.apache.commons.lang.RandomStringUtils;
import org.bukkit.Bukkit;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.java.JavaPlugin;
import org.geysermc.floodgate.api.FloodgateApi;
import org.geysermc.geyser.GeyserImpl;
import org.slf4j.Logger;
/**
* This plugin checks if a player has a paid account and if so tries to skip offline mode authentication.
*/
public class FastLoginBukkit extends JavaPlugin {
public class FastLoginBukkit extends JavaPlugin implements PlatformPlugin<CommandSender> {
private static final int WORKER_THREADS = 5;
//1 minutes should be enough as a timeout for bad internet connection (Server, Client and Mojang)
private final ConcurrentMap<String, BukkitLoginSession> loginSession = CommonUtil.buildCache(1, -1);
private final Map<UUID, PremiumStatus> premiumPlayers = new ConcurrentHashMap<>();
private final Logger logger;
public static UUID parseId(String withoutDashes) {
return UUID.fromString(withoutDashes.substring(0, 8)
+ "-" + withoutDashes.substring(8, 12)
+ "-" + withoutDashes.substring(12, 16)
+ "-" + withoutDashes.substring(16, 20)
+ "-" + withoutDashes.substring(20, 32));
private boolean serverStarted;
private BungeeManager bungeeManager;
private final BukkitScheduler scheduler;
private FastLoginCore<Player, CommandSender, FastLoginBukkit> core;
private FloodgateService floodgateService;
private GeyserService geyserService;
private PremiumPlaceholder premiumPlaceholder;
public FastLoginBukkit() {
this.logger = CommonUtil.initializeLoggerService(getLogger());
this.scheduler = new BukkitScheduler(this, logger);
}
//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 Storage storage;
//this map is thread-safe for async access (Packet Listener)
//SafeCacheBuilder is used in order to be version independent
private final ConcurrentMap<String, PlayerSession> session = SafeCacheBuilder.<String, PlayerSession>newBuilder()
//2 minutes should be enough as a timeout for bad internet connection (Server, Client and Mojang)
.expireAfterWrite(1, TimeUnit.MINUTES)
//mapped by ip:port -> PlayerSession
.build(new CacheLoader<String, PlayerSession>() {
@Override
public PlayerSession load(String key) throws Exception {
//A key should be inserted manually on start packet
throw new UnsupportedOperationException("Not supported");
}
});
private BukkitAuthPlugin authPlugin;
private final MojangApiConnector mojangApiConnector = new MojangApiConnector(this);
@Override
public void onEnable() {
saveDefaultConfig();
core = new FastLoginCore<>(this);
core.load();
if (getServer().getOnlineMode()) {
//we need to require offline to prevent a session request for a offline player
getLogger().severe("Server have to be in offline mode");
//we need to require offline to prevent a loginSession request for an offline player
logger.error("Server has to be in offline mode");
setEnabled(false);
return;
}
try {
if (Bukkit.spigot().getConfig().isBoolean("settings.bungeecord")) {
bungeeCord = Bukkit.spigot().getConfig().getBoolean("settings.bungeecord");
} else {
Method getConfigMethod = FuzzyReflection.fromObject(getServer().spigot(), true)
.getMethodByName("getSpigotConfig");
getConfigMethod.setAccessible(true);
YamlConfiguration spigotConfig = (YamlConfiguration) getConfigMethod.invoke(getServer().spigot());
bungeeCord = spigotConfig.getBoolean("settings.bungeecord");
}
} catch (Exception | NoSuchMethodError ex) {
getLogger().warning("Cannot check bungeecord support. You use a non-spigot build");
}
boolean hookFound = registerHooks();
if (bungeeCord) {
getLogger().info("BungeeCord setting detected. No auth plugin is required");
} else if (!hookFound) {
getLogger().info("No auth plugin were found and bungeecord is deactivated. "
+ "Either one or both of the checks have to pass in order to use this plugin");
if (!initializeFloodgate()) {
setEnabled(false);
return;
}
if (bungeeCord) {
//check for incoming messages from the bungeecord version of this plugin
getServer().getMessenger().registerIncomingPluginChannel(this, getName(), new BungeeCordListener(this));
getServer().getMessenger().registerOutgoingPluginChannel(this, getName());
//register listeners on success
bungeeManager = new BungeeManager(this);
bungeeManager.initialize();
PluginManager pluginManager = getServer().getPluginManager();
if (bungeeManager.isEnabled()) {
markInitialized();
} else {
String driver = getConfig().getString("driver");
String host = getConfig().getString("host", "");
int port = getConfig().getInt("port", 3306);
String database = getConfig().getString("database");
String username = getConfig().getString("username", "");
String password = getConfig().getString("password", "");
this.storage = new Storage(this, driver, host, port, database, username, password);
try {
storage.createTables();
} catch (SQLException sqlEx) {
getLogger().log(Level.SEVERE, "Failed to create database tables. Disabling plugin...", sqlEx);
if (!core.setupDatabase()) {
setEnabled(false);
return;
}
if (getServer().getPluginManager().isPluginEnabled("ProtocolSupport")) {
getServer().getPluginManager().registerEvents(new ProtocolSupportListener(this), this);
if (pluginManager.isPluginEnabled("ProtocolSupport")) {
pluginManager.registerEvents(new ProtocolSupportListener(this, core.getRateLimiter()), this);
} else if (pluginManager.isPluginEnabled("ProtocolLib")) {
ProtocolLibListener.register(this, core.getRateLimiter());
if (isPluginInstalled("floodgate")) {
if (getConfig().getBoolean("floodgatePrefixWorkaround")){
ManualNameChange.register(this, floodgateService);
logger.info("Floodgate prefix injection workaround has been enabled.");
logger.info("If you have problems joining the server, try disabling it in the configuration.");
} else {
logger.warn("We have detected that you are runnging FastLogin alongside Floodgate and ProtocolLib.");
logger.warn("Currently there is an issue with FastLogin that prevents Floodgate name prefixes from showing up "
+ "when it is together used with ProtocolLib.");
logger.warn("If you would like to use Floodgate name prefixes, you can enable an experimental workaround by changing "
+ "the value 'floodgatePrefixWorkaround' to true in config.yml.");
logger.warn("For more information visit https://github.com/games647/FastLogin/issues/493");
}
}
//if server is using paper - we need to set the skin at pre login anyway, so no need for this listener
if (!PaperLib.isPaper() && getConfig().getBoolean("forwardSkin")) {
pluginManager.registerEvents(new SkinApplyListener(this), this);
}
} else {
ProtocolManager protocolManager = ProtocolLibrary.getProtocolManager();
//we are performing HTTP request on these so run it async (seperate from the Netty IO threads)
AsynchronousManager asynchronousManager = protocolManager.getAsynchronousManager();
StartPacketListener startPacketListener = new StartPacketListener(this, protocolManager);
EncryptionPacketListener encryptionPacketListener = new EncryptionPacketListener(this, protocolManager);
asynchronousManager.registerAsyncHandler(startPacketListener).start(WORKER_THREADS);
asynchronousManager.registerAsyncHandler(encryptionPacketListener).start(WORKER_THREADS);
logger.warn("Either ProtocolLib or ProtocolSupport have to be installed if you don't use BungeeCord");
setEnabled(false);
return;
}
}
getServer().getPluginManager().registerEvents(new BukkitJoinListener(this), this);
//delay dependency setup because we load the plugin very early where plugins are initialized yet
getServer().getScheduler().runTaskLater(this, new DelayedAuthHook(this), 5L);
pluginManager.registerEvents(new ConnectionListener(this), this);
//if server is using paper - we need to add one more listener to correct the user cache usage
if (PaperLib.isPaper()) {
pluginManager.registerEvents(new PaperCacheListener(this), this);
}
//register commands using a unique name
getCommand("premium").setExecutor(new PremiumCommand(this));
getCommand("cracked").setExecutor(new CrackedCommand(this));
Optional.ofNullable(getCommand("premium")).ifPresent(c -> c.setExecutor(new PremiumCommand(this)));
Optional.ofNullable(getCommand("cracked")).ifPresent(c -> c.setExecutor(new CrackedCommand(this)));
if (pluginManager.isPluginEnabled("PlaceholderAPI")) {
premiumPlaceholder = new PremiumPlaceholder(this);
premiumPlaceholder.register();
}
dependencyWarnings();
}
private boolean initializeFloodgate() {
if (getServer().getPluginManager().getPlugin("Geyser-Spigot") != null) {
geyserService = new GeyserService(GeyserImpl.getInstance(), core);
}
if (getServer().getPluginManager().getPlugin("floodgate") != null) {
floodgateService = new FloodgateService(FloodgateApi.getInstance(), core);
// Check Floodgate config values and return
return floodgateService.isValidFloodgateConfigString("autoLoginFloodgate")
&& floodgateService.isValidFloodgateConfigString("allowFloodgateNameConflict");
}
return true;
}
@Override
public void onDisable() {
//clean up
session.clear();
loginSession.clear();
premiumPlayers.clear();
//remove old blacklists
for (Player player : getServer().getOnlinePlayers()) {
player.removeMetadata(getName(), this);
if (core != null) {
core.close();
}
if (storage != null) {
storage.close();
if (bungeeManager != null) {
bungeeManager.cleanup();
}
if (premiumPlaceholder != null && getServer().getPluginManager().isPluginEnabled("PlaceholderAPI")) {
try {
premiumPlaceholder.unregister();
} catch (Exception | NoSuchMethodError exception) {
logger.error("Failed to unregister placeholder", exception);
}
}
}
public String generateStringPassword() {
return RandomStringUtils.random(8, true, true);
public FastLoginCore<Player, CommandSender, FastLoginBukkit> getCore() {
return core;
}
/**
* Gets a thread-safe map about players which are connecting to the server are being checked to be premium (paid
* account)
*
* @return a thread-safe session map
* @return a thread-safe loginSession map
*/
public ConcurrentMap<String, PlayerSession> getSessions() {
return session;
public ConcurrentMap<String, BukkitLoginSession> getLoginSessions() {
return loginSession;
}
public BukkitLoginSession getSession(InetSocketAddress address) {
String id = getSessionId(address);
return loginSession.get(id);
}
public String getSessionId(InetSocketAddress address) {
return address.getAddress().getHostAddress() + ':' + address.getPort();
}
public void putSession(InetSocketAddress address, BukkitLoginSession session) {
String id = getSessionId(address);
loginSession.put(id, session);
}
public void removeSession(InetSocketAddress address) {
String id = getSessionId(address);
loginSession.remove(id);
}
public Map<UUID, PremiumStatus> getPremiumPlayers() {
return premiumPlayers;
}
/**
* Gets the server KeyPair. This is used to encrypt or decrypt traffic between the client and server
* Fetches the premium status of an online player.
*
* @return the server KeyPair
* @param onlinePlayer player that is currently online player (play state)
* @return the online status or unknown if an error happened, the player isn't online or BungeeCord doesn't send
* us the status message yet (This means you cannot check the login status on the PlayerJoinEvent).
*/
public KeyPair getServerKey() {
return keyPair;
}
public Storage getStorage() {
return storage;
public PremiumStatus getStatus(UUID onlinePlayer) {
return premiumPlayers.getOrDefault(onlinePlayer, PremiumStatus.UNKNOWN);
}
/**
* Gets the auth plugin hook in order to interact with the plugins. This can be null if no supporting auth plugin
* was found.
* Wait before the server is fully started. This is workaround, because connections right on startup are not
* injected by ProtocolLib
*
* @return interface to any supported auth plugin
* @return true if ProtocolLib can now intercept packets
*/
public BukkitAuthPlugin getAuthPlugin() {
return authPlugin;
public boolean isServerFullyStarted() {
return serverStarted;
}
/**
* Gets the a connection in order to access important features from the Mojang API.
*
* @return the connector instance
*/
public MojangApiConnector getApiConnector() {
return mojangApiConnector;
public void markInitialized() {
this.serverStarted = true;
}
private boolean registerHooks() {
BukkitAuthPlugin authPluginHook = null;
try {
String hooksPackage = this.getClass().getPackage().getName() + ".hooks";
//Look through all classes in the hooks package and look for supporting plugins on the server
for (ClassPath.ClassInfo clazzInfo : ClassPath.from(getClassLoader()).getTopLevelClasses(hooksPackage)) {
//remove the hook suffix
String pluginName = clazzInfo.getSimpleName().replace("Hook", "");
Class<?> clazz = clazzInfo.load();
//uses only member classes which uses AuthPlugin interface (skip interfaces)
if (BukkitAuthPlugin.class.isAssignableFrom(clazz)
//check only for enabled plugins. A single plugin could be disabled by plugin managers
&& getServer().getPluginManager().isPluginEnabled(pluginName)) {
authPluginHook = (BukkitAuthPlugin) clazz.newInstance();
getLogger().log(Level.INFO, "Hooking into auth plugin: {0}", pluginName);
break;
}
}
} catch (InstantiationException | IllegalAccessException | IOException ex) {
getLogger().log(Level.SEVERE, "Couldn't load the integration class", ex);
public BungeeManager getBungeeManager() {
return bungeeManager;
}
@Override
public Path getPluginFolder() {
return getDataFolder().toPath();
}
@Override
public Logger getLog() {
return logger;
}
@Override
public BukkitScheduler getScheduler() {
return scheduler;
}
@Override
public void sendMessage(CommandSender receiver, String message) {
receiver.sendMessage(message);
}
/**
* Checks if a plugin is installed on the server
* @param name the name of the plugin
* @return true if the plugin is installed
*/
@Override
public boolean isPluginInstalled(String name) {
// the plugin may be enabled after FastLogin, so isPluginEnabled() won't work here
return Bukkit.getServer().getPluginManager().getPlugin(name) != null;
}
public FloodgateService getFloodgateService() {
return floodgateService;
}
public GeyserService getGeyserService() {
return geyserService;
}
@Override
public BedrockService<?> getBedrockService() {
if (floodgateService != null) {
return floodgateService;
}
if (authPluginHook == null) {
//run this check for exceptions (errors) and not found plugins
getLogger().warning("No support offline Auth plugin found. ");
return false;
}
authPlugin = authPluginHook;
return true;
return geyserService;
}
public boolean isBungeeCord() {
return bungeeCord;
/**
* Send warning messages to log if incompatible plugins are used
*/
private void dependencyWarnings() {
if (isPluginInstalled("floodgate-bukkit")) {
logger.warn("We have detected that you are running Floodgate 1.0 which is not supported by the Bukkit "
+ "version of FastLogin.");
logger.warn("If you would like to use FastLogin with Floodgate, you can download development builds of "
+ "Floodgate 2.0 from https://ci.opencollab.dev/job/GeyserMC/job/Floodgate/job/dev%252F2.0/");
logger.warn("Don't forget to update Geyser to a supported version as well from "
+ "https://ci.opencollab.dev/job/GeyserMC/job/Geyser/job/floodgate-2.0/");
}
}
}

View File

@ -1,124 +0,0 @@
package com.github.games647.fastlogin.bukkit;
import com.github.games647.fastlogin.bukkit.hooks.BukkitAuthPlugin;
import com.google.common.io.ByteArrayDataOutput;
import com.google.common.io.ByteStreams;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.logging.Level;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.entity.Player;
public class ForceLoginTask implements Runnable {
protected final FastLoginBukkit plugin;
protected final Player player;
public ForceLoginTask(FastLoginBukkit plugin, Player player) {
this.plugin = plugin;
this.player = 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();
PlayerSession session = plugin.getSessions().get(id);
BukkitAuthPlugin authPlugin = plugin.getAuthPlugin();
Storage storage = plugin.getStorage();
PlayerProfile playerProfile = null;
if (storage != null) {
playerProfile = storage.getProfile(player.getName(), false);
}
if (session == null) {
//cracked player
if (playerProfile != null) {
playerProfile.setUuid(null);
playerProfile.setPremium(false);
storage.save(playerProfile);
}
//check if it's the same player as we checked before
} else if (player.getName().equals(session.getUsername())) {
//premium player
if (authPlugin == null) {
//maybe only bungeecord plugin
sendSuccessNotification();
} else {
boolean success = false;
if (isOnlineThreadSafe() && session.isVerified()) {
if (session.needsRegistration()) {
success = forceRegister(authPlugin, player);
} else {
success = forceLogin(authPlugin, player);
}
}
if (success) {
//update only on success to prevent corrupt data
if (playerProfile != null) {
playerProfile.setUuid(session.getUuid());
//save cracked players too
playerProfile.setPremium(session.isVerified());
storage.save(playerProfile);
}
sendSuccessNotification();
}
}
}
}
private boolean forceRegister(BukkitAuthPlugin authPlugin, Player player) {
plugin.getLogger().log(Level.FINE, "Register player {0}", player.getName());
String generatedPassword = plugin.generateStringPassword();
boolean success = authPlugin.forceRegister(player, generatedPassword);
player.sendMessage(ChatColor.DARK_GREEN + "Auto registered with password: " + generatedPassword);
player.sendMessage(ChatColor.DARK_GREEN + "You may want change it?");
return success;
}
private boolean forceLogin(BukkitAuthPlugin authPlugin, Player player) {
plugin.getLogger().log(Level.FINE, "Logging player {0} in", player.getName());
boolean success = authPlugin.forceLogin(player);
player.sendMessage(ChatColor.DARK_GREEN + "Auto logged in");
return success;
}
private void sendSuccessNotification() {
if (plugin.isBungeeCord()) {
ByteArrayDataOutput dataOutput = ByteStreams.newDataOutput();
dataOutput.writeUTF("SUCCESS");
player.sendPluginMessage(plugin, plugin.getName(), dataOutput.toByteArray());
}
}
private boolean isOnlineThreadSafe() {
//the playerlist isn't thread-safe
Future<Boolean> onlineFuture = Bukkit.getScheduler().callSyncMethod(plugin, new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
return player.isOnline();
}
});
try {
return onlineFuture.get();
} catch (InterruptedException | ExecutionException ex) {
plugin.getLogger().log(Level.SEVERE, "Failed to perform thread-safe online check", ex);
return false;
}
}
}

View File

@ -1,124 +0,0 @@
package com.github.games647.fastlogin.bukkit;
import com.comphenix.protocol.wrappers.WrappedSignedProperty;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.UUID;
import java.util.logging.Level;
import java.util.regex.Pattern;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;
public class MojangApiConnector {
//http connection, read timeout and user agent for a connection to mojang api servers
private static final int TIMEOUT = 1 * 1_000;
private static final String USER_AGENT = "Premium-Checker";
//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?";
//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_
private static final String VALID_PLAYERNAME = "^\\w{2,16}$";
//compile the pattern only on plugin enable -> and this have to be threadsafe
private final Pattern playernameMatcher = Pattern.compile(VALID_PLAYERNAME);
private final FastLoginBukkit plugin;
public MojangApiConnector(FastLoginBukkit plugin) {
this.plugin = plugin;
}
/**
*
* @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
try {
HttpURLConnection 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 != null && !line.equals("null")) {
JSONObject userData = (JSONObject) JSONValue.parseWithException(line);
String uuid = (String) userData.get("id");
return parseId(uuid);
}
}
//204 - no content for not found
} catch (Exception ex) {
plugin.getLogger().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 boolean hasJoinedServer(PlayerSession session, String serverId) {
try {
String url = HAS_JOINED_URL + "username=" + session.getUsername() + "&serverId=" + serverId;
HttpURLConnection conn = getConnection(url);
BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String line = reader.readLine();
if (line != null && !line.equals("null")) {
//validate parsing
//http://wiki.vg/Protocol_Encryption#Server
JSONObject userData = (JSONObject) JSONValue.parseWithException(line);
String uuid = (String) userData.get("id");
session.setUuid(parseId(uuid));
JSONArray properties = (JSONArray) userData.get("properties");
JSONObject skinProperty = (JSONObject) properties.get(0);
String propertyName = (String) skinProperty.get("name");
if (propertyName.equals("textures")) {
String skinValue = (String) skinProperty.get("value");
String signature = (String) skinProperty.get("signature");
session.setSkin(WrappedSignedProperty.fromValues(propertyName, skinValue, signature));
}
return true;
}
} catch (Exception ex) {
//catch not only ioexceptions also parse and NPE on unexpected json format
plugin.getLogger().log(Level.WARNING, "Failed to verify session", ex);
}
//this connection doesn't need to be closed. So can make use of keep alive in java
return false;
}
private UUID parseId(String withoutDashes) {
return UUID.fromString(withoutDashes.substring(0, 8)
+ "-" + withoutDashes.substring(8, 12)
+ "-" + withoutDashes.substring(12, 16)
+ "-" + withoutDashes.substring(16, 20)
+ "-" + withoutDashes.substring(20, 32));
}
private HttpURLConnection getConnection(String url) throws IOException {
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
connection.setConnectTimeout(TIMEOUT);
connection.setReadTimeout(TIMEOUT);
//the new Mojang API just uses json as response
connection.setRequestProperty("Content-Type", "application/json");
connection.setRequestProperty("User-Agent", USER_AGENT);
return connection;
}
}

View File

@ -1,78 +0,0 @@
package com.github.games647.fastlogin.bukkit;
import java.util.UUID;
public class PlayerProfile {
private final String playerName;
private long userId;
private UUID uuid;
private boolean premium;
private String lastIp;
private long lastLogin;
public PlayerProfile(long userId, UUID uuid, String playerName, boolean premium
, String lastIp, long lastLogin) {
this.userId = userId;
this.uuid = uuid;
this.playerName = playerName;
this.premium = premium;
this.lastIp = lastIp;
this.lastLogin = lastLogin;
}
public PlayerProfile(UUID uuid, String playerName, boolean premium, String lastIp) {
this.userId = -1;
this.uuid = uuid;
this.playerName = playerName;
this.premium = premium;
this.lastIp = lastIp;
}
public String getPlayerName() {
return playerName;
}
public synchronized long getUserId() {
return userId;
}
protected synchronized void setUserId(long generatedId) {
this.userId = generatedId;
}
public synchronized UUID getUuid() {
return uuid;
}
public synchronized void setUuid(UUID uuid) {
this.uuid = uuid;
}
public synchronized boolean isPremium() {
return premium;
}
public synchronized void setPremium(boolean premium) {
this.premium = premium;
}
public synchronized String getLastIp() {
return lastIp;
}
public synchronized void setLastIp(String lastIp) {
this.lastIp = lastIp;
}
public synchronized long getLastLogin() {
return lastLogin;
}
public synchronized void setLastLogin(long lastLogin) {
this.lastLogin = lastLogin;
}
}

View File

@ -1,144 +0,0 @@
package com.github.games647.fastlogin.bukkit;
import com.comphenix.protocol.wrappers.WrappedSignedProperty;
import java.util.UUID;
import org.apache.commons.lang.ArrayUtils;
/**
* Represents a client connecting to the server.
*
* This session is invalid if the player disconnects or the login was successful
*/
public class PlayerSession {
private final String username;
private final String serverId;
private final byte[] verifyToken;
private UUID uuid;
private WrappedSignedProperty skinProperty;
private boolean verified;
private boolean registered;
public PlayerSession(String username, String serverId, byte[] verifyToken) {
this.username = username;
this.serverId = serverId;
this.verifyToken = ArrayUtils.clone(verifyToken);
}
public PlayerSession(String username) {
this(username, "", ArrayUtils.EMPTY_BYTE_ARRAY);
}
/**
* 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/
*
* Empty if it's a BungeeCord connection
*
* @return random generated server id
*/
public String getServerId() {
return serverId;
}
/**
* Gets the verify token the server sent to the client.
*
* Empty if it's a BungeeCord connection
*
* @return the verify token from the server
*/
public byte[] getVerifyToken() {
return ArrayUtils.clone(verifyToken);
}
/**
* Gets the username the player sent to the server
*
* @return the client sent username
*/
public String getUsername() {
return username;
}
/**
* Gets the premium skin of this player
*
* @return skin property or null if the player has no skin or is a cracked account
*/
public synchronized WrappedSignedProperty getSkin() {
return this.skinProperty;
}
/**
* Sets the premium skin property which was retrieved by the session server
*
* @param skinProperty premium skin property
*/
public synchronized void setSkin(WrappedSignedProperty skinProperty) {
this.skinProperty = skinProperty;
}
/**
* Sets whether the account of this player already exists
*
* @param registered whether the account exists
*/
public synchronized void setRegistered(boolean registered) {
this.registered = registered;
}
/**
* Gets whether the account of this player already exists.
*
* @return whether the account exists
*/
public synchronized boolean needsRegistration() {
return !registered;
}
/**
* Sets whether the player has a premium (paid account) account
* and valid session
*
* @param verified whether the player has valid session
*/
public synchronized void setVerified(boolean verified) {
this.verified = verified;
}
/**
* Get the premium UUID of this player
*
* @return the premium UUID or null if not fetched
*/
public synchronized UUID getUuid() {
return uuid;
}
/**
* Set the online UUID if it's fetched
*
* @param uuid premium UUID
*/
public synchronized void setUuid(UUID uuid) {
this.uuid = uuid;
}
/**
* Get whether the player has a premium (paid account) account
* and valid session
*
* @return whether the player has a valid session
*/
public synchronized boolean isVerified() {
return verified;
}
}

View File

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

View File

@ -1,201 +0,0 @@
package com.github.games647.fastlogin.bukkit;
import com.comphenix.protocol.utility.SafeCacheBuilder;
import com.google.common.cache.CacheLoader;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.UUID;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
public class Storage {
private static final String PREMIUM_TABLE = "premium";
private final ConcurrentMap<String, PlayerProfile> profileCache = SafeCacheBuilder
.<String, PlayerProfile>newBuilder()
.concurrencyLevel(20)
.expireAfterAccess(30, TimeUnit.MINUTES)
.build(new CacheLoader<String, PlayerProfile>() {
@Override
public PlayerProfile load(String key) throws Exception {
//should be fetched manually
throw new UnsupportedOperationException("Not supported yet.");
}
});
private final HikariDataSource dataSource;
private final FastLoginBukkit plugin;
public Storage(FastLoginBukkit plugin, String driver, String host, int port, String databasePath
, String user, String pass) {
this.plugin = plugin;
HikariConfig databaseConfig = new HikariConfig();
databaseConfig.setUsername(user);
databaseConfig.setPassword(pass);
databaseConfig.setDriverClassName(driver);
databasePath = databasePath.replace("{pluginDir}", plugin.getDataFolder().getAbsolutePath());
String jdbcUrl = "jdbc:";
if (driver.contains("sqlite")) {
jdbcUrl += "sqlite" + "://" + databasePath;
databaseConfig.setConnectionTestQuery("SELECT 1");
} else {
jdbcUrl += "mysql" + "://" + host + ':' + port + '/' + databasePath;
}
databaseConfig.setJdbcUrl(jdbcUrl);
this.dataSource = new HikariDataSource(databaseConfig);
}
public void createTables() throws SQLException {
Connection con = null;
try {
con = dataSource.getConnection();
Statement statement = con.createStatement();
String createDataStmt = "CREATE TABLE IF NOT EXISTS " + PREMIUM_TABLE + " ("
+ "`UserID` INTEGER PRIMARY KEY AUTO_INCREMENT, "
+ "`UUID` CHAR(36), "
+ "`Name` VARCHAR(16) NOT NULL, "
+ "`Premium` BOOLEAN NOT NULL, "
+ "`LastIp` VARCHAR(255) NOT NULL, "
+ "`LastLogin` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "
+ "UNIQUE (`UUID`), "
//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");
}
statement.executeUpdate(createDataStmt);
} finally {
closeQuietly(con);
}
}
public PlayerProfile getProfile(String name, boolean fetch) {
if (profileCache.containsKey(name)) {
return profileCache.get(name);
} else if (fetch) {
Connection con = null;
try {
con = dataSource.getConnection();
PreparedStatement loadStatement = con.prepareStatement("SELECT * FROM " + PREMIUM_TABLE
+ " WHERE `Name`=? LIMIT 1");
loadStatement.setString(1, name);
ResultSet resultSet = loadStatement.executeQuery();
if (resultSet.next()) {
long userId = resultSet.getInt(1);
String unparsedUUID = resultSet.getString(2);
UUID uuid;
if (unparsedUUID == null) {
uuid = null;
} else {
uuid = FastLoginBukkit.parseId(unparsedUUID);
}
// String name = resultSet.getString(3);
boolean premium = resultSet.getBoolean(4);
String lastIp = resultSet.getString(5);
long lastLogin = resultSet.getTimestamp(6).getTime();
PlayerProfile playerProfile = new PlayerProfile(userId, uuid, name, premium, lastIp, lastLogin);
profileCache.put(name, playerProfile);
return playerProfile;
} else {
PlayerProfile crackedProfile = new PlayerProfile(null, name, false, "");
profileCache.put(name, crackedProfile);
return crackedProfile;
}
} catch (SQLException sqlEx) {
plugin.getLogger().log(Level.SEVERE, "Failed to query profile", sqlEx);
} finally {
closeQuietly(con);
}
}
return null;
}
public boolean save(PlayerProfile playerProfile) {
Connection con = null;
try {
con = dataSource.getConnection();
UUID uuid = playerProfile.getUuid();
if (playerProfile.getUserId() == -1) {
PreparedStatement saveStatement = con.prepareStatement("INSERT INTO " + PREMIUM_TABLE
+ " (UUID, Name, Premium, LastIp) VALUES (?, ?, ?, ?)", Statement.RETURN_GENERATED_KEYS);
if (uuid == null) {
saveStatement.setString(1, null);
} else {
saveStatement.setString(1, uuid.toString().replace("-", ""));
}
saveStatement.setString(2, playerProfile.getPlayerName());
saveStatement.setBoolean(3, playerProfile.isPremium());
saveStatement.setString(4, playerProfile.getLastIp());
saveStatement.execute();
ResultSet generatedKeys = saveStatement.getGeneratedKeys();
if (generatedKeys != null && generatedKeys.next()) {
playerProfile.setUserId(generatedKeys.getInt(1));
}
} else {
PreparedStatement saveStatement = con.prepareStatement("UPDATE " + PREMIUM_TABLE
+ " SET UUID=?, Name=?, Premium=?, LastIp=?, LastLogin=CURRENT_TIMESTAMP WHERE UserID=?");
if (uuid == null) {
saveStatement.setString(1, null);
} else {
saveStatement.setString(1, uuid.toString().replace("-", ""));
}
saveStatement.setString(2, playerProfile.getPlayerName());
saveStatement.setBoolean(3, playerProfile.isPremium());
saveStatement.setString(4, playerProfile.getLastIp());
// saveStatement.setTimestamp(5, new Timestamp(playerProfile.getLastLogin()));
saveStatement.setLong(5, playerProfile.getUserId());
saveStatement.execute();
}
return true;
} catch (SQLException ex) {
plugin.getLogger().log(Level.SEVERE, "Failed to save playerProfile", ex);
} finally {
closeQuietly(con);
}
return false;
}
public void close() {
dataSource.close();
profileCache.clear();
}
private void closeQuietly(Connection con) {
if (con != null) {
try {
con.close();
} catch (SQLException sqlEx) {
plugin.getLogger().log(Level.SEVERE, "Failed to close connection", sqlEx);
}
}
}
}

View File

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

View File

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

View File

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

View File

@ -1,81 +0,0 @@
package com.github.games647.fastlogin.bukkit.commands;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import com.github.games647.fastlogin.bukkit.PlayerProfile;
import com.google.common.io.ByteArrayDataOutput;
import com.google.common.io.ByteStreams;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
public class CrackedCommand implements CommandExecutor {
protected final FastLoginBukkit plugin;
public CrackedCommand(FastLoginBukkit plugin) {
this.plugin = plugin;
}
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (args.length == 0) {
if (!(sender instanceof Player)) {
//console or command block
sender.sendMessage(ChatColor.DARK_RED + "Only players can remove themselves from the premium list");
return true;
}
final Player player = (Player) sender;
// UUID uuid = player.getUniqueId();
if (plugin.isBungeeCord()) {
notifiyBungeeCord((Player) sender);
sender.sendMessage(ChatColor.YELLOW + "Sending request...");
} else {
//todo: load async if it's not in the cache anymore
final PlayerProfile profile = plugin.getStorage().getProfile(player.getName(), true);
if (profile.isPremium()) {
sender.sendMessage(ChatColor.DARK_GREEN + "Removed from the list of premium players");
profile.setPremium(false);
profile.setUuid(null);
Bukkit.getScheduler().runTaskAsynchronously(plugin, new Runnable() {
@Override
public void run() {
plugin.getStorage().save(profile);
}
});
} else {
sender.sendMessage(ChatColor.DARK_RED + "You are not in the premium list");
}
}
return true;
} else {
sender.sendMessage(ChatColor.DARK_RED + "NOT IMPLEMENTED YET");
//todo:
// String playerName = args[0];
// boolean existed = plugin.getEnabledPremium().remove(playerName);
// if (existed) {
// sender.sendMessage(ChatColor.DARK_GREEN + "Removed from the list of premium players");
// notifiyBungeeCord((Player) sender);
// } else {
// sender.sendMessage(ChatColor.DARK_RED + "User is not in the premium list");
// }
}
return true;
}
private void notifiyBungeeCord(Player target) {
if (plugin.isBungeeCord()) {
ByteArrayDataOutput dataOutput = ByteStreams.newDataOutput();
dataOutput.writeUTF("OFF");
target.sendPluginMessage(plugin, plugin.getName(), dataOutput.toByteArray());
}
}
}

View File

@ -1,87 +0,0 @@
package com.github.games647.fastlogin.bukkit.commands;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import com.github.games647.fastlogin.bukkit.PlayerProfile;
import com.google.common.io.ByteArrayDataOutput;
import com.google.common.io.ByteStreams;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
/**
* Let users activate fast login by command. This only be accessible if
* the user has access to it's account. So we can make sure that not another
* person with a paid account and the same username can steal his account.
*/
public class PremiumCommand implements CommandExecutor {
protected final FastLoginBukkit plugin;
public PremiumCommand(FastLoginBukkit plugin) {
this.plugin = plugin;
}
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (args.length == 0) {
if (!(sender instanceof Player)) {
//console or command block
sender.sendMessage(ChatColor.DARK_RED + "Only players can add themselves as premium");
return true;
}
Player player = (Player) sender;
// UUID uuid = player.getUniqueId();
if (plugin.isBungeeCord()) {
notifiyBungeeCord(player);
sender.sendMessage(ChatColor.YELLOW + "Sending request...");
} else {
// //todo: load async if it's not in the cache anymore
final PlayerProfile profile = plugin.getStorage().getProfile(player.getName(), true);
if (profile.isPremium()) {
sender.sendMessage(ChatColor.DARK_RED + "You are already on the premium list");
} else {
//todo: resolve uuid
profile.setPremium(true);
Bukkit.getScheduler().runTaskAsynchronously(plugin, new Runnable() {
@Override
public void run() {
plugin.getStorage().save(profile);
}
});
sender.sendMessage(ChatColor.DARK_GREEN + "Added to the list of premium players");
}
}
return true;
} else {
sender.sendMessage(ChatColor.DARK_RED + "NOT IMPLEMENTED YET");
//todo: async load
// String playerName = args[0];
// boolean didntexist = plugin.getEnabledPremium().add(playerName);
// if (!didntexist) {
// sender.sendMessage(ChatColor.DARK_RED + "You are already on the premium list");
// } else {
// sender.sendMessage(ChatColor.DARK_GREEN + "Added to the list of premium players");
// }
// notifiyBungeeCord();
}
return true;
}
private void notifiyBungeeCord(Player target) {
if (plugin.isBungeeCord()) {
ByteArrayDataOutput dataOutput = ByteStreams.newDataOutput();
dataOutput.writeUTF("ON");
target.sendPluginMessage(plugin, plugin.getName(), dataOutput.toByteArray());
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,35 +0,0 @@
package com.github.games647.fastlogin.bukkit.hooks;
import fr.xephi.authme.api.NewAPI;
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 BukkitAuthPlugin {
@Override
public boolean forceLogin(Player player) {
//skips registration and login
NewAPI.getInstance().forceLogin(player);
//commented because the operation above is performed async -> race conditions
// return NewAPI.getInstance().isAuthenticated(player);
return true;
}
@Override
public boolean isRegistered(String playerName) throws Exception {
return NewAPI.getInstance().isRegistered(playerName);
}
@Override
public boolean forceRegister(Player player, String password) {
NewAPI.getInstance().forceRegister(player, password);
return true;
}
}

View File

@ -1,60 +0,0 @@
package com.github.games647.fastlogin.bukkit.hooks;
import org.bukkit.entity.Player;
/**
* Represents a supporting authentication plugin in Bukkit/Spigot/... servers
*/
public interface BukkitAuthPlugin {
/**
* Login the premium (paid account) player after
* the player joined successfully the server.
*
* <strong>This operation will be performed async while the player successfully
* joined the server.</strong>
*
* @param player the player that needs to be logged in
* @return if the operation was successful
*/
boolean forceLogin(Player player);
/**
* Checks whether an account exists for this player name.
*
* This check should check if a cracked player account exists
* so we can be sure the premium player doesn't steal the account
* of that player.
*
* This operation will be performed async while the player is
* connecting.
*
* @param playerName player name
* @return if the player has an account
* @throws Exception if an error occurred
*/
boolean isRegistered(String playerName) throws Exception;
/**
* Forces a register in order to protect the paid account.
*
* <strong>This operation will be performed async while the player successfully
* joined the server.</strong>
*
* After a successful registration the player should be logged
* in too.
*
* The method will be called only for premium accounts.
* So it's recommended to set additionally premium property
* if possible.
*
* Background: If we don't register an account, cracked players
* could steal the unregistered account from the paid
* player account
*
* @param player the premium account
* @param password a strong random generated password
* @return if the operation was successful
*/
boolean forceRegister(Player player, String password);
}

View File

@ -1,119 +0,0 @@
package com.github.games647.fastlogin.bukkit.hooks;
import com.comphenix.protocol.reflect.FuzzyReflection;
import de.st_ddt.crazylogin.CrazyLogin;
import de.st_ddt.crazylogin.data.LoginPlayerData;
import de.st_ddt.crazylogin.databases.CrazyLoginDataDatabase;
import de.st_ddt.crazylogin.listener.PlayerListener;
import de.st_ddt.crazylogin.metadata.Authenticated;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.logging.Level;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
/**
* Github: https://github.com/ST-DDT/CrazyLogin
*
* Project page:
*
* Bukkit: http://dev.bukkit.org/server-mods/crazylogin/
*/
public class CrazyLoginHook implements BukkitAuthPlugin {
protected final CrazyLogin crazyLoginPlugin = CrazyLogin.getPlugin();
private final PlayerListener playerListener = getListener();
@Override
public boolean forceLogin(final Player player) {
//not thread-safe operation
Future<LoginPlayerData> future = Bukkit.getScheduler().callSyncMethod(crazyLoginPlugin
, new Callable<LoginPlayerData>() {
@Override
public LoginPlayerData call() throws Exception {
LoginPlayerData playerData = crazyLoginPlugin.getPlayerData(player.getName());
if (playerData != null) {
//mark the account as logged in
playerData.setLoggedIn(true);
String ip = player.getAddress().getAddress().getHostAddress();
//this should be done after login to restore the inventory, unhide players, prevent potential memory leaks...
//from: https://github.com/ST-DDT/CrazyLogin/blob/master/src/main/java/de/st_ddt/crazylogin/CrazyLogin.java#L1948
playerData.resetLoginFails();
player.setFireTicks(0);
if (playerListener != null) {
playerListener.removeMovementBlocker(player);
playerListener.disableHidenInventory(player);
playerListener.disableSaveLogin(player);
playerListener.unhidePlayer(player);
}
//loginFailuresPerIP.remove(IP);
//illegalCommandUsesPerIP.remove(IP);
//tempBans.remove(IP);
playerData.addIP(ip);
player.setMetadata("Authenticated", new Authenticated(crazyLoginPlugin, player));
crazyLoginPlugin.unregisterDynamicHooks();
return playerData;
}
return null;
}
});
try {
LoginPlayerData result = future.get();
if (result != null && result.isLoggedIn()) {
//SQL-Queries should run async
crazyLoginPlugin.getCrazyDatabase().saveWithoutPassword(result);
return true;
}
} catch (InterruptedException | ExecutionException ex) {
crazyLoginPlugin.getLogger().log(Level.SEVERE, "Failed to forceLogin", ex);
return false;
}
return false;
}
@Override
public boolean isRegistered(String playerName) throws Exception {
return crazyLoginPlugin.getPlayerData(playerName) != null;
}
@Override
public boolean forceRegister(final Player player, String password) {
CrazyLoginDataDatabase crazyDatabase = crazyLoginPlugin.getCrazyDatabase();
//this executes a sql query and accesses only thread safe collections so we can run it async
LoginPlayerData playerData = crazyLoginPlugin.getPlayerData(player.getName());
if (playerData == null) {
//create a fake account - this will be saved to the database with the password=FAILEDLOADING
//user cannot login with that password unless the admin uses plain text
//this automatically marks the player as logged in
playerData = new LoginPlayerData(player);
crazyDatabase.save(playerData);
return forceLogin(player);
}
return false;
}
private PlayerListener getListener() {
PlayerListener listener;
try {
listener = FuzzyReflection.getFieldValue(crazyLoginPlugin, PlayerListener.class, true);
} catch (Exception ex) {
crazyLoginPlugin.getLogger().log(Level.SEVERE, "Failed to get the listener instance for auto login", ex);
listener = null;
}
return listener;
}
}

View File

@ -1,92 +0,0 @@
package com.github.games647.fastlogin.bukkit.hooks;
import com.google.common.base.Charsets;
import com.lenis0012.bukkit.ls.LoginSecurity;
import com.lenis0012.bukkit.ls.data.DataManager;
import java.net.InetAddress;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.logging.Level;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
/**
* Github: https://github.com/lenis0012/LoginSecurity-2 Project page:
*
* Bukkit: http://dev.bukkit.org/bukkit-plugins/loginsecurity/ Spigot:
* https://www.spigotmc.org/resources/loginsecurity.19362/
*
* on join:
* https://github.com/lenis0012/LoginSecurity-2/blob/master/src/main/java/com/lenis0012/bukkit/ls/LoginSecurity.java#L282
*/
public class LoginSecurityHook implements BukkitAuthPlugin {
protected final LoginSecurity securityPlugin = LoginSecurity.instance;
@Override
public boolean forceLogin(final Player player) {
//Login command of this plugin: (How the plugin logs the player in)
//https://github.com/lenis0012/LoginSecurity-2/blob/master/src/main/java/com/lenis0012/bukkit/ls/commands/LoginCommand.java#L39
//not thread-safe operation
Future<Boolean> future = Bukkit.getScheduler().callSyncMethod(securityPlugin, new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
String name = player.getName().toLowerCase();
//mark the user as logged in
securityPlugin.authList.remove(name);
//cancel timeout timer
securityPlugin.thread.timeout.remove(name);
//remove effects and restore location
securityPlugin.rehabPlayer(player, name);
return true;
}
});
try {
return future.get();
} catch (InterruptedException | ExecutionException ex) {
securityPlugin.getLogger().log(Level.SEVERE, "Failed to forceLogin", ex);
return false;
}
}
@Override
public boolean isRegistered(String playerName) throws Exception {
//https://github.com/lenis0012/LoginSecurity-2/blob/master/src/main/java/com/lenis0012/bukkit/ls/LoginSecurity.java#L296
DataManager dataManager = securityPlugin.data;
//https://github.com/lenis0012/LoginSecurity-2/blob/master/src/main/java/com/lenis0012/bukkit/ls/LoginSecurity.java#L283
UUID offlineUuid = UUID.nameUUIDFromBytes(("OfflinePlayer:" + playerName).getBytes(Charsets.UTF_8));
return dataManager.isRegistered(offlineUuid.toString().replace("-", ""));
//check for loginsecurity sessions in order to prevent a sql query?
//sesUse && thread.getSession().containsKey(uuid) && checkLastIp(player)) {
}
@Override
public boolean forceRegister(final Player player, final String password) {
final DataManager dataManager = securityPlugin.data;
UUID playerUUID = player.getUniqueId();
final String uuidString = playerUUID.toString().replace("-", "");
final InetAddress ipAddress = player.getAddress().getAddress();
final String passwordHash = securityPlugin.hasher.hash(password);
//this executes a sql query without interacting with other parts so we can run it async.
dataManager.register(uuidString, passwordHash, securityPlugin.hasher.getTypeId(), ipAddress.toString());
String storedPassword = dataManager.getPassword(uuidString);
if (storedPassword != null && storedPassword.equals(passwordHash)) {
//the register method silents any excpetion so check if our entry was saved
return forceLogin(player);
}
return false;
}
}

View File

@ -1,68 +0,0 @@
package com.github.games647.fastlogin.bukkit.hooks;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.logging.Level;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.royaldev.royalauth.AuthPlayer;
import org.royaldev.royalauth.Config;
import org.royaldev.royalauth.RoyalAuth;
/**
* Github: https://github.com/RoyalDev/RoyalAuth
*
* Project page:
*
* Bukkit: http://dev.bukkit.org/bukkit-plugins/royalauth/
*/
public class RoyalAuthHook implements BukkitAuthPlugin {
private final RoyalAuth royalAuthPlugin = (RoyalAuth) Bukkit.getPluginManager().getPlugin("RoyalAuth");
@Override
public boolean forceLogin(final Player player) {
//not thread-safe
Future<Boolean> future = Bukkit.getScheduler().callSyncMethod(royalAuthPlugin, new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
AuthPlayer authPlayer = AuthPlayer.getAuthPlayer(player);
//https://github.com/RoyalDev/RoyalAuth/blob/master/src/main/java/org/royaldev/royalauth/commands/CmdLogin.java#L62
//not thread-safe
authPlayer.login();
return authPlayer.isLoggedIn();
}
});
try {
return future.get();
} catch (InterruptedException | ExecutionException ex) {
royalAuthPlugin.getLogger().log(Level.SEVERE, "Failed to forceLogin", ex);
return false;
}
}
@Override
public boolean isRegistered(String playerName) throws Exception {
AuthPlayer authPlayer = AuthPlayer.getAuthPlayer(playerName);
return authPlayer.isRegistered();
}
@Override
public boolean forceRegister(Player player, String password) {
//https://github.com/RoyalDev/RoyalAuth/blob/master/src/main/java/org/royaldev/royalauth/commands/CmdRegister.java#L50
AuthPlayer authPlayer = AuthPlayer.getAuthPlayer(player);
boolean registerSuccess = authPlayer.setPassword(password, Config.passwordHashType);
if (registerSuccess) {
//login in the player after registration
return forceLogin(player);
}
return false;
}
}

View File

@ -1,61 +0,0 @@
package com.github.games647.fastlogin.bukkit.hooks;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
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;
/**
* Project page:
*
* Bukkit: http://dev.bukkit.org/bukkit-plugins/ultraauth-aa/
* Spigot: https://www.spigotmc.org/resources/ultraauth.17044/
*/
public class UltraAuthHook implements BukkitAuthPlugin {
protected final Plugin ultraAuthPlugin = Main.main;
@Override
public boolean forceLogin(final Player player) {
//not thread-safe
Future<Boolean> future = Bukkit.getScheduler().callSyncMethod(ultraAuthPlugin, new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
UltraAuthAPI.authenticatedPlayer(player);
return UltraAuthAPI.isAuthenticated(player);
}
});
try {
return future.get();
} catch (InterruptedException | ExecutionException ex) {
ultraAuthPlugin.getLogger().log(Level.SEVERE, "Failed to forceLogin", ex);
return false;
}
}
@Override
public boolean isRegistered(String playerName) throws Exception {
return UltraAuthAPI.isRegisterd(new FakePlayer(playerName));
}
@Override
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
return forceLogin(player);
}
return false;
}
}

View File

@ -1,90 +0,0 @@
package com.github.games647.fastlogin.bukkit.hooks;
import de.luricos.bukkit.xAuth.xAuth;
import de.luricos.bukkit.xAuth.xAuthPlayer;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.logging.Level;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
/**
* Github: https://github.com/LycanDevelopment/xAuth/
*
* Project page:
*
* Bukkit: http://dev.bukkit.org/bukkit-plugins/xauth/
*/
public class xAuthHook implements BukkitAuthPlugin {
protected final xAuth xAuthPlugin = xAuth.getPlugin();
@Override
public boolean forceLogin(final Player player) {
//not thread-safe
Future<Boolean> future = Bukkit.getScheduler().callSyncMethod(xAuthPlugin, new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
xAuthPlayer xAuthPlayer = xAuthPlugin.getPlayerManager().getPlayer(player);
if (xAuthPlayer != null) {
//we checked that the player is premium (paid account)
xAuthPlayer.setPremium(true);
//unprotect the inventory, op status...
return xAuthPlugin.getPlayerManager().doLogin(xAuthPlayer);
}
return false;
}
});
try {
return future.get();
} catch (InterruptedException | ExecutionException ex) {
xAuthPlugin.getLogger().log(Level.SEVERE, "Failed to forceLogin", ex);
return false;
}
}
@Override
public boolean isRegistered(String playerName) throws Exception {
//this will load the player if it's not in the cache
xAuthPlayer xAuthPlayer = xAuthPlugin.getPlayerManager().getPlayer(playerName);
return xAuthPlayer != null && xAuthPlayer.isRegistered();
}
@Override
public boolean forceRegister(final Player player, final String password) {
//not thread-safe
Future<Boolean> future = Bukkit.getScheduler().callSyncMethod(xAuthPlugin, new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
xAuthPlayer xAuthPlayer = xAuthPlugin.getPlayerManager().getPlayer(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);
if (registerSuccess) {
//login in the player after registration
return forceLogin(player);
}
}
return false;
}
});
try {
return future.get();
} catch (InterruptedException | ExecutionException ex) {
xAuthPlugin.getLogger().log(Level.SEVERE, "Failed to forceLogin", ex);
return false;
}
}
}

View File

@ -1,57 +0,0 @@
package com.github.games647.fastlogin.bukkit.listener;
import com.comphenix.protocol.wrappers.WrappedGameProfile;
import com.comphenix.protocol.wrappers.WrappedSignedProperty;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import com.github.games647.fastlogin.bukkit.ForceLoginTask;
import com.github.games647.fastlogin.bukkit.PlayerSession;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
/**
* This listener tells authentication plugins if the player has a premium account and we checked it successfully. So the
* plugin can skip authentication.
*/
public class BukkitJoinListener implements Listener {
private static final long DELAY_LOGIN = 20L / 2;
protected final FastLoginBukkit plugin;
public BukkitJoinListener(FastLoginBukkit plugin) {
this.plugin = plugin;
}
@EventHandler(ignoreCancelled = true)
public void onPlayerJoin(PlayerJoinEvent joinEvent) {
final Player player = joinEvent.getPlayer();
//removing the session because we now use it
final PlayerSession session = plugin.getSessions().get(player.getAddress().toString());
if (session != null && plugin.getConfig().getBoolean("forwardSkin")) {
WrappedGameProfile gameProfile = WrappedGameProfile.fromPlayer(player);
WrappedSignedProperty skin = session.getSkin();
if (skin != null) {
gameProfile.getProperties().put("textures", skin);
}
}
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);
}
}
@EventHandler
public void onPlayerQuit(PlayerQuitEvent quitEvent) {
final Player player = quitEvent.getPlayer();
//prevent memory leaks
player.removeMetadata(plugin.getName(), plugin);
}
}

View File

@ -1,121 +0,0 @@
package com.github.games647.fastlogin.bukkit.listener;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import com.github.games647.fastlogin.bukkit.ForceLoginTask;
import com.github.games647.fastlogin.bukkit.PlayerSession;
import com.github.games647.fastlogin.bukkit.hooks.BukkitAuthPlugin;
import com.google.common.base.Charsets;
import com.google.common.io.ByteArrayDataInput;
import com.google.common.io.ByteStreams;
import com.google.common.io.Files;
import java.io.File;
import java.io.IOException;
import java.util.UUID;
import java.util.logging.Level;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.metadata.FixedMetadataValue;
import org.bukkit.plugin.messaging.PluginMessageListener;
/**
* Responsible for receiving messages from a BungeeCord instance.
*
* This class also receives the plugin message from the bungeecord version of this plugin in order to get notified if
* the connection is in online mode.
*/
public class BungeeCordListener implements PluginMessageListener {
private static final String FILE_NAME = "proxy-whitelist.txt";
protected final FastLoginBukkit plugin;
//null if whitelist is empty so bungeecord support is disabled
private final UUID proxyId;
public BungeeCordListener(FastLoginBukkit plugin) {
this.plugin = plugin;
this.proxyId = loadBungeeCordId();
}
@Override
public void onPluginMessageReceived(String channel, Player player, byte[] message) {
if (!channel.equals(plugin.getName())) {
return;
}
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});
final String playerName = dataInput.readUTF();
//check if the player is still online or disconnected
final Player checkedPlayer = plugin.getServer().getPlayerExact(playerName);
//fail if target player is blacklisted because already authed 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();
UUID sourceId = new UUID(mostSignificantBits, leastSignificantBits);
plugin.getLogger().log(Level.FINEST, "Received proxy id {0} from {1}", new Object[]{sourceId, player});
//fail if BungeeCord support is disabled (id = null)
if (sourceId.equals(proxyId)) {
final PlayerSession playerSession = new PlayerSession(playerName);
final String id = '/' + checkedPlayer.getAddress().getAddress().getHostAddress() + ':'
+ checkedPlayer.getAddress().getPort();
if ("AUTO_LOGIN".equalsIgnoreCase(subchannel)) {
playerSession.setVerified(true);
playerSession.setRegistered(true);
plugin.getSessions().put(id, playerSession);
} else if ("AUTO_REGISTER".equalsIgnoreCase(subchannel)) {
playerSession.setVerified(true);
Bukkit.getScheduler().runTaskAsynchronously(plugin, new Runnable() {
@Override
public void run() {
BukkitAuthPlugin authPlugin = plugin.getAuthPlugin();
try {
//we need to check if the player is registered on Bukkit too
if (authPlugin != null && !authPlugin.isRegistered(playerName)) {
plugin.getSessions().put(id, playerSession);
}
} catch (Exception ex) {
plugin.getLogger().log(Level.SEVERE, "Failed to query isRegistered", ex);
}
}
});
}
Bukkit.getScheduler().runTaskAsynchronously(plugin, new ForceLoginTask(plugin, player));
}
}
}
public UUID loadBungeeCordId() {
File whitelistFile = new File(plugin.getDataFolder(), FILE_NAME);
//create a new folder if it doesn't exist. Fail silently otherwise
whitelistFile.getParentFile().mkdir();
try {
if (!whitelistFile.exists()) {
whitelistFile.createNewFile();
}
String firstLine = Files.readFirstLine(whitelistFile, Charsets.UTF_8);
if (firstLine != null && !firstLine.isEmpty()) {
return UUID.fromString(firstLine.trim());
}
} catch (IOException ex) {
plugin.getLogger().log(Level.SEVERE, "Failed to create file for Proxy whitelist", ex);
} catch (Exception ex) {
plugin.getLogger().log(Level.SEVERE, "Failed to retrieve proxy Id. Disabling BungeeCord support", ex);
}
return null;
}
}

View File

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

View File

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

View File

@ -1,228 +0,0 @@
package com.github.games647.fastlogin.bukkit.listener;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.ProtocolManager;
import com.comphenix.protocol.events.PacketAdapter;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.injector.server.TemporaryPlayerFactory;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.wrappers.WrappedChatComponent;
import com.comphenix.protocol.wrappers.WrappedGameProfile;
import com.github.games647.fastlogin.bukkit.EncryptionUtil;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import com.github.games647.fastlogin.bukkit.PlayerSession;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigInteger;
import java.security.PrivateKey;
import java.util.Arrays;
import java.util.UUID;
import java.util.logging.Level;
import javax.crypto.SecretKey;
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 {
private final ProtocolManager protocolManager;
//hides the inherit Plugin plugin field, but we need this type
private final FastLoginBukkit plugin;
public EncryptionPacketListener(FastLoginBukkit plugin, ProtocolManager protocolManger) {
//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;
this.protocolManager = protocolManger;
}
/**
* 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) {
Player player = packetEvent.getPlayer();
//the player name is unknown to ProtocolLib (so getName() doesn't work) - now uses ip:port as key
String uniqueSessionKey = player.getAddress().toString();
PlayerSession session = plugin.getSessions().get(uniqueSessionKey);
if (session == null) {
disconnect(packetEvent, "Invalid request", Level.FINE
, "Player {0} tried to send encryption response at invalid state"
, player.getAddress());
return;
}
PrivateKey privateKey = plugin.getServerKey().getPrivate();
byte[] sharedSecret = packetEvent.getPacket().getByteArrays().read(0);
SecretKey loginKey = EncryptionUtil.decryptSharedKey(privateKey, sharedSecret);
if (!checkVerifyToken(session, privateKey, packetEvent) || !encryptConnection(player, loginKey, packetEvent)) {
return;
}
//this makes sure the request from the client is for us
//this might be relevant http://www.sk89q.com/2011/09/minecraft-name-spoofing-exploit/
String generatedId = session.getServerId();
//https://github.com/bergerkiller/CraftSource/blob/master/net.minecraft.server/LoginListener.java#L193
//generate the server id based on client and server data
byte[] serverIdHash = EncryptionUtil.getServerIdHash(generatedId, plugin.getServerKey().getPublic(), loginKey);
String serverId = (new BigInteger(serverIdHash)).toString(16);
String username = session.getUsername();
if (plugin.getApiConnector().hasJoinedServer(session, serverId)) {
plugin.getLogger().log(Level.FINE, "Player {0} has a verified premium account", username);
session.setVerified(true);
setPremiumUUID(session, player);
receiveFakeStartPacket(username, player);
} else {
//user tried to fake a authentication
disconnect(packetEvent, "Invalid session", Level.FINE
, "Player {0} ({1}) tried to log in with an invalid session ServerId: {2}"
, session.getUsername(), player.getAddress(), serverId);
}
//this is a fake packet; it shouldn't be send to the server
packetEvent.setCancelled(true);
}
private void setPremiumUUID(PlayerSession session, Player player) {
UUID uuid = session.getUuid();
if (plugin.getConfig().getBoolean("premiumUuid") && uuid != null) {
try {
Object networkManager = getNetworkManager(player);
//https://github.com/bergerkiller/CraftSource/blob/master/net.minecraft.server/NetworkManager.java#L69
Field spoofField = FuzzyReflection.fromObject(networkManager).getFieldByType("spoofedUUID", UUID.class);
spoofField.set(networkManager, uuid);
} catch (ReflectiveOperationException reflectiveOperationException) {
plugin.getLogger().log(Level.SEVERE, "Error setting premium uuid", reflectiveOperationException);
}
}
}
private boolean checkVerifyToken(PlayerSession session, PrivateKey privateKey, PacketEvent packetEvent) {
byte[] requestVerify = session.getVerifyToken();
//encrypted verify token
byte[] responseVerify = packetEvent.getPacket().getByteArrays().read(1);
//https://github.com/bergerkiller/CraftSource/blob/master/net.minecraft.server/LoginListener.java#L182
if (!Arrays.equals(requestVerify, EncryptionUtil.decryptData(privateKey, responseVerify))) {
//check if the verify token are equal to the server sent one
disconnect(packetEvent, "Invalid token", Level.FINE
, "Player {0} ({1}) tried to login with an invalid verify token. "
+ "Server: {2} Client: {3}"
, session.getUsername(), packetEvent.getPlayer().getAddress(), requestVerify, responseVerify);
return false;
}
return true;
}
//try to get the networkManager from ProtocolLib
private Object getNetworkManager(Player player)
throws IllegalAccessException, NoSuchFieldException {
Object socketInjector = TemporaryPlayerFactory.getInjectorFromPlayer(player);
Field injectorField = socketInjector.getClass().getDeclaredField("injector");
injectorField.setAccessible(true);
Object rawInjector = injectorField.get(socketInjector);
injectorField = rawInjector.getClass().getDeclaredField("networkManager");
injectorField.setAccessible(true);
return injectorField.get(rawInjector);
}
private boolean encryptConnection(Player player, SecretKey loginKey, PacketEvent packetEvent)
throws IllegalArgumentException {
try {
//get the NMS connection handle of this player
Object networkManager = getNetworkManager(player);
//try to detect the method by parameters
Method encryptConnectionMethod = FuzzyReflection.fromObject(networkManager)
.getMethodByParameters("a", SecretKey.class);
//encrypt/decrypt following packets
//the client expects this behaviour
encryptConnectionMethod.invoke(networkManager, loginKey);
} catch (ReflectiveOperationException ex) {
disconnect(packetEvent, "Error occurred", Level.SEVERE, "Couldn't enable encryption", ex);
return false;
}
return true;
}
private void disconnect(PacketEvent packetEvent, String kickReason, Level logLevel, String logMessage
, Object... arguments) {
plugin.getLogger().log(logLevel, logMessage, arguments);
kickPlayer(packetEvent.getPlayer(), kickReason);
//cancel the event in order to prevent the server receiving an invalid packet
packetEvent.setCancelled(true);
}
private void kickPlayer(Player player, String reason) {
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
protocolManager.sendServerPacket(player, kickPacket);
//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);
}
}
//fake a new login packet in order to let the server handle all the other stuff
private void receiveFakeStartPacket(String username, Player from) {
//see StartPacketListener for packet information
PacketContainer startPacket = protocolManager.createPacket(PacketType.Login.Client.START);
//uuid is ignored by the packet definition
WrappedGameProfile fakeProfile = new WrappedGameProfile(UUID.randomUUID(), username);
startPacket.getGameProfiles().write(0, fakeProfile);
try {
//we don't want to handle our own packets so ignore filters
protocolManager.recieveClientPacket(from, startPacket, false);
} catch (InvocationTargetException | IllegalAccessException ex) {
plugin.getLogger().log(Level.WARNING, "Failed to fake a new start packet", ex);
//cancel the event in order to prevent the server receiving an invalid packet
kickPlayer(from, "Error occured");
}
}
}

View File

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

View File

@ -1,84 +0,0 @@
package com.github.games647.fastlogin.bukkit.listener;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import com.github.games647.fastlogin.bukkit.PlayerProfile;
import com.github.games647.fastlogin.bukkit.PlayerSession;
import com.github.games647.fastlogin.bukkit.hooks.BukkitAuthPlugin;
import java.net.InetSocketAddress;
import java.util.UUID;
import java.util.logging.Level;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import protocolsupport.api.events.PlayerLoginStartEvent;
import protocolsupport.api.events.PlayerPropertiesResolveEvent;
public class ProtocolSupportListener implements Listener {
protected final FastLoginBukkit plugin;
public ProtocolSupportListener(FastLoginBukkit plugin) {
this.plugin = plugin;
}
@EventHandler(ignoreCancelled = true)
public void onLoginStart(PlayerLoginStartEvent loginStartEvent) {
if (loginStartEvent.isLoginDenied()) {
return;
}
String username = loginStartEvent.getName();
//remove old data every time on a new login in order to keep the session only for one person
plugin.getSessions().remove(username);
PlayerProfile playerProfile = plugin.getStorage().getProfile(username, true);
if (playerProfile != null) {
if (playerProfile.isPremium()) {
if (playerProfile.getUserId() != -1) {
startPremiumSession(username, loginStartEvent, true);
}
} else if (playerProfile.getUserId() == -1) {
//user not exists in the db
BukkitAuthPlugin authPlugin = plugin.getAuthPlugin();
try {
if (plugin.getConfig().getBoolean("autoRegister") && !authPlugin.isRegistered(username)) {
UUID premiumUUID = plugin.getApiConnector().getPremiumUUID(username);
if (premiumUUID != null) {
plugin.getLogger().log(Level.FINER, "Player {0} uses a premium username", username);
startPremiumSession(username, loginStartEvent, false);
}
}
} catch (Exception ex) {
plugin.getLogger().log(Level.SEVERE, "Failed to query isRegistered", ex);
}
}
}
}
@EventHandler(ignoreCancelled = true)
public void onPropertiesResolve(PlayerPropertiesResolveEvent propertiesResolveEvent) {
//skin was resolved -> premium player
if (propertiesResolveEvent.hasProperty("textures")) {
InetSocketAddress address = propertiesResolveEvent.getAddress();
PlayerSession session = plugin.getSessions().get(address.toString());
if (session != null) {
session.setVerified(true);
}
}
}
private void startPremiumSession(String playerName, PlayerLoginStartEvent loginStartEvent, boolean registered) {
loginStartEvent.setOnlineMode(true);
InetSocketAddress address = loginStartEvent.getAddress();
PlayerSession playerSession = new PlayerSession(playerName, null, null);
playerSession.setRegistered(registered);
plugin.getSessions().put(address.toString(), playerSession);
if (plugin.getConfig().getBoolean("premiumUuid")) {
loginStartEvent.setUseOnlineModeUUID(true);
}
}
}

View File

@ -1,149 +0,0 @@
package com.github.games647.fastlogin.bukkit.listener;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.ProtocolManager;
import com.comphenix.protocol.events.PacketAdapter;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.events.PacketEvent;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import com.github.games647.fastlogin.bukkit.PlayerProfile;
import com.github.games647.fastlogin.bukkit.PlayerSession;
import com.github.games647.fastlogin.bukkit.hooks.BukkitAuthPlugin;
import java.lang.reflect.InvocationTargetException;
import java.security.PublicKey;
import java.util.Random;
import java.util.UUID;
import java.util.logging.Level;
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 {
private static final int VERIFY_TOKEN_LENGTH = 4;
private final ProtocolManager protocolManager;
//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, ProtocolManager protocolManger) {
//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;
this.protocolManager = protocolManger;
}
/**
* 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) {
final 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 to the server"
, new Object[]{sessionKey, username});
PlayerProfile playerProfile = plugin.getStorage().getProfile(username, true);
if (playerProfile != null) {
if (playerProfile.isPremium()) {
if (playerProfile.getUserId() != -1) {
enablePremiumLogin(username, sessionKey, player, packetEvent, true);
}
} else if (playerProfile.getUserId() == -1) {
//user not exists in the db
BukkitAuthPlugin authPlugin = plugin.getAuthPlugin();
try {
if (plugin.getConfig().getBoolean("autoRegister") && !authPlugin.isRegistered(username)) {
UUID premiumUUID = plugin.getApiConnector().getPremiumUUID(username);
if (premiumUUID != null) {
plugin.getLogger().log(Level.FINER, "Player {0} uses a premium username", username);
enablePremiumLogin(username, sessionKey, player, packetEvent, false);
}
}
} catch (Exception ex) {
plugin.getLogger().log(Level.SEVERE, "Failed to query isRegistered", ex);
}
}
}
}
//minecraft server implementation
//https://github.com/bergerkiller/CraftSource/blob/master/net.minecraft.server/LoginListener.java#L161
private void enablePremiumLogin(String username, String sessionKey, Player player, PacketEvent packetEvent
, boolean registered) {
//randomized server id to make sure the request is for our server
//this could be relevant http://www.sk89q.com/2011/09/minecraft-name-spoofing-exploit/
String serverId = Long.toString(random.nextLong(), 16);
//generate a random token which should be the same when we receive it from the client
byte[] verifyToken = new byte[VERIFY_TOKEN_LENGTH];
random.nextBytes(verifyToken);
boolean success = sentEncryptionRequest(player, serverId, verifyToken);
if (success) {
PlayerSession playerSession = new PlayerSession(username, serverId, verifyToken);
playerSession.setRegistered(registered);
plugin.getSessions().put(sessionKey, playerSession);
//cancel only if the player has a paid account otherwise login as normal offline player
packetEvent.setCancelled(true);
}
}
private boolean sentEncryptionRequest(Player player, String serverId, byte[] verifyToken) {
try {
/**
* Packet Information: http://wiki.vg/Protocol#Encryption_Request
*
* ServerID="" (String)
* key=public server key
* verifyToken=random 4 byte array
*/
PacketContainer newPacket = protocolManager.createPacket(PacketType.Login.Server.ENCRYPTION_BEGIN);
newPacket.getStrings().write(0, serverId);
newPacket.getSpecificModifier(PublicKey.class).write(0, plugin.getServerKey().getPublic());
newPacket.getByteArrays().write(0, verifyToken);
//serverId is a empty string
protocolManager.sendServerPacket(player, newPacket);
return true;
} catch (InvocationTargetException ex) {
plugin.getLogger().log(Level.SEVERE, "Cannot send encryption packet. Falling back to normal login", ex);
}
return false;
}
}

View File

@ -0,0 +1,157 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2022 games647 and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.bukkit.listener.protocollib;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Random;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
/**
* Encryption and decryption minecraft util for connection between servers
* and paid Minecraft account clients.
*
* @see net.minecraft.server.MinecraftEncryption
*/
public class EncryptionUtil {
public static final int VERIFY_TOKEN_LENGTH = 4;
public static final String KEY_PAIR_ALGORITHM = "RSA";
private EncryptionUtil() {
// utility
}
/**
* Generate an RSA key pair
*
* @return The RSA key pair.
*/
public static KeyPair generateKeyPair() {
// KeyPair b()
try {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_PAIR_ALGORITHM);
keyPairGenerator.initialize(1_024);
return keyPairGenerator.generateKeyPair();
} catch (NoSuchAlgorithmException nosuchalgorithmexception) {
// Should be existing in every vm
throw new ExceptionInInitializerError(nosuchalgorithmexception);
}
}
/**
* Generate a random token. This is used to verify that we are communicating with the same player
* in a login session.
*
* @param random random generator
* @return an error with 4 bytes long
*/
public static byte[] generateVerifyToken(Random random) {
// extracted from LoginListener
byte[] token = new byte[VERIFY_TOKEN_LENGTH];
random.nextBytes(token);
return token;
}
/**
* Generate the server id based on client and server data.
*
* @param sessionId session for the current login attempt
* @param sharedSecret shared secret between the client and the server
* @param publicKey public key of the server
* @return the server id formatted as a hexadecimal string.
*/
public static String getServerIdHashString(String sessionId, SecretKey sharedSecret, PublicKey publicKey) {
// found in LoginListener
try {
byte[] serverHash = getServerIdHash(sessionId, publicKey, sharedSecret);
return (new BigInteger(serverHash)).toString(16);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return "";
}
/**
* Decrypts the content and extracts the key spec.
*
* @param privateKey private server key
* @param sharedKey the encrypted shared key
* @return shared secret key
* @throws GeneralSecurityException if it fails to decrypt the data
*/
public static SecretKey decryptSharedKey(PrivateKey privateKey, byte[] sharedKey) throws GeneralSecurityException {
// SecretKey a(PrivateKey var0, byte[] var1)
return new SecretKeySpec(decrypt(privateKey, sharedKey), "AES");
}
public static byte[] decrypt(PrivateKey key, byte[] data) throws GeneralSecurityException {
// b(Key var0, byte[] var1)
Cipher cipher = Cipher.getInstance(key.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, key);
return decrypt(cipher, data);
}
/**
* Decrypted the given data using the cipher.
*
* @param cipher decryption cypher initialized with the private key
* @param data the encrypted data
* @return clear text data
* @throws GeneralSecurityException if it fails to decrypt the data
*/
private static byte[] decrypt(Cipher cipher, byte[] data) throws GeneralSecurityException {
// inlined: byte[] a(int var0, Key var1, byte[] var2), Cipher a(int var0, String var1, Key
// var2)
return cipher.doFinal(data);
}
private static byte[] getServerIdHash(
String sessionId, PublicKey publicKey, SecretKey sharedSecret)
throws NoSuchAlgorithmException {
// byte[] a(String var0, PublicKey var1, SecretKey var2)
MessageDigest digest = MessageDigest.getInstance("SHA-1");
// inlined from byte[] a(String var0, byte[]... var1)
digest.update(sessionId.getBytes(StandardCharsets.ISO_8859_1));
digest.update(sharedSecret.getEncoded());
digest.update(publicKey.getEncoded());
return digest.digest();
}
}

View File

@ -0,0 +1,85 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2022 games647 and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.bukkit.listener.protocollib;
import com.comphenix.protocol.events.PacketAdapter;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.wrappers.WrappedGameProfile;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import com.github.games647.fastlogin.core.hooks.bedrock.FloodgateService;
import org.geysermc.floodgate.api.FloodgateApi;
import static com.comphenix.protocol.PacketType.Login.Client.START;
import com.comphenix.protocol.ProtocolLibrary;
/**
* Manually inject Floodgate player name prefixes.
* <br>
* This is used as a workaround, because Floodgate fails to inject
* the prefixes when it's used together with ProtocolLib and FastLogin.
* <br>
* For more information visit: https://github.com/games647/FastLogin/issues/493
*/
public class ManualNameChange extends PacketAdapter {
private final FloodgateService floodgate;
public ManualNameChange(FastLoginBukkit plugin, FloodgateService floodgate) {
super(params()
.plugin(plugin)
.types(START));
this.plugin = plugin;
this.floodgate = floodgate;
}
public static void register(FastLoginBukkit plugin, FloodgateService floodgate) {
// they will be created with a static builder, because otherwise it will throw a NoClassDefFoundError
ProtocolLibrary.getProtocolManager()
.getAsynchronousManager()
.registerAsyncHandler(new ManualNameChange(plugin, floodgate))
.start();
}
@Override
public void onPacketReceiving(PacketEvent packetEvent) {
PacketContainer packet = packetEvent.getPacket();
WrappedGameProfile originalProfile = packet.getGameProfiles().read(0);
if (floodgate.getBedrockPlayer(originalProfile.getName()) == null) {
//not a Floodgate player, no need to add a prefix
return;
}
packet.setMeta("original_name", originalProfile.getName());
String prefixedName = FloodgateApi.getInstance().getPlayerPrefix() + originalProfile.getName();
WrappedGameProfile updatedProfile = originalProfile.withName(prefixedName);
packet.getGameProfiles().write(0, updatedProfile);
}
}

View File

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

View File

@ -0,0 +1,139 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2022 games647 and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.bukkit.listener.protocollib;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.events.PacketAdapter;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.events.PacketEvent;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import com.github.games647.fastlogin.core.RateLimiter;
import java.security.KeyPair;
import java.security.SecureRandom;
import org.bukkit.entity.Player;
import static com.comphenix.protocol.PacketType.Login.Client.ENCRYPTION_BEGIN;
import static com.comphenix.protocol.PacketType.Login.Client.START;
public class ProtocolLibListener extends PacketAdapter {
public static final String SOURCE_META_KEY = "source";
private final FastLoginBukkit plugin;
//just create a new once on plugin enable. This used for verify token generation
private final SecureRandom random = new SecureRandom();
private final KeyPair keyPair = EncryptionUtil.generateKeyPair();
private final RateLimiter rateLimiter;
public ProtocolLibListener(FastLoginBukkit plugin, RateLimiter rateLimiter) {
//run async in order to not block the server, because we are making api calls to Mojang
super(params()
.plugin(plugin)
.types(START, ENCRYPTION_BEGIN)
.optionAsync());
this.plugin = plugin;
this.rateLimiter = rateLimiter;
}
public static void register(FastLoginBukkit plugin, RateLimiter rateLimiter) {
// they will be created with a static builder, because otherwise it will throw a NoClassDefFoundError
// TODO: make synchronous processing, but do web or database requests async
ProtocolLibrary.getProtocolManager()
.getAsynchronousManager()
.registerAsyncHandler(new ProtocolLibListener(plugin, rateLimiter))
.start();
}
@Override
public void onPacketReceiving(PacketEvent packetEvent) {
if (packetEvent.isCancelled()
|| plugin.getCore().getAuthPluginHook() == null
|| !plugin.isServerFullyStarted()) {
return;
}
if (isFastLoginPacket(packetEvent)) {
// this is our own packet
return;
}
Player sender = packetEvent.getPlayer();
PacketType packetType = packetEvent.getPacketType();
if (packetType == START) {
if (!rateLimiter.tryAcquire()) {
plugin.getLog().warn("Simple Anti-Bot join limit - Ignoring {}", sender);
return;
}
onLogin(packetEvent, sender);
} else {
onEncryptionBegin(packetEvent, sender);
}
}
private Boolean isFastLoginPacket(PacketEvent packetEvent) {
return packetEvent.getPacket().getMeta(SOURCE_META_KEY)
.map(val -> val.equals(plugin.getName()))
.orElse(false);
}
private void onEncryptionBegin(PacketEvent packetEvent, Player sender) {
byte[] sharedSecret = packetEvent.getPacket().getByteArrays().read(0);
packetEvent.getAsyncMarker().incrementProcessingDelay();
Runnable verifyTask = new VerifyResponseTask(plugin, packetEvent, sender, sharedSecret, keyPair);
plugin.getScheduler().runAsync(verifyTask);
}
private void onLogin(PacketEvent packetEvent, Player player) {
//this includes ip:port. Should be unique for an incoming login request with a timeout of 2 minutes
String sessionKey = player.getAddress().toString();
//remove old data every time on a new login in order to keep the session only for one person
plugin.removeSession(player.getAddress());
//player.getName() won't work at this state
PacketContainer packet = packetEvent.getPacket();
String username = packet.getGameProfiles().read(0).getName();
if (packetEvent.getPacket().getMeta("original_name").isPresent()) {
//username has been injected by ManualNameChange.java
username = (String) packetEvent.getPacket().getMeta("original_name").get();
}
plugin.getLog().trace("GameProfile {} with {} connecting", sessionKey, username);
packetEvent.getAsyncMarker().incrementProcessingDelay();
Runnable nameCheckTask = new NameCheckTask(plugin, random, player, packetEvent, username, keyPair.getPublic());
plugin.getScheduler().runAsync(nameCheckTask);
}
}

View File

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

View File

@ -0,0 +1,92 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2022 games647 and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.bukkit.listener.protocollib;
import com.comphenix.protocol.reflect.MethodUtils;
import com.comphenix.protocol.reflect.accessors.Accessors;
import com.comphenix.protocol.reflect.accessors.MethodAccessor;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.WrappedGameProfile;
import com.comphenix.protocol.wrappers.WrappedSignedProperty;
import com.github.games647.craftapi.model.skin.Textures;
import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerLoginEvent;
import org.bukkit.event.player.PlayerLoginEvent.Result;
import java.lang.reflect.InvocationTargetException;
public class SkinApplyListener implements Listener {
private static final Class<?> GAME_PROFILE = MinecraftReflection.getGameProfileClass();
private static final MethodAccessor GET_PROPERTIES = Accessors.getMethodAccessor(GAME_PROFILE, "getProperties");
private final FastLoginBukkit plugin;
public SkinApplyListener(FastLoginBukkit plugin) {
this.plugin = plugin;
}
@EventHandler(priority = EventPriority.LOW)
//run this on the loginEvent to let skins plugins see the skin like in normal Minecraft behaviour
public void onPlayerLogin(PlayerLoginEvent loginEvent) {
if (loginEvent.getResult() != Result.ALLOWED) {
return;
}
Player player = loginEvent.getPlayer();
//go through every session, because player.getAddress is null
//loginEvent.getAddress is just a InetAddress not InetSocketAddress, so not unique enough
for (BukkitLoginSession session : plugin.getLoginSessions().values()) {
if (session.getUsername().equals(player.getName())) {
session.getSkin().ifPresent(skin -> applySkin(player, skin.getValue(), skin.getSignature()));
break;
}
}
}
private void applySkin(Player player, String skinData, String signature) {
WrappedGameProfile gameProfile = WrappedGameProfile.fromPlayer(player);
WrappedSignedProperty skin = WrappedSignedProperty.fromValues(Textures.KEY, skinData, signature);
try {
gameProfile.getProperties().put(Textures.KEY, skin);
} catch (ClassCastException castException) {
//Cauldron, MCPC, Thermos, ...
Object map = GET_PROPERTIES.invoke(gameProfile.getHandle());
try {
MethodUtils.invokeMethod(map, "put", new Object[]{Textures.KEY, skin.getHandle()});
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ex) {
plugin.getLog().error("Error setting premium skin of: {}", player, ex);
}
}
}
}

View File

@ -0,0 +1,294 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2022 games647 and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.bukkit.listener.protocollib;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.injector.server.TemporaryPlayerFactory;
import com.comphenix.protocol.reflect.FieldUtils;
import com.comphenix.protocol.reflect.FuzzyReflection;
import com.comphenix.protocol.utility.MinecraftReflection;
import com.comphenix.protocol.wrappers.WrappedChatComponent;
import com.comphenix.protocol.wrappers.WrappedGameProfile;
import com.github.games647.craftapi.model.auth.Verification;
import com.github.games647.craftapi.model.skin.SkinProperty;
import com.github.games647.craftapi.resolver.AbstractResolver;
import com.github.games647.craftapi.resolver.MojangResolver;
import com.github.games647.fastlogin.bukkit.BukkitLoginSession;
import com.github.games647.fastlogin.bukkit.FastLoginBukkit;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.*;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.Key;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.util.Arrays;
import java.util.Optional;
import java.util.UUID;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import org.bukkit.entity.Player;
import static com.comphenix.protocol.PacketType.Login.Client.START;
import static com.comphenix.protocol.PacketType.Login.Server.DISCONNECT;
public class VerifyResponseTask implements Runnable {
private static final String ENCRYPTION_CLASS_NAME = "MinecraftEncryption";
private static final Class<?> ENCRYPTION_CLASS;
static {
ENCRYPTION_CLASS = MinecraftReflection.getMinecraftClass("util." + ENCRYPTION_CLASS_NAME, ENCRYPTION_CLASS_NAME);
}
private final FastLoginBukkit plugin;
private final PacketEvent packetEvent;
private final KeyPair serverKey;
private final Player player;
private final byte[] sharedSecret;
private static Method encryptMethod;
private static Method cipherMethod;
public VerifyResponseTask(FastLoginBukkit plugin, PacketEvent packetEvent, Player player,
byte[] sharedSecret, KeyPair keyPair) {
this.plugin = plugin;
this.packetEvent = packetEvent;
this.player = player;
this.sharedSecret = Arrays.copyOf(sharedSecret, sharedSecret.length);
this.serverKey = keyPair;
}
@Override
public void run() {
try {
BukkitLoginSession session = plugin.getSession(player.getAddress());
if (session == null) {
disconnect("invalid-request",
"GameProfile {0} tried to send encryption response at invalid state",
player.getAddress());
} else {
verifyResponse(session);
}
} finally {
//this is a fake packet; it shouldn't be sent to the server
synchronized (packetEvent.getAsyncMarker().getProcessingLock()) {
packetEvent.setCancelled(true);
}
ProtocolLibrary.getProtocolManager().getAsynchronousManager().signalPacketTransmission(packetEvent);
}
}
private void verifyResponse(BukkitLoginSession session) {
PrivateKey privateKey = serverKey.getPrivate();
SecretKey loginKey;
try {
loginKey = EncryptionUtil.decryptSharedKey(privateKey, sharedSecret);
} catch (GeneralSecurityException securityEx) {
disconnect("error-kick", "Cannot decrypt received contents", securityEx);
return;
}
try {
if (!checkVerifyToken(session) || !enableEncryption(loginKey)) {
return;
}
} catch (Exception ex) {
disconnect("error-kick", "Cannot decrypt received contents", ex);
return;
}
String serverId = EncryptionUtil.getServerIdHashString("", loginKey, serverKey.getPublic());
String requestedUsername = session.getRequestUsername();
InetSocketAddress socketAddress = player.getAddress();
try {
MojangResolver resolver = plugin.getCore().getResolver();
InetAddress address = socketAddress.getAddress();
Optional<Verification> response = resolver.hasJoined(requestedUsername, serverId, address);
if (response.isPresent()) {
Verification verification = response.get();
plugin.getLog().info("Profile {} has a verified premium account", requestedUsername);
String realUsername = verification.getName();
if (realUsername == null) {
disconnect("invalid-session", "Username field null for {}", requestedUsername);
return;
}
SkinProperty[] properties = verification.getProperties();
if (properties.length > 0) {
session.setSkinProperty(properties[0]);
}
session.setVerifiedUsername(realUsername);
session.setUuid(verification.getId());
session.setVerified(true);
setPremiumUUID(session.getUuid());
receiveFakeStartPacket(realUsername);
} else {
//user tried to fake an authentication
disconnect("invalid-session", "GameProfile {} ({}) tried to log in with an invalid session. ServerId: {}", session.getRequestUsername(), socketAddress, serverId);
}
} catch (IOException ioEx) {
disconnect("error-kick", "Failed to connect to session server", ioEx);
}
}
private void setPremiumUUID(UUID premiumUUID) {
if (plugin.getConfig().getBoolean("premiumUuid") && premiumUUID != null) {
try {
Object networkManager = getNetworkManager();
//https://github.com/bergerkiller/CraftSource/blob/master/net.minecraft.server/NetworkManager.java#L69
FieldUtils.writeField(networkManager, "spoofedUUID", premiumUUID, true);
} catch (Exception exc) {
plugin.getLog().error("Error setting premium uuid of {}", player, exc);
}
}
}
private boolean checkVerifyToken(BukkitLoginSession session) throws GeneralSecurityException {
byte[] requestVerify = session.getVerifyToken();
//encrypted verify token
byte[] responseVerify = packetEvent.getPacket().getByteArrays().read(1);
//https://github.com/bergerkiller/CraftSource/blob/master/net.minecraft.server/LoginListener.java#L182
if (!Arrays.equals(requestVerify, EncryptionUtil.decrypt(serverKey.getPrivate(), responseVerify))) {
//check if the verify-token are equal to the server sent one
disconnect("invalid-verify-token",
"GameProfile {0} ({1}) tried to login with an invalid verify token. Server: {2} Client: {3}",
session.getRequestUsername(), packetEvent.getPlayer().getAddress(), requestVerify, responseVerify);
return false;
}
return true;
}
//try to get the networkManager from ProtocolLib
private Object getNetworkManager() throws IllegalAccessException, ClassNotFoundException {
Object injectorContainer = TemporaryPlayerFactory.getInjectorFromPlayer(player);
// ChannelInjector
Class<?> injectorClass = Class.forName("com.comphenix.protocol.injector.netty.Injector");
Object rawInjector = FuzzyReflection.getFieldValue(injectorContainer, injectorClass, true);
return FieldUtils.readField(rawInjector, "networkManager", true);
}
private boolean enableEncryption(SecretKey loginKey) throws IllegalArgumentException {
plugin.getLog().info("Enabling onlinemode encryption for {}", player.getName());
// Initialize method reflections
if (encryptMethod == null) {
Class<?> networkManagerClass = MinecraftReflection.getNetworkManagerClass();
try {
// Try to get the old (pre MC 1.16.4) encryption method
encryptMethod = FuzzyReflection.fromClass(networkManagerClass)
.getMethodByParameters("a", SecretKey.class);
} catch (IllegalArgumentException exception) {
// Get the new encryption method
encryptMethod = FuzzyReflection.fromClass(networkManagerClass)
.getMethodByParameters("a", Cipher.class, Cipher.class);
// Get the needed Cipher helper method (used to generate ciphers from login key)
cipherMethod = FuzzyReflection.fromClass(ENCRYPTION_CLASS)
.getMethodByParameters("a", int.class, Key.class);
}
}
try {
Object networkManager = this.getNetworkManager();
// If cipherMethod is null - use old encryption (pre MC 1.16.4), otherwise use the new cipher one
if (cipherMethod == null) {
// Encrypt/decrypt packet flow, this behaviour is expected by the client
encryptMethod.invoke(networkManager, loginKey);
} else {
// Create ciphers from login key
Object decryptionCipher = cipherMethod.invoke(null, Cipher.DECRYPT_MODE, loginKey);
Object encryptionCipher = cipherMethod.invoke(null, Cipher.ENCRYPT_MODE, loginKey);
// Encrypt/decrypt packet flow, this behaviour is expected by the client
encryptMethod.invoke(networkManager, decryptionCipher, encryptionCipher);
}
} catch (Exception ex) {
disconnect("error-kick", "Couldn't enable encryption", ex);
return false;
}
return true;
}
private void disconnect(String reasonKey, String logMessage, Object... arguments) {
plugin.getLog().error(logMessage, arguments);
kickPlayer(plugin.getCore().getMessage(reasonKey));
}
private void kickPlayer(String reason) {
PacketContainer kickPacket = new PacketContainer(DISCONNECT);
kickPacket.getChatComponents().write(0, WrappedChatComponent.fromText(reason));
try {
//send kick packet at login state
//the normal event.getPlayer.kickPlayer(String) method does only work at play state
ProtocolLibrary.getProtocolManager().sendServerPacket(player, kickPacket);
//tell the server that we want to close the connection
player.kickPlayer("Disconnect");
} catch (InvocationTargetException ex) {
plugin.getLog().error("Error sending kick packet for: {}", player, ex);
}
}
//fake a new login packet in order to let the server handle all the other stuff
private void receiveFakeStartPacket(String username) {
//see StartPacketListener for packet information
PacketContainer startPacket = new PacketContainer(START);
//uuid is ignored by the packet definition
WrappedGameProfile fakeProfile = new WrappedGameProfile(UUID.randomUUID(), username);
startPacket.getGameProfiles().write(0, fakeProfile);
try {
//we don't want to handle our own packets so ignore filters
startPacket.setMeta(ProtocolLibListener.SOURCE_META_KEY, plugin.getName());
ProtocolLibrary.getProtocolManager().recieveClientPacket(player, startPacket, true);
} catch (InvocationTargetException | IllegalAccessException ex) {
plugin.getLog().warn("Failed to fake a new start packet for: {}", username, ex);
//cancel the event in order to prevent the server receiving an invalid packet
kickPlayer(plugin.getCore().getMessage("error-kick"));
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,73 +0,0 @@
# FastLogin config
# Project site: https://www.spigotmc.org/resources/fastlogin.14153
# Source code: https://github.com/games647/FastLogin
#
# You can access the newest config here:
# https://github.com/games647/FastLogin/blob/master/bukkit/src/main/resources/config.yml
# Request a premium login without forcing the player to type a command
#
# If you activate autoRegister, this plugin will check/do these points on login:
# 1. An existing cracked account shouldn't exist
# -> paid accounts cannot steal the existing account of cracked players
# - (Already registered players could still use the /premium command to activate premium checks)
# 2. Automatically registers an account with a strong random generated password
# -> cracked player cannot register an account for the premium player and so cannot the steal the account
#
# Furthermore the premium player check have to be made based on the player name
# This means if a cracked player connects to the server and we request a paid account login from this player
# the player just disconnect and sees the message: 'bad login' or 'invalid session'
# There is no way to change this message
# For more information: https://github.com/games647/FastLogin#why-do-players-have-to-invoke-a-command
autoRegister: 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, ...)
#
# 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.
#
# This is a example and doesn't apply for every plugin.
# Example: If you want to ban players who aren't online at the moment, the ban plugin will look
# after a offline uuid associated to the player, because the server is in offline mode. Then the premium
# 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.
#
# This feature requires Cauldron, Spigot or a fork of Spigot (PaperSpigot, TacoSpigot)
premiumUuid: false
# If your players have a premium account and a skin associated to their account, this plugin
# can download the data and set it to the online player.
#
# 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)
# * has a skin
#
# This means this plugin doesn't need to create a new connection to the Mojang servers, because
# the skin data is included in the Auth-Verification-Response sent by Mojang. If you want to use for other
# 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, ...
forwardSkin: true
# Database configuration
# Recommened 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 and SQLite
#driver: com.mysql.jdbc.Driver
#host: localhost
#port: 3306
#database: fastlogin
#username: myUser
#password: myPassword

View File

@ -1,28 +1,36 @@
# 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's 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.commit.id.abbrev}
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}
website: ${project.url}
dev-url: ${project.url}
# Without Protocollib the plugin does not work at all
depend: [ProtocolLib]
# This plugin doesn't have to be transformed for compatibility with Minecraft >= 1.13
api-version: '1.13'
softdepend:
# We depend on either ProtocolLib or ProtocolSupport
- ProtocolSupport
- ProtocolLib
# Premium variable
- PlaceholderAPI
# Bedrock Player Bridge
- Geyser-Spigot
- floodgate
# Auth plugins
- xAuth
- AuthMe
- CrazyLogin
- LoginSecurity
- RoyalAuth
- SodionAuth
- xAuth
- LogIt
- UltraAuth
- CrazyLogin
commands:
${project.parent.name}:
@ -31,11 +39,11 @@ commands:
usage: /<command> [player]
permission: ${project.artifactId}.command.premium
unpremium:
description: 'Label the invoker or the player specified as cracked if he was marked premium before'
aliases: [cracked]
cracked:
description: 'Label the invoker or the player specified as cracked if marked premium before'
aliases: [unpremium]
usage: /<command> [player]
permission: ${project.artifactId}.command.unpremium
permission: ${project.artifactId}.command.cracked
permissions:
${project.artifactId}.command.premium:
@ -47,11 +55,11 @@ permissions:
children:
${project.artifactId}.command.premium: true
${project.artifactId}.command.unpremium:
${project.artifactId}.command.cracked:
description: 'Label themselves as cracked'
default: true
${project.artifactId}.command..unpremium.other:
${project.artifactId}.command.cracked.other:
description: 'Label others as cracked'
children:
${project.artifactId}.command.unpremium: true
${project.artifactId}.command.cracked: true

View File

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

Binary file not shown.

View File

@ -1,60 +1,205 @@
<!--
SPDX-License-Identifier: MIT
The MIT License (MIT)
Copyright (c) 2015-2022 games647 and contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.github.games647</groupId>
<artifactId>fastlogin</artifactId>
<version>1.1</version>
<version>1.11-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<!--This have to be in lowercase because it's used by plugin.yml-->
<!--This has to be in lowercase because it's used by plugin.yml-->
<artifactId>fastlogin.bungee</artifactId>
<packaging>jar</packaging>
<!--Represents the main plugin-->
<name>FastLoginBungee</name>
<repositories>
<!--Waterfall-->
<!-- <repository>
<id>ellune-releases</id>
<url>https://repo.ellune.net/content/repositories/snapshots/</url>
</repository>-->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.3.0</version>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
<shadedArtifactAttached>false</shadedArtifactAttached>
<artifactSet>
<excludes>
<!--Those classes are already present in BungeeCord version-->
<exclude>net.md-5:bungeecord-config</exclude>
<exclude>com.google.code.gson:gson</exclude>
<exclude>com.google.guava:guava</exclude>
</excludes>
</artifactSet>
<relocations>
<relocation>
<pattern>com.zaxxer.hikari</pattern>
<shadedPattern>fastlogin.hikari</shadedPattern>
</relocation>
<relocation>
<pattern>org.slf4j</pattern>
<shadedPattern>fastlogin.slf4j</shadedPattern>
</relocation>
</relocations>
<!-- Rename the service file too to let SLF4J api find our own relocated jdk logger -->
<!-- Located in META-INF/services -->
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
</transformers>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/MANIFEST.MF</exclude>
<exclude>**/module-info.class</exclude>
</excludes>
</filter>
</filters>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<!--BungeeCord with also the part outside the API-->
<repositories>
<repository>
<id>RYRED-REPO</id>
<url>http://mvn.ryred.co/repository/snapshots/</url>
<id>codemc-repo</id>
<url>https://repo.codemc.io/repository/maven-public/</url>
</repository>
<repository>
<id>spigotplugins-repo</id>
<url>https://maven.gamestrike.de/mvn/</url>
</repository>
<!--Github automatic maven builds-->
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<dependencies>
<!--Common plugin component-->
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>fastlogin.core</artifactId>
<version>${project.version}</version>
</dependency>
<!--BungeeCord with also the part outside the API-->
<dependency>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-proxy</artifactId>
<version>1.9-SNAPSHOT</version>
<version>1.19-R0.1-SNAPSHOT</version>
<scope>provided</scope>
<!-- Use our own newer api version -->
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--Floodgate for Xbox Live Authentication-->
<dependency>
<groupId>org.geysermc.floodgate</groupId>
<artifactId>api</artifactId>
<version>${floodgate.version}</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>io.netty</groupId>
<artifactId>*</artifactId>
</exclusion>
<exclusion>
<groupId>org.geysermc.cumulus</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Bedrock player bridge -->
<dependency>
<groupId>org.geysermc</groupId>
<artifactId>core</artifactId>
<version>${geyser.version}</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- We need the API, but it was excluded above -->
<dependency>
<groupId>org.geysermc</groupId>
<artifactId>geyser-api</artifactId>
<version>${geyser.version}</version>
<scope>provided</scope>
</dependency>
<!-- <dependency>
<groupId>io.github.waterfallmc</groupId>
<artifactId>waterfall-api</artifactId>
<version>1.9-SNAPSHOT</version>
<type>jar</type>
<scope>provided</scope>
</dependency>-->
<!--Login plugin-->
<dependency>
<groupId>me.vik1395</groupId>
<artifactId>BungeeAuth</artifactId>
<version>1.4</version>
<scope>system</scope>
<systemPath>${project.basedir}/lib/BungeeAuth-1.4.jar</systemPath>
</dependency>
<dependency>
<groupId>com.github.MatteCarra</groupId>
<artifactId>BungeeAuth</artifactId>
<version>-1.2.1-gc367d92-8</version>
<groupId>de.xxschrandxx.bca</groupId>
<artifactId>BungeeCordAuthenticator</artifactId>
<version>0.0.2</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</project>

View File

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

View File

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

View File

@ -1,143 +1,217 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2022 games647 and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.bungee;
import com.github.games647.fastlogin.bungee.hooks.BungeeAuthHook;
import com.github.games647.fastlogin.bungee.hooks.BungeeAuthPlugin;
import com.google.common.cache.CacheBuilder;
import com.github.games647.fastlogin.bungee.hook.BungeeAuthHook;
import com.github.games647.fastlogin.bungee.hook.BungeeCordAuthenticatorBungeeHook;
import com.github.games647.fastlogin.bungee.listener.ConnectListener;
import com.github.games647.fastlogin.bungee.listener.PluginMessageListener;
import com.github.games647.fastlogin.core.AsyncScheduler;
import com.github.games647.fastlogin.core.CommonUtil;
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
import com.github.games647.fastlogin.core.hooks.bedrock.BedrockService;
import com.github.games647.fastlogin.core.hooks.bedrock.FloodgateService;
import com.github.games647.fastlogin.core.hooks.bedrock.GeyserService;
import com.github.games647.fastlogin.core.message.ChangePremiumMessage;
import com.github.games647.fastlogin.core.message.ChannelMessage;
import com.github.games647.fastlogin.core.message.NamespaceKey;
import com.github.games647.fastlogin.core.message.SuccessMessage;
import com.github.games647.fastlogin.core.shared.FastLoginCore;
import com.github.games647.fastlogin.core.shared.PlatformPlugin;
import com.google.common.collect.MapMaker;
import com.google.common.io.ByteArrayDataOutput;
import com.google.common.io.ByteStreams;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.util.Random;
import java.util.UUID;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.concurrent.ThreadFactory;
import net.md_5.bungee.Util;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.connection.PendingConnection;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.connection.Server;
import net.md_5.bungee.api.plugin.Plugin;
import net.md_5.bungee.config.Configuration;
import net.md_5.bungee.config.ConfigurationProvider;
import net.md_5.bungee.config.YamlConfiguration;
import net.md_5.bungee.api.plugin.PluginManager;
import net.md_5.bungee.api.scheduler.GroupedThreadFactory;
import org.geysermc.floodgate.api.FloodgateApi;
import org.geysermc.geyser.GeyserImpl;
import org.slf4j.Logger;
/**
* BungeeCord version of FastLogin. This plugin keeps track on online mode connections.
*/
public class FastLoginBungee extends Plugin {
public class FastLoginBungee extends Plugin implements PlatformPlugin<CommandSender> {
private static final char[] CHARACTERS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
.toCharArray();
private final ConcurrentMap<PendingConnection, BungeeLoginSession> session = new MapMaker().weakKeys().makeMap();
public static UUID parseId(String withoutDashes) {
return Util.getUUID(withoutDashes);
}
private BungeeAuthPlugin bungeeAuthPlugin;
private final MojangApiConnector mojangApiConnector = new MojangApiConnector(this);
private Storage storage;
private Configuration configuration;
private final Random random = new Random();
private final ConcurrentMap<PendingConnection, Object> pendingAutoRegister = CacheBuilder
.newBuilder()
.expireAfterWrite(1, TimeUnit.MINUTES)
.<PendingConnection, Object>build().asMap();
private FastLoginCore<ProxiedPlayer, CommandSender, FastLoginBungee> core;
private AsyncScheduler scheduler;
private FloodgateService floodgateService;
private GeyserService geyserService;
private Logger logger;
@Override
public void onEnable() {
if (!getDataFolder().exists()) {
getDataFolder().mkdir();
}
logger = CommonUtil.initializeLoggerService(getLogger());
scheduler = new AsyncScheduler(logger, task -> getProxy().getScheduler().runAsync(this, task));
File configFile = new File(getDataFolder(), "config.yml");
if (!configFile.exists()) {
try (InputStream in = getResourceAsStream("config.yml")) {
Files.copy(in, configFile.toPath());
} catch (IOException ioExc) {
getLogger().log(Level.SEVERE, "Error saving default config", ioExc);
}
}
try {
configuration = ConfigurationProvider.getProvider(YamlConfiguration.class).load(configFile);
String driver = configuration.getString("driver");
String host = configuration.getString("host", "");
int port = configuration.getInt("port", 3306);
String database = configuration.getString("database");
String username = configuration.getString("username", "");
String password = configuration.getString("password", "");
storage = new Storage(this, driver, host, port, database, username, password);
try {
storage.createTables();
} catch (Exception ex) {
getLogger().log(Level.SEVERE, "Failed to setup database. Disabling plugin...", ex);
return;
}
} catch (IOException ioExc) {
getLogger().log(Level.SEVERE, "Error loading config. Disabling plugin...", ioExc);
core = new FastLoginCore<>(this);
core.load();
if (!core.setupDatabase()) {
return;
}
//events
getProxy().getPluginManager().registerListener(this, new PlayerConnectionListener(this));
if (isPluginInstalled("floodgate")) {
floodgateService = new FloodgateService(FloodgateApi.getInstance(), core);
}
//this is required to listen to messages from the server
getProxy().registerChannel(getDescription().getName());
if (isPluginInstalled("Geyser-BungeeCord")) {
geyserService = new GeyserService(GeyserImpl.getInstance(), core);
}
//events
PluginManager pluginManager = getProxy().getPluginManager();
ConnectListener connectListener = new ConnectListener(this, core.getRateLimiter());
pluginManager.registerListener(this, connectListener);
pluginManager.registerListener(this, new PluginMessageListener(this));
//this is required to listen to incoming messages from the server
getProxy().registerChannel(NamespaceKey.getCombined(getName(), ChangePremiumMessage.CHANGE_CHANNEL));
getProxy().registerChannel(NamespaceKey.getCombined(getName(), SuccessMessage.SUCCESS_CHANNEL));
registerHook();
}
public String generateStringPassword() {
StringBuilder generatedPassword = new StringBuilder(8);
for (int i = 1; i <= 8; i++) {
generatedPassword.append(CHARACTERS[random.nextInt(CHARACTERS.length - 1)]);
}
return generatedPassword.toString();
}
@Override
public void onDisable() {
if (storage != null) {
storage.close();
if (core != null) {
core.close();
}
}
public Configuration getConfiguration() {
return configuration;
public FastLoginCore<ProxiedPlayer, CommandSender, FastLoginBungee> getCore() {
return core;
}
public Storage getStorage() {
return storage;
}
public MojangApiConnector getMojangApiConnector() {
return mojangApiConnector;
}
public ConcurrentMap<PendingConnection, Object> getPendingAutoRegister() {
return pendingAutoRegister;
}
/**
* Get the auth plugin hook for BungeeCord
*
* @return the auth hook for BungeeCord. null if none found
*/
public BungeeAuthPlugin getBungeeAuthPlugin() {
return bungeeAuthPlugin;
public ConcurrentMap<PendingConnection, BungeeLoginSession> getSession() {
return session;
}
private void registerHook() {
Plugin plugin = getProxy().getPluginManager().getPlugin("BungeeAuth");
if (plugin != null) {
bungeeAuthPlugin = new BungeeAuthHook();
getLogger().info("Hooked into BungeeAuth");
try {
List<Class<? extends AuthPlugin<ProxiedPlayer>>> hooks = Arrays.asList(
BungeeAuthHook.class, BungeeCordAuthenticatorBungeeHook.class);
for (Class<? extends AuthPlugin<ProxiedPlayer>> clazz : hooks) {
String pluginName = clazz.getSimpleName();
pluginName = pluginName.substring(0, pluginName.length() - "Hook".length());
//uses only member classes which uses AuthPlugin interface (skip interfaces)
Plugin plugin = getProxy().getPluginManager().getPlugin(pluginName);
if (plugin != null) {
logger.info("Hooking into auth plugin: {}", pluginName);
core.setAuthPluginHook(
clazz.getDeclaredConstructor(FastLoginBungee.class).newInstance(this));
break;
}
}
} catch (ReflectiveOperationException ex) {
logger.error("Couldn't load the auth hook class", ex);
}
}
public void sendPluginMessage(Server server, ChannelMessage message) {
if (server != null) {
ByteArrayDataOutput dataOutput = ByteStreams.newDataOutput();
message.writeTo(dataOutput);
NamespaceKey channel = new NamespaceKey(getName(), message.getChannelName());
server.sendData(channel.getCombinedName(), dataOutput.toByteArray());
}
}
@Override
public String getName() {
return getDescription().getName();
}
@Override
public Path getPluginFolder() {
return getDataFolder().toPath();
}
@Override
public Logger getLog() {
return logger;
}
@Override
public void sendMessage(CommandSender receiver, String message) {
receiver.sendMessage(TextComponent.fromLegacyText(message));
}
@Override
@SuppressWarnings("deprecation")
public ThreadFactory getThreadFactory() {
return new ThreadFactoryBuilder()
.setNameFormat(getName() + " Pool Thread #%1$d")
//Hikari create daemons by default
.setDaemon(true)
.setThreadFactory(new GroupedThreadFactory(this, getName()))
.build();
}
@Override
public AsyncScheduler getScheduler() {
return scheduler;
}
@Override
public boolean isPluginInstalled(String name) {
return getProxy().getPluginManager().getPlugin(name) != null;
}
public FloodgateService getFloodgateService() {
return floodgateService;
}
public GeyserService getGeyserService() {
return geyserService;
}
@Override
public BedrockService<?> getBedrockService() {
if (floodgateService != null) {
return floodgateService;
}
return geyserService;
}
}

View File

@ -1,74 +0,0 @@
package com.github.games647.fastlogin.bungee;
import com.github.games647.fastlogin.bungee.hooks.BungeeAuthPlugin;
import com.google.common.io.ByteArrayDataOutput;
import com.google.common.io.ByteStreams;
import java.util.UUID;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.connection.Server;
public class ForceLoginTask implements Runnable {
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;
this.server = server;
}
@Override
public void run() {
PlayerProfile playerProfile = plugin.getStorage().getProfile(player.getName(), false);
//force login only on success
if (player.getPendingConnection().isOnlineMode()) {
boolean autoRegister = plugin.getPendingAutoRegister().remove(player.getPendingConnection()) != null;
BungeeAuthPlugin authPlugin = plugin.getBungeeAuthPlugin();
if (authPlugin == null) {
sendBukkitLoginNotification(autoRegister);
} else if (player.isConnected()) {
if (autoRegister) {
String password = plugin.generateStringPassword();
if (authPlugin.forceRegister(player, password)) {
sendBukkitLoginNotification(autoRegister);
}
} else if (authPlugin.forceLogin(player)) {
sendBukkitLoginNotification(autoRegister);
}
}
} else {
//cracked player
//update only on success to prevent corrupt data
playerProfile.setPremium(false);
plugin.getStorage().save(playerProfile);
}
}
private void sendBukkitLoginNotification(boolean autoRegister) {
ByteArrayDataOutput dataOutput = ByteStreams.newDataOutput();
//subchannel name
if (autoRegister) {
dataOutput.writeUTF("AUTO_REGISTER");
} else {
dataOutput.writeUTF("AUTO_LOGIN");
}
//Data is sent through a random player. We have to tell the Bukkit version of this plugin the target
dataOutput.writeUTF(player.getName());
//proxy identifier to check if it's a acceptable proxy
UUID proxyId = UUID.fromString(plugin.getProxy().getConfig().getUuid());
dataOutput.writeLong(proxyId.getMostSignificantBits());
dataOutput.writeLong(proxyId.getLeastSignificantBits());
if (server != null) {
server.sendData(plugin.getDescription().getName(), dataOutput.toByteArray());
}
}
}

View File

@ -1,80 +0,0 @@
package com.github.games647.fastlogin.bungee;
import com.google.gson.Gson;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.UUID;
import java.util.logging.Level;
import java.util.regex.Pattern;
import net.md_5.bungee.BungeeCord;
public class MojangApiConnector {
//http connection, read timeout and user agent for a connection to mojang api servers
private static final int TIMEOUT = 1 * 1_000;
private static final String USER_AGENT = "Premium-Checker";
//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?";
//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_
private static final String VALID_PLAYERNAME = "^\\w{2,16}$";
//compile the pattern only on plugin enable -> and this have to be threadsafe
private final Pattern playernameMatcher = Pattern.compile(VALID_PLAYERNAME);
private final FastLoginBungee plugin;
private final Gson gson = new Gson();
public MojangApiConnector(FastLoginBungee plugin) {
this.plugin = plugin;
}
/**
*
* @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
try {
HttpURLConnection 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 != null && !line.equals("null")) {
MojangPlayer mojangPlayer = BungeeCord.getInstance().gson.fromJson(line, MojangPlayer.class);
return FastLoginBungee.parseId(mojangPlayer.getId());
}
}
//204 - no content for not found
} catch (Exception ex) {
plugin.getLogger().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;
}
private HttpURLConnection getConnection(String url) throws IOException {
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
connection.setConnectTimeout(TIMEOUT);
connection.setReadTimeout(TIMEOUT);
//the new Mojang API just uses json as response
connection.setRequestProperty("Content-Type", "application/json");
connection.setRequestProperty("User-Agent", USER_AGENT);
return connection;
}
}

View File

@ -1,15 +0,0 @@
package com.github.games647.fastlogin.bungee;
public class MojangPlayer {
private String id;
private String name;
public String getId() {
return id;
}
public String getName() {
return name;
}
}

View File

@ -1,204 +0,0 @@
package com.github.games647.fastlogin.bungee;
import com.github.games647.fastlogin.bungee.hooks.BungeeAuthPlugin;
import com.google.common.base.Charsets;
import com.google.common.io.ByteArrayDataInput;
import com.google.common.io.ByteStreams;
import java.lang.reflect.Field;
import java.util.UUID;
import java.util.logging.Level;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.connection.PendingConnection;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.connection.Server;
import net.md_5.bungee.api.event.PluginMessageEvent;
import net.md_5.bungee.api.event.PostLoginEvent;
import net.md_5.bungee.api.event.PreLoginEvent;
import net.md_5.bungee.api.event.ServerConnectedEvent;
import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.connection.InitialHandler;
import net.md_5.bungee.connection.LoginResult;
import net.md_5.bungee.connection.LoginResult.Property;
import net.md_5.bungee.event.EventHandler;
/**
* Enables online mode logins for specified users and sends
* plugin message to the Bukkit version of this plugin in
* order to clear that the connection is online mode.
*/
public class PlayerConnectionListener implements Listener {
protected final FastLoginBungee plugin;
public PlayerConnectionListener(FastLoginBungee plugin) {
this.plugin = plugin;
}
@EventHandler
public void onPreLogin(final PreLoginEvent preLoginEvent) {
if (preLoginEvent.isCancelled()) {
return;
}
preLoginEvent.registerIntent(plugin);
ProxyServer.getInstance().getScheduler().runAsync(plugin, new Runnable() {
@Override
public void run() {
PendingConnection connection = preLoginEvent.getConnection();
String username = connection.getName();
try {
PlayerProfile playerProfile = plugin.getStorage().getProfile(username, true);
if (playerProfile != null) {
if (playerProfile.isPremium()) {
if (playerProfile.getUserId() != -1) {
connection.setOnlineMode(true);
}
} else if (playerProfile.getUserId() == -1) {
//user not exists in the db
BungeeAuthPlugin authPlugin = plugin.getBungeeAuthPlugin();
if (plugin.getConfiguration().getBoolean("autoRegister")
&& (authPlugin == null || !authPlugin.isRegistered(username))) {
UUID premiumUUID = plugin.getMojangApiConnector().getPremiumUUID(username);
if (premiumUUID != null) {
plugin.getLogger().log(Level.FINER, "Player {0} uses a premium username", username);
connection.setOnlineMode(true);
plugin.getPendingAutoRegister().put(connection, new Object());
}
}
}
}
} catch (Exception ex) {
plugin.getLogger().log(Level.SEVERE, "Failed to check premium state", ex);
} finally {
preLoginEvent.completeIntent(plugin);
}
}
});
}
@EventHandler
public void onLogin(PostLoginEvent loginEvent) {
ProxiedPlayer player = loginEvent.getPlayer();
PendingConnection connection = player.getPendingConnection();
String username = connection.getName();
if (connection.isOnlineMode()) {
PlayerProfile playerProfile = plugin.getStorage().getProfile(player.getName(), false);
playerProfile.setUuid(player.getUniqueId());
//bungeecord will do this automatically so override it on disabled option
InitialHandler initialHandler = (InitialHandler) connection;
if (!plugin.getConfiguration().getBoolean("premiumUuid")) {
try {
UUID offlineUUID = UUID.nameUUIDFromBytes(("OfflinePlayer:" + username).getBytes(Charsets.UTF_8));
Field idField = initialHandler.getClass().getDeclaredField("uniqueId");
idField.setAccessible(true);
idField.set(connection, offlineUUID);
//bungeecord doesn't support overriding the premium uuid
// connection.setUniqueId(offlineUUID);
} catch (NoSuchFieldException | IllegalAccessException ex) {
plugin.getLogger().log(Level.SEVERE, "Failed to set offline uuid", ex);
}
}
if (!plugin.getConfiguration().getBoolean("forwardSkin")) {
//this is null on offline mode
LoginResult loginProfile = initialHandler.getLoginProfile();
if (loginProfile != null) {
loginProfile.setProperties(new Property[]{});
}
}
}
}
@EventHandler
public void onServerConnected(ServerConnectedEvent serverConnectedEvent) {
ProxiedPlayer player = serverConnectedEvent.getPlayer();
ForceLoginTask loginTask = new ForceLoginTask(plugin, player, serverConnectedEvent.getServer());
ProxyServer.getInstance().getScheduler().runAsync(plugin, loginTask);
}
@EventHandler
public void onPluginMessage(PluginMessageEvent pluginMessageEvent) {
String channel = pluginMessageEvent.getTag();
if (pluginMessageEvent.isCancelled() || !plugin.getDescription().getName().equals(channel)) {
return;
}
//the client shouldn't be able to read the messages in order to know something about server internal states
//moreover the client shouldn't be able fake a running premium check by sending the result message
pluginMessageEvent.setCancelled(true);
//check if the message is sent from the server
if (Server.class.isAssignableFrom(pluginMessageEvent.getSender().getClass())) {
byte[] data = pluginMessageEvent.getData();
ByteArrayDataInput dataInput = ByteStreams.newDataInput(data);
String subchannel = dataInput.readUTF();
final ProxiedPlayer forPlayer = (ProxiedPlayer) pluginMessageEvent.getReceiver();
if ("ON".equals(subchannel)) {
ProxyServer.getInstance().getScheduler().runAsync(plugin, new Runnable() {
@Override
public void run() {
PlayerProfile playerProfile = plugin.getStorage().getProfile(forPlayer.getName(), true);
if (playerProfile.isPremium()) {
if (forPlayer.isConnected()) {
TextComponent textComponent = new TextComponent("You are already on the premium list");
textComponent.setColor(ChatColor.DARK_RED);
forPlayer.sendMessage(textComponent);
}
return;
}
playerProfile.setPremium(true);
//todo: set uuid
plugin.getStorage().save(playerProfile);
}
});
} else if ("OFF".equals(subchannel)) {
ProxyServer.getInstance().getScheduler().runAsync(plugin, new Runnable() {
@Override
public void run() {
PlayerProfile playerProfile = plugin.getStorage().getProfile(forPlayer.getName(), true);
if (!playerProfile.isPremium()) {
if (forPlayer.isConnected()) {
TextComponent textComponent = new TextComponent("You are not in the premium list");
textComponent.setColor(ChatColor.DARK_RED);
forPlayer.sendMessage(textComponent);
}
return;
}
playerProfile.setPremium(false);
playerProfile.setUuid(null);
//todo: set uuid
plugin.getStorage().save(playerProfile);
TextComponent textComponent = new TextComponent("Added to the list of premium players");
textComponent.setColor(ChatColor.DARK_GREEN);
forPlayer.sendMessage(textComponent);
}
});
} else if ("SUCCESS".equals(subchannel)) {
if (forPlayer.getPendingConnection().isOnlineMode()) {
//bukkit module successfully received and force logged in the user
//update only on success to prevent corrupt data
PlayerProfile playerProfile = plugin.getStorage().getProfile(forPlayer.getName(), false);
playerProfile.setPremium(true);
//we override this in the loginevent
// playerProfile.setUuid(forPlayer.getUniqueId());
plugin.getStorage().save(playerProfile);
TextComponent textComponent = new TextComponent("Removed to the list of premium players");
textComponent.setColor(ChatColor.DARK_GREEN);
forPlayer.sendMessage(textComponent);
}
}
}
}
}

View File

@ -1,78 +0,0 @@
package com.github.games647.fastlogin.bungee;
import java.util.UUID;
public class PlayerProfile {
private final String playerName;
private long userId;
private UUID uuid;
private boolean premium;
private String lastIp;
private long lastLogin;
public PlayerProfile(long userId, UUID uuid, String playerName, boolean premium
, String lastIp, long lastLogin) {
this.userId = userId;
this.uuid = uuid;
this.playerName = playerName;
this.premium = premium;
this.lastIp = lastIp;
this.lastLogin = lastLogin;
}
public PlayerProfile(UUID uuid, String playerName, boolean premium, String lastIp) {
this.userId = -1;
this.uuid = uuid;
this.playerName = playerName;
this.premium = premium;
this.lastIp = lastIp;
}
public String getPlayerName() {
return playerName;
}
public synchronized long getUserId() {
return userId;
}
protected synchronized void setUserId(long generatedId) {
this.userId = generatedId;
}
public synchronized UUID getUuid() {
return uuid;
}
public synchronized void setUuid(UUID uuid) {
this.uuid = uuid;
}
public synchronized boolean isPremium() {
return premium;
}
public synchronized void setPremium(boolean premium) {
this.premium = premium;
}
public synchronized String getLastIp() {
return lastIp;
}
public synchronized void setLastIp(String lastIp) {
this.lastIp = lastIp;
}
public synchronized long getLastLogin() {
return lastLogin;
}
public synchronized void setLastLogin(long lastLogin) {
this.lastLogin = lastLogin;
}
}

View File

@ -1,201 +0,0 @@
package com.github.games647.fastlogin.bungee;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.UUID;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
public class Storage {
private static final String PREMIUM_TABLE = "premium";
private final ConcurrentMap<String, PlayerProfile> profileCache = CacheBuilder
.<String, PlayerProfile>newBuilder()
.concurrencyLevel(20)
.expireAfterAccess(30, TimeUnit.MINUTES)
.build(new CacheLoader<String, PlayerProfile>() {
@Override
public PlayerProfile load(String key) throws Exception {
//should be fetched manually
throw new UnsupportedOperationException("Not supported yet.");
}
}).asMap();
private final HikariDataSource dataSource;
private final FastLoginBungee plugin;
public Storage(FastLoginBungee plugin, String driver, String host, int port, String databasePath
, String user, String pass) {
this.plugin = plugin;
HikariConfig databaseConfig = new HikariConfig();
databaseConfig.setUsername(user);
databaseConfig.setPassword(pass);
databaseConfig.setDriverClassName(driver);
databasePath = databasePath.replace("{pluginDir}", plugin.getDataFolder().getAbsolutePath());
String jdbcUrl = "jdbc:";
if (driver.contains("sqlite")) {
jdbcUrl += "sqlite" + "://" + databasePath;
databaseConfig.setConnectionTestQuery("SELECT 1");
} else {
jdbcUrl += "mysql" + "://" + host + ':' + port + '/' + databasePath;
}
databaseConfig.setJdbcUrl(jdbcUrl);
this.dataSource = new HikariDataSource(databaseConfig);
}
public void createTables() throws SQLException {
Connection con = null;
try {
con = dataSource.getConnection();
Statement statement = con.createStatement();
String createDataStmt = "CREATE TABLE IF NOT EXISTS " + PREMIUM_TABLE + " ("
+ "`UserID` INTEGER PRIMARY KEY AUTO_INCREMENT, "
+ "`UUID` CHAR(36), "
+ "`Name` VARCHAR(16) NOT NULL, "
+ "`Premium` BOOLEAN NOT NULL, "
+ "`LastIp` VARCHAR(255) NOT NULL, "
+ "`LastLogin` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "
+ "UNIQUE (`UUID`), "
//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");
}
statement.executeUpdate(createDataStmt);
} finally {
closeQuietly(con);
}
}
public PlayerProfile getProfile(String name, boolean fetch) {
if (profileCache.containsKey(name)) {
return profileCache.get(name);
} else if (fetch) {
Connection con = null;
try {
con = dataSource.getConnection();
PreparedStatement loadStatement = con.prepareStatement("SELECT * FROM " + PREMIUM_TABLE
+ " WHERE `Name`=? LIMIT 1");
loadStatement.setString(1, name);
ResultSet resultSet = loadStatement.executeQuery();
if (resultSet.next()) {
long userId = resultSet.getInt(1);
String unparsedUUID = resultSet.getString(2);
UUID uuid;
if (unparsedUUID == null) {
uuid = null;
} else {
uuid = FastLoginBungee.parseId(unparsedUUID);
}
// String name = resultSet.getString(3);
boolean premium = resultSet.getBoolean(4);
String lastIp = resultSet.getString(5);
long lastLogin = resultSet.getTimestamp(6).getTime();
PlayerProfile playerProfile = new PlayerProfile(userId, uuid, name, premium, lastIp, lastLogin);
profileCache.put(name, playerProfile);
return playerProfile;
} else {
PlayerProfile crackedProfile = new PlayerProfile(null, name, false, "");
profileCache.put(name, crackedProfile);
return crackedProfile;
}
} catch (SQLException sqlEx) {
plugin.getLogger().log(Level.SEVERE, "Failed to query profile", sqlEx);
} finally {
closeQuietly(con);
}
}
return null;
}
public boolean save(PlayerProfile playerProfile) {
Connection con = null;
try {
con = dataSource.getConnection();
UUID uuid = playerProfile.getUuid();
if (playerProfile.getUserId() == -1) {
PreparedStatement saveStatement = con.prepareStatement("INSERT INTO " + PREMIUM_TABLE
+ " (UUID, Name, Premium, LastIp) VALUES (?, ?, ?, ?)", Statement.RETURN_GENERATED_KEYS);
if (uuid == null) {
saveStatement.setString(1, null);
} else {
saveStatement.setString(1, uuid.toString().replace("-", ""));
}
saveStatement.setString(2, playerProfile.getPlayerName());
saveStatement.setBoolean(3, playerProfile.isPremium());
saveStatement.setString(4, playerProfile.getLastIp());
saveStatement.execute();
ResultSet generatedKeys = saveStatement.getGeneratedKeys();
if (generatedKeys != null && generatedKeys.next()) {
playerProfile.setUserId(generatedKeys.getInt(1));
}
} else {
PreparedStatement saveStatement = con.prepareStatement("UPDATE " + PREMIUM_TABLE
+ " SET UUID=?, Name=?, Premium=?, LastIp=?, LastLogin=CURRENT_TIMESTAMP WHERE UserID=?");
if (uuid == null) {
saveStatement.setString(1, null);
} else {
saveStatement.setString(1, uuid.toString().replace("-", ""));
}
saveStatement.setString(2, playerProfile.getPlayerName());
saveStatement.setBoolean(3, playerProfile.isPremium());
saveStatement.setString(4, playerProfile.getLastIp());
// saveStatement.setTimestamp(5, new Timestamp(playerProfile.getLastLogin()));
saveStatement.setLong(5, playerProfile.getUserId());
saveStatement.execute();
}
return true;
} catch (SQLException ex) {
plugin.getLogger().log(Level.SEVERE, "Failed to save playerProfile", ex);
} finally {
closeQuietly(con);
}
return false;
}
public void close() {
dataSource.close();
profileCache.clear();
}
private void closeQuietly(Connection con) {
if (con != null) {
try {
con.close();
} catch (SQLException sqlEx) {
plugin.getLogger().log(Level.SEVERE, "Failed to close connection", sqlEx);
}
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,92 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2022 games647 and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.bungee.hook;
import com.github.games647.fastlogin.bungee.FastLoginBungee;
import com.github.games647.fastlogin.core.hooks.AuthPlugin;
import de.xxschrandxx.bca.bungee.BungeeCordAuthenticatorBungee;
import de.xxschrandxx.bca.bungee.api.BungeeCordAuthenticatorBungeeAPI;
import java.sql.SQLException;
import java.util.logging.Level;
import net.md_5.bungee.api.connection.ProxiedPlayer;
/**
* GitHub:
* https://github.com/xXSchrandXx/SpigotPlugins/tree/master/BungeeCordAuthenticator
* <p>
* Project page:
* <p>
* Spigot: https://www.spigotmc.org/resources/bungeecordauthenticator.87669/
*/
public class BungeeCordAuthenticatorBungeeHook implements AuthPlugin<ProxiedPlayer> {
public final BungeeCordAuthenticatorBungeeAPI api;
public BungeeCordAuthenticatorBungeeHook(FastLoginBungee plugin) {
api = ((BungeeCordAuthenticatorBungee) plugin.getProxy().getPluginManager()
.getPlugin("BungeeCordAuthenticatorBungee")).getAPI();
plugin.getLog().info("BungeeCordAuthenticatorHook | Hooked successful!");
}
@Override
public boolean forceLogin(ProxiedPlayer player) {
if (api.isAuthenticated(player)) {
return true;
}
try {
api.setAuthenticated(player);
} catch (SQLException sqlEx) {
api.getLogger().log(Level.WARNING, "Failed to force login", sqlEx);
return false;
}
return true;
}
@Override
public boolean isRegistered(String playerName) {
try {
return api.getSQL().checkPlayerEntry(playerName);
} catch (SQLException sqlEx) {
api.getLogger().log(Level.WARNING, "Failed to check registration", sqlEx);
return false;
}
}
@Override
public boolean forceRegister(ProxiedPlayer player, String password) {
try {
return api.createPlayerEntry(player, password);
} catch (SQLException sqlEx) {
api.getLogger().log(Level.WARNING, "Failed to force register", sqlEx);
return false;
}
}
}

View File

@ -1,104 +0,0 @@
package com.github.games647.fastlogin.bungee.hooks;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;
import me.vik1395.BungeeAuth.ListenerClass;
import me.vik1395.BungeeAuth.Main;
import me.vik1395.BungeeAuth.Password.PasswordHandler;
import me.vik1395.BungeeAuth.Tables;
import net.md_5.bungee.api.connection.ProxiedPlayer;
/**
* Github: https://github.com/MatteCarra/BungeeAuth
*
* Project page:
*
* Spigot: https://www.spigotmc.org/resources/bungeeauth.493/
*/
public class BungeeAuthHook implements BungeeAuthPlugin {
//https://github.com/MatteCarra/BungeeAuth/blob/master/src/me/vik1395/BungeeAuth/Login.java#L32
private final Tables databaseConnection = new Tables();
@Override
public boolean forceLogin(final ProxiedPlayer player) {
//https://github.com/MatteCarra/BungeeAuth/blob/master/src/me/vik1395/BungeeAuth/Login.java#L92-95
Main.plonline.add(player.getName());
//renamed from ct to databaseConnection
// databaseConnection.setStatus(player.getName(), "online");
final Class<?>[] parameterTypes = new Class<?>[]{String.class, String.class};
final Object[] arguments = new Object[]{player.getName(), "online"};
try {
callProtected("setStatus", parameterTypes, arguments);
ListenerClass.movePlayer(player, false);
//proparly not thread-safe
ListenerClass.prelogin.get(player.getName()).cancel();
} catch (Exception ex) {
Main.plugin.getLogger().severe("[BungeeAuth] Error force loging in player");
return false;
}
return true;
}
@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);
}
@Override
public boolean forceRegister(final 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.
//renamed t to databaseConnection
// databaseConnection.newPlayerEntry(player.getName(), hash, pType, "", lastip, regdate, lastip, lastseen);
final Class<?>[] parameterTypes = new Class<?>[] {String.class, String.class, String.class, String.class
, String.class, String.class, String.class, String.class};
final 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().severe("[BungeeAuth] Error when creating a new player in the Database");
return false;
}
return true;
}
//pail ;(
private void callProtected(String methodName, Class<?>[] parameterTypes, Object[] arguments) throws Exception {
Class<? extends Tables> tableClass = databaseConnection.getClass();
Method method = tableClass.getDeclaredMethod(methodName, parameterTypes);
method.setAccessible(true);
method.invoke(databaseConnection, arguments);
}
}

View File

@ -1,55 +0,0 @@
package com.github.games647.fastlogin.bungee.hooks;
import net.md_5.bungee.api.connection.ProxiedPlayer;
/**
* Represents a supporting authentication plugin in BungeeCord/Waterfall/... servers
*/
public interface BungeeAuthPlugin {
/**
* Login the premium (paid account) player after
* the player joined successfully a server.
*
* @param player the player that needs to be logged in
* @return if the operation was successful
*/
boolean forceLogin(ProxiedPlayer player);
/**
* Checks whether an account exists for this player name.
*
* This check should check if a cracked player account exists
* so we can be sure the premium player doesn't steal the account
* of that player.
*
* This operation will be performed async while the player is
* connecting
*
* @param playerName player name
* @return if the player has an account
* @throws Exception if an error occurred
*/
boolean isRegistered(String playerName) throws Exception;
/**
* Forces a register in order to protect the paid account.
* The method will be invoked after the player joined a server.
*
* After a successful registration the player should be logged
* in too.
*
* The method will be called only for premium accounts.
* So it's recommended to set additionally premium property
* if possible.
*
* If we don't register an account, cracked players
* could steal the unregistered account from the paid
* player account
*
* @param player the premium account
* @param password a strong random generated password
* @return if the operation was successful
*/
boolean forceRegister(ProxiedPlayer player, String password);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,16 +1,21 @@
# 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}
author: games647, http://github.com/games647/FastLogin/graphs/contributors
version: ${project.version}-${git.commit.id.abbrev}
author: games647, https://github.com/games647/FastLogin/graphs/contributors
softdepends:
softDepends:
# BungeeCord auth plugins
- BungeeAuth
- BungeeCordAuthenticatorBungee
- SodionAuth
# Bedrock Player Bridge
- Geyser-BungeeCord
- floodgate
description: |
${project.description}
${project.description}

191
core/pom.xml Normal file
View File

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

View File

@ -0,0 +1,84 @@
/*
* SPDX-License-Identifier: MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2015-2022 games647 and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.github.games647.fastlogin.core;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicInteger;
import org.slf4j.Logger;
/**
* This limits the number of threads that are used at maximum. Thread creation can be very heavy for the CPU and
* context switching between threads too. However, we need many threads for blocking HTTP and database calls.
* Nevertheless, this number can be further limited, because the number of actually working database threads
* is limited by the size of our database pool. The goal is to separate concerns into processing and blocking only
* threads.
*/
public class AsyncScheduler {
private static final int MAX_CAPACITY = 1024;
//todo: single thread for delaying and scheduling tasks
private final Logger logger;
// 30 threads are still too many - the optimal solution is to separate into processing and blocking threads
// where processing threads could only be max number of cores while blocking threads could be minimized using
// non-blocking I/O and a single event executor
private final Executor processingPool;
private final AtomicInteger currentlyRunning = new AtomicInteger();
/*
private final ExecutorService databaseExecutor = new ThreadPoolExecutor(1, 10,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>(MAX_CAPACITY));
*/
public AsyncScheduler(Logger logger, Executor processingPool) {
this.logger = logger;
this.processingPool = processingPool;
}
public CompletableFuture<Void> runAsync(Runnable task) {
return CompletableFuture.runAsync(() -> {
currentlyRunning.incrementAndGet();
try {
task.run();
} finally {
currentlyRunning.getAndDecrement();
}
}, processingPool).exceptionally(error -> {
logger.warn("Error occurred on thread pool", error);
return null;
});
}
public void shutdown() {
// MoreExecutors.shutdownAndAwaitTermination(processingPool, 1, TimeUnit.MINUTES);
//MoreExecutors.shutdownAndAwaitTermination(databaseExecutor, 1, TimeUnit.MINUTES);
}
}

View File

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

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