TriumphGUI for menus, fix missing inv/echest view message, fix data saving despite no updates, close #42

feat/data-edit-commands
William 2 years ago
parent 394b8ff1d1
commit 43cd367ca3

@ -34,6 +34,7 @@ allprojects {
maven { url 'https://hub.spigotmc.org/nexus/content/repositories/snapshots/' } maven { url 'https://hub.spigotmc.org/nexus/content/repositories/snapshots/' }
maven { url 'https://repo.minebench.de/' } maven { url 'https://repo.minebench.de/' }
maven { url 'https://repo.alessiodp.com/releases/' } maven { url 'https://repo.alessiodp.com/releases/' }
maven { url 'https://repo.mattstudios.me/artifactory/public/' }
maven { url 'https://jitpack.io' } maven { url 'https://jitpack.io' }
maven { url 'https://libraries.minecraft.net/' } maven { url 'https://libraries.minecraft.net/' }
} }

@ -5,6 +5,7 @@ dependencies {
implementation 'net.william278:hsldataconverter:1.0' implementation 'net.william278:hsldataconverter:1.0'
implementation 'me.lucko:commodore:2.2' implementation 'me.lucko:commodore:2.2'
implementation 'net.kyori:adventure-platform-bukkit:4.1.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 'org.spigotmc:spigot-api:1.16.5-R0.1-SNAPSHOT'
compileOnly 'commons-io:commons-io:2.11.0' compileOnly 'commons-io:commons-io:2.11.0'
@ -34,6 +35,7 @@ shadowJar {
relocate 'me.lucko.commodore', 'net.william278.husksync.libraries.commodore' relocate 'me.lucko.commodore', 'net.william278.husksync.libraries.commodore'
relocate 'net.byteflux.libby', 'net.william278.husksync.libraries.libby' relocate 'net.byteflux.libby', 'net.william278.husksync.libraries.libby'
relocate 'org.bstats', 'net.william278.husksync.libraries.bstats' 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.mpdbconverter', 'net.william278.husksync.libraries.mpdbconverter'
relocate 'net.william278.hslmigrator', 'net.william278.husksync.libraries.hslconverter' relocate 'net.william278.hslmigrator', 'net.william278.husksync.libraries.hslconverter'
relocate 'net.william278.annotaml', 'net.william278.husksync.libraries.annotaml' relocate 'net.william278.annotaml', 'net.william278.husksync.libraries.annotaml'

@ -13,7 +13,6 @@ import net.william278.husksync.data.DataAdapter;
import net.william278.husksync.data.JsonDataAdapter; import net.william278.husksync.data.JsonDataAdapter;
import net.william278.husksync.database.Database; import net.william278.husksync.database.Database;
import net.william278.husksync.database.MySqlDatabase; import net.william278.husksync.database.MySqlDatabase;
import net.william278.husksync.editor.DataEditor;
import net.william278.husksync.event.BukkitEventCannon; import net.william278.husksync.event.BukkitEventCannon;
import net.william278.husksync.event.EventCannon; import net.william278.husksync.event.EventCannon;
import net.william278.husksync.hook.PlanHook; import net.william278.husksync.hook.PlanHook;
@ -59,7 +58,6 @@ public class BukkitHuskSync extends JavaPlugin implements HuskSync {
private ResourceReader resourceReader; private ResourceReader resourceReader;
private EventListener eventListener; private EventListener eventListener;
private DataAdapter dataAdapter; private DataAdapter dataAdapter;
private DataEditor dataEditor;
private EventCannon eventCannon; private EventCannon eventCannon;
private Settings settings; private Settings settings;
private Locales locales; private Locales locales;
@ -114,9 +112,6 @@ public class BukkitHuskSync extends JavaPlugin implements HuskSync {
// Prepare event cannon // Prepare event cannon
eventCannon = new BukkitEventCannon(); eventCannon = new BukkitEventCannon();
// Prepare data editor
dataEditor = new DataEditor(locales);
// Prepare migrators // Prepare migrators
availableMigrators = new ArrayList<>(); availableMigrators = new ArrayList<>();
availableMigrators.add(new LegacyMigrator(this)); availableMigrators.add(new LegacyMigrator(this));
@ -258,11 +253,6 @@ public class BukkitHuskSync extends JavaPlugin implements HuskSync {
return dataAdapter; return dataAdapter;
} }
@Override
public @NotNull DataEditor getDataEditor() {
return dataEditor;
}
@Override @Override
public @NotNull EventCannon getEventCannon() { public @NotNull EventCannon getEventCannon() {
return eventCannon; return eventCannon;

@ -27,8 +27,6 @@ public class BukkitSerializer {
public static CompletableFuture<String> serializeItemStackArray(@NotNull ItemStack[] inventoryContents) public static CompletableFuture<String> serializeItemStackArray(@NotNull ItemStack[] inventoryContents)
throws DataSerializationException { throws DataSerializationException {
return CompletableFuture.supplyAsync(() -> { return CompletableFuture.supplyAsync(() -> {
BukkitHuskSync.getInstance().getLoggingAdapter().debug("[HS] Serializing inventory contents");
// Return an empty string if there is no inventory item data to serialize // Return an empty string if there is no inventory item data to serialize
if (inventoryContents.length == 0) { if (inventoryContents.length == 0) {
return ""; return "";

@ -3,9 +3,7 @@ package net.william278.husksync.listener;
import net.william278.husksync.BukkitHuskSync; import net.william278.husksync.BukkitHuskSync;
import net.william278.husksync.data.BukkitInventoryMap; import net.william278.husksync.data.BukkitInventoryMap;
import net.william278.husksync.data.BukkitSerializer; import net.william278.husksync.data.BukkitSerializer;
import net.william278.husksync.data.DataSerializationException;
import net.william278.husksync.data.ItemData; import net.william278.husksync.data.ItemData;
import net.william278.husksync.editor.ItemEditorMenuType;
import net.william278.husksync.player.BukkitPlayer; import net.william278.husksync.player.BukkitPlayer;
import net.william278.husksync.player.OnlineUser; import net.william278.husksync.player.OnlineUser;
import org.bukkit.Bukkit; 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.EntityDamageEvent;
import org.bukkit.event.entity.EntityPickupItemEvent; import org.bukkit.event.entity.EntityPickupItemEvent;
import org.bukkit.event.entity.PlayerDeathEvent; 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.inventory.InventoryOpenEvent;
import org.bukkit.event.player.PlayerDropItemEvent; import org.bukkit.event.player.PlayerDropItemEvent;
import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerInteractEvent;
@ -29,9 +25,7 @@ import org.bukkit.event.world.WorldSaveEvent;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.logging.Level;
import java.util.stream.Collectors; import java.util.stream.Collectors;
public class BukkitEventListener extends EventListener implements Listener { public class BukkitEventListener extends EventListener implements Listener {
@ -61,27 +55,6 @@ public class BukkitEventListener extends EventListener implements Listener {
.collect(Collectors.toList()))); .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) @EventHandler(ignoreCancelled = true)
public void onPlayerDeath(PlayerDeathEvent event) { public void onPlayerDeath(PlayerDeathEvent event) {
final OnlineUser user = BukkitPlayer.adapt(event.getEntity()); 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()))); 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) @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onInventoryOpen(@NotNull InventoryOpenEvent event) { public void onInventoryOpen(@NotNull InventoryOpenEvent event) {
if (event.getPlayer() instanceof Player player) { if (event.getPlayer() instanceof Player player) {

@ -2,12 +2,14 @@ package net.william278.husksync.player;
import de.themoep.minedown.adventure.MineDown; import de.themoep.minedown.adventure.MineDown;
import de.themoep.minedown.adventure.MineDownParser; 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.kyori.adventure.audience.Audience;
import net.william278.desertwell.Version; import net.william278.desertwell.Version;
import net.william278.husksync.BukkitHuskSync; import net.william278.husksync.BukkitHuskSync;
import net.william278.husksync.config.Settings; import net.william278.husksync.config.Settings;
import net.william278.husksync.data.*; import net.william278.husksync.data.*;
import net.william278.husksync.editor.ItemEditorMenu;
import org.bukkit.*; import org.bukkit.*;
import org.bukkit.advancement.Advancement; import org.bukkit.advancement.Advancement;
import org.bukkit.advancement.AdvancementProgress; import org.bukkit.advancement.AdvancementProgress;
@ -15,7 +17,7 @@ import org.bukkit.attribute.Attribute;
import org.bukkit.entity.EntityType; import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.player.PlayerTeleportEvent; 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.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType; import org.bukkit.persistence.PersistentDataType;
import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffect;
@ -565,16 +567,49 @@ public class BukkitPlayer extends OnlineUser {
} }
@Override @Override
public void showMenu(@NotNull ItemEditorMenu menu) { public CompletableFuture<Optional<ItemData>> showMenu(@NotNull ItemData itemData, boolean editable,
BukkitSerializer.deserializeItemStackArray(menu.itemData.serializedItems).thenAccept(inventoryContents -> { int rows, @NotNull MineDown title) {
//todo show the inventory properly final CompletableFuture<Optional<ItemData>> updatedData = new CompletableFuture<>();
/*final Inventory inventory = Bukkit.createInventory(player, menu.itemEditorMenuType.slotCount,
BaseComponent.toLegacyText(menu.menuTitle.toComponent()));*/ // Deserialize the item data to be shown and show it in a triumph GUI
final Inventory inventory = Bukkit.createInventory(player, menu.itemEditorMenuType.slotCount, BukkitSerializer.deserializeItemStackArray(itemData.serializedItems).thenAccept(items -> {
menu.menuTitle.message()); try {
inventory.setContents(inventoryContents); // Build the GUI and populate with items
Bukkit.getScheduler().runTask(BukkitHuskSync.getInstance(), () -> player.openInventory(inventory)); 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 @Override

@ -4,7 +4,6 @@ import net.william278.desertwell.UpdateChecker;
import net.william278.husksync.config.Locales; import net.william278.husksync.config.Locales;
import net.william278.husksync.config.Settings; import net.william278.husksync.config.Settings;
import net.william278.husksync.data.DataAdapter; import net.william278.husksync.data.DataAdapter;
import net.william278.husksync.editor.DataEditor;
import net.william278.husksync.database.Database; import net.william278.husksync.database.Database;
import net.william278.husksync.event.EventCannon; import net.william278.husksync.event.EventCannon;
import net.william278.husksync.migrator.Migrator; import net.william278.husksync.migrator.Migrator;
@ -71,14 +70,6 @@ public interface HuskSync {
@NotNull @NotNull
DataAdapter getDataAdapter(); DataAdapter getDataAdapter();
/**
* Returns the data editor implementation
*
* @return the {@link DataEditor} implementation
*/
@NotNull
DataEditor getDataEditor();
/** /**
* Returns the event firing cannon * Returns the event firing cannon
* *

@ -1,15 +1,17 @@
package net.william278.husksync.command; package net.william278.husksync.command;
import de.themoep.minedown.adventure.MineDown;
import net.william278.husksync.HuskSync; import net.william278.husksync.HuskSync;
import net.william278.husksync.data.*; import net.william278.husksync.data.DataSaveCause;
import net.william278.husksync.editor.ItemEditorMenu; 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.OnlineUser;
import net.william278.husksync.player.User; import net.william278.husksync.player.User;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.text.DateFormat; import java.text.SimpleDateFormat;
import java.util.List; import java.util.List;
import java.util.Locale;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors; 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, private void showEnderChestMenu(@NotNull OnlineUser player, @NotNull UserDataSnapshot userDataSnapshot,
@NotNull User dataOwner, final boolean allowEdit) { @NotNull User dataOwner, boolean allowEdit) {
CompletableFuture.runAsync(() -> { CompletableFuture.runAsync(() -> {
final UserData data = userDataSnapshot.userData(); final UserData data = userDataSnapshot.userData();
final ItemEditorMenu menu = ItemEditorMenu.createEnderChestMenu( data.getEnderChest().ifPresent(itemData -> {
data.getEnderChest().orElse(ItemData.empty()), // Show message
dataOwner, player, plugin.getLocales(), allowEdit); plugin.getLocales().getLocale("ender_chest_viewer_opened", dataOwner.username,
plugin.getLocales().getLocale("viewing_ender_chest_of", dataOwner.username, new SimpleDateFormat("MMM dd yyyy, HH:mm:ss.sss")
DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, Locale.getDefault())
.format(userDataSnapshot.versionTimestamp())) .format(userDataSnapshot.versionTimestamp()))
.ifPresent(player::sendMessage); .ifPresent(player::sendMessage);
plugin.getDataEditor().openItemEditorMenu(player, menu).thenAccept(enderChestDataOnClose -> {
if (!menu.canEdit) { // 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; return;
} }
// Create the updated data
final UserDataBuilder builder = UserData.builder(plugin.getMinecraftVersion()); final UserDataBuilder builder = UserData.builder(plugin.getMinecraftVersion());
data.getStatus().ifPresent(builder::setStatus); data.getStatus().ifPresent(builder::setStatus);
data.getInventory().ifPresent(builder::setInventory);
data.getAdvancements().ifPresent(builder::setAdvancements); data.getAdvancements().ifPresent(builder::setAdvancements);
data.getLocation().ifPresent(builder::setLocation); data.getLocation().ifPresent(builder::setLocation);
data.getPersistentDataContainer().ifPresent(builder::setPersistentDataContainer); data.getPersistentDataContainer().ifPresent(builder::setPersistentDataContainer);
data.getStatistics().ifPresent(builder::setStatistics); data.getStatistics().ifPresent(builder::setStatistics);
data.getPotionEffects().ifPresent(builder::setPotionEffects); data.getPotionEffects().ifPresent(builder::setPotionEffects);
builder.setEnderChest(enderChestDataOnClose); data.getInventory().ifPresent(builder::setInventory);
final UserData updatedUserData = builder.build(); builder.setEnderChest(dataOnClose.get());
plugin.getDatabase().setUserData(dataOwner, updatedUserData, DataSaveCause.ENDERCHEST_COMMAND).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(); plugin.getRedisManager().sendUserDataUpdate(dataOwner, updatedUserData).join();
}); });
}); });
});
} }
@Override @Override

@ -1,15 +1,17 @@
package net.william278.husksync.command; package net.william278.husksync.command;
import de.themoep.minedown.adventure.MineDown;
import net.william278.husksync.HuskSync; import net.william278.husksync.HuskSync;
import net.william278.husksync.data.*; import net.william278.husksync.data.DataSaveCause;
import net.william278.husksync.editor.ItemEditorMenu; 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.OnlineUser;
import net.william278.husksync.player.User; import net.william278.husksync.player.User;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.text.DateFormat; import java.text.SimpleDateFormat;
import java.util.List; import java.util.List;
import java.util.Locale;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -56,33 +58,42 @@ public class InventoryCommand extends CommandBase implements TabCompletable {
@NotNull User dataOwner, boolean allowEdit) { @NotNull User dataOwner, boolean allowEdit) {
CompletableFuture.runAsync(() -> { CompletableFuture.runAsync(() -> {
final UserData data = userDataSnapshot.userData(); final UserData data = userDataSnapshot.userData();
final ItemEditorMenu menu = ItemEditorMenu.createInventoryMenu( data.getInventory().ifPresent(itemData -> {
data.getInventory().orElse(ItemData.empty()), // Show message
dataOwner, player, plugin.getLocales(), allowEdit); plugin.getLocales().getLocale("inventory_viewer_opened", dataOwner.username,
plugin.getLocales().getLocale("viewing_inventory_of", dataOwner.username, new SimpleDateFormat("MMM dd yyyy, HH:mm:ss.sss")
DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, Locale.getDefault())
.format(userDataSnapshot.versionTimestamp())) .format(userDataSnapshot.versionTimestamp()))
.ifPresent(player::sendMessage); .ifPresent(player::sendMessage);
plugin.getDataEditor().openItemEditorMenu(player, menu).thenAccept(inventoryDataOnClose -> {
if (!menu.canEdit) { // 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; return;
} }
plugin.getLoggingAdapter().debug("Inventory data changed, updating user, etc!");
// Create the updated data
final UserDataBuilder builder = UserData.builder(plugin.getMinecraftVersion()); final UserDataBuilder builder = UserData.builder(plugin.getMinecraftVersion());
data.getStatus().ifPresent(builder::setStatus); data.getStatus().ifPresent(builder::setStatus);
data.getEnderChest().ifPresent(builder::setEnderChest);
data.getAdvancements().ifPresent(builder::setAdvancements); data.getAdvancements().ifPresent(builder::setAdvancements);
data.getLocation().ifPresent(builder::setLocation); data.getLocation().ifPresent(builder::setLocation);
data.getPersistentDataContainer().ifPresent(builder::setPersistentDataContainer); data.getPersistentDataContainer().ifPresent(builder::setPersistentDataContainer);
data.getStatistics().ifPresent(builder::setStatistics); data.getStatistics().ifPresent(builder::setStatistics);
data.getPotionEffects().ifPresent(builder::setPotionEffects); data.getPotionEffects().ifPresent(builder::setPotionEffects);
builder.setEnderChest(inventoryDataOnClose); data.getEnderChest().ifPresent(builder::setEnderChest);
final UserData updatedUserData = builder.build(); builder.setInventory(dataOnClose.get());
// Set the updated data
final UserData updatedUserData = builder.build();
plugin.getDatabase().setUserData(dataOwner, updatedUserData, DataSaveCause.INVENTORY_COMMAND).join(); plugin.getDatabase().setUserData(dataOwner, updatedUserData, DataSaveCause.INVENTORY_COMMAND).join();
plugin.getRedisManager().sendUserDataUpdate(dataOwner, updatedUserData).join(); plugin.getRedisManager().sendUserDataUpdate(dataOwner, updatedUserData).join();
}); });
}); });
});
} }
@Override @Override

@ -3,6 +3,7 @@ package net.william278.husksync.command;
import net.william278.husksync.HuskSync; import net.william278.husksync.HuskSync;
import net.william278.husksync.data.DataSaveCause; import net.william278.husksync.data.DataSaveCause;
import net.william278.husksync.data.UserData; import net.william278.husksync.data.UserData;
import net.william278.husksync.util.DataSnapshotList;
import net.william278.husksync.player.OnlineUser; import net.william278.husksync.player.OnlineUser;
import net.william278.husksync.util.DataDumper; import net.william278.husksync.util.DataDumper;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -45,11 +46,12 @@ public class UserDataCommand extends CommandBase implements TabCompletable {
if (args.length >= 3) { if (args.length >= 3) {
try { try {
final UUID versionUuid = UUID.fromString(args[2]); final UUID versionUuid = UUID.fromString(args[2]);
CompletableFuture.runAsync(() -> plugin.getDatabase().getUserByName(username.toLowerCase()).thenAccept( CompletableFuture.runAsync(() -> plugin.getDatabase()
optionalUser -> optionalUser.ifPresentOrElse( .getUserByName(username.toLowerCase())
user -> plugin.getDatabase().getUserData(user, versionUuid).thenAccept(data -> .thenAccept(optionalUser -> optionalUser
data.ifPresentOrElse(userData -> plugin.getDataEditor() .ifPresentOrElse(user -> plugin.getDatabase().getUserData(user, versionUuid)
.displayDataOverview(player, userData, user), .thenAccept(data -> data.ifPresentOrElse(
userData -> userData.displayDataOverview(player, user, plugin.getLocales()),
() -> plugin.getLocales().getLocale("error_invalid_version_uuid") () -> plugin.getLocales().getLocale("error_invalid_version_uuid")
.ifPresent(player::sendMessage))), .ifPresent(player::sendMessage))),
() -> plugin.getLocales().getLocale("error_invalid_player") () -> plugin.getLocales().getLocale("error_invalid_player")
@ -60,12 +62,12 @@ public class UserDataCommand extends CommandBase implements TabCompletable {
.ifPresent(player::sendMessage); .ifPresent(player::sendMessage);
} }
} else { } else {
CompletableFuture.runAsync(() -> plugin.getDatabase().getUserByName(username.toLowerCase()).thenAccept( CompletableFuture.runAsync(() -> plugin.getDatabase()
optionalUser -> optionalUser.ifPresentOrElse( .getUserByName(username.toLowerCase())
user -> plugin.getDatabase().getCurrentUserData(user).thenAccept( .thenAccept(optionalUser -> optionalUser
latestData -> latestData.ifPresentOrElse( .ifPresentOrElse(user -> plugin.getDatabase().getCurrentUserData(user)
userData -> plugin.getDataEditor() .thenAccept(latestData -> latestData.ifPresentOrElse(
.displayDataOverview(player, userData, user), userData -> userData.displayDataOverview(player, user, plugin.getLocales()),
() -> plugin.getLocales().getLocale("error_no_data_to_display") () -> plugin.getLocales().getLocale("error_no_data_to_display")
.ifPresent(player::sendMessage))), .ifPresent(player::sendMessage))),
() -> plugin.getLocales().getLocale("error_invalid_player") () -> plugin.getLocales().getLocale("error_invalid_player")
@ -84,8 +86,9 @@ public class UserDataCommand extends CommandBase implements TabCompletable {
return; return;
} }
final String username = args[1]; final String username = args[1];
CompletableFuture.runAsync(() -> plugin.getDatabase().getUserByName(username.toLowerCase()).thenAccept( CompletableFuture.runAsync(() -> plugin.getDatabase()
optionalUser -> optionalUser.ifPresentOrElse( .getUserByName(username.toLowerCase())
.thenAccept(optionalUser -> optionalUser.ifPresentOrElse(
user -> plugin.getDatabase().getUserData(user).thenAccept(dataList -> { user -> plugin.getDatabase().getUserData(user).thenAccept(dataList -> {
// Check if there is data to display // Check if there is data to display
if (dataList.isEmpty()) { if (dataList.isEmpty()) {
@ -107,8 +110,9 @@ public class UserDataCommand extends CommandBase implements TabCompletable {
} }
} }
// Show list // Show the list to the player
plugin.getDataEditor().displayDataSnapshotList(player, dataList, user, page); DataSnapshotList.create(dataList, user, plugin.getLocales())
.displayPage(player, page);
}), }),
() -> plugin.getLocales().getLocale("error_invalid_player") () -> plugin.getLocales().getLocale("error_invalid_player")
.ifPresent(player::sendMessage)))); .ifPresent(player::sendMessage))));
@ -128,8 +132,9 @@ public class UserDataCommand extends CommandBase implements TabCompletable {
final String username = args[1]; final String username = args[1];
try { try {
final UUID versionUuid = UUID.fromString(args[2]); final UUID versionUuid = UUID.fromString(args[2]);
CompletableFuture.runAsync(() -> plugin.getDatabase().getUserByName(username.toLowerCase()).thenAccept( CompletableFuture.runAsync(() -> plugin.getDatabase()
optionalUser -> optionalUser.ifPresentOrElse( .getUserByName(username.toLowerCase())
.thenAccept(optionalUser -> optionalUser.ifPresentOrElse(
user -> plugin.getDatabase().deleteUserData(user, versionUuid).thenAccept(deleted -> { user -> plugin.getDatabase().deleteUserData(user, versionUuid).thenAccept(deleted -> {
if (deleted) { if (deleted) {
plugin.getLocales().getLocale("data_deleted", plugin.getLocales().getLocale("data_deleted",
@ -166,8 +171,9 @@ public class UserDataCommand extends CommandBase implements TabCompletable {
final String username = args[1]; final String username = args[1];
try { try {
final UUID versionUuid = UUID.fromString(args[2]); final UUID versionUuid = UUID.fromString(args[2]);
CompletableFuture.runAsync(() -> plugin.getDatabase().getUserByName(username.toLowerCase()).thenAccept( CompletableFuture.runAsync(() -> plugin.getDatabase()
optionalUser -> optionalUser.ifPresentOrElse( .getUserByName(username.toLowerCase())
.thenAccept(optionalUser -> optionalUser.ifPresentOrElse(
user -> plugin.getDatabase().getUserData(user, versionUuid).thenAccept(data -> { user -> plugin.getDatabase().getUserData(user, versionUuid).thenAccept(data -> {
if (data.isEmpty()) { if (data.isEmpty()) {
plugin.getLocales().getLocale("error_invalid_version_uuid") plugin.getLocales().getLocale("error_invalid_version_uuid")
@ -212,8 +218,9 @@ public class UserDataCommand extends CommandBase implements TabCompletable {
final String username = args[1]; final String username = args[1];
try { try {
final UUID versionUuid = UUID.fromString(args[2]); final UUID versionUuid = UUID.fromString(args[2]);
CompletableFuture.runAsync(() -> plugin.getDatabase().getUserByName(username.toLowerCase()).thenAccept( CompletableFuture.runAsync(() -> plugin.getDatabase()
optionalUser -> optionalUser.ifPresentOrElse( .getUserByName(username.toLowerCase())
.thenAccept(optionalUser -> optionalUser.ifPresentOrElse(
user -> plugin.getDatabase().getUserData(user, versionUuid).thenAccept( user -> plugin.getDatabase().getUserData(user, versionUuid).thenAccept(
optionalUserData -> optionalUserData.ifPresentOrElse(userData -> { optionalUserData -> optionalUserData.ifPresentOrElse(userData -> {
if (userData.pinned()) { if (userData.pinned()) {
@ -259,8 +266,9 @@ public class UserDataCommand extends CommandBase implements TabCompletable {
final String username = args[1]; final String username = args[1];
try { try {
final UUID versionUuid = UUID.fromString(args[2]); final UUID versionUuid = UUID.fromString(args[2]);
CompletableFuture.runAsync(() -> plugin.getDatabase().getUserByName(username.toLowerCase()).thenAccept( CompletableFuture.runAsync(() -> plugin.getDatabase()
optionalUser -> optionalUser.ifPresentOrElse( .getUserByName(username.toLowerCase())
.thenAccept(optionalUser -> optionalUser.ifPresentOrElse(
user -> plugin.getDatabase().getUserData(user, versionUuid).thenAccept( user -> plugin.getDatabase().getUserData(user, versionUuid).thenAccept(
optionalUserData -> optionalUserData.ifPresentOrElse(userData -> { optionalUserData -> optionalUserData.ifPresentOrElse(userData -> {
try { try {

@ -1,8 +1,15 @@
package net.william278.husksync.data; 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 org.jetbrains.annotations.NotNull;
import java.text.SimpleDateFormat;
import java.util.Date; import java.util.Date;
import java.util.List;
import java.util.StringJoiner;
import java.util.UUID; import java.util.UUID;
/** /**
@ -32,6 +39,82 @@ public record UserDataSnapshot(@NotNull UUID versionUUID, @NotNull Date versionT
DataSaveCause.API, false, userData); 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> advancementData, @NotNull Locales locales) {
final StringJoiner joiner = new StringJoiner("\n");
final List<AdvancementData> 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 * Compare UserData by creation timestamp
* *

@ -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<UUID, ItemEditorMenu> 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<ItemData> 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> advancementData) {
final StringJoiner joiner = new StringJoiner("\n");
final List<AdvancementData> 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<UserDataSnapshot> 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<ItemEditorMenu> getEditingInventoryData(@NotNull OnlineUser user) {
return this.openInventoryMenus.containsKey(user.uuid) ? Optional.of(this.openInventoryMenus.get(user.uuid))
: Optional.empty();
}
}

@ -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<ItemData> 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<ItemData> 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);
}
}

@ -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;
}
}

@ -3,7 +3,6 @@ package net.william278.husksync.listener;
import net.william278.husksync.HuskSync; import net.william278.husksync.HuskSync;
import net.william278.husksync.data.DataSaveCause; import net.william278.husksync.data.DataSaveCause;
import net.william278.husksync.data.ItemData; import net.william278.husksync.data.ItemData;
import net.william278.husksync.editor.ItemEditorMenuType;
import net.william278.husksync.player.OnlineUser; import net.william278.husksync.player.OnlineUser;
import org.jetbrains.annotations.NotNull; 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 * Determine whether a player event should be cancelled
* *

@ -1,13 +1,12 @@
package net.william278.husksync.player; package net.william278.husksync.player;
import de.themoep.minedown.adventure.MineDown; import de.themoep.minedown.adventure.MineDown;
import net.william278.desertwell.Version;
import net.william278.husksync.config.Settings; import net.william278.husksync.config.Settings;
import net.william278.husksync.data.*; import net.william278.husksync.data.*;
import net.william278.husksync.editor.ItemEditorMenu;
import net.william278.husksync.event.EventCannon; import net.william278.husksync.event.EventCannon;
import net.william278.husksync.event.PreSyncEvent; import net.william278.husksync.event.PreSyncEvent;
import net.william278.husksync.util.Logger; import net.william278.husksync.util.Logger;
import net.william278.desertwell.Version;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.ArrayList; import java.util.ArrayList;
@ -207,12 +206,8 @@ public abstract class OnlineUser extends User {
*/ */
public abstract boolean hasPermission(@NotNull String node); public abstract boolean hasPermission(@NotNull String node);
/** public abstract CompletableFuture<Optional<ItemData>> showMenu(@NotNull ItemData itemData, boolean editable,
* Show the player a {@link ItemEditorMenu} GUI int rows, @NotNull MineDown title);
*
* @param menu The {@link ItemEditorMenu} interface to show
*/
public abstract void showMenu(@NotNull ItemEditorMenu menu);
/** /**
* Returns true if the player is dead * Returns true if the player is dead

@ -1,4 +1,4 @@
package net.william278.husksync.editor; package net.william278.husksync.util;
import net.william278.husksync.config.Locales; import net.william278.husksync.config.Locales;
import net.william278.husksync.data.UserDataSnapshot; import net.william278.husksync.data.UserDataSnapshot;
@ -7,9 +7,8 @@ import net.william278.husksync.player.User;
import net.william278.paginedown.PaginatedList; import net.william278.paginedown.PaginatedList;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.text.DateFormat; import java.text.SimpleDateFormat;
import java.util.List; import java.util.List;
import java.util.Locale;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
/** /**
@ -29,8 +28,8 @@ public class DataSnapshotList {
this.paginatedList = PaginatedList.of(snapshots.stream() this.paginatedList = PaginatedList.of(snapshots.stream()
.map(snapshot -> locales.getRawLocale("data_list_item", .map(snapshot -> locales.getRawLocale("data_list_item",
getNumberIcon(snapshotNumber.getAndIncrement()), getNumberIcon(snapshotNumber.getAndIncrement()),
DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, new SimpleDateFormat("MMM dd yyyy, HH:mm:ss.sss")
Locale.getDefault()).format(snapshot.versionTimestamp()), .format(snapshot.versionTimestamp()),
snapshot.versionUUID().toString().split("-")[0], snapshot.versionUUID().toString().split("-")[0],
snapshot.versionUUID().toString(), snapshot.versionUUID().toString(),
snapshot.cause().name().toLowerCase().replaceAll("_", " "), snapshot.cause().name().toLowerCase().replaceAll("_", " "),
@ -53,7 +52,7 @@ public class DataSnapshotList {
* @param locales The {@link Locales} instance * @param locales The {@link Locales} instance
* @return A new {@link DataSnapshotList}, to be viewed with {@link #displayPage(OnlineUser, int)} * @return A new {@link DataSnapshotList}, to be viewed with {@link #displayPage(OnlineUser, int)}
*/ */
protected static DataSnapshotList create(@NotNull List<UserDataSnapshot> snapshots, @NotNull User user, public static DataSnapshotList create(@NotNull List<UserDataSnapshot> snapshots, @NotNull User user,
@NotNull Locales locales) { @NotNull Locales locales) {
return new DataSnapshotList(snapshots, user, 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 onlineUser The online user to display the message to
* @param page The page number to display * @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)); onlineUser.sendMessage(paginatedList.getNearestValidPage(page));
} }

@ -3,14 +3,10 @@ package net.william278.husksync.player;
import de.themoep.minedown.adventure.MineDown; import de.themoep.minedown.adventure.MineDown;
import net.william278.husksync.config.Settings; import net.william278.husksync.config.Settings;
import net.william278.husksync.data.*; import net.william278.husksync.data.*;
import net.william278.husksync.editor.ItemEditorMenu;
import net.william278.desertwell.Version; import net.william278.desertwell.Version;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.ArrayList; import java.util.*;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
public class DummyPlayer extends OnlineUser { public class DummyPlayer extends OnlineUser {
@ -152,10 +148,13 @@ public class DummyPlayer extends OnlineUser {
} }
@Override @Override
public void showMenu(@NotNull ItemEditorMenu menu) { public CompletableFuture<Optional<ItemData>> showMenu(@NotNull ItemData itemData, boolean editable,
int rows, @NotNull MineDown title) {
// do nothing // do nothing
return CompletableFuture.completedFuture(Optional.empty());
} }
@Override @Override
public boolean isDead() { public boolean isDead() {
return false; return false;

Loading…
Cancel
Save