9
0
mirror of https://github.com/WiIIiam278/HuskSync.git synced 2025-12-19 14:59:21 +00:00

feat: various logical improvements to data syncing

* Update data in Redis during world saves & commands
* Always set data time to 1 year regardless of sync mode
This commit is contained in:
William278
2025-03-07 16:54:19 +00:00
parent e56041eae2
commit 305f90f697
9 changed files with 25 additions and 38 deletions

View File

@@ -23,7 +23,6 @@ import de.themoep.minedown.adventure.MineDown;
import net.william278.husksync.HuskSync;
import net.william278.husksync.data.Data;
import net.william278.husksync.data.DataSnapshot;
import net.william278.husksync.redis.RedisKeyType;
import net.william278.husksync.redis.RedisManager;
import net.william278.husksync.user.OnlineUser;
import net.william278.husksync.user.User;
@@ -84,18 +83,17 @@ public class EnderChestCommand extends ItemsCommand {
// Create and pack the snapshot with the updated enderChest
final DataSnapshot.Packed snapshot = latestData.get().copy();
boolean pin = plugin.getSettings().getSynchronization().doAutoPin(DataSnapshot.SaveCause.ENDERCHEST_COMMAND);
snapshot.edit(plugin, (data) -> {
data.getEnderChest().ifPresent(enderChest -> enderChest.setContents(items));
data.setSaveCause(DataSnapshot.SaveCause.ENDERCHEST_COMMAND);
data.setPinned(
plugin.getSettings().getSynchronization().doAutoPin(DataSnapshot.SaveCause.ENDERCHEST_COMMAND)
);
data.setPinned(pin);
});
// Save data
final RedisManager redis = plugin.getRedisManager();
plugin.getDataSyncer().saveData(holder, snapshot, (user, data) -> {
redis.getUserData(user).ifPresent(d -> redis.setUserData(user, snapshot, RedisKeyType.TTL_1_YEAR));
redis.getUserData(user).ifPresent(d -> redis.setUserData(user, snapshot));
redis.sendUserDataUpdate(user, data);
});
}

View File

@@ -23,7 +23,6 @@ import de.themoep.minedown.adventure.MineDown;
import net.william278.husksync.HuskSync;
import net.william278.husksync.data.Data;
import net.william278.husksync.data.DataSnapshot;
import net.william278.husksync.redis.RedisKeyType;
import net.william278.husksync.redis.RedisManager;
import net.william278.husksync.user.OnlineUser;
import net.william278.husksync.user.User;
@@ -85,18 +84,17 @@ public class InventoryCommand extends ItemsCommand {
// Create and pack the snapshot with the updated inventory
final DataSnapshot.Packed snapshot = latestData.get().copy();
boolean pin = plugin.getSettings().getSynchronization().doAutoPin(DataSnapshot.SaveCause.INVENTORY_COMMAND);
snapshot.edit(plugin, (data) -> {
data.getInventory().ifPresent(inventory -> inventory.setContents(items));
data.setSaveCause(DataSnapshot.SaveCause.INVENTORY_COMMAND);
data.setPinned(
plugin.getSettings().getSynchronization().doAutoPin(DataSnapshot.SaveCause.INVENTORY_COMMAND)
);
data.setPinned(pin);
});
// Save data
final RedisManager redis = plugin.getRedisManager();
plugin.getDataSyncer().saveData(holder, snapshot, (user, data) -> {
redis.getUserData(user).ifPresent(d -> redis.setUserData(user, snapshot, RedisKeyType.TTL_1_YEAR));
redis.getUserData(user).ifPresent(d -> redis.setUserData(user, snapshot));
redis.sendUserDataUpdate(user, data);
});
}

View File

