From 6dd7c6f8d035a21c332375d245c4c2fb6dcaa179 Mon Sep 17 00:00:00 2001 From: Navoei Date: Fri, 29 Jul 2022 15:51:11 -0500 Subject: [PATCH] Better Async Tasks Now using a BukkitRunnable and a Map to determine if an async task is running. This allows the plugin to prevent the jukebox from being modified before the task is complete, which prevents the jukebox from bugging out and causing discs to be lost. --- gradle.properties | 2 +- .../customdiscsplugin/event/JukeBox.java | 152 +++++++++--------- 2 files changed, 76 insertions(+), 78 deletions(-) diff --git a/gradle.properties b/gradle.properties index eb31eb2..e9bbd4d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -11,6 +11,6 @@ mod_id=customdiscsplugin # Target an older API to make it compatible with older versions of Simple Voice Chat voicechat_api_version=2.2.22 -plugin_version=1.3 +plugin_version=1.3.1 maven_group=me.Navoei.customdiscsplugin archives_base_name=custom-discs \ No newline at end of file diff --git a/src/main/java/me/Navoei/customdiscsplugin/event/JukeBox.java b/src/main/java/me/Navoei/customdiscsplugin/event/JukeBox.java index 9fded2b..7995efe 100644 --- a/src/main/java/me/Navoei/customdiscsplugin/event/JukeBox.java +++ b/src/main/java/me/Navoei/customdiscsplugin/event/JukeBox.java @@ -26,6 +26,7 @@ import org.bukkit.inventory.ItemFlag; import org.bukkit.inventory.ItemStack; import org.bukkit.persistence.PersistentDataContainer; import org.bukkit.persistence.PersistentDataType; +import org.bukkit.scheduler.BukkitRunnable; import javax.sound.sampled.AudioFormat; import javax.sound.sampled.AudioInputStream; @@ -40,6 +41,7 @@ import java.util.concurrent.ConcurrentHashMap; public class JukeBox implements Listener{ private final Map playerMap = new ConcurrentHashMap<>(); + private final Map asyncTaskMap = new ConcurrentHashMap<>(); public static AudioFormat FORMAT = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 48000F, 16, 1, 2, 48000F, false); @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) @@ -57,7 +59,7 @@ public class JukeBox implements Listener{ "CustomDisc"); if (container.has(key, PersistentDataType.BYTE_ARRAY)) { event.setCancelled(true); - ejectDisc(block); + ejectDisc(block, player); return; } @@ -65,11 +67,11 @@ public class JukeBox implements Listener{ event.setCancelled(true); + UUID id = UUID.nameUUIDFromBytes(block.getLocation().toString().getBytes()); + Component soundFileNameComponent = Objects.requireNonNull(event.getItem().getItemMeta().lore()).get(1).asComponent(); String soundFileName = PlainTextComponentSerializer.plainText().serialize(soundFileNameComponent); - UUID id = UUID.nameUUIDFromBytes(block.getLocation().toString().getBytes()); - Path soundFilePath = Path.of(CustomDiscs.getInstance().getDataFolder().getPath(), "musicdata", soundFileName); if (soundFilePath.toFile().exists()) { @@ -80,9 +82,12 @@ public class JukeBox implements Listener{ LocationalAudioChannel audioChannel = VoicePlugin.voicechatServerApi.createLocationalAudioChannel(id, VoicePlugin.voicechatApi.fromServerLevel(block.getLocation().getWorld()), VoicePlugin.voicechatApi.createPosition(block.getLocation().getX() + 0.5d, block.getLocation().getY() + 0.5d, block.getLocation().getZ() + 0.5d)); //Run the audio player asynchronously to prevent lag when inserting discs. - Bukkit.getScheduler().runTaskAsynchronously(CustomDiscs.getInstance(), () -> { - AudioPlayer audioPlayer = null; + //Put the task in a hashmap to be able to access it later. + BukkitRunnable runnable = new BukkitRunnable() { + @Override + public void run() { //Try playing the voice chat audio player. + AudioPlayer audioPlayer = null; try { audioPlayer = VoicePlugin.voicechatServerApi.createAudioPlayer(audioChannel, VoicePlugin.voicechatApi.createEncoder(), readSoundFile(soundFilePath)); } catch (UnsupportedAudioFileException | IOException e) { @@ -94,12 +99,7 @@ public class JukeBox implements Listener{ assert audioPlayer != null; audioPlayer.startPlaying(); - //Run this in sync with the main thread after the disc is done loading. Bukkit.getScheduler().runTask(CustomDiscs.getInstance(), () -> { - //set the persistent data container - container.set(key, PersistentDataType.BYTE_ARRAY, event.getItem().serializeAsBytes()); - tileState.update(); - //Send Player Action Bar TextComponent customLoreSong = Component.text() .content("Now Playing: " + songName) @@ -108,9 +108,25 @@ public class JukeBox implements Listener{ player.sendActionBar(customLoreSong.asComponent()); }); - }); - //Remove the item from the player's hand in sync to prevent players from glitching the jukebox. - player.getInventory().setItem(Objects.requireNonNull(event.getHand()), null); + } + }; + + asyncTaskMap.put(block.getLocation(), runnable); + asyncTaskMap.get(block.getLocation()).runTaskAsynchronously(CustomDiscs.getInstance()); + + //Send Player Action Bar + TextComponent customLoadingSong = Component.text() + .content("Loading Disc...") + .color(NamedTextColor.GRAY) + .build(); + player.sendActionBar(customLoadingSong.asComponent()); + + //set the persistent data container + container.set(key, PersistentDataType.BYTE_ARRAY, event.getItem().serializeAsBytes()); + tileState.update(); + + //Remove the item from the player's hand in sync to prevent players from glitching the jukebox. + player.getInventory().setItem(Objects.requireNonNull(event.getHand()), null); } else { player.sendMessage(ChatColor.RED + "Sound file not found."); @@ -130,55 +146,37 @@ public class JukeBox implements Listener{ if (event.getAction() != Action.RIGHT_CLICK_BLOCK || event.getClickedBlock() == null || block == null) return; if (event.getClickedBlock().getType() != Material.JUKEBOX) return; - UUID id = UUID.nameUUIDFromBytes(block.getLocation().toString().getBytes()); - - //Spawn in item at the block position - TileState tileState = (TileState) block.getState(); - PersistentDataContainer container = tileState.getPersistentDataContainer(); - NamespacedKey key = new NamespacedKey(CustomDiscs.getInstance(), - "CustomDisc"); - if (!container.has(key, PersistentDataType.BYTE_ARRAY)) return; - container.get(key, PersistentDataType.BYTE_ARRAY); - - Location dropItemLocation = new Location(block.getWorld(), block.getLocation().getX(), block.getLocation().getY()+0.5d, block.getLocation().getZ()); - - block.getWorld().dropItemNaturally(dropItemLocation, ItemStack.deserializeBytes(container.get(key, PersistentDataType.BYTE_ARRAY))); - - container.remove(key); - tileState.update(); - - player.swingMainHand(); - - if (isAudioPlayerPlaying(playerMap, id)) { - stopAudioPlayer(playerMap, id); + if (isAsyncTaskRunning(asyncTaskMap, block.getLocation())) { + //Send Player Action Bar + TextComponent songLoading = Component.text() + .content("Disc is currently loading!") + .color(NamedTextColor.RED) + .build(); + player.sendActionBar(songLoading.asComponent()); + event.setCancelled(true); + } else { + ejectDisc(block, player); } - } @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void onJukeboxBreak(BlockBreakEvent event) { Block block = event.getBlock(); + Player player = event.getPlayer(); if (block.getType() != Material.JUKEBOX) return; - UUID id = UUID.nameUUIDFromBytes(block.getLocation().toString().getBytes()); - - //Spawn in item at the block position - TileState tileState = (TileState) block.getState(); - PersistentDataContainer container = tileState.getPersistentDataContainer(); - NamespacedKey key = new NamespacedKey(CustomDiscs.getInstance(), - "CustomDisc"); - if (!container.has(key, PersistentDataType.BYTE_ARRAY)) return; - container.get(key, PersistentDataType.BYTE_ARRAY); - - block.getWorld().dropItemNaturally(block.getLocation(), ItemStack.deserializeBytes(container.get(key, PersistentDataType.BYTE_ARRAY))); - - container.remove(key); - tileState.update(); - - if (isAudioPlayerPlaying(playerMap, id)) { - stopAudioPlayer(playerMap, id); + if (isAsyncTaskRunning(asyncTaskMap, block.getLocation())) { + //Send Player Action Bar + TextComponent songLoading = Component.text() + .content("Disc is currently loading!") + .color(NamedTextColor.RED) + .build(); + player.sendActionBar(songLoading.asComponent()); + event.setCancelled(true); + } else { + ejectDisc(block, player); } } @@ -189,34 +187,16 @@ public class JukeBox implements Listener{ for (Block explodedBlock : event.blockList()) { if (explodedBlock.getType() == Material.JUKEBOX) { - UUID id = UUID.nameUUIDFromBytes(explodedBlock.getLocation().toString().getBytes()); - - //Spawn in item at the block position - TileState tileState = (TileState) explodedBlock.getState(); - PersistentDataContainer container = tileState.getPersistentDataContainer(); - NamespacedKey key = new NamespacedKey(CustomDiscs.getInstance(), - "CustomDisc"); - if (!container.has(key, PersistentDataType.BYTE_ARRAY)) return; - container.get(key, PersistentDataType.BYTE_ARRAY); - - explodedBlock.getWorld().dropItemNaturally(explodedBlock.getLocation(), ItemStack.deserializeBytes(container.get(key, PersistentDataType.BYTE_ARRAY))); - - container.remove(key); - tileState.update(); - - if (isAudioPlayerPlaying(playerMap, id)) { - stopAudioPlayer(playerMap, id); + if (isAsyncTaskRunning(asyncTaskMap, explodedBlock.getLocation())) { + event.setCancelled(true); + } else { + ejectDisc(explodedBlock, null); } } } } - public boolean isAudioPlayerPlaying(Map playerMap, UUID id) { - AudioPlayer audioPlayer = playerMap.get(id); - if (audioPlayer == null) return false; - return audioPlayer.isPlaying(); - } private boolean jukeboxContainsPersistentData(Block b) { TileState tileState = (TileState) b.getState(); @@ -282,8 +262,7 @@ public class JukeBox implements Listener{ return false; } - //Might Replace some code with this: - private void ejectDisc(Block block) { + private void ejectDisc(Block block, Player player) { UUID id = UUID.nameUUIDFromBytes(block.getLocation().toString().getBytes()); //Spawn in item at the block position @@ -294,7 +273,7 @@ public class JukeBox implements Listener{ if (!container.has(key, PersistentDataType.BYTE_ARRAY)) return; container.get(key, PersistentDataType.BYTE_ARRAY); - block.getWorld().dropItemNaturally(block.getLocation(), ItemStack.deserializeBytes(container.get(key, PersistentDataType.BYTE_ARRAY))); + block.getWorld().dropItemNaturally(block.getLocation().add(0.0, 0.5, 0.0), ItemStack.deserializeBytes(container.get(key, PersistentDataType.BYTE_ARRAY))); container.remove(key); tileState.update(); @@ -302,6 +281,17 @@ public class JukeBox implements Listener{ if (isAudioPlayerPlaying(playerMap, id)) { stopAudioPlayer(playerMap, id); } + + asyncTaskMap.remove(block.getLocation()); + + if (player == null) return; + player.swingMainHand(); + } + + public boolean isAudioPlayerPlaying(Map playerMap, UUID id) { + AudioPlayer audioPlayer = playerMap.get(id); + if (audioPlayer == null) return false; + return audioPlayer.isPlaying(); } private void stopAudioPlayer(Map playerMap, UUID id) { @@ -309,6 +299,14 @@ public class JukeBox implements Listener{ if (audioPlayer != null && audioPlayer.isPlaying()) { audioPlayer.stopPlaying(); } + playerMap.remove(id); + } + + public boolean isAsyncTaskRunning(Map asyncTaskMap, Location blockLocation) { + if (!asyncTaskMap.containsKey(blockLocation)) return false; + int taskId = asyncTaskMap.get(blockLocation).getTaskId(); + System.out.println("Task is currently:" + Bukkit.getScheduler().isCurrentlyRunning(taskId) + " Task#" + taskId); + return Bukkit.getScheduler().isCurrentlyRunning(taskId); } private static String getFileExtension(String s) {