From 4dfbc0e32b73ad3b693d7d8abf1770189da912a8 Mon Sep 17 00:00:00 2001 From: William Date: Wed, 10 Apr 2024 15:41:23 +0100 Subject: [PATCH] fix: more gracefully handle platform mismatches --- .../husksync/data/DataSnapshot.java | 12 +++++------ .../husksync/redis/RedisManager.java | 21 ++++++++++++++----- .../william278/husksync/sync/DataSyncer.java | 14 +++++++++---- 3 files changed, 32 insertions(+), 15 deletions(-) diff --git a/common/src/main/java/net/william278/husksync/data/DataSnapshot.java b/common/src/main/java/net/william278/husksync/data/DataSnapshot.java index bae6865b..6bb20f55 100644 --- a/common/src/main/java/net/william278/husksync/data/DataSnapshot.java +++ b/common/src/main/java/net/william278/husksync/data/DataSnapshot.java @@ -111,14 +111,14 @@ public class DataSnapshot { @Nullable OffsetDateTime timestamp) throws IllegalStateException { final DataSnapshot.Packed snapshot = plugin.getDataAdapter().fromBytes(data, DataSnapshot.Packed.class); if (snapshot.getMinecraftVersion().compareTo(plugin.getMinecraftVersion()) > 0) { - throw new IllegalStateException(String.format("Cannot set data for user because the Minecraft version of " + - "their user data (%s) is newer than the server's Minecraft version (%s)." + + throw new IllegalStateException(String.format("Cannot deserialize data because the Minecraft version of " + + "the data snapshot (%s) is newer than the server's Minecraft version (%s)." + "Please ensure each server is running the same version of Minecraft.", snapshot.getMinecraftVersion(), plugin.getMinecraftVersion())); } if (snapshot.getFormatVersion() > CURRENT_FORMAT_VERSION) { - throw new IllegalStateException(String.format("Cannot set data for user because the format version of " + - "their user data (%s) is newer than the current format version (%s). " + + throw new IllegalStateException(String.format("Cannot deserialize data because the format version of " + + "the data snapshot (%s) is newer than the current format version (%s). " + "Please ensure each server is running the latest version of HuskSync.", snapshot.getFormatVersion(), CURRENT_FORMAT_VERSION)); } @@ -135,8 +135,8 @@ public class DataSnapshot { )); } if (!snapshot.getPlatformType().equalsIgnoreCase(plugin.getPlatformType())) { - throw new IllegalStateException(String.format("Cannot set data for user because the platform type of " + - "their user data (%s) is different to the server platform type (%s). " + + throw new IllegalStateException(String.format("Cannot deserialize data because the platform type of " + + "the data snapshot (%s) is different to the server platform type (%s). " + "Please ensure each server is running the same platform type.", snapshot.getPlatformType(), plugin.getPlatformType())); } diff --git a/common/src/main/java/net/william278/husksync/redis/RedisManager.java b/common/src/main/java/net/william278/husksync/redis/RedisManager.java index 5dbed40a..d442504c 100644 --- a/common/src/main/java/net/william278/husksync/redis/RedisManager.java +++ b/common/src/main/java/net/william278/husksync/redis/RedisManager.java @@ -158,10 +158,15 @@ public class RedisManager extends JedisPubSub { final RedisMessage redisMessage = RedisMessage.fromJson(plugin, message); switch (messageType) { case UPDATE_USER_DATA -> plugin.getOnlineUser(redisMessage.getTargetUuid()).ifPresent( - user -> user.applySnapshot( - DataSnapshot.deserialize(plugin, redisMessage.getPayload()), - DataSnapshot.UpdateCause.UPDATED - ) + user -> { + try { + final DataSnapshot.Packed data = DataSnapshot.deserialize(plugin, redisMessage.getPayload()); + user.applySnapshot(data, DataSnapshot.UpdateCause.UPDATED); + } catch (Throwable e) { + plugin.log(Level.SEVERE, "An exception occurred updating user data from Redis", e); + user.completeSync(false, DataSnapshot.UpdateCause.UPDATED, plugin); + } + } ); case REQUEST_USER_DATA -> plugin.getOnlineUser(redisMessage.getTargetUuid()).ifPresent( user -> RedisMessage.create( @@ -174,7 +179,13 @@ public class RedisManager extends JedisPubSub { redisMessage.getTargetUuid() ); if (future != null) { - future.complete(Optional.of(DataSnapshot.deserialize(plugin, redisMessage.getPayload()))); + try { + final DataSnapshot.Packed data = DataSnapshot.deserialize(plugin, redisMessage.getPayload()); + future.complete(Optional.of(data)); + } catch (Throwable e) { + plugin.log(Level.SEVERE, "An exception occurred returning user data from Redis", e); + future.complete(Optional.empty()); + } pendingRequests.remove(redisMessage.getTargetUuid()); } } diff --git a/common/src/main/java/net/william278/husksync/sync/DataSyncer.java b/common/src/main/java/net/william278/husksync/sync/DataSyncer.java index f69f8117..f7f4b1c9 100644 --- a/common/src/main/java/net/william278/husksync/sync/DataSyncer.java +++ b/common/src/main/java/net/william278/husksync/sync/DataSyncer.java @@ -38,6 +38,7 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.function.BiConsumer; import java.util.function.Function; import java.util.function.Supplier; +import java.util.logging.Level; /** * Handles the synchronization of data when a player changes servers or logs in @@ -156,10 +157,15 @@ public abstract class DataSyncer { // Set a user's data from the database, or set them as a new user @ApiStatus.Internal protected void setUserFromDatabase(@NotNull OnlineUser user) { - getDatabase().getLatestSnapshot(user).ifPresentOrElse( - snapshot -> user.applySnapshot(snapshot, DataSnapshot.UpdateCause.SYNCHRONIZED), - () -> user.completeSync(true, DataSnapshot.UpdateCause.NEW_USER, plugin) - ); + try { + getDatabase().getLatestSnapshot(user).ifPresentOrElse( + snapshot -> user.applySnapshot(snapshot, DataSnapshot.UpdateCause.SYNCHRONIZED), + () -> user.completeSync(true, DataSnapshot.UpdateCause.NEW_USER, plugin) + ); + } catch (Throwable e) { + plugin.log(Level.WARNING, "Failed to set %s's data from the database".formatted(user.getUsername()), e); + user.completeSync(false, DataSnapshot.UpdateCause.SYNCHRONIZED, plugin); + } } // Continuously listen for data from Redis