games647 7e9da9fd7c Migrate to Java 8 (sponsored contribution)
Java >8 includes many helpful features and API additions. Current
phoronix benchmarks indicated a very similar performance. Furthermore,
the fact that Java 18 even works with 1.8.8 contributed to the
decision to move to Java 17 like vanilla Java in 1.19.

This also helps us to learn about the newer features added for our
personal interests. As this is a free project, this motivated us to make
this step.

Nevertheless, many server owners were frustrated about this decision.
Thanks to financial contribution, we revised this decision until Java 8
is end of life or no longer used actively used according to bstats.org

diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/EncryptionUtil.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/EncryptionUtil.java
index 68095f6..81abad1 100644
--- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/EncryptionUtil.java
+++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/EncryptionUtil.java
@@ -50,7 +50,7 @@ import java.time.Instant;
 import java.util.Arrays;
 import java.util.Base64;
 import java.util.Base64.Encoder;
-import java.util.random.RandomGenerator;
+import java.util.Random;

 import javax.crypto.BadPaddingException;
 import javax.crypto.Cipher;
@@ -59,6 +59,8 @@ import javax.crypto.NoSuchPaddingException;
 import javax.crypto.SecretKey;
 import javax.crypto.spec.SecretKeySpec;

+import lombok.var;
+
 /**
  * Encryption and decryption minecraft util for connection between servers
  * and paid Minecraft account clients.
@@ -112,7 +114,7 @@ final class EncryptionUtil {
      * @param random random generator
      * @return a token with 4 bytes long
      */
