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
This commit is contained in:
games647
2022-07-22 13:26:52 +02:00
13 changed files with 92 additions and 66 deletions

View File

@ -50,7 +50,7 @@ import java.time.Instant;
import java.util.Arrays; import java.util.Arrays;
import java.util.Base64; import java.util.Base64;
import java.util.Base64.Encoder; import java.util.Base64.Encoder;
import java.util.random.RandomGenerator; import java.util.Random;
import javax.crypto.BadPaddingException; import javax.crypto.BadPaddingException;
import javax.crypto.Cipher; import javax.crypto.Cipher;
@ -59,6 +59,8 @@ import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey; import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec; import javax.crypto.spec.SecretKeySpec;
import lombok.val;
/** /**
* Encryption and decryption minecraft util for connection between servers * Encryption and decryption minecraft util for connection between servers
* and paid Minecraft account clients. * and paid Minecraft account clients.
@ -85,7 +87,7 @@ final class EncryptionUtil {
} }
private EncryptionUtil() { private EncryptionUtil() {
// utility throw new RuntimeException("No instantiation of utility classes allowed");
} }
/** /**
@ -112,7 +114,7 @@ final class EncryptionUtil {
* @param random random generator * @param random random generator
* @return a token with 4 bytes long * @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]; byte[] token = new byte[VERIFY_TOKEN_LENGTH];
random.nextBytes(token); random.nextBytes(token);
return token; return token;
@ -177,9 +179,9 @@ final class EncryptionUtil {
private static PublicKey loadMojangSessionKey() private static PublicKey loadMojangSessionKey()
throws IOException, NoSuchAlgorithmException, InvalidKeySpecException { throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
var keyUrl = FastLoginBukkit.class.getClassLoader().getResource("yggdrasil_session_pubkey.der"); val keyUrl = FastLoginBukkit.class.getClassLoader().getResource("yggdrasil_session_pubkey.der");
var keyData = Resources.toByteArray(keyUrl); val keyData = Resources.toByteArray(keyUrl);
var keySpec = new X509EncodedKeySpec(keyData); val keySpec = new X509EncodedKeySpec(keyData);
return KeyFactory.getInstance("RSA").generatePublic(keySpec); return KeyFactory.getInstance("RSA").generatePublic(keySpec);
} }

View File

@ -56,6 +56,7 @@ import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException; import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException; import javax.crypto.NoSuchPaddingException;
import lombok.var;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import static com.comphenix.protocol.PacketType.Login.Client.ENCRYPTION_BEGIN; 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); Either<byte[], ?> either = packet.getSpecificModifier(Either.class).read(0);
if (clientPublicKey == null) { if (clientPublicKey == null) {
Optional<byte[]> left = either.left(); 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); plugin.getLog().error("No verify token sent if requested without player signed key {}", sender);
return false; return false;
} }
@ -179,7 +180,7 @@ public class ProtocolLibListener extends PacketAdapter {
return EncryptionUtil.verifyNonce(expectedToken, keyPair.getPrivate(), left.get()); return EncryptionUtil.verifyNonce(expectedToken, keyPair.getPrivate(), left.get());
} else { } else {
Optional<?> optSignatureData = either.right(); Optional<?> optSignatureData = either.right();
if (optSignatureData.isEmpty()) { if (!optSignatureData.isPresent()) {
plugin.getLog().error("No signature given to sent player signing key {}", sender); plugin.getLog().error("No signature given to sent player signing key {}", sender);
return false; return false;
} }
@ -219,7 +220,7 @@ public class ProtocolLibListener extends PacketAdapter {
.optionRead(0); .optionRead(0);
var clientKey = profileKey.flatMap(opt -> opt).flatMap(this::verifyPublicKey); var clientKey = profileKey.flatMap(opt -> opt).flatMap(this::verifyPublicKey);
if (verifyClientKeys && clientKey.isEmpty()) { if (verifyClientKeys && !clientKey.isPresent()) {
// missing or incorrect // missing or incorrect
// expired always not allowed // expired always not allowed
player.kickPlayer(plugin.getCore().getMessage("invalid-public-key")); player.kickPlayer(plugin.getCore().getMessage("invalid-public-key"));
@ -240,7 +241,7 @@ public class ProtocolLibListener extends PacketAdapter {
Instant expires = profileKey.getExpireTime(); Instant expires = profileKey.getExpireTime();
PublicKey key = profileKey.getKey(); PublicKey key = profileKey.getKey();
byte[] signature = profileKey.getSignature(); byte[] signature = profileKey.getSignature();
ClientPublicKey clientKey = new ClientPublicKey(expires, key, signature); ClientPublicKey clientKey = ClientPublicKey.of(expires, key, signature);
try { try {
if (EncryptionUtil.verifyClientKey(clientKey, Instant.now())) { if (EncryptionUtil.verifyClientKey(clientKey, Instant.now())) {
return Optional.of(clientKey); return Optional.of(clientKey);

View File

@ -60,6 +60,7 @@ import java.util.UUID;
import javax.crypto.Cipher; import javax.crypto.Cipher;
import javax.crypto.SecretKey; import javax.crypto.SecretKey;
import lombok.val;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import static com.comphenix.protocol.PacketType.Login.Client.START; import static com.comphenix.protocol.PacketType.Login.Client.START;
@ -268,7 +269,7 @@ public class VerifyResponseTask implements Runnable {
startPacket.getStrings().write(0, username); startPacket.getStrings().write(0, username);
EquivalentConverter<WrappedProfileKeyData> converter = BukkitConverters.getWrappedPublicKeyDataConverter(); EquivalentConverter<WrappedProfileKeyData> converter = BukkitConverters.getWrappedPublicKeyDataConverter();
var wrappedKey = Optional.ofNullable(clientKey).map(key -> val wrappedKey = Optional.ofNullable(clientKey).map(key ->
new WrappedProfileKeyData(clientKey.expiry(), clientKey.key(), clientKey.signature()) new WrappedProfileKeyData(clientKey.expiry(), clientKey.key(), clientKey.signature())
); );

View File

@ -28,7 +28,15 @@ package com.github.games647.fastlogin.bukkit.listener.protocollib.packet;
import java.security.PublicKey; import java.security.PublicKey;
import java.time.Instant; import java.time.Instant;
public record ClientPublicKey(Instant expiry, PublicKey key, byte[] signature) { import lombok.Value;
import lombok.experimental.Accessors;
@Accessors(fluent = true)
@Value(staticConstructor = "of")
public class ClientPublicKey {
Instant expiry;
PublicKey key;
byte[] signature;
public boolean isExpired(Instant verifyTimestamp) { public boolean isExpired(Instant verifyTimestamp) {
return !verifyTimestamp.isBefore(expiry); return !verifyTimestamp.isBefore(expiry);

View File

@ -30,6 +30,7 @@ import com.github.games647.fastlogin.core.CommonUtil;
import net.md_5.bungee.api.chat.TextComponent; import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.chat.ComponentSerializer; import net.md_5.bungee.chat.ComponentSerializer;
import lombok.val;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
@ -38,13 +39,12 @@ class FastLoginBukkitTest {
@Test @Test
void testRGB() { void testRGB() {
var message = "&x00002a00002b&lText"; val message = "&x00002a00002b&lText";
var msg = CommonUtil.translateColorCodes(message); val msg = CommonUtil.translateColorCodes(message);
assertEquals(msg, "§x00002a00002b§lText"); assertEquals(msg, "§x00002a00002b§lText");
var components = TextComponent.fromLegacyText(msg); val components = TextComponent.fromLegacyText(msg);
var expected = """ val expected = "{\"bold\":true,\"color\":\"#00a00b\",\"text\":\"Text\"}";
{"bold":true,"color":"#00a00b","text":"Text"}""";
assertEquals(ComponentSerializer.toString(components), expected); assertEquals(ComponentSerializer.toString(components), expected);
} }
} }

View File

@ -32,11 +32,13 @@ import com.google.gson.stream.JsonWriter;
import java.io.IOException; import java.io.IOException;
import java.util.Base64; import java.util.Base64;
import lombok.val;
public class Base64Adapter extends TypeAdapter<byte[]> { public class Base64Adapter extends TypeAdapter<byte[]> {
@Override @Override
public void write(JsonWriter out, byte[] value) throws IOException { public void write(JsonWriter out, byte[] value) throws IOException {
var encoded = Base64.getEncoder().encodeToString(value); val encoded = Base64.getEncoder().encodeToString(value);
out.value(encoded); out.value(encoded);
} }

View File

@ -50,6 +50,7 @@ import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey; import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec; import javax.crypto.spec.SecretKeySpec;
import lombok.val;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource; import org.junit.jupiter.params.provider.ValueSource;
@ -60,7 +61,7 @@ class EncryptionUtilTest {
@Test @Test
void testVerifyToken() { void testVerifyToken() {
var random = ThreadLocalRandom.current(); val random = ThreadLocalRandom.current();
byte[] token = EncryptionUtil.generateVerifyToken(random); byte[] token = EncryptionUtil.generateVerifyToken(random);
assertAll( assertAll(
@ -88,10 +89,10 @@ class EncryptionUtilTest {
@Test @Test
void testExpiredClientKey() throws Exception { void testExpiredClientKey() throws Exception {
var clientKey = ResourceLoader.loadClientKey("client_keys/valid_public_key.json"); val clientKey = ResourceLoader.loadClientKey("client_keys/valid_public_key.json");
// Client expires at the exact second mentioned, so use it for verification // Client expires at the exact second mentioned, so use it for verification
var expiredTimestamp = clientKey.expiry(); val expiredTimestamp = clientKey.expiry();
assertFalse(EncryptionUtil.verifyClientKey(clientKey, expiredTimestamp)); assertFalse(EncryptionUtil.verifyClientKey(clientKey, expiredTimestamp));
} }
@ -105,7 +106,7 @@ class EncryptionUtilTest {
"client_keys/invalid_wrong_signature.json" "client_keys/invalid_wrong_signature.json"
}) })
void testInvalidClientKey(String clientKeySource) throws Exception { void testInvalidClientKey(String clientKeySource) throws Exception {
var clientKey = ResourceLoader.loadClientKey(clientKeySource); val clientKey = ResourceLoader.loadClientKey(clientKeySource);
Instant expireTimestamp = clientKey.expiry().minus(5, ChronoUnit.HOURS); Instant expireTimestamp = clientKey.expiry().minus(5, ChronoUnit.HOURS);
assertFalse(EncryptionUtil.verifyClientKey(clientKey, expireTimestamp)); assertFalse(EncryptionUtil.verifyClientKey(clientKey, expireTimestamp));
@ -113,8 +114,8 @@ class EncryptionUtilTest {
@Test @Test
void testValidClientKey() throws Exception { void testValidClientKey() throws Exception {
var clientKey = ResourceLoader.loadClientKey("client_keys/valid_public_key.json"); val clientKey = ResourceLoader.loadClientKey("client_keys/valid_public_key.json");
var verificationTimestamp = clientKey.expiry().minus(5, ChronoUnit.HOURS); val verificationTimestamp = clientKey.expiry().minus(5, ChronoUnit.HOURS);
assertTrue(EncryptionUtil.verifyClientKey(clientKey, verificationTimestamp)); assertTrue(EncryptionUtil.verifyClientKey(clientKey, verificationTimestamp));
} }
@ -122,7 +123,7 @@ class EncryptionUtilTest {
@Test @Test
void testDecryptSharedSecret() throws Exception { void testDecryptSharedSecret() throws Exception {
KeyPair serverPair = EncryptionUtil.generateKeyPair(); KeyPair serverPair = EncryptionUtil.generateKeyPair();
var serverPK = serverPair.getPublic(); val serverPK = serverPair.getPublic();
SecretKey secretKey = generateSharedKey(); SecretKey secretKey = generateSharedKey();
byte[] encryptedSecret = encrypt(serverPK, secretKey.getEncoded()); byte[] encryptedSecret = encrypt(serverPK, secretKey.getEncoded());
@ -134,7 +135,7 @@ class EncryptionUtilTest {
private static byte[] encrypt(PublicKey receiverKey, byte... message) private static byte[] encrypt(PublicKey receiverKey, byte... message)
throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException,
IllegalBlockSizeException, BadPaddingException { IllegalBlockSizeException, BadPaddingException {
var encryptCipher = Cipher.getInstance(receiverKey.getAlgorithm()); val encryptCipher = Cipher.getInstance(receiverKey.getAlgorithm());
encryptCipher.init(Cipher.ENCRYPT_MODE, receiverKey); encryptCipher.init(Cipher.ENCRYPT_MODE, receiverKey);
return encryptCipher.doFinal(message); return encryptCipher.doFinal(message);
} }
@ -150,9 +151,9 @@ class EncryptionUtilTest {
@Test @Test
void testServerIdHash() throws Exception { void testServerIdHash() throws Exception {
var serverId = ""; val serverId = "";
var sharedSecret = generateSharedKey(); val sharedSecret = generateSharedKey();
var serverPK = ResourceLoader.loadClientKey("client_keys/valid_public_key.json").key(); val serverPK = ResourceLoader.loadClientKey("client_keys/valid_public_key.json").key();
String sessionHash = getServerHash(serverId, sharedSecret, serverPK); String sessionHash = getServerHash(serverId, sharedSecret, serverPK);
assertEquals(EncryptionUtil.getServerIdHashString(serverId, sharedSecret, serverPK), sessionHash); assertEquals(EncryptionUtil.getServerIdHashString(serverId, sharedSecret, serverPK), sessionHash);
@ -166,7 +167,7 @@ class EncryptionUtilTest {
// sha1.update(server's encoded public key from Encryption Request) // sha1.update(server's encoded public key from Encryption Request)
// hash := sha1.hexdigest() # String of hex characters // hash := sha1.hexdigest() # String of hex characters
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
var hasher = Hashing.sha1().newHasher(); val hasher = Hashing.sha1().newHasher();
hasher.putString(serverId, StandardCharsets.US_ASCII); hasher.putString(serverId, StandardCharsets.US_ASCII);
hasher.putBytes(sharedSecret.getEncoded()); hasher.putBytes(sharedSecret.getEncoded());
hasher.putBytes(serverPK.getEncoded()); hasher.putBytes(serverPK.getEncoded());
@ -179,9 +180,9 @@ class EncryptionUtilTest {
@Test @Test
void testServerIdHashWrongSecret() throws Exception { void testServerIdHashWrongSecret() throws Exception {
var serverId = ""; val serverId = "";
var sharedSecret = generateSharedKey(); val sharedSecret = generateSharedKey();
var serverPK = ResourceLoader.loadClientKey("client_keys/valid_public_key.json").key(); val serverPK = ResourceLoader.loadClientKey("client_keys/valid_public_key.json").key();
String sessionHash = getServerHash(serverId, sharedSecret, serverPK); String sessionHash = getServerHash(serverId, sharedSecret, serverPK);
assertNotEquals(EncryptionUtil.getServerIdHashString("", generateSharedKey(), serverPK), sessionHash); assertNotEquals(EncryptionUtil.getServerIdHashString("", generateSharedKey(), serverPK), sessionHash);
@ -189,12 +190,12 @@ class EncryptionUtilTest {
@Test @Test
void testServerIdHashWrongServerKey() { void testServerIdHashWrongServerKey() {
var serverId = ""; val serverId = "";
var sharedSecret = generateSharedKey(); val sharedSecret = generateSharedKey();
var serverPK = EncryptionUtil.generateKeyPair().getPublic(); val serverPK = EncryptionUtil.generateKeyPair().getPublic();
String sessionHash = getServerHash(serverId, sharedSecret, serverPK); String sessionHash = getServerHash(serverId, sharedSecret, serverPK);
var wrongPK = EncryptionUtil.generateKeyPair().getPublic(); val wrongPK = EncryptionUtil.generateKeyPair().getPublic();
assertNotEquals(EncryptionUtil.getServerIdHashString("", sharedSecret, wrongPK), sessionHash); assertNotEquals(EncryptionUtil.getServerIdHashString("", sharedSecret, wrongPK), sessionHash);
} }
@ -238,8 +239,8 @@ class EncryptionUtilTest {
@Test @Test
void testNonce() throws Exception { void testNonce() throws Exception {
byte[] expected = {1, 2, 3, 4}; byte[] expected = {1, 2, 3, 4};
var serverKey = EncryptionUtil.generateKeyPair(); val serverKey = EncryptionUtil.generateKeyPair();
var encryptedNonce = encrypt(serverKey.getPublic(), expected); val encryptedNonce = encrypt(serverKey.getPublic(), expected);
assertTrue(EncryptionUtil.verifyNonce(expected, serverKey.getPrivate(), encryptedNonce)); assertTrue(EncryptionUtil.verifyNonce(expected, serverKey.getPrivate(), encryptedNonce));
} }
@ -247,19 +248,19 @@ class EncryptionUtilTest {
@Test @Test
void testNonceIncorrect() throws Exception { void testNonceIncorrect() throws Exception {
byte[] expected = {1, 2, 3, 4}; byte[] expected = {1, 2, 3, 4};
var serverKey = EncryptionUtil.generateKeyPair(); val serverKey = EncryptionUtil.generateKeyPair();
// flipped first character // flipped first character
var encryptedNonce = encrypt(serverKey.getPublic(), new byte[]{0, 2, 3, 4}); val encryptedNonce = encrypt(serverKey.getPublic(), new byte[]{0, 2, 3, 4});
assertFalse(EncryptionUtil.verifyNonce(expected, serverKey.getPrivate(), encryptedNonce)); assertFalse(EncryptionUtil.verifyNonce(expected, serverKey.getPrivate(), encryptedNonce));
} }
@Test @Test
void testNonceFailedDecryption() throws Exception { void testNonceFailedDecryption() throws Exception {
byte[] expected = {1, 2, 3, 4}; byte[] expected = {1, 2, 3, 4};
var serverKey = EncryptionUtil.generateKeyPair(); val serverKey = EncryptionUtil.generateKeyPair();
// generate a new keypair that is different // generate a new keypair that is different
var encryptedNonce = encrypt(EncryptionUtil.generateKeyPair().getPublic(), expected); val encryptedNonce = encrypt(EncryptionUtil.generateKeyPair().getPublic(), expected);
assertThrows(GeneralSecurityException.class, assertThrows(GeneralSecurityException.class,
() -> EncryptionUtil.verifyNonce(expected, serverKey.getPrivate(), encryptedNonce) () -> EncryptionUtil.verifyNonce(expected, serverKey.getPrivate(), encryptedNonce)
@ -269,7 +270,7 @@ class EncryptionUtilTest {
@Test @Test
void testNonceIncorrectEmpty() { void testNonceIncorrectEmpty() {
byte[] expected = {1, 2, 3, 4}; byte[] expected = {1, 2, 3, 4};
var serverKey = EncryptionUtil.generateKeyPair(); val serverKey = EncryptionUtil.generateKeyPair();
byte[] encryptedNonce = {}; byte[] encryptedNonce = {};
assertThrows(GeneralSecurityException.class, assertThrows(GeneralSecurityException.class,

View File

@ -33,6 +33,7 @@ import com.google.gson.JsonObject;
import java.io.IOException; import java.io.IOException;
import java.io.Reader; import java.io.Reader;
import java.io.StringReader; import java.io.StringReader;
import java.net.URL;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.security.KeyFactory; import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
@ -57,26 +58,26 @@ public class ResourceLoader {
) { ) {
PemObject pemObject = pemReader.readPemObject(); PemObject pemObject = pemReader.readPemObject();
byte[] content = pemObject.getContent(); 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); return (RSAPrivateKey) factory.generatePrivate(privateKeySpec);
} }
} }
protected static ClientPublicKey loadClientKey(String path) protected static ClientPublicKey loadClientKey(String path)
throws NoSuchAlgorithmException, IOException, InvalidKeySpecException { throws NoSuchAlgorithmException, IOException, InvalidKeySpecException {
var keyUrl = Resources.getResource(path); URL keyUrl = Resources.getResource(path);
var lines = Resources.toString(keyUrl, StandardCharsets.US_ASCII); String lines = Resources.toString(keyUrl, StandardCharsets.US_ASCII);
var object = new Gson().fromJson(lines, JsonObject.class); JsonObject object = new Gson().fromJson(lines, JsonObject.class);
Instant expires = Instant.parse(object.getAsJsonPrimitive("expires_at").getAsString()); Instant expires = Instant.parse(object.getAsJsonPrimitive("expires_at").getAsString());
String key = object.getAsJsonPrimitive("key").getAsString(); String key = object.getAsJsonPrimitive("key").getAsString();
RSAPublicKey publicKey = parsePublicKey(key); RSAPublicKey publicKey = parsePublicKey(key);
byte[] signature = Base64.getDecoder().decode(object.getAsJsonPrimitive("signature").getAsString()); byte[] signature = Base64.getDecoder().decode(object.getAsJsonPrimitive("signature").getAsString());
return new ClientPublicKey(expires, publicKey, signature); return ClientPublicKey.of(expires, publicKey, signature);
} }
private static RSAPublicKey parsePublicKey(String keySpec) private static RSAPublicKey parsePublicKey(String keySpec)
@ -87,9 +88,9 @@ public class ResourceLoader {
) { ) {
PemObject pemObject = pemReader.readPemObject(); PemObject pemObject = pemReader.readPemObject();
byte[] content = pemObject.getContent(); 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); return (RSAPublicKey) factory.generatePublic(pubKeySpec);
} }
} }

View File

@ -32,11 +32,13 @@ import com.google.gson.annotations.JsonAdapter;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import lombok.val;
public class SignatureTestData { public class SignatureTestData {
public static SignatureTestData fromResource(String resourceName) throws IOException { public static SignatureTestData fromResource(String resourceName) throws IOException {
var keyUrl = Resources.getResource(resourceName); val keyUrl = Resources.getResource(resourceName);
var encodedSignature = Resources.toString(keyUrl, StandardCharsets.US_ASCII); val encodedSignature = Resources.toString(keyUrl, StandardCharsets.US_ASCII);
return new Gson().fromJson(encodedSignature, SignatureTestData.class); return new Gson().fromJson(encodedSignature, SignatureTestData.class);
} }

View File

@ -57,6 +57,7 @@ import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.connection.PendingConnection; import net.md_5.bungee.api.connection.PendingConnection;
import net.md_5.bungee.api.connection.ProxiedPlayer; import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.connection.Server; import net.md_5.bungee.api.connection.Server;
import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.api.plugin.Plugin; import net.md_5.bungee.api.plugin.Plugin;
import net.md_5.bungee.api.plugin.PluginManager; import net.md_5.bungee.api.plugin.PluginManager;
import net.md_5.bungee.api.scheduler.GroupedThreadFactory; import net.md_5.bungee.api.scheduler.GroupedThreadFactory;
@ -100,7 +101,7 @@ public class FastLoginBungee extends Plugin implements PlatformPlugin<CommandSen
//events //events
PluginManager pluginManager = getProxy().getPluginManager(); PluginManager pluginManager = getProxy().getPluginManager();
ConnectListener connectListener = new ConnectListener(this, core.getAntiBot()); Listener connectListener = new ConnectListener(this, core.getAntiBot());
pluginManager.registerListener(this, connectListener); pluginManager.registerListener(this, connectListener);
pluginManager.registerListener(this, new PluginMessageListener(this)); pluginManager.registerListener(this, new PluginMessageListener(this));

View File

@ -25,10 +25,10 @@
*/ */
package com.github.games647.fastlogin.core; package com.github.games647.fastlogin.core;
import com.github.games647.craftapi.cache.SafeCacheBuilder; import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.logging.Level; import java.util.logging.Level;
@ -43,7 +43,7 @@ public final class CommonUtil {
private static final char TRANSLATED_CHAR = '§'; private static final char TRANSLATED_CHAR = '§';
public static <K, V> ConcurrentMap<K, V> buildCache(int expireAfterWrite, int maxSize) { 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) { if (expireAfterWrite > 0) {
builder.expireAfterWrite(expireAfterWrite, TimeUnit.MINUTES); builder.expireAfterWrite(expireAfterWrite, TimeUnit.MINUTES);
@ -53,9 +53,7 @@ public final class CommonUtil {
builder.maximumSize(maxSize); builder.maximumSize(maxSize);
} }
return builder.build(CacheLoader.from(() -> { return builder.<K, V>build().asMap();
throw new UnsupportedOperationException();
}));
} }
public static String translateColorCodes(String rawMessage) { public static String translateColorCodes(String rawMessage) {
@ -74,7 +72,7 @@ public final class CommonUtil {
* This creates a SLF4J logger. In the process it initializes the SLF4J service provider. This method looks * 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 * 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. * initialized once, so this method should be called early.
* * <p>
* The provider is bound to the service class `SLF4JServiceProvider`. Relocating this class makes it available * 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 * for exclusive own usage. Other dependencies will use the relocated service too, and therefore will find the
* initialized provider. * initialized provider.
@ -98,7 +96,8 @@ public final class CommonUtil {
Constructor<JDK14LoggerAdapter> cons = adapterClass.getDeclaredConstructor(java.util.logging.Logger.class); Constructor<JDK14LoggerAdapter> cons = adapterClass.getDeclaredConstructor(java.util.logging.Logger.class);
cons.setAccessible(true); cons.setAccessible(true);
return cons.newInstance(parent); return cons.newInstance(parent);
} catch (ReflectiveOperationException reflectEx) { } catch (IllegalAccessException | InstantiationException | InvocationTargetException
| NoSuchMethodException reflectEx) {
parent.log(Level.WARNING, "Cannot create slf4j logging adapter", reflectEx); parent.log(Level.WARNING, "Cannot create slf4j logging adapter", reflectEx);
parent.log(Level.WARNING, "Creating logger instance manually..."); parent.log(Level.WARNING, "Creating logger instance manually...");
return LoggerFactory.getLogger(parent.getName()); return LoggerFactory.getLogger(parent.getName());
@ -109,6 +108,6 @@ public final class CommonUtil {
} }
private CommonUtil() { private CommonUtil() {
//Utility class throw new RuntimeException("No instantiation of utility classes allowed");
} }
} }

View File

@ -118,10 +118,11 @@ public class StoredProfile extends Profile {
return true; return true;
} }
if (!(o instanceof StoredProfile that)) { if (!(o instanceof StoredProfile)) {
return false; return false;
} }
StoredProfile that = (StoredProfile) o;
if (!super.equals(o)) { if (!super.equals(o)) {
return false; return false;
} }

View File

@ -48,7 +48,7 @@
<!-- Set default for non-git clones --> <!-- Set default for non-git clones -->
<git.commit.id>Unknown</git.commit.id> <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.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target> <maven.compiler.target>${java.version}</maven.compiler.target>
@ -189,5 +189,12 @@
<version>5.8.2</version> <version>5.8.2</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
<scope>provided</scope>
</dependency>
</dependencies> </dependencies>
</project> </project>