commit bfa49448abe9122d969b2aa5e1aff27f22333ed0 Author: EpicKnarvik97 Date: Sun Oct 30 04:33:51 2022 +0100 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4788b4b --- /dev/null +++ b/.gitignore @@ -0,0 +1,113 @@ +# User-specific stuff +.idea/ + +*.iml +*.ipr +*.iws + +# IntelliJ +out/ + +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +target/ + +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next + +release.properties +dependency-reduced-pom.xml +buildNumber.properties +.mvn/timing.properties +.mvn/wrapper/maven-wrapper.jar +.flattened-pom.xml + +# Common working directory +run/ diff --git a/README.MD b/README.MD new file mode 100644 index 0000000..509f9e1 --- /dev/null +++ b/README.MD @@ -0,0 +1,14 @@ +# Minstrel + +This plugin aims to add a Minstrel trait to Citizens NPCs which is able to play a set of songs from a custom resource +pack. While there are plugins which use workarounds, this plugin aims to make the best npc-based "music box" add-on one +can create within the Spigot API. While kind of hard to set up, this allows using any music, without compromise. Note: +To make music hear-able only within the area of an NPC, you must make sure the track is Mono (1 channel). If you use a +Stereo (2 channels) soundtrack, you'll hear the music everywhere, which is cool if you want to stream music globally. + +Volume between 0 and 1 decides how loud the volume is. If volume is set above 1, it will still play at full volume, but +can be heard further away. + +## Dependencies + +- Citizens \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..0419761 --- /dev/null +++ b/pom.xml @@ -0,0 +1,92 @@ + + + 4.0.0 + + net.knarcraft + minstrel + 1.0-SNAPSHOT + jar + + Minstrel + + Adds a minstrel trait to Citizens NPCs + + 1.8 + UTF-8 + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + ${java.version} + ${java.version} + + + + org.apache.maven.plugins + maven-shade-plugin + 3.2.4 + + + package + + shade + + + false + + + + + + + + src/main/resources + true + + + + + + + spigotmc-repo + https://hub.spigotmc.org/nexus/content/repositories/snapshots/ + + + sonatype + https://oss.sonatype.org/content/groups/public/ + + + citizens-repo + http://repo.citizensnpcs.co/ + + + + + + org.spigotmc + spigot-api + 1.19.2-R0.1-SNAPSHOT + provided + + + net.citizensnpcs + citizens-main + 2.0.30-SNAPSHOT + jar + provided + + + * + * + + + + + diff --git a/src/main/java/net/knarcraft/minstrel/MinstrelPlugin.java b/src/main/java/net/knarcraft/minstrel/MinstrelPlugin.java new file mode 100644 index 0000000..5db41e9 --- /dev/null +++ b/src/main/java/net/knarcraft/minstrel/MinstrelPlugin.java @@ -0,0 +1,62 @@ +package net.knarcraft.minstrel; + +import net.citizensnpcs.api.CitizensAPI; +import org.bukkit.Bukkit; +import org.bukkit.SoundCategory; +import org.bukkit.plugin.PluginManager; +import org.bukkit.plugin.java.JavaPlugin; + +import java.util.ArrayList; +import java.util.List; + +/** + * The main class of this plugin + */ +public final class MinstrelPlugin extends JavaPlugin { + + private static MinstrelPlugin instance; + private static Playlist testPlaylist; + + @Override + public void onEnable() { + // Plugin startup logic + instance = this; + + //Register the blacksmith trait with Citizens + CitizensAPI.getTraitFactory().registerTrait( + net.citizensnpcs.api.trait.TraitInfo.create(MinstrelTrait.class).withName("minstrel")); + + Song testSong = new Song(SoundCategory.RECORDS, "minecraft:records.custom.medieval_3_g_mixolydian", 114); + List songs = new ArrayList<>(); + songs.add(testSong); + testPlaylist = new Playlist(songs, true); + + PluginManager pluginManager = Bukkit.getPluginManager(); + pluginManager.registerEvents(new PlayerListener(), this); + } + + @Override + public void onDisable() { + // Plugin shutdown logic + //TODO: Stop all songs in all playlists + } + + /** + * Gets the playlist which is currently hard-coded for testing + * + * @return

