diff --git a/bukkit/build.gradle b/bukkit/build.gradle index 8abc1c41..655c3ccd 100644 --- a/bukkit/build.gradle +++ b/bukkit/build.gradle @@ -4,6 +4,7 @@ dependencies { implementation 'net.william278:mpdbdataconverter:1.0.1' implementation 'net.william278:hsldataconverter:1.0' implementation 'net.william278:MapDataAPI:1.0.2' + implementation 'net.william278:AndJam: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' @@ -12,6 +13,7 @@ dependencies { compileOnly 'commons-io:commons-io:2.11.0' compileOnly 'de.themoep:minedown-adventure:1.7.1-SNAPSHOT' compileOnly 'dev.dejvokep:boosted-yaml:1.3' + compileOnly 'com.github.Roxeez:AdvancementAPI:9b959a88cc' compileOnly 'com.zaxxer:HikariCP:5.0.1' compileOnly 'net.william278:DesertWell:1.1' compileOnly 'net.william278:Annotaml:2.0' @@ -33,7 +35,9 @@ shadowJar { relocate 'net.william278.desertwell', 'net.william278.husksync.libraries.desertwell' relocate 'net.william278.paginedown', 'net.william278.husksync.libraries.paginedown' relocate 'net.william278.mapdataapi', 'net.william278.husksync.libraries.mapdataapi' + relocate 'net.william278.andjam', 'net.william278.husksync.libraries.andjam' relocate 'net.querz', 'net.william278.husksync.libraries.nbt' + relocate 'net.roxeez', 'net.william278.husksync.libraries' relocate 'me.lucko.commodore', 'net.william278.husksync.libraries.commodore' relocate 'net.byteflux.libby', 'net.william278.husksync.libraries.libby' 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 80867d3a..10ee2963 100644 --- a/bukkit/src/main/java/net/william278/husksync/player/BukkitPlayer.java +++ b/bukkit/src/main/java/net/william278/husksync/player/BukkitPlayer.java @@ -6,6 +6,8 @@ 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.roxeez.advancement.display.FrameType; +import net.william278.andjam.Toast; import net.william278.desertwell.Version; import net.william278.husksync.BukkitHuskSync; import net.william278.husksync.config.Settings; @@ -534,7 +536,7 @@ public class BukkitPlayer extends OnlineUser { } }, () -> BukkitHuskSync.getInstance().getLoggingAdapter().log(Level.WARNING, "Could not set " + player.getName() + "'s persistent data key " + keyString + - " as it has an invalid type. Skipping!")); + " as it has an invalid type. Skipping!")); } }); }).exceptionally(throwable -> { @@ -628,6 +630,19 @@ public class BukkitPlayer extends OnlineUser { .replace().toComponent()); } + @Override + public void sendToast(@NotNull MineDown title, @NotNull MineDown description, + @NotNull String iconMaterial, @NotNull String backgroundType) { + final Material material = Material.matchMaterial(iconMaterial); + Toast.builder(BukkitHuskSync.getInstance()) + .setTitle(title.toComponent()) + .setDescription(description.toComponent()) + .setIcon(material != null ? material : Material.BARRIER) + .setFrameType(FrameType.valueOf(backgroundType)) + .build() + .show(player); + } + @Override public void sendMessage(@NotNull MineDown mineDown) { audience.sendMessage(mineDown diff --git a/common/src/main/java/net/william278/husksync/config/Settings.java b/common/src/main/java/net/william278/husksync/config/Settings.java index 97175146..3b2ae505 100644 --- a/common/src/main/java/net/william278/husksync/config/Settings.java +++ b/common/src/main/java/net/william278/husksync/config/Settings.java @@ -108,6 +108,9 @@ public class Settings { @YamlKey("synchronization.compress_data") public boolean compressData = true; + @YamlKey("synchronization.notification_display_slot") + public NotificationDisplaySlot notificationDisplaySlot = NotificationDisplaySlot.TOAST; + @YamlKey("synchronization.save_dead_player_inventories") public boolean saveDeadPlayerInventories = true; @@ -162,6 +165,28 @@ public class Settings { } } + /** + * Determines the slot a system notification should be displayed in + */ + public enum NotificationDisplaySlot { + /** + * Displays the notification in the action bar + */ + ACTION_BAR, + /** + * Displays the notification in the chat + */ + CHAT, + /** + * Displays the notification in an advancement toast + */ + TOAST, + /** + * Does not display the notification + */ + NONE + } + /** * Represents enabled synchronisation features */ 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 66c6df89..495e79bf 100644 --- a/common/src/main/java/net/william278/husksync/listener/EventListener.java +++ b/common/src/main/java/net/william278/husksync/listener/EventListener.java @@ -1,5 +1,6 @@ package net.william278.husksync.listener; +import de.themoep.minedown.adventure.MineDown; import net.william278.husksync.HuskSync; import net.william278.husksync.data.DataSaveCause; import net.william278.husksync.data.ItemData; @@ -121,7 +122,19 @@ public abstract class EventListener { */ private void handleSynchronisationCompletion(@NotNull OnlineUser user, boolean succeeded) { if (succeeded) { - plugin.getLocales().getLocale("synchronisation_complete").ifPresent(user::sendActionBar); + switch (plugin.getSettings().notificationDisplaySlot) { + case CHAT -> plugin.getLocales().getLocale("synchronisation_complete") + .ifPresent(user::sendActionBar); + case ACTION_BAR -> plugin.getLocales().getLocale("synchronisation_complete") + .ifPresent(user::sendActionBar); + case TOAST -> { + // todo locale implementation + user.sendToast(new MineDown("Synchronization complete"), + new MineDown("Your data has been synchronized"), + "minecraft:structure_void", + "task"); + } + } plugin.getDatabase().ensureUser(user).join(); lockedPlayers.remove(user.uuid); plugin.getEventCannon().fireSyncCompleteEvent(user); 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 49387e92..36196fca 100644 --- a/common/src/main/java/net/william278/husksync/player/OnlineUser.java +++ b/common/src/main/java/net/william278/husksync/player/OnlineUser.java @@ -198,6 +198,17 @@ public abstract class OnlineUser extends User { */ public abstract void sendActionBar(@NotNull MineDown mineDown); + /** + * Dispatch a toast message to this player + * + * @param title the title of the toast + * @param description the description of the toast + * @param iconMaterial the namespace-keyed material to use as an icon of the toast + * @param backgroundType the background ("ToastType") of the toast + */ + public abstract void sendToast(@NotNull MineDown title, @NotNull MineDown description, + @NotNull String iconMaterial, @NotNull String backgroundType); + /** * Returns if the player has the permission node * @@ -246,15 +257,15 @@ public abstract class OnlineUser extends User { // Prevent synchronising user data from newer versions of Minecraft if (Version.fromMinecraftVersionString(data.getMinecraftVersion()).compareTo(serverMinecraftVersion) > 0) { logger.log(Level.SEVERE, "Cannot set data for " + username + - " because the Minecraft version of their user data (" + data.getMinecraftVersion() + - ") is newer than the server's Minecraft version (" + serverMinecraftVersion + ")."); + " because the Minecraft version of their user data (" + data.getMinecraftVersion() + + ") is newer than the server's Minecraft version (" + serverMinecraftVersion + ")."); return false; } // Prevent synchronising user data from newer versions of the plugin if (data.getFormatVersion() > UserData.CURRENT_FORMAT_VERSION) { logger.log(Level.SEVERE, "Cannot set data for " + username + - " because the format version of their user data (v" + data.getFormatVersion() + - ") is newer than the current format version (v" + UserData.CURRENT_FORMAT_VERSION + ")."); + " because the format version of their user data (v" + data.getFormatVersion() + + ") is newer than the current format version (v" + UserData.CURRENT_FORMAT_VERSION + ")."); return false; } diff --git a/common/src/main/java/net/william278/husksync/redis/RedisManager.java b/common/src/main/java/net/william278/husksync/redis/RedisManager.java index b4745cd7..870d6d6f 100644 --- a/common/src/main/java/net/william278/husksync/redis/RedisManager.java +++ b/common/src/main/java/net/william278/husksync/redis/RedisManager.java @@ -1,5 +1,6 @@ package net.william278.husksync.redis; +import de.themoep.minedown.adventure.MineDown; import net.william278.husksync.HuskSync; import net.william278.husksync.data.UserData; import net.william278.husksync.player.User; @@ -85,8 +86,19 @@ public class RedisManager { user.setData(userData, plugin.getSettings(), plugin.getEventCannon(), plugin.getLoggingAdapter(), plugin.getMinecraftVersion()).thenAccept(succeeded -> { if (succeeded) { - plugin.getLocales().getLocale("data_update_complete") - .ifPresent(user::sendActionBar); + switch (plugin.getSettings().notificationDisplaySlot) { + case CHAT -> plugin.getLocales().getLocale("data_update_complete") + .ifPresent(user::sendMessage); + case ACTION_BAR -> plugin.getLocales().getLocale("data_update_complete") + .ifPresent(user::sendActionBar); + case TOAST -> { + // todo locale implementation + user.sendToast(new MineDown("Data updated"), + new MineDown("Your data has been updated"), + "minecraft:structure_void", + "task"); + } + } plugin.getEventCannon().fireSyncCompleteEvent(user); } else { plugin.getLocales().getLocale("data_update_failed") 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 aa52b8d5..abc1b378 100644 --- a/common/src/test/java/net/william278/husksync/player/DummyPlayer.java +++ b/common/src/test/java/net/william278/husksync/player/DummyPlayer.java @@ -142,6 +142,12 @@ public class DummyPlayer extends OnlineUser { // do nothing } + @Override + public void sendToast(@NotNull MineDown title, @NotNull MineDown description, + @NotNull String iconMaterial, @NotNull String backgroundType) { + // do nothing + } + @Override public boolean hasPermission(@NotNull String node) { return true;