diff --git a/pom.xml b/pom.xml
index fd0d864..4d4523a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -19,10 +19,15 @@
spigotmc-repo
https://hub.spigotmc.org/nexus/content/repositories/snapshots/
+
maxhenkel
https://maven.maxhenkel.de/repository/public
+
+ minebench-repo
+ https://repo.minebench.de/
+
@@ -51,5 +56,38 @@
ffmpeg
0.8.0
+
+
+ de.themoep
+ inventorygui
+ 1.6.4-SNAPSHOT
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-shade-plugin
+ 3.6.0
+
+
+ package
+
+ shade
+
+
+ true
+ false
+
+
+ de.themoep:inventorygui
+
+
+
+
+
+
+
+
diff --git a/src/main/java/eu/m724/musicPlugin/MusicCommands.java b/src/main/java/eu/m724/musicPlugin/MusicCommands.java
deleted file mode 100644
index b14ba85..0000000
--- a/src/main/java/eu/m724/musicPlugin/MusicCommands.java
+++ /dev/null
@@ -1,137 +0,0 @@
-package eu.m724.musicPlugin;
-
-import eu.m724.musicPlugin.file.AudioFileStorage;
-import eu.m724.musicPlugin.item.PortableMediaPlayer;
-import eu.m724.musicPlugin.item.PortableMediaPlayers;
-import eu.m724.musicPlugin.player.MusicPlayer;
-import eu.m724.musicPlugin.file.AudioFile;
-import eu.m724.musicPlugin.player.StaticMusicPlayer;
-import net.md_5.bungee.api.chat.ClickEvent;
-import net.md_5.bungee.api.chat.ComponentBuilder;
-import net.md_5.bungee.api.chat.HoverEvent;
-import net.md_5.bungee.api.chat.hover.content.Text;
-import org.bukkit.command.Command;
-import org.bukkit.command.CommandExecutor;
-import org.bukkit.command.CommandSender;
-import org.bukkit.entity.Player;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.net.URI;
-import java.util.Map;
-
-public class MusicCommands implements CommandExecutor {
- private final AudioFileStorage storage;
- private MusicPlayer player;
-
- public MusicCommands(AudioFileStorage storage) {
- this.storage = storage;
- }
-
- @Override
- public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
- if (command.getName().equals("download")) {
- try {
- var cf = storage.download(URI.create(args[0]).toURL());
- sender.sendMessage("Started download");
- cf.handle((hash, ex) -> {
- if (ex != null)
- sender.sendMessage("ERROR downloading: " + ex.getMessage());
- else
- sender.spigot().sendMessage(
- new ComponentBuilder("Hash: " + hash)
- .event(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text("Click to copy")))
- .event(new ClickEvent(ClickEvent.Action.COPY_TO_CLIPBOARD, hash))
- .append("\nClick here to convert to 48 kbps")
- .event(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text("Click to do that")))
- .event(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/convert " + hash + " 48000"))
- .create()
- );
- return null;
- });
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- } else if (command.getName().equals("convert")) {
- try {
- var cf = storage.convert(args[0], Integer.parseInt(args[1]));
- sender.sendMessage("Converting " + args[0] + " to " + args[1] + "bps...");
- cf.thenAccept(f -> {
- sender.spigot().sendMessage(
- new ComponentBuilder("Converted " + args[0] + " to " + args[1] + "bps! Click to play")
- .event(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text("Click to play")))
- .event(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/play " + args[0] + " " + args[1]))
- .create()
- );
- }).exceptionally(ex -> {
- sender.sendMessage("ERROR converting: " + ex.getMessage());
- return null;
- });
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- } else if (command.getName().equals("play")) {
- if (args.length == 1) {
- StringBuilder msg = new StringBuilder("Available bitrates:");
- for (Map.Entry entry : storage.getVersions(args[0]).entrySet()) {
- msg.append(" ").append(entry.getKey());
- }
- sender.sendMessage(msg.toString());
- return true;
- }
- var file = storage.get(args[0], Integer.parseInt(args[1]));
- sender.sendMessage("Initializeng");
- player = new StaticMusicPlayer(((Player)sender).getLocation());
- player.init();
-
- try (FileInputStream fis = new FileInputStream(file)) {
- var song = new AudioFile(fis);
- song.load();
- player.setAudio(song);
- player.unpause();
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
-
- sender.sendMessage("done si r now playing it");
- } else if (command.getName().equals("stop")) {
- player.stop();
- sender.sendMessage("stopedd");
- } else if (command.getName().equals("pause")) {
- player.pause();
- sender.sendMessage("puased");
- } else if (command.getName().equals("resume")) {
- player.unpause();
- sender.sendMessage("unapuised");
- } else if (command.getName().equals("pmp")) {
- var p = (Player) sender;
- var pmp = PortableMediaPlayers.get(p.getItemInHand());
-
- switch (args[0]) {
- case "info" -> {
- sender.sendMessage(String.valueOf(pmp.id));
- sender.sendMessage(String.valueOf(pmp.premium));
- sender.sendMessage(pmp.engraving);
- }
- case "create" -> {
- pmp = PortableMediaPlayer.create(args[0].equals("yes"), args[1]);
- p.getInventory().addItem(pmp.getItemStack());
- }
- case "play" -> {
- var file = storage.get(args[1], Integer.parseInt(args[2]));
-
- try (FileInputStream fis = new FileInputStream(file)) {
- var song = new AudioFile(fis);
- song.load();
- pmp.play(song);
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
- }
- }
-
- return true;
- }
-}
diff --git a/src/main/java/eu/m724/musicPlugin/MusicPlugin.java b/src/main/java/eu/m724/musicPlugin/MusicPlugin.java
deleted file mode 100644
index 2309119..0000000
--- a/src/main/java/eu/m724/musicPlugin/MusicPlugin.java
+++ /dev/null
@@ -1,61 +0,0 @@
-package eu.m724.musicPlugin;
-
-import de.maxhenkel.voicechat.api.BukkitVoicechatService;
-import de.maxhenkel.voicechat.api.VoicechatConnection;
-import de.maxhenkel.voicechat.api.VoicechatServerApi;
-import eu.m724.musicPlugin.file.AudioFileStorage;
-import eu.m724.musicPlugin.item.ItemEvents;
-import eu.m724.musicPlugin.item.speaker.BlockChecker;
-import org.bukkit.plugin.java.JavaPlugin;
-
-public final class MusicPlugin extends JavaPlugin {
- @Override
- public void onEnable() {
- Statics.plugin = this;
-
- BukkitVoicechatService service = getServer().getServicesManager().load(BukkitVoicechatService.class);
-
- if (service != null) {
- service.registerPlugin(
- new MyVoicechatPlugin(
- this::onApiStarted,
- this::onPlayerConnected
- )
- );
- }
-
- getDataFolder().mkdir();
- var mcmd = new MusicCommands(new AudioFileStorage(getDataFolder()));
- getCommand("download").setExecutor(mcmd);
- getCommand("convert").setExecutor(mcmd);
- getCommand("play").setExecutor(mcmd);
- getCommand("stop").setExecutor(mcmd);
- getCommand("pause").setExecutor(mcmd);
- getCommand("resume").setExecutor(mcmd);
- getCommand("pmp").setExecutor(mcmd);
-
- getServer().getPluginManager().registerEvents(new ItemEvents(), this);
-
- // TODO do this better, maybe along events
- new BlockChecker().runTaskTimerAsynchronously(this, 0, 20);
- }
-
- private void onApiStarted(VoicechatServerApi api) {
- getLogger().info("registerating...");
-
- var category = api.volumeCategoryBuilder()
- .setId("musicc")
- .setName("Music players")
- .build();
-
- api.registerVolumeCategory(category);
-
- Statics.api = api;
-
- getLogger().info("Sucess");
- }
-
- private void onPlayerConnected(VoicechatConnection connection) {
- getLogger().info("Player connected: " + connection);
- }
-}
diff --git a/src/main/java/eu/m724/musicPlugin/MyVoicechatPlugin.java b/src/main/java/eu/m724/musicPlugin/MyVoicechatPlugin.java
deleted file mode 100644
index 0a254ad..0000000
--- a/src/main/java/eu/m724/musicPlugin/MyVoicechatPlugin.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package eu.m724.musicPlugin;
-
-import de.maxhenkel.voicechat.api.VoicechatApi;
-import de.maxhenkel.voicechat.api.VoicechatConnection;
-import de.maxhenkel.voicechat.api.VoicechatPlugin;
-import de.maxhenkel.voicechat.api.VoicechatServerApi;
-import de.maxhenkel.voicechat.api.events.EventRegistration;
-import de.maxhenkel.voicechat.api.events.PlayerConnectedEvent;
-import de.maxhenkel.voicechat.api.events.VoicechatServerStartedEvent;
-
-import java.util.function.Consumer;
-
-public class MyVoicechatPlugin implements VoicechatPlugin {
- private final Consumer apiConsumer;
- private final Consumer playerConnected;
-
- public MyVoicechatPlugin(Consumer apiConsumer, Consumer playerConnected) {
- this.apiConsumer = apiConsumer;
- this.playerConnected = playerConnected;
- }
-
- @Override
- public String getPluginId() {
- return "myplgugonbo";
- }
-
- @Override
- public void initialize(VoicechatApi api) {
- VoicechatPlugin.super.initialize(api);
- }
-
- @Override
- public void registerEvents(EventRegistration registration) {
- registration.registerEvent(VoicechatServerStartedEvent.class, this::onServerStarted);
- registration.registerEvent(PlayerConnectedEvent.class, this::onPlayerConnected);
- }
-
- public void onServerStarted(VoicechatServerStartedEvent event) {
- apiConsumer.accept(event.getVoicechat());
- }
-
- public void onPlayerConnected(PlayerConnectedEvent event) {
- playerConnected.accept(event.getConnection());
- }
-}
diff --git a/src/main/java/eu/m724/musicPlugin/Statics.java b/src/main/java/eu/m724/musicPlugin/Statics.java
deleted file mode 100644
index 55bda0c..0000000
--- a/src/main/java/eu/m724/musicPlugin/Statics.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package eu.m724.musicPlugin;
-
-import de.maxhenkel.voicechat.api.VoicechatServerApi;
-
-// TODO find a better way
-public class Statics {
- public static MusicPlugin plugin;
- public static VoicechatServerApi api;
-}
diff --git a/src/main/java/eu/m724/musicPlugin/file/AudioFile.java b/src/main/java/eu/m724/musicPlugin/file/AudioFile.java
deleted file mode 100644
index 63c1d48..0000000
--- a/src/main/java/eu/m724/musicPlugin/file/AudioFile.java
+++ /dev/null
@@ -1,72 +0,0 @@
-package eu.m724.musicPlugin.file;
-
-import org.gagravarr.ogg.OggFile;
-import org.gagravarr.opus.OpusAudioData;
-import org.gagravarr.opus.OpusFile;
-
-import java.io.*;
-import java.util.ArrayList;
-
-public class AudioFile {
- private final InputStream inputStream;
-
- private NotEncoder encoder;
- private String title, artist;
- private int duration;
-
- public AudioFile(File file) {
- try {
- this.inputStream = new FileInputStream(file);
- } catch (FileNotFoundException e) {
- throw new RuntimeException(e);
- }
- }
-
- public AudioFile(InputStream inputStream) {
- this.inputStream = inputStream;
- }
-
- public void load() throws IOException {
- if (encoder != null) return;
-
- var list = new ArrayList();
-
- var opus = new OpusFile(new OggFile(inputStream));
- var frames = 0;
-
- OpusAudioData packet;
- while ((packet = opus.getNextAudioPacket()) != null) {
- list.add(packet.getData());
- frames += packet.getNumberOfFrames();
- }
-
- this.duration = frames / 50; // a frame is 20 ms long so 20 * 50 is 1000
- System.out.printf("audiop file has %s framrs\n", frames);
-
- this.title = opus.getTags().getTitle();
- this.artist = opus.getTags().getArtist();
-
- this.encoder = new NotEncoder(list.toArray(byte[][]::new));
- }
-
-
- public NotEncoder getEncoder() {
- return encoder;
- }
-
- /**
- * @return if data was loaded i.e. if load was called
- */
- public boolean isLoaded() {
- return encoder != null;
- }
-
- /** @return Track duration in seconds */
- public int getTrackDuration() { return duration; }
-
- /** @return Track title if available */
- public String getTrackTitle() { return title; }
-
- /** @return Track artist if available */
- public String getTrackArtist() { return artist; }
-}
diff --git a/src/main/java/eu/m724/musicPlugin/file/AudioFileConverter.java b/src/main/java/eu/m724/musicPlugin/file/AudioFileConverter.java
deleted file mode 100644
index af13a2e..0000000
--- a/src/main/java/eu/m724/musicPlugin/file/AudioFileConverter.java
+++ /dev/null
@@ -1,39 +0,0 @@
-package eu.m724.musicPlugin.file;
-
-import net.bramp.ffmpeg.FFmpeg;
-import net.bramp.ffmpeg.FFmpegExecutor;
-import net.bramp.ffmpeg.builder.FFmpegBuilder;
-
-import java.io.File;
-import java.io.IOException;
-import java.net.URL;
-import java.util.concurrent.CompletableFuture;
-
-public class AudioFileConverter {
- private final FFmpeg ffmpeg;
-
- public AudioFileConverter(FFmpeg ffmpeg) {
- this.ffmpeg = ffmpeg;
- }
-
- public CompletableFuture convert(File input, File output, int bitrate) throws IOException {
- return convert(input.getAbsolutePath(), output, bitrate);
- }
-
- public CompletableFuture convert(URL source, File output, int bitrate) throws IOException {
- return convert(source.toString(), output, bitrate);
- }
-
- private CompletableFuture convert(String input, File output, int bitrate) throws IOException {
- var builder = new FFmpegBuilder()
- .setInput(input)
- .addOutput(output.getAbsolutePath())
- .setAudioChannels(1)
- .setAudioSampleRate(48_000)
- .setAudioBitRate(bitrate)
- .done();
-
- var executor = new FFmpegExecutor(ffmpeg);
- return CompletableFuture.runAsync(executor.createJob(builder));
- }
-}
diff --git a/src/main/java/eu/m724/musicPlugin/file/AudioFileStorage.java b/src/main/java/eu/m724/musicPlugin/file/AudioFileStorage.java
deleted file mode 100644
index c35a783..0000000
--- a/src/main/java/eu/m724/musicPlugin/file/AudioFileStorage.java
+++ /dev/null
@@ -1,118 +0,0 @@
-package eu.m724.musicPlugin.file;
-
-import net.bramp.ffmpeg.FFmpeg;
-import org.apache.commons.lang3.RandomStringUtils;
-
-import java.io.*;
-import java.net.URL;
-import java.nio.file.Files;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.util.*;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.CompletionException;
-import java.util.stream.Collectors;
-
-public class AudioFileStorage {
- private final File basePath;
- private final AudioFileConverter converter;
-
- public AudioFileStorage(File basePath) {
- this.basePath = basePath;
-
- try {
- this.converter = new AudioFileConverter(new FFmpeg()); // TODO better error handling and not here
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
-
- /**
- * Get versions (bitrates) of an audio file
- *
- * @param hash TODO NOT SANITIZED
- * @return Map of Bps to file, or null if no such hash
- */
- public Map getVersions(String hash) {
- var dir = new File(basePath, hash);
-
- var files = dir.listFiles();
- if (files == null) return null;
-
- return Arrays.stream(files).collect(Collectors.toMap(
- f -> Integer.parseInt(f.getName().split("\\.")[0]),
- f -> f
- ));
- }
-
- /**
- * Get an audio file of given original hash and bitrate
- *
- * @param hash TODO NOT SANITIZED
- * @param bitrate the bitrate in bps like 32000
- */
- public File get(String hash, int bitrate) {
- return new File(basePath, hash + "/" + bitrate + ".opus");
- }
-
- /**
- * Get the original file with a hash
- */
- public File getOriginal(String hash) {
- return new File(basePath, hash + "/0.original");
- }
-
- /**
- * Converts a (original) file to some bitrate
- *
- * @param hash the hash
- * @param bitrate the target bitrate in bps
- * @return the future with the new file
- */
- public CompletableFuture convert(String hash, int bitrate) throws IOException {
- var file = get(hash, bitrate);
- if (file.exists()) return CompletableFuture.completedFuture(file);
-
- var og = getOriginal(hash);
- if (!og.exists()) return null;
-
- return converter.convert(og, file, bitrate).thenApply(v -> file);
- }
-
- /**
- * Downloads an audio file and saves it as original
- *
- * @param url the url to download from
- * @return the hash
- */
- public CompletableFuture download(URL url) {
- return CompletableFuture.supplyAsync(() -> {
- try {
- var temp = new File(basePath, "temp_" + RandomStringUtils.randomAlphabetic(8));
- var digest = MessageDigest.getInstance("SHA-256");
-
- try (
- var input = new BufferedInputStream(url.openStream());
- var output = new FileOutputStream(temp)
- ) {
- var dataBuffer = new byte[1024];
- int bytesRead;
- while ((bytesRead = input.read(dataBuffer, 0, 1024)) != -1) {
- output.write(dataBuffer, 0, bytesRead);
- digest.update(dataBuffer, 0, bytesRead);
- }
- }
-
- String hash = HexFormat.of().formatHex(digest.digest());
- var og = getOriginal(hash);
- og.getParentFile().mkdir();
- if (!og.exists())
- Files.move(temp.toPath(), og.toPath());
-
- return hash;
- } catch (IOException | NoSuchAlgorithmException e) {
- throw new CompletionException(e);
- }
- });
- }
-}
diff --git a/src/main/java/eu/m724/musicPlugin/file/NotEncoder.java b/src/main/java/eu/m724/musicPlugin/file/NotEncoder.java
deleted file mode 100644
index 9aaf626..0000000
--- a/src/main/java/eu/m724/musicPlugin/file/NotEncoder.java
+++ /dev/null
@@ -1,47 +0,0 @@
-package eu.m724.musicPlugin.file;
-
-import de.maxhenkel.voicechat.api.opus.OpusEncoder;
-
-public class NotEncoder implements OpusEncoder {
- private final byte[][] opusFrames;
- private int i = 0;
- private final int size;
-
- private boolean closed = false;
-
- public NotEncoder(byte[][] opusFrames) {
- this.opusFrames = opusFrames;
- this.size = opusFrames.length;
- }
-
- public void seek(int frame) {
- i = Math.clamp(frame, 0, size);
- }
-
- public int getFrame() { return i; }
-
- public boolean hasRemaining() {
- return i < size;
- }
-
- @Override
- public byte[] encode(short[] rawAudio) {
- if (i < size)
- return opusFrames[i++];
- return null;
- }
-
- @Override
- public void resetState() {
- closed = false;
- i = 0;
- }
-
- @Override
- public boolean isClosed() {
- return closed;
- }
-
- @Override
- public void close() { closed = true; }
-}
diff --git a/src/main/java/eu/m724/musicPlugin/item/speaker/Speaker.java b/src/main/java/eu/m724/musicPlugin/item/speaker/Speaker.java
deleted file mode 100644
index 8a5c6fb..0000000
--- a/src/main/java/eu/m724/musicPlugin/item/speaker/Speaker.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package eu.m724.musicPlugin.item.speaker;
-
-import de.maxhenkel.voicechat.api.audiochannel.AudioChannel;
-import eu.m724.musicPlugin.file.AudioFile;
-import eu.m724.musicPlugin.player.MovingMusicPlayer;
-import eu.m724.musicPlugin.player.MusicPlayer;
-
-import java.util.function.Consumer;
-
-public abstract class Speaker {
- private final MusicPlayer musicPlayer;
- private Consumer destroyCallback;
-
- protected Speaker(MusicPlayer musicPlayer) {
- this.musicPlayer = musicPlayer;
- musicPlayer.init();
- }
-
- public void destroy() {
- musicPlayer.stop();
-
- onDestroy();
- destroyCallback.accept(null);
- }
-
- public void setDestroyCallback(Consumer consumer) {
- this.destroyCallback = consumer;
- }
-
- public MusicPlayer getMusicPlayer() {
- return musicPlayer;
- }
-
- abstract void onDestroy();
-}
diff --git a/src/main/java/eu/m724/musicPlugin/player/EntityMusicPlayer.java b/src/main/java/eu/m724/musicPlugin/player/EntityMusicPlayer.java
deleted file mode 100644
index becaabd..0000000
--- a/src/main/java/eu/m724/musicPlugin/player/EntityMusicPlayer.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package eu.m724.musicPlugin.player;
-
-import de.maxhenkel.voicechat.api.audiochannel.AudioChannel;
-import de.maxhenkel.voicechat.api.audiochannel.EntityAudioChannel;
-import org.bukkit.entity.Entity;
-
-import java.util.UUID;
-
-public class EntityMusicPlayer extends MusicPlayer {
- private final Entity entity;
-
- public EntityMusicPlayer(Entity entity) {
- this.entity = entity;
- }
-
- @Override
- AudioChannel createChannel() {
- var channel = api.createEntityAudioChannel(
- UUID.randomUUID(),
- api.fromEntity(entity)
- );
-
- channel.setCategory("musicc");
- channel.setDistance(32);
-
- return channel;
- }
-
- @Override
- public void setDistance(int distance) {
- ((EntityAudioChannel)this.channel).setDistance(distance);
- }
-}
diff --git a/src/main/java/eu/m724/musicPlugin/player/LocalMusicPlayer.java b/src/main/java/eu/m724/musicPlugin/player/LocalMusicPlayer.java
deleted file mode 100644
index 7c54947..0000000
--- a/src/main/java/eu/m724/musicPlugin/player/LocalMusicPlayer.java
+++ /dev/null
@@ -1,41 +0,0 @@
-package eu.m724.musicPlugin.player;
-
-import de.maxhenkel.voicechat.api.ServerLevel;
-import de.maxhenkel.voicechat.api.VoicechatConnection;
-import de.maxhenkel.voicechat.api.audiochannel.AudioChannel;
-import eu.m724.musicPlugin.Statics;
-import org.bukkit.entity.Player;
-
-import java.util.UUID;
-
-public class LocalMusicPlayer extends MusicPlayer {
- private final ServerLevel level;
- private final VoicechatConnection connection;
-
- public LocalMusicPlayer(Player player) throws NotConnectedException {
- this.level = Statics.api.fromServerLevel(player.getWorld());
- this.connection = Statics.api.getConnectionOf(player.getUniqueId());
-
- if (connection == null) {
- throw new NotConnectedException();
- }
- }
-
- @Override
- AudioChannel createChannel() {
- var channel = api.createStaticAudioChannel(
- UUID.randomUUID(),
- level,
- connection
- );
-
- channel.setCategory("musicc");
-
- return channel;
- }
-
- @Override
- public void setDistance(int distance) { } // distance doesn't apply to this one
-
- public static class NotConnectedException extends Exception {}
-}
diff --git a/src/main/java/eu/m724/musicPlugin/player/MovingMusicPlayer.java b/src/main/java/eu/m724/musicPlugin/player/MovingMusicPlayer.java
deleted file mode 100644
index d157bb0..0000000
--- a/src/main/java/eu/m724/musicPlugin/player/MovingMusicPlayer.java
+++ /dev/null
@@ -1,56 +0,0 @@
-package eu.m724.musicPlugin.player;
-
-import de.maxhenkel.voicechat.api.audiochannel.AudioChannel;
-import de.maxhenkel.voicechat.api.audiochannel.LocationalAudioChannel;
-import org.bukkit.Location;
-import org.bukkit.entity.Entity;
-
-import java.util.UUID;
-
-public class MovingMusicPlayer extends MusicPlayer {
- private Entity entity;
- private Location location;
-
- public void moveTo(Location location) {
- System.out.println("Mvoed to a");
-
- this.location = location;
- this.channel = createChannel();
-
- if (isReady() && !isPlaying()) {
- this.pause();
- this.unpause();
- }
- }
-
- public void attachTo(Entity entity) {
- var channel = api.createEntityAudioChannel(
- UUID.randomUUID(),
- api.fromEntity(entity)
- );
-
- channel.setCategory("musicc");
- channel.setDistance(4);
-
- this.channel = channel;
- }
-
- @Override
- AudioChannel createChannel() {
- var channel = api.createLocationalAudioChannel(
- UUID.randomUUID(),
- api.fromServerLevel(location.getWorld()),
- api.createPosition(location.getX(), location.getY(), location.getZ())
- );
-
- channel.setCategory("musicc");
- channel.setDistance(4);
-
- return channel;
- }
-
- @Override
- public void setDistance(int distance) {
- ((LocationalAudioChannel)this.channel).setDistance(distance);
- }
-}
diff --git a/src/main/java/eu/m724/musicPlugin/player/MusicPlayer.java b/src/main/java/eu/m724/musicPlugin/player/MusicPlayer.java
deleted file mode 100644
index 3c8b6c1..0000000
--- a/src/main/java/eu/m724/musicPlugin/player/MusicPlayer.java
+++ /dev/null
@@ -1,181 +0,0 @@
-package eu.m724.musicPlugin.player;
-
-import de.maxhenkel.voicechat.api.VoicechatServerApi;
-import de.maxhenkel.voicechat.api.audiochannel.AudioChannel;
-import de.maxhenkel.voicechat.api.audiochannel.AudioPlayer;
-import eu.m724.musicPlugin.Statics;
-import eu.m724.musicPlugin.file.AudioFile;
-
-import java.io.IOException;
-import java.util.function.Consumer;
-
-public abstract class MusicPlayer {
- // TODO find a better way
- final VoicechatServerApi api = Statics.api;
-
- AudioChannel channel;
-
- private boolean ready = false, playing = false;
- private AudioPlayer player;
- private AudioFile audioFile;
-
- private Consumer onAction = (r) -> {};
-
- abstract AudioChannel createChannel();
- abstract public void setDistance(int distance);
-
- /**
- * Initializes this music player
- */
- public void init() {
- this.channel = createChannel();
- }
-
- /**
- * Set the consumer that will be called after playback is paused, stopped, resumed etc
- * There can be only one for one music player
- *
- * @see TrackAction
- */
- public void setOnAction(Consumer onAction) {
- this.onAction = onAction;
- }
-
-
- /* Playback control */
-
- /**
- * Sets audio file to play
- * If it's not loaded, it will be loaded synchronously.
- * The file is not rewinded. If you want to start from beginning, {@link MusicPlayer#seek(int)}
- * After this, do {@link MusicPlayer#unpause()}
- *
- * @see MusicPlayer#pause()
- * @see MusicPlayer#stop()
- */
- public void setAudio(AudioFile audioFile) {
- if (audioFile != null)
- this.audioFile = audioFile;
-
- if (ready)
- stop();
-
- if (!audioFile.isLoaded()) {
- try {
- System.out.println("Audio not already loaded, so I load it");
- audioFile.load();
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
-
- System.out.printf("Audio loaded it's %d seconds long, so I'm guessing %d frames\n", audioFile.getTrackDuration(), audioFile.getTrackDuration() * 50);
-
- ready = true;
-
- unpause();
- }
-
- public void unpause() {
- if (!ready || playing) return;
- System.out.println("playback unpaused");
-
- var sa = new short[960];
- var enc = audioFile.getEncoder();
-
- player = api.createAudioPlayer(channel, enc, () -> enc.hasRemaining() ? sa : null);
- player.setOnStopped(this::onStop);
- player.startPlaying();
-
- // TODO this may take a long time and it might glitch if it does
- onAction.accept(TrackAction.UNPAUSE);
-
- playing = true;
-
- System.out.println("playback unpaused now");
- }
-
- /**
- * Stops playback and rewinds
- */
- public void stop() {
- if (!ready) return;
-
- stopPlayback(false);
- audioFile.getEncoder().resetState();
- }
-
- /**
- * Pauses playback
- */
- public void pause() {
- if (!ready || playing) return;
- stopPlayback(true);
- }
-
- /**
- * Seek to some point in current track
- *
- * @param target the target time in milliseconds
- */
- public void seek(int target) {
- audioFile.getEncoder().seek(target / 20); // a frame is 20 ms usually
- }
-
-
- /* Internal methods */
-
-
- private void stopPlayback(boolean pause) {
- System.out.println("playback stopped");
- playing = false;
- ready = pause;
-
- player.stopPlaying();
- player = null;
-
- onStop();
- }
-
- private void onStop() {
- if (ready && !playing) { // paused
- System.out.println("I detected pause");
- onAction.accept(TrackAction.PAUSE);
- playing = false;
- } else if (ready) { // not paused and still playing
- System.out.println("I detected end");
- onAction.accept(TrackAction.DURATION);
- ready = false;
- } else { // not playing
- System.out.println("I detected stop");
- onAction.accept(TrackAction.STOP);
- }
- }
-
- public enum TrackAction {
- /**
- * Song stopped playing because it played
- */
- DURATION,
- /**
- * {@link MusicPlayer#stop()} was called, or started playing a new track
- */
- STOP,
- /**
- * {@link MusicPlayer#pause()} was called
- */
- PAUSE,
- /**
- * {@link MusicPlayer#unpause()} was called
- */
- UNPAUSE
- }
-
- public boolean isReady() {
- return ready;
- }
-
- public boolean isPlaying() {
- return ready && playing;
- }
-}
diff --git a/src/main/java/eu/m724/music_plugin/DebugLogger.java b/src/main/java/eu/m724/music_plugin/DebugLogger.java
new file mode 100644
index 0000000..11eecb0
--- /dev/null
+++ b/src/main/java/eu/m724/music_plugin/DebugLogger.java
@@ -0,0 +1,51 @@
+package eu.m724.music_plugin;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public class DebugLogger {
+ static Logger logger;
+
+ public static void info(String message, Object... format) {
+ log(Level.INFO, message, format);
+ }
+
+ public static void warning(String message, Object... format) {
+ log(Level.WARNING, message, format);
+ }
+
+ public static void severe(String message, Object... format) {
+ log(Level.SEVERE, message, format);
+ }
+
+ public static void fine(String message, Object... format) {
+ log(Level.FINE, message, format);
+ }
+
+ public static void finer(String message, Object... format) {
+ log(Level.FINER, message, format);
+ }
+
+ private static void log(Level level, String message, Object... format) {
+ if (logger.getLevel().intValue() > level.intValue()) return;
+
+ var caller = Thread.currentThread().getStackTrace()[3].getClassName();
+
+ if (caller.startsWith("eu.m724.music_plugin."))
+ caller = caller.substring(21);
+
+ message = "[" + caller + "] " + message.formatted(format);
+
+ if (level.intValue() < Level.INFO.intValue()) { // levels below info are never logged even if set for some reason
+ // colors text gray (cyan is close to gray)
+ if (level == Level.FINE) {
+ message = "\033[38;5;250m" + message + "\033[39m";
+ } else {
+ message = "\033[38;5;245m" + message + "\033[39m";
+ }
+ level = Level.INFO;
+ }
+
+ logger.log(level, message);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/eu/m724/music_plugin/MusicCommands.java b/src/main/java/eu/m724/music_plugin/MusicCommands.java
new file mode 100644
index 0000000..866c1ce
--- /dev/null
+++ b/src/main/java/eu/m724/music_plugin/MusicCommands.java
@@ -0,0 +1,44 @@
+package eu.m724.music_plugin;
+
+import eu.m724.music_plugin.item.PortableMediaPlayer;
+import eu.m724.music_plugin.item.PortableMediaPlayers;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandExecutor;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+
+import java.io.IOException;
+
+public class MusicCommands implements CommandExecutor {
+ @Override
+ public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
+ if (command.getName().equals("pmp")) {
+ var p = (Player) sender;
+ var pmp = PortableMediaPlayers.get(p.getItemInHand());
+
+ switch (args[0]) {
+ case "info" -> {
+ sender.sendMessage(String.valueOf(pmp.id));
+ sender.sendMessage(String.valueOf(pmp.premium));
+ sender.sendMessage(pmp.engraving);
+ }
+ case "create" -> {
+ pmp = PortableMediaPlayer.create(args[0].equals("yes"), args[1]);
+ p.getInventory().addItem(pmp.getItemStack());
+ }
+ case "play" -> {
+ var storage = MusicPlugin.getStorage();
+ var path = storage.get(args[1], Integer.parseInt(args[2]));
+
+ try {
+ pmp.play(path.toFile());
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ }
+
+ return true;
+ }
+}
diff --git a/src/main/java/eu/m724/music_plugin/MusicPlugin.java b/src/main/java/eu/m724/music_plugin/MusicPlugin.java
new file mode 100644
index 0000000..000bc48
--- /dev/null
+++ b/src/main/java/eu/m724/music_plugin/MusicPlugin.java
@@ -0,0 +1,90 @@
+package eu.m724.music_plugin;
+
+import de.maxhenkel.voicechat.api.BukkitVoicechatService;
+import de.maxhenkel.voicechat.api.VoicechatServerApi;
+import eu.m724.music_plugin.audio.storage.AudioFileStorage;
+import eu.m724.music_plugin.audio.Converter;
+import eu.m724.music_plugin.audio.storage.Downloader;
+import eu.m724.music_plugin.item.ItemEvents;
+import eu.m724.music_plugin.item.speaker.BlockChecker;
+import net.bramp.ffmpeg.FFmpeg;
+import org.bukkit.NamespacedKey;
+import org.bukkit.plugin.java.JavaPlugin;
+
+import java.io.IOException;
+import java.util.logging.Level;
+
+public final class MusicPlugin extends JavaPlugin {
+ private static MusicPlugin INSTANCE;
+ private static VoicechatServerApi VOICECHAT_API;
+ private static AudioFileStorage AUDIO_FILE_STORAGE;
+ private static Converter CONVERTER;
+ private static Downloader DOWNLOADER;
+
+ @Override
+ public void onEnable() {
+ var start = System.nanoTime();
+ INSTANCE = this;
+
+ getLogger().setLevel(Level.FINEST);
+ DebugLogger.logger = getLogger();
+
+ var service = getServer().getServicesManager().load(BukkitVoicechatService.class);
+ service.registerPlugin(new MyVoicechatPlugin(api -> VOICECHAT_API = api));
+
+ getDataFolder().mkdir();
+ var storagePath = getDataFolder().toPath().resolve("storage");
+
+ AUDIO_FILE_STORAGE = new AudioFileStorage(storagePath);
+ DOWNLOADER = new Downloader(storagePath);
+
+ try {
+ CONVERTER = new Converter(new FFmpeg());
+ } catch (IOException e) {
+ throw new RuntimeException("Failed to initialize FFmpeg", e);
+ }
+
+ getCommand("test").setExecutor(new TestCommand());
+
+ /*var mcmd = new MusicCommands(new AudioFileStorage(getDataFolder()));
+ getCommand("download").setExecutor(mcmd);
+ getCommand("convert").setExecutor(mcmd);
+ getCommand("play").setExecutor(mcmd);
+ getCommand("stop").setExecutor(mcmd);
+ getCommand("pause").setExecutor(mcmd);
+ getCommand("resume").setExecutor(mcmd);
+ getCommand("pmp").setExecutor(mcmd);*/
+
+ getServer().getPluginManager().registerEvents(new ItemEvents(), this);
+
+ // TODO do this better, maybe along events
+ new BlockChecker().runTaskTimerAsynchronously(this, 0, 20);
+
+ var end = System.nanoTime();
+ DebugLogger.fine("Enabled in %.3f milliseconds", (end - start) / 1000000.0);
+ }
+
+ public static MusicPlugin getInstance() {
+ return INSTANCE;
+ }
+
+ public static VoicechatServerApi getVoicechatApi() {
+ return VOICECHAT_API;
+ }
+
+ public static AudioFileStorage getStorage() {
+ return AUDIO_FILE_STORAGE;
+ }
+
+ public static Converter getConverter() {
+ return CONVERTER;
+ }
+
+ public static Downloader getDownloader() {
+ return DOWNLOADER;
+ }
+
+ public static NamespacedKey getNamespacedKey(String key) {
+ return new NamespacedKey(INSTANCE, key);
+ }
+}
diff --git a/src/main/java/eu/m724/music_plugin/MyVoicechatPlugin.java b/src/main/java/eu/m724/music_plugin/MyVoicechatPlugin.java
new file mode 100644
index 0000000..9f56d14
--- /dev/null
+++ b/src/main/java/eu/m724/music_plugin/MyVoicechatPlugin.java
@@ -0,0 +1,42 @@
+package eu.m724.music_plugin;
+
+import de.maxhenkel.voicechat.api.VoicechatPlugin;
+import de.maxhenkel.voicechat.api.VoicechatServerApi;
+import de.maxhenkel.voicechat.api.events.EventRegistration;
+import de.maxhenkel.voicechat.api.events.VoicechatServerStartedEvent;
+
+import java.util.function.Consumer;
+
+public class MyVoicechatPlugin implements VoicechatPlugin {
+ private final Consumer apiConsumer;
+
+ public MyVoicechatPlugin(Consumer apiConsumer) {
+ this.apiConsumer = apiConsumer;
+ }
+
+ @Override
+ public String getPluginId() {
+ return "music724";
+ }
+
+ @Override
+ public void registerEvents(EventRegistration registration) {
+ registration.registerEvent(VoicechatServerStartedEvent.class, this::onServerStarted);
+ }
+
+ private void onServerStarted(VoicechatServerStartedEvent event) {
+ var api = event.getVoicechat();
+
+ var category = api.volumeCategoryBuilder()
+ .setId("music")
+ .setName("Music players")
+ .build();
+
+ api.registerVolumeCategory(category);
+
+ apiConsumer.accept(api);
+
+ DebugLogger.fine("Registered");
+ }
+}
+
diff --git a/src/main/java/eu/m724/music_plugin/TestCommand.java b/src/main/java/eu/m724/music_plugin/TestCommand.java
new file mode 100644
index 0000000..d4a5180
--- /dev/null
+++ b/src/main/java/eu/m724/music_plugin/TestCommand.java
@@ -0,0 +1,99 @@
+package eu.m724.music_plugin;
+
+import eu.m724.music_plugin.audio.channel.LocationalChannel;
+import eu.m724.music_plugin.audio.player.OpusFilePlayer;
+import net.md_5.bungee.api.chat.ClickEvent;
+import net.md_5.bungee.api.chat.ComponentBuilder;
+import net.md_5.bungee.api.chat.HoverEvent;
+import net.md_5.bungee.api.chat.hover.content.Text;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandExecutor;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+import org.gagravarr.opus.OpusFile;
+
+import java.io.IOException;
+import java.net.URI;
+
+// TODO remove
+public class TestCommand implements CommandExecutor {
+ private OpusFilePlayer opusFilePlayer;
+
+ @Override
+ public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
+ var player = (Player) sender;
+
+ if (args[0].equals("download")) {
+ testDownload(player, args[1]);
+ } else if (args[0].equals("convert")) {
+ testConvert(player, args[1], Integer.parseInt(args[2]));
+ } else if (args[0].equals("play")) {
+ testPlay(player, args[1], Integer.parseInt(args[2]));
+ } else if (args[0].equals("pause")) {
+ opusFilePlayer.pause();
+ } else if (args[0].equals("unpause")) {
+ opusFilePlayer.unpause();
+ }
+
+ return true;
+ }
+
+ private void testDownload(Player player, String url) {
+ player.sendMessage("Downloading " + url);
+ MusicPlugin.getDownloader().download(URI.create(url)).handle((hash, ex) -> {
+ if (ex != null)
+ player.sendMessage("ERROR downloading: " + ex.getMessage());
+ else
+ player.spigot().sendMessage(
+ new ComponentBuilder("Hash: " + hash)
+ .event(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text("Click to copy")))
+ .event(new ClickEvent(ClickEvent.Action.COPY_TO_CLIPBOARD, hash))
+ .append("\nClick here to convert to 48 kbps")
+ .event(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text("Click to do that")))
+ .event(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/convert " + hash + " 48000"))
+ .create()
+ );
+ return null;
+ });
+ }
+
+ private void testConvert(Player player, String hash, int bitrate) {
+ try {
+ MusicPlugin.getStorage().convert(MusicPlugin.getConverter(), hash, bitrate).handle((f, ex) -> {
+ if (ex != null)
+ player.sendMessage("ERROR converting: " + ex.getMessage());
+ else
+ player.spigot().sendMessage(
+ new ComponentBuilder("Converted " + hash.substring(0, 7) + "... to " + bitrate + "bps! Click to play")
+ .event(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new Text("Click to play")))
+ .event(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/test play " + hash + " " + bitrate))
+ .create()
+ );
+ return null;
+ });
+ } catch (IOException e) {
+ e.printStackTrace();
+ player.sendMessage("Error");
+ }
+ }
+
+ private void testPlay(Player player, String hash, int bitrate) {
+ var channel = new LocationalChannel(player.getLocation()).getAudioChannel();
+
+ if (opusFilePlayer != null) {
+ opusFilePlayer.stop();
+ }
+
+ try {
+ opusFilePlayer = new OpusFilePlayer(new OpusFile(MusicPlugin.getStorage().get(hash, bitrate).toFile()), channel);
+ opusFilePlayer.play();
+ opusFilePlayer.setOnTrackEvent(e -> {
+ player.sendMessage("Event: " + e);
+ });
+ player.sendMessage("Now playing");
+ } catch (IOException e) {
+ e.printStackTrace();
+ player.sendMessage("Error");
+ }
+ }
+}
diff --git a/src/main/java/eu/m724/music_plugin/audio/Converter.java b/src/main/java/eu/m724/music_plugin/audio/Converter.java
new file mode 100644
index 0000000..7eb3799
--- /dev/null
+++ b/src/main/java/eu/m724/music_plugin/audio/Converter.java
@@ -0,0 +1,56 @@
+package eu.m724.music_plugin.audio;
+
+import net.bramp.ffmpeg.FFmpeg;
+import net.bramp.ffmpeg.FFmpegExecutor;
+import net.bramp.ffmpeg.builder.FFmpegBuilder;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.nio.file.Path;
+import java.util.concurrent.CompletableFuture;
+
+public class Converter {
+ private final FFmpeg ffmpeg;
+
+ public Converter(FFmpeg ffmpeg) {
+ this.ffmpeg = ffmpeg;
+ }
+
+ /**
+ * Converts a media file to OPUS with given bitrate.
+ *
+ * @param bitrate the target bitrate in Bps (bits per second)
+ * @return the future that results in {@code output} when done
+ * @throws IOException if something went wrong with FFmpeg
+ */
+ public CompletableFuture convert(Path input, Path output, int bitrate) throws IOException {
+ return convert(input.toAbsolutePath().toString(), output, bitrate);
+ }
+
+ /**
+ * Converts a media file to OPUS with given bitrate.
+ *
+ * @deprecated It's better to use this plugin to download
+ * @param bitrate the target bitrate in Bps (bits per second)
+ * @return the future that results in {@code output} when done
+ * @throws IOException if something went wrong with FFmpeg
+ */
+ @Deprecated(forRemoval = true)
+ public CompletableFuture convert(URL source, Path output, int bitrate) throws IOException {
+ return convert(source.toString(), output, bitrate);
+ }
+
+ private CompletableFuture convert(String input, Path output, int bitrate) throws IOException {
+ var builder = new FFmpegBuilder()
+ .setInput(input)
+ .addOutput(output.toAbsolutePath().toUri())
+ .setAudioChannels(1)
+ .setAudioSampleRate(48_000)
+ .setAudioBitRate(bitrate)
+ .done();
+
+ var executor = new FFmpegExecutor(ffmpeg);
+ return CompletableFuture.runAsync(executor.createJob(builder)).thenApply(v -> output);
+ }
+}
diff --git a/src/main/java/eu/m724/music_plugin/audio/OpusFileEncoder.java b/src/main/java/eu/m724/music_plugin/audio/OpusFileEncoder.java
new file mode 100644
index 0000000..668ac27
--- /dev/null
+++ b/src/main/java/eu/m724/music_plugin/audio/OpusFileEncoder.java
@@ -0,0 +1,63 @@
+package eu.m724.music_plugin.audio;
+
+import de.maxhenkel.voicechat.api.opus.OpusEncoder;
+import eu.m724.music_plugin.DebugLogger;
+import org.apache.commons.lang3.NotImplementedException;
+import org.gagravarr.opus.OpusFile;
+
+import java.io.File;
+import java.io.IOException;
+
+public class OpusFileEncoder implements OpusEncoder {
+ private final OpusFile opusFile;
+ private boolean closed = false;
+
+ public OpusFileEncoder(OpusFile file) {
+ this.opusFile = file;
+ }
+
+ @Override
+ public byte[] encode(short[] rawAudio) {
+ try {
+ var packet = opusFile.getNextAudioPacket();
+ if (packet != null)
+ return packet.getData();
+ else
+ return new byte[0];
+ } catch (IOException e) {
+ throw new RuntimeException("Reading next packet", e);
+ }
+ }
+
+ @Override
+ public void resetState() {
+ // TODO
+ throw new NotImplementedException();
+ }
+
+ @Override
+ public boolean isClosed() {
+ return closed;
+ }
+
+ @Override
+ public void close() {
+ DebugLogger.fine("opus file just closed");
+ if (closed) return;
+ closed = true;
+
+ try {
+ opusFile.close();
+ } catch (IOException e) {
+ throw new RuntimeException("Closing opus file", e);
+ }
+ }
+
+ /* SUPPLIER */
+
+ private final short[] array = new short[960];
+
+ public short[] supplier() {
+ return !closed ? array : null;
+ }
+}
diff --git a/src/main/java/eu/m724/music_plugin/audio/channel/AbstractChannel.java b/src/main/java/eu/m724/music_plugin/audio/channel/AbstractChannel.java
new file mode 100644
index 0000000..3afdd0e
--- /dev/null
+++ b/src/main/java/eu/m724/music_plugin/audio/channel/AbstractChannel.java
@@ -0,0 +1,16 @@
+package eu.m724.music_plugin.audio.channel;
+
+import de.maxhenkel.voicechat.api.audiochannel.AudioChannel;
+
+public abstract class AbstractChannel {
+ protected T channel;
+
+ public T getAudioChannel() {
+ if (this.channel == null)
+ this.channel = doCreateChannel();
+ return this.channel;
+ }
+
+ protected abstract T doCreateChannel();
+ abstract void setDistance(int distance);
+}
diff --git a/src/main/java/eu/m724/music_plugin/audio/channel/EntityChannel.java b/src/main/java/eu/m724/music_plugin/audio/channel/EntityChannel.java
new file mode 100644
index 0000000..3a6b129
--- /dev/null
+++ b/src/main/java/eu/m724/music_plugin/audio/channel/EntityChannel.java
@@ -0,0 +1,36 @@
+package eu.m724.music_plugin.audio.channel;
+
+import de.maxhenkel.voicechat.api.audiochannel.EntityAudioChannel;
+import eu.m724.music_plugin.MusicPlugin;
+import org.bukkit.entity.Entity;
+
+import java.util.UUID;
+
+public class EntityChannel extends AbstractChannel {
+ private final Entity entity;
+
+ public EntityChannel(Entity entity) {
+ this.entity = entity;
+ }
+
+ @Override
+ protected EntityAudioChannel doCreateChannel() {
+ var api = MusicPlugin.getVoicechatApi();
+
+ var channel = api.createEntityAudioChannel(
+ UUID.randomUUID(),
+ api.fromEntity(entity)
+ );
+
+ channel.setCategory("music");
+ channel.setDistance(32);
+
+ return channel;
+ }
+
+ @Override
+ public void setDistance(int distance) {
+ // we go getChannel() not just channel, because getChannel() creates it if it doesn't exist
+ getAudioChannel().setDistance(distance);
+ }
+}
diff --git a/src/main/java/eu/m724/musicPlugin/player/StaticMusicPlayer.java b/src/main/java/eu/m724/music_plugin/audio/channel/LocationalChannel.java
similarity index 53%
rename from src/main/java/eu/m724/musicPlugin/player/StaticMusicPlayer.java
rename to src/main/java/eu/m724/music_plugin/audio/channel/LocationalChannel.java
index f751555..b51fd38 100644
--- a/src/main/java/eu/m724/musicPlugin/player/StaticMusicPlayer.java
+++ b/src/main/java/eu/m724/music_plugin/audio/channel/LocationalChannel.java
@@ -1,27 +1,29 @@
-package eu.m724.musicPlugin.player;
+package eu.m724.music_plugin.audio.channel;
-import de.maxhenkel.voicechat.api.audiochannel.AudioChannel;
import de.maxhenkel.voicechat.api.audiochannel.LocationalAudioChannel;
+import eu.m724.music_plugin.MusicPlugin;
import org.bukkit.Location;
import java.util.UUID;
-public class StaticMusicPlayer extends MusicPlayer {
+public class LocationalChannel extends AbstractChannel {
private final Location location;
- public StaticMusicPlayer(Location location) {
+ public LocationalChannel(Location location) {
this.location = location;
}
@Override
- AudioChannel createChannel() {
+ protected LocationalAudioChannel doCreateChannel() {
+ var api = MusicPlugin.getVoicechatApi();
+
var channel = api.createLocationalAudioChannel(
UUID.randomUUID(),
api.fromServerLevel(location.getWorld()),
api.createPosition(location.getX(), location.getY(), location.getZ())
);
- channel.setCategory("musicc");
+ channel.setCategory("music");
channel.setDistance(32);
return channel;
@@ -29,6 +31,7 @@ public class StaticMusicPlayer extends MusicPlayer {
@Override
public void setDistance(int distance) {
- ((LocationalAudioChannel)this.channel).setDistance(distance);
+ // we go getChannel() not just channel, because getChannel() creates it if it doesn't exist
+ getAudioChannel().setDistance(distance);
}
}
diff --git a/src/main/java/eu/m724/music_plugin/audio/player/OpusFilePlayer.java b/src/main/java/eu/m724/music_plugin/audio/player/OpusFilePlayer.java
new file mode 100644
index 0000000..dbcdcff
--- /dev/null
+++ b/src/main/java/eu/m724/music_plugin/audio/player/OpusFilePlayer.java
@@ -0,0 +1,129 @@
+package eu.m724.music_plugin.audio.player;
+
+import de.maxhenkel.voicechat.api.audiochannel.AudioChannel;
+import de.maxhenkel.voicechat.api.audiochannel.AudioPlayer;
+import eu.m724.music_plugin.DebugLogger;
+import eu.m724.music_plugin.MusicPlugin;
+import eu.m724.music_plugin.audio.OpusFileEncoder;
+import org.gagravarr.opus.OpusFile;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.function.Consumer;
+
+public class OpusFilePlayer {
+ private final OpusFileEncoder encoder;
+
+ private AudioChannel channel;
+ private AudioPlayer audioPlayer;
+ private boolean playing = false;
+
+ private Consumer onTrackEvent = (r) -> {};
+
+ public OpusFilePlayer(OpusFile opusFile, AudioChannel channel) {
+ this.encoder = new OpusFileEncoder(opusFile);
+ this.channel = channel;
+ }
+
+ public OpusFilePlayer(File file, AudioChannel channel) throws IOException {
+ this(new OpusFile(file), channel);
+ }
+
+ /**
+ * Set the consumer that will be called after playback is paused, stopped, resumed etc
+ * There can be only one for one music player
+ *
+ * @see TrackEvent
+ */
+ public void setOnTrackEvent(Consumer onTrackEvent) {
+ this.onTrackEvent = onTrackEvent;
+ }
+
+ public void setChannel(AudioChannel channel) {
+ DebugLogger.finer("Changing channel...");
+ this.channel = channel;
+
+ if (!playing) {
+ // to not call events pointlessly
+ var ote = this.onTrackEvent;
+ this.onTrackEvent = (r) -> {};
+
+ // playing recreates the audio channel
+ pause();
+ play();
+
+ this.onTrackEvent = ote;
+ }
+
+ DebugLogger.fine("Channel changed");
+ }
+
+ /**
+ * Starts or resumes playback
+ */
+ public void play() {
+ DebugLogger.finer("Playback starting...");
+ if (playing) return;
+
+ audioPlayer = MusicPlugin.getVoicechatApi().createAudioPlayer(channel, encoder, encoder::supplier);
+ audioPlayer.setOnStopped(this::onPlayerStopped);
+ audioPlayer.startPlaying();
+
+ playing = true;
+
+ onTrackEvent.accept(TrackEvent.START);
+ DebugLogger.fine("Playback started");
+ }
+
+ /**
+ * An alias for {@link #play()}
+ */
+ public void unpause() {
+ play();
+ }
+
+ /**
+ * Pause playback
+ * To resume, {@link #play()}
+ */
+ public void pause() {
+ stop(false);
+ }
+
+ /**
+ * Stops playback (pauses and rewinds)
+ */
+ public void stop() {
+ stop(true);
+ }
+
+ private void stop(boolean reset) {
+ DebugLogger.fine("Playback stopping... (reset: %s)", reset);
+ if (!playing) return;
+
+ playing = false;
+ audioPlayer.stopPlaying();
+
+ if (reset)
+ encoder.resetState();
+
+ DebugLogger.fine("Playback stopped (reset: %s)", reset);
+ }
+
+ private void onPlayerStopped() {
+ if (playing) {
+ DebugLogger.fine("onPlayerStopped called and playing, calling END");
+ onTrackEvent.accept(TrackEvent.END);
+ } else {
+ DebugLogger.fine("onPlayerStopped called and not playing, calling STOP");
+ onTrackEvent.accept(TrackEvent.STOP);
+ }
+ }
+
+ /**
+ * @return Is player playing (not paused)
+ */
+ public boolean isPlaying() {
+ return playing;
+ }
+}
diff --git a/src/main/java/eu/m724/music_plugin/audio/player/TrackEvent.java b/src/main/java/eu/m724/music_plugin/audio/player/TrackEvent.java
new file mode 100644
index 0000000..b850e3a
--- /dev/null
+++ b/src/main/java/eu/m724/music_plugin/audio/player/TrackEvent.java
@@ -0,0 +1,17 @@
+package eu.m724.music_plugin.audio.player;
+
+public enum TrackEvent {
+ /**
+ * Track ended
+ */
+ END,
+ /**
+ * Track was stopped (or paused)
+ * If it ended, {@link TrackEvent#END} is used instead
+ */
+ STOP,
+ /**
+ * Track started playing (or unpaused)
+ */
+ START
+}
diff --git a/src/main/java/eu/m724/music_plugin/audio/storage/AudioFileStorage.java b/src/main/java/eu/m724/music_plugin/audio/storage/AudioFileStorage.java
new file mode 100644
index 0000000..f90433a
--- /dev/null
+++ b/src/main/java/eu/m724/music_plugin/audio/storage/AudioFileStorage.java
@@ -0,0 +1,89 @@
+package eu.m724.music_plugin.audio.storage;
+
+import eu.m724.music_plugin.audio.Converter;
+
+import java.io.*;
+import java.nio.file.Files;
+import java.nio.file.NotDirectoryException;
+import java.nio.file.Path;
+import java.util.*;
+import java.util.concurrent.CompletableFuture;
+import java.util.stream.Collectors;
+
+public class AudioFileStorage {
+ private final Path basePath;
+
+ public AudioFileStorage(Path basePath) {
+ this.basePath = basePath;
+ }
+
+ /**
+ * Get versions (bitrates) of an audio file
+ * Returns a map of bitrate: file, or null if no such hash
+ * Original is 0
+ *
+ * @param hash TODO NOT SANITIZED
+ * @return Map of Bps to file, or null if no such hash
+ */
+ public Map getVersions(String hash) throws IOException {
+ assert isHashValid(hash);
+
+ var dir = basePath.resolve(hash);
+
+ try (var files = Files.list(dir)) {
+ return files.collect(Collectors.toMap(
+ k -> Integer.parseInt(k.getFileName().toString().split("\\.")[0]),
+ v -> v
+ ));
+ } catch (NotDirectoryException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Get an audio file of given original hash and bitrate
+ *
+ * @param hash the hex sha256 hash of the original
+ * @param bitrate the bitrate in bps like 32000
+ */
+ public Path get(String hash, int bitrate) {
+ assert isHashValid(hash);
+ return basePath.resolve(hash).resolve(bitrate + ".opus");
+ }
+
+ /**
+ * Get the original file with a hash
+ */
+ public Path getOriginal(String hash) {
+ assert isHashValid(hash);
+ return basePath.resolve(hash).resolve("0.original");
+ }
+
+ /**
+ * Checks if a hash is a valid SHA256 hash
+ * @param hash the hash in hex format
+ */
+ private boolean isHashValid(String hash) {
+ var bytes = HexFormat.of().parseHex(hash);
+ return bytes.length == 32;
+ }
+
+ // TODO move those
+
+ /**
+ * Converts a (original) file to some bitrate
+ *
+ * @param hash the hash
+ * @param bitrate the target bitrate in bps
+ * @return the future with the new file
+ */
+ public CompletableFuture convert(Converter converter, String hash, int bitrate) throws IOException {
+ var file = get(hash, bitrate);
+ if (Files.exists(file)) return CompletableFuture.completedFuture(file);
+
+ var og = getOriginal(hash);
+ if (!Files.exists(og)) return null;
+
+ return converter.convert(og, file, bitrate).thenApply(v -> file);
+ }
+}
diff --git a/src/main/java/eu/m724/music_plugin/audio/storage/Downloader.java b/src/main/java/eu/m724/music_plugin/audio/storage/Downloader.java
new file mode 100644
index 0000000..47af394
--- /dev/null
+++ b/src/main/java/eu/m724/music_plugin/audio/storage/Downloader.java
@@ -0,0 +1,86 @@
+package eu.m724.music_plugin.audio.storage;
+
+import eu.m724.music_plugin.DebugLogger;
+import org.apache.commons.lang3.RandomStringUtils;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+import java.nio.file.FileAlreadyExistsException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.HexFormat;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+
+public class Downloader {
+ private final Path basePath;
+
+ public Downloader(Path basePath) {
+ this.basePath = basePath;
+ }
+
+ /**
+ * Downloads an audio file and saves it as original
+ *
+ * @param uri the uri to download from
+ * @return the downloaded file's SHA-256 hash (in a future)
+ */
+ public CompletableFuture download(URI uri) {
+ DebugLogger.fine("About to download from " + uri);
+
+ try (
+ var client = HttpClient.newBuilder().followRedirects(HttpClient.Redirect.ALWAYS).build();
+ ) {
+ var request = HttpRequest.newBuilder(uri).GET().build();
+ return client.sendAsync(request, HttpResponse.BodyHandlers.ofInputStream()).thenApply(this::futureFunction);
+ }
+ }
+
+ private String futureFunction(HttpResponse response) {
+ var contentLength = response.headers().firstValue("Content-Length");
+ DebugLogger.finer("Connected. Size: %s", contentLength.orElse(null));
+
+ var temp = basePath.resolve("temp_" + RandomStringUtils.randomAlphabetic(8));
+ MessageDigest digest;
+ try {
+ digest = MessageDigest.getInstance("SHA-256");
+ } catch (NoSuchAlgorithmException e) {
+ throw new RuntimeException(e);
+ }
+
+ try (
+ var input = response.body();
+ var output = Files.newOutputStream(temp)
+ ) {
+ var dataBuffer = new byte[1024 * 1024];
+ int bytesRead;
+ while ((bytesRead = input.read(dataBuffer, 0, dataBuffer.length)) != -1) {
+ output.write(dataBuffer, 0, bytesRead);
+ digest.update(dataBuffer, 0, bytesRead);
+ }
+ } catch (IOException e) {
+ throw new RuntimeException("Exception downloading from " + response.uri(), e);
+ }
+
+ String hash = HexFormat.of().formatHex(digest.digest());
+ DebugLogger.fine("Downloaded, hash: " + hash);
+
+ try {
+ Files.createDirectory(basePath.resolve(hash));
+ Files.move(temp, basePath.resolve(hash).resolve("0.original"));
+ } catch (FileAlreadyExistsException e) {
+ DebugLogger.fine("Already exists!");
+ } catch (IOException e) {
+ throw new CompletionException("Exception saving file", e);
+ }
+
+ DebugLogger.fine("Done");
+ return hash;
+ }
+}
diff --git a/src/main/java/eu/m724/musicPlugin/item/ItemEvents.java b/src/main/java/eu/m724/music_plugin/item/ItemEvents.java
similarity index 86%
rename from src/main/java/eu/m724/musicPlugin/item/ItemEvents.java
rename to src/main/java/eu/m724/music_plugin/item/ItemEvents.java
index 66fcb6f..7e62569 100644
--- a/src/main/java/eu/m724/musicPlugin/item/ItemEvents.java
+++ b/src/main/java/eu/m724/music_plugin/item/ItemEvents.java
@@ -1,22 +1,13 @@
-package eu.m724.musicPlugin.item;
+package eu.m724.music_plugin.item;
-import eu.m724.musicPlugin.item.speaker.BlockSpeaker;
-import eu.m724.musicPlugin.item.speaker.Speaker;
-import eu.m724.musicPlugin.player.MovingMusicPlayer;
+import eu.m724.music_plugin.item.speaker.BlockSpeaker;
import org.bukkit.GameMode;
import org.bukkit.Material;
import org.bukkit.Particle;
-import org.bukkit.block.Block;
-import org.bukkit.entity.Item;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
-import org.bukkit.event.block.BlockBreakEvent;
-import org.bukkit.event.block.BlockDamageEvent;
-import org.bukkit.event.entity.EntityDeathEvent;
-import org.bukkit.event.entity.EntityDropItemEvent;
import org.bukkit.event.entity.EntityPickupItemEvent;
-import org.bukkit.event.entity.ItemDespawnEvent;
import org.bukkit.event.inventory.InventoryMoveItemEvent;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.event.player.PlayerDropItemEvent;
diff --git a/src/main/java/eu/m724/musicPlugin/item/PortableMediaPlayer.java b/src/main/java/eu/m724/music_plugin/item/PortableMediaPlayer.java
similarity index 55%
rename from src/main/java/eu/m724/musicPlugin/item/PortableMediaPlayer.java
rename to src/main/java/eu/m724/music_plugin/item/PortableMediaPlayer.java
index 2d01d84..d0009d2 100644
--- a/src/main/java/eu/m724/musicPlugin/item/PortableMediaPlayer.java
+++ b/src/main/java/eu/m724/music_plugin/item/PortableMediaPlayer.java
@@ -1,8 +1,10 @@
-package eu.m724.musicPlugin.item;
+package eu.m724.music_plugin.item;
-import eu.m724.musicPlugin.file.AudioFile;
-import eu.m724.musicPlugin.item.speaker.Speaker;
-import eu.m724.musicPlugin.player.MusicPlayer;
+import eu.m724.music_plugin.DebugLogger;
+import eu.m724.music_plugin.MusicPlugin;
+import eu.m724.music_plugin.audio.player.OpusFilePlayer;
+import eu.m724.music_plugin.audio.player.TrackEvent;
+import eu.m724.music_plugin.item.speaker.Speaker;
import net.md_5.bungee.api.ChatColor;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
@@ -11,14 +13,16 @@ import org.bukkit.inventory.ItemFlag;
import org.bukkit.inventory.ItemStack;
import org.bukkit.persistence.PersistentDataType;
+import java.io.File;
+import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
public class PortableMediaPlayer {
- static NamespacedKey idKey = new NamespacedKey("tweaks724", "player_id");
- static NamespacedKey dataKey = new NamespacedKey("tweaks724", "player_data");
+ static final NamespacedKey idKey = MusicPlugin.getNamespacedKey("player_id");
+ static final NamespacedKey dataKey = MusicPlugin.getNamespacedKey("player_data");;
public final int id;
@@ -29,8 +33,8 @@ public class PortableMediaPlayer {
public final boolean premium;
public final String engraving;
- private PlayedSong played;
- private Speaker speaker;
+ private OpusFilePlayer player; // TODO rename?
+ private Speaker> speaker;
PortableMediaPlayer(int id, int storageSeconds, int audioBitrate, boolean premium, String engraving) {
this.id = id;
@@ -44,7 +48,7 @@ public class PortableMediaPlayer {
return new PortableMediaPlayer(ThreadLocalRandom.current().nextInt(), 600, 32000, premium, engraving);
}
- public void setSpeaker(Speaker speaker) {
+ public void setSpeaker(Speaker> speaker) {
if (speaker.equals(this.speaker))
return;
@@ -54,80 +58,73 @@ public class PortableMediaPlayer {
this.speaker.destroy();
}
- speaker.setDestroyCallback(c -> {
+ speaker.setDestroyCallback(v -> {
System.out.println("spekar rip");
- if (played != null)
- played.pause(); // this one stops the tracker not playback, which is stopped in Speaker
+ if (player != null)
+ player.pause();
this.speaker = null;
});
this.speaker = speaker;
-
- if (played != null && !played.paused()) {
- unpause();
- }
+ player.setChannel(speaker.getChannel().getAudioChannel());
}
- public void play(AudioFile audioFile) {
+ public void play(File file) throws IOException {
+ DebugLogger.finer("pmp play");
if (speaker == null) return;
- played = new PlayedSong(audioFile);
+ //played = new Track(audioFile);
+ if (player != null)
+ player.stop();
- speaker.getMusicPlayer().setOnAction(action -> {
- if (action == MusicPlayer.TrackAction.PAUSE) {
- // track paused
- System.out.println("Okay its paused");
- played.pause();
- played.hint(played.file.getEncoder().getFrame());
- } else if (action == MusicPlayer.TrackAction.UNPAUSE) { // track unpaused
- System.out.println("Okay its unpaused");
- played.unpause();
- } else if (action == MusicPlayer.TrackAction.DURATION) { // track ended
- System.out.println("Okay its ened");
- next();
+ this.player = new OpusFilePlayer(file, speaker.getChannel().getAudioChannel());
+
+ player.setOnTrackEvent(event -> {
+ if (event == TrackEvent.START) {
+ DebugLogger.finer("I detected track START");
+ //played.unpause();
+ } else if (event == TrackEvent.STOP) {
+ DebugLogger.finer("I detected track STOP");
+ //played.pause();
+ //played.hint(played.file.getEncoder().getFrame());
+ } else if (event == TrackEvent.END) {
+ DebugLogger.finer("I detected track END");
+ //next();
}
});
- unpause();
+ player.play();
}
void pause() {
- System.out.println("pmp pase");
- if (played == null || speaker == null) return;
- System.out.println("pmp pasedd");
+ DebugLogger.finer("pmp pause");
- speaker.getMusicPlayer().pause();
+ if (player != null)
+ player.pause();
}
void unpause() {
- System.out.println("pmp unpase");
- if (played == null || speaker == null) return;
- System.out.println("pmp unpasedd");
+ DebugLogger.finer("pmp unpause");
- speaker.getMusicPlayer().setAudio(played.file);
- speaker.getMusicPlayer().seek(played.getProgress());
- speaker.getMusicPlayer().unpause();
+ if (player != null)
+ player.unpause();
}
void stop() {
- System.out.println("pmp stop");
+ DebugLogger.finer("pmp stop");
- if (speaker != null)
- speaker.destroy();
-
- this.played = null;
+ if (player != null)
+ player.stop();
}
+ // TODO
void prev() {
- System.out.println("pmp prev");
- this.played = new PlayedSong(played.file);
- unpause();
+ DebugLogger.fine("pmp previous (does nothing)");
}
+ // TODO
void next() {
- System.out.println("pmp next");
- this.played = null;
- // TODO
+ System.out.println("pmp next (does nothing)");
}
/* Item functions */
@@ -136,7 +133,7 @@ public class PortableMediaPlayer {
var is = new ItemStack(Material.IRON_INGOT);
var meta = is.getItemMeta();
- meta.setItemName("Portable music player");
+ meta.setItemName("Portable Music Player");
meta.addEnchant(Enchantment.UNBREAKING, 1, false);
meta.addItemFlags(ItemFlag.HIDE_ENCHANTS);
meta.getPersistentDataContainer().set(idKey, PersistentDataType.INTEGER, id);
@@ -151,7 +148,7 @@ public class PortableMediaPlayer {
private byte[] getData() {
var buffer = ByteBuffer.allocate(11 + engraving.length());
- buffer.put((byte) 0); // version
+ buffer.put((byte) 0); // data format version
buffer.put((byte) (premium ? 1 : 0));
buffer.putShort((short) storageSeconds); // make int if 18 hours is not enough. or store as minutes
diff --git a/src/main/java/eu/m724/musicPlugin/item/PortableMediaPlayers.java b/src/main/java/eu/m724/music_plugin/item/PortableMediaPlayers.java
similarity index 98%
rename from src/main/java/eu/m724/musicPlugin/item/PortableMediaPlayers.java
rename to src/main/java/eu/m724/music_plugin/item/PortableMediaPlayers.java
index ebf7571..feab236 100644
--- a/src/main/java/eu/m724/musicPlugin/item/PortableMediaPlayers.java
+++ b/src/main/java/eu/m724/music_plugin/item/PortableMediaPlayers.java
@@ -1,4 +1,4 @@
-package eu.m724.musicPlugin.item;
+package eu.m724.music_plugin.item;
import org.bukkit.inventory.ItemStack;
import org.bukkit.persistence.PersistentDataType;
diff --git a/src/main/java/eu/m724/musicPlugin/item/speaker/BlockChecker.java b/src/main/java/eu/m724/music_plugin/item/speaker/BlockChecker.java
similarity index 85%
rename from src/main/java/eu/m724/musicPlugin/item/speaker/BlockChecker.java
rename to src/main/java/eu/m724/music_plugin/item/speaker/BlockChecker.java
index 43e4d07..5586734 100644
--- a/src/main/java/eu/m724/musicPlugin/item/speaker/BlockChecker.java
+++ b/src/main/java/eu/m724/music_plugin/item/speaker/BlockChecker.java
@@ -1,4 +1,4 @@
-package eu.m724.musicPlugin.item.speaker;
+package eu.m724.music_plugin.item.speaker;
import org.bukkit.scheduler.BukkitRunnable;
diff --git a/src/main/java/eu/m724/musicPlugin/item/speaker/BlockSpeaker.java b/src/main/java/eu/m724/music_plugin/item/speaker/BlockSpeaker.java
similarity index 82%
rename from src/main/java/eu/m724/musicPlugin/item/speaker/BlockSpeaker.java
rename to src/main/java/eu/m724/music_plugin/item/speaker/BlockSpeaker.java
index f167c96..2bc6aee 100644
--- a/src/main/java/eu/m724/musicPlugin/item/speaker/BlockSpeaker.java
+++ b/src/main/java/eu/m724/music_plugin/item/speaker/BlockSpeaker.java
@@ -1,17 +1,16 @@
-package eu.m724.musicPlugin.item.speaker;
+package eu.m724.music_plugin.item.speaker;
-import eu.m724.musicPlugin.Statics;
-import eu.m724.musicPlugin.player.StaticMusicPlayer;
+import eu.m724.music_plugin.DebugLogger;
+import eu.m724.music_plugin.MusicPlugin;
+import eu.m724.music_plugin.audio.channel.LocationalChannel;
import org.bukkit.Location;
import org.bukkit.Material;
-import org.bukkit.Particle;
import org.bukkit.metadata.FixedMetadataValue;
import java.util.HashMap;
import java.util.Map;
-import java.util.concurrent.ThreadLocalRandom;
-public class BlockSpeaker extends Speaker {
+public class BlockSpeaker extends Speaker {
static final Map speakers = new HashMap<>();
public static BlockSpeaker get(Location location) {
@@ -25,7 +24,8 @@ public class BlockSpeaker extends Speaker {
return null;
var isTopSpeaker = below.getBlock().getType() == Material.NOTE_BLOCK;
- System.out.println("SPekaer is top: " + isTopSpeaker);
+ DebugLogger.fine("SPekaer is top: " + isTopSpeaker);
+
return speakers.compute(isTopSpeaker ? below : location, (k, v) -> new BlockSpeaker(k));
}
@@ -35,9 +35,9 @@ public class BlockSpeaker extends Speaker {
private boolean large;
public BlockSpeaker(Location location) {
- super(new StaticMusicPlayer(location));
+ super(new LocationalChannel(location));
this.location = location;
- this.location.getBlock().setMetadata("t_speaker", new FixedMetadataValue(Statics.plugin, true));
+ this.location.getBlock().setMetadata("t_speaker", new FixedMetadataValue(MusicPlugin.getInstance(), true));
}
@Override
@@ -50,17 +50,18 @@ public class BlockSpeaker extends Speaker {
if (location.clone().add(0, 1, 0).getBlock().getType() == Material.NOTE_BLOCK) {
if (!large) {
System.out.println("Speaker now large");
- getMusicPlayer().setDistance(64);
+ getChannel().setDistance(64);
this.large = true;
}
} else {
if (large) {
System.out.println("Speaker now small");
- getMusicPlayer().setDistance(32);
+ getChannel().setDistance(32);
this.large = false;
}
}
- if (getMusicPlayer().isPlaying()) {
+ // TODO bring it back
+ /*if (getMusicPlayer().isPlaying()) {
if (large) {
location.getWorld().spawnParticle(Particle.NOTE, location.clone().add(0.6, 0.5, 0), 1, 0, ThreadLocalRandom.current().nextDouble(-1, 0.5), ThreadLocalRandom.current().nextDouble(-0.4, 0.4));
location.getWorld().spawnParticle(Particle.NOTE, location.clone().add(-0.6, 0.5, 0), 1, 0, ThreadLocalRandom.current().nextDouble(-1, 0.5), ThreadLocalRandom.current().nextDouble(-0.4, 0.4));
@@ -69,7 +70,7 @@ public class BlockSpeaker extends Speaker {
} else {
location.getWorld().spawnParticle(Particle.NOTE, location.clone().add(0, 0.7, 0), 1, ThreadLocalRandom.current().nextDouble(-0.4, 0.4), 0, ThreadLocalRandom.current().nextDouble(-0.4, 0.4));
}
- }
+ }*/
} else {
System.out.println("Speaker disaper");
destroy();
diff --git a/src/main/java/eu/m724/music_plugin/item/speaker/Speaker.java b/src/main/java/eu/m724/music_plugin/item/speaker/Speaker.java
new file mode 100644
index 0000000..51cf0a0
--- /dev/null
+++ b/src/main/java/eu/m724/music_plugin/item/speaker/Speaker.java
@@ -0,0 +1,29 @@
+package eu.m724.music_plugin.item.speaker;
+
+import eu.m724.music_plugin.audio.channel.AbstractChannel;
+
+import java.util.function.Consumer;
+
+public abstract class Speaker> {
+ private final T channel;
+ private Consumer destroyCallback;
+
+ protected Speaker(T channel) {
+ this.channel = channel;
+ }
+
+ public void destroy() {
+ onDestroy();
+ destroyCallback.accept(null);
+ }
+
+ public void setDestroyCallback(Consumer consumer) {
+ this.destroyCallback = consumer;
+ }
+
+ public T getChannel() {
+ return this.channel;
+ }
+
+ abstract void onDestroy();
+}
diff --git a/src/main/java/eu/m724/music_plugin/library/Library.java b/src/main/java/eu/m724/music_plugin/library/Library.java
new file mode 100644
index 0000000..45bd2b7
--- /dev/null
+++ b/src/main/java/eu/m724/music_plugin/library/Library.java
@@ -0,0 +1,21 @@
+package eu.m724.music_plugin.library;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class Library {
+ private List