Merge remote-tracking branch 'origin/feature/floodgate-data-version' into feature/1.18

This commit is contained in:
Camotoy 2021-11-30 11:09:16 -05:00
commit 7df013daf9
No known key found for this signature in database
GPG key ID: 7EEFB66FE798081F
15 changed files with 46 additions and 296 deletions

View file

@ -70,8 +70,8 @@ public final class AesCipher implements FloodgateCipher {
cipherText = topping.encode(cipherText); cipherText = topping.encode(cipherText);
} }
return ByteBuffer.allocate(iv.length + cipherText.length + HEADER_LENGTH + 1) return ByteBuffer.allocate(HEADER.length + iv.length + cipherText.length + 1)
.put(IDENTIFIER) // header .put(HEADER)
.put(iv) .put(iv)
.put((byte) 0x21) .put((byte) 0x21)
.put(cipherText) .put(cipherText)
@ -83,15 +83,15 @@ public final class AesCipher implements FloodgateCipher {
Cipher cipher = Cipher.getInstance(CIPHER_NAME); Cipher cipher = Cipher.getInstance(CIPHER_NAME);
int bufferLength = cipherTextWithIv.length - HEADER_LENGTH; int bufferLength = cipherTextWithIv.length - HEADER.length;
ByteBuffer buffer = ByteBuffer.wrap(cipherTextWithIv, HEADER_LENGTH, bufferLength); ByteBuffer buffer = ByteBuffer.wrap(cipherTextWithIv, HEADER.length, bufferLength);
int ivLength = IV_LENGTH; int ivLength = IV_LENGTH;
if (topping != null) { if (topping != null) {
int mark = buffer.position(); int mark = buffer.position();
// we need the first index, the second is for the optional RawSkin // we need the first index, the second is for the actual data
boolean found = false; boolean found = false;
while (buffer.hasRemaining() && !found) { while (buffer.hasRemaining() && !found) {
if (buffer.get() == 0x21) { if (buffer.get() == 0x21) {

View file

@ -39,7 +39,7 @@ public final class AesKeyProducer implements KeyProducer {
public SecretKey produce() { public SecretKey produce() {
try { try {
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(KEY_SIZE, getSecureRandom()); keyGenerator.init(KEY_SIZE, secureRandom());
return keyGenerator.generateKey(); return keyGenerator.generateKey();
} catch (Exception exception) { } catch (Exception exception) {
throw new RuntimeException(exception); throw new RuntimeException(exception);
@ -55,7 +55,7 @@ public final class AesKeyProducer implements KeyProducer {
} }
} }
private SecureRandom getSecureRandom() throws NoSuchAlgorithmException { private SecureRandom secureRandom() throws NoSuchAlgorithmException {
// use Windows-PRNG for windows (default impl is SHA1PRNG) // use Windows-PRNG for windows (default impl is SHA1PRNG)
if (System.getProperty("os.name").startsWith("Windows")) { if (System.getProperty("os.name").startsWith("Windows")) {
return SecureRandom.getInstance("Windows-PRNG"); return SecureRandom.getInstance("Windows-PRNG");

View file

@ -26,33 +26,32 @@
package org.geysermc.floodgate.crypto; package org.geysermc.floodgate.crypto;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.geysermc.floodgate.util.InvalidFormatException; import org.geysermc.floodgate.util.InvalidFormatException;
import java.nio.charset.StandardCharsets;
import java.security.Key; import java.security.Key;
import static java.nio.charset.StandardCharsets.UTF_8;
/** /**
* Responsible for both encrypting and decrypting data * Responsible for both encrypting and decrypting data
*/ */
public interface FloodgateCipher { public interface FloodgateCipher {
// use invalid username characters at the beginning and the end of the identifier, int VERSION = 0;
// to make sure that it doesn't get messed up with usernames byte[] IDENTIFIER = "^Floodgate^".getBytes(UTF_8);
byte[] IDENTIFIER = "^Floodgate^".getBytes(StandardCharsets.UTF_8); byte[] HEADER = (new String(IDENTIFIER, UTF_8) + (char) (VERSION + 0x3E)).getBytes(UTF_8);
int HEADER_LENGTH = IDENTIFIER.length;
static boolean hasHeader(String data) { static int version(String data) {
if (data.length() < IDENTIFIER.length) { if (data.length() <= HEADER.length) {
return false; return -1;
} }
for (int i = 0; i < IDENTIFIER.length; i++) { for (int i = 0; i < IDENTIFIER.length; i++) {
if (IDENTIFIER[i] != data.charAt(i)) { if (IDENTIFIER[i] != data.charAt(i)) {
return false; return -1;
} }
} }
return true;
return data.charAt(IDENTIFIER.length) - 0x3E;
} }
/** /**
@ -79,7 +78,7 @@ public interface FloodgateCipher {
* @throws Exception when the encryption failed * @throws Exception when the encryption failed
*/ */
default byte[] encryptFromString(String data) throws Exception { default byte[] encryptFromString(String data) throws Exception {
return encrypt(data.getBytes(StandardCharsets.UTF_8)); return encrypt(data.getBytes(UTF_8));
} }
/** /**
@ -104,7 +103,7 @@ public interface FloodgateCipher {
if (decrypted == null) { if (decrypted == null) {
return null; return null;
} }
return new String(decrypted, StandardCharsets.UTF_8); return new String(decrypted, UTF_8);
} }
/** /**
@ -116,7 +115,7 @@ public interface FloodgateCipher {
* @throws Exception when the decrypting failed * @throws Exception when the decrypting failed
*/ */
default byte[] decryptFromString(String data) throws Exception { default byte[] decryptFromString(String data) throws Exception {
return decrypt(data.getBytes(StandardCharsets.UTF_8)); return decrypt(data.getBytes(UTF_8));
} }
/** /**
@ -127,37 +126,21 @@ public interface FloodgateCipher {
* @throws InvalidFormatException when the header is invalid * @throws InvalidFormatException when the header is invalid
*/ */
default void checkHeader(byte[] data) throws InvalidFormatException { default void checkHeader(byte[] data) throws InvalidFormatException {
final int identifierLength = IDENTIFIER.length; if (data.length <= HEADER.length) {
throw new InvalidFormatException(
if (data.length <= HEADER_LENGTH) { "Data length is smaller then header." +
throw new InvalidFormatException("Data length is smaller then header." + "Needed " + HEADER.length + ", got " + data.length
"Needed " + HEADER_LENGTH + ", got " + data.length,
true
); );
} }
for (int i = 0; i < identifierLength; i++) { for (int i = 0; i < IDENTIFIER.length; i++) {
if (IDENTIFIER[i] != data[i]) { if (IDENTIFIER[i] != data[i]) {
StringBuilder receivedIdentifier = new StringBuilder(); String identifier = new String(IDENTIFIER, UTF_8);
for (byte b : IDENTIFIER) { String received = new String(data, 0, IDENTIFIER.length, UTF_8);
receivedIdentifier.append(b);
}
throw new InvalidFormatException( throw new InvalidFormatException(
String.format("Expected identifier %s, got %s", "Expected identifier " + identifier + ", got " + received
new String(IDENTIFIER, StandardCharsets.UTF_8),
receivedIdentifier.toString()
),
true
); );
} }
} }
} }
@Data
@AllArgsConstructor
class HeaderResult {
private int version;
private int startIndex;
}
} }

View file

@ -1,91 +0,0 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* 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.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.floodgate.time;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.nio.ByteBuffer;
/*
* Thanks:
* https://datatracker.ietf.org/doc/html/rfc1769
* https://github.com/jonsagara/SimpleNtpClient
* https://stackoverflow.com/a/29138806
*/
public final class SntpClientUtils {
private static final int NTP_PORT = 123;
private static final int NTP_PACKET_SIZE = 48;
private static final int NTP_MODE = 3; // client
private static final int NTP_VERSION = 3;
private static final int RECEIVE_TIME_POSITION = 32;
private static final long NTP_TIME_OFFSET = ((365L * 70L) + 17L) * 24L * 60L * 60L;
public static long requestTimeOffset(String host, int timeout) {
try (DatagramSocket socket = new DatagramSocket()) {
socket.setSoTimeout(timeout);
InetAddress address = InetAddress.getByName(host);
ByteBuffer buff = ByteBuffer.allocate(NTP_PACKET_SIZE);
DatagramPacket request = new DatagramPacket(
buff.array(), NTP_PACKET_SIZE, address, NTP_PORT
);
// mode is in the least signification 3 bits
// version is in bits 3-5
buff.put((byte) (NTP_MODE | (NTP_VERSION << 3)));
long originateTime = System.currentTimeMillis();
socket.send(request);
DatagramPacket response = new DatagramPacket(buff.array(), NTP_PACKET_SIZE);
socket.receive(response);
long responseTime = System.currentTimeMillis();
// everything before isn't important for us
buff.position(RECEIVE_TIME_POSITION);
long receiveTime = readTimestamp(buff);
long transmitTime = readTimestamp(buff);
return ((receiveTime - originateTime) + (transmitTime - responseTime)) / 2;
} catch (Exception ignored) {
}
return Long.MIN_VALUE;
}
private static long readTimestamp(ByteBuffer buffer) {
//todo look into the ntp 2036 problem
long seconds = buffer.getInt() & 0xffffffffL;
long fraction = buffer.getInt() & 0xffffffffL;
return ((seconds - NTP_TIME_OFFSET) * 1000) + ((fraction * 1000) / 0x100000000L);
}
}

View file

@ -1,67 +0,0 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* 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.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.floodgate.time;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public final class TimeSyncer {
private final ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
private long timeOffset = Long.MIN_VALUE; // value when it failed to get the offset
public TimeSyncer(String timeServer) {
executorService.scheduleWithFixedDelay(() -> {
// 5 tries to get the time offset, since UDP doesn't guaranty a response
for (int i = 0; i < 5; i++) {
long offset = SntpClientUtils.requestTimeOffset(timeServer, 3000);
if (offset != Long.MIN_VALUE) {
timeOffset = offset;
return;
}
}
}, 0, 30, TimeUnit.MINUTES);
}
public void shutdown() {
executorService.shutdown();
}
public long getTimeOffset() {
return timeOffset;
}
public long getRealMillis() {
if (hasUsefulOffset()) {
return System.currentTimeMillis() + getTimeOffset();
}
return System.currentTimeMillis();
}
public boolean hasUsefulOffset() {
return timeOffset != Long.MIN_VALUE;
}
}

View file

@ -1,35 +0,0 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* 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.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.floodgate.util;
public final class Base64Utils {
public static int getEncodedLength(int length) {
if (length <= 0) {
return -1;
}
return 4 * ((length + 2) / 3);
}
}

View file

@ -28,7 +28,6 @@ package org.geysermc.floodgate.util;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.Getter; import lombok.Getter;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.geysermc.floodgate.time.TimeSyncer;
/** /**
* This class contains the raw data send by Geyser to Floodgate or from Floodgate to Floodgate. This * This class contains the raw data send by Geyser to Floodgate or from Floodgate to Floodgate. This
@ -38,7 +37,7 @@ import org.geysermc.floodgate.time.TimeSyncer;
@Getter @Getter
@RequiredArgsConstructor(access = AccessLevel.PRIVATE) @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
public final class BedrockData implements Cloneable { public final class BedrockData implements Cloneable {
public static final int EXPECTED_LENGTH = 13; public static final int EXPECTED_LENGTH = 12;
private final String version; private final String version;
private final String username; private final String username;
@ -54,25 +53,23 @@ public final class BedrockData implements Cloneable {
private final int subscribeId; private final int subscribeId;
private final String verifyCode; private final String verifyCode;
private final long timestamp;
private final int dataLength; private final int dataLength;
public static BedrockData of( public static BedrockData of(
String version, String username, String xuid, int deviceOs, String version, String username, String xuid, int deviceOs,
String languageCode, int uiProfile, int inputMode, String ip, String languageCode, int uiProfile, int inputMode, String ip,
LinkedPlayer linkedPlayer, boolean fromProxy, int subscribeId, LinkedPlayer linkedPlayer, boolean fromProxy, int subscribeId,
String verifyCode, TimeSyncer timeSyncer) { String verifyCode) {
return new BedrockData(version, username, xuid, deviceOs, languageCode, inputMode, return new BedrockData(version, username, xuid, deviceOs, languageCode, inputMode,
uiProfile, ip, linkedPlayer, fromProxy, subscribeId, verifyCode, uiProfile, ip, linkedPlayer, fromProxy, subscribeId, verifyCode, EXPECTED_LENGTH);
timeSyncer.getRealMillis(), EXPECTED_LENGTH);
} }
public static BedrockData of( public static BedrockData of(
String version, String username, String xuid, int deviceOs, String version, String username, String xuid, int deviceOs,
String languageCode, int uiProfile, int inputMode, String ip, String languageCode, int uiProfile, int inputMode, String ip,
int subscribeId, String verifyCode, TimeSyncer timeSyncer) { int subscribeId, String verifyCode) {
return of(version, username, xuid, deviceOs, languageCode, uiProfile, inputMode, ip, null, return of(version, username, xuid, deviceOs, languageCode, uiProfile, inputMode, ip, null,
false, subscribeId, verifyCode, timeSyncer); false, subscribeId, verifyCode);
} }
public static BedrockData fromString(String data) { public static BedrockData fromString(String data) {
@ -86,12 +83,12 @@ public final class BedrockData implements Cloneable {
return new BedrockData( return new BedrockData(
split[0], split[1], split[2], Integer.parseInt(split[3]), split[4], split[0], split[1], split[2], Integer.parseInt(split[3]), split[4],
Integer.parseInt(split[5]), Integer.parseInt(split[6]), split[7], linkedPlayer, Integer.parseInt(split[5]), Integer.parseInt(split[6]), split[7], linkedPlayer,
"1".equals(split[9]), Integer.parseInt(split[10]), split[11], Long.parseLong(split[12]), split.length "1".equals(split[9]), Integer.parseInt(split[10]), split[11], split.length
); );
} }
private static BedrockData emptyData(int dataLength) { private static BedrockData emptyData(int dataLength) {
return new BedrockData(null, null, null, -1, null, -1, -1, null, null, false, -1, null, -1, return new BedrockData(null, null, null, -1, null, -1, -1, null, null, false, -1, null,
dataLength); dataLength);
} }
@ -105,7 +102,7 @@ public final class BedrockData implements Cloneable {
return version + '\0' + username + '\0' + xuid + '\0' + deviceOs + '\0' + return version + '\0' + username + '\0' + xuid + '\0' + deviceOs + '\0' +
languageCode + '\0' + uiProfile + '\0' + inputMode + '\0' + ip + '\0' + languageCode + '\0' + uiProfile + '\0' + inputMode + '\0' + ip + '\0' +
(linkedPlayer != null ? linkedPlayer.toString() : "null") + '\0' + (linkedPlayer != null ? linkedPlayer.toString() : "null") + '\0' +
(fromProxy ? 1 : 0) + '\0' + subscribeId + '\0' + verifyCode + '\0' + timestamp; (fromProxy ? 1 : 0) + '\0' + subscribeId + '\0' + verifyCode;
} }
@Override @Override

View file

@ -59,7 +59,7 @@ public enum DeviceOs {
* @param id the DeviceOs identifier * @param id the DeviceOs identifier
* @return The DeviceOs or {@link #UNKNOWN} if the DeviceOs wasn't found * @return The DeviceOs or {@link #UNKNOWN} if the DeviceOs wasn't found
*/ */
public static DeviceOs getById(int id) { public static DeviceOs fromId(int id) {
return id < VALUES.length ? VALUES[id] : VALUES[0]; return id < VALUES.length ? VALUES[id] : VALUES[0];
} }

View file

@ -41,7 +41,7 @@ public enum InputMode {
* @param id the InputMode identifier * @param id the InputMode identifier
* @return The InputMode or {@link #UNKNOWN} if the DeviceOs wasn't found * @return The InputMode or {@link #UNKNOWN} if the DeviceOs wasn't found
*/ */
public static InputMode getById(int id) { public static InputMode fromId(int id) {
return VALUES.length > id ? VALUES[id] : VALUES[0]; return VALUES.length > id ? VALUES[id] : VALUES[0];
} }
} }

View file

@ -26,26 +26,8 @@
package org.geysermc.floodgate.util; package org.geysermc.floodgate.util;
import lombok.Getter;
@Getter
public class InvalidFormatException extends Exception { public class InvalidFormatException extends Exception {
private boolean header = false;
public InvalidFormatException() {
super();
}
public InvalidFormatException(String message) { public InvalidFormatException(String message) {
super(message); super(message);
} }
public InvalidFormatException(String message, boolean header) {
super(message);
this.header = header;
}
public InvalidFormatException(String message, Throwable cause) {
super(message, cause);
}
} }

View file

@ -38,7 +38,7 @@ public enum UiProfile {
* @param id the UiProfile identifier * @param id the UiProfile identifier
* @return The UiProfile or {@link #CLASSIC} if the UiProfile wasn't found * @return The UiProfile or {@link #CLASSIC} if the UiProfile wasn't found
*/ */
public static UiProfile getById(int id) { public static UiProfile fromId(int id) {
return VALUES.length > id ? VALUES[id] : VALUES[0]; return VALUES.length > id ? VALUES[id] : VALUES[0];
} }
} }

View file

@ -81,11 +81,11 @@ public enum WebsocketEventType {
this.id = id; this.id = id;
} }
public static WebsocketEventType getById(int id) { public static WebsocketEventType fromId(int id) {
return VALUES.length > id ? VALUES[id] : null; return VALUES.length > id ? VALUES[id] : null;
} }
public int getId() { public int id() {
return id; return id;
} }
} }

View file

@ -48,7 +48,6 @@ import org.geysermc.floodgate.crypto.AesKeyProducer;
import org.geysermc.floodgate.crypto.Base64Topping; import org.geysermc.floodgate.crypto.Base64Topping;
import org.geysermc.floodgate.crypto.FloodgateCipher; import org.geysermc.floodgate.crypto.FloodgateCipher;
import org.geysermc.floodgate.news.NewsItemAction; import org.geysermc.floodgate.news.NewsItemAction;
import org.geysermc.floodgate.time.TimeSyncer;
import org.geysermc.geyser.api.GeyserApi; import org.geysermc.geyser.api.GeyserApi;
import org.geysermc.geyser.command.CommandManager; import org.geysermc.geyser.command.CommandManager;
import org.geysermc.geyser.configuration.GeyserConfiguration; import org.geysermc.geyser.configuration.GeyserConfiguration;
@ -110,7 +109,6 @@ public class GeyserImpl implements GeyserApi {
@Setter @Setter
private static boolean shouldStartListener = true; private static boolean shouldStartListener = true;
private final TimeSyncer timeSyncer;
private FloodgateCipher cipher; private FloodgateCipher cipher;
private FloodgateSkinUploader skinUploader; private FloodgateSkinUploader skinUploader;
private final NewsHandler newsHandler; private final NewsHandler newsHandler;
@ -201,9 +199,7 @@ public class GeyserImpl implements GeyserApi {
// Ensure that PacketLib does not create an event loop for handling packets; we'll do that ourselves // Ensure that PacketLib does not create an event loop for handling packets; we'll do that ourselves
TcpSession.USE_EVENT_LOOP_FOR_PACKETS = false; TcpSession.USE_EVENT_LOOP_FOR_PACKETS = false;
TimeSyncer timeSyncer = null;
if (config.getRemote().getAuthType() == AuthType.FLOODGATE) { if (config.getRemote().getAuthType() == AuthType.FLOODGATE) {
timeSyncer = new TimeSyncer(Constants.NTP_SERVER);
try { try {
Key key = new AesKeyProducer().produceFrom(config.getFloodgateKeyPath()); Key key = new AesKeyProducer().produceFrom(config.getFloodgateKeyPath());
cipher = new AesCipher(new Base64Topping()); cipher = new AesCipher(new Base64Topping());
@ -214,7 +210,6 @@ public class GeyserImpl implements GeyserApi {
logger.severe(GeyserLocale.getLocaleStringLog("geyser.auth.floodgate.bad_key"), exception); logger.severe(GeyserLocale.getLocaleStringLog("geyser.auth.floodgate.bad_key"), exception);
} }
} }
this.timeSyncer = timeSyncer;
String branch = "unknown"; String branch = "unknown";
int buildNumber = -1; int buildNumber = -1;
@ -441,9 +436,6 @@ public class GeyserImpl implements GeyserApi {
scheduledThread.shutdown(); scheduledThread.shutdown();
bedrockServer.close(); bedrockServer.close();
if (timeSyncer != null) {
timeSyncer.shutdown();
}
if (skinUploader != null) { if (skinUploader != null) {
skinUploader.close(); skinUploader.close();
} }
@ -491,10 +483,6 @@ public class GeyserImpl implements GeyserApi {
return bootstrap.getWorldManager(); return bootstrap.getWorldManager();
} }
public TimeSyncer getTimeSyncer() {
return timeSyncer;
}
public static GeyserImpl getInstance() { public static GeyserImpl getInstance() {
return instance; return instance;
} }

View file

@ -787,6 +787,8 @@ public class GeyserSession implements GeyserConnection, CommandSender {
FloodgateSkinUploader skinUploader = geyser.getSkinUploader(); FloodgateSkinUploader skinUploader = geyser.getSkinUploader();
FloodgateCipher cipher = geyser.getCipher(); FloodgateCipher cipher = geyser.getCipher();
System.out.println(new String(FloodgateCipher.HEADER, StandardCharsets.UTF_8));
encryptedData = cipher.encryptFromString(BedrockData.of( encryptedData = cipher.encryptFromString(BedrockData.of(
clientData.getGameVersion(), clientData.getGameVersion(),
authData.name(), authData.name(),
@ -797,17 +799,8 @@ public class GeyserSession implements GeyserConnection, CommandSender {
clientData.getCurrentInputMode().ordinal(), clientData.getCurrentInputMode().ordinal(),
upstream.getAddress().getAddress().getHostAddress(), upstream.getAddress().getAddress().getHostAddress(),
skinUploader.getId(), skinUploader.getId(),
skinUploader.getVerifyCode(), skinUploader.getVerifyCode()
geyser.getTimeSyncer()
).toString()); ).toString());
if (!geyser.getTimeSyncer().hasUsefulOffset()) {
geyser.getLogger().warning(
"We couldn't make sure that your system clock is accurate. " +
"This can cause issues with logging in."
);
}
} catch (Exception e) { } catch (Exception e) {
geyser.getLogger().error(GeyserLocale.getLocaleStringLog("geyser.auth.floodgate.encrypt_fail"), e); geyser.getLogger().error(GeyserLocale.getLocaleStringLog("geyser.auth.floodgate.encrypt_fail"), e);
disconnect(GeyserLocale.getPlayerLocaleString("geyser.auth.floodgate.encryption_fail", getClientData().getLanguageCode())); disconnect(GeyserLocale.getPlayerLocaleString("geyser.auth.floodgate.encryption_fail", getClientData().getLanguageCode()));

View file

@ -87,7 +87,7 @@ public final class FloodgateSkinUploader {
} }
int typeId = node.get("event_id").asInt(); int typeId = node.get("event_id").asInt();
WebsocketEventType type = WebsocketEventType.getById(typeId); WebsocketEventType type = WebsocketEventType.fromId(typeId);
if (type == null) { if (type == null) {
logger.warning(String.format( logger.warning(String.format(
"Got (unknown) type %s. Ensure that Geyser is on the latest version and report this issue!", "Got (unknown) type %s. Ensure that Geyser is on the latest version and report this issue!",