@@ -77,6 +77,7 @@ public abstract class PluginCommand extends Command {
}
@NotNull
@SuppressWarnings("SameParameterValue")
protected <S> ArgumentElement<S, OnlineUser> onlineUser(@NotNull String name) {
return new ArgumentElement<>(name, reader -> {
final String username = reader.readString();

View File

@@ -22,7 +22,6 @@ package net.william278.husksync.command;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import net.william278.husksync.HuskSync;
import net.william278.husksync.data.DataSnapshot;
import net.william278.husksync.redis.RedisKeyType;
import net.william278.husksync.redis.RedisManager;
import net.william278.husksync.user.CommandUser;
import net.william278.husksync.user.OnlineUser;
@@ -154,7 +153,7 @@ public class UserDataCommand extends PluginCommand {
// Save data
final RedisManager redis = plugin.getRedisManager();
plugin.getDataSyncer().saveData(user, data, (u, s) -> {
redis.getUserData(u).ifPresent(d -> redis.setUserData(u, s, RedisKeyType.TTL_1_YEAR));
redis.getUserData(u).ifPresent(d -> redis.setUserData(u, s));
redis.sendUserDataUpdate(u, s);
plugin.getLocales().getLocale("data_restored", u.getName(), u.getUuid().toString(),
s.getShortId(), s.getId().toString()).ifPresent(executor::sendMessage);

View File

@@ -80,8 +80,8 @@ public abstract class EventListener {
}
usersInWorld.stream()
.filter(user -> !plugin.isLocked(user.getUuid()) && !user.isNpc())
.forEach(user -> plugin.getDataSyncer().saveData(
user, user.createSnapshot(DataSnapshot.SaveCause.WORLD_SAVE)
.forEach(user -> plugin.getDataSyncer().saveCurrentUserData(
user, DataSnapshot.SaveCause.WORLD_SAVE
));
}
@@ -94,12 +94,13 @@ public abstract class EventListener {
protected void saveOnPlayerDeath(@NotNull OnlineUser user, @NotNull Data.Items items) {
final SaveOnDeathSettings settings = plugin.getSettings().getSynchronization().getSaveOnDeath();
if (plugin.isDisabling() || !settings.isEnabled() || plugin.isLocked(user.getUuid())
|| user.isNpc() || (!settings.isSaveEmptyItems() && items.isEmpty())) {
|| user.isNpc() || (!settings.isSaveEmptyItems() && items.isEmpty())) {
return;
}
// We don't persist this to Redis for syncing, as this snapshot is from a state they won't be in post-respawn
final DataSnapshot.Packed snapshot = user.createSnapshot(DataSnapshot.SaveCause.DEATH);
snapshot.edit(plugin, (data -> data.getInventory().ifPresent(inventory -> inventory.setContents(items))));
snapshot.edit(plugin, (data -> data.getInventory().ifPresent(inv -> inv.setContents(items))));
plugin.getDataSyncer().saveData(user, snapshot);
}
@@ -108,16 +109,12 @@ public abstract class EventListener {
* Handle the plugin disabling
*/
public void handlePluginDisable() {
// Save for all online players
// Save for all online players.
plugin.getOnlineUsers().stream()
.filter(user -> !plugin.isLocked(user.getUuid()) && !user.isNpc())
.forEach(user -> {
plugin.lockPlayer(user.getUuid());
plugin.getDataSyncer().saveData(
user,
user.createSnapshot(DataSnapshot.SaveCause.SERVER_SHUTDOWN),
(saved, data) -> plugin.getRedisManager().clearUserData(saved)
);
plugin.getDataSyncer().saveCurrentUserData(user, DataSnapshot.SaveCause.SERVER_SHUTDOWN);
});
// Close outstanding connections

View File

@@ -93,7 +93,7 @@ public class RedisManager extends JedisPubSub {
jedisPool.getResource().ping();
} catch (JedisException e) {
throw new IllegalStateException("Failed to establish connection with Redis. "
+ "Please check the supplied credentials in the config file", e);
+ "Please check the supplied credentials in the config file", e);
}
// Subscribe using a thread (rather than a task)
@@ -213,6 +213,8 @@ public class RedisManager extends JedisPubSub {
public void sendUserDataUpdate(@NotNull User user, @NotNull DataSnapshot.Packed data) {
plugin.runAsync(() -> {
this.setUserData(user, data);
final RedisMessage redisMessage = RedisMessage.create(user.getUuid(), data.asBytes(plugin));
redisMessage.dispatch(plugin, RedisMessage.Type.UPDATE_USER_DATA);
});
@@ -226,6 +228,7 @@ public class RedisManager extends JedisPubSub {
.orElse(this.requestData(requestId, user));
}
// Request a user's dat x-server
private CompletableFuture<Optional<DataSnapshot.Packed>> requestData(@NotNull UUID requestId, @NotNull User user) {
final CompletableFuture<Optional<DataSnapshot.Packed>> future = new CompletableFuture<>();
pendingRequests.put(requestId, future);
@@ -247,19 +250,13 @@ public class RedisManager extends JedisPubSub {
});
}
/**
* Set a user's data to Redis
*
* @param user the user to set data for
* @param data the user's data to set
* @param timeToLive The time to cache the data for
*/
// Set a user's data to Redis
@Blocking
public void setUserData(@NotNull User user, @NotNull DataSnapshot.Packed data, int timeToLive) {
public void setUserData(@NotNull User user, @NotNull DataSnapshot.Packed data) {
try (Jedis jedis = jedisPool.getResource()) {
jedis.setex(
getKey(RedisKeyType.LATEST_SNAPSHOT, user.getUuid(), clusterId),
timeToLive,
RedisKeyType.TTL_1_YEAR,
data.asBytes(plugin)
);
plugin.debug(String.format("[%s] Set %s key on Redis", user.getName(), RedisKeyType.LATEST_SNAPSHOT));

View File

@@ -23,7 +23,6 @@ import net.william278.husksync.HuskSync;
import net.william278.husksync.api.HuskSyncAPI;
import net.william278.husksync.data.DataSnapshot;
import net.william278.husksync.database.Database;
import net.william278.husksync.redis.RedisKeyType;
import net.william278.husksync.redis.RedisManager;
import net.william278.husksync.user.OnlineUser;
import net.william278.husksync.user.User;
@@ -104,7 +103,7 @@ public abstract class DataSyncer {
public void saveCurrentUserData(@NotNull OnlineUser onlineUser, @NotNull DataSnapshot.SaveCause cause) {
this.saveData(
onlineUser, onlineUser.createSnapshot(cause),
(user, data) -> getRedis().setUserData(user, data, RedisKeyType.TTL_10_SECONDS)
(user, data) -> getRedis().setUserData(user, data)
);
}

View File

@@ -21,7 +21,6 @@ package net.william278.husksync.sync;
import net.william278.husksync.HuskSync;
import net.william278.husksync.data.DataSnapshot;
import net.william278.husksync.redis.RedisKeyType;
import net.william278.husksync.user.OnlineUser;
import org.jetbrains.annotations.NotNull;
@@ -63,7 +62,7 @@ public class DelayDataSyncer extends DataSyncer {
getRedis().setUserServerSwitch(onlineUser);
saveData(
onlineUser, onlineUser.createSnapshot(DataSnapshot.SaveCause.DISCONNECT),
(user, data) -> getRedis().setUserData(user, data, RedisKeyType.TTL_10_SECONDS)
(user, data) -> getRedis().setUserData(user, data)
);
});
}

View File

@@ -21,7 +21,6 @@ package net.william278.husksync.sync;
import net.william278.husksync.HuskSync;
import net.william278.husksync.data.DataSnapshot;
import net.william278.husksync.redis.RedisKeyType;
import net.william278.husksync.user.OnlineUser;
import org.jetbrains.annotations.NotNull;
@@ -62,7 +61,7 @@ public class LockstepDataSyncer extends DataSyncer {
plugin.runAsync(() -> saveData(
onlineUser, onlineUser.createSnapshot(DataSnapshot.SaveCause.DISCONNECT),
(user, data) -> {
getRedis().setUserData(user, data, RedisKeyType.TTL_1_YEAR);
getRedis().setUserData(user, data);
getRedis().setUserCheckedOut(user, false);
}
));