diff --git a/build.gradle b/build.gradle index d2449330..adb3b425 100644 --- a/build.gradle +++ b/build.gradle @@ -34,6 +34,7 @@ allprojects { maven { url 'https://hub.spigotmc.org/nexus/content/repositories/snapshots/' } maven { url 'https://repo.minebench.de/' } maven { url 'https://repo.alessiodp.com/releases/' } + maven { url 'https://repo.mattstudios.me/artifactory/public/' } maven { url 'https://jitpack.io' } maven { url 'https://libraries.minecraft.net/' } } diff --git a/bukkit/build.gradle b/bukkit/build.gradle index 36c20272..de84b40a 100644 --- a/bukkit/build.gradle +++ b/bukkit/build.gradle @@ -5,6 +5,7 @@ dependencies { implementation 'net.william278:hsldataconverter:1.0' implementation 'me.lucko:commodore:2.2' implementation 'net.kyori:adventure-platform-bukkit:4.1.2' + implementation 'dev.triumphteam:triumph-gui:3.1.3' compileOnly 'org.spigotmc:spigot-api:1.16.5-R0.1-SNAPSHOT' compileOnly 'commons-io:commons-io:2.11.0' @@ -34,6 +35,7 @@ shadowJar { relocate 'me.lucko.commodore', 'net.william278.husksync.libraries.commodore' relocate 'net.byteflux.libby', 'net.william278.husksync.libraries.libby' relocate 'org.bstats', 'net.william278.husksync.libraries.bstats' + relocate 'dev.triumphteam.gui', 'net.william278.husksync.libraries.triumphgui' relocate 'net.william278.mpdbconverter', 'net.william278.husksync.libraries.mpdbconverter' relocate 'net.william278.hslmigrator', 'net.william278.husksync.libraries.hslconverter' relocate 'net.william278.annotaml', 'net.william278.husksync.libraries.annotaml' diff --git a/bukkit/src/main/java/net/william278/husksync/BukkitHuskSync.java b/bukkit/src/main/java/net/william278/husksync/BukkitHuskSync.java index e0ffacbd..9fa24325 100644 --- a/bukkit/src/main/java/net/william278/husksync/BukkitHuskSync.java +++ b/bukkit/src/main/java/net/william278/husksync/BukkitHuskSync.java @@ -13,7 +13,6 @@ import net.william278.husksync.data.DataAdapter; import net.william278.husksync.data.JsonDataAdapter; import net.william278.husksync.database.Database; import net.william278.husksync.database.MySqlDatabase; -import net.william278.husksync.editor.DataEditor; import net.william278.husksync.event.BukkitEventCannon; import net.william278.husksync.event.EventCannon; import net.william278.husksync.hook.PlanHook; @@ -59,7 +58,6 @@ public class BukkitHuskSync extends JavaPlugin implements HuskSync { private ResourceReader resourceReader; private EventListener eventListener; private DataAdapter dataAdapter; - private DataEditor dataEditor; private EventCannon eventCannon; private Settings settings; private Locales locales; @@ -114,9 +112,6 @@ public class BukkitHuskSync extends JavaPlugin implements HuskSync { // Prepare event cannon eventCannon = new BukkitEventCannon(); - // Prepare data editor - dataEditor = new DataEditor(locales); - // Prepare migrators availableMigrators = new ArrayList<>(); availableMigrators.add(new LegacyMigrator(this)); @@ -258,11 +253,6 @@ public class BukkitHuskSync extends JavaPlugin implements HuskSync { return dataAdapter; } - @Override - public @NotNull DataEditor getDataEditor() { - return dataEditor; - } - @Override public @NotNull EventCannon getEventCannon() { return eventCannon; diff --git a/bukkit/src/main/java/net/william278/husksync/data/BukkitSerializer.java b/bukkit/src/main/java/net/william278/husksync/data/BukkitSerializer.java index 03828631..5ee42eb1 100644 --- a/bukkit/src/main/java/net/william278/husksync/data/BukkitSerializer.java +++ b/bukkit/src/main/java/net/william278/husksync/data/BukkitSerializer.java @@ -27,8 +27,6 @@ public class BukkitSerializer { public static CompletableFuture serializeItemStackArray(@NotNull ItemStack[] inventoryContents) throws DataSerializationException { return CompletableFuture.supplyAsync(() -> { - BukkitHuskSync.getInstance().getLoggingAdapter().debug("[HS] Serializing inventory contents"); - // Return an empty string if there is no inventory item data to serialize if (inventoryContents.length == 0) { return ""; diff --git a/bukkit/src/main/java/net/william278/husksync/listener/BukkitEventListener.java b/bukkit/src/main/java/net/william278/husksync/listener/BukkitEventListener.java index 27623cb0..b7efd3de 100644 --- a/bukkit/src/main/java/net/william278/husksync/listener/BukkitEventListener.java +++ b/bukkit/src/main/java/net/william278/husksync/listener/BukkitEventListener.java @@ -3,9 +3,7 @@ package net.william278.husksync.listener; import net.william278.husksync.BukkitHuskSync; import net.william278.husksync.data.BukkitInventoryMap; import net.william278.husksync.data.BukkitSerializer; -import net.william278.husksync.data.DataSerializationException; import net.william278.husksync.data.ItemData; -import net.william278.husksync.editor.ItemEditorMenuType; import net.william278.husksync.player.BukkitPlayer; import net.william278.husksync.player.OnlineUser; import org.bukkit.Bukkit; @@ -18,8 +16,6 @@ import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.event.entity.EntityDamageEvent; import org.bukkit.event.entity.EntityPickupItemEvent; import org.bukkit.event.entity.PlayerDeathEvent; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.event.inventory.InventoryCloseEvent; import org.bukkit.event.inventory.InventoryOpenEvent; import org.bukkit.event.player.PlayerDropItemEvent; import org.bukkit.event.player.PlayerInteractEvent; @@ -29,9 +25,7 @@ import org.bukkit.event.world.WorldSaveEvent; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; -import java.util.Arrays; import java.util.concurrent.CompletableFuture; -import java.util.logging.Level; import java.util.stream.Collectors; public class BukkitEventListener extends EventListener implements Listener { @@ -61,27 +55,6 @@ public class BukkitEventListener extends EventListener implements Listener { .collect(Collectors.toList()))); } - @EventHandler(ignoreCancelled = true) - public void onInventoryClose(@NotNull InventoryCloseEvent event) { - CompletableFuture.runAsync(() -> { - if (event.getPlayer() instanceof Player player) { - final OnlineUser user = BukkitPlayer.adapt(player); - plugin.getDataEditor().getEditingInventoryData(user).ifPresent(menu -> { - try { - BukkitSerializer.serializeItemStackArray(Arrays.copyOf(event.getInventory().getContents(), - menu.itemEditorMenuType == ItemEditorMenuType.INVENTORY_VIEWER - ? player.getInventory().getSize() - : player.getEnderChest().getSize())).thenAccept( - serializedInventory -> super.handleMenuClose(user, new ItemData(serializedInventory))); - } catch (DataSerializationException e) { - plugin.getLoggingAdapter().log(Level.SEVERE, - "Failed to serialize inventory data during menu close", e); - } - }); - } - }); - } - @EventHandler(ignoreCancelled = true) public void onPlayerDeath(PlayerDeathEvent event) { final OnlineUser user = BukkitPlayer.adapt(event.getEntity()); @@ -137,13 +110,6 @@ public class BukkitEventListener extends EventListener implements Listener { event.setCancelled(cancelPlayerEvent(BukkitPlayer.adapt(event.getPlayer()))); } - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) - public void onInventoryClick(@NotNull InventoryClickEvent event) { - if (event.getWhoClicked() instanceof Player player) { - event.setCancelled(cancelInventoryClick(BukkitPlayer.adapt(player))); - } - } - @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) public void onInventoryOpen(@NotNull InventoryOpenEvent event) { if (event.getPlayer() instanceof Player player) { diff --git a/bukkit/src/main/java/net/william278/husksync/player/BukkitPlayer.java b/bukkit/src/main/java/net/william278/husksync/player/BukkitPlayer.java index 49f340ff..1ef48112 100644 --- a/bukkit/src/main/java/net/william278/husksync/player/BukkitPlayer.java +++ b/bukkit/src/main/java/net/william278/husksync/player/BukkitPlayer.java @@ -2,12 +2,14 @@ package net.william278.husksync.player; import de.themoep.minedown.adventure.MineDown; import de.themoep.minedown.adventure.MineDownParser; +import dev.triumphteam.gui.builder.gui.StorageBuilder; +import dev.triumphteam.gui.guis.Gui; +import dev.triumphteam.gui.guis.StorageGui; import net.kyori.adventure.audience.Audience; import net.william278.desertwell.Version; import net.william278.husksync.BukkitHuskSync; import net.william278.husksync.config.Settings; import net.william278.husksync.data.*; -import net.william278.husksync.editor.ItemEditorMenu; import org.bukkit.*; import org.bukkit.advancement.Advancement; import org.bukkit.advancement.AdvancementProgress; @@ -15,7 +17,7 @@ import org.bukkit.attribute.Attribute; import org.bukkit.entity.EntityType; import org.bukkit.entity.Player; import org.bukkit.event.player.PlayerTeleportEvent; -import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; import org.bukkit.persistence.PersistentDataContainer; import org.bukkit.persistence.PersistentDataType; import org.bukkit.potion.PotionEffect; @@ -565,16 +567,49 @@ public class BukkitPlayer extends OnlineUser { } @Override - public void showMenu(@NotNull ItemEditorMenu menu) { - BukkitSerializer.deserializeItemStackArray(menu.itemData.serializedItems).thenAccept(inventoryContents -> { - //todo show the inventory properly - /*final Inventory inventory = Bukkit.createInventory(player, menu.itemEditorMenuType.slotCount, - BaseComponent.toLegacyText(menu.menuTitle.toComponent()));*/ - final Inventory inventory = Bukkit.createInventory(player, menu.itemEditorMenuType.slotCount, - menu.menuTitle.message()); - inventory.setContents(inventoryContents); - Bukkit.getScheduler().runTask(BukkitHuskSync.getInstance(), () -> player.openInventory(inventory)); + public CompletableFuture> showMenu(@NotNull ItemData itemData, boolean editable, + int rows, @NotNull MineDown title) { + final CompletableFuture> updatedData = new CompletableFuture<>(); + + // Deserialize the item data to be shown and show it in a triumph GUI + BukkitSerializer.deserializeItemStackArray(itemData.serializedItems).thenAccept(items -> { + try { + // Build the GUI and populate with items + final int itemCount = items.length; + final StorageBuilder guiBuilder = Gui.storage().title(title.toComponent()).rows(rows).disableAllInteractions(); + final StorageGui gui = editable ? guiBuilder.enableAllInteractions().create() : guiBuilder.create(); + for (int i = 0; i < itemCount; i++) { + if (items[i] != null) { + gui.getInventory().setItem(i, items[i]); + } + } + + // Complete the future with updated data (if editable) when the GUI is closed + gui.setCloseGuiAction(event -> { + if (!editable) { + updatedData.complete(Optional.empty()); + return; + } + + // Get and save the updated items + final ItemStack[] updatedItems = Arrays.copyOf(event.getPlayer().getOpenInventory() + .getTopInventory().getContents().clone(), itemCount); + BukkitSerializer.serializeItemStackArray(updatedItems).thenAccept(serializedItems -> { + if (serializedItems.equals(itemData.serializedItems)) { + updatedData.complete(Optional.empty()); + return; + } + updatedData.complete(Optional.of(new ItemData(serializedItems))); + }); + }); + + // Display the GUI (synchronously; on the main server thread) + Bukkit.getScheduler().runTask(BukkitHuskSync.getInstance(), () -> gui.open(player)); + } catch (Exception e) { + e.printStackTrace(); + } }); + return updatedData; } @Override diff --git a/common/src/main/java/net/william278/husksync/HuskSync.java b/common/src/main/java/net/william278/husksync/HuskSync.java index 153c5565..24bf9530 100644 --- a/common/src/main/java/net/william278/husksync/HuskSync.java +++ b/common/src/main/java/net/william278/husksync/HuskSync.java @@ -4,7 +4,6 @@ import net.william278.desertwell.UpdateChecker; import net.william278.husksync.config.Locales; import net.william278.husksync.config.Settings; import net.william278.husksync.data.DataAdapter; -import net.william278.husksync.editor.DataEditor; import net.william278.husksync.database.Database; import net.william278.husksync.event.EventCannon; import net.william278.husksync.migrator.Migrator; @@ -71,14 +70,6 @@ public interface HuskSync { @NotNull DataAdapter getDataAdapter(); - /** - * Returns the data editor implementation - * - * @return the {@link DataEditor} implementation - */ - @NotNull - DataEditor getDataEditor(); - /** * Returns the event firing cannon * 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 acf0f293..b1c2a3fb 100644 --- a/common/src/main/java/net/william278/husksync/command/EnderChestCommand.java +++ b/common/src/main/java/net/william278/husksync/command/EnderChestCommand.java @@ -1,15 +1,17 @@ package net.william278.husksync.command; +import de.themoep.minedown.adventure.MineDown; import net.william278.husksync.HuskSync; -import net.william278.husksync.data.*; -import net.william278.husksync.editor.ItemEditorMenu; +import net.william278.husksync.data.DataSaveCause; +import net.william278.husksync.data.UserData; +import net.william278.husksync.data.UserDataBuilder; +import net.william278.husksync.data.UserDataSnapshot; import net.william278.husksync.player.OnlineUser; import net.william278.husksync.player.User; import org.jetbrains.annotations.NotNull; -import java.text.DateFormat; +import java.text.SimpleDateFormat; import java.util.List; -import java.util.Locale; import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; @@ -53,37 +55,42 @@ public class EnderChestCommand extends CommandBase implements TabCompletable { } private void showEnderChestMenu(@NotNull OnlineUser player, @NotNull UserDataSnapshot userDataSnapshot, - @NotNull User dataOwner, final boolean allowEdit) { + @NotNull User dataOwner, boolean allowEdit) { CompletableFuture.runAsync(() -> { final UserData data = userDataSnapshot.userData(); - final ItemEditorMenu menu = ItemEditorMenu.createEnderChestMenu( - data.getEnderChest().orElse(ItemData.empty()), - dataOwner, player, plugin.getLocales(), allowEdit); - plugin.getLocales().getLocale("viewing_ender_chest_of", dataOwner.username, - DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, Locale.getDefault()) - .format(userDataSnapshot.versionTimestamp())) - .ifPresent(player::sendMessage); - plugin.getDataEditor().openItemEditorMenu(player, menu).thenAccept(enderChestDataOnClose -> { - if (!menu.canEdit) { - return; - } + data.getEnderChest().ifPresent(itemData -> { + // Show message + plugin.getLocales().getLocale("ender_chest_viewer_opened", dataOwner.username, + new SimpleDateFormat("MMM dd yyyy, HH:mm:ss.sss") + .format(userDataSnapshot.versionTimestamp())) + .ifPresent(player::sendMessage); + + // Show inventory menu + player.showMenu(itemData, allowEdit, 3, plugin.getLocales() + .getLocale("ender_chest_viewer_menu_title", dataOwner.username) + .orElse(new MineDown("Ender Chest Viewer"))).thenAccept(dataOnClose -> { + if (dataOnClose.isEmpty() || !allowEdit) { + return; + } - final UserDataBuilder builder = UserData.builder(plugin.getMinecraftVersion()); - data.getStatus().ifPresent(builder::setStatus); - data.getInventory().ifPresent(builder::setInventory); - data.getAdvancements().ifPresent(builder::setAdvancements); - data.getLocation().ifPresent(builder::setLocation); - data.getPersistentDataContainer().ifPresent(builder::setPersistentDataContainer); - data.getStatistics().ifPresent(builder::setStatistics); - data.getPotionEffects().ifPresent(builder::setPotionEffects); - builder.setEnderChest(enderChestDataOnClose); - final UserData updatedUserData = builder.build(); + // Create the updated data + final UserDataBuilder builder = UserData.builder(plugin.getMinecraftVersion()); + data.getStatus().ifPresent(builder::setStatus); + data.getAdvancements().ifPresent(builder::setAdvancements); + data.getLocation().ifPresent(builder::setLocation); + data.getPersistentDataContainer().ifPresent(builder::setPersistentDataContainer); + data.getStatistics().ifPresent(builder::setStatistics); + data.getPotionEffects().ifPresent(builder::setPotionEffects); + data.getInventory().ifPresent(builder::setInventory); + builder.setEnderChest(dataOnClose.get()); - plugin.getDatabase().setUserData(dataOwner, updatedUserData, DataSaveCause.ENDERCHEST_COMMAND).join(); - plugin.getRedisManager().sendUserDataUpdate(dataOwner, updatedUserData).join(); + // Set the updated data + final UserData updatedUserData = builder.build(); + plugin.getDatabase().setUserData(dataOwner, updatedUserData, DataSaveCause.INVENTORY_COMMAND).join(); + plugin.getRedisManager().sendUserDataUpdate(dataOwner, updatedUserData).join(); + }); }); }); - } @Override 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 665c0bd1..d3fab93d 100644 --- a/common/src/main/java/net/william278/husksync/command/InventoryCommand.java +++ b/common/src/main/java/net/william278/husksync/command/InventoryCommand.java @@ -1,15 +1,17 @@ package net.william278.husksync.command; +import de.themoep.minedown.adventure.MineDown; import net.william278.husksync.HuskSync; -import net.william278.husksync.data.*; -import net.william278.husksync.editor.ItemEditorMenu; +import net.william278.husksync.data.DataSaveCause; +import net.william278.husksync.data.UserData; +import net.william278.husksync.data.UserDataBuilder; +import net.william278.husksync.data.UserDataSnapshot; import net.william278.husksync.player.OnlineUser; import net.william278.husksync.player.User; import org.jetbrains.annotations.NotNull; -import java.text.DateFormat; +import java.text.SimpleDateFormat; import java.util.List; -import java.util.Locale; import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; @@ -56,31 +58,40 @@ public class InventoryCommand extends CommandBase implements TabCompletable { @NotNull User dataOwner, boolean allowEdit) { CompletableFuture.runAsync(() -> { final UserData data = userDataSnapshot.userData(); - final ItemEditorMenu menu = ItemEditorMenu.createInventoryMenu( - data.getInventory().orElse(ItemData.empty()), - dataOwner, player, plugin.getLocales(), allowEdit); - plugin.getLocales().getLocale("viewing_inventory_of", dataOwner.username, - DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, Locale.getDefault()) - .format(userDataSnapshot.versionTimestamp())) - .ifPresent(player::sendMessage); - plugin.getDataEditor().openItemEditorMenu(player, menu).thenAccept(inventoryDataOnClose -> { - if (!menu.canEdit) { - return; - } + data.getInventory().ifPresent(itemData -> { + // Show message + plugin.getLocales().getLocale("inventory_viewer_opened", dataOwner.username, + new SimpleDateFormat("MMM dd yyyy, HH:mm:ss.sss") + .format(userDataSnapshot.versionTimestamp())) + .ifPresent(player::sendMessage); + + // Show inventory menu + player.showMenu(itemData, allowEdit, 5, plugin.getLocales() + .getLocale("inventory_viewer_menu_title", dataOwner.username) + .orElse(new MineDown("Inventory Viewer"))) + .thenAccept(dataOnClose -> { + if (dataOnClose.isEmpty() || !allowEdit) { + return; + } + + plugin.getLoggingAdapter().debug("Inventory data changed, updating user, etc!"); - final UserDataBuilder builder = UserData.builder(plugin.getMinecraftVersion()); - data.getStatus().ifPresent(builder::setStatus); - data.getEnderChest().ifPresent(builder::setEnderChest); - data.getAdvancements().ifPresent(builder::setAdvancements); - data.getLocation().ifPresent(builder::setLocation); - data.getPersistentDataContainer().ifPresent(builder::setPersistentDataContainer); - data.getStatistics().ifPresent(builder::setStatistics); - data.getPotionEffects().ifPresent(builder::setPotionEffects); - builder.setEnderChest(inventoryDataOnClose); - final UserData updatedUserData = builder.build(); + // Create the updated data + final UserDataBuilder builder = UserData.builder(plugin.getMinecraftVersion()); + data.getStatus().ifPresent(builder::setStatus); + data.getAdvancements().ifPresent(builder::setAdvancements); + data.getLocation().ifPresent(builder::setLocation); + data.getPersistentDataContainer().ifPresent(builder::setPersistentDataContainer); + data.getStatistics().ifPresent(builder::setStatistics); + data.getPotionEffects().ifPresent(builder::setPotionEffects); + data.getEnderChest().ifPresent(builder::setEnderChest); + builder.setInventory(dataOnClose.get()); - plugin.getDatabase().setUserData(dataOwner, updatedUserData, DataSaveCause.INVENTORY_COMMAND).join(); - plugin.getRedisManager().sendUserDataUpdate(dataOwner, updatedUserData).join(); + // Set the updated data + final UserData updatedUserData = builder.build(); + plugin.getDatabase().setUserData(dataOwner, updatedUserData, DataSaveCause.INVENTORY_COMMAND).join(); + plugin.getRedisManager().sendUserDataUpdate(dataOwner, updatedUserData).join(); + }); }); }); } 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 66f14eda..9e1763f4 100644 --- a/common/src/main/java/net/william278/husksync/command/UserDataCommand.java +++ b/common/src/main/java/net/william278/husksync/command/UserDataCommand.java @@ -3,6 +3,7 @@ package net.william278.husksync.command; import net.william278.husksync.HuskSync; import net.william278.husksync.data.DataSaveCause; import net.william278.husksync.data.UserData; +import net.william278.husksync.util.DataSnapshotList; import net.william278.husksync.player.OnlineUser; import net.william278.husksync.util.DataDumper; import org.jetbrains.annotations.NotNull; @@ -45,31 +46,32 @@ public class UserDataCommand extends CommandBase implements TabCompletable { if (args.length >= 3) { try { final UUID versionUuid = UUID.fromString(args[2]); - CompletableFuture.runAsync(() -> plugin.getDatabase().getUserByName(username.toLowerCase()).thenAccept( - optionalUser -> optionalUser.ifPresentOrElse( - user -> plugin.getDatabase().getUserData(user, versionUuid).thenAccept(data -> - data.ifPresentOrElse(userData -> plugin.getDataEditor() - .displayDataOverview(player, userData, user), - () -> plugin.getLocales().getLocale("error_invalid_version_uuid") - .ifPresent(player::sendMessage))), - () -> plugin.getLocales().getLocale("error_invalid_player") - .ifPresent(player::sendMessage)))); + CompletableFuture.runAsync(() -> plugin.getDatabase() + .getUserByName(username.toLowerCase()) + .thenAccept(optionalUser -> optionalUser + .ifPresentOrElse(user -> plugin.getDatabase().getUserData(user, versionUuid) + .thenAccept(data -> data.ifPresentOrElse( + userData -> userData.displayDataOverview(player, user, plugin.getLocales()), + () -> plugin.getLocales().getLocale("error_invalid_version_uuid") + .ifPresent(player::sendMessage))), + () -> plugin.getLocales().getLocale("error_invalid_player") + .ifPresent(player::sendMessage)))); } catch (IllegalArgumentException e) { plugin.getLocales().getLocale("error_invalid_syntax", "/userdata view [version_uuid]") .ifPresent(player::sendMessage); } } else { - CompletableFuture.runAsync(() -> plugin.getDatabase().getUserByName(username.toLowerCase()).thenAccept( - optionalUser -> optionalUser.ifPresentOrElse( - user -> plugin.getDatabase().getCurrentUserData(user).thenAccept( - latestData -> latestData.ifPresentOrElse( - userData -> plugin.getDataEditor() - .displayDataOverview(player, userData, user), - () -> plugin.getLocales().getLocale("error_no_data_to_display") - .ifPresent(player::sendMessage))), - () -> plugin.getLocales().getLocale("error_invalid_player") - .ifPresent(player::sendMessage)))); + CompletableFuture.runAsync(() -> plugin.getDatabase() + .getUserByName(username.toLowerCase()) + .thenAccept(optionalUser -> optionalUser + .ifPresentOrElse(user -> plugin.getDatabase().getCurrentUserData(user) + .thenAccept(latestData -> latestData.ifPresentOrElse( + userData -> userData.displayDataOverview(player, user, plugin.getLocales()), + () -> plugin.getLocales().getLocale("error_no_data_to_display") + .ifPresent(player::sendMessage))), + () -> plugin.getLocales().getLocale("error_invalid_player") + .ifPresent(player::sendMessage)))); } } case "list" -> { @@ -84,8 +86,9 @@ public class UserDataCommand extends CommandBase implements TabCompletable { return; } final String username = args[1]; - CompletableFuture.runAsync(() -> plugin.getDatabase().getUserByName(username.toLowerCase()).thenAccept( - optionalUser -> optionalUser.ifPresentOrElse( + CompletableFuture.runAsync(() -> plugin.getDatabase() + .getUserByName(username.toLowerCase()) + .thenAccept(optionalUser -> optionalUser.ifPresentOrElse( user -> plugin.getDatabase().getUserData(user).thenAccept(dataList -> { // Check if there is data to display if (dataList.isEmpty()) { @@ -107,8 +110,9 @@ public class UserDataCommand extends CommandBase implements TabCompletable { } } - // Show list - plugin.getDataEditor().displayDataSnapshotList(player, dataList, user, page); + // Show the list to the player + DataSnapshotList.create(dataList, user, plugin.getLocales()) + .displayPage(player, page); }), () -> plugin.getLocales().getLocale("error_invalid_player") .ifPresent(player::sendMessage)))); @@ -128,8 +132,9 @@ public class UserDataCommand extends CommandBase implements TabCompletable { final String username = args[1]; try { final UUID versionUuid = UUID.fromString(args[2]); - CompletableFuture.runAsync(() -> plugin.getDatabase().getUserByName(username.toLowerCase()).thenAccept( - optionalUser -> optionalUser.ifPresentOrElse( + CompletableFuture.runAsync(() -> plugin.getDatabase() + .getUserByName(username.toLowerCase()) + .thenAccept(optionalUser -> optionalUser.ifPresentOrElse( user -> plugin.getDatabase().deleteUserData(user, versionUuid).thenAccept(deleted -> { if (deleted) { plugin.getLocales().getLocale("data_deleted", @@ -166,8 +171,9 @@ public class UserDataCommand extends CommandBase implements TabCompletable { final String username = args[1]; try { final UUID versionUuid = UUID.fromString(args[2]); - CompletableFuture.runAsync(() -> plugin.getDatabase().getUserByName(username.toLowerCase()).thenAccept( - optionalUser -> optionalUser.ifPresentOrElse( + CompletableFuture.runAsync(() -> plugin.getDatabase() + .getUserByName(username.toLowerCase()) + .thenAccept(optionalUser -> optionalUser.ifPresentOrElse( user -> plugin.getDatabase().getUserData(user, versionUuid).thenAccept(data -> { if (data.isEmpty()) { plugin.getLocales().getLocale("error_invalid_version_uuid") @@ -212,8 +218,9 @@ public class UserDataCommand extends CommandBase implements TabCompletable { final String username = args[1]; try { final UUID versionUuid = UUID.fromString(args[2]); - CompletableFuture.runAsync(() -> plugin.getDatabase().getUserByName(username.toLowerCase()).thenAccept( - optionalUser -> optionalUser.ifPresentOrElse( + CompletableFuture.runAsync(() -> plugin.getDatabase() + .getUserByName(username.toLowerCase()) + .thenAccept(optionalUser -> optionalUser.ifPresentOrElse( user -> plugin.getDatabase().getUserData(user, versionUuid).thenAccept( optionalUserData -> optionalUserData.ifPresentOrElse(userData -> { if (userData.pinned()) { @@ -259,8 +266,9 @@ public class UserDataCommand extends CommandBase implements TabCompletable { final String username = args[1]; try { final UUID versionUuid = UUID.fromString(args[2]); - CompletableFuture.runAsync(() -> plugin.getDatabase().getUserByName(username.toLowerCase()).thenAccept( - optionalUser -> optionalUser.ifPresentOrElse( + CompletableFuture.runAsync(() -> plugin.getDatabase() + .getUserByName(username.toLowerCase()) + .thenAccept(optionalUser -> optionalUser.ifPresentOrElse( user -> plugin.getDatabase().getUserData(user, versionUuid).thenAccept( optionalUserData -> optionalUserData.ifPresentOrElse(userData -> { try { diff --git a/common/src/main/java/net/william278/husksync/data/UserDataSnapshot.java b/common/src/main/java/net/william278/husksync/data/UserDataSnapshot.java index 1e1ba6ab..ce1982cc 100644 --- a/common/src/main/java/net/william278/husksync/data/UserDataSnapshot.java +++ b/common/src/main/java/net/william278/husksync/data/UserDataSnapshot.java @@ -1,8 +1,15 @@ package net.william278.husksync.data; +import net.william278.husksync.command.Permission; +import net.william278.husksync.config.Locales; +import net.william278.husksync.player.OnlineUser; +import net.william278.husksync.player.User; import org.jetbrains.annotations.NotNull; +import java.text.SimpleDateFormat; import java.util.Date; +import java.util.List; +import java.util.StringJoiner; import java.util.UUID; /** @@ -32,6 +39,82 @@ public record UserDataSnapshot(@NotNull UUID versionUUID, @NotNull Date versionT DataSaveCause.API, false, userData); } + /** + * Display a menu in chat to an {@link OnlineUser} about this {@link UserDataSnapshot} for a {@link User dataOwner} + * + * @param user The {@link OnlineUser} to display the menu to + * @param dataOwner The {@link User} whose data this snapshot captures a state of + * @param locales The {@link Locales} to use for displaying the menu + */ + public void displayDataOverview(@NotNull OnlineUser user, @NotNull User dataOwner, @NotNull Locales locales) { + // Title message, timestamp, owner and cause. + locales.getLocale("data_manager_title", versionUUID().toString().split("-")[0], + versionUUID().toString(), dataOwner.username, dataOwner.uuid.toString()) + .ifPresent(user::sendMessage); + locales.getLocale("data_manager_timestamp", + new SimpleDateFormat("MMM dd yyyy, HH:mm:ss.sss").format(versionTimestamp())) + .ifPresent(user::sendMessage); + if (pinned()) { + locales.getLocale("data_manager_pinned").ifPresent(user::sendMessage); + } + locales.getLocale("data_manager_cause", cause().name().toLowerCase().replaceAll("_", " ")) + .ifPresent(user::sendMessage); + + // User status data, if present in the snapshot + userData().getStatus() + .flatMap(statusData -> locales.getLocale("data_manager_status", + Integer.toString((int) statusData.health), + Integer.toString((int) statusData.maxHealth), + Integer.toString(statusData.hunger), + Integer.toString(statusData.expLevel), + statusData.gameMode.toLowerCase())) + .ifPresent(user::sendMessage); + + // Advancement and statistic data, if both are present in the snapshot + userData().getAdvancements() + .flatMap(advancementData -> userData().getStatistics() + .flatMap(statisticsData -> locales.getLocale("data_manager_advancements_statistics", + Integer.toString(advancementData.size()), + generateAdvancementPreview(advancementData, locales), + String.format("%.2f", (((statisticsData.untypedStatistics.getOrDefault( + "PLAY_ONE_MINUTE", 0)) / 20d) / 60d) / 60d)))) + .ifPresent(user::sendMessage); + + if (user.hasPermission(Permission.COMMAND_INVENTORY.node) + && user.hasPermission(Permission.COMMAND_ENDER_CHEST.node)) { + locales.getLocale("data_manager_item_buttons", dataOwner.username, versionUUID().toString()) + .ifPresent(user::sendMessage); + } + if (user.hasPermission(Permission.COMMAND_USER_DATA_MANAGE.node)) { + locales.getLocale("data_manager_management_buttons", dataOwner.username, versionUUID().toString()) + .ifPresent(user::sendMessage); + } + if (user.hasPermission(Permission.COMMAND_USER_DATA_DUMP.node)) { + locales.getLocale("data_manager_system_buttons", dataOwner.username, versionUUID().toString()) + .ifPresent(user::sendMessage); + } + } + + @NotNull + private String generateAdvancementPreview(@NotNull List advancementData, @NotNull Locales locales) { + final StringJoiner joiner = new StringJoiner("\n"); + final List advancementsToPreview = advancementData.stream().filter(dataItem -> + !dataItem.key.startsWith("minecraft:recipes/")).toList(); + final int PREVIEW_SIZE = 8; + for (int i = 0; i < advancementsToPreview.size(); i++) { + joiner.add(advancementsToPreview.get(i).key); + if (i >= PREVIEW_SIZE) { + break; + } + } + final int remainingAdvancements = advancementsToPreview.size() - PREVIEW_SIZE; + if (remainingAdvancements > 0) { + joiner.add(locales.getRawLocale("data_manager_advancements_preview_remaining", + Integer.toString(remainingAdvancements)).orElse("+" + remainingAdvancements + "…")); + } + return joiner.toString(); + } + /** * Compare UserData by creation timestamp * diff --git a/common/src/main/java/net/william278/husksync/editor/DataEditor.java b/common/src/main/java/net/william278/husksync/editor/DataEditor.java deleted file mode 100644 index 32c7c5c4..00000000 --- a/common/src/main/java/net/william278/husksync/editor/DataEditor.java +++ /dev/null @@ -1,181 +0,0 @@ -package net.william278.husksync.editor; - -import net.william278.husksync.command.Permission; -import net.william278.husksync.config.Locales; -import net.william278.husksync.data.AdvancementData; -import net.william278.husksync.data.ItemData; -import net.william278.husksync.data.UserDataSnapshot; -import net.william278.husksync.player.OnlineUser; -import net.william278.husksync.player.User; -import org.jetbrains.annotations.NotNull; - -import java.text.SimpleDateFormat; -import java.util.*; -import java.util.concurrent.CompletableFuture; - -/** - * Provides methods for displaying and editing user data - */ -public class DataEditor { - - /** - * Map of currently open inventory and ender chest data editors - */ - @NotNull - protected final HashMap openInventoryMenus; - - private final Locales locales; - - public DataEditor(@NotNull Locales locales) { - this.openInventoryMenus = new HashMap<>(); - this.locales = locales; - } - - /** - * Open an inventory or ender chest editor menu - * - * @param user The online user to open the editor for - * @param itemEditorMenu The {@link ItemEditorMenu} to open - * @see ItemEditorMenu#createInventoryMenu(ItemData, User, OnlineUser, Locales, boolean) - * @see ItemEditorMenu#createEnderChestMenu(ItemData, User, OnlineUser, Locales, boolean) - */ - public CompletableFuture openItemEditorMenu(@NotNull OnlineUser user, - @NotNull ItemEditorMenu itemEditorMenu) { - this.openInventoryMenus.put(user.uuid, itemEditorMenu); - return itemEditorMenu.showInventory(user); - } - - /** - * Close an inventory or ender chest editor menu - * - * @param user The online user to close the editor for - * @param itemData the {@link ItemData} contained within the menu at the time of closing - */ - public void closeInventoryMenu(@NotNull OnlineUser user, @NotNull ItemData itemData) { - if (this.openInventoryMenus.containsKey(user.uuid)) { - this.openInventoryMenus.get(user.uuid).closeInventory(itemData); - } - this.openInventoryMenus.remove(user.uuid); - } - - /** - * Returns whether edits to the inventory or ender chest menu are allowed - * - * @param user The online user with an inventory open to check - * @return {@code true} if edits to the inventory or ender chest menu are allowed; {@code false} otherwise, including if they don't have an inventory open - */ - public boolean cancelMenuEdit(@NotNull OnlineUser user) { - if (this.openInventoryMenus.containsKey(user.uuid)) { - return !this.openInventoryMenus.get(user.uuid).canEdit; - } - return false; - } - - /** - * Display a chat menu detailing information about {@link UserDataSnapshot} - * - * @param user The online user to display the message to - * @param userData The {@link UserDataSnapshot} to display information about - * @param dataOwner The {@link User} who owns the {@link UserDataSnapshot} - */ - public void displayDataOverview(@NotNull OnlineUser user, @NotNull UserDataSnapshot userData, - @NotNull User dataOwner) { - // Title message, timestamp, owner and cause. - locales.getLocale("data_manager_title", - userData.versionUUID().toString().split("-")[0], - userData.versionUUID().toString(), - dataOwner.username, - dataOwner.uuid.toString()) - .ifPresent(user::sendMessage); - locales.getLocale("data_manager_timestamp", - new SimpleDateFormat("MMM dd yyyy, HH:mm:ss.sss").format(userData.versionTimestamp())) - .ifPresent(user::sendMessage); - if (userData.pinned()) { - locales.getLocale("data_manager_pinned").ifPresent(user::sendMessage); - } - locales.getLocale("data_manager_cause", - userData.cause().name().toLowerCase().replaceAll("_", " ")) - .ifPresent(user::sendMessage); - - // User status data, if present in the snapshot - userData.userData().getStatus() - .flatMap(statusData -> locales.getLocale("data_manager_status", - Integer.toString((int) statusData.health), - Integer.toString((int) statusData.maxHealth), - Integer.toString(statusData.hunger), - Integer.toString(statusData.expLevel), - statusData.gameMode.toLowerCase())) - .ifPresent(user::sendMessage); - - // Advancement and statistic data, if both are present in the snapshot - userData.userData().getAdvancements() - .flatMap(advancementData -> userData.userData().getStatistics() - .flatMap(statisticsData -> locales.getLocale("data_manager_advancements_statistics", - Integer.toString(advancementData.size()), - generateAdvancementPreview(advancementData), - String.format("%.2f", (((statisticsData.untypedStatistics.getOrDefault( - "PLAY_ONE_MINUTE", 0)) / 20d) / 60d) / 60d)))) - .ifPresent(user::sendMessage); - - if (user.hasPermission(Permission.COMMAND_INVENTORY.node) - && user.hasPermission(Permission.COMMAND_ENDER_CHEST.node)) { - locales.getLocale("data_manager_item_buttons", - dataOwner.username, userData.versionUUID().toString()) - .ifPresent(user::sendMessage); - } - if (user.hasPermission(Permission.COMMAND_USER_DATA_MANAGE.node)) { - locales.getLocale("data_manager_management_buttons", - dataOwner.username, userData.versionUUID().toString()) - .ifPresent(user::sendMessage); - } - if (user.hasPermission(Permission.COMMAND_USER_DATA_DUMP.node)) { - locales.getLocale("data_manager_system_buttons", - dataOwner.username, userData.versionUUID().toString()) - .ifPresent(user::sendMessage); - } - } - - @NotNull - private String generateAdvancementPreview(@NotNull List advancementData) { - final StringJoiner joiner = new StringJoiner("\n"); - final List advancementsToPreview = advancementData.stream().filter(dataItem -> - !dataItem.key.startsWith("minecraft:recipes/")).toList(); - final int PREVIEW_SIZE = 8; - for (int i = 0; i < advancementsToPreview.size(); i++) { - joiner.add(advancementsToPreview.get(i).key); - if (i >= PREVIEW_SIZE) { - break; - } - } - final int remainingAdvancements = advancementsToPreview.size() - PREVIEW_SIZE; - if (remainingAdvancements > 0) { - joiner.add(locales.getRawLocale("data_manager_advancements_preview_remaining", - Integer.toString(remainingAdvancements)).orElse("+" + remainingAdvancements + "…")); - } - return joiner.toString(); - } - - /** - * Display a paginated chat list of {@link UserDataSnapshot}s - * - * @param user The online user to display the message to - * @param userDataList The list of {@link UserDataSnapshot}s to display - * @param dataOwner The {@link User} who owns the {@link UserDataSnapshot}s - * @param page The page of the list to display - */ - public void displayDataSnapshotList(@NotNull OnlineUser user, @NotNull List userDataList, - @NotNull User dataOwner, final int page) { - DataSnapshotList.create(userDataList, dataOwner, locales).displayPage(user, page); - } - - /** - * Returns whether the user has an inventory editor menu open - * - * @param user {@link OnlineUser} to check - * @return {@code true} if the user has an inventory editor open; {@code false} otherwise - */ - public Optional getEditingInventoryData(@NotNull OnlineUser user) { - return this.openInventoryMenus.containsKey(user.uuid) ? Optional.of(this.openInventoryMenus.get(user.uuid)) - : Optional.empty(); - } -} diff --git a/common/src/main/java/net/william278/husksync/editor/ItemEditorMenu.java b/common/src/main/java/net/william278/husksync/editor/ItemEditorMenu.java deleted file mode 100644 index 36b984f8..00000000 --- a/common/src/main/java/net/william278/husksync/editor/ItemEditorMenu.java +++ /dev/null @@ -1,56 +0,0 @@ -package net.william278.husksync.editor; - -import de.themoep.minedown.adventure.MineDown; -import net.william278.husksync.command.Permission; -import net.william278.husksync.config.Locales; -import net.william278.husksync.data.ItemData; -import net.william278.husksync.player.OnlineUser; -import net.william278.husksync.player.User; -import org.jetbrains.annotations.NotNull; - -import java.util.concurrent.CompletableFuture; - -public class ItemEditorMenu { - - public final ItemData itemData; - public final ItemEditorMenuType itemEditorMenuType; - public final MineDown menuTitle; - public final boolean canEdit; - - private CompletableFuture inventoryDataCompletableFuture; - - private ItemEditorMenu(@NotNull ItemData itemData, ItemEditorMenuType itemEditorMenuType, - @NotNull MineDown menuTitle, boolean canEdit) { - this.itemData = itemData; - this.menuTitle = menuTitle; - this.itemEditorMenuType = itemEditorMenuType; - this.canEdit = canEdit; - } - - public CompletableFuture showInventory(@NotNull OnlineUser user) { - inventoryDataCompletableFuture = new CompletableFuture<>(); - user.showMenu(this); - return inventoryDataCompletableFuture; - } - - public void closeInventory(@NotNull ItemData itemData) { - inventoryDataCompletableFuture.complete(itemData); - } - - public static ItemEditorMenu createInventoryMenu(@NotNull ItemData itemData, @NotNull User dataOwner, - @NotNull OnlineUser viewer, @NotNull Locales locales, - boolean canEdit) { - return new ItemEditorMenu(itemData, ItemEditorMenuType.INVENTORY_VIEWER, - locales.getLocale(ItemEditorMenuType.INVENTORY_VIEWER.localeKey, dataOwner.username).orElse(new MineDown("")), - viewer.hasPermission(Permission.COMMAND_INVENTORY_EDIT.node) && canEdit); - } - - public static ItemEditorMenu createEnderChestMenu(@NotNull ItemData itemData, @NotNull User dataOwner, - @NotNull OnlineUser viewer, @NotNull Locales locales, - boolean canEdit) { - return new ItemEditorMenu(itemData, ItemEditorMenuType.ENDER_CHEST_VIEWER, - locales.getLocale(ItemEditorMenuType.ENDER_CHEST_VIEWER.localeKey, dataOwner.username).orElse(new MineDown("")), - viewer.hasPermission(Permission.COMMAND_ENDER_CHEST_EDIT.node) && canEdit); - } - -} diff --git a/common/src/main/java/net/william278/husksync/editor/ItemEditorMenuType.java b/common/src/main/java/net/william278/husksync/editor/ItemEditorMenuType.java deleted file mode 100644 index 95f4b5e0..00000000 --- a/common/src/main/java/net/william278/husksync/editor/ItemEditorMenuType.java +++ /dev/null @@ -1,16 +0,0 @@ -package net.william278.husksync.editor; - -import org.jetbrains.annotations.NotNull; - -public enum ItemEditorMenuType { - INVENTORY_VIEWER(45, "inventory_viewer_menu_title"), - ENDER_CHEST_VIEWER(27, "ender_chest_viewer_menu_title"); - - public final int slotCount; - final String localeKey; - - ItemEditorMenuType(int slotCount, @NotNull String localeKey) { - this.slotCount = slotCount; - this.localeKey = localeKey; - } -} diff --git a/common/src/main/java/net/william278/husksync/listener/EventListener.java b/common/src/main/java/net/william278/husksync/listener/EventListener.java index 751338e0..04334eeb 100644 --- a/common/src/main/java/net/william278/husksync/listener/EventListener.java +++ b/common/src/main/java/net/william278/husksync/listener/EventListener.java @@ -3,7 +3,6 @@ package net.william278.husksync.listener; import net.william278.husksync.HuskSync; import net.william278.husksync.data.DataSaveCause; import net.william278.husksync.data.ItemData; -import net.william278.husksync.editor.ItemEditorMenuType; import net.william278.husksync.player.OnlineUser; import org.jetbrains.annotations.NotNull; @@ -194,30 +193,6 @@ public abstract class EventListener { })); } - /** - * Handle an inventory menu closing - * - * @param user The user who closed the menu - * @param menuInventory Serialized {@link ItemData} containing the inventory contents - * @implNote The size of the serialized {@link ItemData} array is determined by the {@link ItemEditorMenuType} of the closed inventory - */ - protected final void handleMenuClose(@NotNull OnlineUser user, @NotNull ItemData menuInventory) { - if (disabling) { - return; - } - plugin.getDataEditor().closeInventoryMenu(user, menuInventory); - } - - /** - * Determine whether an inventory click should be cancelled - * - * @param user {@link OnlineUser} performing the event - * @return Whether the event should be cancelled - */ - protected final boolean cancelInventoryClick(@NotNull OnlineUser user) { - return plugin.getDataEditor().cancelMenuEdit(user) || cancelPlayerEvent(user); - } - /** * Determine whether a player event should be cancelled * diff --git a/common/src/main/java/net/william278/husksync/player/OnlineUser.java b/common/src/main/java/net/william278/husksync/player/OnlineUser.java index 82fcc349..452b1a5d 100644 --- a/common/src/main/java/net/william278/husksync/player/OnlineUser.java +++ b/common/src/main/java/net/william278/husksync/player/OnlineUser.java @@ -1,13 +1,12 @@ package net.william278.husksync.player; import de.themoep.minedown.adventure.MineDown; +import net.william278.desertwell.Version; import net.william278.husksync.config.Settings; import net.william278.husksync.data.*; -import net.william278.husksync.editor.ItemEditorMenu; import net.william278.husksync.event.EventCannon; import net.william278.husksync.event.PreSyncEvent; import net.william278.husksync.util.Logger; -import net.william278.desertwell.Version; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; @@ -207,12 +206,8 @@ public abstract class OnlineUser extends User { */ public abstract boolean hasPermission(@NotNull String node); - /** - * Show the player a {@link ItemEditorMenu} GUI - * - * @param menu The {@link ItemEditorMenu} interface to show - */ - public abstract void showMenu(@NotNull ItemEditorMenu menu); + public abstract CompletableFuture> showMenu(@NotNull ItemData itemData, boolean editable, + int rows, @NotNull MineDown title); /** * Returns true if the player is dead diff --git a/common/src/main/java/net/william278/husksync/editor/DataSnapshotList.java b/common/src/main/java/net/william278/husksync/util/DataSnapshotList.java similarity index 85% rename from common/src/main/java/net/william278/husksync/editor/DataSnapshotList.java rename to common/src/main/java/net/william278/husksync/util/DataSnapshotList.java index 1aa539c2..96137667 100644 --- a/common/src/main/java/net/william278/husksync/editor/DataSnapshotList.java +++ b/common/src/main/java/net/william278/husksync/util/DataSnapshotList.java @@ -1,4 +1,4 @@ -package net.william278.husksync.editor; +package net.william278.husksync.util; import net.william278.husksync.config.Locales; import net.william278.husksync.data.UserDataSnapshot; @@ -7,9 +7,8 @@ import net.william278.husksync.player.User; import net.william278.paginedown.PaginatedList; import org.jetbrains.annotations.NotNull; -import java.text.DateFormat; +import java.text.SimpleDateFormat; import java.util.List; -import java.util.Locale; import java.util.concurrent.atomic.AtomicInteger; /** @@ -29,8 +28,8 @@ public class DataSnapshotList { this.paginatedList = PaginatedList.of(snapshots.stream() .map(snapshot -> locales.getRawLocale("data_list_item", getNumberIcon(snapshotNumber.getAndIncrement()), - DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, - Locale.getDefault()).format(snapshot.versionTimestamp()), + new SimpleDateFormat("MMM dd yyyy, HH:mm:ss.sss") + .format(snapshot.versionTimestamp()), snapshot.versionUUID().toString().split("-")[0], snapshot.versionUUID().toString(), snapshot.cause().name().toLowerCase().replaceAll("_", " "), @@ -53,8 +52,8 @@ public class DataSnapshotList { * @param locales The {@link Locales} instance * @return A new {@link DataSnapshotList}, to be viewed with {@link #displayPage(OnlineUser, int)} */ - protected static DataSnapshotList create(@NotNull List snapshots, @NotNull User user, - @NotNull Locales locales) { + public static DataSnapshotList create(@NotNull List snapshots, @NotNull User user, + @NotNull Locales locales) { return new DataSnapshotList(snapshots, user, locales); } @@ -77,7 +76,7 @@ public class DataSnapshotList { * @param onlineUser The online user to display the message to * @param page The page number to display */ - protected void displayPage(@NotNull OnlineUser onlineUser, int page) { + public void displayPage(@NotNull OnlineUser onlineUser, int page) { onlineUser.sendMessage(paginatedList.getNearestValidPage(page)); } diff --git a/common/src/main/resources/locales/en-gb.yml b/common/src/main/resources/locales/en-gb.yml index 63e463e1..9aa39350 100644 --- a/common/src/main/resources/locales/en-gb.yml +++ b/common/src/main/resources/locales/en-gb.yml @@ -10,8 +10,8 @@ error_no_data_to_display: '[Error:](#ff3300) [Could not find any user data to di error_invalid_version_uuid: '[Error:](#ff3300) [Could not find any user data for that version UUID.](#ff7e5e)' inventory_viewer_menu_title: '&0%1%''s Inventory' ender_chest_viewer_menu_title: '&0%1%''s Ender Chest' -inventory_viewer_opened: '[Viewing snapshot of](#00fb9a) [%1%](#00fb9a bold) [''s inventory as of ⌚ %2%](#00fb9a)' -ender_chest_viewer_opened: '[Viewing snapshot of](#00fb9a) [%1%](#00fb9a bold) [''s Ender Chest as of ⌚ %2%](#00fb9a)' +inventory_viewer_opened: '[Viewing snapshot of](#00fb9a) [%1%](#00fb9a bold)[''s inventory as of ⌚ %2%](#00fb9a)' +ender_chest_viewer_opened: '[Viewing snapshot of](#00fb9a) [%1%](#00fb9a bold)[''s Ender Chest as of ⌚ %2%](#00fb9a)' data_update_complete: '[🔔 Your data has been updated!](#00fb9a)' data_update_failed: '[🔔 Failed to update your data! Please contact an administrator.](#ff7e5e)' data_manager_title: '[Viewing user data snapshot](#00fb9a) [%1%](#00fb9a show_text=&7Version UUID:\n&8%2%) [for](#00fb9a) [%3%](#00fb9a bold show_text=&7Player UUID:\n&8%4%)[:](#00fb9a)' diff --git a/common/src/main/resources/locales/ja-jp.yml b/common/src/main/resources/locales/ja-jp.yml index dee14e71..4d537f1b 100644 --- a/common/src/main/resources/locales/ja-jp.yml +++ b/common/src/main/resources/locales/ja-jp.yml @@ -10,8 +10,8 @@ error_no_data_to_display: '[Error:](#ff3300) [Could not find any user data to di error_invalid_version_uuid: '[Error:](#ff3300) [Could not find any user data for that version UUID.](#ff7e5e)' inventory_viewer_menu_title: '&0%1%''s Inventory' ender_chest_viewer_menu_title: '&0%1%''s Ender Chest' -inventory_viewer_opened: '[Viewing snapshot of](#00fb9a) [%1%](#00fb9a bold) [''s inventory as of ⌚ %2%](#00fb9a)' -ender_chest_viewer_opened: '[Viewing snapshot of](#00fb9a) [%1%](#00fb9a bold) [''s Ender Chest as of ⌚ %2%](#00fb9a)' +inventory_viewer_opened: '[Viewing snapshot of](#00fb9a) [%1%](#00fb9a bold)[''s inventory as of ⌚ %2%](#00fb9a)' +ender_chest_viewer_opened: '[Viewing snapshot of](#00fb9a) [%1%](#00fb9a bold)[''s Ender Chest as of ⌚ %2%](#00fb9a)' data_update_complete: '[🔔 Your data has been updated!](#00fb9a)' data_update_failed: '[🔔 Failed to update your data! Please contact an administrator.](#ff7e5e)' data_manager_title: '[Viewing user data snapshot](#00fb9a) [%1%](#00fb9a show_text=&7Version UUID:\n&8%2%) [for](#00fb9a) [%3%](#00fb9a bold show_text=&7Player UUID:\n&8%4%)[:](#00fb9a)' diff --git a/common/src/main/resources/locales/uk-ua.yml b/common/src/main/resources/locales/uk-ua.yml index a87a6a79..bd9e045d 100644 --- a/common/src/main/resources/locales/uk-ua.yml +++ b/common/src/main/resources/locales/uk-ua.yml @@ -10,8 +10,8 @@ error_no_data_to_display: '[Error:](#ff3300) [Could not find any user data to di error_invalid_version_uuid: '[Error:](#ff3300) [Could not find any user data for that version UUID.](#ff7e5e)' inventory_viewer_menu_title: '&0%1%''s Inventory' ender_chest_viewer_menu_title: '&0%1%''s Ender Chest' -inventory_viewer_opened: '[Viewing snapshot of](#00fb9a) [%1%](#00fb9a bold) [''s inventory as of ⌚ %2%](#00fb9a)' -ender_chest_viewer_opened: '[Viewing snapshot of](#00fb9a) [%1%](#00fb9a bold) [''s Ender Chest as of ⌚ %2%](#00fb9a)' +inventory_viewer_opened: '[Viewing snapshot of](#00fb9a) [%1%](#00fb9a bold)[''s inventory as of ⌚ %2%](#00fb9a)' +ender_chest_viewer_opened: '[Viewing snapshot of](#00fb9a) [%1%](#00fb9a bold)[''s Ender Chest as of ⌚ %2%](#00fb9a)' data_update_complete: '[🔔 Your data has been updated!](#00fb9a)' data_update_failed: '[🔔 Failed to update your data! Please contact an administrator.](#ff7e5e)' data_manager_title: '[Viewing user data snapshot](#00fb9a) [%1%](#00fb9a show_text=&7Version UUID:\n&8%2%) [for](#00fb9a) [%3%](#00fb9a bold show_text=&7Player UUID:\n&8%4%)[:](#00fb9a)' diff --git a/common/src/test/java/net/william278/husksync/player/DummyPlayer.java b/common/src/test/java/net/william278/husksync/player/DummyPlayer.java index 7b5c8be1..94a13cfd 100644 --- a/common/src/test/java/net/william278/husksync/player/DummyPlayer.java +++ b/common/src/test/java/net/william278/husksync/player/DummyPlayer.java @@ -3,14 +3,10 @@ package net.william278.husksync.player; import de.themoep.minedown.adventure.MineDown; import net.william278.husksync.config.Settings; import net.william278.husksync.data.*; -import net.william278.husksync.editor.ItemEditorMenu; import net.william278.desertwell.Version; import org.jetbrains.annotations.NotNull; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.UUID; +import java.util.*; import java.util.concurrent.CompletableFuture; public class DummyPlayer extends OnlineUser { @@ -152,10 +148,13 @@ public class DummyPlayer extends OnlineUser { } @Override - public void showMenu(@NotNull ItemEditorMenu menu) { + public CompletableFuture> showMenu(@NotNull ItemData itemData, boolean editable, + int rows, @NotNull MineDown title) { // do nothing + return CompletableFuture.completedFuture(Optional.empty()); } + @Override public boolean isDead() { return false;