-    public static byte[] generateVerifyToken(RandomGenerator random) {
+    public static byte[] generateVerifyToken(Random random) {
         byte[] token = new byte[VERIFY_TOKEN_LENGTH];
         random.nextBytes(token);
         return token;
diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/ProtocolLibListener.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/ProtocolLibListener.java
index 2e21ccb..4ce14ff 100644
--- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/ProtocolLibListener.java
+++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/ProtocolLibListener.java
@@ -56,6 +56,7 @@ import javax.crypto.BadPaddingException;
 import javax.crypto.IllegalBlockSizeException;
 import javax.crypto.NoSuchPaddingException;

+import lombok.var;
 import org.bukkit.entity.Player;

 import static com.comphenix.protocol.PacketType.Login.Client.ENCRYPTION_BEGIN;
@@ -171,7 +172,7 @@ public class ProtocolLibListener extends PacketAdapter {
                 Either<byte[], ?> either = packet.getSpecificModifier(Either.class).read(0);
                 if (clientPublicKey == null) {
                     Optional<byte[]> left = either.left();
-                    if (left.isEmpty()) {
+                    if (!left.isPresent()) {
                         plugin.getLog().error("No verify token sent if requested without player signed key {}", sender);
                         return false;
                     }
@@ -179,7 +180,7 @@ public class ProtocolLibListener extends PacketAdapter {
                     return EncryptionUtil.verifyNonce(expectedToken, keyPair.getPrivate(), left.get());
                 } else {
                     Optional<?> optSignatureData = either.right();
-                    if (optSignatureData.isEmpty()) {
+                    if (!optSignatureData.isPresent()) {
                         plugin.getLog().error("No signature given to sent player signing key {}", sender);
                         return false;
                     }
@@ -219,7 +220,7 @@ public class ProtocolLibListener extends PacketAdapter {
             .optionRead(0);

         var clientKey = profileKey.flatMap(opt -> opt).flatMap(this::verifyPublicKey);
-        if (verifyClientKeys && clientKey.isEmpty()) {
+        if (verifyClientKeys && !clientKey.isPresent()) {
             // missing or incorrect
             // expired always not allowed
             player.kickPlayer(plugin.getCore().getMessage("invalid-public-key"));
diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/VerifyResponseTask.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/VerifyResponseTask.java
index 829d764..dc208ff 100644
--- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/VerifyResponseTask.java
+++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/VerifyResponseTask.java
@@ -60,6 +60,7 @@ import java.util.UUID;
 import javax.crypto.Cipher;
 import javax.crypto.SecretKey;

+import lombok.var;
 import org.bukkit.entity.Player;

 import static com.comphenix.protocol.PacketType.Login.Client.START;
diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/packet/ClientPublicKey.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/packet/ClientPublicKey.java
index e375e29..619d41d 100644
--- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/packet/ClientPublicKey.java
+++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/packet/ClientPublicKey.java
@@ -27,8 +27,62 @@ package com.github.games647.fastlogin.bukkit.listener.protocollib.packet;

 import java.security.PublicKey;
 import java.time.Instant;
+import java.util.Arrays;
+import java.util.Objects;
+import java.util.StringJoiner;

-public record ClientPublicKey(Instant expiry, PublicKey key, byte[] signature) {
+public final class ClientPublicKey {
+    private final Instant expiry;
+    private final PublicKey key;
+    private final byte[] signature;
+
+    public ClientPublicKey(Instant expiry, PublicKey key, byte[] signature) {
+        this.expiry = expiry;
+        this.key = key;
+        this.signature = signature;
+    }
+
+    public Instant expiry() {
+        return expiry;
+    }
+
+    public PublicKey key() {
+        return key;
+    }
+
+    public byte[] signature() {
+        return signature;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == this) {
+            return true;
+        }
+
+        if (obj == null || obj.getClass() != this.getClass()) {
+            return false;
+        }
+
+        ClientPublicKey that = (ClientPublicKey) obj;
+        return Objects.equals(this.expiry, that.expiry)
+                && Objects.equals(this.key, that.key)
+                && Arrays.equals(this.signature, that.signature);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(expiry, key, signature);
+    }
+
+    @Override
+    public String toString() {
+        return new StringJoiner(", ", ClientPublicKey.class.getSimpleName() + "[", "]")
+                .add("expiry=" + expiry)
+                .add("key=" + key)
+                .add("signature=" + Arrays.toString(signature))
+                .toString();
+    }

     public boolean isExpired(Instant verifyTimestamp) {
         return !verifyTimestamp.isBefore(expiry);
diff --git a/bukkit/src/test/java/com/github/games647/fastlogin/bukkit/FastLoginBukkitTest.java b/bukkit/src/test/java/com/github/games647/fastlogin/bukkit/FastLoginBukkitTest.java
index 07ff623..e4ce55a 100644
--- a/bukkit/src/test/java/com/github/games647/fastlogin/bukkit/FastLoginBukkitTest.java
+++ b/bukkit/src/test/java/com/github/games647/fastlogin/bukkit/FastLoginBukkitTest.java
@@ -30,6 +30,7 @@ import com.github.games647.fastlogin.core.CommonUtil;
 import net.md_5.bungee.api.chat.TextComponent;
 import net.md_5.bungee.chat.ComponentSerializer;

+import lombok.var;
 import org.junit.jupiter.api.Test;

 import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -43,8 +44,7 @@ class FastLoginBukkitTest {
         assertEquals(msg, "§x00002a00002b§lText");

         var components = TextComponent.fromLegacyText(msg);
-        var expected = """
-            {"bold":true,"color":"#00a00b","text":"Text"}""";
+        var expected = "{\"bold\":true,\"color\":\"#00a00b\",\"text\":\"Text\"}";
         assertEquals(ComponentSerializer.toString(components), expected);
     }
 }
diff --git a/bukkit/src/test/java/com/github/games647/fastlogin/bukkit/listener/protocollib/Base64Adapter.java b/bukkit/src/test/java/com/github/games647/fastlogin/bukkit/listener/protocollib/Base64Adapter.java
index c2a2d1a..29b2f3d 100644
--- a/bukkit/src/test/java/com/github/games647/fastlogin/bukkit/listener/protocollib/Base64Adapter.java
+++ b/bukkit/src/test/java/com/github/games647/fastlogin/bukkit/listener/protocollib/Base64Adapter.java
@@ -32,6 +32,8 @@ import com.google.gson.stream.JsonWriter;
 import java.io.IOException;
 import java.util.Base64;

+import lombok.var;
+
 public class Base64Adapter extends TypeAdapter<byte[]> {

     @Override
diff --git a/bukkit/src/test/java/com/github/games647/fastlogin/bukkit/listener/protocollib/EncryptionUtilTest.java b/bukkit/src/test/java/com/github/games647/fastlogin/bukkit/listener/protocollib/EncryptionUtilTest.java
index e1b8bea..df3888e 100644
--- a/bukkit/src/test/java/com/github/games647/fastlogin/bukkit/listener/protocollib/EncryptionUtilTest.java
+++ b/bukkit/src/test/java/com/github/games647/fastlogin/bukkit/listener/protocollib/EncryptionUtilTest.java
@@ -50,6 +50,7 @@ import javax.crypto.NoSuchPaddingException;
 import javax.crypto.SecretKey;
 import javax.crypto.spec.SecretKeySpec;

+import lombok.var;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.ValueSource;
diff --git a/bukkit/src/test/java/com/github/games647/fastlogin/bukkit/listener/protocollib/ResourceLoader.java b/bukkit/src/test/java/com/github/games647/fastlogin/bukkit/listener/protocollib/ResourceLoader.java
index 4361fe9..ac0991e 100644
--- a/bukkit/src/test/java/com/github/games647/fastlogin/bukkit/listener/protocollib/ResourceLoader.java
+++ b/bukkit/src/test/java/com/github/games647/fastlogin/bukkit/listener/protocollib/ResourceLoader.java
@@ -33,6 +33,7 @@ import com.google.gson.JsonObject;
 import java.io.IOException;
 import java.io.Reader;
 import java.io.StringReader;
+import java.net.URL;
 import java.nio.charset.StandardCharsets;
 import java.security.KeyFactory;
 import java.security.NoSuchAlgorithmException;
@@ -57,19 +58,19 @@ public class ResourceLoader {
         ) {
             PemObject pemObject = pemReader.readPemObject();
             byte[] content = pemObject.getContent();
-            var privateKeySpec = new PKCS8EncodedKeySpec(content);
+            PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(content);

-            var factory = KeyFactory.getInstance("RSA");
+            KeyFactory factory = KeyFactory.getInstance("RSA");
             return (RSAPrivateKey) factory.generatePrivate(privateKeySpec);
         }
     }

     protected static ClientPublicKey loadClientKey(String path)
         throws NoSuchAlgorithmException, IOException, InvalidKeySpecException {
-        var keyUrl = Resources.getResource(path);
+        URL keyUrl = Resources.getResource(path);

-        var lines = Resources.toString(keyUrl, StandardCharsets.US_ASCII);
-        var object = new Gson().fromJson(lines, JsonObject.class);
+        String lines = Resources.toString(keyUrl, StandardCharsets.US_ASCII);
+        JsonObject object = new Gson().fromJson(lines, JsonObject.class);

         Instant expires = Instant.parse(object.getAsJsonPrimitive("expires_at").getAsString());
         String key = object.getAsJsonPrimitive("key").getAsString();
@@ -87,9 +88,9 @@ public class ResourceLoader {
         ) {
             PemObject pemObject = pemReader.readPemObject();
             byte[] content = pemObject.getContent();
-            var pubKeySpec = new X509EncodedKeySpec(content);
+            X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(content);

-            var factory = KeyFactory.getInstance("RSA");
+            KeyFactory factory = KeyFactory.getInstance("RSA");
             return (RSAPublicKey) factory.generatePublic(pubKeySpec);
         }
     }
diff --git a/bukkit/src/test/java/com/github/games647/fastlogin/bukkit/listener/protocollib/SignatureTestData.java b/bukkit/src/test/java/com/github/games647/fastlogin/bukkit/listener/protocollib/SignatureTestData.java
index ee62e24..9a75fc5 100644
--- a/bukkit/src/test/java/com/github/games647/fastlogin/bukkit/listener/protocollib/SignatureTestData.java
+++ b/bukkit/src/test/java/com/github/games647/fastlogin/bukkit/listener/protocollib/SignatureTestData.java
@@ -32,6 +32,8 @@ import com.google.gson.annotations.JsonAdapter;
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;

+import lombok.var;
+
 public class SignatureTestData {

     public static SignatureTestData fromResource(String resourceName) throws IOException {
diff --git a/core/src/main/java/com/github/games647/fastlogin/core/CommonUtil.java b/core/src/main/java/com/github/games647/fastlogin/core/CommonUtil.java
index 324e240..89dc7ee 100644
--- a/core/src/main/java/com/github/games647/fastlogin/core/CommonUtil.java
+++ b/core/src/main/java/com/github/games647/fastlogin/core/CommonUtil.java
@@ -25,8 +25,7 @@
  */
 package com.github.games647.fastlogin.core;

-import com.github.games647.craftapi.cache.SafeCacheBuilder;
-import com.google.common.cache.CacheLoader;
+import com.google.common.cache.CacheBuilder;

 import java.lang.reflect.Constructor;
 import java.util.concurrent.ConcurrentMap;
@@ -43,7 +42,7 @@ public final class CommonUtil {
     private static final char TRANSLATED_CHAR = '§';

     public static <K, V> ConcurrentMap<K, V> buildCache(int expireAfterWrite, int maxSize) {
-        SafeCacheBuilder<Object, Object> builder = SafeCacheBuilder.newBuilder();
+        CacheBuilder<Object, Object> builder = CacheBuilder.newBuilder();

         if (expireAfterWrite > 0) {
             builder.expireAfterWrite(expireAfterWrite, TimeUnit.MINUTES);
@@ -53,9 +52,7 @@ public final class CommonUtil {
             builder.maximumSize(maxSize);
         }

-        return builder.build(CacheLoader.from(() -> {
-            throw new UnsupportedOperationException();
-        }));
+        return (ConcurrentMap<K, V>) builder.build().asMap();
     }

     public static String translateColorCodes(String rawMessage) {
diff --git a/core/src/main/java/com/github/games647/fastlogin/core/StoredProfile.java b/core/src/main/java/com/github/games647/fastlogin/core/StoredProfile.java
index ee58bde..26492d3 100644
--- a/core/src/main/java/com/github/games647/fastlogin/core/StoredProfile.java
+++ b/core/src/main/java/com/github/games647/fastlogin/core/StoredProfile.java
@@ -118,10 +118,11 @@ public class StoredProfile extends Profile {
             return true;
         }

-        if (!(o instanceof StoredProfile that)) {
+        if (!(o instanceof StoredProfile)) {
             return false;
         }

+        StoredProfile that = (StoredProfile) o;
         if (!super.equals(o)) {
             return false;
         }
diff --git a/pom.xml b/pom.xml
index f2f3950..e1b26b0 100644
--- a/pom.xml
+++ b/pom.xml
@@ -48,7 +48,7 @@
         <!-- Set default for non-git clones -->
         <git.commit.id>Unknown</git.commit.id>

-        <java.version>17</java.version>
+        <java.version>8</java.version>
         <maven.compiler.source>${java.version}</maven.compiler.source>
         <maven.compiler.target>${java.version}</maven.compiler.target>

@@ -189,5 +189,12 @@
             <version>5.8.2</version>
             <scope>test</scope>
         </dependency>
+
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <version>1.18.24</version>
+            <scope>provided</scope>
+        </dependency>
     </dependencies>
 </project>
2022-07-22 13:26:38 +02:00
2022-07-17 16:54:06 +02:00
2022-07-17 16:54:06 +02:00
2022-07-17 16:54:06 +02:00
2021-05-09 09:09:52 +02:00
2022-06-28 18:34:23 +02:00
2022-03-13 10:06:17 +01:00
2022-07-17 16:54:06 +02:00

FastLogin

Checks if a Minecraft player has a paid account (premium). If so, they can skip offline authentication (auth plugins). So they don't need to enter passwords. This is also called auto login (auto-login).

Features

  • Detect paid accounts from others
  • Automatically login paid accounts (premium)
  • Support various of auth plugins
  • Cauldron support
  • Forge/Sponge message support
  • Premium UUID support
  • Forward skins
  • Detect username changed and will update the existing database record
  • BungeeCord support
  • Auto register new premium players
  • Plugin: ProtocolSupport is supported and can be used as an alternative to ProtocolLib
  • No client modifications needed
  • Good performance by using async operations
  • Locale messages
  • Support for Bedrock players proxies through FloodGate

Issues

Please use issues for bug reports, suggestions, questions and more. Please check for existing issues. Existing issues can be voted up by adding up vote to the original post. Closing issues means that they are marked as resolved. Comments are still allowed and it could be re-opened.

Development builds

Development builds contain the latest changes from the Source-Code. They are bleeding edge and could introduce new bugs, but also include features, enhancements and bug fixes that are not yet in a released version. If you click on the left side on Changes, you can see iterative change sets leading to a specific build.

You can download them from here: https://ci.codemc.org/job/Games647/job/FastLogin/


Commands

/premium [player] Label the invoker or the argument as paid account
/cracked [player] Label the invoker or the argument as cracked account

Permissions

fastlogin.bukkit.command.premium
fastlogin.bukkit.command.cracked
fastlogin.command.premium.other
fastlogin.command.cracked.other

Placeholder

This plugin supports PlaceholderAPI on Spigot. It exports the following variable %fastlogin_status%. In BungeeCord environments, the status of a player will be delivered with a delay after the player already successful joined the server. This takes about a couple of milliseconds. In this case the value will be Unknown.

Possible values: Premium, Cracked, Unknown

Requirements

  • Java 17+
  • Server software in offlinemode:
  • An auth plugin.

Supported auth plugins

Spigot/Paper

BungeeCord/Waterfall

Network requests

This plugin performs network requests to:


How to install

Spigot/Paper

  1. Download and install ProtocolLib/ProtocolSupport
  2. Download and install FastLogin (or FastLoginBukkit for newer versions)
  3. Set your server in offline mode by setting the value onlinemode in your server.properties to false

BungeeCord/Waterfall or Velocity

Install the plugin on both platforms, that is proxy (BungeeCord or Velocity) and backend server (Spigot).

  1. Activate proxy support in the server configuration
    • This is often found in spigot.yml or paper.yml
  2. Restart the backend server
  3. Now there is allowed-proxies.txt file in the FastLogin folder of the restarted server
    • BungeeCord: Put your stats-id from the BungeeCord config into this file
    • Velocity: On plugin startup the plugin generates a proxyId.txt inside the plugins folder of the proxy
  4. Activate ip forwarding in your proxy config
  5. Check your database settings in the config of FastLogin on your proxy
    • The proxies only ship with a limited set of drivers where Spigot supports more. Therefore, these are supported:
    • BungeeCord: 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 configure the firewall for your Spigot server so that it's only accessible through your proxy
Description
Checks if a Minecraft player has a valid paid account. If so, they can skip offline authentication automatically. (premium auto login)
Readme MIT 11 MiB
Languages
Java 100%