diff --git a/.idea/artifacts/MCTPAudio_jar.xml b/.idea/artifacts/MCTPAudio_jar.xml
index e6e74de..5b631ee 100644
--- a/.idea/artifacts/MCTPAudio_jar.xml
+++ b/.idea/artifacts/MCTPAudio_jar.xml
@@ -4,6 +4,7 @@
+
\ No newline at end of file
diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml
new file mode 100644
index 0000000..e96534f
--- /dev/null
+++ b/.idea/uiDesigner.xml
@@ -0,0 +1,124 @@
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
\ No newline at end of file
diff --git a/MCTPAudio.iml b/MCTPAudio.iml
index bb5f66f..6c4466f 100644
--- a/MCTPAudio.iml
+++ b/MCTPAudio.iml
@@ -16,20 +16,9 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
@@ -38,7 +27,7 @@
-
+
@@ -47,7 +36,7 @@
-
+
@@ -56,5 +45,14 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/me/mctp/Main.java b/src/me/mctp/Main.java
index 0edbcf3..22abd86 100644
--- a/src/me/mctp/Main.java
+++ b/src/me/mctp/Main.java
@@ -1,8 +1,11 @@
package me.mctp;
+import me.mctp.commands.MCTPAudioCMD;
+import me.mctp.commands.MCTPShowCMD;
import me.mctp.listener.LogoutListener;
import me.mctp.listener.WGListener;
import me.mctp.managers.WGManager;
+import me.mctp.radio.Playlist;
import me.mctp.socket.Client;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
@@ -14,6 +17,7 @@ public class Main extends JavaPlugin implements Listener {
private static Plugin pl;
private static Client client;
+ private static Playlist playlist;
public static String prefix = (ChatColor.GOLD + "[" + ChatColor.YELLOW + "MCTP" + ChatColor.GOLD + "] " + ChatColor.GRAY);
@@ -29,6 +33,7 @@ public class Main extends JavaPlugin implements Listener {
}
getConfig().addDefault("Regions.demosound", "https://audiopagina.mcthemeparks.eu/gallery/voletarium.mp3");
+ getConfig().addDefault("HueRegions.demosound", "255_0_0_254");
getConfig().options().copyDefaults(true);
saveConfig();
@@ -37,12 +42,15 @@ public class Main extends JavaPlugin implements Listener {
client = new Client("ws://144.91.100.169:30217");
client.connect();
- getCommand("audio").setExecutor(new MCTPAudioCmd());
- getCommand("mctpaudio").setExecutor(new MCTPAudioCmd());
+ getCommand("audio").setExecutor(new MCTPAudioCMD());
+ getCommand("mctpaudio").setExecutor(new MCTPAudioCMD());
+ getCommand("mctpshow").setExecutor(new MCTPShowCMD());
Bukkit.getPluginManager().registerEvents(new WGListener(), this);
Bukkit.getPluginManager().registerEvents(new LogoutListener(), this);
+ playlist = new Playlist();
+
Bukkit.getLogger().info(prefix + " __ __ ____ _____ ____ _ _ _ ");
Bukkit.getLogger().info(prefix + " | \\/ |/ ___|_ _| _ \\ / \\ _ _ __| (_) ___ ");
Bukkit.getLogger().info(prefix + " | |\\/| | | | | | |_) / _ \\| | | |/ _` | |/ _ \\ ");
@@ -75,4 +83,8 @@ public class Main extends JavaPlugin implements Listener {
public static Client getClient() {
return client;
}
+
+ public static Playlist getPlaylist() {
+ return playlist;
+ }
}
diff --git a/src/me/mctp/api/maps/Playlist.java b/src/me/mctp/api/maps/Playlist.java
new file mode 100644
index 0000000..c4524e3
--- /dev/null
+++ b/src/me/mctp/api/maps/Playlist.java
@@ -0,0 +1,28 @@
+package me.mctp.api.maps;
+
+import java.util.*;
+
+public class Playlist extends ArrayList {
+ public void shuffle() {
+ Random random = new Random();
+
+ for (int index = 0; index < this.size(); index++) {
+ int secondIndex = random.nextInt(this.size());
+ swap(index, secondIndex);
+ }
+ }
+
+ public E getRandom() {
+ Random random = new Random();
+
+ return this.get(random.nextInt(this.size()));
+ }
+
+ private void swap(int firstIndex, int secondIndex) {
+ E first = this.get(firstIndex);
+ E second = this.get(secondIndex);
+
+ this.set(secondIndex, first);
+ this.set(firstIndex, second);
+ }
+}
\ No newline at end of file
diff --git a/src/me/mctp/MCTPAudioCmd.java b/src/me/mctp/commands/MCTPAudioCMD.java
similarity index 81%
rename from src/me/mctp/MCTPAudioCmd.java
rename to src/me/mctp/commands/MCTPAudioCMD.java
index e06e969..a6758ba 100644
--- a/src/me/mctp/MCTPAudioCmd.java
+++ b/src/me/mctp/commands/MCTPAudioCMD.java
@@ -1,8 +1,10 @@
-package me.mctp;
+package me.mctp.commands;
+import me.mctp.Main;
import me.mctp.api.AudioType;
import me.mctp.api.HueType;
import me.mctp.managers.PinManager;
+import me.mctp.utils.HeadUtil;
import me.mctp.utils.SpigotPlayerSelector;
import net.md_5.bungee.api.chat.ClickEvent;
import net.md_5.bungee.api.chat.TextComponent;
@@ -15,11 +17,13 @@ import org.bukkit.entity.Player;
import org.json.simple.JSONObject;
import java.util.ArrayList;
+import java.util.List;
-public class MCTPAudioCmd implements CommandExecutor {
+public class MCTPAudioCMD implements CommandExecutor {
public static String prefix = (ChatColor.GOLD + "[" + ChatColor.YELLOW + "MCTP" + ChatColor.GOLD + "] " + ChatColor.GRAY);
+ @Override
public boolean onCommand(CommandSender sender, Command cmd, String commandlabel, String[] args) {
if (cmd.getName().equalsIgnoreCase("mctpaudio")) {
if (!sender.hasPermission("mctp.audio")) {
@@ -27,9 +31,24 @@ public class MCTPAudioCmd implements CommandExecutor {
return true;
}
- // /mctpaudio play SELECTOR TYPE URL
- // /mctpaudio stop SELECTOR TYPE
- if (args.length == 4 && args[0].equalsIgnoreCase("play")) {
+ if (args.length == 1 && args[0].equalsIgnoreCase("toggleradio")) {
+ if (Main.getPlaylist().isRunning()) {
+ Main.getPlaylist().stop();
+ sender.sendMessage(prefix + "De auto radio is stopgezet. Zodra het huidige nummer is afgelopen, gebeurt er niks meer.");
+ } else {
+ Main.getPlaylist().init();
+ sender.sendMessage(prefix + "De auto radio is weer gestart.");
+ }
+ return true;
+ } else if (args.length == 2 && args[0].equalsIgnoreCase("addsong")) {
+ List urls = Main.getPlugin().getConfig().getStringList("RadioSongs");
+ urls.add(args[1]);
+ Main.getPlugin().getConfig().set("RadioSongs", urls);
+ Main.getPlugin().saveConfig();
+ Main.getPlaylist().addSong(args[1]);
+ sender.sendMessage(prefix + "Nummer toegevoegd aan de lijst.");
+ return true;
+ } else if (args.length == 4 && args[0].equalsIgnoreCase("play")) {
AudioType type;
try {
type = AudioType.valueOf(args[2].toUpperCase());
@@ -116,6 +135,16 @@ public class MCTPAudioCmd implements CommandExecutor {
players.addAll(selector.getPlayers(sender));
}
+ //CHECK FOR THE REGION SELECTOR -> Then save
+ if (args[1].startsWith("@a") && HeadUtil.getArgument(args[1], "region").length() != 0) {
+ String regionID = HeadUtil.getArgument(args[1], "region");
+ String data = r + "_" + g + "_" + b + "_" + type.name();
+ if (brightness != null) data += "_" + brightness;
+
+ Main.getPlugin().getConfig().set("HueRegions." + regionID, data);
+ Main.getPlugin().saveConfig();
+ }
+
JSONObject data;
for (Player p : players) {
data = new JSONObject();
diff --git a/src/me/mctp/commands/MCTPShowCMD.java b/src/me/mctp/commands/MCTPShowCMD.java
new file mode 100644
index 0000000..fcb8f06
--- /dev/null
+++ b/src/me/mctp/commands/MCTPShowCMD.java
@@ -0,0 +1,81 @@
+package me.mctp.commands;
+
+import me.mctp.Main;
+import me.mctp.utils.Laser;
+import org.bukkit.ChatColor;
+import org.bukkit.Sound;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandExecutor;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+import org.bukkit.scheduler.BukkitRunnable;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class MCTPShowCMD implements CommandExecutor {
+ public static String prefix = (ChatColor.GOLD + "[" + ChatColor.YELLOW + "MCTP" + ChatColor.GOLD + "] " + ChatColor.GRAY);
+
+ private Map lasers =new HashMap<>();
+
+ @Override
+ public boolean onCommand(CommandSender sender, Command cmd, String commandlabel, String[] args) {
+ if (cmd.getName().equalsIgnoreCase("mctpshow")) {
+ if (!sender.hasPermission("mctp.show")) {
+ sender.sendMessage(prefix + "You don't have the permission to do this.");
+ return true;
+ }
+
+ if (args.length == 1 && args[0].equalsIgnoreCase("demo")) {
+ Player p = (Player) sender;
+
+ if (lasers.containsKey(p)){
+ lasers.get(p).cancel();
+ }else {
+ try{
+ lasers.put(p, new LaserRunnable(p));
+ lasers.get(p).runTaskTimer(Main.getPlugin(), 5, 1);
+ }catch (ReflectiveOperationException e){
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+ return true;
+ }
+
+ public class LaserRunnable extends BukkitRunnable {
+ public static final byte LOADING_TIME = 30;
+ public static final byte RANGE = 10;
+
+ private final Laser laser;
+ private final Player p;
+
+ public byte loading = 0;
+
+ public LaserRunnable(Player p) throws ReflectiveOperationException{
+ this.p = p;
+ this.laser = new Laser(p.getLocation(), p.getLocation().add(0, 1, 0), -1, 50);
+ this.laser.start(Main.getPlugin());
+ }
+
+ public void run(){
+ if (loading != LOADING_TIME){
+ loading++;
+ p.getWorld().playSound(p.getLocation(), Sound.BLOCK_NOTE_BLOCK_PLING, 0.7f, loading == LOADING_TIME ? 1.5f : 0.2f);
+ }
+ try{
+ laser.moveStart(p.getLocation().add(0, 0.8, 0));
+ laser.moveEnd(p.getLocation().add(0, 1.2, 0).add(p.getLocation().getDirection().multiply(loading == LOADING_TIME ? RANGE : loading / (LOADING_TIME / RANGE * 1.3))));
+ }catch (ReflectiveOperationException e){
+ e.printStackTrace();
+ }
+ }
+
+ public synchronized void cancel() throws IllegalStateException{
+ laser.stop();
+ lasers.remove(p);
+ super.cancel();
+ }
+ }
+}
diff --git a/src/me/mctp/listener/WGListener.java b/src/me/mctp/listener/WGListener.java
index 6460cf7..6d128ef 100644
--- a/src/me/mctp/listener/WGListener.java
+++ b/src/me/mctp/listener/WGListener.java
@@ -1,69 +1,105 @@
package me.mctp.listener;
+import com.sk89q.worldguard.protection.regions.ProtectedRegion;
import me.mctp.Main;
import me.mctp.managers.PinManager;
+import me.mctp.managers.WGManager;
import net.raidstone.wgevents.events.RegionsEnteredEvent;
-import net.raidstone.wgevents.events.RegionsLeftEvent;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
+import org.bukkit.event.player.PlayerMoveEvent;
import org.json.simple.JSONObject;
-import java.util.Collections;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.Set;
+import java.util.*;
+import java.util.stream.Collectors;
public class WGListener implements Listener {
+ /** Music detection */
@EventHandler
- public void onRegionEnter(RegionsEnteredEvent e) {
- Set list = Main.getPlugin().getConfig().getConfigurationSection("Regions").getKeys(false);
- Set list2 = Main.getPlugin().getConfig().getConfigurationSection("HueRegions").getKeys(false);
+ public void onMove(PlayerMoveEvent e) {
+ if (e.getTo() == null || e.getFrom().getWorld() == null || e.getTo().getWorld() == null) return;
- if (!Collections.disjoint(list, e.getRegionsNames())) {
- //One element is the same -> In a region
-
- Optional name = e.getRegionsNames().stream().filter(list::contains).findFirst();
- if (!name.isPresent()) return;
- String regionURL = Main.getPlugin().getConfig().getString("Regions." + name.get());
-
- JSONObject data = new JSONObject();
+ if (e.getFrom().getBlockX() != e.getTo().getBlockX() || e.getFrom().getBlockY() != e.getTo().getBlockY() || e.getFrom().getBlockZ() != e.getTo().getBlockZ()) {
+ Set list = Main.getPlugin().getConfig().getConfigurationSection("Regions").getKeys(false);
if (!PinManager.hasPin(Objects.requireNonNull(e.getPlayer()).getUniqueId())) return;
- data.put("task", "MUSIC");
- data.put("path", regionURL);
- data.put("uuid", e.getPlayer().getUniqueId().toString().replace("-", ""));
- Main.getClient().sendData(data);
- }
+ List regions = WGManager.getRegionsIn(e.getFrom());
+ List regionNames = regions.stream().map(ProtectedRegion::getId).collect(Collectors.toList());
+ List regions2 = WGManager.getRegionsIn(e.getTo());
+ List regionNames2 = regions2.stream().map(ProtectedRegion::getId).collect(Collectors.toList());
- if (!Collections.disjoint(list2, e.getRegionsNames())) {
- //One element is the same -> In a region
+ if ((Collections.disjoint(list, regionNames) && !Collections.disjoint(list, regionNames2)) || (!Collections.disjoint(list, regionNames) && !Collections.disjoint(list, regionNames2))) {
+ //Walked in a region
- //TODO Add hue support
+ if (!Collections.disjoint(list, regionNames) && !Collections.disjoint(list, regionNames2)) {
+ Optional name = regionNames.stream().filter(list::contains).findFirst();
+ Optional name2 = regionNames2.stream().filter(list::contains).findFirst();
+ if (name.isPresent() && name2.isPresent() && name.get().equals(name2.get())) {
+ return;
+ }
+
+ if (name.isPresent() && name2.isPresent()) {
+ String regionURL = Main.getPlugin().getConfig().getString("Regions." + name.get());
+ String regionURL2 = Main.getPlugin().getConfig().getString("Regions." + name2.get());
+
+ if (regionURL.equals(regionURL2)) {
+ return;
+ }
+ }
+ }
+
+ Optional name = regionNames2.stream().filter(list::contains).findFirst();
+ if (!name.isPresent()) return;
+ String regionURL = Main.getPlugin().getConfig().getString("Regions." + name.get());
+
+ JSONObject data = new JSONObject();
+ data.put("task", "MUSIC");
+ data.put("path", regionURL);
+ data.put("uuid", e.getPlayer().getUniqueId().toString().replace("-", ""));
+ Main.getClient().sendData(data);
+ } else if (!Collections.disjoint(list, regionNames) && Collections.disjoint(list, regionNames2)) {
+ //Not in a region, stop...
+ JSONObject data = new JSONObject();
+ data.put("task", "MUSIC");
+ data.put("path", "");
+ data.put("uuid", e.getPlayer().getUniqueId().toString().replace("-", ""));
+ Main.getClient().sendData(data);
+ }
}
}
+ /** Hue detection */
@EventHandler
- public void onRegionExit(RegionsLeftEvent e) {
- Set list = Main.getPlugin().getConfig().getConfigurationSection("Regions").getKeys(false);
+ public void onRegionEnter(RegionsEnteredEvent e) {
Set list2 = Main.getPlugin().getConfig().getConfigurationSection("HueRegions").getKeys(false);
- if (!Collections.disjoint(list, e.getRegionsNames())) {
- //One element is the same -> In a region
- JSONObject data = new JSONObject();
-
- if (!PinManager.hasPin(Objects.requireNonNull(e.getPlayer()).getUniqueId())) return;
-
- data.put("task", "MUSIC");
- data.put("path", "");
- data.put("uuid", e.getPlayer().getUniqueId().toString().replace("-", ""));
- Main.getClient().sendData(data);
- }
-
if (!Collections.disjoint(list2, e.getRegionsNames())) {
//One element is the same -> In a region
- //TODO Add hue support
+ Optional name = e.getRegionsNames().stream().filter(list2::contains).findFirst();
+ if (!name.isPresent()) return;
+ String configData = Main.getPlugin().getConfig().getString("HueRegions." + name.get());
+
+ if (configData == null) return;
+ String[] configDataSplit = configData.split("_");
+
+ int r = Integer.parseInt(configDataSplit[0]);
+ int g = Integer.parseInt(configDataSplit[1]);
+ int b = Integer.parseInt(configDataSplit[2]);
+ String type = configDataSplit[3];
+ Integer brightness = null;
+ if (configDataSplit.length == 5) brightness = Integer.parseInt(configDataSplit[4]);
+
+ if (!PinManager.hasPin(Objects.requireNonNull(e.getPlayer()).getUniqueId())) return;
+
+ JSONObject data = new JSONObject();
+ data.put("task", "HUE");
+ data.put("rgb", r + ":" + g + ":" + b);
+ data.put("type", type);
+ if (brightness != null) data.put("brightness", brightness);
+ data.put("uuid", e.getPlayer().getUniqueId().toString().replace("-", ""));
+ Main.getClient().sendData(data);
}
}
}
diff --git a/src/me/mctp/radio/Playlist.java b/src/me/mctp/radio/Playlist.java
new file mode 100644
index 0000000..8a36597
--- /dev/null
+++ b/src/me/mctp/radio/Playlist.java
@@ -0,0 +1,114 @@
+package me.mctp.radio;
+
+import com.mpatric.mp3agic.InvalidDataException;
+import com.mpatric.mp3agic.UnsupportedTagException;
+import me.mctp.Main;
+import me.mctp.managers.PinManager;
+import me.mctp.utils.HeadUtil;
+import org.bukkit.Bukkit;
+import org.bukkit.entity.Player;
+import org.bukkit.scheduler.BukkitTask;
+import org.json.simple.JSONObject;
+
+import java.io.IOException;
+
+public class Playlist {
+ private me.mctp.api.maps.Playlist songList = new me.mctp.api.maps.Playlist<>();
+ private boolean running = false;
+ private BukkitTask currentTimer;
+
+ /**
+ * Init, load all the songs from the data and start first
+ */
+ public Playlist() {
+ init();
+ }
+
+ /**
+ * Load the songs into the system and start
+ */
+ public void init() {
+ for (String URL : Main.getPlugin().getConfig().getStringList("RadioSongs")) {
+ addSong(URL);
+ }
+
+ Bukkit.getScheduler().runTaskAsynchronously(Main.getPlugin(), this::nextSong);
+
+ running = true;
+ }
+
+ /**
+ * Stop the playlist (clears the queue)
+ */
+ public void stop() {
+ songList.clear();
+
+ if (currentTimer != null) {
+ currentTimer.cancel();
+ currentTimer = null;
+ }
+
+ running = false;
+ }
+
+ /**
+ * Add a song by the url
+ * @param url The song url (mp3)
+ */
+ public void addSong(String url) {
+ songList.add(url);
+ songList.shuffle();
+ }
+
+ /**
+ * Go to the next song
+ */
+ public void nextSong() {
+ if (currentTimer != null) return;
+
+ //Get song
+ String nextURL = songList.getRandom();
+ if (nextURL == null) return;
+
+ JSONObject data;
+ for (Player p : Bukkit.getOnlinePlayers()) {
+ data = new JSONObject();
+
+ if (!PinManager.hasPin(p.getUniqueId())) continue;
+
+ data.put("task", "RADIO");
+ data.put("path", nextURL);
+ data.put("uuid", p.getUniqueId().toString().replace("-", ""));
+ Main.getClient().sendData(data);
+ }
+
+ int ticks;
+ try {
+ ticks = HeadUtil.getTicksOfFile(nextURL);
+ } catch (IOException | InvalidDataException | UnsupportedTagException e) {
+ e.printStackTrace();
+ nextSong();
+ return;
+ }
+
+ if (ticks == 0) {
+ Bukkit.getLogger().info("0 ticks");
+ nextSong();
+ return;
+ }
+
+ Bukkit.getLogger().info("Started song with duration: " + ticks/20 + " sec.");
+
+ currentTimer = Bukkit.getScheduler().runTaskLaterAsynchronously(Main.getPlugin(), () -> {
+ currentTimer = null;
+ nextSong();
+ }, ticks);
+
+ //Shuffle playlist again
+ songList.shuffle();
+ }
+
+ public boolean isRunning() {
+ return running;
+ }
+}
diff --git a/src/me/mctp/socket/Client.java b/src/me/mctp/socket/Client.java
index 656d3e7..b2f3189 100644
--- a/src/me/mctp/socket/Client.java
+++ b/src/me/mctp/socket/Client.java
@@ -1,6 +1,6 @@
package me.mctp.socket;
-import me.mctp.MCTPAudioCmd;
+import me.mctp.commands.MCTPAudioCMD;
import me.mctp.Main;
import me.mctp.managers.PinManager;
import org.bukkit.Bukkit;
@@ -73,7 +73,7 @@ public class Client {
Player p = Bukkit.getPlayer(pUUID);
if (p != null && p.isOnline()) {
- p.sendMessage(MCTPAudioCmd.prefix + "You are now connected with the audioclient.");
+ p.sendMessage(MCTPAudioCMD.prefix + "You are now connected with the audioclient.");
}
JSONObject reply = new JSONObject();
@@ -88,7 +88,7 @@ public class Client {
UUID pUUID = JSONUtil.formatFromInput(uuid);
Player p = Bukkit.getPlayer(pUUID);
if (p != null && p.isOnline()) {
- p.sendMessage(MCTPAudioCmd.prefix + "You are now disconnected from the audioclient.");
+ p.sendMessage(MCTPAudioCMD.prefix + "You are now disconnected from the audioclient.");
}
}
}
diff --git a/src/me/mctp/utils/HeadUtil.java b/src/me/mctp/utils/HeadUtil.java
new file mode 100644
index 0000000..ff0eac0
--- /dev/null
+++ b/src/me/mctp/utils/HeadUtil.java
@@ -0,0 +1,84 @@
+package me.mctp.utils;
+
+import com.mpatric.mp3agic.InvalidDataException;
+import com.mpatric.mp3agic.Mp3File;
+import com.mpatric.mp3agic.UnsupportedTagException;
+import org.bukkit.craftbukkit.libs.org.apache.commons.io.FilenameUtils;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.net.URLConnection;
+
+public class HeadUtil {
+ public static String getArgument(String selector, String key) {
+ StringBuilder result = new StringBuilder();
+ String[] arguments = selector.split(key + "=");
+ if (arguments.length == 1) return "";
+ for (byte type : arguments[1].getBytes()) {
+ char element = (char) type;
+ if (element == ',' || element == ']') {
+ return result.toString();
+ } else {
+ result.append(element);
+ }
+ }
+
+ return result.toString().replaceAll(".", "");
+ }
+
+ private static String downloadFromUrl(URL url, String localFilename) throws IOException {
+ InputStream is = null;
+ FileOutputStream fos = null;
+
+ String tempDir = System.getProperty("java.io.tmpdir");
+ String outputPath = tempDir + "/" + localFilename;
+
+ try {
+ //connect
+ URLConnection urlConn = url.openConnection();
+
+ //get inputstream from connection
+ is = urlConn.getInputStream();
+ fos = new FileOutputStream(outputPath);
+
+ // 4KB buffer
+ byte[] buffer = new byte[4096];
+ int length;
+
+ // read from source and write into local file
+ while ((length = is.read(buffer)) > 0) {
+ fos.write(buffer, 0, length);
+ }
+ return outputPath;
+ } finally {
+ try {
+ if (is != null) {
+ is.close();
+ }
+ } finally {
+ if (fos != null) {
+ fos.close();
+ }
+ }
+ }
+ }
+
+ public static int getTicksOfFile(String input) throws IOException, InvalidDataException, UnsupportedTagException {
+ URL url = new URL(input);
+
+ String result = downloadFromUrl(url, FilenameUtils.getName(url.getPath()));
+ File file = new File(result);
+
+ if (!file.exists()) return 0;
+
+ Mp3File mp3File = new Mp3File(file);
+ long sec = mp3File.getLengthInSeconds();
+
+ file.delete();
+
+ return (int) (sec * 20);
+ }
+}
diff --git a/src/me/mctp/utils/Laser.java b/src/me/mctp/utils/Laser.java
new file mode 100644
index 0000000..75e4f1f
--- /dev/null
+++ b/src/me/mctp/utils/Laser.java
@@ -0,0 +1,376 @@
+package me.mctp.utils;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.UUID;
+
+import org.apache.commons.lang.Validate;
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.entity.Player;
+import org.bukkit.plugin.Plugin;
+import org.bukkit.scheduler.BukkitRunnable;
+
+/**
+ * A whole class to create Guardian Beams by reflection
+ * Inspired by the API GuardianBeamAPI
+ * 1.9 -> 1.15
+ *
+ * @see GitHub page
+ * @author SkytAsul
+ */
+public class Laser {
+ private final int duration;
+ private final int distanceSquared;
+ private Location start;
+ private Location end;
+
+ private final Object createGuardianPacket;
+ private final Object createSquidPacket;
+ private final Object teamAddPacket;
+ private final Object destroyPacket;
+ private final Object metadataPacketGuardian;
+ private final Object metadataPacketSquid;
+ private final Object fakeGuardianDataWatcher;
+
+ private final int squid;
+ private final UUID squidUUID;
+ private final int guardian;
+ private final UUID guardianUUID;
+
+ private BukkitRunnable run;
+ private HashSet show = new HashSet<>();
+
+ /**
+ * Create a Laser instance
+ * @param start Location where laser will starts
+ * @param end Location where laser will ends
+ * @param duration Duration of laser in seconds (-1 if infinite)
+ * @param distance Distance where laser will be visible
+ */
+ public Laser(Location start, Location end, int duration, int distance) throws ReflectiveOperationException {
+ this.start = start;
+ this.end = end;
+ this.duration = duration;
+ distanceSquared = distance * distance;
+
+ createSquidPacket = Packets.createPacketSquidSpawn(end);
+ squid = (int) Packets.getField(Packets.packetSpawn, "a", createSquidPacket);
+ squidUUID = (UUID) Packets.getField(Packets.packetSpawn, "b", createSquidPacket);
+ metadataPacketSquid = Packets.createPacketMetadata(squid, Packets.fakeSquidWatcher);
+ Packets.setDirtyWatcher(Packets.fakeSquidWatcher);
+
+ fakeGuardianDataWatcher = Packets.createFakeDataWatcher();
+ createGuardianPacket = Packets.createPacketGuardianSpawn(start, fakeGuardianDataWatcher, squid);
+ guardian = (int) Packets.getField(Packets.packetSpawn, "a", createGuardianPacket);
+ guardianUUID = (UUID) Packets.getField(Packets.packetSpawn, "b", createGuardianPacket);
+ metadataPacketGuardian = Packets.createPacketMetadata(guardian, fakeGuardianDataWatcher);
+
+ teamAddPacket = Packets.createPacketTeamAddEntities(squidUUID, guardianUUID);
+ destroyPacket = Packets.createPacketRemoveEntities(squid, guardian);
+ }
+
+ public void start(Plugin plugin) {
+ Validate.isTrue(run == null, "Task already started");
+ run = new BukkitRunnable() {
+ int time = duration;
+
+ @Override
+ public void run() {
+ try {
+ if (time == 0) {
+ cancel();
+ return;
+ }
+ for (Player p : start.getWorld().getPlayers()) {
+ if (isCloseEnough(p.getLocation())) {
+ if (!show.contains(p)) {
+ sendStartPackets(p);
+ show.add(p);
+ }
+ }else if (show.contains(p)) {
+ Packets.sendPacket(p, destroyPacket);
+ show.remove(p);
+ }
+ }
+ if (time != -1) time--;
+ }catch (ReflectiveOperationException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public synchronized void cancel() throws IllegalStateException {
+ super.cancel();
+ try {
+ for (Player p : show) {
+ Packets.sendPacket(p, destroyPacket);
+ }
+ }catch (ReflectiveOperationException e) {
+ e.printStackTrace();
+ }
+ run = null;
+ }
+ };
+ run.runTaskTimerAsynchronously(plugin, 0L, 20L);
+ }
+
+ public void stop() {
+ Validate.isTrue(run != null, "Task not started");
+ run.cancel();
+ }
+
+ public void moveStart(Location location) throws ReflectiveOperationException {
+ this.start = location;
+ Object packet = Packets.createPacketMoveEntity(start, guardian);
+ for (Player p : show) {
+ Packets.sendPacket(p, packet);
+ }
+ }
+
+ public Location getStart() {
+ return start;
+ }
+
+ public void moveEnd(Location location) throws ReflectiveOperationException {
+ this.end = location;
+ Object packet = Packets.createPacketMoveEntity(end, squid);
+ for (Player p : show) {
+ Packets.sendPacket(p, packet);
+ }
+ }
+
+ public Location getEnd() {
+ return end;
+ }
+
+ public void callColorChange() throws ReflectiveOperationException{
+ for (Player p : show) {
+ Packets.sendPacket(p, metadataPacketGuardian);
+ }
+ }
+
+ public boolean isStarted() {
+ return run != null;
+ }
+
+ private void sendStartPackets(Player p) throws ReflectiveOperationException {
+ Packets.sendPacket(p, createSquidPacket);
+ Packets.sendPacket(p, createGuardianPacket);
+ if (Packets.version > 14) {
+ Packets.sendPacket(p, metadataPacketSquid);
+ Packets.sendPacket(p, metadataPacketGuardian);
+ }
+ Packets.sendPacket(p, Packets.packetTeamCreate);
+ Packets.sendPacket(p, teamAddPacket);
+ }
+
+ private boolean isCloseEnough(Location location) {
+ return start.distanceSquared(location) <= distanceSquared ||
+ end.distanceSquared(location) <= distanceSquared;
+ }
+
+
+
+ private static class Packets {
+ private static int lastIssuedEID = 2000000000;
+
+ static int generateEID() {
+ return lastIssuedEID++;
+ }
+
+ private static int version = Integer.parseInt(Bukkit.getServer().getClass().getPackage().getName().replace(".", ",").split(",")[3].substring(1).split("_")[1]);
+ private static String npack = "net.minecraft.server." + Bukkit.getServer().getClass().getPackage().getName().replace(".", ",").split(",")[3] + ".";
+ private static String cpack = Bukkit.getServer().getClass().getPackage().getName() + ".";
+ private static Object packetTeamCreate;
+ private static Constructor> watcherConstructor;
+ private static Method watcherSet;
+ private static Method watcherRegister;
+ private static Method watcherDirty;
+ private static Class> packetSpawn;
+ private static Class> packetRemove;
+ private static Class> packetTeleport;
+ private static Class> packetTeam;
+ private static Class> packetMetadata;
+ private static Object watcherObject1; // invisilibity
+ private static Object watcherObject2; // spikes
+ private static Object watcherObject3; // attack id
+ private static int squidID;
+ private static int guardianID;
+
+ private static Object fakeSquid;
+ private static Object fakeSquidWatcher;
+
+ static {
+ try {
+ String watcherName1 = null, watcherName2 = null, watcherName3 = null;
+ if (version < 13) {
+ watcherName1 = "Z";
+ watcherName2 = "bA";
+ watcherName3 = "bB";
+ squidID = 94;
+ guardianID = 68;
+ }else if (version == 13) {
+ watcherName1 = "ac";
+ watcherName2 = "bF";
+ watcherName3 = "bG";
+ squidID = 70;
+ guardianID = 28;
+ }else if (version == 14) {
+ watcherName1 = "W";
+ watcherName2 = "b";
+ watcherName3 = "bD";
+ squidID = 73;
+ guardianID = 30;
+ }else if (version > 14) {
+ watcherName1 = "T";
+ watcherName2 = "b";
+ watcherName3 = "bA";
+ squidID = 74;
+ guardianID = 31;
+ }
+ watcherObject1 = getField(Class.forName(npack + "Entity"), watcherName1, null);
+ watcherObject2 = getField(Class.forName(npack + "EntityGuardian"), watcherName2, null);
+ watcherObject3 = getField(Class.forName(npack + "EntityGuardian"), watcherName3, null);
+
+ watcherConstructor = Class.forName(npack + "DataWatcher").getDeclaredConstructor(Class.forName(npack + "Entity"));
+ watcherSet = getMethod(Class.forName(npack + "DataWatcher"), "set");
+ watcherRegister = getMethod(Class.forName(npack + "DataWatcher"), "register");
+ if (version >= 15) watcherDirty = getMethod(Class.forName(npack + "DataWatcher"), "markDirty");
+ packetSpawn = Class.forName(npack + "PacketPlayOutSpawnEntityLiving");
+ packetRemove = Class.forName(npack + "PacketPlayOutEntityDestroy");
+ packetTeleport = Class.forName(npack + "PacketPlayOutEntityTeleport");
+ packetTeam = Class.forName(npack + "PacketPlayOutScoreboardTeam");
+ packetMetadata = Class.forName(npack + "PacketPlayOutEntityMetadata");
+
+ packetTeamCreate = packetTeam.newInstance();
+ setField(packetTeamCreate, "a", "noclip");
+ setField(packetTeamCreate, "i", 0);
+ setField(packetTeamCreate, "f", "never");
+
+ Object world = Class.forName(cpack + "CraftWorld").getDeclaredMethod("getHandle").invoke(Bukkit.getWorlds().get(0));
+ Object[] entityConstructorParams = version < 14 ? new Object[] { world } : new Object[] { Class.forName(npack + "EntityTypes").getDeclaredField("SQUID").get(null), world };
+ fakeSquid = getMethod(Class.forName(cpack + "entity.CraftSquid"), "getHandle").invoke(Class.forName(cpack + "entity.CraftSquid").getDeclaredConstructors()[0].newInstance(
+ null, Class.forName(npack + "EntitySquid").getDeclaredConstructors()[0].newInstance(
+ entityConstructorParams)));
+ fakeSquidWatcher = createFakeDataWatcher();
+ tryWatcherSet(fakeSquidWatcher, watcherObject1, (byte) 32);
+ }catch (ReflectiveOperationException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public static void sendPacket(Player p, Object packet) throws ReflectiveOperationException {
+ Object entityPlayer = Class.forName(cpack + "entity.CraftPlayer").getDeclaredMethod("getHandle").invoke(p);
+ Object playerConnection = entityPlayer.getClass().getDeclaredField("playerConnection").get(entityPlayer);
+ playerConnection.getClass().getDeclaredMethod("sendPacket", Class.forName(npack + "Packet")).invoke(playerConnection, packet);
+ }
+
+ public static Object createFakeDataWatcher() throws ReflectiveOperationException {
+ Object watcher = watcherConstructor.newInstance(fakeSquid);
+ if (version > 13) setField(watcher, "registrationLocked", false);
+ return watcher;
+ }
+
+ public static void setDirtyWatcher(Object watcher) throws ReflectiveOperationException {
+ if (version >= 15) watcherDirty.invoke(watcher, watcherObject1);
+ }
+
+ public static Object createPacketSquidSpawn(Location location) throws ReflectiveOperationException {
+ Object packet = packetSpawn.newInstance();
+ setField(packet, "a", generateEID());
+ setField(packet, "b", UUID.randomUUID());
+ setField(packet, "c", squidID);
+ setField(packet, "d", location.getX());
+ setField(packet, "e", location.getY());
+ setField(packet, "f", location.getZ());
+ setField(packet, "j", (byte) (location.getYaw() * 256.0F / 360.0F));
+ setField(packet, "k", (byte) (location.getPitch() * 256.0F / 360.0F));
+ if (version <= 14) setField(packet, "m", fakeSquidWatcher);
+ return packet;
+ }
+
+ public static Object createPacketGuardianSpawn(Location location, Object watcher, int squidId) throws ReflectiveOperationException {
+ Object packet = packetSpawn.newInstance();
+ setField(packet, "a", generateEID());
+ setField(packet, "b", UUID.randomUUID());
+ setField(packet, "c", guardianID);
+ setField(packet, "d", location.getX());
+ setField(packet, "e", location.getY());
+ setField(packet, "f", location.getZ());
+ setField(packet, "j", (byte) (location.getYaw() * 256.0F / 360.0F));
+ setField(packet, "k", (byte) (location.getPitch() * 256.0F / 360.0F));
+ tryWatcherSet(watcher, watcherObject1, (byte) 32);
+ tryWatcherSet(watcher, watcherObject2, false);
+ tryWatcherSet(watcher, watcherObject3, squidId);
+ if (version <= 14) setField(packet, "m", watcher);
+ return packet;
+ }
+
+ public static Object createPacketRemoveEntities(int squidId, int guardianId) throws ReflectiveOperationException {
+ Object packet = packetRemove.newInstance();
+ setField(packet, "a", new int[] { squidId, guardianId });
+ return packet;
+ }
+
+ public static Object createPacketMoveEntity(Location location, int entityId) throws ReflectiveOperationException {
+ Object packet = packetTeleport.newInstance();
+ setField(packet, "a", entityId);
+ setField(packet, "b", location.getX());
+ setField(packet, "c", location.getY());
+ setField(packet, "d", location.getZ());
+ setField(packet, "e", (byte) (location.getYaw() * 256.0F / 360.0F));
+ setField(packet, "f", (byte) (location.getPitch() * 256.0F / 360.0F));
+ setField(packet, "g", true);
+ return packet;
+ }
+
+ public static Object createPacketTeamAddEntities(UUID squidUUID, UUID guardianUUID) throws ReflectiveOperationException {
+ Object packet = packetTeam.newInstance();
+ setField(packet, "a", "noclip");
+ setField(packet, "i", 3);
+ Collection players = (Collection) getField(packetTeam, "h", packet);
+ players.add(squidUUID.toString());
+ players.add(guardianUUID.toString());
+ return packet;
+ }
+
+ private static Object createPacketMetadata(int entityId, Object watcher) throws ReflectiveOperationException {
+ return packetMetadata.getConstructor(int.class, watcher.getClass(), boolean.class).newInstance(entityId, watcher, false);
+ }
+
+ private static void tryWatcherSet(Object watcher, Object watcherObject, Object watcherData) throws ReflectiveOperationException {
+ try {
+ watcherSet.invoke(watcher, watcherObject, watcherData);
+ }catch (InvocationTargetException ex) {
+ watcherRegister.invoke(watcher, watcherObject, watcherData);
+ if (version >= 15) watcherDirty.invoke(watcher, watcherObject);
+ }
+ }
+
+ /* Reflection utils */
+ private static Method getMethod(Class> clazz, String name) {
+ for (Method m : clazz.getDeclaredMethods()) {
+ if (m.getName().equals(name)) return m;
+ }
+ return null;
+ }
+
+ private static void setField(Object instance, String name, Object value) throws ReflectiveOperationException {
+ Validate.notNull(instance);
+ Field field = instance.getClass().getDeclaredField(name);
+ field.setAccessible(true);
+ field.set(instance, value);
+ }
+
+ private static Object getField(Class> clazz, String name, Object instance) throws ReflectiveOperationException {
+ Field field = clazz.getDeclaredField(name);
+ field.setAccessible(true);
+ return field.get(instance);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/plugin.yml b/src/plugin.yml
index 5410f17..fb02282 100644
--- a/src/plugin.yml
+++ b/src/plugin.yml
@@ -1,6 +1,6 @@
main: me.mctp.Main
name: MCTPAudio
-version: 1.0
+version: 1.2
api-version: 1.14
author: MaybeFromNL_SBDeveloper
description: Copyright MaybeFromNL & SBDeveloper
@@ -9,6 +9,8 @@ depend: ['WorldGuard']
commands:
mctpaudio:
description: Main command
+ mctpshow:
+ description: Show command
audio:
description: Connect command