This commit is contained in:
Minecon724 2024-12-17 18:53:38 +01:00
parent 49224629c7
commit ff747c9013
Signed by: Minecon724
GPG key ID: 3CCC4D267742C8E8
11 changed files with 386 additions and 122 deletions

View file

@ -2,6 +2,7 @@ package eu.m724.musicPlugin;
import eu.m724.musicPlugin.file.AudioFileStorage;
import eu.m724.musicPlugin.item.PortableMediaPlayer;
import eu.m724.musicPlugin.item.Speakers;
import eu.m724.musicPlugin.player.MusicPlayer;
import eu.m724.musicPlugin.file.AudioFile;
import eu.m724.musicPlugin.player.StaticMusicPlayer;
@ -104,14 +105,29 @@ public class MusicCommands implements CommandExecutor {
sender.sendMessage("unapuised");
} else if (command.getName().equals("pmp")) {
var p = (Player) sender;
var pmp = PortableMediaPlayer.fromItemStack(p.getItemInHand()).join();
if (pmp != null) {
sender.sendMessage(String.valueOf(pmp.id));
sender.sendMessage(String.valueOf(pmp.premium));
sender.sendMessage(pmp.engraving);
} else {
pmp = PortableMediaPlayer.create(args[0].equals("yes"), args[1]);
p.getInventory().addItem(pmp.getItemStack());
var pmp = PortableMediaPlayer.fromItemStack(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.getMusicPlayer().play(song);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}

View file

@ -4,8 +4,7 @@ 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.ItemStorage;
import org.bukkit.entity.Item;
import eu.m724.musicPlugin.item.ItemEvents;
import org.bukkit.plugin.java.JavaPlugin;
public final class MusicPlugin extends JavaPlugin {
@ -32,7 +31,7 @@ public final class MusicPlugin extends JavaPlugin {
getCommand("resume").setExecutor(mcmd);
getCommand("pmp").setExecutor(mcmd);
ItemStorage.init(this);
getServer().getPluginManager().registerEvents(new ItemEvents(), this);
}

View file

@ -1,6 +1,116 @@
package eu.m724.musicPlugin.item;
import eu.m724.musicPlugin.item.speaker.BlockSpeaker;
import eu.m724.musicPlugin.item.speaker.Speaker;
import eu.m724.musicPlugin.player.MovingMusicPlayer;
import org.bukkit.Material;
import org.bukkit.Particle;
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.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;
import org.bukkit.event.player.PlayerInteractEvent;
public class ItemEvents implements Listener {
@EventHandler
public void onInteract(PlayerInteractEvent event) {
var right = event.getAction() == Action.RIGHT_CLICK_AIR || event.getAction() == Action.RIGHT_CLICK_BLOCK;
if (event.getItem() == null) return;
var player = PortableMediaPlayer.fromItemStack(event.getItem());
if (player == null) return;
System.out.println("A player used");
if (!event.getPlayer().isSneaking()) {
if (right) {
System.out.println("nos higft + rmb next song");
// TODO forward song
} else {
System.out.println("no shift + lmb rpevous song");
// TODO back song
}
} else {
if (right) {
System.out.println("shirt + rmb open inventory");
// TODO open inventory
} else {
System.out.println("shift + lmb bind spekar");
var block = event.getClickedBlock();
if (block != null) {
if (block.getType() == Material.NOTE_BLOCK) {
var speaker = BlockSpeaker.create(block.getLocation());
if (speaker != null) {
player.setSpeaker(speaker);
block.getWorld().spawnParticle(Particle.HAPPY_VILLAGER, block.getLocation(), 10);
event.setCancelled(true); // prevent break
} else {
event.getPlayer().sendMessage("This speaker is occupied");
}
}
}
}
}
}
@EventHandler
public void onDeath(EntityDeathEvent event) {
if (event.getEntity() instanceof Item item) {
var id = PortableMediaPlayer.idFromItemStack(item.getItemStack());
if (id == -1) return;
System.out.println("A player ded");
Speakers.destroy(id);
}
}
@EventHandler
public void onDespawn(ItemDespawnEvent event) {
var id = PortableMediaPlayer.idFromItemStack(event.getEntity().getItemStack());
if (id == -1) return;
System.out.println("A player despeawnd");
Speakers.destroy(id);
}
@EventHandler
public void onDrop(PlayerDropItemEvent event) {
var id = PortableMediaPlayer.idFromItemStack(event.getItemDrop().getItemStack());
if (id == -1) return;
System.out.println("A playe rdropped");
Speakers.attachTo(id, event.getItemDrop());
}
@EventHandler
public void onPickup(EntityPickupItemEvent event) {
var id = PortableMediaPlayer.idFromItemStack(event.getItem().getItemStack());
if (id == -1) return;
System.out.println("A player puckuperd");
Speakers.attachTo(id, event.getEntity());
}
public void onMove(InventoryMoveItemEvent event) {
if (event.getDestination().getType() == InventoryType.PLAYER) {
var id = PortableMediaPlayer.idFromItemStack(event.getItem());
if (id == -1) return;
System.out.println("A player storaged :((");
Speakers.destroy(id);
}
}
}

View file

@ -1,71 +0,0 @@
package eu.m724.musicPlugin.item;
import eu.m724.musicPlugin.MusicPlugin;
import org.bukkit.inventory.ItemStack;
import org.bukkit.persistence.PersistentDataType;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.HashMap;
import java.util.HexFormat;
import java.util.Map;
public class ItemStorage {
private static ItemStorage INSTANCE;
private File directory;
private Map<Integer, PortableMediaPlayer> cache = new HashMap<>(); // TODO clean cache
public static void init(MusicPlugin plugin) {
INSTANCE = new ItemStorage();
INSTANCE.directory = new File(plugin.getDataFolder(), "storage");
INSTANCE.directory.mkdir();
}
static ItemStorage getInstance() {
return INSTANCE;
}
public PortableMediaPlayer getPortableMediaPlayer(int id) throws IOException {
var file = new File(directory, HexFormat.of().toHexDigits(id) + ".pmpdata.bin");
if (!file.exists()) return null;
ByteBuffer buffer = ByteBuffer.wrap(Files.readAllBytes(file.toPath()));
if (buffer.get() != 0) { // TODO version mismatch
}
var premium = buffer.get() == 1;
var eb = new byte[buffer.get() & 0xFF];
buffer.get(eb);
var engraving = new String(eb, StandardCharsets.UTF_8);
return new PortableMediaPlayer(id, premium, engraving);
}
public void savePortableMediaPlayer(PortableMediaPlayer player) throws IOException {
var file = new File(directory, HexFormat.of().toHexDigits(player.id) + ".pmpdata.bin");
Files.write(file.toPath(), getData(player));
}
public byte[] getData(PortableMediaPlayer player) {
var buffer = ByteBuffer.allocate(8 + player.engraving.length());
buffer.put((byte) 0); // version
buffer.put((byte) (player.premium ? 1 : 0));
buffer.put((byte) player.engraving.length());
buffer.put(player.engraving.getBytes(StandardCharsets.UTF_8));
return buffer.array();
}
}

View file

@ -1,5 +1,8 @@
package eu.m724.musicPlugin.item;
import eu.m724.musicPlugin.item.speaker.Speaker;
import eu.m724.musicPlugin.player.MovingMusicPlayer;
import eu.m724.musicPlugin.player.MusicPlayer;
import net.md_5.bungee.api.ChatColor;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
@ -8,42 +11,53 @@ import org.bukkit.inventory.ItemFlag;
import org.bukkit.inventory.ItemStack;
import org.bukkit.persistence.PersistentDataType;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.ThreadLocalRandom;
public class PortableMediaPlayer {
private static NamespacedKey namespacedKey = new NamespacedKey("tweaks724", "player");
private static NamespacedKey idKey = new NamespacedKey("tweaks724", "player_id");
private static NamespacedKey dataKey = new NamespacedKey("tweaks724", "player_data");
public final int id;
// TODO configurable
public final int storageSeconds = 600;
public final int audioBitrate = 32000;
private final int storageSeconds;
private final int audioBitrate;
public final boolean premium;
public final String engraving;
SongLibrary library;
private final MovingMusicPlayer musicPlayer = new MovingMusicPlayer();
private Speaker linkedSpeaker;
public PortableMediaPlayer(int id, boolean premium, String engraving) {
public PortableMediaPlayer(int id, int storageSeconds, int audioBitrate, boolean premium, String engraving) {
this.id = id;
this.storageSeconds = storageSeconds;
this.audioBitrate = audioBitrate;
this.premium = premium;
this.engraving = engraving;
}
public static PortableMediaPlayer create(boolean premium, String engraving) {
var pmp = new PortableMediaPlayer(ThreadLocalRandom.current().nextInt(), premium, engraving);
CompletableFuture.runAsync(() -> {
try {
ItemStorage.getInstance().savePortableMediaPlayer(pmp);
} catch (IOException ex) {
ex.printStackTrace();
}
});
return pmp;
return new PortableMediaPlayer(ThreadLocalRandom.current().nextInt(), 600, 32000, premium, engraving);
}
public void setSpeaker(Speaker speaker) {
Speakers.speakers.put(id, )
if (speaker.equals(linkedSpeaker))
return;
if (linkedSpeaker != null)
linkedSpeaker.destroy();
linkedSpeaker = speaker;
speaker.setMusicPlayer(musicPlayer);
}
public MusicPlayer getMusicPlayer() {
return musicPlayer;
}
public ItemStack getItemStack() {
@ -53,7 +67,8 @@ public class PortableMediaPlayer {
meta.setItemName("Portable music player");
meta.addEnchant(Enchantment.UNBREAKING, 1, false);
meta.addItemFlags(ItemFlag.HIDE_ENCHANTS);
meta.getPersistentDataContainer().set(namespacedKey, PersistentDataType.INTEGER, id);
meta.getPersistentDataContainer().set(idKey, PersistentDataType.INTEGER, id);
meta.getPersistentDataContainer().set(dataKey, PersistentDataType.BYTE_ARRAY, getData());
if (engraving != null)
meta.setLore(List.of(premium ? (ChatColor.GOLD + "" + ChatColor.BOLD + engraving) : (ChatColor.GRAY + ChatColor.stripColor(engraving))));
@ -62,19 +77,59 @@ public class PortableMediaPlayer {
return is;
}
public static CompletableFuture<PortableMediaPlayer> fromItemStack(ItemStack itemStack) {
return CompletableFuture.supplyAsync(() -> {
var meta = itemStack.getItemMeta();
if (meta == null) return null;
public static int idFromItemStack(ItemStack itemStack) {
var meta = itemStack.getItemMeta();
if (meta == null) return -1;
var id = meta.getPersistentDataContainer().get(namespacedKey, PersistentDataType.INTEGER);
if (id == null) return null;
var id = meta.getPersistentDataContainer().get(idKey, PersistentDataType.INTEGER);
if (id == null) return -1;
try {
return ItemStorage.getInstance().getPortableMediaPlayer(id);
} catch (IOException e) {
throw new CompletionException(e);
}
});
return id;
}
public static PortableMediaPlayer fromItemStack(ItemStack itemStack) {
var meta = itemStack.getItemMeta();
if (meta == null) return null;
var id = meta.getPersistentDataContainer().get(idKey, PersistentDataType.INTEGER);
if (id == null) return null;
var data = meta.getPersistentDataContainer().get(dataKey, PersistentDataType.BYTE_ARRAY);
if (data == null) return null;
return fromData(id, data);
}
private byte[] getData() {
var buffer = ByteBuffer.allocate(11 + engraving.length());
buffer.put((byte) 0); // version
buffer.put((byte) (premium ? 1 : 0));
buffer.putShort((short) storageSeconds); // make int if 18 hours is not enough. or store as minutes
buffer.put((byte) (audioBitrate / 1000));
buffer.put((byte) engraving.length());
buffer.put(engraving.getBytes(StandardCharsets.UTF_8));
return buffer.array();
}
private static PortableMediaPlayer fromData(int id, byte[] data) {
ByteBuffer buffer = ByteBuffer.wrap(data);
if (buffer.get() != 0) { // TODO version mismatch
}
var premium = buffer.get() == 1;
var storageSeconds = buffer.getShort() & 0xFFFF;
var audioBitrate = (buffer.get() & 0xFF) * 1000;
var eb = new byte[buffer.get() & 0xFF];
buffer.get(eb);
var engraving = new String(eb, StandardCharsets.UTF_8);
return new PortableMediaPlayer(id, storageSeconds, audioBitrate, premium, engraving);
}
}

View file

@ -1,8 +0,0 @@
package eu.m724.musicPlugin.item;
import java.util.List;
public class SongLibrary {
private List<String> library;
private int playingIndex = 0;
}

View file

@ -0,0 +1,33 @@
package eu.m724.musicPlugin.item;
import eu.m724.musicPlugin.item.speaker.BlockSpeaker;
import eu.m724.musicPlugin.item.speaker.Speaker;
import eu.m724.musicPlugin.player.MovingMusicPlayer;
import org.bukkit.Location;
import org.bukkit.block.Block;
import org.bukkit.entity.Entity;
import java.util.HashMap;
import java.util.Map;
public class Speakers {
static Map<Integer, Speaker> speakers = new HashMap<>();
static Map<Integer, MovingMusicPlayer> players = new HashMap<>();
static Speaker get(int id) {
return speakers.get(id);
}
static void destroy(int id) {
var s = speakers.remove(id);
if (s != null) s.destroy();
}
static void moveTo(int id, Location location) {
//speakers.computeIfAbsent(id, i -> new Speaker()).moveTo(location);
}
static void attachTo(int id, Entity entity) {
//speakers.computeIfAbsent(id, i -> new Speaker()).attachTo(entity);
}
}

View file

@ -0,0 +1,40 @@
package eu.m724.musicPlugin.item.speaker;
import eu.m724.musicPlugin.player.MovingMusicPlayer;
import eu.m724.musicPlugin.player.MusicPlayer;
import eu.m724.musicPlugin.player.StaticMusicPlayer;
import org.bukkit.Location;
import java.util.HashMap;
import java.util.Map;
public class BlockSpeaker extends Speaker {
private static final Map<Location, BlockSpeaker> speakers = new HashMap<>();
public static BlockSpeaker get(Location location) {
return speakers.get(location);
}
public static BlockSpeaker create(Location location) {
if (speakers.containsKey(location)) return null;
return speakers.compute(location, (k, v) -> new BlockSpeaker(location));
}
/* */
private final Location location;
public BlockSpeaker(Location location) {
this.location = location;
}
@Override
void onSetMusicPlayer(MovingMusicPlayer musicPlayer) {
musicPlayer.moveTo(location);
}
@Override
void onDestroy() {
speakers.remove(location);
}
}

View file

@ -0,0 +1,27 @@
package eu.m724.musicPlugin.item.speaker;
import eu.m724.musicPlugin.player.MovingMusicPlayer;
public abstract class Speaker {
private MovingMusicPlayer musicPlayer;
public MovingMusicPlayer getMusicPlayer() {
return musicPlayer;
}
public void setMusicPlayer(MovingMusicPlayer musicPlayer) {
if (musicPlayer != null) {
musicPlayer.pause();
}
this.musicPlayer = musicPlayer;
}
public void destroy() {
this.musicPlayer.stop();
this.musicPlayer = null;
}
abstract void onSetMusicPlayer(MovingMusicPlayer musicPlayer);
abstract void onDestroy();
}

View file

@ -0,0 +1,55 @@
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) {
if (channel == null || !location.getWorld().equals(this.location.getWorld())) {
this.location = location;
this.channel = createChannel();
if (isPlaying()) {
this.pause();
this.unpause();
}
} else {
((LocationalAudioChannel)channel).updateLocation(
api.createPosition(location.getX(), location.getY(), location.getZ())
);
}
}
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;
}
}

View file

@ -13,7 +13,7 @@ public abstract class MusicPlayer {
// TODO find a better way
final VoicechatServerApi api = Statics.api;
private AudioChannel channel;
AudioChannel channel;
private boolean playing = false, paused = false;
private AudioPlayer player;
@ -69,6 +69,8 @@ public abstract class MusicPlayer {
}
public void unpause() {
if (playing) return;
var sa = new short[960];
var enc = audioFile.getEncoder();
@ -88,6 +90,8 @@ public abstract class MusicPlayer {
* Stops playback and rewinds
*/
public void stop() {
if (!playing) return;
stopPlayback(false);
audioFile.getEncoder().resetState();
}
@ -104,7 +108,7 @@ public abstract class MusicPlayer {
private void stopPlayback(boolean pause) {
if (player == null) return;
if (!playing) return;
paused = pause;
playing = false;
@ -147,4 +151,8 @@ public abstract class MusicPlayer {
*/
UNPAUSE
}
public boolean isPlaying() {
return playing;
}
}