Schedules next song individually for each player #4
All checks were successful
KnarCraft/Minstrel/pipeline/head This commit looks good
All checks were successful
KnarCraft/Minstrel/pipeline/head This commit looks good
This commit is contained in:
parent
fa2c1eda08
commit
a243b3fc77
6
pom.xml
6
pom.xml
@ -100,19 +100,19 @@
|
||||
<dependency>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
<artifactId>spigot-api</artifactId>
|
||||
<version>1.19.4-R0.1-SNAPSHOT</version>
|
||||
<version>1.20.1-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.knarcraft</groupId>
|
||||
<artifactId>knarlib</artifactId>
|
||||
<version>1.1</version>
|
||||
<version>1.2.3</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.citizensnpcs</groupId>
|
||||
<artifactId>citizens-main</artifactId>
|
||||
<version>2.0.30-SNAPSHOT</version>
|
||||
<version>2.0.32-SNAPSHOT</version>
|
||||
<type>jar</type>
|
||||
<scope>provided</scope>
|
||||
<exclusions>
|
||||
|
@ -4,6 +4,7 @@ import net.citizensnpcs.api.CitizensAPI;
|
||||
import net.knarcraft.minstrel.command.MinstrelCommand;
|
||||
import net.knarcraft.minstrel.command.MinstrelTabCompleter;
|
||||
import net.knarcraft.minstrel.listener.MinstrelListener;
|
||||
import net.knarcraft.minstrel.manager.QueueManager;
|
||||
import net.knarcraft.minstrel.trait.MinstrelTrait;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.PluginCommand;
|
||||
@ -39,6 +40,8 @@ public final class MinstrelPlugin extends JavaPlugin {
|
||||
minstrelCommand.setExecutor(new MinstrelCommand());
|
||||
minstrelCommand.setTabCompleter(new MinstrelTabCompleter());
|
||||
}
|
||||
|
||||
Bukkit.getScheduler().scheduleSyncRepeatingTask(this, QueueManager::handleQueue, 20, 10);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -57,6 +60,15 @@ public final class MinstrelPlugin extends JavaPlugin {
|
||||
knownMinstrels.add(minstrelTrait);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all currently known minstrels
|
||||
*
|
||||
* @return <p>All currently known minstrels</p>
|
||||
*/
|
||||
public List<MinstrelTrait> getMinstrels() {
|
||||
return new ArrayList<>(knownMinstrels);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an instance of this plugin
|
||||
*
|
||||
|
@ -67,7 +67,7 @@ public class AddSongCommand implements CommandExecutor {
|
||||
|
||||
//If this is the first song in the playlist, start playing
|
||||
if (playlist.getSongs().size() == 1) {
|
||||
playlist.play(minstrelTrait);
|
||||
playlist.changeSong();
|
||||
}
|
||||
sender.sendMessage("New song added");
|
||||
return true;
|
||||
|
@ -1,6 +1,5 @@
|
||||
package net.knarcraft.minstrel.command;
|
||||
|
||||
import net.knarcraft.minstrel.music.Song;
|
||||
import net.knarcraft.minstrel.trait.MinstrelTrait;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
@ -26,12 +25,7 @@ public class ListSongsCommand implements CommandExecutor {
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label,
|
||||
@NotNull String[] args) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("Songs:").append("\n");
|
||||
for (Song song : minstrelTrait.getPlaylist().getSongs()) {
|
||||
builder.append(song).append("\n");
|
||||
}
|
||||
sender.sendMessage(builder.toString());
|
||||
sender.sendMessage("Songs:" + minstrelTrait.getPlaylist());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,7 @@ import net.knarcraft.minstrel.trait.MinstrelTrait;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class MinstrelCommand implements CommandExecutor {
|
||||
@ -72,9 +73,9 @@ public class MinstrelCommand implements CommandExecutor {
|
||||
sender.sendMessage("The pitch cannot be negative");
|
||||
} else {
|
||||
minstrelTrait.setPitch(pitch);
|
||||
if (forceRefresh) {
|
||||
minstrelTrait.getPlaylist().stop();
|
||||
minstrelTrait.getPlaylist().play(minstrelTrait);
|
||||
if (forceRefresh && sender instanceof Player player) {
|
||||
minstrelTrait.getPlaylist().stop(player);
|
||||
minstrelTrait.getPlaylist().play(minstrelTrait, player);
|
||||
}
|
||||
}
|
||||
} catch (NumberFormatException exception) {
|
||||
@ -105,9 +106,9 @@ public class MinstrelCommand implements CommandExecutor {
|
||||
sender.sendMessage("The volume must be greater than 0");
|
||||
} else {
|
||||
minstrelTrait.setVolume(volume);
|
||||
if (forceRefresh) {
|
||||
minstrelTrait.getPlaylist().stop();
|
||||
minstrelTrait.getPlaylist().play(minstrelTrait);
|
||||
if (forceRefresh && sender instanceof Player player) {
|
||||
minstrelTrait.getPlaylist().stop(player);
|
||||
minstrelTrait.getPlaylist().play(minstrelTrait, player);
|
||||
}
|
||||
}
|
||||
} catch (NumberFormatException exception) {
|
||||
|
@ -39,7 +39,7 @@ public class RemoveSongCommand implements CommandExecutor {
|
||||
playlist.stop();
|
||||
playlist.removeSong(index);
|
||||
if (!playlist.getSongs().isEmpty()) {
|
||||
playlist.play(minstrelTrait);
|
||||
playlist.changeSong();
|
||||
}
|
||||
sender.sendMessage("Song removed");
|
||||
} else {
|
||||
|
@ -1,30 +1,34 @@
|
||||
package net.knarcraft.minstrel.listener;
|
||||
|
||||
import net.citizensnpcs.api.CitizensAPI;
|
||||
import net.citizensnpcs.api.event.NPCRemoveEvent;
|
||||
import net.citizensnpcs.api.npc.NPC;
|
||||
import net.knarcraft.minstrel.MinstrelPlugin;
|
||||
import net.knarcraft.minstrel.manager.QueueManager;
|
||||
import net.knarcraft.minstrel.music.SongEndTime;
|
||||
import net.knarcraft.minstrel.trait.MinstrelTrait;
|
||||
import net.knarcraft.minstrel.util.SoundHelper;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.bukkit.event.player.PlayerMoveEvent;
|
||||
|
||||
/**
|
||||
* A listener for minstrel-relate events
|
||||
*/
|
||||
public class MinstrelListener implements Listener {
|
||||
|
||||
/**
|
||||
* Listens for any players joining, and makes all minstrels play for them
|
||||
*
|
||||
* @param event <p>The triggered event</p>
|
||||
*/
|
||||
@EventHandler
|
||||
public void playerJoinListener(PlayerJoinEvent event) {
|
||||
for (NPC npc : CitizensAPI.getNPCRegistry()) {
|
||||
if (npc.hasTrait(MinstrelTrait.class)) {
|
||||
MinstrelTrait minstrelTrait = npc.getTraitNullable(MinstrelTrait.class);
|
||||
minstrelTrait.getPlaylist().play(minstrelTrait, event.getPlayer());
|
||||
public void playerMoveListener(PlayerMoveEvent event) {
|
||||
if (event.getTo() == null || event.getFrom().getBlock().equals(event.getTo().getBlock())) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (MinstrelTrait minstrelTrait : MinstrelPlugin.getInstance().getMinstrels()) {
|
||||
if (!SoundHelper.canHear(event.getPlayer(), minstrelTrait) ||
|
||||
QueueManager.isQueued(event.getPlayer(), minstrelTrait)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QueueManager.queue(new SongEndTime(event.getPlayer(), minstrelTrait, 0));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,66 @@
|
||||
package net.knarcraft.minstrel.manager;
|
||||
|
||||
import net.knarcraft.minstrel.music.SongEndTime;
|
||||
import net.knarcraft.minstrel.trait.MinstrelTrait;
|
||||
import net.knarcraft.minstrel.util.SoundHelper;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.PriorityQueue;
|
||||
import java.util.Queue;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A manager keeping track of the queue for playing the next song
|
||||
*/
|
||||
public class QueueManager {
|
||||
|
||||
private static final Queue<SongEndTime> songEndTimes = new PriorityQueue<>();
|
||||
private static final Map<Player, Set<MinstrelTrait>> queuedMinstrels = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Takes care of any expired songs
|
||||
*/
|
||||
public static void handleQueue() {
|
||||
while (!songEndTimes.isEmpty() && songEndTimes.peek().endTime() < System.currentTimeMillis()) {
|
||||
SongEndTime songEndTime = songEndTimes.remove();
|
||||
if (songEndTime == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Play the next song if the player is near enough to hear
|
||||
MinstrelTrait minstrelTrait = songEndTime.minstrelTrait();
|
||||
Player player = songEndTime.player();
|
||||
if (SoundHelper.canHear(player, minstrelTrait)) {
|
||||
minstrelTrait.getPlaylist().play(minstrelTrait, player);
|
||||
} else {
|
||||
queuedMinstrels.get(player).remove(minstrelTrait);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given minstrel has a song queued for the given player
|
||||
*
|
||||
* @param player <p>The player to check</p>
|
||||
* @param minstrelTrait <p>The minstrel to check</p>
|
||||
* @return <p>True if already queued</p>
|
||||
*/
|
||||
public static boolean isQueued(Player player, MinstrelTrait minstrelTrait) {
|
||||
return queuedMinstrels.get(player) != null && queuedMinstrels.get(player).contains(minstrelTrait);
|
||||
}
|
||||
|
||||
/**
|
||||
* Queues the given song end time
|
||||
*
|
||||
* @param songEndTime <p>The song end time to queue</p>
|
||||
*/
|
||||
public static void queue(SongEndTime songEndTime) {
|
||||
songEndTimes.add(songEndTime);
|
||||
queuedMinstrels.computeIfAbsent(songEndTime.player(), k -> new HashSet<>());
|
||||
queuedMinstrels.get(songEndTime.player()).add(songEndTime.minstrelTrait());
|
||||
}
|
||||
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
package net.knarcraft.minstrel.music;
|
||||
|
||||
import net.knarcraft.minstrel.MinstrelPlugin;
|
||||
import net.knarcraft.minstrel.manager.QueueManager;
|
||||
import net.knarcraft.minstrel.trait.MinstrelTrait;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
@ -16,20 +17,16 @@ import java.util.Map;
|
||||
public class Playlist {
|
||||
|
||||
private final List<Song> songs;
|
||||
private final boolean loop;
|
||||
private int currentlyPlaying = 0;
|
||||
private int schedulerId = -1;
|
||||
private final Map<Player, Song> playerCurrentSong = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Instantiates a new playlist
|
||||
*
|
||||
* @param songs <p>The songs contained in this playlist</p>
|
||||
* @param loop <p>Whether to loop around when this playlist finishes</p>
|
||||
*/
|
||||
public Playlist(List<Song> songs, boolean loop) {
|
||||
public Playlist(List<Song> songs) {
|
||||
this.songs = new ArrayList<>(songs);
|
||||
this.loop = loop;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -81,6 +78,7 @@ public class Playlist {
|
||||
song.stop(player);
|
||||
}
|
||||
}
|
||||
int schedulerId = -1;
|
||||
if (Bukkit.getScheduler().isCurrentlyRunning(schedulerId)) {
|
||||
Bukkit.getScheduler().cancelTask(schedulerId);
|
||||
}
|
||||
@ -102,43 +100,11 @@ public class Playlist {
|
||||
}
|
||||
|
||||
stop(player);
|
||||
Song currentSong = this.songs.get(this.currentlyPlaying - 1);
|
||||
Song currentSong = this.songs.get(this.currentlyPlaying);
|
||||
playerCurrentSong.put(player, currentSong);
|
||||
currentSong.play(trait, player, trait.getVolume(), trait.getPitch());
|
||||
}
|
||||
|
||||
/**
|
||||
* Plays the next song in this playlist
|
||||
*
|
||||
* @param trait <p>The minstrel to play this playlist for</p>
|
||||
*/
|
||||
public void play(MinstrelTrait trait) {
|
||||
//If this playlist is empty, do nothing
|
||||
if (this.songs.size() < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.currentlyPlaying >= songs.size()) {
|
||||
if (this.loop) {
|
||||
this.currentlyPlaying = 0;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//Prevent overlapping music if a song has been played outside the global cycle
|
||||
for (Player player : playerCurrentSong.keySet()) {
|
||||
Song currentSong = playerCurrentSong.get(player);
|
||||
if (currentSong != null) {
|
||||
currentSong.stop(player);
|
||||
}
|
||||
}
|
||||
|
||||
Song currentSong = this.songs.get(this.currentlyPlaying);
|
||||
currentSong.play(trait, trait.getVolume(), trait.getPitch());
|
||||
currentlyPlaying++;
|
||||
schedulerId = Bukkit.getScheduler().scheduleSyncDelayedTask(MinstrelPlugin.getInstance(), () -> play(trait),
|
||||
currentSong.getDuration() * 20L);
|
||||
QueueManager.queue(new SongEndTime(player, trait, System.currentTimeMillis() +
|
||||
(currentSong.getDuration() * 1000L)));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -148,4 +114,31 @@ public class Playlist {
|
||||
this.songs.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes to the next song in the playlist
|
||||
*/
|
||||
public void changeSong() {
|
||||
if (this.songs.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.currentlyPlaying++;
|
||||
this.currentlyPlaying = this.currentlyPlaying % this.songs.size();
|
||||
Bukkit.getScheduler().scheduleSyncDelayedTask(MinstrelPlugin.getInstance(), this::changeSong,
|
||||
this.songs.get(this.currentlyPlaying).getDuration() * 20L);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (int i = 0; i < this.songs.size(); i++) {
|
||||
builder.append("\n");
|
||||
if (this.currentlyPlaying == i) {
|
||||
builder.append(">");
|
||||
}
|
||||
builder.append(this.songs.get(i));
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,8 +1,6 @@
|
||||
package net.knarcraft.minstrel.music;
|
||||
|
||||
import net.knarcraft.minstrel.MinstrelPlugin;
|
||||
import net.knarcraft.minstrel.trait.MinstrelTrait;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.SoundCategory;
|
||||
import org.bukkit.entity.Player;
|
||||
@ -21,7 +19,6 @@ public class Song {
|
||||
private final int durationSeconds;
|
||||
private final SoundCategory category;
|
||||
private final String sound;
|
||||
private boolean isPlaying = false;
|
||||
private final Set<Player> playingFor = new HashSet<>();
|
||||
|
||||
/**
|
||||
@ -53,27 +50,6 @@ public class Song {
|
||||
play(player, trait.getLocation(), volume, pitch);
|
||||
}
|
||||
|
||||
/**
|
||||
* Plays this song at the given minstrel's location
|
||||
*
|
||||
* <p>Volume of 1 means the max volume, which can be heard up to 1 chunk away. Setting it to 2 means it can be
|
||||
* heard 2 chunks away.</p>
|
||||
*
|
||||
* @param trait <p>The minstrel to play this song</p>
|
||||
* @param volume <p>The volume to play this song at</p>
|
||||
* @param pitch <p>The pitch to play this song at</p>
|
||||
*/
|
||||
public void play(MinstrelTrait trait, float volume, float pitch) {
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
play(player, trait.getLocation(), volume, pitch);
|
||||
}
|
||||
|
||||
this.isPlaying = true;
|
||||
//Mark this song as ended, once
|
||||
Bukkit.getScheduler().runTaskLater(MinstrelPlugin.getInstance(), () -> this.isPlaying = false,
|
||||
durationSeconds * 20L);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the duration of this song, in seconds
|
||||
*
|
||||
@ -90,7 +66,7 @@ public class Song {
|
||||
*/
|
||||
public void stop(Player player) {
|
||||
//Don't bother stopping if not playing
|
||||
if (!playingFor.contains(player) && !isPlaying) {
|
||||
if (!playingFor.contains(player)) {
|
||||
return;
|
||||
}
|
||||
playingFor.remove(player);
|
||||
@ -119,12 +95,7 @@ public class Song {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String songString = this.category + ":" + this.sound + ":" + this.durationSeconds;
|
||||
if (this.isPlaying) {
|
||||
return ">" + songString;
|
||||
} else {
|
||||
return songString;
|
||||
}
|
||||
return this.category + ":" + this.sound + ":" + this.durationSeconds;
|
||||
}
|
||||
|
||||
/**
|
||||
|
37
src/main/java/net/knarcraft/minstrel/music/SongEndTime.java
Normal file
37
src/main/java/net/knarcraft/minstrel/music/SongEndTime.java
Normal file
@ -0,0 +1,37 @@
|
||||
package net.knarcraft.minstrel.music;
|
||||
|
||||
import net.knarcraft.minstrel.trait.MinstrelTrait;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* A record of the time when a specific song will end
|
||||
*
|
||||
* @param player <p>The player the song is playing for</p>
|
||||
* @param minstrelTrait <p>The minstrel playing the song</p>
|
||||
* @param endTime <p>The time when the song will end</p>
|
||||
*/
|
||||
public record SongEndTime(Player player, MinstrelTrait minstrelTrait, long endTime) implements Comparable<SongEndTime> {
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (!(other instanceof SongEndTime songEndTime)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return player.equals(songEndTime.player) && minstrelTrait.equals(songEndTime.minstrelTrait);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(@NotNull SongEndTime o) {
|
||||
long difference = this.endTime - o.endTime;
|
||||
if (difference > 0) {
|
||||
return 1;
|
||||
} else if (difference < 0) {
|
||||
return -1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -16,7 +16,7 @@ import java.util.List;
|
||||
*/
|
||||
public class MinstrelTrait extends Trait {
|
||||
|
||||
private final Playlist playlist = new Playlist(new ArrayList<>(), true);
|
||||
private final Playlist playlist = new Playlist(new ArrayList<>());
|
||||
private float volume = 1F;
|
||||
private float pitch = 1F;
|
||||
|
||||
@ -59,7 +59,7 @@ public class MinstrelTrait extends Trait {
|
||||
}
|
||||
this.volume = (float) key.getDouble("volume", 1D);
|
||||
this.pitch = (float) key.getDouble("pitch", 1D);
|
||||
this.playlist.play(this);
|
||||
this.playlist.changeSong();
|
||||
|
||||
//Register the minstrel to allow stopping the playback later
|
||||
MinstrelPlugin.getInstance().addMinstrel(this);
|
||||
@ -143,4 +143,13 @@ public class MinstrelTrait extends Trait {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (!(other instanceof MinstrelTrait minstrelTrait)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return npc.equals(minstrelTrait.npc);
|
||||
}
|
||||
|
||||
}
|
||||
|
21
src/main/java/net/knarcraft/minstrel/util/SoundHelper.java
Normal file
21
src/main/java/net/knarcraft/minstrel/util/SoundHelper.java
Normal file
@ -0,0 +1,21 @@
|
||||
package net.knarcraft.minstrel.util;
|
||||
|
||||
import net.knarcraft.minstrel.trait.MinstrelTrait;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class SoundHelper {
|
||||
|
||||
/**
|
||||
* Checks whether the given player can hear the given minstrel
|
||||
*
|
||||
* @param player <p>The player to check</p>
|
||||
* @param minstrelTrait <p>The minstrel to check</p>
|
||||
* @return <p>True if the player would hear the minstrel</p>
|
||||
*/
|
||||
public static boolean canHear(Player player, MinstrelTrait minstrelTrait) {
|
||||
double distance = player.getLocation().distance(minstrelTrait.getNPC().getStoredLocation());
|
||||
// A player can hear a minstrel 15 blocks away, but the distance increases if volume > 1
|
||||
return distance < 15 * (Math.max(minstrelTrait.getVolume(), 1));
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user