diff --git a/common/src/main/java/net/william278/husksync/api/HuskSyncAPI.java b/common/src/main/java/net/william278/husksync/api/HuskSyncAPI.java index 5fc2272e..9f88c2fa 100644 --- a/common/src/main/java/net/william278/husksync/api/HuskSyncAPI.java +++ b/common/src/main/java/net/william278/husksync/api/HuskSyncAPI.java @@ -149,7 +149,7 @@ public class HuskSyncAPI { */ public CompletableFuture> getCurrentData(@NotNull User user) { return plugin.getRedisManager() - .getUserData(UUID.randomUUID(), user) + .getOnlineUserData(UUID.randomUUID(), user, DataSnapshot.SaveCause.API) .thenApply(data -> data.or(() -> plugin.getDatabase().getLatestSnapshot(user))) .thenApply(data -> data.map(snapshot -> snapshot.unpack(plugin))); } diff --git a/common/src/main/java/net/william278/husksync/command/EnderChestCommand.java b/common/src/main/java/net/william278/husksync/command/EnderChestCommand.java index 45463151..273a58ad 100644 --- a/common/src/main/java/net/william278/husksync/command/EnderChestCommand.java +++ b/common/src/main/java/net/william278/husksync/command/EnderChestCommand.java @@ -36,7 +36,7 @@ import java.util.Optional; public class EnderChestCommand extends ItemsCommand { public EnderChestCommand(@NotNull HuskSync plugin) { - super("enderchest", List.of("echest", "openechest"), plugin); + super("enderchest", List.of("echest", "openechest"), DataSnapshot.SaveCause.ENDERCHEST_COMMAND, plugin); } @Override @@ -83,10 +83,10 @@ 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); + boolean pin = plugin.getSettings().getSynchronization().doAutoPin(saveCause); snapshot.edit(plugin, (data) -> { data.getEnderChest().ifPresent(enderChest -> enderChest.setContents(items)); - data.setSaveCause(DataSnapshot.SaveCause.ENDERCHEST_COMMAND); + data.setSaveCause(saveCause); data.setPinned(pin); }); diff --git a/common/src/main/java/net/william278/husksync/command/InventoryCommand.java b/common/src/main/java/net/william278/husksync/command/InventoryCommand.java index 00cbbf6b..9a69d04f 100644 --- a/common/src/main/java/net/william278/husksync/command/InventoryCommand.java +++ b/common/src/main/java/net/william278/husksync/command/InventoryCommand.java @@ -36,7 +36,7 @@ import java.util.Optional; public class InventoryCommand extends ItemsCommand { public InventoryCommand(@NotNull HuskSync plugin) { - super("inventory", List.of("invsee", "openinv"), plugin); + super("inventory", List.of("invsee", "openinv"), DataSnapshot.SaveCause.INVENTORY_COMMAND, plugin); } @Override @@ -84,10 +84,10 @@ 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); + boolean pin = plugin.getSettings().getSynchronization().doAutoPin(saveCause); snapshot.edit(plugin, (data) -> { data.getInventory().ifPresent(inventory -> inventory.setContents(items)); - data.setSaveCause(DataSnapshot.SaveCause.INVENTORY_COMMAND); + data.setSaveCause(saveCause); data.setPinned(pin); }); diff --git a/common/src/main/java/net/william278/husksync/command/ItemsCommand.java b/common/src/main/java/net/william278/husksync/command/ItemsCommand.java index ecdee745..8c63b71c 100644 --- a/common/src/main/java/net/william278/husksync/command/ItemsCommand.java +++ b/common/src/main/java/net/william278/husksync/command/ItemsCommand.java @@ -34,8 +34,12 @@ import java.util.UUID; public abstract class ItemsCommand extends PluginCommand { - protected ItemsCommand(@NotNull String name, @NotNull List aliases, @NotNull HuskSync plugin) { + protected final DataSnapshot.SaveCause saveCause; + + protected ItemsCommand(@NotNull String name, @NotNull List aliases, + @NotNull DataSnapshot.SaveCause saveCause, @NotNull HuskSync plugin) { super(name, aliases, Permission.Default.IF_OP, ExecutionScope.IN_GAME, plugin); + this.saveCause = saveCause; } @Override @@ -50,7 +54,7 @@ public abstract class ItemsCommand extends PluginCommand { return; } this.showSnapshotItems(online, user, version); - }, user("username"), uuid("version")); + }, user("username"), versionUuid()); command.addSyntax((ctx) -> { final User user = ctx.getArgument("username", User.class); final CommandUser executor = user(command, ctx); @@ -65,7 +69,7 @@ public abstract class ItemsCommand extends PluginCommand { // View (and edit) the latest user data private void showLatestItems(@NotNull OnlineUser viewer, @NotNull User user) { - plugin.getRedisManager().getUserData(user.getUuid(), user).thenAccept(data -> data + plugin.getRedisManager().getOnlineUserData(user.getUuid(), user, saveCause).thenAccept(d -> d .or(() -> plugin.getDatabase().getLatestSnapshot(user)) .or(() -> { plugin.getLocales().getLocale("error_no_data_to_display") diff --git a/common/src/main/java/net/william278/husksync/command/PluginCommand.java b/common/src/main/java/net/william278/husksync/command/PluginCommand.java index 3d04c952..025b1a88 100644 --- a/common/src/main/java/net/william278/husksync/command/PluginCommand.java +++ b/common/src/main/java/net/william278/husksync/command/PluginCommand.java @@ -32,6 +32,7 @@ import net.william278.uniform.element.ArgumentElement; import org.jetbrains.annotations.NotNull; import java.util.Arrays; +import java.util.Comparator; import java.util.List; import java.util.UUID; import java.util.function.Function; @@ -104,14 +105,23 @@ public abstract class PluginCommand extends Command { } @NotNull - protected ArgumentElement uuid(@NotNull String name) { - return new ArgumentElement<>(name, reader -> { + protected ArgumentElement versionUuid() { + return new ArgumentElement<>("version", reader -> { try { return UUID.fromString(reader.readString()); } catch (IllegalArgumentException e) { throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownArgument().createWithContext(reader); } - }, (context, builder) -> builder.buildFuture()); + }, (context, builder) -> { + try { + plugin.getDatabase().getAllSnapshots(context.getArgument("username", User.class)) + .stream().sorted(Comparator.comparing(d -> d.getTimestamp().toEpochSecond())) + .forEach(id -> builder.suggest(id.getId().toString())); + return builder.buildFuture(); + } catch (IllegalArgumentException e) { + return builder.buildFuture(); + } + }); } public enum Type { diff --git a/common/src/main/java/net/william278/husksync/command/UserDataCommand.java b/common/src/main/java/net/william278/husksync/command/UserDataCommand.java index 70854364..ec8fd466 100644 --- a/common/src/main/java/net/william278/husksync/command/UserDataCommand.java +++ b/common/src/main/java/net/william278/husksync/command/UserDataCommand.java @@ -30,9 +30,9 @@ import net.william278.husksync.redis.RedisManager; import net.william278.husksync.user.CommandUser; import net.william278.husksync.user.OnlineUser; import net.william278.husksync.user.User; -import net.william278.husksync.util.UserDataDumper; import net.william278.husksync.util.DataSnapshotList; import net.william278.husksync.util.DataSnapshotOverview; +import net.william278.husksync.util.UserDataDumper; import net.william278.uniform.BaseCommand; import net.william278.uniform.CommandProvider; import net.william278.uniform.Permission; @@ -185,7 +185,7 @@ public class UserDataCommand extends PluginCommand { .ifPresent(executor::sendMessage); } - // Dump a snapshot + // Lookup a snapshot by UUID and dump private void dumpSnapshot(@NotNull CommandUser executor, @NotNull User user, @NotNull UUID version, @NotNull DumpType type) { final Optional data = plugin.getDatabase().getSnapshot(user, version); @@ -194,9 +194,12 @@ public class UserDataCommand extends PluginCommand { .ifPresent(executor::sendMessage); return; } + this.dumpSnapshot(executor, user, data.get(), type); + } - // Dump the data - final DataSnapshot.Packed userData = data.get(); + // Dump a snapshot + private void dumpSnapshot(@NotNull CommandUser executor, @NotNull User user, + @NotNull DataSnapshot.Packed userData, @NotNull DumpType type) { final UserDataDumper dumper = UserDataDumper.create(userData, user, plugin); try { final String url = type == DumpType.WEB ? dumper.toWeb() : dumper.toFile(); @@ -212,17 +215,11 @@ public class UserDataCommand extends PluginCommand { @NotNull private CommandProvider view() { - return (sub) -> { - sub.addSyntax((ctx) -> { - final User user = ctx.getArgument("username", User.class); - viewLatestSnapshot(user(sub, ctx), user); - }, user("username")); - sub.addSyntax((ctx) -> { - final User user = ctx.getArgument("username", User.class); - final UUID version = ctx.getArgument("version", UUID.class); - viewSnapshot(user(sub, ctx), user, version); - }, user("username"), uuid("version")); - }; + return (sub) -> sub.addSyntax((ctx) -> { + final User user = ctx.getArgument("username", User.class); + final UUID version = ctx.getArgument("version", UUID.class); + viewSnapshot(user(sub, ctx), user, version); + }, user("username"), versionUuid()); } @NotNull @@ -246,7 +243,7 @@ public class UserDataCommand extends PluginCommand { final User user = ctx.getArgument("username", User.class); final UUID version = ctx.getArgument("version", UUID.class); deleteSnapshot(user(sub, ctx), user, version); - }, user("username"), uuid("version")); + }, user("username"), versionUuid()); } @NotNull @@ -263,7 +260,7 @@ public class UserDataCommand extends PluginCommand { final User user = ctx.getArgument("username", User.class); final UUID version = ctx.getArgument("version", UUID.class); restoreSnapshot(user(sub, ctx), user, version); - }, user("username"), uuid("version")); + }, user("username"), versionUuid()); } @NotNull @@ -272,17 +269,32 @@ public class UserDataCommand extends PluginCommand { final User user = ctx.getArgument("username", User.class); final UUID version = ctx.getArgument("version", UUID.class); pinSnapshot(user(sub, ctx), user, version); - }, user("username"), uuid("version")); + }, user("username"), versionUuid()); } @NotNull private CommandProvider dump() { - return (sub) -> sub.addSyntax((ctx) -> { - final User user = ctx.getArgument("username", User.class); - final UUID version = ctx.getArgument("version", UUID.class); - final DumpType type = ctx.getArgument("type", DumpType.class); - dumpSnapshot(user(sub, ctx), user, version, type); - }, user("username"), uuid("version"), dumpType()); + return (sub) -> { + sub.addSyntax((ctx) -> { + final User user = ctx.getArgument("username", User.class); + final CommandUser executor = user(sub, ctx); + plugin.getRedisManager() + .getOnlineUserData(UUID.randomUUID(), user, DataSnapshot.SaveCause.DUMP_COMMAND) + .thenAccept((data) -> data + .or(() -> plugin.getDatabase().getLatestSnapshot(user)) + .ifPresentOrElse( + (s) -> dumpSnapshot(executor, user, s, DumpType.WEB), + () -> plugin.getLocales().getLocale("error_no_data_to_display") + .ifPresent(executor::sendMessage) + )); + }, user("username")); + sub.addSyntax((ctx) -> { + final User user = ctx.getArgument("username", User.class); + final UUID version = ctx.getArgument("version", UUID.class); + final DumpType type = ctx.getArgument("type", DumpType.class); + dumpSnapshot(user(sub, ctx), user, version, type); + }, user("username"), versionUuid(), dumpType()); + }; } private ArgumentElement dumpType() { 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 87e9b4b1..eb037d77 100644 --- a/common/src/main/java/net/william278/husksync/data/DataSnapshot.java +++ b/common/src/main/java/net/william278/husksync/data/DataSnapshot.java @@ -887,6 +887,13 @@ public class DataSnapshot { */ public static final SaveCause SAVE_COMMAND = of("SAVE_COMMAND", true); + /** + * Indicates data was saved from executing the {@code /userdata dump} command + * + * @since 3.8 + */ + public static final SaveCause DUMP_COMMAND = of("DUMP_COMMAND", true); + /** * Indicates data was saved by an API call * 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 ca8526e4..a176237a 100644 --- a/common/src/main/java/net/william278/husksync/redis/RedisManager.java +++ b/common/src/main/java/net/william278/husksync/redis/RedisManager.java @@ -218,16 +218,17 @@ public class RedisManager extends JedisPubSub { }); } - public CompletableFuture> getUserData(@NotNull UUID requestId, @NotNull User user) { + public CompletableFuture> getOnlineUserData(@NotNull UUID requestId, @NotNull User user, + @NotNull DataSnapshot.SaveCause saveCause) { return plugin.getOnlineUser(user.getUuid()) .map(online -> CompletableFuture.completedFuture( - Optional.of(online.createSnapshot(DataSnapshot.SaveCause.API))) + Optional.of(online.createSnapshot(saveCause))) ) - .orElse(this.requestData(requestId, user)); + .orElse(this.getNetworkedUserData(requestId, user)); } // Request a user's dat x-server - private CompletableFuture> requestData(@NotNull UUID requestId, @NotNull User user) { + private CompletableFuture> getNetworkedUserData(@NotNull UUID requestId, @NotNull User user) { final CompletableFuture> future = new CompletableFuture<>(); pendingRequests.put(requestId, future); plugin.runAsync(() -> { diff --git a/common/src/main/resources/locales/bg-bg.yml b/common/src/main/resources/locales/bg-bg.yml index c83ee7c7..475de908 100644 --- a/common/src/main/resources/locales/bg-bg.yml +++ b/common/src/main/resources/locales/bg-bg.yml @@ -42,6 +42,7 @@ locales: save_cause_death: 'death' save_cause_server_shutdown: 'server shutdown' save_cause_save_command: 'save command' + save_cause_dump_command: 'dump command' save_cause_inventory_command: 'inventory command' save_cause_enderchest_command: 'enderchest command' save_cause_backup_restore: 'backup restore' diff --git a/common/src/main/resources/locales/de-de.yml b/common/src/main/resources/locales/de-de.yml index bdbce728..b854971e 100644 --- a/common/src/main/resources/locales/de-de.yml +++ b/common/src/main/resources/locales/de-de.yml @@ -42,6 +42,7 @@ locales: save_cause_death: 'Tod' save_cause_server_shutdown: 'server gestoppt' save_cause_save_command: 'save command' + save_cause_dump_command: 'dump command' save_cause_inventory_command: 'inventar Befehl' save_cause_enderchest_command: 'enderchest Befehl' save_cause_backup_restore: 'backup wiederhergestellt' diff --git a/common/src/main/resources/locales/en-gb.yml b/common/src/main/resources/locales/en-gb.yml index 012bbddf..c6ad07b5 100644 --- a/common/src/main/resources/locales/en-gb.yml +++ b/common/src/main/resources/locales/en-gb.yml @@ -42,6 +42,7 @@ locales: save_cause_death: 'death' save_cause_server_shutdown: 'server shutdown' save_cause_save_command: 'save command' + save_cause_dump_command: 'dump command' save_cause_inventory_command: 'inventory command' save_cause_enderchest_command: 'enderchest command' save_cause_backup_restore: 'backup restore' diff --git a/common/src/main/resources/locales/es-es.yml b/common/src/main/resources/locales/es-es.yml index f6a80d32..261d69c2 100644 --- a/common/src/main/resources/locales/es-es.yml +++ b/common/src/main/resources/locales/es-es.yml @@ -42,6 +42,7 @@ locales: save_cause_death: 'death' save_cause_server_shutdown: 'server shutdown' save_cause_save_command: 'save command' + save_cause_dump_command: 'dump command' save_cause_inventory_command: 'inventory command' save_cause_enderchest_command: 'enderchest command' save_cause_backup_restore: 'backup restore' diff --git a/common/src/main/resources/locales/fr-fr.yml b/common/src/main/resources/locales/fr-fr.yml index 479a5c1f..624a0a11 100644 --- a/common/src/main/resources/locales/fr-fr.yml +++ b/common/src/main/resources/locales/fr-fr.yml @@ -42,6 +42,7 @@ locales: save_cause_death: 'mort' save_cause_server_shutdown: 'arrêt du serveur' save_cause_save_command: 'save command' + save_cause_dump_command: 'dump command' save_cause_inventory_command: 'commande d''inventaire' save_cause_enderchest_command: 'commande du coffre de l''Ender' save_cause_backup_restore: 'restauration de sauvegarde' diff --git a/common/src/main/resources/locales/id-id.yml b/common/src/main/resources/locales/id-id.yml index d8e4b4b2..818df314 100644 --- a/common/src/main/resources/locales/id-id.yml +++ b/common/src/main/resources/locales/id-id.yml @@ -42,6 +42,7 @@ locales: save_cause_death: 'kematian' save_cause_server_shutdown: 'pematian server' save_cause_save_command: 'save command' + save_cause_dump_command: 'dump command' save_cause_inventory_command: 'perintah inventaris' save_cause_enderchest_command: 'perintah enderchest' save_cause_backup_restore: 'pemulihan cadangan' diff --git a/common/src/main/resources/locales/it-it.yml b/common/src/main/resources/locales/it-it.yml index 69d6a685..67d7eae1 100644 --- a/common/src/main/resources/locales/it-it.yml +++ b/common/src/main/resources/locales/it-it.yml @@ -42,6 +42,7 @@ locales: save_cause_death: 'death' save_cause_server_shutdown: 'server shutdown' save_cause_save_command: 'save command' + save_cause_dump_command: 'dump command' save_cause_inventory_command: 'inventory command' save_cause_enderchest_command: 'enderchest command' save_cause_backup_restore: 'backup restore' diff --git a/common/src/main/resources/locales/ja-jp.yml b/common/src/main/resources/locales/ja-jp.yml index 0156c047..30bb8769 100644 --- a/common/src/main/resources/locales/ja-jp.yml +++ b/common/src/main/resources/locales/ja-jp.yml @@ -42,6 +42,7 @@ locales: save_cause_death: 'death' save_cause_server_shutdown: 'server shutdown' save_cause_save_command: 'save command' + save_cause_dump_command: 'dump command' save_cause_inventory_command: 'inventory command' save_cause_enderchest_command: 'enderchest command' save_cause_backup_restore: 'backup restore' diff --git a/common/src/main/resources/locales/ko-kr.yml b/common/src/main/resources/locales/ko-kr.yml index df2a3d41..f9991e90 100644 --- a/common/src/main/resources/locales/ko-kr.yml +++ b/common/src/main/resources/locales/ko-kr.yml @@ -42,6 +42,7 @@ locales: save_cause_death: 'death' save_cause_server_shutdown: 'server shutdown' save_cause_save_command: 'save command' + save_cause_dump_command: 'dump command' save_cause_inventory_command: 'inventory command' save_cause_enderchest_command: 'enderchest command' save_cause_backup_restore: 'backup restore' diff --git a/common/src/main/resources/locales/nl-nl.yml b/common/src/main/resources/locales/nl-nl.yml index 40120d92..c53faaf1 100644 --- a/common/src/main/resources/locales/nl-nl.yml +++ b/common/src/main/resources/locales/nl-nl.yml @@ -42,6 +42,7 @@ locales: save_cause_death: 'death' save_cause_server_shutdown: 'server shutdown' save_cause_save_command: 'save command' + save_cause_dump_command: 'dump command' save_cause_inventory_command: 'inventory command' save_cause_enderchest_command: 'enderchest command' save_cause_backup_restore: 'backup restore' diff --git a/common/src/main/resources/locales/pt-br.yml b/common/src/main/resources/locales/pt-br.yml index 4b9d0368..8e5cfc83 100644 --- a/common/src/main/resources/locales/pt-br.yml +++ b/common/src/main/resources/locales/pt-br.yml @@ -42,6 +42,7 @@ locales: save_cause_death: 'death' save_cause_server_shutdown: 'server shutdown' save_cause_save_command: 'save command' + save_cause_dump_command: 'dump command' save_cause_inventory_command: 'inventory command' save_cause_enderchest_command: 'enderchest command' save_cause_backup_restore: 'backup restore' diff --git a/common/src/main/resources/locales/ru-ru.yml b/common/src/main/resources/locales/ru-ru.yml index 0ba928a3..d06c8f4f 100644 --- a/common/src/main/resources/locales/ru-ru.yml +++ b/common/src/main/resources/locales/ru-ru.yml @@ -42,6 +42,7 @@ locales: save_cause_death: 'смерть' save_cause_server_shutdown: 'отключение сервера' save_cause_save_command: 'save command' + save_cause_dump_command: 'dump command' save_cause_inventory_command: 'команда inventory' save_cause_enderchest_command: 'команда enderchest' save_cause_backup_restore: 'восстановление из снимка' diff --git a/common/src/main/resources/locales/tr-tr.yml b/common/src/main/resources/locales/tr-tr.yml index 9b89b829..00004c66 100644 --- a/common/src/main/resources/locales/tr-tr.yml +++ b/common/src/main/resources/locales/tr-tr.yml @@ -42,6 +42,7 @@ locales: save_cause_death: 'ölüm' save_cause_server_shutdown: 'sunucu kapatma' save_cause_save_command: 'save command' + save_cause_dump_command: 'dump command' save_cause_inventory_command: 'envanter komutu' save_cause_enderchest_command: 'ender sandığı komutu' save_cause_backup_restore: 'yedek geri yükleme' diff --git a/common/src/main/resources/locales/uk-ua.yml b/common/src/main/resources/locales/uk-ua.yml index 5f8723c9..03fb2127 100644 --- a/common/src/main/resources/locales/uk-ua.yml +++ b/common/src/main/resources/locales/uk-ua.yml @@ -42,6 +42,7 @@ locales: save_cause_death: 'death' save_cause_server_shutdown: 'server shutdown' save_cause_save_command: 'save command' + save_cause_dump_command: 'dump command' save_cause_inventory_command: 'inventory command' save_cause_enderchest_command: 'enderchest command' save_cause_backup_restore: 'backup restore' diff --git a/common/src/main/resources/locales/zh-cn.yml b/common/src/main/resources/locales/zh-cn.yml index 63aeb810..67d0f7ff 100644 --- a/common/src/main/resources/locales/zh-cn.yml +++ b/common/src/main/resources/locales/zh-cn.yml @@ -42,6 +42,7 @@ locales: save_cause_death: '死亡' save_cause_server_shutdown: '服务器关闭' save_cause_save_command: 'save command' + save_cause_dump_command: 'dump command' save_cause_inventory_command: '背包命令' save_cause_enderchest_command: '末影箱命令' save_cause_backup_restore: '备份还原' diff --git a/common/src/main/resources/locales/zh-tw.yml b/common/src/main/resources/locales/zh-tw.yml index 2cc441f8..aad649b8 100644 --- a/common/src/main/resources/locales/zh-tw.yml +++ b/common/src/main/resources/locales/zh-tw.yml @@ -42,6 +42,7 @@ locales: save_cause_death: '死亡' save_cause_server_shutdown: '伺服器關閉' save_cause_save_command: 'save command' + save_cause_dump_command: 'dump command' save_cause_inventory_command: '背包指令' save_cause_enderchest_command: '終界箱指令' save_cause_backup_restore: '備份還原'