The test playlist

+ */ + public static Playlist getTestPlaylist() { + return testPlaylist; + } + + /** + * Gets an instance of this plugin + * + * @return

An instance of this plugin

+ */ + public static MinstrelPlugin getInstance() { + return instance; + } + +} diff --git a/src/main/java/net/knarcraft/minstrel/MinstrelTrait.java b/src/main/java/net/knarcraft/minstrel/MinstrelTrait.java new file mode 100644 index 0000000..5ed2ba5 --- /dev/null +++ b/src/main/java/net/knarcraft/minstrel/MinstrelTrait.java @@ -0,0 +1,44 @@ +package net.knarcraft.minstrel; + +import net.citizensnpcs.api.trait.Trait; +import net.citizensnpcs.api.util.DataKey; +import org.bukkit.Location; + +/** + * The minstrel trait itself, which contains all NPC data for this trait + */ +public class MinstrelTrait extends Trait { + + public MinstrelTrait() { + super("minstrel"); + } + + /** + * Gets the location of this minstrel + * + * @return

The location of this minstrel

+ */ + public Location getLocation() { + return this.getNPC().getStoredLocation(); + } + + /** + * Loads all config values stored in citizens' config file for this NPC + * + * @param key

The data key used for the config root

+ */ + @Override + public void load(DataKey key) { + //TODO: Actually load the playlist set to this minstrel + MinstrelPlugin.getTestPlaylist().play(this); + } + + public float getVolume() { + return 1F; + } + + public float getPitch() { + return 1F; + } + +} diff --git a/src/main/java/net/knarcraft/minstrel/PlayerListener.java b/src/main/java/net/knarcraft/minstrel/PlayerListener.java new file mode 100644 index 0000000..cbc7abc --- /dev/null +++ b/src/main/java/net/knarcraft/minstrel/PlayerListener.java @@ -0,0 +1,23 @@ +package net.knarcraft.minstrel; + +import net.citizensnpcs.api.CitizensAPI; +import net.citizensnpcs.api.npc.NPC; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerJoinEvent; + +/** + * A listener for events where a player isn't hearing music when they should + */ +public class PlayerListener implements Listener { + + @EventHandler + public void playerJoinListener(PlayerJoinEvent event) { + for (NPC npc : CitizensAPI.getNPCRegistry()) { + if (npc.hasTrait(MinstrelTrait.class)) { + MinstrelPlugin.getTestPlaylist().play(npc.getTraitNullable(MinstrelTrait.class), event.getPlayer()); + } + } + } + +} diff --git a/src/main/java/net/knarcraft/minstrel/Playlist.java b/src/main/java/net/knarcraft/minstrel/Playlist.java new file mode 100644 index 0000000..e3786cb --- /dev/null +++ b/src/main/java/net/knarcraft/minstrel/Playlist.java @@ -0,0 +1,116 @@ +package net.knarcraft.minstrel; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.List; + +/** + * A representation of a playlist containing songs + */ +public class Playlist { + + private final List songs; + private final boolean loop; + private int currentlyPlaying = 0; + private int schedulerId = 0; + + /** + * Instantiates a new playlist + * + * @param songs

The songs contained in this playlist

+ * @param loop

Whether to loop around when this playlist finishes

+ */ + public Playlist(List songs, boolean loop) { + this.songs = new ArrayList<>(songs); + this.loop = loop; + } + + /** + * Gets a copy of the songs in this playlist + * + * @return

The songs in this playlist

+ */ + public List getSongs() { + return new ArrayList<>(this.songs); + } + + /** + * Adds a new song to this playlist + * + * @param song

The song to add

+ */ + public void addSong(Song song) { + this.songs.add(song); + } + + /** + * Removes a song from this playlist + * + * @param index

The index of the song to remove

+ */ + public void removeSong(int index) { + this.songs.remove(index); + } + + /** + * Plays the current song for the given player + * + *

This should only be used if a player recently joined, or for some other reason cannot currently hear the + * music.

+ * + * @param trait

The the minstrel to play this playlist for

+ * @param player

The player to play to

+ */ + public void play(MinstrelTrait trait, Player player) { + //If this playlist is empty, do nothing + if (this.songs.size() < 1) { + return; + } + + for (Song song : this.songs) { + song.stop(player); + } + + Song currentSong = this.songs.get(this.currentlyPlaying - 1); + currentSong.play(trait, player, trait.getVolume(), trait.getPitch()); + } + + /** + * Plays the next song in this playlist + * + * @param trait

The minstrel to play this playlist for

+ */ + 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; + } + } + + for (Player player : Bukkit.getOnlinePlayers()) { + for (Song song : this.songs) { + song.stop(player); + } + } + + Song currentSong = this.songs.get(this.currentlyPlaying); + currentSong.play(trait, trait.getVolume(), trait.getPitch()); + currentlyPlaying++; + schedulerId = Bukkit.getScheduler().scheduleSyncRepeatingTask(MinstrelPlugin.getInstance(), () -> { + if (!currentSong.isPlaying()) { + Bukkit.getScheduler().cancelTask(schedulerId); + play(trait); + } + }, currentSong.getDuration(), 20); + } + +} diff --git a/src/main/java/net/knarcraft/minstrel/Song.java b/src/main/java/net/knarcraft/minstrel/Song.java new file mode 100644 index 0000000..99d2909 --- /dev/null +++ b/src/main/java/net/knarcraft/minstrel/Song.java @@ -0,0 +1,115 @@ +package net.knarcraft.minstrel; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.SoundCategory; +import org.bukkit.entity.Player; + +/** + * A representation of a song playable by a minstrel + * + *

Note: Any custom song has to be mono to fade out like a normal jukebox. If it's stereo, you'll end up with a + * global song instead.

+ */ +public class Song { + + private final int durationSeconds; + private final SoundCategory category; + private final String sound; + private boolean isPlaying = false; + + /** + * Instantiates a new song + * + * @param category

The category the song belongs to

+ * @param sound

The song to use

+ * @param durationSeconds

The duration of the song, in seconds

+ */ + public Song(SoundCategory category, String sound, int durationSeconds) { + this.category = category; + this.sound = sound; + this.durationSeconds = durationSeconds; + } + + /** + * Plays this song at the given minstrel's location, but only for the given player + * + *

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.

+ * + * @param trait

The minstrel to play this song

+ * @param player

The player to play this song for

+ * @param volume

The volume to play this song at

+ * @param pitch

The pitch to play this song at

+ */ + public void play(MinstrelTrait trait, Player player, float volume, float pitch) { + play(player, trait.getLocation(), volume, pitch); + } + + /** + * Plays this song at the given minstrel's location + * + *

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.

+ * + * @param trait

The minstrel to play this song

+ * @param volume

The volume to play this song at

+ * @param pitch

The pitch to play this song at

+ */ + 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 whether this song is currently playing + * + * @return

True if this song is currently playing

+ */ + public boolean isPlaying() { + return this.isPlaying; + } + + /** + * Gets the duration of this song, in seconds + * + * @return

The duration of this song

+ */ + public int getDuration() { + return durationSeconds; + } + + /** + * Stops this song for the given player + * + * @param player

The player to stop this song for

+ */ + public void stop(Player player) { + if (this.category == null) { + player.stopSound(this.sound); + } else { + player.stopSound(this.sound, this.category); + } + } + + /** + * Plays this song for the given player, at the given location + * + * @param player

The player to play this song for

+ * @param location

The location to play the song at

+ * @param volume

The volume to play at

+ * @param pitch

The pitch to play at

+ */ + private void play(Player player, Location location, float volume, float pitch) { + if (this.category == null) { + player.playSound(location, this.sound, volume, pitch); + } else { + player.playSound(location, this.sound, this.category, volume, pitch); + } + } + +} diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml new file mode 100644 index 0000000..78326df --- /dev/null +++ b/src/main/resources/plugin.yml @@ -0,0 +1,8 @@ +name: Minstrel +version: '${project.version}' +main: net.knarcraft.minstrel.MinstrelPlugin +api-version: 1.19 +prefix: Minstrel +depend: [ Citizens ] +authors: [ EpicKnarvik97 ] +description: Adds a minstrel trait to Citizens NPCs