diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/HolographicDisplays.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/HolographicDisplays.java new file mode 100644 index 00000000..628f7c32 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/HolographicDisplays.java @@ -0,0 +1,260 @@ +package com.gmail.filoghost.holographicdisplays; + +import java.util.Set; + +import org.bukkit.Bukkit; +import org.bukkit.plugin.java.JavaPlugin; +import org.bukkit.scheduler.BukkitRunnable; + +import com.gmail.filoghost.holographicdisplays.SimpleUpdater.ResponseHandler; +import com.gmail.filoghost.holographicdisplays.bridge.bungeecord.BungeeServerTracker; +import com.gmail.filoghost.holographicdisplays.bridge.protocollib.ProtocolLibHook; +import com.gmail.filoghost.holographicdisplays.commands.main.HologramsCommandHandler; +import com.gmail.filoghost.holographicdisplays.disk.Configuration; +import com.gmail.filoghost.holographicdisplays.disk.HologramDatabase; +import com.gmail.filoghost.holographicdisplays.disk.UnicodeSymbols; +import com.gmail.filoghost.holographicdisplays.exception.HologramNotFoundException; +import com.gmail.filoghost.holographicdisplays.exception.InvalidFormatException; +import com.gmail.filoghost.holographicdisplays.exception.WorldNotFoundException; +import com.gmail.filoghost.holographicdisplays.listener.MainListener; +import com.gmail.filoghost.holographicdisplays.metrics.MetricsLite; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.NMSManager; +import com.gmail.filoghost.holographicdisplays.object.NamedHologram; +import com.gmail.filoghost.holographicdisplays.object.NamedHologramManager; +import com.gmail.filoghost.holographicdisplays.placeholder.AnimationsRegister; +import com.gmail.filoghost.holographicdisplays.placeholder.PlaceholdersManager; +import com.gmail.filoghost.holographicdisplays.task.BungeeCleanupTask; +import com.gmail.filoghost.holographicdisplays.task.WorldPlayerCounterTask; +import com.gmail.filoghost.holographicdisplays.util.VersionUtils; + +public class HolographicDisplays extends JavaPlugin { + + // The main instance of the plugin. + private static HolographicDisplays instance; + + // The manager for net.minecraft.server access. + private static NMSManager nmsManager; + + // Since 1.8 we use armor stands instead of wither skulls. + private static boolean is1_8; + + // True if ProtocolLib is installed and successfully loaded. + private static boolean useProtocolLib; + + // The new version found by the updater, null if there is no new version. + private static String newVersion; + + @Override + public void onEnable() { + + // Blocks plugin reloaders and the /reload command. + if (instance != null) { + getLogger().warning("Please do not use /reload or plugin reloaders. Do \"/holograms reload\" instead."); + return; + } + + instance = this; + + // Load placeholders.yml. + UnicodeSymbols.load(this); + + // Load the configuration. + Configuration.load(this); + + if (Configuration.updateNotification) { + new SimpleUpdater(this, 75097).checkForUpdates(new ResponseHandler() { + + @Override + public void onUpdateFound(final String newVersion) { + + HolographicDisplays.newVersion = newVersion; + getLogger().info("Found a new version available: " + newVersion); + getLogger().info("Download it on Bukkit Dev:"); + getLogger().info("dev.bukkit.org/bukkit-plugins/holographic-displays"); + } + }); + } + + String version = VersionUtils.getBukkitVersion(); + + if (version == null) { + // Caused by MCPC+ / Cauldron renaming packages, extract the version from Bukkit.getVersion(). + version = VersionUtils.getMinecraftVersion(); + + if ("1.6.4".equals(version)) { + version = "v1_6_R3"; + } else if ("1.7.2".equals(version)) { + version = "v1_7_R1"; + } else if ("1.7.5".equals(version)) { + version = "v1_7_R2"; + } else if ("1.7.8".equals(version)) { + version = "v1_7_R3"; + } else if ("1.7.10".equals(version)) { + version = "v1_7_R4"; + } else if ("1.8".equals(version)) { + version = "v1_8_R1"; + } else { + // Cannot definitely get the version. This will cause the plugin to disable itself. + version = null; + } + } + + // It's simple, we don't need reflection. + if ("v1_6_R3".equals(version)) { + nmsManager = new com.gmail.filoghost.holographicdisplays.nms.v1_6_R3.NmsManagerImpl(); + } else if ("v1_7_R1".equals(version)) { + nmsManager = new com.gmail.filoghost.holographicdisplays.nms.v1_7_R1.NmsManagerImpl(); + } else if ("v1_7_R2".equals(version)) { + nmsManager = new com.gmail.filoghost.holographicdisplays.nms.v1_7_R2.NmsManagerImpl(); + } else if ("v1_7_R3".equals(version)) { + nmsManager = new com.gmail.filoghost.holographicdisplays.nms.v1_7_R3.NmsManagerImpl(); + } else if ("v1_7_R4".equals(version)) { + nmsManager = new com.gmail.filoghost.holographicdisplays.nms.v1_7_R4.NmsManagerImpl(); + } else if ("v1_8_R1".equals(version)) { + is1_8 = true; + nmsManager = new com.gmail.filoghost.holographicdisplays.nms.v1_8_R1.NmsManagerImpl(); + } else { + printWarnAndDisable( + "******************************************************", + " This version of HolographicDisplays can", + " only work on these server versions:", + " 1.6.4, from 1.7 to 1.8.1.", + " The plugin will be disabled.", + "******************************************************" + ); + return; + } + + try { + if (VersionUtils.isMCPCOrCauldron()) { + getLogger().info("Trying to enable Cauldron/MCPC+ support..."); + } + + nmsManager.registerCustomEntities(); + + if (VersionUtils.isMCPCOrCauldron()) { + getLogger().info("Successfully added support for Cauldron/MCPC+!"); + } + + } catch (Exception e) { + e.printStackTrace(); + printWarnAndDisable( + "******************************************************", + " HolographicDisplays was unable to register", + " custom entities, the plugin will be disabled.", + " Are you using the correct Bukkit version?", + "******************************************************" + ); + return; + } + + // ProtocolLib check. + try { + if (Bukkit.getPluginManager().isPluginEnabled("ProtocolLib")) { + useProtocolLib = ProtocolLibHook.load(nmsManager, this, is1_8); + } + } catch (Exception ex) { + ex.printStackTrace(); + getLogger().warning("Failed to load ProtocolLib support. Is it updated?"); + } + + // Load animation files and the placeholder manager. + PlaceholdersManager.load(this); + try { + AnimationsRegister.loadAnimations(this); + } catch (Exception ex) { + ex.printStackTrace(); + getLogger().warning("Failed to load animation files!"); + } + + // Initalize other static classes. + HologramDatabase.loadYamlFile(this); + BungeeServerTracker.startTask(Configuration.bungeeRefreshSeconds); + + // Start repeating tasks. + Bukkit.getScheduler().scheduleSyncRepeatingTask(this, new BungeeCleanupTask(), 5 * 60 * 20, 5 * 60 * 20); + Bukkit.getScheduler().scheduleSyncRepeatingTask(this, new WorldPlayerCounterTask(), 0L, 3 * 20); + + Set savedHologramsNames = HologramDatabase.getHolograms(); + if (savedHologramsNames != null && savedHologramsNames.size() > 0) { + for (String singleHologramName : savedHologramsNames) { + try { + NamedHologram singleHologram = HologramDatabase.loadHologram(singleHologramName); + NamedHologramManager.addHologram(singleHologram); + } catch (HologramNotFoundException e) { + getLogger().warning("Hologram '" + singleHologramName + "' not found, skipping it."); + } catch (InvalidFormatException e) { + getLogger().warning("Hologram '" + singleHologramName + "' has an invalid location format."); + } catch (WorldNotFoundException e) { + getLogger().warning("Hologram '" + singleHologramName + "' was in the world '" + e.getMessage() + "' but it wasn't loaded."); + } catch (Exception e) { + e.printStackTrace(); + getLogger().warning("Unhandled exception while loading the hologram '" + singleHologramName + "'. Please contact the developer."); + } + } + } + + getCommand("holograms").setExecutor(new HologramsCommandHandler()); + Bukkit.getPluginManager().registerEvents(new MainListener(nmsManager), this); + + try { + MetricsLite metrics = new MetricsLite(this); + metrics.start(); + } catch (Exception ignore) { } + + // The entities are loaded when the server is ready. + new BukkitRunnable() { + @Override + public void run() { + for (NamedHologram hologram : NamedHologramManager.getHolograms()) { + hologram.refreshAll(); + } + } + }.runTaskLater(this, 10L); + } + + + @Override + public void onDisable() { + for (NamedHologram hologram : NamedHologramManager.getHolograms()) { + hologram.despawnEntities(); + } + } + + public static NMSManager getNMSManager() { + return nmsManager; + } + + public static boolean is1_8() { + return is1_8; + } + + private static void printWarnAndDisable(String... messages) { + StringBuffer buffer = new StringBuffer("\n "); + for (String message : messages) { + buffer.append('\n'); + buffer.append(message); + } + buffer.append('\n'); + System.out.println(buffer.toString()); + try { + Thread.sleep(5000); + } catch (InterruptedException ex) { } + instance.setEnabled(false); + } + + public static HolographicDisplays getInstance() { + return instance; + } + + + public static String getNewVersion() { + return newVersion; + } + + + public static boolean useProtocolLib() { + return useProtocolLib; + } + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/SimpleUpdater.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/SimpleUpdater.java new file mode 100644 index 00000000..db7fca32 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/SimpleUpdater.java @@ -0,0 +1,173 @@ +package com.gmail.filoghost.holographicdisplays; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLConnection; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.bukkit.Bukkit; +import org.bukkit.plugin.Plugin; +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; +import org.json.simple.JSONValue; + +/** + * A very simple and lightweight updater, without download features. + * @autor filoghost + */ +public final class SimpleUpdater { + + public interface ResponseHandler { + + /** + * Called when the updater finds a new version. + * @param newVersion - the new version + */ + public void onUpdateFound(final String newVersion); + + } + + private Plugin plugin; + private int projectId; + + public SimpleUpdater(Plugin plugin, int projectId) { + if (plugin == null) { + throw new NullPointerException("Plugin cannot be null"); + } + + this.plugin = plugin; + this.projectId = projectId; + } + + /** + * This method creates a new async thread to check for updates. + * @param responseHandler the response handler + */ + public void checkForUpdates(final ResponseHandler responseHandler) { + Thread updaterThread = new Thread(new Runnable() { + @Override + public void run() { + + try { + + JSONArray filesArray = (JSONArray) readJson("https://api.curseforge.com/servermods/files?projectIds=" + projectId); + + if (filesArray.size() == 0) { + // The array cannot be empty, there must be at least one file. The project ID is not valid. + plugin.getLogger().warning("The author of this plugin has misconfigured the Updater system."); + plugin.getLogger().warning("The project ID (" + projectId + ") provided for updating is invalid."); + plugin.getLogger().warning("Please notify the author of this error."); + return; + } + + String updateName = (String) ((JSONObject) filesArray.get(filesArray.size() - 1)).get("name"); + final String newVersion = extractVersion(updateName); + + if (newVersion == null) { + throw new NumberFormatException(); + } + + if (isNewerVersion(newVersion)) { + Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, new Runnable() { + + @Override + public void run() { + responseHandler.onUpdateFound(newVersion); + } + }); + } + + } catch (IOException e) { + plugin.getLogger().warning("Could not contact BukkitDev to check for updates."); + } catch (NumberFormatException e) { + plugin.getLogger().warning("The author of this plugin has misconfigured the Updater system."); + plugin.getLogger().warning("File versions should follow the format 'PluginName vVERSION'"); + plugin.getLogger().warning("Please notify the author of this error."); + } catch (Exception e) { + e.printStackTrace(); + plugin.getLogger().warning("Unable to check for updates: unhandled exception."); + } + + } + }); + updaterThread.start(); + } + + private Object readJson(String url) throws MalformedURLException, IOException { + + URLConnection conn = new URL(url).openConnection(); + conn.setConnectTimeout(5000); + conn.setReadTimeout(8000); + conn.addRequestProperty("User-Agent", "Updater (by filoghost)"); + conn.setDoOutput(true); + + return JSONValue.parse(new BufferedReader(new InputStreamReader(conn.getInputStream()))); + } + + /** + * Compare the version found with the plugin's version, from an array of integer separated by full stops. + * Examples: + * v1.2 > v1.12 + * v2.1 = v2.01 + * @param remoteVersion the remote version of the plugin + * @return true if the remove version is newer + */ + private boolean isNewerVersion(String remoteVersion) { + String pluginVersion = plugin.getDescription().getVersion(); + + if (pluginVersion == null || !pluginVersion.matches("v?[0-9\\.]+")) { + // Do not throw exceptions, just consider it as v0. + pluginVersion = "0"; + } + + if (!remoteVersion.matches("v?[0-9\\.]+")) { + // Should always be checked before by this class. + throw new IllegalArgumentException("fetched version's format is incorrect"); + } + + // Remove all the "v" from the versions, replace multiple full stops with a single full stop, and split them. + String[] pluginVersionSplit = pluginVersion.replace("v", "").replaceAll("[\\.]{2,}", ".").split("\\."); + String[] remoteVersionSplit = remoteVersion.replace("v", "").replaceAll("[\\.]{2,}", ".").split("\\."); + + int longest = Math.max(pluginVersionSplit.length, remoteVersionSplit.length); + + int[] pluginVersionArray = new int[longest]; + int[] remoteVersionArray = new int[longest]; + + for (int i = 0; i < pluginVersionSplit.length; i++) { + pluginVersionArray[i] = Integer.parseInt(pluginVersionSplit[i]); + } + + for (int i = 0; i < remoteVersionSplit.length; i++) { + remoteVersionArray[i] = Integer.parseInt(remoteVersionSplit[i]); + } + + for (int i = 0; i < longest; i++) { + int diff = remoteVersionArray[i] - pluginVersionArray[i]; + if (diff > 0) { + return true; + } else if (diff < 0) { + return false; + } + + // Continue the loop until diff = 0. + } + + return false; + } + + private String extractVersion(String input) { + Matcher matcher = Pattern.compile("v[0-9\\.]+").matcher(input); + + String result = null; + if (matcher.find()) { + result = matcher.group(); + } + + return result; + } +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/bridge/bungeecord/BungeeChannel.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/bridge/bungeecord/BungeeChannel.java new file mode 100644 index 00000000..1b4f03bb --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/bridge/bungeecord/BungeeChannel.java @@ -0,0 +1,85 @@ +package com.gmail.filoghost.holographicdisplays.bridge.bungeecord; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.EOFException; +import java.io.IOException; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.messaging.PluginMessageListener; + +import com.gmail.filoghost.holographicdisplays.HolographicDisplays; +import com.gmail.filoghost.holographicdisplays.disk.Configuration; + +public class BungeeChannel implements PluginMessageListener { + + private static BungeeChannel instance; + + public static BungeeChannel getInstance() { + if (instance == null) { + instance = new BungeeChannel(HolographicDisplays.getInstance()); + } + return instance; + } + + public BungeeChannel(Plugin plugin) { + Bukkit.getMessenger().registerOutgoingPluginChannel(plugin, "BungeeCord"); + Bukkit.getMessenger().registerIncomingPluginChannel(plugin, "BungeeCord", instance); + Bukkit.getMessenger().registerOutgoingPluginChannel(plugin, "RedisBungee"); + Bukkit.getMessenger().registerIncomingPluginChannel(plugin, "RedisBungee", instance); + } + + @Override + public void onPluginMessageReceived(String channel, Player player, byte[] message) { + if (!channel.equals("BungeeCord") && !channel.equals("RedisBungee")) { + return; + } + + DataInputStream in = new DataInputStream(new ByteArrayInputStream(message)); + + try { + String subChannel = in.readUTF(); + + if (subChannel.equals("PlayerCount")) { + + String server = in.readUTF(); + + if (in.available() > 0) { + int online = in.readInt(); + BungeeServerTracker.handlePing(server, online); + } + } + + } catch (EOFException e) { + // Do nothing. + } catch (IOException e) { + // This should never happen. + e.printStackTrace(); + } + } + + @SuppressWarnings("deprecation") + public void askPlayerCount(String server) { + ByteArrayOutputStream b = new ByteArrayOutputStream(); + DataOutputStream out = new DataOutputStream(b); + + try { + out.writeUTF("PlayerCount"); + out.writeUTF(server); + } catch (IOException e) { + // It should not happen. + e.printStackTrace(); + HolographicDisplays.getInstance().getLogger().warning("I/O Exception while asking for player count on server '" + server + "'."); + } + + // OR, if you don't need to send it to a specific player + Player[] players = Bukkit.getOnlinePlayers(); + if (players.length > 0) { + players[0].sendPluginMessage(HolographicDisplays.getInstance(), Configuration.useRedisBungee ? "RedisBungee" : "BungeeCord", b.toByteArray()); + } + } +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/bridge/bungeecord/BungeeServerInfo.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/bridge/bungeecord/BungeeServerInfo.java new file mode 100644 index 00000000..7151c08a --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/bridge/bungeecord/BungeeServerInfo.java @@ -0,0 +1,29 @@ +package com.gmail.filoghost.holographicdisplays.bridge.bungeecord; + +public class BungeeServerInfo { + + private int onlinePlayers; + private long lastRequest; + + protected BungeeServerInfo(int onlinePlayers, long lastRequest) { + this.onlinePlayers = onlinePlayers; + this.lastRequest = lastRequest; + } + + public int getOnlinePlayers() { + return onlinePlayers; + } + + public void setOnlinePlayers(int onlinePlayers) { + this.onlinePlayers = onlinePlayers; + } + + public long getLastRequest() { + return lastRequest; + } + + public void setLastRequest(long lastRequest) { + this.lastRequest = lastRequest; + } + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/bridge/bungeecord/BungeeServerTracker.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/bridge/bungeecord/BungeeServerTracker.java new file mode 100644 index 00000000..5a8ba914 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/bridge/bungeecord/BungeeServerTracker.java @@ -0,0 +1,72 @@ +package com.gmail.filoghost.holographicdisplays.bridge.bungeecord; + +import java.util.HashMap; +import java.util.Map; + +import org.bukkit.Bukkit; + +import com.gmail.filoghost.holographicdisplays.HolographicDisplays; + +public class BungeeServerTracker { + + private static Map trackedServers = new HashMap(); + private static int taskID = -1; + + public static void resetTrackedServers() { + trackedServers.clear(); + } + + public static void track(String server) { + if (!trackedServers.containsKey(server)) { + BungeeServerInfo info = new BungeeServerInfo(0, System.currentTimeMillis()); + trackedServers.put(server, info); + BungeeChannel.getInstance().askPlayerCount(server); + } + } + + public static void untrack(String server) { + trackedServers.remove(server); + } + + // Handle a successful ping. + protected static void handlePing(String server, int online) { + BungeeServerInfo info = trackedServers.get(server); + if (info == null) { + info = new BungeeServerInfo(online, System.currentTimeMillis()); + trackedServers.put(server, info); + } else { + info.setOnlinePlayers(online); + } + } + + public static int getPlayersOnline(String server) { + BungeeServerInfo info = trackedServers.get(server); + if (info != null) { + info.setLastRequest(System.currentTimeMillis()); + return info.getOnlinePlayers(); + } else { + // It was not tracked, add it. + track(server); + return 0; + } + } + + public static Map getTrackedServers() { + return trackedServers; + } + + public static void startTask(int refreshSeconds) { + + if (taskID != -1) { + Bukkit.getScheduler().cancelTask(taskID); + } + + taskID = Bukkit.getScheduler().scheduleSyncRepeatingTask(HolographicDisplays.getInstance(), new Runnable() { + public void run() { + for (String server : trackedServers.keySet()) { + BungeeChannel.getInstance().askPlayerCount(server); + } + } + }, 0, refreshSeconds * 20); + } +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/bridge/protocollib/AbstractPacket.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/bridge/protocollib/AbstractPacket.java new file mode 100644 index 00000000..5dd4aaa4 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/bridge/protocollib/AbstractPacket.java @@ -0,0 +1,82 @@ +/* + * PacketWrapper - Contains wrappers for each packet in Minecraft. + * Copyright (C) 2012 Kristian S. Stangeland + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; + * if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + +package com.gmail.filoghost.holographicdisplays.bridge.protocollib; + +import java.lang.reflect.InvocationTargetException; + +import org.bukkit.entity.Player; + +import com.comphenix.protocol.PacketType; +import com.comphenix.protocol.ProtocolLibrary; +import com.comphenix.protocol.events.PacketContainer; +import com.google.common.base.Objects; + +public abstract class AbstractPacket { + // The packet we will be modifying + protected PacketContainer handle; + + /** + * Constructs a new strongly typed wrapper for the given packet. + * @param handle - handle to the raw packet data. + * @param type - the packet type. + */ + protected AbstractPacket(PacketContainer handle, PacketType type) { + // Make sure we're given a valid packet + if (handle == null) + throw new IllegalArgumentException("Packet handle cannot be NULL."); + if (!Objects.equal(handle.getType(), type)) + throw new IllegalArgumentException( + handle.getHandle() + " is not a packet of type " + type); + + this.handle = handle; + } + + /** + * Retrieve a handle to the raw packet data. + * @return Raw packet data. + */ + public PacketContainer getHandle() { + return handle; + } + + /** + * Send the current packet to the given receiver. + * @param receiver - the receiver. + * @throws RuntimeException If the packet cannot be sent. + */ + public void sendPacket(Player receiver) { + try { + ProtocolLibrary.getProtocolManager().sendServerPacket(receiver, getHandle()); + } catch (InvocationTargetException e) { + throw new RuntimeException("Cannot send packet.", e); + } + } + + /** + * Simulate receiving the current packet from the given sender. + * @param sender - the sender. + * @throws RuntimeException If the packet cannot be received. + */ + public void recievePacket(Player sender) { + try { + ProtocolLibrary.getProtocolManager().recieveClientPacket(sender, getHandle()); + } catch (Exception e) { + throw new RuntimeException("Cannot recieve packet.", e); + } + } +} \ No newline at end of file diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/bridge/protocollib/ProtocolLibHook.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/bridge/protocollib/ProtocolLibHook.java new file mode 100644 index 00000000..989e2acb --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/bridge/protocollib/ProtocolLibHook.java @@ -0,0 +1,305 @@ +package com.gmail.filoghost.holographicdisplays.bridge.protocollib; + +import java.util.List; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; + +import com.comphenix.protocol.PacketType; +import com.comphenix.protocol.ProtocolLibrary; +import com.comphenix.protocol.events.ListenerPriority; +import com.comphenix.protocol.events.PacketAdapter; +import com.comphenix.protocol.events.PacketContainer; +import com.comphenix.protocol.events.PacketEvent; +import com.comphenix.protocol.wrappers.WrappedDataWatcher; +import com.comphenix.protocol.wrappers.WrappedWatchableObject; +import com.gmail.filoghost.holographicdisplays.HolographicDisplays; +import com.gmail.filoghost.holographicdisplays.bridge.protocollib.WrapperPlayServerSpawnEntity.ObjectTypes; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.NMSManager; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSEntityBase; +import com.gmail.filoghost.holographicdisplays.object.CraftHologram; +import com.gmail.filoghost.holographicdisplays.object.line.CraftHologramLine; +import com.gmail.filoghost.holographicdisplays.object.line.CraftItemLine; +import com.gmail.filoghost.holographicdisplays.object.line.CraftTextLine; +import com.gmail.filoghost.holographicdisplays.object.line.CraftTouchSlimeLine; +import com.gmail.filoghost.holographicdisplays.object.line.CraftTouchableLine; +import com.gmail.filoghost.holographicdisplays.util.Utils; +import com.gmail.filoghost.holographicdisplays.util.VersionUtils; + +public class ProtocolLibHook { + + private static boolean hasProtocolLib; + private static NMSManager nmsManager; + + private static int customNameWatcherIndex; + + public static boolean load(NMSManager nmsManager, Plugin plugin, boolean is1_8) { + ProtocolLibHook.nmsManager = nmsManager; + + if (Bukkit.getPluginManager().isPluginEnabled("ProtocolLib")) { + + hasProtocolLib = true; + + plugin.getLogger().info("Found ProtocolLib, adding support for player relative variables."); + if (is1_8) { + customNameWatcherIndex = 2; + } else { + customNameWatcherIndex = 10; + } + + + ProtocolLibrary.getProtocolManager().addPacketListener(new PacketAdapter(plugin, ListenerPriority.NORMAL, PacketType.Play.Server.SPAWN_ENTITY_LIVING, PacketType.Play.Server.SPAWN_ENTITY, PacketType.Play.Server.ENTITY_METADATA) { + + @Override + public void onPacketSending(PacketEvent event) { + + PacketContainer packet = event.getPacket(); + + // Spawn entity packet + if (packet.getType() == PacketType.Play.Server.SPAWN_ENTITY_LIVING) { + + WrapperPlayServerSpawnEntityLiving spawnEntityPacket = new WrapperPlayServerSpawnEntityLiving(packet); + Entity entity = spawnEntityPacket.getEntity(event); + + if (entity == null || !isHologramType(entity.getType())) { + return; + } + + CraftHologram hologram = getHologram(entity); + if (hologram == null) { + return; + } + + Player player = event.getPlayer(); + if (!hologram.getVisibilityManager().isVisibleTo(player)) { + event.setCancelled(true); + return; + } + + WrappedDataWatcher dataWatcher = spawnEntityPacket.getMetadata(); + String customName = dataWatcher.getString(customNameWatcherIndex); + + if (customName.contains("{player}") || customName.contains("{displayname}")) { + + WrappedDataWatcher dataWatcherClone = dataWatcher.deepClone(); + dataWatcherClone.setObject(customNameWatcherIndex, customName.replace("{player}", player.getName()).replace("{displayname}", player.getDisplayName())); + spawnEntityPacket.setMetadata(dataWatcherClone); + event.setPacket(spawnEntityPacket.getHandle()); + + } + + } else if (packet.getType() == PacketType.Play.Server.SPAWN_ENTITY) { + + WrapperPlayServerSpawnEntity spawnEntityPacket = new WrapperPlayServerSpawnEntity(packet); + int objectId = spawnEntityPacket.getType(); + if (objectId != ObjectTypes.ITEM_STACK && objectId != ObjectTypes.WITHER_SKULL && objectId != ObjectTypes.ARMOR_STAND) { + return; + } + + Entity entity = spawnEntityPacket.getEntity(event); + if (entity == null) { + return; + } + + CraftHologram hologram = getHologram(entity); + if (hologram == null) { + return; + } + + Player player = event.getPlayer(); + if (!hologram.getVisibilityManager().isVisibleTo(player)) { + event.setCancelled(true); + return; + } + + } else if (packet.getType() == PacketType.Play.Server.ENTITY_METADATA) { + + WrapperPlayServerEntityMetadata entityMetadataPacket = new WrapperPlayServerEntityMetadata(packet); + Entity entity = entityMetadataPacket.getEntity(event); + + if (entity == null) { + return; + } + + if (entity.getType() != EntityType.HORSE && !VersionUtils.isArmorstand(entity.getType())) { + // Enough, only horses and armorstands are used with custom names. + return; + } + + CraftHologram hologram = getHologram(entity); + if (hologram == null) { + return; + } + + Player player = event.getPlayer(); + if (!hologram.getVisibilityManager().isVisibleTo(player)) { + event.setCancelled(true); + return; + } + + List dataWatcherValues = entityMetadataPacket.getEntityMetadata(); + + for (int i = 0; i < dataWatcherValues.size(); i++) { + + if (dataWatcherValues.get(i).getIndex() == customNameWatcherIndex) { + + Object customNameObject = dataWatcherValues.get(i).deepClone().getValue(); + if (customNameObject instanceof String == false) { + return; + } + + String customName = (String) customNameObject; + + if (customName.contains("{player}") || customName.contains("{displayname}")) { + + entityMetadataPacket = new WrapperPlayServerEntityMetadata(packet.deepClone()); + List clonedList = entityMetadataPacket.getEntityMetadata(); + WrappedWatchableObject clonedElement = clonedList.get(i); + clonedElement.setValue(customName.replace("{player}", player.getName()).replace("{displayname}", player.getDisplayName())); + entityMetadataPacket.setEntityMetadata(clonedList); + event.setPacket(entityMetadataPacket.getHandle()); + return; + + } + } + } + } + } + }); + + return true; + } + + return false; + } + + public static void sendDestroyEntitiesPacket(Player player, CraftHologram hologram) { + if (!hasProtocolLib) { + return; + } + + List ids = Utils.newList(); + for (CraftHologramLine line : hologram.getLinesUnsafe()) { + if (line.isSpawned()) { + for (int id : line.getEntitiesIDs()) { + ids.add(id); + } + } + } + + if (!ids.isEmpty()) { + WrapperPlayServerEntityDestroy packet = new WrapperPlayServerEntityDestroy(); + packet.setEntities(ids); + packet.sendPacket(player); + } + } + + public static void sendCreateEntitiesPacket(Player player, CraftHologram hologram) { + if (!hasProtocolLib) { + return; + } + + for (CraftHologramLine line : hologram.getLinesUnsafe()) { + if (line.isSpawned()) { + + if (line instanceof CraftTextLine) { + CraftTextLine textLine = (CraftTextLine) line; + + if (textLine.isSpawned()) { + + AbstractPacket nameablePacket = new WrapperPlayServerSpawnEntityLiving(textLine.getNmsNameble().getBukkitEntityNMS()); + nameablePacket.sendPacket(player); + + if (textLine.getNmsSkullVehicle() != null) { + AbstractPacket vehiclePacket = new WrapperPlayServerSpawnEntity(textLine.getNmsSkullVehicle().getBukkitEntityNMS(), ObjectTypes.WITHER_SKULL, 0); + vehiclePacket.sendPacket(player); + + WrapperPlayServerAttachEntity attachPacket = new WrapperPlayServerAttachEntity(); + attachPacket.setVehicleId(textLine.getNmsSkullVehicle().getIdNMS()); + attachPacket.setEntityId(textLine.getNmsNameble().getIdNMS()); + attachPacket.sendPacket(player); + } + } + + } else if (line instanceof CraftItemLine) { + CraftItemLine itemLine = (CraftItemLine) line; + + if (itemLine.isSpawned()) { + AbstractPacket itemPacket = new WrapperPlayServerSpawnEntity(itemLine.getNmsItem().getBukkitEntityNMS(), ObjectTypes.ITEM_STACK, 1); + itemPacket.sendPacket(player); + + AbstractPacket vehiclePacket; + if (HolographicDisplays.is1_8()) { + // In 1.8 we have armor stands, that are living entities. + vehiclePacket = new WrapperPlayServerSpawnEntityLiving(itemLine.getNmsVehicle().getBukkitEntityNMS()); + } else { + vehiclePacket = new WrapperPlayServerSpawnEntity(itemLine.getNmsVehicle().getBukkitEntityNMS(), ObjectTypes.WITHER_SKULL, 0); + } + + vehiclePacket.sendPacket(player); + + WrapperPlayServerAttachEntity attachPacket = new WrapperPlayServerAttachEntity(); + attachPacket.setVehicleId(itemLine.getNmsVehicle().getIdNMS()); + attachPacket.setEntityId(itemLine.getNmsItem().getIdNMS()); + attachPacket.sendPacket(player); + + WrapperPlayServerEntityMetadata itemDataPacket = new WrapperPlayServerEntityMetadata(); + + List metadata = Utils.newList(); + metadata.add(new WrappedWatchableObject(10, itemLine.getItemStack())); + metadata.add(new WrappedWatchableObject(1, (short) 300)); + metadata.add(new WrappedWatchableObject(0, (byte) 0)); + itemDataPacket.setEntityMetadata(metadata); + itemDataPacket.setEntityId(itemLine.getNmsItem().getIdNMS()); + itemDataPacket.sendPacket(player); + } + } + + // Unsafe cast, however both CraftTextLine and CraftItemLine are touchable. + CraftTouchableLine touchableLine = (CraftTouchableLine) line; + + if (touchableLine.isSpawned() && touchableLine.getTouchSlime() != null) { + + CraftTouchSlimeLine touchSlime = touchableLine.getTouchSlime(); + + if (touchSlime.isSpawned()) { + AbstractPacket vehiclePacket; + + if (HolographicDisplays.is1_8()) { + // Armor stand vehicle + vehiclePacket = new WrapperPlayServerSpawnEntityLiving(touchSlime.getNmsVehicle().getBukkitEntityNMS()); + } else { + // Wither skull vehicle + vehiclePacket = new WrapperPlayServerSpawnEntity(touchSlime.getNmsVehicle().getBukkitEntityNMS(), ObjectTypes.WITHER_SKULL, 0); + } + vehiclePacket.sendPacket(player); + + AbstractPacket slimePacket = new WrapperPlayServerSpawnEntityLiving(touchSlime.getNmsSlime().getBukkitEntityNMS()); + slimePacket.sendPacket(player); + + WrapperPlayServerAttachEntity attachPacket = new WrapperPlayServerAttachEntity(); + attachPacket.setVehicleId(touchSlime.getNmsVehicle().getIdNMS()); + attachPacket.setEntityId(touchSlime.getNmsSlime().getIdNMS()); + attachPacket.sendPacket(player); + } + } + } + } + } + + private static boolean isHologramType(EntityType type) { + return type == EntityType.HORSE || type == EntityType.WITHER_SKULL || type == EntityType.DROPPED_ITEM || type == EntityType.SLIME || VersionUtils.isArmorstand(type); // To maintain backwards compatibility + } + + private static CraftHologram getHologram(Entity bukkitEntity) { + NMSEntityBase entity = nmsManager.getNMSEntityBase(bukkitEntity); + if (entity != null) { + return entity.getHologramLine().getParent(); + } + + return null; + } +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/bridge/protocollib/WrapperPlayServerAttachEntity.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/bridge/protocollib/WrapperPlayServerAttachEntity.java new file mode 100644 index 00000000..df3fff6a --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/bridge/protocollib/WrapperPlayServerAttachEntity.java @@ -0,0 +1,122 @@ +/* + * PacketWrapper - Contains wrappers for each packet in Minecraft. + * Copyright (C) 2012 Kristian S. Stangeland + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; + * if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + +package com.gmail.filoghost.holographicdisplays.bridge.protocollib; + +import org.bukkit.World; +import org.bukkit.entity.Entity; + +import com.comphenix.protocol.PacketType; +import com.comphenix.protocol.events.PacketContainer; +import com.comphenix.protocol.events.PacketEvent; + +public class WrapperPlayServerAttachEntity extends AbstractPacket { + public static final PacketType TYPE = PacketType.Play.Server.ATTACH_ENTITY; + + public WrapperPlayServerAttachEntity() { + super(new PacketContainer(TYPE), TYPE); + handle.getModifier().writeDefaults(); + } + + public WrapperPlayServerAttachEntity(PacketContainer packet) { + super(packet, TYPE); + } + + /** + * Retrieve whether or not the entity is leached onto the vehicle. + * @return TRUE if it is, FALSE otherwise. + */ + public boolean getLeached() { + return handle.getIntegers().read(0) != 0; + } + + /** + * Set whether or not the entity is leached onto the vehicle. + * @param value - TRUE if it is leached, FALSE otherwise. + */ + public void setLeached(boolean value) { + handle.getIntegers().write(0, value ? 1 : 0); + } + + /** + * Retrieve the player entity ID being attached. + * @return The current Entity ID + */ + public int getEntityId() { + return handle.getIntegers().read(1); + } + + /** + * Set the player entity ID being attached. + * @param value - new value. + */ + public void setEntityId(int value) { + handle.getIntegers().write(1, value); + } + + /** + * Retrieve the entity being attached. + * @param world - the current world of the entity. + * @return The entity. + */ + public Entity getEntity(World world) { + return handle.getEntityModifier(world).read(1); + } + + /** + * Retrieve the entity being attached. + * @param event - the packet event. + * @return The entity. + */ + public Entity getEntity(PacketEvent event) { + return getEntity(event.getPlayer().getWorld()); + } + + /** + * Retrieve the vehicle entity ID attached to (-1 for unattaching). + * @return The current Vehicle ID + */ + public int getVehicleId() { + return handle.getIntegers().read(2); + } + + /** + * Set the vehicle entity ID attached to (-1 for unattaching). + * @param value - new value. + */ + public void setVehicleId(int value) { + handle.getIntegers().write(2, value); + } + + /** + * Retrieve the vehicle entity attached to (NULL for unattaching). + * @param world - the current world of the entity. + * @return The vehicle. + */ + public Entity getVehicle(World world) { + return handle.getEntityModifier(world).read(2); + } + + /** + * Retrieve the vehicle entity attached to (NULL for unattaching). + * @param event - the packet event. + * @return The vehicle. + */ + public Entity getVehicle(PacketEvent event) { + return getVehicle(event.getPlayer().getWorld()); + } +} \ No newline at end of file diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/bridge/protocollib/WrapperPlayServerEntityDestroy.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/bridge/protocollib/WrapperPlayServerEntityDestroy.java new file mode 100644 index 00000000..82383eec --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/bridge/protocollib/WrapperPlayServerEntityDestroy.java @@ -0,0 +1,61 @@ +/* + * PacketWrapper - Contains wrappers for each packet in Minecraft. + * Copyright (C) 2012 Kristian S. Stangeland + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; + * if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + +package com.gmail.filoghost.holographicdisplays.bridge.protocollib; + +import java.util.List; + +import com.comphenix.protocol.PacketType; +import com.comphenix.protocol.events.PacketContainer; +import com.google.common.primitives.Ints; + +public class WrapperPlayServerEntityDestroy extends AbstractPacket { + public static final PacketType TYPE = PacketType.Play.Server.ENTITY_DESTROY; + + public WrapperPlayServerEntityDestroy() { + super(new PacketContainer(TYPE), TYPE); + handle.getModifier().writeDefaults(); + } + + public WrapperPlayServerEntityDestroy(PacketContainer packet) { + super(packet, TYPE); + } + + /** + * Retrieve the IDs of the entities that will be destroyed. + * @return The current entities. + */ + public List getEntities() { + return Ints.asList(handle.getIntegerArrays().read(0)); + } + + /** + * Set the entities that will be destroyed. + * @param value - new value. + */ + public void setEntities(int[] entities) { + handle.getIntegerArrays().write(0, entities); + } + + /** + * Set the entities that will be destroyed. + * @param value - new value. + */ + public void setEntities(List entities) { + setEntities(Ints.toArray(entities)); + } +} \ No newline at end of file diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/bridge/protocollib/WrapperPlayServerEntityMetadata.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/bridge/protocollib/WrapperPlayServerEntityMetadata.java new file mode 100644 index 00000000..e3eef964 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/bridge/protocollib/WrapperPlayServerEntityMetadata.java @@ -0,0 +1,94 @@ +/* + * PacketWrapper - Contains wrappers for each packet in Minecraft. + * Copyright (C) 2012 Kristian S. Stangeland + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; + * if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + +package com.gmail.filoghost.holographicdisplays.bridge.protocollib; + +import java.util.List; + +import org.bukkit.World; +import org.bukkit.entity.Entity; + +import com.comphenix.protocol.PacketType; +import com.comphenix.protocol.events.PacketContainer; +import com.comphenix.protocol.events.PacketEvent; +import com.comphenix.protocol.wrappers.WrappedDataWatcher; +import com.comphenix.protocol.wrappers.WrappedWatchableObject; + +public class WrapperPlayServerEntityMetadata extends AbstractPacket { + public static final PacketType TYPE = PacketType.Play.Server.ENTITY_METADATA; + + public WrapperPlayServerEntityMetadata() { + super(new PacketContainer(TYPE), TYPE); + handle.getModifier().writeDefaults(); + } + + public WrapperPlayServerEntityMetadata(PacketContainer packet) { + super(packet, TYPE); + } + + /** + * Retrieve unique entity ID to update. + * @return The current Entity ID + */ + public int getEntityId() { + return handle.getIntegers().read(0); + } + + /** + * Set unique entity ID to update. + * @param value - new value. + */ + public void setEntityId(int value) { + handle.getIntegers().write(0, value); + } + + /** + * Retrieve the entity. + * @param world - the current world of the entity. + * @return The entity. + */ + public Entity getEntity(World world) { + return handle.getEntityModifier(world).read(0); + } + + /** + * Retrieve the entity. + * @param event - the packet event. + * @return The entity. + */ + public Entity getEntity(PacketEvent event) { + return getEntity(event.getPlayer().getWorld()); + } + + /** + * Retrieve a list of all the watchable objects. + *

+ * This can be converted to a data watcher using {@link WrappedDataWatcher#WrappedDataWatcher(List) WrappedDataWatcher(List)} + * @return The current metadata + */ + public List getEntityMetadata() { + return handle.getWatchableCollectionModifier().read(0); + } + + /** + * Set the list of the watchable objects (meta data). + * @param value - new value. + */ + public void setEntityMetadata(List value) { + handle.getWatchableCollectionModifier().write(0, value); + } +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/bridge/protocollib/WrapperPlayServerSpawnEntity.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/bridge/protocollib/WrapperPlayServerSpawnEntity.java new file mode 100644 index 00000000..9feae7ae --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/bridge/protocollib/WrapperPlayServerSpawnEntity.java @@ -0,0 +1,334 @@ +/* + * PacketWrapper - Contains wrappers for each packet in Minecraft. + * Copyright (C) 2012 Kristian S. Stangeland + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; + * if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + +package com.gmail.filoghost.holographicdisplays.bridge.protocollib; +import org.bukkit.World; +import org.bukkit.entity.Entity; + +import com.comphenix.protocol.PacketType; +import com.comphenix.protocol.ProtocolLibrary; +import com.comphenix.protocol.events.PacketContainer; +import com.comphenix.protocol.events.PacketEvent; +import com.comphenix.protocol.injector.PacketConstructor; +import com.comphenix.protocol.reflect.IntEnum; + +public class WrapperPlayServerSpawnEntity extends AbstractPacket { + public static final PacketType TYPE = PacketType.Play.Server.SPAWN_ENTITY; + + private static PacketConstructor entityConstructor; + + /** + * Represents the different object types. + * + * @author Kristian + */ + public static class ObjectTypes extends IntEnum { + public static final int BOAT = 1; + public static final int ITEM_STACK = 2; + public static final int MINECART = 10; + public static final int MINECART_STORAGE = 11; + public static final int MINECART_POWERED = 12; + public static final int ACTIVATED_TNT = 50; + public static final int ENDER_CRYSTAL = 51; + public static final int ARROW_PROJECTILE = 60; + public static final int SNOWBALL_PROJECTILE = 61; + public static final int EGG_PROJECTILE = 62; + public static final int FIRE_BALL_GHAST = 63; + public static final int FIRE_BALL_BLAZE = 64; + public static final int THROWN_ENDERPEARL = 65; + public static final int WITHER_SKULL = 66; + public static final int FALLING_BLOCK = 70; + public static final int ITEM_FRAME = 71; + public static final int EYE_OF_ENDER = 72; + public static final int THROWN_POTION = 73; + public static final int FALLING_DRAGON_EGG = 74; + public static final int THROWN_EXP_BOTTLE = 75; + public static final int FIREWORK = 76; + public static final int ARMOR_STAND = 78; + public static final int FISHING_FLOAT = 90; + + /** + * The singleton instance. Can also be retrieved from the parent class. + */ + private static ObjectTypes INSTANCE = new ObjectTypes(); + + /** + * Retrieve an instance of the object types enum. + * @return Object type enum. + */ + public static ObjectTypes getInstance() { + return INSTANCE; + } + } + + public WrapperPlayServerSpawnEntity() { + super(new PacketContainer(TYPE), TYPE); + handle.getModifier().writeDefaults(); + } + + public WrapperPlayServerSpawnEntity(PacketContainer packet) { + super(packet, TYPE); + } + + public WrapperPlayServerSpawnEntity(Entity entity, int type, int objectData) { + super(fromEntity(entity, type, objectData), TYPE); + } + + // Useful constructor + private static PacketContainer fromEntity(Entity entity, int type, int objectData) { + if (entityConstructor == null) + entityConstructor = ProtocolLibrary.getProtocolManager().createPacketConstructor(TYPE, entity, type, objectData); + return entityConstructor.createPacket(entity, type, objectData); + } + + /** + * Retrieve entity ID of the Object. + * @return The current EID + */ + public int getEntityID() { + return handle.getIntegers().read(0); + } + + /** + * Retrieve the entity that will be spawned. + * @param world - the current world of the entity. + * @return The spawned entity. + */ + public Entity getEntity(World world) { + return handle.getEntityModifier(world).read(0); + } + + /** + * Retrieve the entity that will be spawned. + * @param event - the packet event. + * @return The spawned entity. + */ + public Entity getEntity(PacketEvent event) { + return getEntity(event.getPlayer().getWorld()); + } + + /** + * Set entity ID of the Object. + * @param value - new value. + */ + public void setEntityID(int value) { + handle.getIntegers().write(0, value); + } + + /** + * Retrieve the type of object. See {@link ObjectTypes} + * @return The current Type + */ + public int getType() { + return handle.getIntegers().read(9); + } + + /** + * Set the type of object. See {@link ObjectTypes}. + * @param value - new value. + */ + public void setType(int value) { + handle.getIntegers().write(9, value); + } + + /** + * Retrieve the x position of the object. + *

+ * Note that the coordinate is rounded off to the nearest 1/32 of a meter. + * @return The current X + */ + public double getX() { + return handle.getIntegers().read(1) / 32.0D; + } + + /** + * Set the x position of the object. + * @param value - new value. + */ + public void setX(double value) { + handle.getIntegers().write(1, (int) Math.floor(value * 32.0D)); + } + + /** + * Retrieve the y position of the object. + *

+ * Note that the coordinate is rounded off to the nearest 1/32 of a meter. + * @return The current y + */ + public double getY() { + return handle.getIntegers().read(2) / 32.0D; + } + + /** + * Set the y position of the object. + * @param value - new value. + */ + public void setY(double value) { + handle.getIntegers().write(2, (int) Math.floor(value * 32.0D)); + } + + /** + * Retrieve the z position of the object. + *

+ * Note that the coordinate is rounded off to the nearest 1/32 of a meter. + * @return The current z + */ + public double getZ() { + return handle.getIntegers().read(3) / 32.0D; + } + + /** + * Set the z position of the object. + * @param value - new value. + */ + public void setZ(double value) { + handle.getIntegers().write(3, (int) Math.floor(value * 32.0D)); + } + + /** + * Retrieve the optional speed x. + *

+ * This is ignored if {@link #getObjectData()} is zero. + * @return The optional speed x. + */ + public double getOptionalSpeedX() { + return handle.getIntegers().read(4) / 8000.0D; + } + + /** + * Set the optional speed x. + * @param value - new value. + */ + public void setOptionalSpeedX(double value) { + handle.getIntegers().write(4, (int) (value * 8000.0D)); + } + + /** + * Retrieve the optional speed y. + *

+ * This is ignored if {@link #getObjectData()} is zero. + * @return The optional speed y. + */ + public double getOptionalSpeedY() { + return handle.getIntegers().read(5) / 8000.0D; + } + + /** + * Set the optional speed y. + * @param value - new value. + */ + public void setOptionalSpeedY(double value) { + handle.getIntegers().write(5, (int) (value * 8000.0D)); + } + + /** + * Retrieve the optional speed z. + *

+ * This is ignored if {@link #getObjectData()} is zero. + * @return The optional speed z. + */ + public double getOptionalSpeedZ() { + return handle.getIntegers().read(6) / 8000.0D; + } + + /** + * Set the optional speed z. + * @param value - new value. + */ + public void setOptionalSpeedZ(double value) { + handle.getIntegers().write(6, (int) (value * 8000.0D)); + } + + /** + * Retrieve the yaw. + * @return The current Yaw + */ + public float getYaw() { + return (handle.getIntegers().read(7) * 360.F) / 256.0F; + } + + /** + * Set the yaw of the object spawned. + * @param value - new yaw. + */ + public void setYaw(float value) { + handle.getIntegers().write(7, (int) (value * 256.0F / 360.0F)); + } + + /** + * Retrieve the pitch. + * @return The current pitch. + */ + public float getPitch() { + return (handle.getIntegers().read(8) * 360.F) / 256.0F; + } + + /** + * Set the pitch. + * @param value - new pitch. + */ + public void setPitch(float value) { + handle.getIntegers().write(8, (int) (value * 256.0F / 360.0F)); + } + + /** + * Retrieve object data. + *

+ * The content depends on the object type: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Object Type:Name:Description
ITEM_FRAMEOrientation0-3: South, West, North, East
FALLING_BLOCKBlock TypeBlockID | (Metadata << 0xC)
ProjectilesEntity IDThe entity ID of the thrower
Splash PotionsData ValuePotion data value.
+ * @return The current object Data + */ + public int getObjectData() { + return handle.getIntegers().read(10); + } + + /** + * Set object Data. + *

+ * The content depends on the object type. See {@link #getObjectData()} for more information. + * @param value - new object data. + */ + public void setObjectData(int value) { + handle.getIntegers().write(10, value); + } +} \ No newline at end of file diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/bridge/protocollib/WrapperPlayServerSpawnEntityLiving.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/bridge/protocollib/WrapperPlayServerSpawnEntityLiving.java new file mode 100644 index 00000000..c4df9b58 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/bridge/protocollib/WrapperPlayServerSpawnEntityLiving.java @@ -0,0 +1,275 @@ +/* + * PacketWrapper - Contains wrappers for each packet in Minecraft. + * Copyright (C) 2012 Kristian S. Stangeland + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program; + * if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + +package com.gmail.filoghost.holographicdisplays.bridge.protocollib; + +import org.bukkit.World; +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; + +import com.comphenix.protocol.PacketType; +import com.comphenix.protocol.ProtocolLibrary; +import com.comphenix.protocol.events.PacketContainer; +import com.comphenix.protocol.events.PacketEvent; +import com.comphenix.protocol.injector.PacketConstructor; +import com.comphenix.protocol.wrappers.WrappedDataWatcher; + +public class WrapperPlayServerSpawnEntityLiving extends AbstractPacket { + public static final PacketType TYPE = PacketType.Play.Server.SPAWN_ENTITY_LIVING; + + private static PacketConstructor entityConstructor; + + public WrapperPlayServerSpawnEntityLiving() { + super(new PacketContainer(TYPE), TYPE); + handle.getModifier().writeDefaults(); + } + + public WrapperPlayServerSpawnEntityLiving(PacketContainer packet) { + super(packet, TYPE); + } + + public WrapperPlayServerSpawnEntityLiving(Entity entity) { + super(fromEntity(entity), TYPE); + } + + // Useful constructor + private static PacketContainer fromEntity(Entity entity) { + if (entityConstructor == null) + entityConstructor = ProtocolLibrary.getProtocolManager().createPacketConstructor(TYPE, entity); + return entityConstructor.createPacket(entity); + } + + /** + * Retrieve entity ID. + * @return The current EID + */ + public int getEntityID() { + return handle.getIntegers().read(0); + } + + /** + * Retrieve the entity that will be spawned. + * @param world - the current world of the entity. + * @return The spawned entity. + */ + public Entity getEntity(World world) { + return handle.getEntityModifier(world).read(0); + } + + /** + * Retrieve the entity that will be spawned. + * @param event - the packet event. + * @return The spawned entity. + */ + public Entity getEntity(PacketEvent event) { + return getEntity(event.getPlayer().getWorld()); + } + + /** + * Set entity ID. + * @param value - new value. + */ + public void setEntityID(int value) { + handle.getIntegers().write(0, value); + } + + /** + * Retrieve the type of mob. + * @return The current Type + */ + @SuppressWarnings("deprecation") + public EntityType getType() { + return EntityType.fromId(handle.getIntegers().read(1)); + } + + /** + * Set the type of mob. + * @param value - new value. + */ + @SuppressWarnings("deprecation") + public void setType(EntityType value) { + handle.getIntegers().write(1, (int) value.getTypeId()); + } + + /** + * Retrieve the x position of the object. + *

+ * Note that the coordinate is rounded off to the nearest 1/32 of a meter. + * @return The current X + */ + public double getX() { + return handle.getIntegers().read(2) / 32.0D; + } + + /** + * Set the x position of the object. + * @param value - new value. + */ + public void setX(double value) { + handle.getIntegers().write(2, (int) Math.floor(value * 32.0D)); + } + + /** + * Retrieve the y position of the object. + *

+ * Note that the coordinate is rounded off to the nearest 1/32 of a meter. + * @return The current y + */ + public double getY() { + return handle.getIntegers().read(3) / 32.0D; + } + + /** + * Set the y position of the object. + * @param value - new value. + */ + public void setY(double value) { + handle.getIntegers().write(3, (int) Math.floor(value * 32.0D)); + } + + /** + * Retrieve the z position of the object. + *

+ * Note that the coordinate is rounded off to the nearest 1/32 of a meter. + * @return The current z + */ + public double getZ() { + return handle.getIntegers().read(4) / 32.0D; + } + + /** + * Set the z position of the object. + * @param value - new value. + */ + public void setZ(double value) { + handle.getIntegers().write(4, (int) Math.floor(value * 32.0D)); + } + + /** + * Retrieve the yaw. + * @return The current Yaw + */ + public float getYaw() { + return (handle.getBytes().read(0) * 360.F) / 256.0F; + } + + /** + * Set the yaw of the spawned mob. + * @param value - new yaw. + */ + public void setYaw(float value) { + handle.getBytes().write(0, (byte) (value * 256.0F / 360.0F)); + } + + /** + * Retrieve the pitch. + * @return The current pitch + */ + public float getHeadPitch() { + return (handle.getBytes().read(1) * 360.F) / 256.0F; + } + + /** + * Set the pitch of the spawned mob. + * @param value - new pitch. + */ + public void setHeadPitch(float value) { + handle.getBytes().write(1, (byte) (value * 256.0F / 360.0F)); + } + + /** + * Retrieve the yaw of the mob's head. + * @return The current yaw. + */ + public float getHeadYaw() { + return (handle.getBytes().read(2) * 360.F) / 256.0F; + } + + /** + * Set the yaw of the mob's head. + * @param value - new yaw. + */ + public void setHeadYaw(float value) { + handle.getBytes().write(2, (byte) (value * 256.0F / 360.0F)); + } + + /** + * Retrieve the velocity in the x axis. + * @return The current velocity X + */ + public double getVelocityX() { + return handle.getIntegers().read(5) / 8000.0D; + } + + /** + * Set the velocity in the x axis. + * @param value - new value. + */ + public void setVelocityX(double value) { + handle.getIntegers().write(5, (int) (value * 8000.0D)); + } + + /** + * Retrieve the velocity in the y axis. + * @return The current velocity y + */ + public double getVelocityY() { + return handle.getIntegers().read(6) / 8000.0D; + } + + /** + * Set the velocity in the y axis. + * @param value - new value. + */ + public void setVelocityY(double value) { + handle.getIntegers().write(6, (int) (value * 8000.0D)); + } + + /** + * Retrieve the velocity in the z axis. + * @return The current velocity z + */ + public double getVelocityZ() { + return handle.getIntegers().read(7) / 8000.0D; + } + + /** + * Set the velocity in the z axis. + * @param value - new value. + */ + public void setVelocityZ(double value) { + handle.getIntegers().write(7, (int) (value * 8000.0D)); + } + + /** + * Retrieve the data watcher. + *

+ * Content varies by mob, see Entities. + * @return The current Metadata + */ + public WrappedDataWatcher getMetadata() { + return handle.getDataWatcherModifier().read(0); + } + + /** + * Set the data watcher. + * @param value - new value. + */ + public void setMetadata(WrappedDataWatcher value) { + handle.getDataWatcherModifier().write(0, value); + } +} \ No newline at end of file diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/commands/Colors.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/commands/Colors.java new file mode 100644 index 00000000..d5aac9da --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/commands/Colors.java @@ -0,0 +1,19 @@ +package com.gmail.filoghost.holographicdisplays.commands; + +import org.bukkit.ChatColor; + +public class Colors { + + public static final String + + PRIMARY = "" + ChatColor.AQUA, + PRIMARY_SHADOW = "" + ChatColor.DARK_AQUA, + + SECONDARY = "" + ChatColor.WHITE, + SECONDARY_SHADOW = "" + ChatColor.GRAY, + + BOLD = "" + ChatColor.BOLD, + + ERROR = "" + ChatColor.RED; + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/commands/CommandValidator.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/commands/CommandValidator.java new file mode 100644 index 00000000..91057a8e --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/commands/CommandValidator.java @@ -0,0 +1,86 @@ +package com.gmail.filoghost.holographicdisplays.commands; + +import org.bukkit.Material; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import com.gmail.filoghost.holographicdisplays.exception.CommandException; +import com.gmail.filoghost.holographicdisplays.util.ItemUtils; + +public class CommandValidator { + + public static void notNull(Object obj, String string) throws CommandException { + if (obj == null) { + throw new CommandException(string); + } + } + + public static void isTrue(boolean b, String string) throws CommandException { + if (!b) { + throw new CommandException(string); + } + } + + public static int getInteger(String integer) throws CommandException { + try { + return Integer.parseInt(integer); + } catch (NumberFormatException ex) { + throw new CommandException("Invalid number: '" + integer + "'."); + } + } + + public static boolean isInteger(String integer) { + try { + Integer.parseInt(integer); + return true; + } catch (NumberFormatException ex) { + return false; + } + } + + public static Player getPlayerSender(CommandSender sender) throws CommandException { + if (sender instanceof Player) { + return (Player) sender; + } else { + throw new CommandException("You must be a player to use this command."); + } + } + + public static boolean isPlayerSender(CommandSender sender) { + return sender instanceof Player; + } + + @SuppressWarnings("deprecation") + public static ItemStack matchItemStack(String input) throws CommandException { + + input = input.replace(" ", ""); // Remove the spaces + + int dataValue = 0; + if (input.contains(":")) { + String[] split = input.split(":", 2); + dataValue = getInteger(split[1]); + input = split[0]; + } + + Material match = null; + if (isInteger(input)) { + int id = getInteger(input); + for (Material mat : Material.values()) { + if (mat.getId() == id) { + match = mat; + break; + } + } + } else { + match = ItemUtils.matchMaterial(input); + } + + if (match == null || match == Material.AIR) { + throw new CommandException("Invalid material: " + input); + } + + return new ItemStack(match, 1, (short) dataValue); + } + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/commands/Strings.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/commands/Strings.java new file mode 100644 index 00000000..829ac329 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/commands/Strings.java @@ -0,0 +1,23 @@ +package com.gmail.filoghost.holographicdisplays.commands; + +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; + +public class Strings { + + public static final String NO_SUCH_HOLOGRAM = ChatColor.RED + "A hologram with that name doesn't exist."; + + public static final String BASE_PERM = "holograms."; + + public static final String TIP_PREFIX = "" + ChatColor.YELLOW + ChatColor.BOLD + "TIP" + Colors.SECONDARY_SHADOW + " "; + + + public static String formatTitle(String input) { + return "" + Colors.PRIMARY_SHADOW + ChatColor.BOLD + "----- " + input + Colors.PRIMARY_SHADOW + ChatColor.BOLD + " -----"; + } + + public static void sendWarning(CommandSender recipient, String warning) { + recipient.sendMessage(ChatColor.RED + "( " + ChatColor.DARK_RED + ChatColor.BOLD + "!" + ChatColor.RED + " ) " + Colors.SECONDARY_SHADOW + warning); + } + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/commands/main/HologramSubCommand.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/commands/main/HologramSubCommand.java new file mode 100644 index 00000000..c753e70d --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/commands/main/HologramSubCommand.java @@ -0,0 +1,72 @@ +package com.gmail.filoghost.holographicdisplays.commands.main; + +import java.util.List; + +import org.bukkit.command.CommandSender; + +import com.gmail.filoghost.holographicdisplays.exception.CommandException; + +public abstract class HologramSubCommand { + + private String name; + private String permission; + private String[] aliases; + + public HologramSubCommand(String name) { + this(name, new String[0]); + } + + public HologramSubCommand(String name, String... aliases) { + this.name = name; + this.aliases = aliases; + } + + public String getName() { + return name; + } + + public void setPermission(String permission) { + this.permission = permission; + } + + public String getPermission() { + return permission; + } + + public final boolean hasPermission(CommandSender sender) { + if (permission == null) return true; + return sender.hasPermission(permission); + } + + public abstract String getPossibleArguments(); + + public abstract int getMinimumArguments(); + + public abstract void execute(CommandSender sender, String label, String[] args) throws CommandException; + + public abstract List getTutorial(); + + public abstract SubCommandType getType(); + + public enum SubCommandType { + GENERIC, EDIT_LINES, HIDDEN + } + + + public final boolean isValidTrigger(String name) { + if (this.name.equalsIgnoreCase(name)) { + return true; + } + + if (aliases != null) { + for (String alias : aliases) { + if (alias.equalsIgnoreCase(name)) { + return true; + } + } + } + + return false; + } + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/commands/main/HologramsCommandHandler.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/commands/main/HologramsCommandHandler.java new file mode 100644 index 00000000..a8b3ae3c --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/commands/main/HologramsCommandHandler.java @@ -0,0 +1,106 @@ +package com.gmail.filoghost.holographicdisplays.commands.main; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; + +import com.gmail.filoghost.holographicdisplays.HolographicDisplays; +import com.gmail.filoghost.holographicdisplays.commands.Colors; +import com.gmail.filoghost.holographicdisplays.commands.Strings; +import com.gmail.filoghost.holographicdisplays.commands.main.subs.AddlineCommand; +import com.gmail.filoghost.holographicdisplays.commands.main.subs.AlignCommand; +import com.gmail.filoghost.holographicdisplays.commands.main.subs.CreateCommand; +import com.gmail.filoghost.holographicdisplays.commands.main.subs.DeleteCommand; +import com.gmail.filoghost.holographicdisplays.commands.main.subs.EditCommand; +import com.gmail.filoghost.holographicdisplays.commands.main.subs.FixCommand; +import com.gmail.filoghost.holographicdisplays.commands.main.subs.HelpCommand; +import com.gmail.filoghost.holographicdisplays.commands.main.subs.InsertlineCommand; +import com.gmail.filoghost.holographicdisplays.commands.main.subs.ListCommand; +import com.gmail.filoghost.holographicdisplays.commands.main.subs.MovehereCommand; +import com.gmail.filoghost.holographicdisplays.commands.main.subs.NearCommand; +import com.gmail.filoghost.holographicdisplays.commands.main.subs.ReadimageCommand; +import com.gmail.filoghost.holographicdisplays.commands.main.subs.ReadtextCommand; +import com.gmail.filoghost.holographicdisplays.commands.main.subs.ReloadCommand; +import com.gmail.filoghost.holographicdisplays.commands.main.subs.RemovelineCommand; +import com.gmail.filoghost.holographicdisplays.commands.main.subs.SetlineCommand; +import com.gmail.filoghost.holographicdisplays.commands.main.subs.TeleportCommand; +import com.gmail.filoghost.holographicdisplays.exception.CommandException; +import com.gmail.filoghost.holographicdisplays.util.Utils; + +public class HologramsCommandHandler implements CommandExecutor { + + private List subCommands; + + public HologramsCommandHandler() { + subCommands = Utils.newList(); + + registerSubCommand(new AddlineCommand()); + registerSubCommand(new CreateCommand()); + registerSubCommand(new DeleteCommand()); + registerSubCommand(new EditCommand(this)); + registerSubCommand(new ListCommand()); + registerSubCommand(new NearCommand()); + registerSubCommand(new TeleportCommand()); + registerSubCommand(new MovehereCommand()); + registerSubCommand(new AlignCommand()); + registerSubCommand(new FixCommand()); + registerSubCommand(new ReloadCommand()); + + registerSubCommand(new RemovelineCommand()); + registerSubCommand(new SetlineCommand()); + registerSubCommand(new InsertlineCommand()); + registerSubCommand(new ReadtextCommand()); + registerSubCommand(new ReadimageCommand()); + + registerSubCommand(new HelpCommand(this)); + } + + public void registerSubCommand(HologramSubCommand subCommand) { + subCommands.add(subCommand); + } + + public List getSubCommands() { + return new ArrayList(subCommands); + } + + @Override + public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { + + if (args.length == 0) { + sender.sendMessage(Colors.PRIMARY_SHADOW + "This server is running " + Colors.PRIMARY + "Holographic Displays " + Colors.PRIMARY_SHADOW + "v" + HolographicDisplays.getInstance().getDescription().getVersion() + " by " + Colors.PRIMARY + "filoghost"); + if (sender.hasPermission(Strings.BASE_PERM + "help")) { + sender.sendMessage(Colors.PRIMARY_SHADOW + "Commands: " + Colors.PRIMARY + "/" + label + " help"); + } + return true; + } + + for (HologramSubCommand subCommand : subCommands) { + if (subCommand.isValidTrigger(args[0])) { + + if (!subCommand.hasPermission(sender)) { + sender.sendMessage(Colors.ERROR + "You don't have permission."); + return true; + } + + if (args.length - 1 >= subCommand.getMinimumArguments()) { + try { + subCommand.execute(sender, label, Arrays.copyOfRange(args, 1, args.length)); + } catch (CommandException e) { + sender.sendMessage(Colors.ERROR + e.getMessage()); + } + } else { + sender.sendMessage(Colors.ERROR + "Usage: /" + label + " " + subCommand.getName() + " " + subCommand.getPossibleArguments()); + } + + return true; + } + } + + sender.sendMessage(Colors.ERROR + "Unknown sub-command. Type \"/" + label + " help\" for a list of commands."); + return true; + } +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/commands/main/subs/AddlineCommand.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/commands/main/subs/AddlineCommand.java new file mode 100644 index 00000000..c62ec87f --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/commands/main/subs/AddlineCommand.java @@ -0,0 +1,58 @@ +package com.gmail.filoghost.holographicdisplays.commands.main.subs; + +import java.util.Arrays; +import java.util.List; + +import org.bukkit.command.CommandSender; + +import com.gmail.filoghost.holographicdisplays.commands.Colors; +import com.gmail.filoghost.holographicdisplays.commands.CommandValidator; +import com.gmail.filoghost.holographicdisplays.commands.Strings; +import com.gmail.filoghost.holographicdisplays.commands.main.HologramSubCommand; +import com.gmail.filoghost.holographicdisplays.disk.HologramDatabase; +import com.gmail.filoghost.holographicdisplays.exception.CommandException; +import com.gmail.filoghost.holographicdisplays.object.NamedHologram; +import com.gmail.filoghost.holographicdisplays.object.NamedHologramManager; +import com.gmail.filoghost.holographicdisplays.util.Utils; + +public class AddlineCommand extends HologramSubCommand { + + public AddlineCommand() { + super("addline"); + setPermission(Strings.BASE_PERM + "addline"); + } + + @Override + public String getPossibleArguments() { + return " "; + } + + @Override + public int getMinimumArguments() { + return 2; + } + + @Override + public void execute(CommandSender sender, String label, String[] args) throws CommandException { + NamedHologram hologram = NamedHologramManager.getHologram(args[0].toLowerCase()); + CommandValidator.notNull(hologram, Strings.NO_SUCH_HOLOGRAM); + + hologram.getLinesUnsafe().add(HologramDatabase.readLineFromString(Utils.join(args, " ", 1, args.length), hologram)); + hologram.refreshAll(); + + HologramDatabase.saveHologram(hologram); + HologramDatabase.trySaveToDisk(); + sender.sendMessage(Colors.PRIMARY + "Line added!"); + } + + @Override + public List getTutorial() { + return Arrays.asList("Adds a line to an existing hologram."); + } + + @Override + public SubCommandType getType() { + return SubCommandType.EDIT_LINES; + } + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/commands/main/subs/AlignCommand.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/commands/main/subs/AlignCommand.java new file mode 100644 index 00000000..683a6130 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/commands/main/subs/AlignCommand.java @@ -0,0 +1,79 @@ +package com.gmail.filoghost.holographicdisplays.commands.main.subs; + +import java.util.Arrays; +import java.util.List; + +import org.bukkit.Location; +import org.bukkit.command.CommandSender; + +import com.gmail.filoghost.holographicdisplays.commands.Colors; +import com.gmail.filoghost.holographicdisplays.commands.CommandValidator; +import com.gmail.filoghost.holographicdisplays.commands.Strings; +import com.gmail.filoghost.holographicdisplays.commands.main.HologramSubCommand; +import com.gmail.filoghost.holographicdisplays.disk.HologramDatabase; +import com.gmail.filoghost.holographicdisplays.exception.CommandException; +import com.gmail.filoghost.holographicdisplays.object.NamedHologram; +import com.gmail.filoghost.holographicdisplays.object.NamedHologramManager; + +public class AlignCommand extends HologramSubCommand { + + public AlignCommand() { + super("align"); + setPermission(Strings.BASE_PERM + "align"); + } + + @Override + public String getPossibleArguments() { + return " "; + } + + @Override + public int getMinimumArguments() { + return 3; + } + + @Override + public void execute(CommandSender sender, String label, String[] args) throws CommandException { + NamedHologram hologram = NamedHologramManager.getHologram(args[1].toLowerCase()); + NamedHologram referenceHologram = NamedHologramManager.getHologram(args[2].toLowerCase()); + + CommandValidator.notNull(hologram, Strings.NO_SUCH_HOLOGRAM + " (hologram to align)"); + CommandValidator.notNull(referenceHologram, Strings.NO_SUCH_HOLOGRAM + " (reference hologram)"); + + CommandValidator.isTrue(hologram != referenceHologram, "The hologram must not be the same!"); + + Location loc = hologram.getLocation(); + + if (args[0].equalsIgnoreCase("x")) { + loc.setX(referenceHologram.getX()); + } else if (args[0].equalsIgnoreCase("y")) { + loc.setY(referenceHologram.getY()); + } else if (args[0].equalsIgnoreCase("z")) { + loc.setZ(referenceHologram.getZ()); + } else if (args[0].equalsIgnoreCase("xz")) { + loc.setX(referenceHologram.getX()); + loc.setZ(referenceHologram.getZ()); + } else { + throw new CommandException("You must specify either X, Y, Z or XZ, " + args[0] + " is not a valid axis."); + } + + hologram.teleport(loc.getWorld(), loc.getX(), loc.getY(), loc.getZ()); + hologram.despawnEntities(); + hologram.refreshAll(); + + HologramDatabase.saveHologram(hologram); + HologramDatabase.trySaveToDisk(); + sender.sendMessage(Colors.PRIMARY + "Hologram \"" + hologram.getName() + "\" aligned to the hologram \"" + referenceHologram.getName() + "\" on the " + args[0].toUpperCase() + " axis."); + } + + @Override + public List getTutorial() { + return Arrays.asList("Aligns the first hologram to the second, in the specified axis."); + } + + @Override + public SubCommandType getType() { + return SubCommandType.GENERIC; + } + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/commands/main/subs/CreateCommand.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/commands/main/subs/CreateCommand.java new file mode 100644 index 00000000..38be580c --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/commands/main/subs/CreateCommand.java @@ -0,0 +1,113 @@ +package com.gmail.filoghost.holographicdisplays.commands.main.subs; + +import java.util.Arrays; +import java.util.List; + +import org.bukkit.Location; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; + +import com.gmail.filoghost.holographicdisplays.commands.Colors; +import com.gmail.filoghost.holographicdisplays.commands.CommandValidator; +import com.gmail.filoghost.holographicdisplays.commands.Strings; +import com.gmail.filoghost.holographicdisplays.commands.main.HologramSubCommand; +import com.gmail.filoghost.holographicdisplays.disk.HologramDatabase; +import com.gmail.filoghost.holographicdisplays.exception.CommandException; +import com.gmail.filoghost.holographicdisplays.exception.InvalidCharactersException; +import com.gmail.filoghost.holographicdisplays.object.NamedHologram; +import com.gmail.filoghost.holographicdisplays.object.NamedHologramManager; +import com.gmail.filoghost.holographicdisplays.util.Utils; + +public class CreateCommand extends HologramSubCommand { + + private static final String VALID_HOLOGRAM_NAME_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"; + + + public CreateCommand() { + super("create"); + setPermission(Strings.BASE_PERM + "create"); + } + + @Override + public String getPossibleArguments() { + return " [text]"; + } + + @Override + public int getMinimumArguments() { + return 1; + } + + @SuppressWarnings("deprecation") + @Override + public void execute(CommandSender sender, String label, String[] args) throws CommandException { + Player player = CommandValidator.getPlayerSender(sender); + String name = args[0]; + + if (!name.matches("[a-zA-Z_\\-]+")) { + throw new CommandException("The name must contain only alphanumeric chars, underscores and hyphens."); + } + + name = name.toLowerCase(); + + CommandValidator.isTrue(!NamedHologramManager.isExistingHologram(name), "A hologram with that name already exists."); + + Location spawnLoc = player.getLocation(); + boolean moveUp = player.isOnGround(); + + if (moveUp) { + spawnLoc.add(0.0, 1.2, 0.0); + } + + NamedHologram hologram = new NamedHologram(spawnLoc, name); + NamedHologramManager.addHologram(hologram); + + if (args.length > 1) { + + String text = Utils.join(args, " ", 1, args.length); + CommandValidator.isTrue(!text.equalsIgnoreCase("{empty}"), "The first line should not be empty."); + + hologram.getLinesUnsafe().add(HologramDatabase.readLineFromString(text, hologram)); + player.sendMessage(Colors.SECONDARY_SHADOW + "(Change the lines with /" + label + " edit " + hologram.getName() + ")"); + } else { + hologram.appendTextLine("Default hologram. Change it with " + Colors.PRIMARY + "/" + label + " edit " + hologram.getName()); + } + + hologram.refreshAll(); + + HologramDatabase.saveHologram(hologram); + HologramDatabase.trySaveToDisk(); + Location look = player.getLocation(); + look.setPitch(90); + player.teleport(look, TeleportCause.PLUGIN); + player.sendMessage(Colors.PRIMARY + "You created a hologram named '" + hologram.getName() + "'."); + + if (moveUp) { + player.sendMessage(Colors.SECONDARY_SHADOW + "(You were on the ground, the hologram was automatically moved up. If you use /" + label + " movehere " + hologram.getName() + ", the hologram will be moved to your feet)"); + } + } + + public static String validateName(String name) throws InvalidCharactersException { + for (char c : name.toCharArray()) { + if (VALID_HOLOGRAM_NAME_CHARS.indexOf(c) < 0) { + throw new InvalidCharactersException(Character.toString(c)); + } + } + return name; + } + + @Override + public List getTutorial() { + return Arrays.asList( + "Creates a new hologram with the given name, that must", + "be alphanumeric. The name will be used as reference to", + "that hologram for editing commands."); + } + + @Override + public SubCommandType getType() { + return SubCommandType.GENERIC; + } + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/commands/main/subs/DeleteCommand.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/commands/main/subs/DeleteCommand.java new file mode 100644 index 00000000..d9fa43a7 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/commands/main/subs/DeleteCommand.java @@ -0,0 +1,59 @@ +package com.gmail.filoghost.holographicdisplays.commands.main.subs; + +import java.util.Arrays; +import java.util.List; + +import org.bukkit.command.CommandSender; + +import com.gmail.filoghost.holographicdisplays.commands.Colors; +import com.gmail.filoghost.holographicdisplays.commands.CommandValidator; +import com.gmail.filoghost.holographicdisplays.commands.Strings; +import com.gmail.filoghost.holographicdisplays.commands.main.HologramSubCommand; +import com.gmail.filoghost.holographicdisplays.disk.HologramDatabase; +import com.gmail.filoghost.holographicdisplays.exception.CommandException; +import com.gmail.filoghost.holographicdisplays.object.NamedHologram; +import com.gmail.filoghost.holographicdisplays.object.NamedHologramManager; + +public class DeleteCommand extends HologramSubCommand { + + public DeleteCommand() { + super("delete", "remove"); + setPermission(Strings.BASE_PERM + "delete"); + } + + @Override + public String getPossibleArguments() { + return ""; + } + + @Override + public int getMinimumArguments() { + return 1; + } + + + + @Override + public void execute(CommandSender sender, String label, String[] args) throws CommandException { + NamedHologram hologram = NamedHologramManager.getHologram(args[0].toLowerCase()); + CommandValidator.notNull(hologram, Strings.NO_SUCH_HOLOGRAM); + + hologram.delete(); + NamedHologramManager.removeHologram(hologram); + HologramDatabase.deleteHologram(hologram.getName()); + + HologramDatabase.trySaveToDisk(); + sender.sendMessage(Colors.PRIMARY + "You deleted the hologram '" + hologram.getName() + "'."); + } + + @Override + public List getTutorial() { + return Arrays.asList("Deletes a hologram. Cannot be undone."); + } + + @Override + public SubCommandType getType() { + return SubCommandType.GENERIC; + } + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/commands/main/subs/EditCommand.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/commands/main/subs/EditCommand.java new file mode 100644 index 00000000..3165d93b --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/commands/main/subs/EditCommand.java @@ -0,0 +1,90 @@ +package com.gmail.filoghost.holographicdisplays.commands.main.subs; + +import java.util.Arrays; +import java.util.List; + +import net.minecraft.util.com.google.common.base.Joiner; + +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import com.gmail.filoghost.holographicdisplays.HolographicDisplays; +import com.gmail.filoghost.holographicdisplays.commands.Colors; +import com.gmail.filoghost.holographicdisplays.commands.CommandValidator; +import com.gmail.filoghost.holographicdisplays.commands.Strings; +import com.gmail.filoghost.holographicdisplays.commands.main.HologramSubCommand; +import com.gmail.filoghost.holographicdisplays.commands.main.HologramsCommandHandler; +import com.gmail.filoghost.holographicdisplays.exception.CommandException; +import com.gmail.filoghost.holographicdisplays.object.NamedHologram; +import com.gmail.filoghost.holographicdisplays.object.NamedHologramManager; +import com.gmail.filoghost.holographicdisplays.util.Utils; + +public class EditCommand extends HologramSubCommand { + + private HologramsCommandHandler mainCommandHandler; + + public EditCommand(HologramsCommandHandler mainCommandHandler) { + super("edit"); + setPermission(Strings.BASE_PERM + "edit"); + this.mainCommandHandler = mainCommandHandler; + } + + @Override + public String getPossibleArguments() { + return ""; + } + + @Override + public int getMinimumArguments() { + return 1; + } + + + @Override + public void execute(CommandSender sender, String label, String[] args) throws CommandException { + String name = args[0].toLowerCase(); + NamedHologram hologram = NamedHologramManager.getHologram(name); + CommandValidator.notNull(hologram, Strings.NO_SUCH_HOLOGRAM); + + sender.sendMessage(""); + sender.sendMessage(Strings.formatTitle("How to edit the hologram '" + name + "'")); + for (HologramSubCommand subCommand : mainCommandHandler.getSubCommands()) { + if (subCommand.getType() == SubCommandType.EDIT_LINES) { + String usage = "/" + label + " " + subCommand.getName() + (subCommand.getPossibleArguments().length() > 0 ? " " + subCommand.getPossibleArguments().replace("", hologram.getName()).replace("", hologram.getName()) : ""); + + if (CommandValidator.isPlayerSender(sender)) { + + List help = Utils.newList(); + help.add(Colors.PRIMARY + usage); + for (String tutLine : subCommand.getTutorial()) { + help.add(Colors.SECONDARY_SHADOW + tutLine); + } + + HolographicDisplays.getNMSManager().newFancyMessage(usage) + .color(ChatColor.AQUA) + .suggest(usage) + .tooltip(Joiner.on('\n').join(help)) + .send((Player) sender); + } else { + sender.sendMessage(Colors.PRIMARY + usage); + } + } + } + + if (CommandValidator.isPlayerSender(sender) && HolographicDisplays.getNMSManager().hasChatHoverFeature()) { + HelpCommand.sendHoverTip(sender); + } + } + + @Override + public List getTutorial() { + return Arrays.asList("Shows the commands to manipulate an existing hologram."); + } + + @Override + public SubCommandType getType() { + return SubCommandType.GENERIC; + } + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/commands/main/subs/FixCommand.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/commands/main/subs/FixCommand.java new file mode 100644 index 00000000..1708be37 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/commands/main/subs/FixCommand.java @@ -0,0 +1,72 @@ +package com.gmail.filoghost.holographicdisplays.commands.main.subs; + +import java.util.Arrays; +import java.util.List; + +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.command.CommandSender; + +import com.gmail.filoghost.holographicdisplays.commands.Colors; +import com.gmail.filoghost.holographicdisplays.commands.CommandValidator; +import com.gmail.filoghost.holographicdisplays.commands.Strings; +import com.gmail.filoghost.holographicdisplays.commands.main.HologramSubCommand; +import com.gmail.filoghost.holographicdisplays.exception.CommandException; +import com.gmail.filoghost.holographicdisplays.object.NamedHologram; +import com.gmail.filoghost.holographicdisplays.object.NamedHologramManager; + +public class FixCommand extends HologramSubCommand { + + public FixCommand() { + super("fix"); + setPermission(Strings.BASE_PERM + "fix"); + } + + @Override + public String getPossibleArguments() { + return ""; + } + + @Override + public int getMinimumArguments() { + return 1; + } + + + @Override + public void execute(CommandSender sender, String label, String[] args) throws CommandException { + NamedHologram hologram = NamedHologramManager.getHologram(args[0].toLowerCase()); + CommandValidator.notNull(hologram, Strings.NO_SUCH_HOLOGRAM); + + if (args.length <= 1) { + sender.sendMessage(Colors.PRIMARY + "This command will put a glowstone 16 blocks above the hologram to fix the lightning."); + sender.sendMessage(Colors.PRIMARY + "If you're sure, type " + Colors.SECONDARY + "/" + label + " fix " + args[0].toLowerCase() + " confirm"); + return; + } + + if (args[1].equalsIgnoreCase("confirm")) { + + Block block = hologram.getWorld().getBlockAt((int) hologram.getX(), (int) hologram.getY() + 16, (int) hologram.getZ()); + String oldType = block.getType().toString().replace("_", " ").toLowerCase(); + block.setType(Material.GLOWSTONE); + + sender.sendMessage(Colors.PRIMARY + "Changed the block 16 block above the hologram (" + oldType + ") to glowstone!"); + + } else { + throw new CommandException(args[1] + " is not a valid confirmation! Use \"confirm\"."); + } + } + + @Override + public List getTutorial() { + return Arrays.asList("This command will fix the lightning of a hologram,", + "placing a glowstone block 16 blocks above it.", + "That's the only way to fix it (Only for 1.7 and lower)."); + } + + @Override + public SubCommandType getType() { + return SubCommandType.GENERIC; + } + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/commands/main/subs/HelpCommand.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/commands/main/subs/HelpCommand.java new file mode 100644 index 00000000..3ca38056 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/commands/main/subs/HelpCommand.java @@ -0,0 +1,98 @@ +package com.gmail.filoghost.holographicdisplays.commands.main.subs; + +import java.util.List; + +import net.minecraft.util.com.google.common.base.Joiner; + +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import com.gmail.filoghost.holographicdisplays.HolographicDisplays; +import com.gmail.filoghost.holographicdisplays.commands.Colors; +import com.gmail.filoghost.holographicdisplays.commands.CommandValidator; +import com.gmail.filoghost.holographicdisplays.commands.Strings; +import com.gmail.filoghost.holographicdisplays.commands.main.HologramSubCommand; +import com.gmail.filoghost.holographicdisplays.commands.main.HologramsCommandHandler; +import com.gmail.filoghost.holographicdisplays.exception.CommandException; +import com.gmail.filoghost.holographicdisplays.util.Utils; + +public class HelpCommand extends HologramSubCommand { + + private HologramsCommandHandler mainCommandHandler; + + public HelpCommand(HologramsCommandHandler mainCommandHandler) { + super("help"); + setPermission(Strings.BASE_PERM + "help"); + this.mainCommandHandler = mainCommandHandler; + } + + @Override + public String getPossibleArguments() { + return ""; + } + + @Override + public int getMinimumArguments() { + return 0; + } + + + @Override + public void execute(CommandSender sender, String label, String[] args) throws CommandException { + sender.sendMessage(""); + sender.sendMessage(Strings.formatTitle("Holographic Displays Commands")); + for (HologramSubCommand subCommand : mainCommandHandler.getSubCommands()) { + if (subCommand.getType() == SubCommandType.GENERIC) { + String usage = "/" + label + " " + subCommand.getName() + (subCommand.getPossibleArguments().length() > 0 ? " " + subCommand.getPossibleArguments() : ""); + + if (CommandValidator.isPlayerSender(sender)) { + + List help = Utils.newList(); + help.add(Colors.PRIMARY + usage); + for (String tutLine : subCommand.getTutorial()) { + help.add(Colors.SECONDARY_SHADOW + tutLine); + } + + HolographicDisplays.getNMSManager().newFancyMessage(usage) + .color(ChatColor.AQUA) + .suggest(usage) + .tooltip(Joiner.on('\n').join(help)) + .send((Player) sender); + + } else { + sender.sendMessage(Colors.PRIMARY + usage); + } + } + } + + if (CommandValidator.isPlayerSender(sender) && HolographicDisplays.getNMSManager().hasChatHoverFeature()) { + sendHoverTip(sender); + } + } + + public static void sendHoverTip(CommandSender sender) { + sender.sendMessage(""); + HolographicDisplays.getNMSManager().newFancyMessage("TIP").style(ChatColor.BOLD).color(ChatColor.YELLOW) + .then(" Try to ").color(ChatColor.GRAY) + .then("hover").color(ChatColor.WHITE).style(ChatColor.ITALIC, ChatColor.UNDERLINE) + .tooltip(ChatColor.LIGHT_PURPLE + "Hover on the commands to get info about them.") + .then(" or ").color(ChatColor.GRAY) + .then("click").color(ChatColor.WHITE).style(ChatColor.ITALIC, ChatColor.UNDERLINE) + .tooltip(ChatColor.LIGHT_PURPLE + "Click on the commands to insert them in the chat.") + .then(" on the commands!").color(ChatColor.GRAY) + .send((Player) sender); + } + + @Override + public List getTutorial() { + return null; + } + + @Override + public SubCommandType getType() { + return SubCommandType.HIDDEN; + } + + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/commands/main/subs/InsertlineCommand.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/commands/main/subs/InsertlineCommand.java new file mode 100644 index 00000000..e81e3230 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/commands/main/subs/InsertlineCommand.java @@ -0,0 +1,75 @@ +package com.gmail.filoghost.holographicdisplays.commands.main.subs; + +import java.util.Arrays; +import java.util.List; + +import org.bukkit.command.CommandSender; + +import com.gmail.filoghost.holographicdisplays.commands.Colors; +import com.gmail.filoghost.holographicdisplays.commands.CommandValidator; +import com.gmail.filoghost.holographicdisplays.commands.Strings; +import com.gmail.filoghost.holographicdisplays.commands.main.HologramSubCommand; +import com.gmail.filoghost.holographicdisplays.disk.HologramDatabase; +import com.gmail.filoghost.holographicdisplays.exception.CommandException; +import com.gmail.filoghost.holographicdisplays.object.NamedHologram; +import com.gmail.filoghost.holographicdisplays.object.NamedHologramManager; +import com.gmail.filoghost.holographicdisplays.util.Utils; + +public class InsertlineCommand extends HologramSubCommand { + + + public InsertlineCommand() { + super("insertline"); + setPermission(Strings.BASE_PERM + "insertline"); + } + + @Override + public String getPossibleArguments() { + return " "; + } + + @Override + public int getMinimumArguments() { + return 3; + } + + + @Override + public void execute(CommandSender sender, String label, String[] args) throws CommandException { + NamedHologram hologram = NamedHologramManager.getHologram(args[0].toLowerCase()); + CommandValidator.notNull(hologram, Strings.NO_SUCH_HOLOGRAM); + + int insertAfter = CommandValidator.getInteger(args[1]); + int oldLinesAmount = hologram.size(); + + CommandValidator.isTrue(insertAfter >= 0 && insertAfter <= oldLinesAmount, "The number must be between 0 and " + hologram.size() + "(amount of lines of the hologram)."); + + hologram.getLinesUnsafe().add(insertAfter, HologramDatabase.readLineFromString(Utils.join(args, " ", 2, args.length), hologram)); + hologram.refreshAll(); + + HologramDatabase.saveHologram(hologram); + HologramDatabase.trySaveToDisk(); + + if (insertAfter == 0) { + sender.sendMessage(Colors.PRIMARY + "Line inserted before line n.1!"); + } else if (insertAfter == oldLinesAmount) { + sender.sendMessage(Colors.PRIMARY + "Line appended at the end!"); + sender.sendMessage(Strings.TIP_PREFIX + "Next time use /" + label + " addline to add a line at the end."); + } else { + sender.sendMessage(Colors.PRIMARY + "Line inserted between lines " + insertAfter + " and " + (insertAfter + 1) + "!"); + } + } + + @Override + public List getTutorial() { + return Arrays.asList("Inserts a line after the specified index.", + "If the index is 0, the line will be put before", + "the first line of the hologram."); + } + + @Override + public SubCommandType getType() { + return SubCommandType.EDIT_LINES; + } + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/commands/main/subs/ListCommand.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/commands/main/subs/ListCommand.java new file mode 100644 index 00000000..f59d0722 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/commands/main/subs/ListCommand.java @@ -0,0 +1,81 @@ +package com.gmail.filoghost.holographicdisplays.commands.main.subs; + +import java.util.Arrays; +import java.util.List; + +import org.bukkit.command.CommandSender; + +import com.gmail.filoghost.holographicdisplays.commands.Colors; +import com.gmail.filoghost.holographicdisplays.commands.CommandValidator; +import com.gmail.filoghost.holographicdisplays.commands.Strings; +import com.gmail.filoghost.holographicdisplays.commands.main.HologramSubCommand; +import com.gmail.filoghost.holographicdisplays.exception.CommandException; +import com.gmail.filoghost.holographicdisplays.object.NamedHologram; +import com.gmail.filoghost.holographicdisplays.object.NamedHologramManager; + +public class ListCommand extends HologramSubCommand { + + private static final int HOLOGRAMS_PER_PAGE = 10; + + public ListCommand() { + super("list"); + setPermission(Strings.BASE_PERM + "list"); + } + + @Override + public String getPossibleArguments() { + return "[page]"; + } + + @Override + public int getMinimumArguments() { + return 0; + } + + @Override + public void execute(CommandSender sender, String label, String[] args) throws CommandException { + + int page = args.length > 0 ? CommandValidator.getInteger(args[0]) : 1; + + if (page < 1) { + throw new CommandException("Page number must be 1 or greater."); + } + + int totalPages = NamedHologramManager.size() / HOLOGRAMS_PER_PAGE; + if (NamedHologramManager.size() % HOLOGRAMS_PER_PAGE != 0) { + totalPages++; + } + + + if (NamedHologramManager.size() == 0) { + throw new CommandException("There are no holograms yet. Create one with /" + label + " create."); + } + + sender.sendMessage(""); + sender.sendMessage(Strings.formatTitle("Holograms list " + Colors.SECONDARY + "(Page " + page + " of " + totalPages + ")")); + int fromIndex = (page - 1) * HOLOGRAMS_PER_PAGE; + int toIndex = fromIndex + HOLOGRAMS_PER_PAGE; + + for (int i = fromIndex; i < toIndex; i++) { + if (i < NamedHologramManager.size()) { + NamedHologram hologram = NamedHologramManager.get(i); + sender.sendMessage(Colors.SECONDARY_SHADOW + "- " + Colors.SECONDARY + Colors.BOLD + hologram.getName() + " " + Colors.SECONDARY_SHADOW + "at x: " + (int) hologram.getX() + ", y: " + (int) hologram.getY() + ", z: " + (int) hologram.getZ() + " (lines: " + hologram.size() + ", world: \"" + hologram.getWorld().getName() + "\")"); + } + } + if (page < totalPages) { + sender.sendMessage(Strings.TIP_PREFIX + "See the next page with /" + label + " list " + (page + 1)); + } + + } + + @Override + public List getTutorial() { + return Arrays.asList("Lists all the existing holograms."); + } + + @Override + public SubCommandType getType() { + return SubCommandType.GENERIC; + } + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/commands/main/subs/MovehereCommand.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/commands/main/subs/MovehereCommand.java new file mode 100644 index 00000000..760c6b57 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/commands/main/subs/MovehereCommand.java @@ -0,0 +1,67 @@ +package com.gmail.filoghost.holographicdisplays.commands.main.subs; + +import java.util.Arrays; +import java.util.List; + +import org.bukkit.Location; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; + +import com.gmail.filoghost.holographicdisplays.commands.Colors; +import com.gmail.filoghost.holographicdisplays.commands.CommandValidator; +import com.gmail.filoghost.holographicdisplays.commands.Strings; +import com.gmail.filoghost.holographicdisplays.commands.main.HologramSubCommand; +import com.gmail.filoghost.holographicdisplays.disk.HologramDatabase; +import com.gmail.filoghost.holographicdisplays.exception.CommandException; +import com.gmail.filoghost.holographicdisplays.object.NamedHologram; +import com.gmail.filoghost.holographicdisplays.object.NamedHologramManager; + +public class MovehereCommand extends HologramSubCommand { + + + public MovehereCommand() { + super("movehere"); + setPermission(Strings.BASE_PERM + "movehere"); + } + + @Override + public String getPossibleArguments() { + return ""; + } + + @Override + public int getMinimumArguments() { + return 1; + } + + + @Override + public void execute(CommandSender sender, String label, String[] args) throws CommandException { + Player player = CommandValidator.getPlayerSender(sender); + NamedHologram hologram = NamedHologramManager.getHologram(args[0].toLowerCase()); + CommandValidator.notNull(hologram, Strings.NO_SUCH_HOLOGRAM); + + hologram.teleport(player.getWorld(), player.getLocation().getX(), player.getLocation().getY(), player.getLocation().getZ()); + hologram.despawnEntities(); + hologram.refreshAll(); + + HologramDatabase.saveHologram(hologram); + HologramDatabase.trySaveToDisk(); + Location to = player.getLocation(); + to.setPitch(90); + player.teleport(to, TeleportCause.PLUGIN); + player.sendMessage(Colors.PRIMARY + "You moved the hologram '" + hologram.getName() + "' near to you."); + } + + @Override + public List getTutorial() { + return Arrays.asList("Moves a hologram to your location."); + } + + @Override + public SubCommandType getType() { + return SubCommandType.GENERIC; + } + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/commands/main/subs/NearCommand.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/commands/main/subs/NearCommand.java new file mode 100644 index 00000000..8ec20801 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/commands/main/subs/NearCommand.java @@ -0,0 +1,70 @@ +package com.gmail.filoghost.holographicdisplays.commands.main.subs; + +import java.util.Arrays; +import java.util.List; + +import org.bukkit.World; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import com.gmail.filoghost.holographicdisplays.commands.Colors; +import com.gmail.filoghost.holographicdisplays.commands.CommandValidator; +import com.gmail.filoghost.holographicdisplays.commands.Strings; +import com.gmail.filoghost.holographicdisplays.commands.main.HologramSubCommand; +import com.gmail.filoghost.holographicdisplays.exception.CommandException; +import com.gmail.filoghost.holographicdisplays.object.NamedHologram; +import com.gmail.filoghost.holographicdisplays.object.NamedHologramManager; +import com.gmail.filoghost.holographicdisplays.util.Utils; + +public class NearCommand extends HologramSubCommand { + + public NearCommand() { + super("near"); + setPermission(Strings.BASE_PERM + "near"); + } + + @Override + public String getPossibleArguments() { + return ""; + } + + @Override + public int getMinimumArguments() { + return 1; + } + + @Override + public void execute(CommandSender sender, String label, String[] args) throws CommandException { + Player player = CommandValidator.getPlayerSender(sender); + int radius = CommandValidator.getInteger(args[0]); + CommandValidator.isTrue(radius > 0, "Radius must be at least 1."); + + World world = player.getWorld(); + int radiusSquared = radius * radius; + List nearHolograms = Utils.newList(); + + for (NamedHologram hologram : NamedHologramManager.getHolograms()) { + if (hologram.getLocation().getWorld().equals(world) && hologram.getLocation().distanceSquared(player.getLocation()) <= radiusSquared) { + nearHolograms.add(hologram); + } + } + + CommandValidator.isTrue(!nearHolograms.isEmpty(), "There are no holograms in the given radius."); + + player.sendMessage(Strings.formatTitle("Near holograms")); + for (NamedHologram nearHologram : nearHolograms) { + player.sendMessage(Colors.SECONDARY_SHADOW + "- " + Colors.SECONDARY + Colors.BOLD + nearHologram.getName() + " " + Colors.SECONDARY_SHADOW + "at x: " + (int) nearHologram.getX() + ", y: " + (int) nearHologram.getY() + ", z: " + (int) nearHologram.getZ() + " (lines: " + nearHologram.size() + ")"); + } + } + + @Override + public List getTutorial() { + return Arrays.asList("Get a list of near holograms."); + } + + @Override + public SubCommandType getType() { + return SubCommandType.GENERIC; + } + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/commands/main/subs/ReadimageCommand.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/commands/main/subs/ReadimageCommand.java new file mode 100644 index 00000000..b719aba3 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/commands/main/subs/ReadimageCommand.java @@ -0,0 +1,157 @@ +package com.gmail.filoghost.holographicdisplays.commands.main.subs; + +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Arrays; +import java.util.List; + +import net.minecraft.util.com.google.common.collect.Lists; + +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; + +import com.gmail.filoghost.holographicdisplays.HolographicDisplays; +import com.gmail.filoghost.holographicdisplays.commands.Colors; +import com.gmail.filoghost.holographicdisplays.commands.CommandValidator; +import com.gmail.filoghost.holographicdisplays.commands.Strings; +import com.gmail.filoghost.holographicdisplays.commands.main.HologramSubCommand; +import com.gmail.filoghost.holographicdisplays.disk.HologramDatabase; +import com.gmail.filoghost.holographicdisplays.exception.CommandException; +import com.gmail.filoghost.holographicdisplays.exception.TooWideException; +import com.gmail.filoghost.holographicdisplays.exception.UnreadableImageException; +import com.gmail.filoghost.holographicdisplays.image.ImageMessage; +import com.gmail.filoghost.holographicdisplays.object.NamedHologram; +import com.gmail.filoghost.holographicdisplays.object.NamedHologramManager; +import com.gmail.filoghost.holographicdisplays.util.FileUtils; + +public class ReadimageCommand extends HologramSubCommand { + + + public ReadimageCommand() { + super("readimage", "image"); + setPermission(Strings.BASE_PERM + "readimage"); + } + + @Override + public String getPossibleArguments() { + return " "; + } + + @Override + public int getMinimumArguments() { + return 3; + } + + + @Override + public void execute(CommandSender sender, String label, String[] args) throws CommandException { + + boolean append = false; + + List newArgs = Lists.newArrayList(); + + for (int i = 0; i < args.length; i++) { + if (args[i].equalsIgnoreCase("-a") || args[i].equalsIgnoreCase("-append")) { + append = true; + } else { + newArgs.add(args[i]); + } + } + + args = newArgs.toArray(new String[0]); + + NamedHologram hologram = NamedHologramManager.getHologram(args[0].toLowerCase()); + CommandValidator.notNull(hologram, Strings.NO_SUCH_HOLOGRAM); + + int width = CommandValidator.getInteger(args[2]); + + CommandValidator.isTrue(width >= 2, "The width of the image must be 2 or greater."); + + boolean isUrl = false; + + try { + + String fileName = args[1]; + BufferedImage image = null; + + if (fileName.startsWith("http://") || fileName.startsWith("https://")) { + isUrl = true; + image = FileUtils.readImage(new URL(fileName)); + } else { + + if (fileName.matches(".*[a-zA-Z0-9\\-]+\\.[a-zA-Z0-9\\-]{1,4}\\/.+")) { + Strings.sendWarning(sender, "The image path seems to be an URL. If so, please use http:// or https:// in the path."); + } + + image = FileUtils.readImage(new File(HolographicDisplays.getInstance().getDataFolder(), fileName)); + } + + if (!append) { + hologram.clearLines(); + } + + ImageMessage imageMessage = new ImageMessage(image, width); + String[] newLines = imageMessage.getLines(); + for (int i = 0; i < newLines.length; i++) { + hologram.appendTextLine(newLines[i]); + } + + hologram.refreshAll(); + + if (newLines.length < 5) { + sender.sendMessage(Strings.TIP_PREFIX + "The image has a very low height. You can increase it by increasing the width, it will scale automatically."); + } + + HologramDatabase.saveHologram(hologram); + HologramDatabase.trySaveToDisk(); + + if (append) { + sender.sendMessage(Colors.PRIMARY + "The image was appended int the end of the hologram!"); + } else { + sender.sendMessage(Colors.PRIMARY + "The image was drawn in the hologram!"); + } + + } catch (MalformedURLException e) { + throw new CommandException("The provided URL was not valid."); + } catch (FileNotFoundException e) { + throw new CommandException("The image '" + args[1] + "' doesn't exist in the plugin's folder."); + } catch (TooWideException e) { + throw new CommandException("The image is too large. Max width allowed is " + ImageMessage.MAX_WIDTH + " pixels."); + } catch (UnreadableImageException e) { + throw new CommandException("The plugin was unable to read the image. Be sure that the format is supported."); + } catch (IOException e) { + throw new CommandException("I/O exception while reading the image." + (isUrl ? "Is the URL valid?" : "Is it in use?")); + } catch (Exception e) { + e.printStackTrace(); + throw new CommandException("Unhandled exception while reading the image! Please look the console."); + } + } + + @Override + public List getTutorial() { + return Arrays.asList("Reads an image from a file. Tutorial:", + "1) Move the image in the plugin's folder", + "2) Do not use spaces in the name", + "3) Do /holograms read ", + "4) Choose to automatically resize the image", + "5) (Optional) Use the flag '-a' if you only want to append", + " the image to the hologram without clearing the lines", + "", + "Example: you have an image named 'logo.png', you want to append", + "it to the lines of the hologram named 'test', with a width of", + "50 pixels. In this case you would execute the following command:", + ChatColor.YELLOW + "/holograms readimage test logo.png 50 -a", + "", + "The symbols used to create the image are taken from the config.yml."); + } + + @Override + public SubCommandType getType() { + return SubCommandType.EDIT_LINES; + } + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/commands/main/subs/ReadtextCommand.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/commands/main/subs/ReadtextCommand.java new file mode 100644 index 00000000..77be37e8 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/commands/main/subs/ReadtextCommand.java @@ -0,0 +1,104 @@ +package com.gmail.filoghost.holographicdisplays.commands.main.subs; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.Arrays; +import java.util.List; + +import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; + +import com.gmail.filoghost.holographicdisplays.HolographicDisplays; +import com.gmail.filoghost.holographicdisplays.commands.Colors; +import com.gmail.filoghost.holographicdisplays.commands.CommandValidator; +import com.gmail.filoghost.holographicdisplays.commands.Strings; +import com.gmail.filoghost.holographicdisplays.commands.main.HologramSubCommand; +import com.gmail.filoghost.holographicdisplays.disk.HologramDatabase; +import com.gmail.filoghost.holographicdisplays.disk.StringConverter; +import com.gmail.filoghost.holographicdisplays.exception.CommandException; +import com.gmail.filoghost.holographicdisplays.object.NamedHologram; +import com.gmail.filoghost.holographicdisplays.object.NamedHologramManager; +import com.gmail.filoghost.holographicdisplays.util.FileUtils; + +public class ReadtextCommand extends HologramSubCommand { + + public ReadtextCommand() { + super("readtext", "readlines"); + setPermission(Strings.BASE_PERM + "readtext"); + } + + @Override + public String getPossibleArguments() { + return " "; + } + + @Override + public int getMinimumArguments() { + return 2; + } + + @Override + public void execute(CommandSender sender, String label, String[] args) throws CommandException { + NamedHologram hologram = NamedHologramManager.getHologram(args[0].toLowerCase()); + CommandValidator.notNull(hologram, Strings.NO_SUCH_HOLOGRAM); + + try { + List lines = FileUtils.readLines(new File(HolographicDisplays.getInstance().getDataFolder(), args[1])); + hologram.clearLines(); + + int linesAmount = lines.size(); + if (linesAmount > 40) { + Strings.sendWarning(sender, "The file contained more than 40 lines, that have been limited."); + linesAmount = 40; + } + + for (int i = 0; i < linesAmount; i++) { + hologram.appendTextLine(StringConverter.toReadableFormat(lines.get(i))); + } + + hologram.refreshAll(); + + HologramDatabase.saveHologram(hologram); + HologramDatabase.trySaveToDisk(); + + if (args[1].contains(".")) { + if (isImageExtension(args[1].substring(args[1].lastIndexOf('.') + 1))) { + Strings.sendWarning(sender, "The read file has an image's extension. If it is an image, you should use /" + label + " readimage."); + } + } + + sender.sendMessage(Colors.PRIMARY + "The lines were pasted into the hologram!"); + + } catch (FileNotFoundException e) { + throw new CommandException("A file named '" + args[1] + "' doesn't exist in the plugin's folder."); + } catch (IOException e) { + throw new CommandException("I/O exception while reading the file. Is it in use?"); + } catch (Exception e) { + e.printStackTrace(); + throw new CommandException("Unhandled exception while reading the file! Please look the console."); + } + } + + @Override + public List getTutorial() { + return Arrays.asList("Reads the lines from a text file. Tutorial:", + "1) Create a new text file in the plugin's folder", + "2) Do not use spaces in the name", + "3) Each line will be a line in the hologram", + "4) Do /holograms readlines ", + "", + "Example: you have a file named 'info.txt', and you want", + "to paste it in the hologram named 'test'. In this case you", + "would execute "+ ChatColor.YELLOW + "/holograms readlines test info.txt"); + } + + @Override + public SubCommandType getType() { + return SubCommandType.EDIT_LINES; + } + + private boolean isImageExtension(String input) { + return Arrays.asList("jpg", "png", "jpeg", "gif").contains(input.toLowerCase()); + } +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/commands/main/subs/ReloadCommand.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/commands/main/subs/ReloadCommand.java new file mode 100644 index 00000000..222a2a5b --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/commands/main/subs/ReloadCommand.java @@ -0,0 +1,99 @@ +package com.gmail.filoghost.holographicdisplays.commands.main.subs; + +import java.util.Arrays; +import java.util.List; +import java.util.Set; + +import org.bukkit.command.CommandSender; + +import com.gmail.filoghost.holographicdisplays.HolographicDisplays; +import com.gmail.filoghost.holographicdisplays.bridge.bungeecord.BungeeServerTracker; +import com.gmail.filoghost.holographicdisplays.commands.Colors; +import com.gmail.filoghost.holographicdisplays.commands.Strings; +import com.gmail.filoghost.holographicdisplays.commands.main.HologramSubCommand; +import com.gmail.filoghost.holographicdisplays.disk.Configuration; +import com.gmail.filoghost.holographicdisplays.disk.HologramDatabase; +import com.gmail.filoghost.holographicdisplays.disk.UnicodeSymbols; +import com.gmail.filoghost.holographicdisplays.exception.CommandException; +import com.gmail.filoghost.holographicdisplays.exception.HologramNotFoundException; +import com.gmail.filoghost.holographicdisplays.exception.InvalidFormatException; +import com.gmail.filoghost.holographicdisplays.exception.WorldNotFoundException; +import com.gmail.filoghost.holographicdisplays.object.CraftHologram; +import com.gmail.filoghost.holographicdisplays.object.NamedHologram; +import com.gmail.filoghost.holographicdisplays.object.NamedHologramManager; +import com.gmail.filoghost.holographicdisplays.placeholder.AnimationsRegister; +import com.gmail.filoghost.holographicdisplays.placeholder.PlaceholdersManager; + +public class ReloadCommand extends HologramSubCommand { + + public ReloadCommand() { + super("reload"); + setPermission(Strings.BASE_PERM + "reload"); + } + + @Override + public String getPossibleArguments() { + return ""; + } + + @Override + public int getMinimumArguments() { + return 0; + } + + @Override + public void execute(CommandSender sender, String label, String[] args) throws CommandException { + try { + + long startMillis = System.currentTimeMillis(); + + Configuration.load(HolographicDisplays.getInstance()); + BungeeServerTracker.startTask(Configuration.bungeeRefreshSeconds); + UnicodeSymbols.load(HolographicDisplays.getInstance()); + HologramDatabase.loadYamlFile(HolographicDisplays.getInstance()); + AnimationsRegister.loadAnimations(HolographicDisplays.getInstance()); + PlaceholdersManager.untrackAll(); + + NamedHologramManager.clearAll(); + + Set savedHolograms = HologramDatabase.getHolograms(); + if (savedHolograms != null && savedHolograms.size() > 0) { + for (String singleSavedHologram : savedHolograms) { + try { + NamedHologram singleHologramEntity = HologramDatabase.loadHologram(singleSavedHologram); + NamedHologramManager.addHologram(singleHologramEntity); + } catch (HologramNotFoundException e) { + Strings.sendWarning(sender, "Hologram '" + singleSavedHologram + "' not found, skipping it."); + } catch (InvalidFormatException e) { + Strings.sendWarning(sender, "Hologram '" + singleSavedHologram + "' has an invalid location format."); + } catch (WorldNotFoundException e) { + Strings.sendWarning(sender, "Hologram '" + singleSavedHologram + "' was in the world '" + e.getMessage() + "' but it wasn't loaded."); + } + } + } + + for (CraftHologram hologram : NamedHologramManager.getHolograms()) { + hologram.refreshAll(); + } + + long endMillis = System.currentTimeMillis(); + + sender.sendMessage(Colors.PRIMARY + "Configuration reloaded successfully in " + (endMillis - startMillis) + "ms!"); + + } catch (Exception ex) { + ex.printStackTrace(); + throw new CommandException("Exception while reloading the configuration. Please look the console."); + } + } + + @Override + public List getTutorial() { + return Arrays.asList("Reloads the holograms from the database."); + } + + @Override + public SubCommandType getType() { + return SubCommandType.GENERIC; + } + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/commands/main/subs/RemovelineCommand.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/commands/main/subs/RemovelineCommand.java new file mode 100644 index 00000000..cc0c18d6 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/commands/main/subs/RemovelineCommand.java @@ -0,0 +1,65 @@ +package com.gmail.filoghost.holographicdisplays.commands.main.subs; + +import java.util.Arrays; +import java.util.List; + +import org.bukkit.command.CommandSender; + +import com.gmail.filoghost.holographicdisplays.commands.Colors; +import com.gmail.filoghost.holographicdisplays.commands.CommandValidator; +import com.gmail.filoghost.holographicdisplays.commands.Strings; +import com.gmail.filoghost.holographicdisplays.commands.main.HologramSubCommand; +import com.gmail.filoghost.holographicdisplays.disk.HologramDatabase; +import com.gmail.filoghost.holographicdisplays.exception.CommandException; +import com.gmail.filoghost.holographicdisplays.object.NamedHologram; +import com.gmail.filoghost.holographicdisplays.object.NamedHologramManager; + +public class RemovelineCommand extends HologramSubCommand { + + public RemovelineCommand() { + super("removeline"); + setPermission(Strings.BASE_PERM + "removeline"); + } + + @Override + public String getPossibleArguments() { + return " "; + } + + @Override + public int getMinimumArguments() { + return 2; + } + + + @Override + public void execute(CommandSender sender, String label, String[] args) throws CommandException { + NamedHologram hologram = NamedHologramManager.getHologram(args[0].toLowerCase()); + CommandValidator.notNull(hologram, Strings.NO_SUCH_HOLOGRAM); + + int lineNumber = CommandValidator.getInteger(args[1]); + + CommandValidator.isTrue(lineNumber >= 1 && lineNumber <= hologram.size(), "The line number must be between 1 and " + hologram.size() + "."); + int index = lineNumber - 1; + + CommandValidator.isTrue(hologram.size() > 1, "The hologram should have at least 1 line. If you want to delete it, use /" + label + " delete."); + + hologram.removeLine(index); + hologram.refreshAll(); + + HologramDatabase.saveHologram(hologram); + HologramDatabase.trySaveToDisk(); + sender.sendMessage(Colors.PRIMARY + "Line " + lineNumber + " removed!"); + } + + @Override + public List getTutorial() { + return Arrays.asList("Removes a line from a hologram."); + } + + @Override + public SubCommandType getType() { + return SubCommandType.EDIT_LINES; + } + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/commands/main/subs/SetlineCommand.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/commands/main/subs/SetlineCommand.java new file mode 100644 index 00000000..e95d1526 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/commands/main/subs/SetlineCommand.java @@ -0,0 +1,66 @@ +package com.gmail.filoghost.holographicdisplays.commands.main.subs; + +import java.util.Arrays; +import java.util.List; + +import org.bukkit.command.CommandSender; + +import com.gmail.filoghost.holographicdisplays.commands.Colors; +import com.gmail.filoghost.holographicdisplays.commands.CommandValidator; +import com.gmail.filoghost.holographicdisplays.commands.Strings; +import com.gmail.filoghost.holographicdisplays.commands.main.HologramSubCommand; +import com.gmail.filoghost.holographicdisplays.disk.HologramDatabase; +import com.gmail.filoghost.holographicdisplays.exception.CommandException; +import com.gmail.filoghost.holographicdisplays.object.NamedHologram; +import com.gmail.filoghost.holographicdisplays.object.NamedHologramManager; +import com.gmail.filoghost.holographicdisplays.util.Utils; + +public class SetlineCommand extends HologramSubCommand { + + public SetlineCommand() { + super("setline"); + setPermission(Strings.BASE_PERM + "setline"); + } + + @Override + public String getPossibleArguments() { + return " "; + } + + @Override + public int getMinimumArguments() { + return 3; + } + + + @Override + public void execute(CommandSender sender, String label, String[] args) throws CommandException { + NamedHologram hologram = NamedHologramManager.getHologram(args[0].toLowerCase()); + CommandValidator.notNull(hologram, Strings.NO_SUCH_HOLOGRAM); + + + int lineNumber = CommandValidator.getInteger(args[1]); + CommandValidator.isTrue(lineNumber >= 1 && lineNumber <= hologram.size(), "The line number must be between 1 and " + hologram.size() + "."); + int index = lineNumber - 1; + + hologram.getLinesUnsafe().get(index).despawn(); + hologram.getLinesUnsafe().set(index, HologramDatabase.readLineFromString(Utils.join(args, " ", 2, args.length), hologram)); + hologram.refreshAll(); + + HologramDatabase.saveHologram(hologram); + HologramDatabase.trySaveToDisk(); + sender.sendMessage(Colors.PRIMARY + "Line " + lineNumber + " changed!"); + + } + + @Override + public List getTutorial() { + return Arrays.asList("Changes a line of a hologram."); + } + + @Override + public SubCommandType getType() { + return SubCommandType.EDIT_LINES; + } + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/commands/main/subs/TeleportCommand.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/commands/main/subs/TeleportCommand.java new file mode 100644 index 00000000..d544c6ef --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/commands/main/subs/TeleportCommand.java @@ -0,0 +1,60 @@ +package com.gmail.filoghost.holographicdisplays.commands.main.subs; + +import java.util.Arrays; +import java.util.List; + +import org.bukkit.Location; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; + +import com.gmail.filoghost.holographicdisplays.commands.Colors; +import com.gmail.filoghost.holographicdisplays.commands.CommandValidator; +import com.gmail.filoghost.holographicdisplays.commands.Strings; +import com.gmail.filoghost.holographicdisplays.commands.main.HologramSubCommand; +import com.gmail.filoghost.holographicdisplays.exception.CommandException; +import com.gmail.filoghost.holographicdisplays.object.NamedHologram; +import com.gmail.filoghost.holographicdisplays.object.NamedHologramManager; + + +public class TeleportCommand extends HologramSubCommand { + + public TeleportCommand() { + super("teleport", "tp"); + setPermission(Strings.BASE_PERM + "teleport"); + } + + @Override + public String getPossibleArguments() { + return ""; + } + + @Override + public int getMinimumArguments() { + return 1; + } + + @Override + public void execute(CommandSender sender, String label, String[] args) throws CommandException { + Player player = CommandValidator.getPlayerSender(sender); + NamedHologram hologram = NamedHologramManager.getHologram(args[0].toLowerCase()); + CommandValidator.notNull(hologram, Strings.NO_SUCH_HOLOGRAM); + + Location loc = hologram.getLocation(); + loc.setPitch(90); + player.teleport(loc, TeleportCause.PLUGIN); + player.sendMessage(Colors.PRIMARY + "You were teleported to the hologram named '" + hologram.getName() + "'."); + + } + + @Override + public List getTutorial() { + return Arrays.asList("Teleports you to the given hologram."); + } + + @Override + public SubCommandType getType() { + return SubCommandType.GENERIC; + } + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/disk/ConfigNode.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/disk/ConfigNode.java new file mode 100644 index 00000000..c2889620 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/disk/ConfigNode.java @@ -0,0 +1,32 @@ +package com.gmail.filoghost.holographicdisplays.disk; + +public enum ConfigNode { + + SPACE_BETWEEN_LINES("space-between-lines", 0.02), + IMAGES_SYMBOL("images.symbol", "[x]"), + TRANSPARENCY_SPACE("images.transparency.space", " [|] "), + TRANSPARENCY_COLOR("images.transparency.color", "&7"), + UPDATE_NOTIFICATION("update-notification", true), + BUNGEE_REFRESH_SECONDS("bungee.refresh-seconds", 3), + BUNGEE_USE_REDIS_BUNGEE("bungee.use-RedisBungee", false), + TIME_FORMAT("time.format", "H:mm"), + TIME_ZONE("time.zone", "GMT+1"), + DEBUG("debug", false); + + private final String path; + private final Object value; + + private ConfigNode(String path, Object defaultValue) { + this.path = path; + value = defaultValue; + } + + public String getPath() { + return path; + } + + public Object getDefaultValue() { + return value; + } + +} \ No newline at end of file diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/disk/Configuration.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/disk/Configuration.java new file mode 100644 index 00000000..fbcd216e --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/disk/Configuration.java @@ -0,0 +1,149 @@ +package com.gmail.filoghost.holographicdisplays.disk; + +import java.io.File; +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.List; +import java.util.TimeZone; + +import org.bukkit.ChatColor; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.plugin.Plugin; + +import com.google.common.base.Joiner; + +/** + * Just a bunch of static varibles to hold the settings. + * Useful for fast access. + */ +public class Configuration { + + public static double spaceBetweenLines; + public static String imageSymbol; + public static String transparencySymbol; + public static boolean updateNotification; + public static ChatColor transparencyColor; + + public static SimpleDateFormat timeFormat; + + public static int bungeeRefreshSeconds; + public static String bungeeOnlineFormat; + public static String bungeeOfflineFormat; + public static boolean useRedisBungee; + + public static boolean debug; + + + public static void load(Plugin plugin) { + File configFile = new File(plugin.getDataFolder(), "config.yml"); + if (!configFile.exists()) { + plugin.getDataFolder().mkdirs(); + plugin.saveResource("config.yml", true); + } + + YamlConfiguration config = new YamlConfiguration(); + try { + config.load(configFile); + } catch (InvalidConfigurationException e) { + e.printStackTrace(); + plugin.getLogger().warning("The configuration is not a valid YAML file! Please check it with a tool like http://yaml-online-parser.appspot.com/"); + return; + } catch (IOException e) { + e.printStackTrace(); + plugin.getLogger().warning("I/O error while reading the configuration. Was the file in use?"); + return; + } catch (Exception e) { + e.printStackTrace(); + plugin.getLogger().warning("Unhandled exception while reading the configuration!"); + return; + } + + boolean needsSave = false; + + for (ConfigNode node : ConfigNode.values()) { + if (!config.isSet(node.getPath())) { + needsSave = true; + config.set(node.getPath(), node.getDefaultValue()); + } + } + + // Check the old values. + List nodesToRemove = Arrays.asList( + "vertical-spacing", + "time-format", + "bungee-refresh-seconds", + "using-RedisBungee", + "bungee-online-format", + "bungee-offline-format" + ); + + for (String oldNode : nodesToRemove) { + if (config.isSet(oldNode)) { + config.set(oldNode, null); + needsSave = true; + } + } + + + + if (needsSave) { + config.options().header(Joiner.on('\n').join( + ".", + ". Read the tutorial at: http://dev.bukkit.org/bukkit-plugins/holographic-displays/", + ".", + ". Plugin created by filoghost.", + "." + )); + config.options().copyHeader(true); + try { + config.save(configFile); + } catch (IOException e) { + e.printStackTrace(); + plugin.getLogger().warning("I/O error while saving the configuration. Was the file in use?"); + } + } + + spaceBetweenLines = config.getDouble(ConfigNode.SPACE_BETWEEN_LINES.getPath()); + updateNotification = config.getBoolean(ConfigNode.UPDATE_NOTIFICATION.getPath()); + + imageSymbol = StringConverter.toReadableFormat(config.getString(ConfigNode.IMAGES_SYMBOL.getPath())); + transparencySymbol = StringConverter.toReadableFormat(config.getString(ConfigNode.TRANSPARENCY_SPACE.getPath())); + bungeeRefreshSeconds = config.getInt(ConfigNode.BUNGEE_REFRESH_SECONDS.getPath()); + useRedisBungee = config.getBoolean(ConfigNode.BUNGEE_USE_REDIS_BUNGEE.getPath()); + + debug = config.getBoolean(ConfigNode.DEBUG.getPath()); + + String tempColor = config.getString(ConfigNode.TRANSPARENCY_COLOR.getPath()).replace('&', ChatColor.COLOR_CHAR); + boolean foundColor = false; + for (ChatColor chatColor : ChatColor.values()) { + if (chatColor.toString().equals(tempColor)) { + Configuration.transparencyColor = chatColor; + foundColor = true; + } + } + if (!foundColor) { + Configuration.transparencyColor = ChatColor.GRAY; + plugin.getLogger().warning("You didn't set a valid chat color for transparency in the configuration, light gray (&7) will be used."); + } + + try { + timeFormat = new SimpleDateFormat(config.getString(ConfigNode.TIME_FORMAT.getPath())); + timeFormat.setTimeZone(TimeZone.getTimeZone(config.getString(ConfigNode.TIME_ZONE.getPath()))); + } catch (IllegalArgumentException ex) { + timeFormat = new SimpleDateFormat("H:mm"); + plugin.getLogger().warning("Time format not valid in the configuration, using the default."); + } + + if (bungeeRefreshSeconds < 1) { + plugin.getLogger().warning("The minimum interval for pinging BungeeCord's servers is 1 second. It has been automatically set."); + bungeeRefreshSeconds = 1; + } + + if (bungeeRefreshSeconds > 60) { + plugin.getLogger().warning("The maximum interval for pinging BungeeCord's servers is 60 seconds. It has been automatically set."); + bungeeRefreshSeconds = 60; + } + } +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/disk/HologramDatabase.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/disk/HologramDatabase.java new file mode 100644 index 00000000..ffa273cf --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/disk/HologramDatabase.java @@ -0,0 +1,146 @@ +package com.gmail.filoghost.holographicdisplays.disk; + +import java.io.File; +import java.io.IOException; +import java.util.List; +import java.util.Set; + +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.inventory.ItemStack; +import org.bukkit.plugin.Plugin; + +import com.gmail.filoghost.holographicdisplays.HolographicDisplays; +import com.gmail.filoghost.holographicdisplays.exception.HologramNotFoundException; +import com.gmail.filoghost.holographicdisplays.exception.InvalidFormatException; +import com.gmail.filoghost.holographicdisplays.exception.WorldNotFoundException; +import com.gmail.filoghost.holographicdisplays.object.CraftHologram; +import com.gmail.filoghost.holographicdisplays.object.NamedHologram; +import com.gmail.filoghost.holographicdisplays.object.line.CraftHologramLine; +import com.gmail.filoghost.holographicdisplays.object.line.CraftItemLine; +import com.gmail.filoghost.holographicdisplays.object.line.CraftTextLine; +import com.gmail.filoghost.holographicdisplays.util.ItemUtils; +import com.gmail.filoghost.holographicdisplays.util.Utils; + +public class HologramDatabase { + + private static File file; + private static FileConfiguration config; + + public static void loadYamlFile(Plugin plugin) { + file = new File(plugin.getDataFolder(), "database.yml"); + + if (!file.exists()) { + plugin.getDataFolder().mkdirs(); + plugin.saveResource("database.yml", true); + } + + config = YamlConfiguration.loadConfiguration(file); + } + + public static NamedHologram loadHologram(String name) throws HologramNotFoundException, InvalidFormatException, WorldNotFoundException { + + ConfigurationSection configSection = config.getConfigurationSection(name); + + if (configSection == null) { + throw new HologramNotFoundException(); + } + + List lines = configSection.getStringList("lines"); + String locationString = configSection.getString("location"); + + if (lines == null || locationString == null || lines.size() == 0) { + throw new HologramNotFoundException(); + } + + Location loc = LocationSerializer.locationFromString(locationString); + + NamedHologram hologram = new NamedHologram(loc, name); + for (int i = 0; i < lines.size(); i++) { + hologram.getLinesUnsafe().add(readLineFromString(lines.get(i), hologram)); + } + + return hologram; + } + + public static CraftHologramLine readLineFromString(String rawText, CraftHologram hologram) { + if (rawText.toLowerCase().startsWith("icon:")) { + String iconMaterial = ItemUtils.stripSpacingChars(rawText.substring("icon:".length(), rawText.length())); + + short dataValue = 0; + + if (iconMaterial.contains(":")) { + try { + dataValue = (short) Integer.parseInt(iconMaterial.split(":")[1]); + } catch (NumberFormatException e) { } + iconMaterial = iconMaterial.split(":")[0]; + } + + Material mat = ItemUtils.matchMaterial(iconMaterial); + if (mat == null) { + mat = Material.BEDROCK; + } + + return new CraftItemLine(hologram, new ItemStack(mat, 1, dataValue)); + + } else { + + if (rawText.trim().equalsIgnoreCase("{empty}")) { + return new CraftTextLine(hologram, ""); + } else { + return new CraftTextLine(hologram, StringConverter.toReadableFormat(rawText)); + } + } + } + + public static void deleteHologram(String name) { + config.set(name, null); + } + + public static void saveHologram(NamedHologram hologram) { + + ConfigurationSection configSection = config.isConfigurationSection(hologram.getName()) ? config.getConfigurationSection(hologram.getName()) : config.createSection(hologram.getName()); + + configSection.set("location", LocationSerializer.locationToString(hologram.getLocation())); + List lines = Utils.newList(); + + for (CraftHologramLine line : hologram.getLinesUnsafe()) { + + if (line instanceof CraftTextLine) { + lines.add(StringConverter.toSaveableFormat(((CraftTextLine) line).getText())); + + } else if (line instanceof CraftItemLine) { + CraftItemLine itemLine = (CraftItemLine) line; + lines.add("ICON: " + itemLine.getItemStack().getType().toString().replace("_", " ").toLowerCase() + (itemLine.getItemStack().getDurability() != 0 ? String.valueOf(itemLine.getItemStack().getDurability()) : "")); + } + } + + configSection.set("lines", lines); + } + + public static Set getHolograms() { + return config.getKeys(false); + } + + public static boolean isExistingHologram(String name) { + return config.isConfigurationSection(name); + } + + public static void saveToDisk() throws IOException { + if (config != null && file != null) { + config.save(file); + } + } + + public static void trySaveToDisk() { + try { + saveToDisk(); + } catch (IOException ex) { + ex.printStackTrace(); + HolographicDisplays.getInstance().getLogger().severe("Unable to save database.yml to disk!"); + } + } +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/disk/LocationSerializer.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/disk/LocationSerializer.java new file mode 100644 index 00000000..4bd1dc56 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/disk/LocationSerializer.java @@ -0,0 +1,55 @@ +package com.gmail.filoghost.holographicdisplays.disk; + +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; + +import com.gmail.filoghost.holographicdisplays.exception.InvalidFormatException; +import com.gmail.filoghost.holographicdisplays.exception.WorldNotFoundException; + +public class LocationSerializer { + + private static DecimalFormat decimalFormat; + static { + // More precision is not needed at all. + decimalFormat = new DecimalFormat("0.0000"); + DecimalFormatSymbols formatSymbols = decimalFormat.getDecimalFormatSymbols(); + formatSymbols.setDecimalSeparator('.'); + decimalFormat.setDecimalFormatSymbols(formatSymbols); + } + + public static Location locationFromString(String input) throws WorldNotFoundException, InvalidFormatException { + if (input == null) { + throw new InvalidFormatException(); + } + + String[] parts = input.replace(" ", "").split(","); + + if (parts.length != 4) { + throw new InvalidFormatException(); + } + + try { + double x = Double.parseDouble(parts[1]); + double y = Double.parseDouble(parts[2]); + double z = Double.parseDouble(parts[3]); + + World world = Bukkit.getWorld(parts[0]); + if (world == null) { + throw new WorldNotFoundException(parts[0]); + } + + return new Location(world, x, y, z); + + } catch (NumberFormatException ex) { + throw new InvalidFormatException(); + } + } + + public static String locationToString(Location loc) { + return (loc.getWorld().getName() + ", " + decimalFormat.format(loc.getX()) + ", " + decimalFormat.format(loc.getY()) + ", " + decimalFormat.format(loc.getZ())); + } +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/disk/StringConverter.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/disk/StringConverter.java new file mode 100644 index 00000000..9768b841 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/disk/StringConverter.java @@ -0,0 +1,28 @@ +package com.gmail.filoghost.holographicdisplays.disk; + +import org.bukkit.ChatColor; + +public class StringConverter { + + public static String toReadableFormat(String input) { + if (input == null) { + return null; + } + + input = UnicodeSymbols.placeholdersToSymbols(input); + input = ChatColor.translateAlternateColorCodes('&', input); + return input; + } + + + public static String toSaveableFormat(String input) { + if (input == null) { + return null; + } + + input = UnicodeSymbols.symbolsToPlaceholders(input); + input = input.replace("§", "&"); + return input; + } + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/disk/UnicodeSymbols.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/disk/UnicodeSymbols.java new file mode 100644 index 00000000..23c15d2c --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/disk/UnicodeSymbols.java @@ -0,0 +1,102 @@ +package com.gmail.filoghost.holographicdisplays.disk; + +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.apache.commons.lang.StringEscapeUtils; +import org.bukkit.plugin.Plugin; + +import com.gmail.filoghost.holographicdisplays.util.FileUtils; + +public class UnicodeSymbols { + + private static Map placeholders = new HashMap(); + + public static void load(Plugin plugin) { + placeholders.clear(); + + File file = new File(plugin.getDataFolder(), "symbols.yml"); + + if (!file.exists()) { + plugin.getDataFolder().mkdirs(); + plugin.saveResource("symbols.yml", true); + } + + List lines; + try { + lines = FileUtils.readLines(file); + } catch (IOException e) { + e.printStackTrace(); + plugin.getLogger().warning("I/O error while reading symbols.yml. Was the file in use?"); + return; + } catch (Exception e) { + e.printStackTrace(); + plugin.getLogger().warning("Unhandled exception while reading symbols.yml!"); + return; + } + + for (String line : lines) { + + // Comment or empty line. + if (line.length() == 0 || line.startsWith("#")) { + continue; + } + + if (!line.contains(":")) { + plugin.getLogger().warning("Unable to parse a line(" + line + ") from symbols.yml: it must contain ':' to separate the placeholder and the replacement."); + continue; + } + + int indexOf = line.indexOf(':'); + String placeholder = unquote(line.substring(0, indexOf).trim()); + String replacement = StringEscapeUtils.unescapeJava(unquote(line.substring(indexOf + 1, line.length()).trim())); + + if (placeholder.isEmpty() || replacement.isEmpty()) { + plugin.getLogger().warning("Unable to parse a line(" + line + ") from symbols.yml: the placeholder and the replacement must have both at least 1 character."); + continue; + } + + if (placeholder.length() > 30) { + plugin.getLogger().warning("Unable to parse a line(" + line + ") from symbols.yml: the placeholder cannot be longer than 30 characters."); + continue; + } + + placeholders.put(placeholder, replacement); + } + } + + + protected static String placeholdersToSymbols(String input) { + for (Entry entry : placeholders.entrySet()) { + input = input.replace(entry.getKey(), entry.getValue()); + } + return input; + } + + + protected static String symbolsToPlaceholders(String input) { + for (Entry entry : placeholders.entrySet()) { + input = input.replace(entry.getValue(), entry.getKey()); + } + return input; + } + + + private static String unquote(String input) { + if (input.length() < 2) { + // Cannot be quoted. + return input; + } + if (input.startsWith("'") && input.endsWith("'")) { + return input.substring(1, input.length() - 1); + } else if (input.startsWith("\"") && input.endsWith("\"")) { + return input.substring(1, input.length() - 1); + } + + return input; + } +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/exception/CommandException.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/exception/CommandException.java new file mode 100644 index 00000000..5f979176 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/exception/CommandException.java @@ -0,0 +1,10 @@ +package com.gmail.filoghost.holographicdisplays.exception; + +public class CommandException extends Exception { + + private static final long serialVersionUID = 1L; + + public CommandException(String message) { + super(message); + } +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/exception/HologramNotFoundException.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/exception/HologramNotFoundException.java new file mode 100644 index 00000000..b02d243d --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/exception/HologramNotFoundException.java @@ -0,0 +1,7 @@ +package com.gmail.filoghost.holographicdisplays.exception; + +public class HologramNotFoundException extends Exception { + + private static final long serialVersionUID = 1L; + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/exception/InvalidCharactersException.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/exception/InvalidCharactersException.java new file mode 100644 index 00000000..bf10a0a2 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/exception/InvalidCharactersException.java @@ -0,0 +1,10 @@ +package com.gmail.filoghost.holographicdisplays.exception; + +public class InvalidCharactersException extends Exception { + + private static final long serialVersionUID = 1L; + + public InvalidCharactersException(String message) { + super(message); + } +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/exception/InvalidFormatException.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/exception/InvalidFormatException.java new file mode 100644 index 00000000..5e74e279 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/exception/InvalidFormatException.java @@ -0,0 +1,7 @@ +package com.gmail.filoghost.holographicdisplays.exception; + +public class InvalidFormatException extends Exception { + + private static final long serialVersionUID = 1L; + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/exception/InvalidMaterialException.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/exception/InvalidMaterialException.java new file mode 100644 index 00000000..a8fb2104 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/exception/InvalidMaterialException.java @@ -0,0 +1,11 @@ +package com.gmail.filoghost.holographicdisplays.exception; + +public class InvalidMaterialException extends Exception { + + private static final long serialVersionUID = 1L; + + public InvalidMaterialException(String message) { + super(message); + } + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/exception/SpawnFailedException.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/exception/SpawnFailedException.java new file mode 100644 index 00000000..36785875 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/exception/SpawnFailedException.java @@ -0,0 +1,7 @@ +package com.gmail.filoghost.holographicdisplays.exception; + +public class SpawnFailedException extends Exception { + + private static final long serialVersionUID = 1L; + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/exception/TooWideException.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/exception/TooWideException.java new file mode 100644 index 00000000..d6945136 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/exception/TooWideException.java @@ -0,0 +1,17 @@ +package com.gmail.filoghost.holographicdisplays.exception; + +public class TooWideException extends Exception { + + private static final long serialVersionUID = 1L; + + private int width; + + public TooWideException(int width) { + this.width = width; + } + + public int getWidth() { + return width; + } + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/exception/UnreadableImageException.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/exception/UnreadableImageException.java new file mode 100644 index 00000000..44363dd4 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/exception/UnreadableImageException.java @@ -0,0 +1,7 @@ +package com.gmail.filoghost.holographicdisplays.exception; + +public class UnreadableImageException extends Exception { + + private static final long serialVersionUID = 1L; + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/exception/WorldNotFoundException.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/exception/WorldNotFoundException.java new file mode 100644 index 00000000..40fddd11 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/exception/WorldNotFoundException.java @@ -0,0 +1,11 @@ +package com.gmail.filoghost.holographicdisplays.exception; + +public class WorldNotFoundException extends Exception { + + private static final long serialVersionUID = 1L; + + public WorldNotFoundException(String message) { + super(message); + } + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/image/ImageMessage.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/image/ImageMessage.java new file mode 100644 index 00000000..fc017236 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/image/ImageMessage.java @@ -0,0 +1,207 @@ +package com.gmail.filoghost.holographicdisplays.image; + +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.image.BufferedImage; +import java.util.Map; +import java.util.Map.Entry; + +import org.bukkit.ChatColor; + +import com.gmail.filoghost.holographicdisplays.disk.Configuration; +import com.gmail.filoghost.holographicdisplays.exception.TooWideException; +import com.gmail.filoghost.holographicdisplays.util.Utils; + +/** + * Huge thanks to bobacadodl for this awesome library! + * Bukkit thread: https://forums.bukkit.org/threads/lib-imagemessage-v2-1-send-images-to-players-via-the-chat.204902 + */ +public class ImageMessage { + + public static final int MAX_WIDTH = 150; + + private static final Map colorsMap = Utils.newMap(); + + private static final Map graysMap = Utils.newMap(); + + + static { + colorsMap.put(ChatColor.DARK_BLUE, new Color(0, 0, 170)); + colorsMap.put(ChatColor.DARK_GREEN, new Color(0, 170, 0)); + colorsMap.put(ChatColor.DARK_AQUA, new Color(0, 170, 170)); + colorsMap.put(ChatColor.DARK_RED, new Color(170, 0, 0)); + colorsMap.put(ChatColor.DARK_PURPLE, new Color(170, 0, 170)); + colorsMap.put(ChatColor.GOLD, new Color(255, 170, 0)); + colorsMap.put(ChatColor.BLUE, new Color(85, 85, 255)); + colorsMap.put(ChatColor.GREEN, new Color(85, 255, 85)); + colorsMap.put(ChatColor.AQUA, new Color(85, 255, 255)); + colorsMap.put(ChatColor.RED, new Color(255, 85, 85)); + colorsMap.put(ChatColor.LIGHT_PURPLE, new Color(255, 85, 255)); + colorsMap.put(ChatColor.YELLOW, new Color(255, 255, 85)); + + graysMap.put(ChatColor.BLACK, new Color(0, 0, 0)); + graysMap.put(ChatColor.DARK_GRAY, new Color(85, 85, 85)); + graysMap.put(ChatColor.GRAY, new Color(170, 170, 170)); + graysMap.put(ChatColor.WHITE, new Color(255, 255, 255)); + } + + + private String[] lines; + + public ImageMessage(BufferedImage image, int width) throws TooWideException { + ChatColor[][] chatColors = toChatColorArray(image, width); + lines = toImgMessage(chatColors); + } + + private ChatColor[][] toChatColorArray(BufferedImage image, int width) throws TooWideException { + double ratio = (double) image.getHeight() / image.getWidth(); + int height = (int) (((double)width) * ratio); + if (height == 0) { + height = 1; + } + + if (width > MAX_WIDTH) { + throw new TooWideException(width); + } + + BufferedImage resized = resizeImage(image, width, height); + + ChatColor[][] chatImg = new ChatColor[resized.getWidth()][resized.getHeight()]; + for (int x = 0; x < resized.getWidth(); x++) { + for (int y = 0; y < resized.getHeight(); y++) { + int rgb = resized.getRGB(x, y); + chatImg[x][y] = getClosestChatColor(new Color(rgb, true)); + } + } + return chatImg; + } + + private String[] toImgMessage(ChatColor[][] colors) { + + String[] lines = new String[colors[0].length]; + ChatColor transparencyColor = Configuration.transparencyColor; + String transparencySymbol = Configuration.transparencySymbol; + String imageSymbol = Configuration.imageSymbol; + + for (int y = 0; y < colors[0].length; y++) { + + StringBuffer line = new StringBuffer(); + + ChatColor previous = ChatColor.RESET; + + for (int x = 0; x < colors.length; x++) { + + ChatColor currentColor = colors[x][y]; + + if (currentColor == null) { + + // Use the trasparent char + if (previous != transparencyColor) { + + // Change the previous chat color and append the newer + line.append(transparencyColor); + previous = transparencyColor; + + } + line.append(transparencySymbol); + + } else { + + if (previous != currentColor) { + line.append(currentColor.toString()); + previous = currentColor; + } + + line.append(imageSymbol); + } + } + + lines[y] = line.toString(); + } + + return lines; + } + + private BufferedImage resizeImage(BufferedImage originalImage, int width, int height) { + return toBufferedImage(originalImage.getScaledInstance(width, height, Image.SCALE_DEFAULT)); + } + + private BufferedImage toBufferedImage(Image img) { + + // Creates a buffered image with transparency. + BufferedImage bimage = new BufferedImage(img.getWidth(null), img.getHeight(null), BufferedImage.TYPE_INT_ARGB); + + // Draws the image on to the buffered image. + Graphics2D graphics = bimage.createGraphics(); + graphics.drawImage(img, 0, 0, null); + graphics.dispose(); + + // Returns the buffered image. + return bimage; + } + + private double getDistance(Color c1, Color c2) { + double rmean = (c1.getRed() + c2.getRed()) / 2.0; + double r = c1.getRed() - c2.getRed(); + double g = c1.getGreen() - c2.getGreen(); + int b = c1.getBlue() - c2.getBlue(); + double weightR = 2 + rmean / 256.0; + double weightG = 4.0; + double weightB = 2 + (255 - rmean) / 256.0; + return weightR * r * r + weightG * g * g + weightB * b * b; + } + + private boolean areIdentical(Color c1, Color c2) { + return Math.abs(c1.getRed() - c2.getRed()) <= 5 && + Math.abs(c1.getGreen() - c2.getGreen()) <= 5 && + Math.abs(c1.getBlue() - c2.getBlue()) <= 5; + + } + + private ChatColor getClosestChatColor(Color color) { + if (color.getAlpha() < 80) return null; + + for (Entry entry : colorsMap.entrySet()) { + if (areIdentical(entry.getValue(), color)) { + return entry.getKey(); + } + } + + double bestGrayDistance = -1; + ChatColor bestGrayMatch = null; + + for (Entry entry : graysMap.entrySet()) { + double distance = getDistance(color, entry.getValue()); + + if (distance < bestGrayDistance || bestGrayDistance == -1) { + bestGrayDistance = distance; + bestGrayMatch = entry.getKey(); + } + } + + if (bestGrayDistance < 17500) { + return bestGrayMatch; + } + + double bestColorDistance = -1; + ChatColor bestColorMatch = null; + + for (Entry entry : colorsMap.entrySet()) { + double distance = getDistance(color, entry.getValue()); + + if (distance < bestColorDistance || bestColorDistance == -1) { + bestColorDistance = distance; + bestColorMatch = entry.getKey(); + } + } + + // Minecraft has 15 colors + return bestColorMatch; + } + + + public String[] getLines() { + return lines; + } +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/listener/MainListener.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/listener/MainListener.java new file mode 100644 index 00000000..8cf78979 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/listener/MainListener.java @@ -0,0 +1,150 @@ +package com.gmail.filoghost.holographicdisplays.listener; + +import java.util.Map; +import java.util.logging.Level; + +import org.bukkit.Chunk; +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.CreatureSpawnEvent; +import org.bukkit.event.entity.ItemSpawnEvent; +import org.bukkit.event.entity.ProjectileLaunchEvent; +import org.bukkit.event.player.PlayerInteractEntityEvent; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.event.world.ChunkLoadEvent; +import org.bukkit.event.world.ChunkUnloadEvent; +import org.bukkit.plugin.Plugin; + +import com.gmail.filoghost.holographicdisplays.HolographicDisplays; +import com.gmail.filoghost.holographicdisplays.api.handler.PickupHandler; +import com.gmail.filoghost.holographicdisplays.commands.Colors; +import com.gmail.filoghost.holographicdisplays.commands.Strings; +import com.gmail.filoghost.holographicdisplays.disk.Configuration; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.NMSManager; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSEntityBase; +import com.gmail.filoghost.holographicdisplays.object.CraftHologram; +import com.gmail.filoghost.holographicdisplays.object.NamedHologramManager; +import com.gmail.filoghost.holographicdisplays.object.PluginHologram; +import com.gmail.filoghost.holographicdisplays.object.PluginHologramManager; +import com.gmail.filoghost.holographicdisplays.object.line.CraftTouchSlimeLine; +import com.gmail.filoghost.holographicdisplays.util.Utils; + +public class MainListener implements Listener { + + private NMSManager nmsManager; + + private Map anticlickSpam = Utils.newMap(); + + public MainListener(NMSManager nmsManager) { + this.nmsManager = nmsManager; + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onChunkUnload(ChunkUnloadEvent event) { + for (Entity entity : event.getChunk().getEntities()) { + if (!entity.isDead()) { + NMSEntityBase entityBase = nmsManager.getNMSEntityBase(entity); + + if (entityBase != null) { + entityBase.getHologramLine().getParent().despawnEntities(); + } + } + } + } + + @EventHandler (priority = EventPriority.MONITOR) + public void onChunkLoad(ChunkLoadEvent event) { + Chunk chunk = event.getChunk(); + NamedHologramManager.onChunkLoad(chunk); + PluginHologramManager.onChunkLoad(chunk); + } + + @EventHandler (priority = EventPriority.MONITOR, ignoreCancelled = false) + public void onCreatureSpawn(CreatureSpawnEvent event) { + if (nmsManager.isNMSEntityBase(event.getEntity())) { + if (event.isCancelled()) { + event.setCancelled(false); + } + } + } + + @EventHandler (priority = EventPriority.MONITOR, ignoreCancelled = false) + public void onProjectileLaunch(ProjectileLaunchEvent event) { + if (nmsManager.isNMSEntityBase(event.getEntity())) { + if (event.isCancelled()) { + event.setCancelled(false); + } + } + } + + @EventHandler (priority = EventPriority.MONITOR, ignoreCancelled = false) + public void onItemSpawn(ItemSpawnEvent event) { + if (nmsManager.isNMSEntityBase(event.getEntity())) { + if (event.isCancelled()) { + event.setCancelled(false); + } + } + } + + @EventHandler (priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onSlimeInteract(PlayerInteractEntityEvent event) { + if (event.getRightClicked().getType() == EntityType.SLIME) { + + NMSEntityBase entityBase = nmsManager.getNMSEntityBase(event.getRightClicked()); + if (entityBase == null) return; + + if (entityBase.getHologramLine() instanceof CraftTouchSlimeLine) { + + CraftTouchSlimeLine touchSlime = (CraftTouchSlimeLine) entityBase.getHologramLine(); + + if (touchSlime.getTouchablePiece().getTouchHandler() != null && touchSlime.getParent().getVisibilityManager().isVisibleTo(event.getPlayer())) { + + Long lastClick = anticlickSpam.get(event.getPlayer()); + if (lastClick != null && System.currentTimeMillis() - lastClick.longValue() < 100) { + return; + } + + anticlickSpam.put(event.getPlayer(), System.currentTimeMillis()); + + try { + touchSlime.getTouchablePiece().getTouchHandler().onTouch(event.getPlayer()); + } catch (Exception ex) { + Plugin plugin = touchSlime.getParent() instanceof PluginHologram ? ((PluginHologram) touchSlime.getParent()).getOwner() : HolographicDisplays.getInstance(); + HolographicDisplays.getInstance().getLogger().log(Level.WARNING, "The plugin " + plugin.getName() + " generated an exception when the player " + event.getPlayer().getName() + " touched a hologram.", ex); + } + } + } + } + } + + public static void handleItemLinePickup(Player player, PickupHandler pickupHandler, CraftHologram hologram) { + try { + if (hologram.getVisibilityManager().isVisibleTo(player)) { + pickupHandler.onPickup(player); + } + } catch (Exception ex) { + Plugin plugin = hologram instanceof PluginHologram ? ((PluginHologram) hologram).getOwner() : HolographicDisplays.getInstance(); + HolographicDisplays.getInstance().getLogger().log(Level.WARNING, "The plugin " + plugin.getName() + " generated an exception when the player " + player.getName() + " picked up an item from a hologram.", ex); + } + } + + @EventHandler + public void onJoin(PlayerJoinEvent event) { + if (Configuration.updateNotification && HolographicDisplays.getNewVersion() != null) { + if (event.getPlayer().hasPermission(Strings.BASE_PERM + "update")) { + event.getPlayer().sendMessage(Colors.PRIMARY_SHADOW + "[HolographicDisplays] " + Colors.PRIMARY + "Found an update: " + HolographicDisplays.getNewVersion() + ". Download:"); + event.getPlayer().sendMessage(Colors.PRIMARY_SHADOW + ">> " + Colors.PRIMARY + "http://dev.bukkit.org/bukkit-plugins/holographic-displays"); + } + } + } + + @EventHandler + public void onLeave(PlayerQuitEvent event) { + anticlickSpam.remove(event.getPlayer()); + } +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/metrics/MetricsLite.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/metrics/MetricsLite.java new file mode 100644 index 00000000..4a6b7727 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/metrics/MetricsLite.java @@ -0,0 +1,514 @@ +/* + * Copyright 2011-2013 Tyler Blair. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and contributors and should not be interpreted as representing official policies, + * either expressed or implied, of anybody else. + */ + +package com.gmail.filoghost.holographicdisplays.metrics; + +import org.bukkit.Bukkit; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.PluginDescriptionFile; +import org.bukkit.scheduler.BukkitTask; + +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.net.Proxy; +import java.net.URL; +import java.net.URLConnection; +import java.net.URLEncoder; +import java.util.UUID; +import java.util.logging.Level; +import java.util.zip.GZIPOutputStream; + +public class MetricsLite { + + /** + * The current revision number + */ + private final static int REVISION = 7; + + /** + * The base url of the metrics domain + */ + private static final String BASE_URL = "http://report.mcstats.org"; + + /** + * The url used to report a server's status + */ + private static final String REPORT_URL = "/plugin/%s"; + + /** + * Interval of time to ping (in minutes) + */ + private final static int PING_INTERVAL = 15; + + /** + * The plugin this metrics submits for + */ + private final Plugin plugin; + + /** + * The plugin configuration file + */ + private final YamlConfiguration configuration; + + /** + * The plugin configuration file + */ + private final File configurationFile; + + /** + * Unique server id + */ + private final String guid; + + /** + * Debug mode + */ + private final boolean debug; + + /** + * Lock for synchronization + */ + private final Object optOutLock = new Object(); + + /** + * Id of the scheduled task + */ + private volatile BukkitTask task = null; + + public MetricsLite(Plugin plugin) throws IOException { + if (plugin == null) { + throw new IllegalArgumentException("Plugin cannot be null"); + } + + this.plugin = plugin; + + // load the config + configurationFile = getConfigFile(); + configuration = YamlConfiguration.loadConfiguration(configurationFile); + + // add some defaults + configuration.addDefault("opt-out", false); + configuration.addDefault("guid", UUID.randomUUID().toString()); + configuration.addDefault("debug", false); + + // Do we need to create the file? + if (configuration.get("guid", null) == null) { + configuration.options().header("http://mcstats.org").copyDefaults(true); + configuration.save(configurationFile); + } + + // Load the guid then + guid = configuration.getString("guid"); + debug = configuration.getBoolean("debug", false); + } + + /** + * Start measuring statistics. This will immediately create an async repeating task as the plugin and send + * the initial data to the metrics backend, and then after that it will post in increments of + * PING_INTERVAL * 1200 ticks. + * + * @return True if statistics measuring is running, otherwise false. + */ + public boolean start() { + synchronized (optOutLock) { + // Did we opt out? + if (isOptOut()) { + return false; + } + + // Is metrics already running? + if (task != null) { + return true; + } + + // Begin hitting the server with glorious data + task = plugin.getServer().getScheduler().runTaskTimerAsynchronously(plugin, new Runnable() { + + private boolean firstPost = true; + + public void run() { + try { + // This has to be synchronized or it can collide with the disable method. + synchronized (optOutLock) { + // Disable Task, if it is running and the server owner decided to opt-out + if (isOptOut() && task != null) { + task.cancel(); + task = null; + } + } + + // We use the inverse of firstPost because if it is the first time we are posting, + // it is not a interval ping, so it evaluates to FALSE + // Each time thereafter it will evaluate to TRUE, i.e PING! + postPlugin(!firstPost); + + // After the first post we set firstPost to false + // Each post thereafter will be a ping + firstPost = false; + } catch (IOException e) { + if (debug) { + Bukkit.getLogger().log(Level.INFO, "[Metrics] " + e.getMessage()); + } + } + } + }, 0, PING_INTERVAL * 1200); + + return true; + } + } + + /** + * Has the server owner denied plugin metrics? + * + * @return true if metrics should be opted out of it + */ + public boolean isOptOut() { + synchronized (optOutLock) { + try { + // Reload the metrics file + configuration.load(getConfigFile()); + } catch (IOException ex) { + if (debug) { + Bukkit.getLogger().log(Level.INFO, "[Metrics] " + ex.getMessage()); + } + return true; + } catch (InvalidConfigurationException ex) { + if (debug) { + Bukkit.getLogger().log(Level.INFO, "[Metrics] " + ex.getMessage()); + } + return true; + } + return configuration.getBoolean("opt-out", false); + } + } + + /** + * Enables metrics for the server by setting "opt-out" to false in the config file and starting the metrics task. + * + * @throws java.io.IOException + */ + public void enable() throws IOException { + // This has to be synchronized or it can collide with the check in the task. + synchronized (optOutLock) { + // Check if the server owner has already set opt-out, if not, set it. + if (isOptOut()) { + configuration.set("opt-out", false); + configuration.save(configurationFile); + } + + // Enable Task, if it is not running + if (task == null) { + start(); + } + } + } + + /** + * Disables metrics for the server by setting "opt-out" to true in the config file and canceling the metrics task. + * + * @throws java.io.IOException + */ + public void disable() throws IOException { + // This has to be synchronized or it can collide with the check in the task. + synchronized (optOutLock) { + // Check if the server owner has already set opt-out, if not, set it. + if (!isOptOut()) { + configuration.set("opt-out", true); + configuration.save(configurationFile); + } + + // Disable Task, if it is running + if (task != null) { + task.cancel(); + task = null; + } + } + } + + /** + * Gets the File object of the config file that should be used to store data such as the GUID and opt-out status + * + * @return the File object for the config file + */ + public File getConfigFile() { + // I believe the easiest way to get the base folder (e.g craftbukkit set via -P) for plugins to use + // is to abuse the plugin object we already have + // plugin.getDataFolder() => base/plugins/PluginA/ + // pluginsFolder => base/plugins/ + // The base is not necessarily relative to the startup directory. + File pluginsFolder = plugin.getDataFolder().getParentFile(); + + // return => base/plugins/PluginMetrics/config.yml + return new File(new File(pluginsFolder, "PluginMetrics"), "config.yml"); + } + + /** + * Generic method that posts a plugin to the metrics website + */ + @SuppressWarnings("deprecation") + private void postPlugin(boolean isPing) throws IOException { + // Server software specific section + PluginDescriptionFile description = plugin.getDescription(); + String pluginName = description.getName(); + boolean onlineMode = Bukkit.getServer().getOnlineMode(); // TRUE if online mode is enabled + String pluginVersion = description.getVersion(); + String serverVersion = Bukkit.getVersion(); + int playersOnline = Bukkit.getServer().getOnlinePlayers().length; + + // END server software specific section -- all code below does not use any code outside of this class / Java + + // Construct the post data + StringBuilder json = new StringBuilder(1024); + json.append('{'); + + // The plugin's description file containg all of the plugin data such as name, version, author, etc + appendJSONPair(json, "guid", guid); + appendJSONPair(json, "plugin_version", pluginVersion); + appendJSONPair(json, "server_version", serverVersion); + appendJSONPair(json, "players_online", Integer.toString(playersOnline)); + + // New data as of R6 + String osname = System.getProperty("os.name"); + String osarch = System.getProperty("os.arch"); + String osversion = System.getProperty("os.version"); + String java_version = System.getProperty("java.version"); + int coreCount = Runtime.getRuntime().availableProcessors(); + + // normalize os arch .. amd64 -> x86_64 + if (osarch.equals("amd64")) { + osarch = "x86_64"; + } + + appendJSONPair(json, "osname", osname); + appendJSONPair(json, "osarch", osarch); + appendJSONPair(json, "osversion", osversion); + appendJSONPair(json, "cores", Integer.toString(coreCount)); + appendJSONPair(json, "auth_mode", onlineMode ? "1" : "0"); + appendJSONPair(json, "java_version", java_version); + + // If we're pinging, append it + if (isPing) { + appendJSONPair(json, "ping", "1"); + } + + // close json + json.append('}'); + + // Create the url + URL url = new URL(BASE_URL + String.format(REPORT_URL, urlEncode(pluginName))); + + // Connect to the website + URLConnection connection; + + // Mineshafter creates a socks proxy, so we can safely bypass it + // It does not reroute POST requests so we need to go around it + if (isMineshafterPresent()) { + connection = url.openConnection(Proxy.NO_PROXY); + } else { + connection = url.openConnection(); + } + + + byte[] uncompressed = json.toString().getBytes(); + byte[] compressed = gzip(json.toString()); + + // Headers + connection.addRequestProperty("User-Agent", "MCStats/" + REVISION); + connection.addRequestProperty("Content-Type", "application/json"); + connection.addRequestProperty("Content-Encoding", "gzip"); + connection.addRequestProperty("Content-Length", Integer.toString(compressed.length)); + connection.addRequestProperty("Accept", "application/json"); + connection.addRequestProperty("Connection", "close"); + + connection.setDoOutput(true); + + if (debug) { + System.out.println("[Metrics] Prepared request for " + pluginName + " uncompressed=" + uncompressed.length + " compressed=" + compressed.length); + } + + // Write the data + OutputStream os = connection.getOutputStream(); + os.write(compressed); + os.flush(); + + // Now read the response + final BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream())); + String response = reader.readLine(); + + // close resources + os.close(); + reader.close(); + + if (response == null || response.startsWith("ERR") || response.startsWith("7")) { + if (response == null) { + response = "null"; + } else if (response.startsWith("7")) { + response = response.substring(response.startsWith("7,") ? 2 : 1); + } + + throw new IOException(response); + } + } + + /** + * GZip compress a string of bytes + * + * @param input + * @return + */ + public static byte[] gzip(String input) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + GZIPOutputStream gzos = null; + + try { + gzos = new GZIPOutputStream(baos); + gzos.write(input.getBytes("UTF-8")); + } catch (IOException e) { + e.printStackTrace(); + } finally { + if (gzos != null) try { + gzos.close(); + } catch (IOException ignore) { + } + } + + return baos.toByteArray(); + } + + /** + * Check if mineshafter is present. If it is, we need to bypass it to send POST requests + * + * @return true if mineshafter is installed on the server + */ + private boolean isMineshafterPresent() { + try { + Class.forName("mineshafter.MineServer"); + return true; + } catch (Exception e) { + return false; + } + } + + /** + * Appends a json encoded key/value pair to the given string builder. + * + * @param json + * @param key + * @param value + * @throws UnsupportedEncodingException + */ + private static void appendJSONPair(StringBuilder json, String key, String value) throws UnsupportedEncodingException { + boolean isValueNumeric = false; + + try { + if (value.equals("0") || !value.endsWith("0")) { + Double.parseDouble(value); + isValueNumeric = true; + } + } catch (NumberFormatException e) { + isValueNumeric = false; + } + + if (json.charAt(json.length() - 1) != '{') { + json.append(','); + } + + json.append(escapeJSON(key)); + json.append(':'); + + if (isValueNumeric) { + json.append(value); + } else { + json.append(escapeJSON(value)); + } + } + + /** + * Escape a string to create a valid JSON string + * + * @param text + * @return + */ + private static String escapeJSON(String text) { + StringBuilder builder = new StringBuilder(); + + builder.append('"'); + for (int index = 0; index < text.length(); index++) { + char chr = text.charAt(index); + + switch (chr) { + case '"': + case '\\': + builder.append('\\'); + builder.append(chr); + break; + case '\b': + builder.append("\\b"); + break; + case '\t': + builder.append("\\t"); + break; + case '\n': + builder.append("\\n"); + break; + case '\r': + builder.append("\\r"); + break; + default: + if (chr < ' ') { + String t = "000" + Integer.toHexString(chr); + builder.append("\\u" + t.substring(t.length() - 4)); + } else { + builder.append(chr); + } + break; + } + } + builder.append('"'); + + return builder.toString(); + } + + /** + * Encode text as UTF-8 + * + * @param text the text to encode + * @return the encoded text, as UTF-8 + */ + private static String urlEncode(final String text) throws UnsupportedEncodingException { + return URLEncoder.encode(text, "UTF-8"); + } + +} \ No newline at end of file diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/interfaces/FancyMessage.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/interfaces/FancyMessage.java new file mode 100644 index 00000000..655c8de7 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/interfaces/FancyMessage.java @@ -0,0 +1,74 @@ +package com.gmail.filoghost.holographicdisplays.nms.interfaces; + +import java.io.IOException; + +import org.bukkit.ChatColor; +import org.bukkit.craftbukkit.libs.com.google.gson.stream.JsonWriter; +import org.bukkit.entity.Player; + +public interface FancyMessage { + + public FancyMessage color(final ChatColor color); + + public FancyMessage style(final ChatColor... styles); + + public FancyMessage file(final String path); + + public FancyMessage link(final String url); + + public FancyMessage suggest(final String command); + + public FancyMessage command(final String command); + + public FancyMessage tooltip(final String text); + + public FancyMessage then(final Object obj); + + public String toJSONString(); + + public void send(Player player); + + static class MessagePart { + + public ChatColor color = null; + public ChatColor[] styles = null; + public String clickActionName = null; + public String clickActionData = null; + public String hoverActionName = null; + public String hoverActionData = null; + public final String text; + + public MessagePart(final String text) { + this.text = text; + } + + public JsonWriter writeJson(final JsonWriter json) throws IOException { + json.beginObject().name("text").value(text); + if (color != null) { + json.name("color").value(color.name().toLowerCase()); + } + if (styles != null) { + for (final ChatColor style : styles) { + json.name(style == ChatColor.UNDERLINE ? "underlined" : style.name().toLowerCase()).value(true); + } + } + if (clickActionName != null && clickActionData != null) { + json.name("clickEvent") + .beginObject() + .name("action").value(clickActionName) + .name("value").value(clickActionData) + .endObject(); + } + if (hoverActionName != null && hoverActionData != null) { + json.name("hoverEvent") + .beginObject() + .name("action").value(hoverActionName) + .name("value").value(hoverActionData) + .endObject(); + } + return json.endObject(); + } + + } + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/interfaces/NMSManager.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/interfaces/NMSManager.java new file mode 100644 index 00000000..ccc35ea4 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/interfaces/NMSManager.java @@ -0,0 +1,38 @@ +package com.gmail.filoghost.holographicdisplays.nms.interfaces; + +import org.bukkit.inventory.ItemStack; + +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSArmorStand; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSEntityBase; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSHorse; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSItem; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSSlime; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSWitherSkull; +import com.gmail.filoghost.holographicdisplays.object.line.CraftHologramLine; +import com.gmail.filoghost.holographicdisplays.object.line.CraftItemLine; +import com.gmail.filoghost.holographicdisplays.object.line.CraftTouchSlimeLine; + +public interface NMSManager { + + // A method to register all the custom entities of the plugin, it may fail. + public void registerCustomEntities() throws Exception; + + public NMSArmorStand spawnNMSArmorStand(org.bukkit.World world, double x, double y, double z, CraftHologramLine parentPiece); + + public NMSHorse spawnNMSHorse(org.bukkit.World world, double x, double y, double z, CraftHologramLine parentPiece); + + public NMSWitherSkull spawnNMSWitherSkull(org.bukkit.World bukkitWorld, double x, double y, double z, CraftHologramLine parentPiece); + + public NMSItem spawnNMSItem(org.bukkit.World bukkitWorld, double x, double y, double z, CraftItemLine parentPiece, ItemStack stack); + + public NMSSlime spawnNMSSlime(org.bukkit.World bukkitWorld, double x, double y, double z, CraftTouchSlimeLine parentPiece); + + public boolean isNMSEntityBase(org.bukkit.entity.Entity bukkitEntity); + + public NMSEntityBase getNMSEntityBase(org.bukkit.entity.Entity bukkitEntity); + + public FancyMessage newFancyMessage(String text); + + public boolean hasChatHoverFeature(); + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/interfaces/entity/NMSArmorStand.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/interfaces/entity/NMSArmorStand.java new file mode 100644 index 00000000..3c518d66 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/interfaces/entity/NMSArmorStand.java @@ -0,0 +1,5 @@ +package com.gmail.filoghost.holographicdisplays.nms.interfaces.entity; + +public interface NMSArmorStand extends NMSNameable, NMSRideable { + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/interfaces/entity/NMSEntityBase.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/interfaces/entity/NMSEntityBase.java new file mode 100644 index 00000000..cad06778 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/interfaces/entity/NMSEntityBase.java @@ -0,0 +1,31 @@ +package com.gmail.filoghost.holographicdisplays.nms.interfaces.entity; + +import com.gmail.filoghost.holographicdisplays.object.line.CraftHologramLine; + +/** + * An interface to represent a custom NMS entity being part of a hologram. + */ +public interface NMSEntityBase { + + // Returns the linked CraftHologramLine, all the entities are part of a piece. Should never be null. + public CraftHologramLine getHologramLine(); + + // Sets if the entity should tick or not. + public void setLockTick(boolean lock); + + // Sets the location through NMS. + public void setLocationNMS(double x, double y, double z); + + // Returns if the entity is dead through NMS. + public boolean isDeadNMS(); + + // Kills the entity through NMS. + public void killEntityNMS(); + + // The entity ID. + public int getIdNMS(); + + // Returns the bukkit entity. + public org.bukkit.entity.Entity getBukkitEntityNMS(); + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/interfaces/entity/NMSHorse.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/interfaces/entity/NMSHorse.java new file mode 100644 index 00000000..bdaf7ff5 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/interfaces/entity/NMSHorse.java @@ -0,0 +1,5 @@ +package com.gmail.filoghost.holographicdisplays.nms.interfaces.entity; + +public interface NMSHorse extends NMSNameable { + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/interfaces/entity/NMSItem.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/interfaces/entity/NMSItem.java new file mode 100644 index 00000000..6bb50585 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/interfaces/entity/NMSItem.java @@ -0,0 +1,13 @@ +package com.gmail.filoghost.holographicdisplays.nms.interfaces.entity; + +import org.bukkit.inventory.ItemStack; + +public interface NMSItem extends NMSEntityBase { + + // Sets the bukkit ItemStack for this item. + public void setItemStackNMS(ItemStack stack); + + // Sets if this item can be picked up by players. + public void allowPickup(boolean pickup); + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/interfaces/entity/NMSNameable.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/interfaces/entity/NMSNameable.java new file mode 100644 index 00000000..c33125b6 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/interfaces/entity/NMSNameable.java @@ -0,0 +1,11 @@ +package com.gmail.filoghost.holographicdisplays.nms.interfaces.entity; + +public interface NMSNameable extends NMSEntityBase { + + // Sets a custom name for this entity. + public void setCustomNameNMS(String name); + + // Returns the custom name of this entity. + public String getCustomNameNMS(); + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/interfaces/entity/NMSRideable.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/interfaces/entity/NMSRideable.java new file mode 100644 index 00000000..42090c69 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/interfaces/entity/NMSRideable.java @@ -0,0 +1,8 @@ +package com.gmail.filoghost.holographicdisplays.nms.interfaces.entity; + +public interface NMSRideable extends NMSEntityBase { + + // Sets the passenger of this entity through NMS. + public void setPassengerNMS(NMSEntityBase passenger); + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/interfaces/entity/NMSSlime.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/interfaces/entity/NMSSlime.java new file mode 100644 index 00000000..0bf17b13 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/interfaces/entity/NMSSlime.java @@ -0,0 +1,5 @@ +package com.gmail.filoghost.holographicdisplays.nms.interfaces.entity; + +public interface NMSSlime extends NMSEntityBase { + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/interfaces/entity/NMSWitherSkull.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/interfaces/entity/NMSWitherSkull.java new file mode 100644 index 00000000..9d51c535 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/interfaces/entity/NMSWitherSkull.java @@ -0,0 +1,5 @@ +package com.gmail.filoghost.holographicdisplays.nms.interfaces.entity; + +public interface NMSWitherSkull extends NMSRideable { + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_6_R3/CraftNMSHorse.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_6_R3/CraftNMSHorse.java new file mode 100644 index 00000000..fecd6a37 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_6_R3/CraftNMSHorse.java @@ -0,0 +1,64 @@ +package com.gmail.filoghost.holographicdisplays.nms.v1_6_R3; + +import java.util.Collection; + +import org.bukkit.EntityEffect; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_6_R3.CraftServer; +import org.bukkit.craftbukkit.v1_6_R3.entity.CraftHorse; +import org.bukkit.entity.AnimalTamer; +import org.bukkit.entity.Entity; +import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; +import org.bukkit.potion.PotionEffect; +import org.bukkit.util.Vector; + +public class CraftNMSHorse extends CraftHorse { + + public CraftNMSHorse(CraftServer server, EntityNMSHorse entity) { + super(server, entity); + } + + // Disallow all the bukkit methods. + + @Override + public void remove() { + // Cannot be removed, this is the most important to override. + } + + // Methods from Horse class + @Override public void setVariant(Variant variant) { } + @Override public void setColor(Color color) { } + @Override public void setStyle(Style style) { } + @Override public void setCarryingChest(boolean chest) { } + @Override public void setDomestication(int domestication) { } + @Override public void setJumpStrength(double jump) { } + + // Methods form Ageable class + @Override public void setAge(int age) { } + @Override public void setAgeLock(boolean lock) { } + @Override public void setBreed(boolean breed) { } + @Override public void setAdult() { } + @Override public void setBaby() { } + + // Methods from LivingEntity class + @Override public boolean addPotionEffect(PotionEffect effect) { return false; } + @Override public boolean addPotionEffect(PotionEffect effect, boolean param) { return false; } + @Override public boolean addPotionEffects(Collection effects) { return false; } + @Override public void setRemoveWhenFarAway(boolean remove) { } + + // Methods from Entity + @Override public void setVelocity(Vector vel) { } + @Override public boolean teleport(Location loc) { return false; } + @Override public boolean teleport(Entity entity) { return false; } + @Override public boolean teleport(Location loc, TeleportCause cause) { return false; } + @Override public boolean teleport(Entity entity, TeleportCause cause) { return false; } + @Override public void setFireTicks(int ticks) { } + @Override public boolean setPassenger(Entity entity) { return false; } + @Override public boolean eject() { return false; } + @Override public boolean leaveVehicle() { return false; } + @Override public void playEffect(EntityEffect effect) { } + + // Methods from Tameable + @Override public void setTamed(boolean tame) { } + @Override public void setOwner(AnimalTamer owner) { } +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_6_R3/CraftNMSItem.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_6_R3/CraftNMSItem.java new file mode 100644 index 00000000..d54cf299 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_6_R3/CraftNMSItem.java @@ -0,0 +1,40 @@ +package com.gmail.filoghost.holographicdisplays.nms.v1_6_R3; + +import org.bukkit.EntityEffect; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_6_R3.CraftServer; +import org.bukkit.craftbukkit.v1_6_R3.entity.CraftItem; +import org.bukkit.entity.Entity; +import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; +import org.bukkit.inventory.ItemStack; +import org.bukkit.util.Vector; + +public class CraftNMSItem extends CraftItem { + + public CraftNMSItem(CraftServer server, EntityNMSItem entity) { + super(server, entity); + } + + // Disallow all the bukkit methods. + + @Override + public void remove() { + // Cannot be removed, this is the most important to override. + } + + // Methods from Entity + @Override public void setVelocity(Vector vel) { } + @Override public boolean teleport(Location loc) { return false; } + @Override public boolean teleport(Entity entity) { return false; } + @Override public boolean teleport(Location loc, TeleportCause cause) { return false; } + @Override public boolean teleport(Entity entity, TeleportCause cause) { return false; } + @Override public void setFireTicks(int ticks) { } + @Override public boolean setPassenger(Entity entity) { return false; } + @Override public boolean eject() { return false; } + @Override public boolean leaveVehicle() { return false; } + @Override public void playEffect(EntityEffect effect) { } + + // Methods from Item + @Override public void setItemStack(ItemStack stack) { } + @Override public void setPickupDelay(int delay) { } +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_6_R3/CraftNMSSlime.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_6_R3/CraftNMSSlime.java new file mode 100644 index 00000000..842d5bfb --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_6_R3/CraftNMSSlime.java @@ -0,0 +1,47 @@ +package com.gmail.filoghost.holographicdisplays.nms.v1_6_R3; + +import java.util.Collection; + +import org.bukkit.EntityEffect; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_6_R3.CraftServer; +import org.bukkit.craftbukkit.v1_6_R3.entity.CraftSlime; +import org.bukkit.entity.Entity; +import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; +import org.bukkit.potion.PotionEffect; +import org.bukkit.util.Vector; + +public class CraftNMSSlime extends CraftSlime { + + public CraftNMSSlime(CraftServer server, EntityNMSSlime entity) { + super(server, entity); + } + + // Disallow all the bukkit methods. + + @Override + public void remove() { + // Cannot be removed, this is the most important to override. + } + + // Methods from LivingEntity class + @Override public boolean addPotionEffect(PotionEffect effect) { return false; } + @Override public boolean addPotionEffect(PotionEffect effect, boolean param) { return false; } + @Override public boolean addPotionEffects(Collection effects) { return false; } + @Override public void setRemoveWhenFarAway(boolean remove) { } + + // Methods from Entity + @Override public void setVelocity(Vector vel) { } + @Override public boolean teleport(Location loc) { return false; } + @Override public boolean teleport(Entity entity) { return false; } + @Override public boolean teleport(Location loc, TeleportCause cause) { return false; } + @Override public boolean teleport(Entity entity, TeleportCause cause) { return false; } + @Override public void setFireTicks(int ticks) { } + @Override public boolean setPassenger(Entity entity) { return false; } + @Override public boolean eject() { return false; } + @Override public boolean leaveVehicle() { return false; } + @Override public void playEffect(EntityEffect effect) { } + + // Methods from Slime + @Override public void setSize(int size) { } +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_6_R3/CraftNMSWitherSkull.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_6_R3/CraftNMSWitherSkull.java new file mode 100644 index 00000000..9cd72d71 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_6_R3/CraftNMSWitherSkull.java @@ -0,0 +1,46 @@ +package com.gmail.filoghost.holographicdisplays.nms.v1_6_R3; + +import org.bukkit.EntityEffect; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_6_R3.CraftServer; +import org.bukkit.craftbukkit.v1_6_R3.entity.CraftWitherSkull; +import org.bukkit.entity.Entity; +import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; +import org.bukkit.util.Vector; + +public class CraftNMSWitherSkull extends CraftWitherSkull { + + public CraftNMSWitherSkull(CraftServer server, EntityNMSWitherSkull entity) { + super(server, entity); + } + + // Disallow all the bukkit methods. + + @Override + public void remove() { + // Cannot be removed, this is the most important to override. + } + + // Method from Fireball + @Override public void setDirection(Vector dir) { } + + // Method from Projectile + @Override public void setBounce(boolean bounce) { } + + // Methods from Explosive + @Override public void setYield(float yield) { } + @Override public void setIsIncendiary(boolean fire) { } + + // Methods from Entity + @Override public void setVelocity(Vector vel) { } + @Override public boolean teleport(Location loc) { return false; } + @Override public boolean teleport(Entity entity) { return false; } + @Override public boolean teleport(Location loc, TeleportCause cause) { return false; } + @Override public boolean teleport(Entity entity, TeleportCause cause) { return false; } + @Override public void setFireTicks(int ticks) { } + @Override public boolean setPassenger(Entity entity) { return false; } + @Override public boolean eject() { return false; } + @Override public boolean leaveVehicle() { return false; } + @Override public void playEffect(EntityEffect effect) { } + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_6_R3/EntityNMSHorse.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_6_R3/EntityNMSHorse.java new file mode 100644 index 00000000..87821420 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_6_R3/EntityNMSHorse.java @@ -0,0 +1,157 @@ +package com.gmail.filoghost.holographicdisplays.nms.v1_6_R3; + +import net.minecraft.server.v1_6_R3.EntityHorse; +import net.minecraft.server.v1_6_R3.NBTTagCompound; +import net.minecraft.server.v1_6_R3.World; + +import org.bukkit.craftbukkit.v1_6_R3.entity.CraftEntity; + +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSHorse; +import com.gmail.filoghost.holographicdisplays.object.line.CraftHologramLine; + +public class EntityNMSHorse extends EntityHorse implements NMSHorse { + + private boolean lockTick; + private CraftHologramLine parentPiece; + + public EntityNMSHorse(World world, CraftHologramLine parentPiece) { + super(world); + super.ageLocked = true; + super.persistent = true; + super.boundingBox.a = 0.0; + super.boundingBox.b = 0.0; + super.boundingBox.c = 0.0; + super.boundingBox.d = 0.0; + super.boundingBox.e = 0.0; + super.boundingBox.f = 0.0; + a(0.0F, 0.0F); + setAge(-1700000); // This is a magic value. No one will see the real horse. + this.parentPiece = parentPiece; + } + + @Override + public void l_() { + // Checks every 20 ticks. + if (ticksLived % 20 == 0) { + // The horse dies without a vehicle. + if (this.vehicle == null) { + die(); + } + } + + if (!lockTick) { + super.l_(); + } + } + + @Override + public void b(NBTTagCompound nbttagcompound) { + // Do not save NBT. + } + + @Override + public boolean c(NBTTagCompound nbttagcompound) { + // Do not save NBT. + return false; + } + + @Override + public boolean d(NBTTagCompound nbttagcompound) { + // Do not save NBT. + return false; + } + + @Override + public void e(NBTTagCompound nbttagcompound) { + // Do not save NBT. + } + + @Override + public boolean isInvulnerable() { + /* + * The field Entity.invulnerable is private. + * It's only used while saving NBTTags, but since the entity would be killed + * on chunk unload, we prefer to override isInvulnerable(). + */ + return true; + } + + @Override + public void setCustomName(String customName) { + // Locks the custom name. + } + + @Override + public void setCustomNameVisible(boolean visible) { + // Locks the custom name. + } + + @Override + public void makeSound(String sound, float volume, float pitch) { + // Remove sounds. + } + + @Override + public void setLockTick(boolean lock) { + lockTick = lock; + } + + @Override + public void die() { + setLockTick(false); + super.die(); + } + + @Override + public void setCustomNameNMS(String name) { + if (name != null && name.length() > 64) { + name = name.substring(0, 64); + } + super.setCustomName(name); + super.setCustomNameVisible(name != null); + } + + @Override + public CraftEntity getBukkitEntity() { + if (super.bukkitEntity == null) { + this.bukkitEntity = new CraftNMSHorse(this.world.getServer(), this); + } + return this.bukkitEntity; + } + + @Override + public boolean isDeadNMS() { + return super.dead; + } + + @Override + public String getCustomNameNMS() { + return super.getCustomName(); + } + + @Override + public void killEntityNMS() { + die(); + } + + @Override + public void setLocationNMS(double x, double y, double z) { + super.setPosition(x, y, z); + } + + @Override + public int getIdNMS() { + return this.id; + } + + @Override + public CraftHologramLine getHologramLine() { + return parentPiece; + } + + @Override + public org.bukkit.entity.Entity getBukkitEntityNMS() { + return getBukkitEntity(); + } + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_6_R3/EntityNMSItem.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_6_R3/EntityNMSItem.java new file mode 100644 index 00000000..96e86609 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_6_R3/EntityNMSItem.java @@ -0,0 +1,183 @@ +package com.gmail.filoghost.holographicdisplays.nms.v1_6_R3; + +import net.minecraft.server.v1_6_R3.Block; +import net.minecraft.server.v1_6_R3.EntityHuman; +import net.minecraft.server.v1_6_R3.EntityItem; +import net.minecraft.server.v1_6_R3.EntityPlayer; +import net.minecraft.server.v1_6_R3.ItemStack; +import net.minecraft.server.v1_6_R3.NBTTagCompound; +import net.minecraft.server.v1_6_R3.NBTTagList; +import net.minecraft.server.v1_6_R3.NBTTagString; +import net.minecraft.server.v1_6_R3.World; + +import org.bukkit.craftbukkit.v1_6_R3.entity.CraftEntity; +import org.bukkit.craftbukkit.v1_6_R3.inventory.CraftItemStack; +import org.bukkit.entity.Player; + +import com.gmail.filoghost.holographicdisplays.listener.MainListener; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSItem; +import com.gmail.filoghost.holographicdisplays.object.line.CraftItemLine; +import com.gmail.filoghost.holographicdisplays.util.ItemUtils; + +public class EntityNMSItem extends EntityItem implements NMSItem { + + private boolean lockTick; + private CraftItemLine parentPiece; + + public EntityNMSItem(World world, CraftItemLine piece) { + super(world); + super.pickupDelay = Integer.MAX_VALUE; + this.parentPiece = piece; + } + + @Override + public void l_() { + // Checks every 20 ticks. + if (ticksLived % 20 == 0) { + // The item dies without a vehicle. + if (this.vehicle == null) { + die(); + } + } + + if (!lockTick) { + super.l_(); + } + } + + @Override + public ItemStack getItemStack() { + // Dirty method to check if the icon is being picked up + StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace(); + if (stacktrace.length > 2 && stacktrace[2].getClassName().contains("EntityInsentient")) { + return null; // Try to pickup this, dear entity ignoring the pickupDelay! + } + + return super.getItemStack(); + } + + // Method called when a player is near. + @Override + public void b_(EntityHuman human) { + + if (parentPiece.getPickupHandler() != null && human instanceof EntityPlayer) { + MainListener.handleItemLinePickup((Player) human.getBukkitEntity(), parentPiece.getPickupHandler(), parentPiece.getParent()); + // It is never added to the inventory. + } + } + + @Override + public void b(NBTTagCompound nbttagcompound) { + // Do not save NBT. + } + + @Override + public boolean c(NBTTagCompound nbttagcompound) { + // Do not save NBT. + return false; + } + + @Override + public boolean d(NBTTagCompound nbttagcompound) { + // Do not save NBT. + return false; + } + + @Override + public void e(NBTTagCompound nbttagcompound) { + // Do not save NBT. + } + + @Override + public boolean isInvulnerable() { + /* + * The field Entity.invulnerable is private. + * It's only used while saving NBTTags, but since the entity would be killed + * on chunk unload, we prefer to override isInvulnerable(). + */ + return true; + } + + @Override + public void setLockTick(boolean lock) { + lockTick = lock; + } + + @Override + public void die() { + setLockTick(false); + super.die(); + } + + @Override + public CraftEntity getBukkitEntity() { + if (super.bukkitEntity == null) { + this.bukkitEntity = new CraftNMSItem(this.world.getServer(), this); + } + return this.bukkitEntity; + } + + @Override + public boolean isDeadNMS() { + return this.dead; + } + + @Override + public void killEntityNMS() { + die(); + } + + @Override + public void setLocationNMS(double x, double y, double z) { + super.setPosition(x, y, z); + } + + @Override + public void setItemStackNMS(org.bukkit.inventory.ItemStack stack) { + ItemStack newItem = CraftItemStack.asNMSCopy(stack); + + if (newItem == null) { + newItem = new ItemStack(Block.BEDROCK); + } + + if (newItem.tag == null) { + newItem.tag = new NBTTagCompound(); + } + NBTTagCompound display = newItem.tag.getCompound("display"); + + if (!newItem.tag.hasKey("display")) { + newItem.tag.set("display", display); + } + + NBTTagList tagList = new NBTTagList(); + tagList.add(new NBTTagString(ItemUtils.ANTISTACK_LORE)); // Antistack lore + + display.set("Lore", tagList); + newItem.count = 0; + setItemStack(newItem); + } + + @Override + public int getIdNMS() { + return this.id; + } + + @Override + public CraftItemLine getHologramLine() { + return parentPiece; + } + + @Override + public void allowPickup(boolean pickup) { + if (pickup) { + super.pickupDelay = 0; + } else { + super.pickupDelay = Integer.MAX_VALUE; + } + } + + @Override + public org.bukkit.entity.Entity getBukkitEntityNMS() { + return getBukkitEntity(); + } +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_6_R3/EntityNMSSlime.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_6_R3/EntityNMSSlime.java new file mode 100644 index 00000000..ad50e46a --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_6_R3/EntityNMSSlime.java @@ -0,0 +1,144 @@ +package com.gmail.filoghost.holographicdisplays.nms.v1_6_R3; + +import net.minecraft.server.v1_6_R3.EntitySlime; +import net.minecraft.server.v1_6_R3.NBTTagCompound; +import net.minecraft.server.v1_6_R3.World; + +import org.bukkit.craftbukkit.v1_6_R3.entity.CraftEntity; + +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSSlime; +import com.gmail.filoghost.holographicdisplays.object.line.CraftHologramLine; +import com.gmail.filoghost.holographicdisplays.object.line.CraftTouchSlimeLine; + +public class EntityNMSSlime extends EntitySlime implements NMSSlime { + + private boolean lockTick; + private CraftTouchSlimeLine parentPiece; + + public EntityNMSSlime(World world, CraftTouchSlimeLine parentPiece) { + super(world); + super.persistent = true; + super.boundingBox.a = 0.0; + super.boundingBox.b = 0.0; + super.boundingBox.c = 0.0; + super.boundingBox.d = 0.0; + super.boundingBox.e = 0.0; + super.boundingBox.f = 0.0; + a(0.0F, 0.0F); + setSize(1); + setInvisible(true); + this.parentPiece = parentPiece; + } + + @Override + public void l_() { + // Checks every 20 ticks. + if (ticksLived % 20 == 0) { + // The slime dies without a vehicle. + if (this.vehicle == null) { + die(); + } + } + + if (!lockTick) { + super.l_(); + } + } + + @Override + public void b(NBTTagCompound nbttagcompound) { + // Do not save NBT. + } + + @Override + public boolean c(NBTTagCompound nbttagcompound) { + // Do not save NBT. + return false; + } + + @Override + public boolean d(NBTTagCompound nbttagcompound) { + // Do not save NBT. + return false; + } + + @Override + public void e(NBTTagCompound nbttagcompound) { + // Do not save NBT. + } + + @Override + public boolean isInvulnerable() { + /* + * The field Entity.invulnerable is private. + * It's only used while saving NBTTags, but since the entity would be killed + * on chunk unload, we prefer to override isInvulnerable(). + */ + return true; + } + + @Override + public void setCustomName(String customName) { + // Locks the custom name. + } + + @Override + public void setCustomNameVisible(boolean visible) { + // Locks the custom name. + } + + @Override + public void makeSound(String sound, float volume, float pitch) { + // Remove sounds. + } + + @Override + public void setLockTick(boolean lock) { + lockTick = lock; + } + + @Override + public void die() { + setLockTick(false); + super.die(); + } + + @Override + public CraftEntity getBukkitEntity() { + if (super.bukkitEntity == null) { + this.bukkitEntity = new CraftNMSSlime(this.world.getServer(), this); + } + return this.bukkitEntity; + } + + @Override + public boolean isDeadNMS() { + return super.dead; + } + + @Override + public void killEntityNMS() { + die(); + } + + @Override + public void setLocationNMS(double x, double y, double z) { + super.setPosition(x, y, z); + } + + @Override + public int getIdNMS() { + return this.id; + } + + @Override + public CraftHologramLine getHologramLine() { + return parentPiece; + } + + @Override + public org.bukkit.entity.Entity getBukkitEntityNMS() { + return getBukkitEntity(); + } + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_6_R3/EntityNMSWitherSkull.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_6_R3/EntityNMSWitherSkull.java new file mode 100644 index 00000000..76ab72c7 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_6_R3/EntityNMSWitherSkull.java @@ -0,0 +1,177 @@ +package com.gmail.filoghost.holographicdisplays.nms.v1_6_R3; + +import org.bukkit.craftbukkit.v1_6_R3.entity.CraftEntity; + +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSEntityBase; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSWitherSkull; +import com.gmail.filoghost.holographicdisplays.object.line.CraftHologramLine; +import com.gmail.filoghost.holographicdisplays.util.Utils; + +import net.minecraft.server.v1_6_R3.Entity; +import net.minecraft.server.v1_6_R3.EntityWitherSkull; +import net.minecraft.server.v1_6_R3.NBTTagCompound; +import net.minecraft.server.v1_6_R3.Packet34EntityTeleport; +import net.minecraft.server.v1_6_R3.World; +import net.minecraft.server.v1_6_R3.EntityPlayer; + +public class EntityNMSWitherSkull extends EntityWitherSkull implements NMSWitherSkull { + + private boolean lockTick; + private CraftHologramLine parentPiece; + + private int teleportedRecently; + + public EntityNMSWitherSkull(World world, CraftHologramLine parentPiece) { + super(world); + super.motX = 0.0; + super.motY = 0.0; + super.motZ = 0.0; + super.dirX = 0.0; + super.dirY = 0.0; + super.dirZ = 0.0; + super.boundingBox.a = 0.0; + super.boundingBox.b = 0.0; + super.boundingBox.c = 0.0; + super.boundingBox.d = 0.0; + super.boundingBox.e = 0.0; + super.boundingBox.f = 0.0; + a(0.0F, 0.0F); + this.parentPiece = parentPiece; + } + + @Override + public void b(NBTTagCompound nbttagcompound) { + // Do not save NBT. + } + + @Override + public boolean c(NBTTagCompound nbttagcompound) { + // Do not save NBT. + return false; + } + + @Override + public boolean d(NBTTagCompound nbttagcompound) { + // Do not save NBT. + return false; + } + + @Override + public void e(NBTTagCompound nbttagcompound) { + // Do not save NBT. + } + + @Override + public boolean isInvulnerable() { + /* + * The field Entity.invulnerable is private. + * It's only used while saving NBTTags, but since the entity would be killed + * on chunk unload, we prefer to override isInvulnerable(). + */ + return true; + } + + @Override + public void l_() { + // Fixed the position being modified by random move packets. + if (teleportedRecently != -1) { + if (teleportedRecently++ > 10) { + teleportedRecently = -1; + Packet34EntityTeleport teleportPacket = new Packet34EntityTeleport(this); + + for (Object obj : this.world.players) { + if (obj instanceof EntityPlayer) { + EntityPlayer nmsPlayer = (EntityPlayer) obj; + + double distanceSquared = Utils.square(nmsPlayer.locX - this.locX) + Utils.square(nmsPlayer.locZ - this.locZ); + if (distanceSquared < 8192 && nmsPlayer.playerConnection != null) { + nmsPlayer.playerConnection.sendPacket(teleportPacket); + } + } + } + } + } + + if (!lockTick) { + super.l_(); + } + } + + @Override + public void makeSound(String sound, float f1, float f2) { + // Remove sounds. + } + + @Override + public void setLockTick(boolean lock) { + lockTick = lock; + } + + @Override + public void die() { + setLockTick(false); + super.die(); + } + + @Override + public CraftEntity getBukkitEntity() { + if (super.bukkitEntity == null) { + this.bukkitEntity = new CraftNMSWitherSkull(this.world.getServer(), this); + } + return this.bukkitEntity; + } + + @Override + public void killEntityNMS() { + die(); + } + + @Override + public void setLocationNMS(double x, double y, double z) { + super.setPosition(x, y, z); + + teleportedRecently = 0; + + // Send a packet near to update the position. + Packet34EntityTeleport teleportPacket = new Packet34EntityTeleport(this); + + for (Object obj : this.world.players) { + if (obj instanceof EntityPlayer) { + EntityPlayer nmsPlayer = (EntityPlayer) obj; + + double distanceSquared = Utils.square(nmsPlayer.locX - this.locX) + Utils.square(nmsPlayer.locZ - this.locZ); + if (distanceSquared < 8192 && nmsPlayer.playerConnection != null) { + nmsPlayer.playerConnection.sendPacket(teleportPacket); + } + } + } + } + + @Override + public boolean isDeadNMS() { + return this.dead; + } + + @Override + public void setPassengerNMS(NMSEntityBase passenger) { + if (passenger instanceof Entity) { + ((Entity) passenger).setPassengerOf(this); + } + } + + @Override + public int getIdNMS() { + return this.id; + } + + @Override + public CraftHologramLine getHologramLine() { + return parentPiece; + } + + @Override + public org.bukkit.entity.Entity getBukkitEntityNMS() { + return getBukkitEntity(); + } + +} \ No newline at end of file diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_6_R3/FancyMessageImpl.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_6_R3/FancyMessageImpl.java new file mode 100644 index 00000000..c8efe010 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_6_R3/FancyMessageImpl.java @@ -0,0 +1,95 @@ +package com.gmail.filoghost.holographicdisplays.nms.v1_6_R3; + +import java.util.ArrayList; +import java.util.List; + +import org.bukkit.ChatColor; +import org.bukkit.entity.Player; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.FancyMessage; + +public class FancyMessageImpl implements FancyMessage { + + private final List messageParts; + + public FancyMessageImpl(final String firstPartText) { + messageParts = new ArrayList(); + messageParts.add(new MessagePart(firstPartText)); + } + + @Override + public FancyMessageImpl color(final ChatColor color) { + if (!color.isColor()) { + throw new IllegalArgumentException(color.name() + " is not a color"); + } + latest().color = color; + return this; + } + + @Override + public FancyMessageImpl style(final ChatColor... styles) { + for (final ChatColor style : styles) { + if (!style.isFormat()) { + throw new IllegalArgumentException(style.name() + " is not a style"); + } + } + latest().styles = styles; + return this; + } + + @Override + public FancyMessageImpl file(final String path) { + return this; + } + + @Override + public FancyMessageImpl link(final String url) { + return this; + } + + @Override + public FancyMessageImpl suggest(final String command) { + return this; + } + + @Override + public FancyMessageImpl command(final String command) { + return this; + } + + @Override + public FancyMessageImpl tooltip(final String text) { + return this; + } + + @Override + public FancyMessageImpl then(final Object obj) { + messageParts.add(new MessagePart(obj.toString())); + return this; + } + + @Override + public String toJSONString() { + StringBuilder sb = new StringBuilder(); + for (MessagePart part : messageParts) { + if (part.color != null) { + sb.append(part.color.toString()); + } + if (part.styles != null && part.styles.length > 0) { + for (ChatColor style : part.styles) { + sb.append(style.toString()); + } + } + sb.append(part.text); + } + return sb.toString(); + } + + @Override + public void send(Player player) { + player.sendMessage(toJSONString()); + } + + private MessagePart latest() { + return messageParts.get(messageParts.size() - 1); + } +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_6_R3/NmsManagerImpl.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_6_R3/NmsManagerImpl.java new file mode 100644 index 00000000..620a4203 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_6_R3/NmsManagerImpl.java @@ -0,0 +1,127 @@ +package com.gmail.filoghost.holographicdisplays.nms.v1_6_R3; + +import net.minecraft.server.v1_6_R3.Entity; +import net.minecraft.server.v1_6_R3.EntityTypes; +import net.minecraft.server.v1_6_R3.WorldServer; + +import org.apache.commons.lang.NotImplementedException; +import org.bukkit.World; +import org.bukkit.craftbukkit.v1_6_R3.CraftWorld; +import org.bukkit.craftbukkit.v1_6_R3.entity.CraftEntity; +import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason; +import org.bukkit.inventory.ItemStack; + +import com.gmail.filoghost.holographicdisplays.nms.interfaces.FancyMessage; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.NMSManager; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSArmorStand; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSEntityBase; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSHorse; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSItem; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSWitherSkull; +import com.gmail.filoghost.holographicdisplays.object.line.CraftHologramLine; +import com.gmail.filoghost.holographicdisplays.object.line.CraftItemLine; +import com.gmail.filoghost.holographicdisplays.object.line.CraftTouchSlimeLine; +import com.gmail.filoghost.holographicdisplays.util.DebugHandler; +import com.gmail.filoghost.holographicdisplays.util.ReflectionUtils; +import com.gmail.filoghost.holographicdisplays.util.VersionUtils; + +public class NmsManagerImpl implements NMSManager { + + @Override + public void registerCustomEntities() throws Exception { + registerCustomEntity(EntityNMSHorse.class, "EntityHorse", 100); + registerCustomEntity(EntityNMSWitherSkull.class, "WitherSkull", 19); + registerCustomEntity(EntityNMSItem.class, "Item", 1); + registerCustomEntity(EntityNMSSlime.class, "Slime", 55); + } + + @SuppressWarnings("rawtypes") + public void registerCustomEntity(Class entityClass, String name, int id) throws Exception { + if (VersionUtils.isMCPCOrCauldron()) { + // MCPC+ / Cauldron entity registration. + Class entityTypesClass = Class.forName("net.minecraft.server.v1_6_R3.EntityTypes"); + ReflectionUtils.putInPrivateStaticMap(entityTypesClass, "field_75626_c", entityClass, name); + ReflectionUtils.putInPrivateStaticMap(entityTypesClass, "field_75624_e", entityClass, Integer.valueOf(id)); + } else { + // Normal entity registration. + ReflectionUtils.putInPrivateStaticMap(EntityTypes.class, "c", entityClass, name); + ReflectionUtils.putInPrivateStaticMap(EntityTypes.class, "e", entityClass, Integer.valueOf(id)); + } + } + + @Override + public NMSHorse spawnNMSHorse(org.bukkit.World world, double x, double y, double z, CraftHologramLine parentPiece) { + WorldServer nmsWorld = ((CraftWorld) world).getHandle(); + EntityNMSHorse invisibleHorse = new EntityNMSHorse(nmsWorld, parentPiece); + invisibleHorse.setLocationNMS(x, y, z); + if (!nmsWorld.addEntity(invisibleHorse, SpawnReason.CUSTOM)) { + DebugHandler.handleSpawnFail(parentPiece); + } + return invisibleHorse; + } + + @Override + public NMSWitherSkull spawnNMSWitherSkull(org.bukkit.World bukkitWorld, double x, double y, double z, CraftHologramLine parentPiece) { + WorldServer nmsWorld = ((CraftWorld) bukkitWorld).getHandle(); + EntityNMSWitherSkull staticWitherSkull = new EntityNMSWitherSkull(nmsWorld, parentPiece); + staticWitherSkull.setLocationNMS(x, y, z); + if (!nmsWorld.addEntity(staticWitherSkull, SpawnReason.CUSTOM)) { + DebugHandler.handleSpawnFail(parentPiece); + } + return staticWitherSkull; + } + + @Override + public NMSItem spawnNMSItem(org.bukkit.World bukkitWorld, double x, double y, double z, CraftItemLine parentPiece, ItemStack stack) { + WorldServer nmsWorld = ((CraftWorld) bukkitWorld).getHandle(); + EntityNMSItem customItem = new EntityNMSItem(nmsWorld, parentPiece); + customItem.setLocationNMS(x, y, z); + customItem.setItemStackNMS(stack); + if (!nmsWorld.addEntity(customItem, SpawnReason.CUSTOM)) { + DebugHandler.handleSpawnFail(parentPiece); + } + return customItem; + } + + @Override + public EntityNMSSlime spawnNMSSlime(org.bukkit.World bukkitWorld, double x, double y, double z, CraftTouchSlimeLine parentPiece) { + WorldServer nmsWorld = ((CraftWorld) bukkitWorld).getHandle(); + EntityNMSSlime touchSlime = new EntityNMSSlime(nmsWorld, parentPiece); + touchSlime.setLocationNMS(x, y, z); + if (!nmsWorld.addEntity(touchSlime, SpawnReason.CUSTOM)) { + DebugHandler.handleSpawnFail(parentPiece); + } + return touchSlime; + } + + @Override + public boolean isNMSEntityBase(org.bukkit.entity.Entity bukkitEntity) { + return ((CraftEntity) bukkitEntity).getHandle() instanceof NMSEntityBase; + } + + @Override + public NMSEntityBase getNMSEntityBase(org.bukkit.entity.Entity bukkitEntity) { + + Entity nmsEntity = ((CraftEntity) bukkitEntity).getHandle(); + if (nmsEntity instanceof NMSEntityBase) { + return ((NMSEntityBase) nmsEntity); + } + + return null; + } + + @Override + public FancyMessage newFancyMessage(String text) { + return new FancyMessageImpl(text); + } + + @Override + public boolean hasChatHoverFeature() { + return false; + } + + @Override + public NMSArmorStand spawnNMSArmorStand(World world, double x, double y, double z, CraftHologramLine parentPiece) { + throw new NotImplementedException("Method can only be used on 1.8 or higher"); + } +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R1/CraftNMSHorse.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R1/CraftNMSHorse.java new file mode 100644 index 00000000..b6263a07 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R1/CraftNMSHorse.java @@ -0,0 +1,64 @@ +package com.gmail.filoghost.holographicdisplays.nms.v1_7_R1; + +import java.util.Collection; + +import org.bukkit.EntityEffect; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_7_R1.CraftServer; +import org.bukkit.craftbukkit.v1_7_R1.entity.CraftHorse; +import org.bukkit.entity.AnimalTamer; +import org.bukkit.entity.Entity; +import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; +import org.bukkit.potion.PotionEffect; +import org.bukkit.util.Vector; + +public class CraftNMSHorse extends CraftHorse { + + public CraftNMSHorse(CraftServer server, EntityNMSHorse entity) { + super(server, entity); + } + + // Disallow all the bukkit methods. + + @Override + public void remove() { + // Cannot be removed, this is the most important to override. + } + + // Methods from Horse class + @Override public void setVariant(Variant variant) { } + @Override public void setColor(Color color) { } + @Override public void setStyle(Style style) { } + @Override public void setCarryingChest(boolean chest) { } + @Override public void setDomestication(int domestication) { } + @Override public void setJumpStrength(double jump) { } + + // Methods form Ageable class + @Override public void setAge(int age) { } + @Override public void setAgeLock(boolean lock) { } + @Override public void setBreed(boolean breed) { } + @Override public void setAdult() { } + @Override public void setBaby() { } + + // Methods from LivingEntity class + @Override public boolean addPotionEffect(PotionEffect effect) { return false; } + @Override public boolean addPotionEffect(PotionEffect effect, boolean param) { return false; } + @Override public boolean addPotionEffects(Collection effects) { return false; } + @Override public void setRemoveWhenFarAway(boolean remove) { } + + // Methods from Entity + @Override public void setVelocity(Vector vel) { } + @Override public boolean teleport(Location loc) { return false; } + @Override public boolean teleport(Entity entity) { return false; } + @Override public boolean teleport(Location loc, TeleportCause cause) { return false; } + @Override public boolean teleport(Entity entity, TeleportCause cause) { return false; } + @Override public void setFireTicks(int ticks) { } + @Override public boolean setPassenger(Entity entity) { return false; } + @Override public boolean eject() { return false; } + @Override public boolean leaveVehicle() { return false; } + @Override public void playEffect(EntityEffect effect) { } + + // Methods from Tameable + @Override public void setTamed(boolean tame) { } + @Override public void setOwner(AnimalTamer owner) { } +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R1/CraftNMSItem.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R1/CraftNMSItem.java new file mode 100644 index 00000000..376605c9 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R1/CraftNMSItem.java @@ -0,0 +1,40 @@ +package com.gmail.filoghost.holographicdisplays.nms.v1_7_R1; + +import org.bukkit.EntityEffect; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_7_R1.CraftServer; +import org.bukkit.craftbukkit.v1_7_R1.entity.CraftItem; +import org.bukkit.entity.Entity; +import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; +import org.bukkit.inventory.ItemStack; +import org.bukkit.util.Vector; + +public class CraftNMSItem extends CraftItem { + + public CraftNMSItem(CraftServer server, EntityNMSItem entity) { + super(server, entity); + } + + // Disallow all the bukkit methods. + + @Override + public void remove() { + // Cannot be removed, this is the most important to override. + } + + // Methods from Entity + @Override public void setVelocity(Vector vel) { } + @Override public boolean teleport(Location loc) { return false; } + @Override public boolean teleport(Entity entity) { return false; } + @Override public boolean teleport(Location loc, TeleportCause cause) { return false; } + @Override public boolean teleport(Entity entity, TeleportCause cause) { return false; } + @Override public void setFireTicks(int ticks) { } + @Override public boolean setPassenger(Entity entity) { return false; } + @Override public boolean eject() { return false; } + @Override public boolean leaveVehicle() { return false; } + @Override public void playEffect(EntityEffect effect) { } + + // Methods from Item + @Override public void setItemStack(ItemStack stack) { } + @Override public void setPickupDelay(int delay) { } +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R1/CraftNMSSlime.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R1/CraftNMSSlime.java new file mode 100644 index 00000000..1f9e99f4 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R1/CraftNMSSlime.java @@ -0,0 +1,47 @@ +package com.gmail.filoghost.holographicdisplays.nms.v1_7_R1; + +import java.util.Collection; + +import org.bukkit.EntityEffect; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_7_R1.CraftServer; +import org.bukkit.craftbukkit.v1_7_R1.entity.CraftSlime; +import org.bukkit.entity.Entity; +import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; +import org.bukkit.potion.PotionEffect; +import org.bukkit.util.Vector; + +public class CraftNMSSlime extends CraftSlime { + + public CraftNMSSlime(CraftServer server, EntityNMSSlime entity) { + super(server, entity); + } + + // Disallow all the bukkit methods. + + @Override + public void remove() { + // Cannot be removed, this is the most important to override. + } + + // Methods from LivingEntity class + @Override public boolean addPotionEffect(PotionEffect effect) { return false; } + @Override public boolean addPotionEffect(PotionEffect effect, boolean param) { return false; } + @Override public boolean addPotionEffects(Collection effects) { return false; } + @Override public void setRemoveWhenFarAway(boolean remove) { } + + // Methods from Entity + @Override public void setVelocity(Vector vel) { } + @Override public boolean teleport(Location loc) { return false; } + @Override public boolean teleport(Entity entity) { return false; } + @Override public boolean teleport(Location loc, TeleportCause cause) { return false; } + @Override public boolean teleport(Entity entity, TeleportCause cause) { return false; } + @Override public void setFireTicks(int ticks) { } + @Override public boolean setPassenger(Entity entity) { return false; } + @Override public boolean eject() { return false; } + @Override public boolean leaveVehicle() { return false; } + @Override public void playEffect(EntityEffect effect) { } + + // Methods from Slime + @Override public void setSize(int size) { } +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R1/CraftNMSWitherSkull.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R1/CraftNMSWitherSkull.java new file mode 100644 index 00000000..980a40f6 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R1/CraftNMSWitherSkull.java @@ -0,0 +1,46 @@ +package com.gmail.filoghost.holographicdisplays.nms.v1_7_R1; + +import org.bukkit.EntityEffect; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_7_R1.CraftServer; +import org.bukkit.craftbukkit.v1_7_R1.entity.CraftWitherSkull; +import org.bukkit.entity.Entity; +import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; +import org.bukkit.util.Vector; + +public class CraftNMSWitherSkull extends CraftWitherSkull { + + public CraftNMSWitherSkull(CraftServer server, EntityNMSWitherSkull entity) { + super(server, entity); + } + + // Disallow all the bukkit methods. + + @Override + public void remove() { + // Cannot be removed, this is the most important to override. + } + + // Method from Fireball + @Override public void setDirection(Vector dir) { } + + // Method from Projectile + @Override public void setBounce(boolean bounce) { } + + // Methods from Explosive + @Override public void setYield(float yield) { } + @Override public void setIsIncendiary(boolean fire) { } + + // Methods from Entity + @Override public void setVelocity(Vector vel) { } + @Override public boolean teleport(Location loc) { return false; } + @Override public boolean teleport(Entity entity) { return false; } + @Override public boolean teleport(Location loc, TeleportCause cause) { return false; } + @Override public boolean teleport(Entity entity, TeleportCause cause) { return false; } + @Override public void setFireTicks(int ticks) { } + @Override public boolean setPassenger(Entity entity) { return false; } + @Override public boolean eject() { return false; } + @Override public boolean leaveVehicle() { return false; } + @Override public void playEffect(EntityEffect effect) { } + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R1/EntityNMSHorse.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R1/EntityNMSHorse.java new file mode 100644 index 00000000..f8fb273d --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R1/EntityNMSHorse.java @@ -0,0 +1,156 @@ +package com.gmail.filoghost.holographicdisplays.nms.v1_7_R1; + +import net.minecraft.server.v1_7_R1.EntityHorse; +import net.minecraft.server.v1_7_R1.NBTTagCompound; +import net.minecraft.server.v1_7_R1.World; + +import org.bukkit.craftbukkit.v1_7_R1.entity.CraftEntity; + +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSHorse; +import com.gmail.filoghost.holographicdisplays.object.line.CraftHologramLine; + +public class EntityNMSHorse extends EntityHorse implements NMSHorse { + + private boolean lockTick; + private CraftHologramLine parentPiece; + + public EntityNMSHorse(World world, CraftHologramLine parentPiece) { + super(world); + super.ageLocked = true; + super.persistent = true; + super.boundingBox.a = 0.0; + super.boundingBox.b = 0.0; + super.boundingBox.c = 0.0; + super.boundingBox.d = 0.0; + super.boundingBox.e = 0.0; + super.boundingBox.f = 0.0; + a(0.0F, 0.0F); + setAge(-1700000); // This is a magic value. No one will see the real horse. + this.parentPiece = parentPiece; + } + + @Override + public void h() { + // Checks every 20 ticks. + if (ticksLived % 20 == 0) { + // The horse dies without a vehicle. + if (this.vehicle == null) { + die(); + } + } + + if (!lockTick) { + super.h(); + } + } + + @Override + public void b(NBTTagCompound nbttagcompound) { + // Do not save NBT. + } + + @Override + public boolean c(NBTTagCompound nbttagcompound) { + // Do not save NBT. + return false; + } + + @Override + public boolean d(NBTTagCompound nbttagcompound) { + // Do not save NBT. + return false; + } + + @Override + public void e(NBTTagCompound nbttagcompound) { + // Do not save NBT. + } + + @Override + public boolean isInvulnerable() { + /* + * The field Entity.invulnerable is private. + * It's only used while saving NBTTags, but since the entity would be killed + * on chunk unload, we prefer to override isInvulnerable(). + */ + return true; + } + + @Override + public void setCustomName(String customName) { + // Locks the custom name. + } + + @Override + public void setCustomNameVisible(boolean visible) { + // Locks the custom name. + } + + @Override + public void makeSound(String sound, float volume, float pitch) { + // Remove sounds. + } + + @Override + public void setLockTick(boolean lock) { + lockTick = lock; + } + + @Override + public void die() { + setLockTick(false); + super.die(); + } + + @Override + public void setCustomNameNMS(String name) { + if (name != null && name.length() > 300) { + name = name.substring(0, 300); + } + super.setCustomName(name); + super.setCustomNameVisible(name != null); + } + + @Override + public CraftEntity getBukkitEntity() { + if (super.bukkitEntity == null) { + this.bukkitEntity = new CraftNMSHorse(this.world.getServer(), this); + } + return this.bukkitEntity; + } + + @Override + public boolean isDeadNMS() { + return super.dead; + } + + @Override + public String getCustomNameNMS() { + return super.getCustomName(); + } + + @Override + public void killEntityNMS() { + die(); + } + + @Override + public void setLocationNMS(double x, double y, double z) { + super.setPosition(x, y, z); + } + + @Override + public int getIdNMS() { + return this.getId(); + } + + @Override + public CraftHologramLine getHologramLine() { + return parentPiece; + } + + @Override + public org.bukkit.entity.Entity getBukkitEntityNMS() { + return getBukkitEntity(); + } +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R1/EntityNMSItem.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R1/EntityNMSItem.java new file mode 100644 index 00000000..a47f6af5 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R1/EntityNMSItem.java @@ -0,0 +1,183 @@ +package com.gmail.filoghost.holographicdisplays.nms.v1_7_R1; + +import net.minecraft.server.v1_7_R1.Blocks; +import net.minecraft.server.v1_7_R1.EntityHuman; +import net.minecraft.server.v1_7_R1.EntityItem; +import net.minecraft.server.v1_7_R1.EntityPlayer; +import net.minecraft.server.v1_7_R1.ItemStack; +import net.minecraft.server.v1_7_R1.NBTTagCompound; +import net.minecraft.server.v1_7_R1.NBTTagList; +import net.minecraft.server.v1_7_R1.NBTTagString; +import net.minecraft.server.v1_7_R1.World; + +import org.bukkit.craftbukkit.v1_7_R1.entity.CraftEntity; +import org.bukkit.craftbukkit.v1_7_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; + +import com.gmail.filoghost.holographicdisplays.listener.MainListener; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSItem; +import com.gmail.filoghost.holographicdisplays.object.line.CraftItemLine; +import com.gmail.filoghost.holographicdisplays.util.ItemUtils; + +public class EntityNMSItem extends EntityItem implements NMSItem { + + private boolean lockTick; + private CraftItemLine parentPiece; + + public EntityNMSItem(World world, CraftItemLine piece) { + super(world); + super.pickupDelay = Integer.MAX_VALUE; + this.parentPiece = piece; + } + + @Override + public void h() { + // Checks every 20 ticks. + if (ticksLived % 20 == 0) { + // The item dies without a vehicle. + if (this.vehicle == null) { + die(); + } + } + + if (!lockTick) { + super.h(); + } + } + + @Override + public ItemStack getItemStack() { + // Dirty method to check if the icon is being picked up + StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace(); + if (stacktrace.length > 2 && stacktrace[2].getClassName().contains("EntityInsentient")) { + return null; // Try to pickup this, dear entity ignoring the pickupDelay! + } + + return super.getItemStack(); + } + + // Method called when a player is near. + @Override + public void b_(EntityHuman human) { + + if (parentPiece.getPickupHandler() != null && human instanceof EntityPlayer) { + MainListener.handleItemLinePickup((Player) human.getBukkitEntity(), parentPiece.getPickupHandler(), parentPiece.getParent()); + // It is never added to the inventory. + } + } + + @Override + public void b(NBTTagCompound nbttagcompound) { + // Do not save NBT. + } + + @Override + public boolean c(NBTTagCompound nbttagcompound) { + // Do not save NBT. + return false; + } + + @Override + public boolean d(NBTTagCompound nbttagcompound) { + // Do not save NBT. + return false; + } + + @Override + public void e(NBTTagCompound nbttagcompound) { + // Do not save NBT. + } + + @Override + public boolean isInvulnerable() { + /* + * The field Entity.invulnerable is private. + * It's only used while saving NBTTags, but since the entity would be killed + * on chunk unload, we prefer to override isInvulnerable(). + */ + return true; + } + + @Override + public void setLockTick(boolean lock) { + lockTick = lock; + } + + @Override + public void die() { + setLockTick(false); + super.die(); + } + + @Override + public CraftEntity getBukkitEntity() { + if (super.bukkitEntity == null) { + this.bukkitEntity = new CraftNMSItem(this.world.getServer(), this); + } + return this.bukkitEntity; + } + + @Override + public boolean isDeadNMS() { + return this.dead; + } + + @Override + public void killEntityNMS() { + die(); + } + + @Override + public void setLocationNMS(double x, double y, double z) { + super.setPosition(x, y, z); + } + + @Override + public void setItemStackNMS(org.bukkit.inventory.ItemStack stack) { + ItemStack newItem = CraftItemStack.asNMSCopy(stack); + + if (newItem == null) { + newItem = new ItemStack(Blocks.BEDROCK); + } + + if (newItem.tag == null) { + newItem.tag = new NBTTagCompound(); + } + NBTTagCompound display = newItem.tag.getCompound("display"); + + if (!newItem.tag.hasKey("display")) { + newItem.tag.set("display", display); + } + + NBTTagList tagList = new NBTTagList(); + tagList.add(new NBTTagString(ItemUtils.ANTISTACK_LORE)); // Antistack lore + + display.set("Lore", tagList); + newItem.count = 0; + setItemStack(newItem); + } + + @Override + public int getIdNMS() { + return this.getId(); + } + + @Override + public CraftItemLine getHologramLine() { + return parentPiece; + } + + @Override + public void allowPickup(boolean pickup) { + if (pickup) { + super.pickupDelay = 0; + } else { + super.pickupDelay = Integer.MAX_VALUE; + } + } + + @Override + public org.bukkit.entity.Entity getBukkitEntityNMS() { + return getBukkitEntity(); + } +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R1/EntityNMSSlime.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R1/EntityNMSSlime.java new file mode 100644 index 00000000..bf49ae53 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R1/EntityNMSSlime.java @@ -0,0 +1,144 @@ +package com.gmail.filoghost.holographicdisplays.nms.v1_7_R1; + +import net.minecraft.server.v1_7_R1.EntitySlime; +import net.minecraft.server.v1_7_R1.NBTTagCompound; +import net.minecraft.server.v1_7_R1.World; + +import org.bukkit.craftbukkit.v1_7_R1.entity.CraftEntity; + +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSSlime; +import com.gmail.filoghost.holographicdisplays.object.line.CraftHologramLine; +import com.gmail.filoghost.holographicdisplays.object.line.CraftTouchSlimeLine; + +public class EntityNMSSlime extends EntitySlime implements NMSSlime { + + private boolean lockTick; + private CraftTouchSlimeLine parentPiece; + + public EntityNMSSlime(World world, CraftTouchSlimeLine parentPiece) { + super(world); + super.persistent = true; + super.boundingBox.a = 0.0; + super.boundingBox.b = 0.0; + super.boundingBox.c = 0.0; + super.boundingBox.d = 0.0; + super.boundingBox.e = 0.0; + super.boundingBox.f = 0.0; + a(0.0F, 0.0F); + setSize(1); + setInvisible(true); + this.parentPiece = parentPiece; + } + + @Override + public void h() { + // Checks every 20 ticks. + if (ticksLived % 20 == 0) { + // The slime dies without a vehicle. + if (this.vehicle == null) { + die(); + } + } + + if (!lockTick) { + super.h(); + } + } + + @Override + public void b(NBTTagCompound nbttagcompound) { + // Do not save NBT. + } + + @Override + public boolean c(NBTTagCompound nbttagcompound) { + // Do not save NBT. + return false; + } + + @Override + public boolean d(NBTTagCompound nbttagcompound) { + // Do not save NBT. + return false; + } + + @Override + public void e(NBTTagCompound nbttagcompound) { + // Do not save NBT. + } + + @Override + public boolean isInvulnerable() { + /* + * The field Entity.invulnerable is private. + * It's only used while saving NBTTags, but since the entity would be killed + * on chunk unload, we prefer to override isInvulnerable(). + */ + return true; + } + + @Override + public void setCustomName(String customName) { + // Locks the custom name. + } + + @Override + public void setCustomNameVisible(boolean visible) { + // Locks the custom name. + } + + @Override + public void makeSound(String sound, float volume, float pitch) { + // Remove sounds. + } + + @Override + public void setLockTick(boolean lock) { + lockTick = lock; + } + + @Override + public void die() { + setLockTick(false); + super.die(); + } + + @Override + public CraftEntity getBukkitEntity() { + if (super.bukkitEntity == null) { + this.bukkitEntity = new CraftNMSSlime(this.world.getServer(), this); + } + return this.bukkitEntity; + } + + @Override + public boolean isDeadNMS() { + return super.dead; + } + + @Override + public void killEntityNMS() { + die(); + } + + @Override + public void setLocationNMS(double x, double y, double z) { + super.setPosition(x, y, z); + } + + @Override + public int getIdNMS() { + return this.getId(); + } + + @Override + public CraftHologramLine getHologramLine() { + return parentPiece; + } + + @Override + public org.bukkit.entity.Entity getBukkitEntityNMS() { + return getBukkitEntity(); + } + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R1/EntityNMSWitherSkull.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R1/EntityNMSWitherSkull.java new file mode 100644 index 00000000..b9afda82 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R1/EntityNMSWitherSkull.java @@ -0,0 +1,166 @@ +package com.gmail.filoghost.holographicdisplays.nms.v1_7_R1; + +import org.bukkit.craftbukkit.v1_7_R1.entity.CraftEntity; + +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSEntityBase; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSWitherSkull; +import com.gmail.filoghost.holographicdisplays.object.line.CraftHologramLine; +import com.gmail.filoghost.holographicdisplays.util.Utils; + +import net.minecraft.server.v1_7_R1.Entity; +import net.minecraft.server.v1_7_R1.EntityWitherSkull; +import net.minecraft.server.v1_7_R1.NBTTagCompound; +import net.minecraft.server.v1_7_R1.PacketPlayOutEntityTeleport; +import net.minecraft.server.v1_7_R1.World; +import net.minecraft.server.v1_7_R1.EntityPlayer; + +public class EntityNMSWitherSkull extends EntityWitherSkull implements NMSWitherSkull { + + private boolean lockTick; + private CraftHologramLine parentPiece; + + public EntityNMSWitherSkull(World world, CraftHologramLine parentPiece) { + super(world); + super.motX = 0.0; + super.motY = 0.0; + super.motZ = 0.0; + super.dirX = 0.0; + super.dirY = 0.0; + super.dirZ = 0.0; + super.boundingBox.a = 0.0; + super.boundingBox.b = 0.0; + super.boundingBox.c = 0.0; + super.boundingBox.d = 0.0; + super.boundingBox.e = 0.0; + super.boundingBox.f = 0.0; + a(0.0F, 0.0F); + this.parentPiece = parentPiece; + } + + @Override + public void b(NBTTagCompound nbttagcompound) { + // Do not save NBT. + } + + @Override + public boolean c(NBTTagCompound nbttagcompound) { + // Do not save NBT. + return false; + } + + @Override + public boolean d(NBTTagCompound nbttagcompound) { + // Do not save NBT. + return false; + } + + @Override + public void e(NBTTagCompound nbttagcompound) { + // Do not save NBT. + } + + @Override + public boolean isInvulnerable() { + /* + * The field Entity.invulnerable is private. + * It's only used while saving NBTTags, but since the entity would be killed + * on chunk unload, we prefer to override isInvulnerable(). + */ + return true; + } + + @Override + public int getId() { + + StackTraceElement[] elements = Thread.currentThread().getStackTrace(); + if (elements.length > 2 && elements[2] != null && elements[2].getFileName().equals("EntityTrackerEntry.java") && elements[2].getLineNumber() > 134 && elements[2].getLineNumber() < 144) { + // Then this method is being called when creating a new packet, we return a fake ID! + return -1; + } + + return super.getId(); + } + + @Override + public void h() { + if (!lockTick) { + super.h(); + } + } + + @Override + public void makeSound(String sound, float f1, float f2) { + // Remove sounds. + } + + @Override + public void setLockTick(boolean lock) { + lockTick = lock; + } + + @Override + public void die() { + setLockTick(false); + super.die(); + } + + @Override + public CraftEntity getBukkitEntity() { + if (super.bukkitEntity == null) { + this.bukkitEntity = new CraftNMSWitherSkull(this.world.getServer(), this); + } + return this.bukkitEntity; + } + + @Override + public void killEntityNMS() { + die(); + } + + @Override + public void setLocationNMS(double x, double y, double z) { + super.setPosition(x, y, z); + + // Send a packet near to update the position. + PacketPlayOutEntityTeleport teleportPacket = new PacketPlayOutEntityTeleport(this); + + for (Object obj : this.world.players) { + if (obj instanceof EntityPlayer) { + EntityPlayer nmsPlayer = (EntityPlayer) obj; + + double distanceSquared = Utils.square(nmsPlayer.locX - this.locX) + Utils.square(nmsPlayer.locZ - this.locZ); + if (distanceSquared < 8192 && nmsPlayer.playerConnection != null) { + nmsPlayer.playerConnection.sendPacket(teleportPacket); + } + } + } + } + + @Override + public boolean isDeadNMS() { + return this.dead; + } + + @Override + public void setPassengerNMS(NMSEntityBase passenger) { + if (passenger instanceof Entity) { + ((Entity) passenger).setPassengerOf(this); + } + } + + @Override + public int getIdNMS() { + return this.getId(); + } + + @Override + public CraftHologramLine getHologramLine() { + return parentPiece; + } + + @Override + public org.bukkit.entity.Entity getBukkitEntityNMS() { + return getBukkitEntity(); + } + +} \ No newline at end of file diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R1/FancyMessageImpl.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R1/FancyMessageImpl.java new file mode 100644 index 00000000..c325669e --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R1/FancyMessageImpl.java @@ -0,0 +1,125 @@ +package com.gmail.filoghost.holographicdisplays.nms.v1_7_R1; + +import java.io.IOException; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.List; + +import net.minecraft.server.v1_7_R1.ChatSerializer; +import net.minecraft.server.v1_7_R1.PacketPlayOutChat; + +import org.bukkit.ChatColor; +import org.bukkit.craftbukkit.libs.com.google.gson.stream.JsonWriter; +import org.bukkit.craftbukkit.v1_7_R1.entity.CraftPlayer; +import org.bukkit.entity.Player; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.FancyMessage; + +public class FancyMessageImpl implements FancyMessage { + + private List messageParts; + + public FancyMessageImpl(String firstPartText) { + messageParts = new ArrayList(); + messageParts.add(new MessagePart(firstPartText)); + } + + @Override + public FancyMessageImpl color(ChatColor color) { + if (!color.isColor()) { + throw new IllegalArgumentException(color.name() + " is not a color"); + } + latest().color = color; + return this; + } + + @Override + public FancyMessageImpl style(ChatColor... styles) { + for (ChatColor style : styles) { + if (!style.isFormat()) { + throw new IllegalArgumentException(style.name() + " is not a style"); + } + } + latest().styles = styles; + return this; + } + + @Override + public FancyMessageImpl file(String path) { + onClick("open_file", path); + return this; + } + + @Override + public FancyMessageImpl link(String url) { + onClick("open_url", url); + return this; + } + + @Override + public FancyMessageImpl suggest(String command) { + onClick("suggest_command", command); + return this; + } + + @Override + public FancyMessageImpl command(String command) { + onClick("run_command", command); + return this; + } + + @Override + public FancyMessageImpl tooltip(String text) { + onHover("show_text", text); + return this; + } + + @Override + public FancyMessageImpl then(Object obj) { + messageParts.add(new MessagePart(obj.toString())); + return this; + } + + @Override + public String toJSONString() { + StringWriter stringWriter = new StringWriter(); + JsonWriter json = new JsonWriter(stringWriter); + + try { + if (messageParts.size() == 1) { + latest().writeJson(json); + } else { + json.beginObject().name("text").value("").name("extra").beginArray(); + for (MessagePart part : messageParts) { + part.writeJson(json); + } + json.endArray().endObject(); + } + + } catch (IOException e) { + throw new RuntimeException("invalid message"); + } + return stringWriter.toString(); + } + + @Override + public void send(Player player){ + ((CraftPlayer) player).getHandle().playerConnection.sendPacket(new PacketPlayOutChat(ChatSerializer.a(toJSONString()))); + } + + private MessagePart latest() { + return messageParts.get(messageParts.size() - 1); + } + + private void onClick(String name, String data) { + MessagePart latest = latest(); + latest.clickActionName = name; + latest.clickActionData = data; + } + + private void onHover(String name, String data) { + MessagePart latest = latest(); + latest.hoverActionName = name; + latest.hoverActionData = data; + } + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R1/NmsManagerImpl.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R1/NmsManagerImpl.java new file mode 100644 index 00000000..9bd93367 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R1/NmsManagerImpl.java @@ -0,0 +1,127 @@ +package com.gmail.filoghost.holographicdisplays.nms.v1_7_R1; + +import net.minecraft.server.v1_7_R1.Entity; +import net.minecraft.server.v1_7_R1.EntityTypes; +import net.minecraft.server.v1_7_R1.WorldServer; + +import org.apache.commons.lang.NotImplementedException; +import org.bukkit.World; +import org.bukkit.craftbukkit.v1_7_R1.CraftWorld; +import org.bukkit.craftbukkit.v1_7_R1.entity.CraftEntity; +import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason; +import org.bukkit.inventory.ItemStack; + +import com.gmail.filoghost.holographicdisplays.nms.interfaces.FancyMessage; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.NMSManager; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSArmorStand; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSEntityBase; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSHorse; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSItem; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSWitherSkull; +import com.gmail.filoghost.holographicdisplays.object.line.CraftHologramLine; +import com.gmail.filoghost.holographicdisplays.object.line.CraftItemLine; +import com.gmail.filoghost.holographicdisplays.object.line.CraftTouchSlimeLine; +import com.gmail.filoghost.holographicdisplays.util.DebugHandler; +import com.gmail.filoghost.holographicdisplays.util.ReflectionUtils; +import com.gmail.filoghost.holographicdisplays.util.VersionUtils; + +public class NmsManagerImpl implements NMSManager { + + @Override + public void registerCustomEntities() throws Exception { + registerCustomEntity(EntityNMSHorse.class, "EntityHorse", 100); + registerCustomEntity(EntityNMSWitherSkull.class, "WitherSkull", 19); + registerCustomEntity(EntityNMSItem.class, "Item", 1); + registerCustomEntity(EntityNMSSlime.class, "Slime", 55); + } + + @SuppressWarnings("rawtypes") + public void registerCustomEntity(Class entityClass, String name, int id) throws Exception { + if (VersionUtils.isMCPCOrCauldron()) { + // MCPC+ / Cauldron entity registration. + Class entityTypesClass = Class.forName("net.minecraft.server.v1_7_R1.EntityTypes"); + ReflectionUtils.putInPrivateStaticMap(entityTypesClass, "field_75626_c", entityClass, name); + ReflectionUtils.putInPrivateStaticMap(entityTypesClass, "field_75624_e", entityClass, Integer.valueOf(id)); + } else { + // Normal entity registration. + ReflectionUtils.putInPrivateStaticMap(EntityTypes.class, "d", entityClass, name); + ReflectionUtils.putInPrivateStaticMap(EntityTypes.class, "f", entityClass, Integer.valueOf(id)); + } + } + + @Override + public NMSHorse spawnNMSHorse(org.bukkit.World world, double x, double y, double z, CraftHologramLine parentPiece) { + WorldServer nmsWorld = ((CraftWorld) world).getHandle(); + EntityNMSHorse invisibleHorse = new EntityNMSHorse(nmsWorld, parentPiece); + invisibleHorse.setLocationNMS(x, y, z); + if (!nmsWorld.addEntity(invisibleHorse, SpawnReason.CUSTOM)) { + DebugHandler.handleSpawnFail(parentPiece); + } + return invisibleHorse; + } + + @Override + public NMSWitherSkull spawnNMSWitherSkull(org.bukkit.World bukkitWorld, double x, double y, double z, CraftHologramLine parentPiece) { + WorldServer nmsWorld = ((CraftWorld) bukkitWorld).getHandle(); + EntityNMSWitherSkull staticWitherSkull = new EntityNMSWitherSkull(nmsWorld, parentPiece); + staticWitherSkull.setLocationNMS(x, y, z); + if (!nmsWorld.addEntity(staticWitherSkull, SpawnReason.CUSTOM)) { + DebugHandler.handleSpawnFail(parentPiece); + } + return staticWitherSkull; + } + + @Override + public NMSItem spawnNMSItem(org.bukkit.World bukkitWorld, double x, double y, double z, CraftItemLine parentPiece, ItemStack stack) { + WorldServer nmsWorld = ((CraftWorld) bukkitWorld).getHandle(); + EntityNMSItem customItem = new EntityNMSItem(nmsWorld, parentPiece); + customItem.setLocationNMS(x, y, z); + customItem.setItemStackNMS(stack); + if (!nmsWorld.addEntity(customItem, SpawnReason.CUSTOM)) { + DebugHandler.handleSpawnFail(parentPiece); + } + return customItem; + } + + @Override + public EntityNMSSlime spawnNMSSlime(org.bukkit.World bukkitWorld, double x, double y, double z, CraftTouchSlimeLine parentPiece) { + WorldServer nmsWorld = ((CraftWorld) bukkitWorld).getHandle(); + EntityNMSSlime touchSlime = new EntityNMSSlime(nmsWorld, parentPiece); + touchSlime.setLocationNMS(x, y, z); + if (!nmsWorld.addEntity(touchSlime, SpawnReason.CUSTOM)) { + DebugHandler.handleSpawnFail(parentPiece); + } + return touchSlime; + } + + @Override + public boolean isNMSEntityBase(org.bukkit.entity.Entity bukkitEntity) { + return ((CraftEntity) bukkitEntity).getHandle() instanceof NMSEntityBase; + } + + @Override + public NMSEntityBase getNMSEntityBase(org.bukkit.entity.Entity bukkitEntity) { + + Entity nmsEntity = ((CraftEntity) bukkitEntity).getHandle(); + if (nmsEntity instanceof NMSEntityBase) { + return ((NMSEntityBase) nmsEntity); + } + + return null; + } + + @Override + public FancyMessage newFancyMessage(String text) { + return new FancyMessageImpl(text); + } + + @Override + public boolean hasChatHoverFeature() { + return true; + } + + @Override + public NMSArmorStand spawnNMSArmorStand(World world, double x, double y, double z, CraftHologramLine parentPiece) { + throw new NotImplementedException("Method can only be used on 1.8 or higher"); + } +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R2/CraftNMSHorse.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R2/CraftNMSHorse.java new file mode 100644 index 00000000..c6a6b628 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R2/CraftNMSHorse.java @@ -0,0 +1,64 @@ +package com.gmail.filoghost.holographicdisplays.nms.v1_7_R2; + +import java.util.Collection; + +import org.bukkit.EntityEffect; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_7_R2.CraftServer; +import org.bukkit.craftbukkit.v1_7_R2.entity.CraftHorse; +import org.bukkit.entity.AnimalTamer; +import org.bukkit.entity.Entity; +import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; +import org.bukkit.potion.PotionEffect; +import org.bukkit.util.Vector; + +public class CraftNMSHorse extends CraftHorse { + + public CraftNMSHorse(CraftServer server, EntityNMSHorse entity) { + super(server, entity); + } + + // Disallow all the bukkit methods. + + @Override + public void remove() { + // Cannot be removed, this is the most important to override. + } + + // Methods from Horse class + @Override public void setVariant(Variant variant) { } + @Override public void setColor(Color color) { } + @Override public void setStyle(Style style) { } + @Override public void setCarryingChest(boolean chest) { } + @Override public void setDomestication(int domestication) { } + @Override public void setJumpStrength(double jump) { } + + // Methods form Ageable class + @Override public void setAge(int age) { } + @Override public void setAgeLock(boolean lock) { } + @Override public void setBreed(boolean breed) { } + @Override public void setAdult() { } + @Override public void setBaby() { } + + // Methods from LivingEntity class + @Override public boolean addPotionEffect(PotionEffect effect) { return false; } + @Override public boolean addPotionEffect(PotionEffect effect, boolean param) { return false; } + @Override public boolean addPotionEffects(Collection effects) { return false; } + @Override public void setRemoveWhenFarAway(boolean remove) { } + + // Methods from Entity + @Override public void setVelocity(Vector vel) { } + @Override public boolean teleport(Location loc) { return false; } + @Override public boolean teleport(Entity entity) { return false; } + @Override public boolean teleport(Location loc, TeleportCause cause) { return false; } + @Override public boolean teleport(Entity entity, TeleportCause cause) { return false; } + @Override public void setFireTicks(int ticks) { } + @Override public boolean setPassenger(Entity entity) { return false; } + @Override public boolean eject() { return false; } + @Override public boolean leaveVehicle() { return false; } + @Override public void playEffect(EntityEffect effect) { } + + // Methods from Tameable + @Override public void setTamed(boolean tame) { } + @Override public void setOwner(AnimalTamer owner) { } +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R2/CraftNMSItem.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R2/CraftNMSItem.java new file mode 100644 index 00000000..83518ac9 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R2/CraftNMSItem.java @@ -0,0 +1,40 @@ +package com.gmail.filoghost.holographicdisplays.nms.v1_7_R2; + +import org.bukkit.EntityEffect; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_7_R2.CraftServer; +import org.bukkit.craftbukkit.v1_7_R2.entity.CraftItem; +import org.bukkit.entity.Entity; +import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; +import org.bukkit.inventory.ItemStack; +import org.bukkit.util.Vector; + +public class CraftNMSItem extends CraftItem { + + public CraftNMSItem(CraftServer server, EntityNMSItem entity) { + super(server, entity); + } + + // Disallow all the bukkit methods. + + @Override + public void remove() { + // Cannot be removed, this is the most important to override. + } + + // Methods from Entity + @Override public void setVelocity(Vector vel) { } + @Override public boolean teleport(Location loc) { return false; } + @Override public boolean teleport(Entity entity) { return false; } + @Override public boolean teleport(Location loc, TeleportCause cause) { return false; } + @Override public boolean teleport(Entity entity, TeleportCause cause) { return false; } + @Override public void setFireTicks(int ticks) { } + @Override public boolean setPassenger(Entity entity) { return false; } + @Override public boolean eject() { return false; } + @Override public boolean leaveVehicle() { return false; } + @Override public void playEffect(EntityEffect effect) { } + + // Methods from Item + @Override public void setItemStack(ItemStack stack) { } + @Override public void setPickupDelay(int delay) { } +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R2/CraftNMSSlime.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R2/CraftNMSSlime.java new file mode 100644 index 00000000..66a7b064 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R2/CraftNMSSlime.java @@ -0,0 +1,47 @@ +package com.gmail.filoghost.holographicdisplays.nms.v1_7_R2; + +import java.util.Collection; + +import org.bukkit.EntityEffect; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_7_R2.CraftServer; +import org.bukkit.craftbukkit.v1_7_R2.entity.CraftSlime; +import org.bukkit.entity.Entity; +import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; +import org.bukkit.potion.PotionEffect; +import org.bukkit.util.Vector; + +public class CraftNMSSlime extends CraftSlime { + + public CraftNMSSlime(CraftServer server, EntityNMSSlime entity) { + super(server, entity); + } + + // Disallow all the bukkit methods. + + @Override + public void remove() { + // Cannot be removed, this is the most important to override. + } + + // Methods from LivingEntity class + @Override public boolean addPotionEffect(PotionEffect effect) { return false; } + @Override public boolean addPotionEffect(PotionEffect effect, boolean param) { return false; } + @Override public boolean addPotionEffects(Collection effects) { return false; } + @Override public void setRemoveWhenFarAway(boolean remove) { } + + // Methods from Entity + @Override public void setVelocity(Vector vel) { } + @Override public boolean teleport(Location loc) { return false; } + @Override public boolean teleport(Entity entity) { return false; } + @Override public boolean teleport(Location loc, TeleportCause cause) { return false; } + @Override public boolean teleport(Entity entity, TeleportCause cause) { return false; } + @Override public void setFireTicks(int ticks) { } + @Override public boolean setPassenger(Entity entity) { return false; } + @Override public boolean eject() { return false; } + @Override public boolean leaveVehicle() { return false; } + @Override public void playEffect(EntityEffect effect) { } + + // Methods from Slime + @Override public void setSize(int size) { } +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R2/CraftNMSWitherSkull.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R2/CraftNMSWitherSkull.java new file mode 100644 index 00000000..d66ee101 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R2/CraftNMSWitherSkull.java @@ -0,0 +1,46 @@ +package com.gmail.filoghost.holographicdisplays.nms.v1_7_R2; + +import org.bukkit.EntityEffect; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_7_R2.CraftServer; +import org.bukkit.craftbukkit.v1_7_R2.entity.CraftWitherSkull; +import org.bukkit.entity.Entity; +import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; +import org.bukkit.util.Vector; + +public class CraftNMSWitherSkull extends CraftWitherSkull { + + public CraftNMSWitherSkull(CraftServer server, EntityNMSWitherSkull entity) { + super(server, entity); + } + + // Disallow all the bukkit methods. + + @Override + public void remove() { + // Cannot be removed, this is the most important to override. + } + + // Method from Fireball + @Override public void setDirection(Vector dir) { } + + // Method from Projectile + @Override public void setBounce(boolean bounce) { } + + // Methods from Explosive + @Override public void setYield(float yield) { } + @Override public void setIsIncendiary(boolean fire) { } + + // Methods from Entity + @Override public void setVelocity(Vector vel) { } + @Override public boolean teleport(Location loc) { return false; } + @Override public boolean teleport(Entity entity) { return false; } + @Override public boolean teleport(Location loc, TeleportCause cause) { return false; } + @Override public boolean teleport(Entity entity, TeleportCause cause) { return false; } + @Override public void setFireTicks(int ticks) { } + @Override public boolean setPassenger(Entity entity) { return false; } + @Override public boolean eject() { return false; } + @Override public boolean leaveVehicle() { return false; } + @Override public void playEffect(EntityEffect effect) { } + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R2/EntityNMSHorse.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R2/EntityNMSHorse.java new file mode 100644 index 00000000..f47da365 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R2/EntityNMSHorse.java @@ -0,0 +1,157 @@ +package com.gmail.filoghost.holographicdisplays.nms.v1_7_R2; + +import net.minecraft.server.v1_7_R2.EntityHorse; +import net.minecraft.server.v1_7_R2.NBTTagCompound; +import net.minecraft.server.v1_7_R2.World; + +import org.bukkit.craftbukkit.v1_7_R2.entity.CraftEntity; + +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSHorse; +import com.gmail.filoghost.holographicdisplays.object.line.CraftHologramLine; + +public class EntityNMSHorse extends EntityHorse implements NMSHorse { + + private boolean lockTick; + private CraftHologramLine parentPiece; + + public EntityNMSHorse(World world, CraftHologramLine parentPiece) { + super(world); + super.ageLocked = true; + super.persistent = true; + super.boundingBox.a = 0.0; + super.boundingBox.b = 0.0; + super.boundingBox.c = 0.0; + super.boundingBox.d = 0.0; + super.boundingBox.e = 0.0; + super.boundingBox.f = 0.0; + a(0.0F, 0.0F); + setAge(-1700000); // This is a magic value. No one will see the real horse. + this.parentPiece = parentPiece; + } + + @Override + public void h() { + // Checks every 20 ticks. + if (ticksLived % 20 == 0) { + // The horse dies without a vehicle. + if (this.vehicle == null) { + die(); + } + } + + if (!lockTick) { + super.h(); + } + } + + @Override + public void b(NBTTagCompound nbttagcompound) { + // Do not save NBT. + } + + @Override + public boolean c(NBTTagCompound nbttagcompound) { + // Do not save NBT. + return false; + } + + @Override + public boolean d(NBTTagCompound nbttagcompound) { + // Do not save NBT. + return false; + } + + @Override + public void e(NBTTagCompound nbttagcompound) { + // Do not save NBT. + } + + @Override + public boolean isInvulnerable() { + /* + * The field Entity.invulnerable is private. + * It's only used while saving NBTTags, but since the entity would be killed + * on chunk unload, we prefer to override isInvulnerable(). + */ + return true; + } + + @Override + public void setCustomName(String customName) { + // Locks the custom name. + } + + @Override + public void setCustomNameVisible(boolean visible) { + // Locks the custom name. + } + + @Override + public void makeSound(String sound, float volume, float pitch) { + // Remove sounds. + } + + @Override + public void setLockTick(boolean lock) { + lockTick = lock; + } + + @Override + public void die() { + setLockTick(false); + super.die(); + } + + @Override + public void setCustomNameNMS(String name) { + if (name != null && name.length() > 300) { + name = name.substring(0, 300); + } + super.setCustomName(name); + super.setCustomNameVisible(name != null); + } + + @Override + public CraftEntity getBukkitEntity() { + if (super.bukkitEntity == null) { + this.bukkitEntity = new CraftNMSHorse(this.world.getServer(), this); + } + return this.bukkitEntity; + } + + @Override + public boolean isDeadNMS() { + return super.dead; + } + + @Override + public String getCustomNameNMS() { + return super.getCustomName(); + } + + @Override + public void killEntityNMS() { + die(); + } + + @Override + public void setLocationNMS(double x, double y, double z) { + super.setPosition(x, y, z); + } + + @Override + public int getIdNMS() { + return this.getId(); + } + + @Override + public CraftHologramLine getHologramLine() { + return parentPiece; + } + + @Override + public org.bukkit.entity.Entity getBukkitEntityNMS() { + return getBukkitEntity(); + } + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R2/EntityNMSItem.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R2/EntityNMSItem.java new file mode 100644 index 00000000..e7be7b52 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R2/EntityNMSItem.java @@ -0,0 +1,183 @@ +package com.gmail.filoghost.holographicdisplays.nms.v1_7_R2; + +import net.minecraft.server.v1_7_R2.Blocks; +import net.minecraft.server.v1_7_R2.EntityHuman; +import net.minecraft.server.v1_7_R2.EntityItem; +import net.minecraft.server.v1_7_R2.EntityPlayer; +import net.minecraft.server.v1_7_R2.ItemStack; +import net.minecraft.server.v1_7_R2.NBTTagCompound; +import net.minecraft.server.v1_7_R2.NBTTagList; +import net.minecraft.server.v1_7_R2.NBTTagString; +import net.minecraft.server.v1_7_R2.World; + +import org.bukkit.craftbukkit.v1_7_R2.entity.CraftEntity; +import org.bukkit.craftbukkit.v1_7_R2.inventory.CraftItemStack; +import org.bukkit.entity.Player; + +import com.gmail.filoghost.holographicdisplays.listener.MainListener; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSItem; +import com.gmail.filoghost.holographicdisplays.object.line.CraftItemLine; +import com.gmail.filoghost.holographicdisplays.util.ItemUtils; + +public class EntityNMSItem extends EntityItem implements NMSItem { + + private boolean lockTick; + private CraftItemLine parentPiece; + + public EntityNMSItem(World world, CraftItemLine piece) { + super(world); + super.pickupDelay = Integer.MAX_VALUE; + this.parentPiece = piece; + } + + @Override + public void h() { + // Checks every 20 ticks. + if (ticksLived % 20 == 0) { + // The item dies without a vehicle. + if (this.vehicle == null) { + die(); + } + } + + if (!lockTick) { + super.h(); + } + } + + @Override + public ItemStack getItemStack() { + // Dirty method to check if the icon is being picked up + StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace(); + if (stacktrace.length > 2 && stacktrace[2].getClassName().contains("EntityInsentient")) { + return null; // Try to pickup this, dear entity ignoring the pickupDelay! + } + + return super.getItemStack(); + } + + // Method called when a player is near. + @Override + public void b_(EntityHuman human) { + + if (parentPiece.getPickupHandler() != null && human instanceof EntityPlayer) { + MainListener.handleItemLinePickup((Player) human.getBukkitEntity(), parentPiece.getPickupHandler(), parentPiece.getParent()); + // It is never added to the inventory. + } + } + + @Override + public void b(NBTTagCompound nbttagcompound) { + // Do not save NBT. + } + + @Override + public boolean c(NBTTagCompound nbttagcompound) { + // Do not save NBT. + return false; + } + + @Override + public boolean d(NBTTagCompound nbttagcompound) { + // Do not save NBT. + return false; + } + + @Override + public void e(NBTTagCompound nbttagcompound) { + // Do not save NBT. + } + + @Override + public boolean isInvulnerable() { + /* + * The field Entity.invulnerable is private. + * It's only used while saving NBTTags, but since the entity would be killed + * on chunk unload, we prefer to override isInvulnerable(). + */ + return true; + } + + @Override + public void setLockTick(boolean lock) { + lockTick = lock; + } + + @Override + public void die() { + setLockTick(false); + super.die(); + } + + @Override + public CraftEntity getBukkitEntity() { + if (super.bukkitEntity == null) { + this.bukkitEntity = new CraftNMSItem(this.world.getServer(), this); + } + return this.bukkitEntity; + } + + @Override + public boolean isDeadNMS() { + return this.dead; + } + + @Override + public void killEntityNMS() { + die(); + } + + @Override + public void setLocationNMS(double x, double y, double z) { + super.setPosition(x, y, z); + } + + @Override + public void setItemStackNMS(org.bukkit.inventory.ItemStack stack) { + ItemStack newItem = CraftItemStack.asNMSCopy(stack); + + if (newItem == null) { + newItem = new ItemStack(Blocks.BEDROCK); + } + + if (newItem.tag == null) { + newItem.tag = new NBTTagCompound(); + } + NBTTagCompound display = newItem.tag.getCompound("display"); + + if (!newItem.tag.hasKey("display")) { + newItem.tag.set("display", display); + } + + NBTTagList tagList = new NBTTagList(); + tagList.add(new NBTTagString(ItemUtils.ANTISTACK_LORE)); // Antistack lore + + display.set("Lore", tagList); + newItem.count = 0; + setItemStack(newItem); + } + + @Override + public int getIdNMS() { + return this.getId(); + } + + @Override + public CraftItemLine getHologramLine() { + return parentPiece; + } + + @Override + public void allowPickup(boolean pickup) { + if (pickup) { + super.pickupDelay = 0; + } else { + super.pickupDelay = Integer.MAX_VALUE; + } + } + + @Override + public org.bukkit.entity.Entity getBukkitEntityNMS() { + return getBukkitEntity(); + } +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R2/EntityNMSSlime.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R2/EntityNMSSlime.java new file mode 100644 index 00000000..effac0e0 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R2/EntityNMSSlime.java @@ -0,0 +1,144 @@ +package com.gmail.filoghost.holographicdisplays.nms.v1_7_R2; + +import net.minecraft.server.v1_7_R2.EntitySlime; +import net.minecraft.server.v1_7_R2.NBTTagCompound; +import net.minecraft.server.v1_7_R2.World; + +import org.bukkit.craftbukkit.v1_7_R2.entity.CraftEntity; + +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSSlime; +import com.gmail.filoghost.holographicdisplays.object.line.CraftHologramLine; +import com.gmail.filoghost.holographicdisplays.object.line.CraftTouchSlimeLine; + +public class EntityNMSSlime extends EntitySlime implements NMSSlime { + + private boolean lockTick; + private CraftTouchSlimeLine parentPiece; + + public EntityNMSSlime(World world, CraftTouchSlimeLine parentPiece) { + super(world); + super.persistent = true; + super.boundingBox.a = 0.0; + super.boundingBox.b = 0.0; + super.boundingBox.c = 0.0; + super.boundingBox.d = 0.0; + super.boundingBox.e = 0.0; + super.boundingBox.f = 0.0; + a(0.0F, 0.0F); + setSize(1); + setInvisible(true); + this.parentPiece = parentPiece; + } + + @Override + public void h() { + // Checks every 20 ticks. + if (ticksLived % 20 == 0) { + // The slime dies without a vehicle. + if (this.vehicle == null) { + die(); + } + } + + if (!lockTick) { + super.h(); + } + } + + @Override + public void b(NBTTagCompound nbttagcompound) { + // Do not save NBT. + } + + @Override + public boolean c(NBTTagCompound nbttagcompound) { + // Do not save NBT. + return false; + } + + @Override + public boolean d(NBTTagCompound nbttagcompound) { + // Do not save NBT. + return false; + } + + @Override + public void e(NBTTagCompound nbttagcompound) { + // Do not save NBT. + } + + @Override + public boolean isInvulnerable() { + /* + * The field Entity.invulnerable is private. + * It's only used while saving NBTTags, but since the entity would be killed + * on chunk unload, we prefer to override isInvulnerable(). + */ + return true; + } + + @Override + public void setCustomName(String customName) { + // Locks the custom name. + } + + @Override + public void setCustomNameVisible(boolean visible) { + // Locks the custom name. + } + + @Override + public void makeSound(String sound, float volume, float pitch) { + // Remove sounds. + } + + @Override + public void setLockTick(boolean lock) { + lockTick = lock; + } + + @Override + public void die() { + setLockTick(false); + super.die(); + } + + @Override + public CraftEntity getBukkitEntity() { + if (super.bukkitEntity == null) { + this.bukkitEntity = new CraftNMSSlime(this.world.getServer(), this); + } + return this.bukkitEntity; + } + + @Override + public boolean isDeadNMS() { + return super.dead; + } + + @Override + public void killEntityNMS() { + die(); + } + + @Override + public void setLocationNMS(double x, double y, double z) { + super.setPosition(x, y, z); + } + + @Override + public int getIdNMS() { + return this.getId(); + } + + @Override + public CraftHologramLine getHologramLine() { + return parentPiece; + } + + @Override + public org.bukkit.entity.Entity getBukkitEntityNMS() { + return getBukkitEntity(); + } + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R2/EntityNMSWitherSkull.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R2/EntityNMSWitherSkull.java new file mode 100644 index 00000000..28e58814 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R2/EntityNMSWitherSkull.java @@ -0,0 +1,168 @@ +package com.gmail.filoghost.holographicdisplays.nms.v1_7_R2; + +import net.minecraft.server.v1_7_R2.Entity; +import net.minecraft.server.v1_7_R2.EntityPlayer; +import net.minecraft.server.v1_7_R2.EntityWitherSkull; +import net.minecraft.server.v1_7_R2.NBTTagCompound; +import net.minecraft.server.v1_7_R2.PacketPlayOutEntityTeleport; +import net.minecraft.server.v1_7_R2.World; + +import org.bukkit.craftbukkit.v1_7_R2.entity.CraftEntity; + +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSEntityBase; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSWitherSkull; +import com.gmail.filoghost.holographicdisplays.object.line.CraftHologramLine; +import com.gmail.filoghost.holographicdisplays.util.Utils; + +public class EntityNMSWitherSkull extends EntityWitherSkull implements NMSWitherSkull { + + private boolean lockTick; + private CraftHologramLine parentPiece; + + public EntityNMSWitherSkull(World world, CraftHologramLine parentPiece) { + super(world); + super.motX = 0.0; + super.motY = 0.0; + super.motZ = 0.0; + super.dirX = 0.0; + super.dirY = 0.0; + super.dirZ = 0.0; + super.boundingBox.a = 0.0; + super.boundingBox.b = 0.0; + super.boundingBox.c = 0.0; + super.boundingBox.d = 0.0; + super.boundingBox.e = 0.0; + super.boundingBox.f = 0.0; + a(0.0F, 0.0F); + this.parentPiece = parentPiece; + } + + + @Override + public void b(NBTTagCompound nbttagcompound) { + // Do not save NBT. + } + + @Override + public boolean c(NBTTagCompound nbttagcompound) { + // Do not save NBT. + return false; + } + + @Override + public boolean d(NBTTagCompound nbttagcompound) { + // Do not save NBT. + return false; + } + + @Override + public void e(NBTTagCompound nbttagcompound) { + // Do not save NBT. + } + + + @Override + public boolean isInvulnerable() { + /* + * The field Entity.invulnerable is private. + * It's only used while saving NBTTags, but since the entity would be killed + * on chunk unload, we prefer to override isInvulnerable(). + */ + return true; + } + + @Override + public int getId() { + + StackTraceElement[] elements = Thread.currentThread().getStackTrace(); + if (elements.length > 2 && elements[2] != null && elements[2].getFileName().equals("EntityTrackerEntry.java") && elements[2].getLineNumber() > 134 && elements[2].getLineNumber() < 144) { + // Then this method is being called when creating a new packet, we return a fake ID! + return -1; + } + + return super.getId(); + } + + @Override + public void h() { + if (!lockTick) { + super.h(); + } + } + + @Override + public void makeSound(String sound, float f1, float f2) { + // Remove sounds. + } + + @Override + public void setLockTick(boolean lock) { + lockTick = lock; + } + + @Override + public void die() { + setLockTick(false); + super.die(); + } + + @Override + public CraftEntity getBukkitEntity() { + if (super.bukkitEntity == null) { + this.bukkitEntity = new CraftNMSWitherSkull(this.world.getServer(), this); + } + return this.bukkitEntity; + } + + @Override + public void killEntityNMS() { + die(); + } + + @Override + public void setLocationNMS(double x, double y, double z) { + super.setPosition(x, y, z); + + // Send a packet near to update the position. + PacketPlayOutEntityTeleport teleportPacket = new PacketPlayOutEntityTeleport(this); + + for (Object obj : this.world.players) { + if (obj instanceof EntityPlayer) { + EntityPlayer nmsPlayer = (EntityPlayer) obj; + + double distanceSquared = Utils.square(nmsPlayer.locX - this.locX) + Utils.square(nmsPlayer.locZ - this.locZ); + if (distanceSquared < 8192 && nmsPlayer.playerConnection != null) { + nmsPlayer.playerConnection.sendPacket(teleportPacket); + } + } + } + } + + @Override + public boolean isDeadNMS() { + return this.dead; + } + + @Override + public void setPassengerNMS(NMSEntityBase passenger) { + if (passenger instanceof Entity) { + ((Entity) passenger).setPassengerOf(this); + } + } + + @Override + public int getIdNMS() { + return this.getId(); + } + + @Override + public CraftHologramLine getHologramLine() { + return parentPiece; + } + + @Override + public org.bukkit.entity.Entity getBukkitEntityNMS() { + return getBukkitEntity(); + } + +} \ No newline at end of file diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R2/FancyMessageImpl.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R2/FancyMessageImpl.java new file mode 100644 index 00000000..dbfeae6a --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R2/FancyMessageImpl.java @@ -0,0 +1,125 @@ +package com.gmail.filoghost.holographicdisplays.nms.v1_7_R2; + +import java.io.IOException; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.List; + +import net.minecraft.server.v1_7_R2.ChatSerializer; +import net.minecraft.server.v1_7_R2.PacketPlayOutChat; + +import org.bukkit.ChatColor; +import org.bukkit.craftbukkit.libs.com.google.gson.stream.JsonWriter; +import org.bukkit.craftbukkit.v1_7_R2.entity.CraftPlayer; +import org.bukkit.entity.Player; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.FancyMessage; + +public class FancyMessageImpl implements FancyMessage { + + private List messageParts; + + public FancyMessageImpl(String firstPartText) { + messageParts = new ArrayList(); + messageParts.add(new MessagePart(firstPartText)); + } + + @Override + public FancyMessageImpl color(ChatColor color) { + if (!color.isColor()) { + throw new IllegalArgumentException(color.name() + " is not a color"); + } + latest().color = color; + return this; + } + + @Override + public FancyMessageImpl style(ChatColor... styles) { + for (ChatColor style : styles) { + if (!style.isFormat()) { + throw new IllegalArgumentException(style.name() + " is not a style"); + } + } + latest().styles = styles; + return this; + } + + @Override + public FancyMessageImpl file(String path) { + onClick("open_file", path); + return this; + } + + @Override + public FancyMessageImpl link(String url) { + onClick("open_url", url); + return this; + } + + @Override + public FancyMessageImpl suggest(String command) { + onClick("suggest_command", command); + return this; + } + + @Override + public FancyMessageImpl command(String command) { + onClick("run_command", command); + return this; + } + + @Override + public FancyMessageImpl tooltip(String text) { + onHover("show_text", text); + return this; + } + + @Override + public FancyMessageImpl then(Object obj) { + messageParts.add(new MessagePart(obj.toString())); + return this; + } + + @Override + public String toJSONString() { + StringWriter stringWriter = new StringWriter(); + JsonWriter json = new JsonWriter(stringWriter); + + try { + if (messageParts.size() == 1) { + latest().writeJson(json); + } else { + json.beginObject().name("text").value("").name("extra").beginArray(); + for (MessagePart part : messageParts) { + part.writeJson(json); + } + json.endArray().endObject(); + } + + } catch (IOException e) { + throw new RuntimeException("invalid message"); + } + return stringWriter.toString(); + } + + @Override + public void send(Player player){ + ((CraftPlayer) player).getHandle().playerConnection.sendPacket(new PacketPlayOutChat(ChatSerializer.a(toJSONString()))); + } + + private MessagePart latest() { + return messageParts.get(messageParts.size() - 1); + } + + private void onClick(String name, String data) { + MessagePart latest = latest(); + latest.clickActionName = name; + latest.clickActionData = data; + } + + private void onHover(String name, String data) { + MessagePart latest = latest(); + latest.hoverActionName = name; + latest.hoverActionData = data; + } + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R2/NmsManagerImpl.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R2/NmsManagerImpl.java new file mode 100644 index 00000000..e29fa1eb --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R2/NmsManagerImpl.java @@ -0,0 +1,127 @@ +package com.gmail.filoghost.holographicdisplays.nms.v1_7_R2; + +import net.minecraft.server.v1_7_R2.Entity; +import net.minecraft.server.v1_7_R2.EntityTypes; +import net.minecraft.server.v1_7_R2.WorldServer; + +import org.apache.commons.lang.NotImplementedException; +import org.bukkit.World; +import org.bukkit.craftbukkit.v1_7_R2.CraftWorld; +import org.bukkit.craftbukkit.v1_7_R2.entity.CraftEntity; +import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason; +import org.bukkit.inventory.ItemStack; + +import com.gmail.filoghost.holographicdisplays.nms.interfaces.FancyMessage; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.NMSManager; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSArmorStand; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSEntityBase; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSHorse; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSItem; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSWitherSkull; +import com.gmail.filoghost.holographicdisplays.object.line.CraftHologramLine; +import com.gmail.filoghost.holographicdisplays.object.line.CraftItemLine; +import com.gmail.filoghost.holographicdisplays.object.line.CraftTouchSlimeLine; +import com.gmail.filoghost.holographicdisplays.util.DebugHandler; +import com.gmail.filoghost.holographicdisplays.util.ReflectionUtils; +import com.gmail.filoghost.holographicdisplays.util.VersionUtils; + +public class NmsManagerImpl implements NMSManager { + + @Override + public void registerCustomEntities() throws Exception { + registerCustomEntity(EntityNMSHorse.class, "EntityHorse", 100); + registerCustomEntity(EntityNMSWitherSkull.class, "WitherSkull", 19); + registerCustomEntity(EntityNMSItem.class, "Item", 1); + registerCustomEntity(EntityNMSSlime.class, "Slime", 55); + } + + @SuppressWarnings("rawtypes") + public void registerCustomEntity(Class entityClass, String name, int id) throws Exception { + if (VersionUtils.isMCPCOrCauldron()) { + // MCPC+ / Cauldron entity registration. + Class entityTypesClass = Class.forName("net.minecraft.server.v1_7_R2.EntityTypes"); + ReflectionUtils.putInPrivateStaticMap(entityTypesClass, "field_75626_c", entityClass, name); + ReflectionUtils.putInPrivateStaticMap(entityTypesClass, "field_75624_e", entityClass, Integer.valueOf(id)); + } else { + // Normal entity registration. + ReflectionUtils.putInPrivateStaticMap(EntityTypes.class, "d", entityClass, name); + ReflectionUtils.putInPrivateStaticMap(EntityTypes.class, "f", entityClass, Integer.valueOf(id)); + } + } + + @Override + public NMSHorse spawnNMSHorse(org.bukkit.World world, double x, double y, double z, CraftHologramLine parentPiece) { + WorldServer nmsWorld = ((CraftWorld) world).getHandle(); + EntityNMSHorse invisibleHorse = new EntityNMSHorse(nmsWorld, parentPiece); + invisibleHorse.setLocationNMS(x, y, z); + if (!nmsWorld.addEntity(invisibleHorse, SpawnReason.CUSTOM)) { + DebugHandler.handleSpawnFail(parentPiece); + } + return invisibleHorse; + } + + @Override + public NMSWitherSkull spawnNMSWitherSkull(org.bukkit.World bukkitWorld, double x, double y, double z, CraftHologramLine parentPiece) { + WorldServer nmsWorld = ((CraftWorld) bukkitWorld).getHandle(); + EntityNMSWitherSkull staticWitherSkull = new EntityNMSWitherSkull(nmsWorld, parentPiece); + staticWitherSkull.setLocationNMS(x, y, z); + if (!nmsWorld.addEntity(staticWitherSkull, SpawnReason.CUSTOM)) { + DebugHandler.handleSpawnFail(parentPiece); + } + return staticWitherSkull; + } + + @Override + public NMSItem spawnNMSItem(org.bukkit.World bukkitWorld, double x, double y, double z, CraftItemLine parentPiece, ItemStack stack) { + WorldServer nmsWorld = ((CraftWorld) bukkitWorld).getHandle(); + EntityNMSItem customItem = new EntityNMSItem(nmsWorld, parentPiece); + customItem.setLocationNMS(x, y, z); + customItem.setItemStackNMS(stack); + if (!nmsWorld.addEntity(customItem, SpawnReason.CUSTOM)) { + DebugHandler.handleSpawnFail(parentPiece); + } + return customItem; + } + + @Override + public EntityNMSSlime spawnNMSSlime(org.bukkit.World bukkitWorld, double x, double y, double z, CraftTouchSlimeLine parentPiece) { + WorldServer nmsWorld = ((CraftWorld) bukkitWorld).getHandle(); + EntityNMSSlime touchSlime = new EntityNMSSlime(nmsWorld, parentPiece); + touchSlime.setLocationNMS(x, y, z); + if (!nmsWorld.addEntity(touchSlime, SpawnReason.CUSTOM)) { + DebugHandler.handleSpawnFail(parentPiece); + } + return touchSlime; + } + + @Override + public boolean isNMSEntityBase(org.bukkit.entity.Entity bukkitEntity) { + return ((CraftEntity) bukkitEntity).getHandle() instanceof NMSEntityBase; + } + + @Override + public NMSEntityBase getNMSEntityBase(org.bukkit.entity.Entity bukkitEntity) { + + Entity nmsEntity = ((CraftEntity) bukkitEntity).getHandle(); + if (nmsEntity instanceof NMSEntityBase) { + return ((NMSEntityBase) nmsEntity); + } + + return null; + } + + @Override + public FancyMessage newFancyMessage(String text) { + return new FancyMessageImpl(text); + } + + @Override + public boolean hasChatHoverFeature() { + return true; + } + + @Override + public NMSArmorStand spawnNMSArmorStand(World world, double x, double y, double z, CraftHologramLine parentPiece) { + throw new NotImplementedException("Method can only be used on 1.8 or higher"); + } +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R3/CraftNMSHorse.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R3/CraftNMSHorse.java new file mode 100644 index 00000000..65d4b9ed --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R3/CraftNMSHorse.java @@ -0,0 +1,64 @@ +package com.gmail.filoghost.holographicdisplays.nms.v1_7_R3; + +import java.util.Collection; + +import org.bukkit.EntityEffect; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_7_R3.CraftServer; +import org.bukkit.craftbukkit.v1_7_R3.entity.CraftHorse; +import org.bukkit.entity.AnimalTamer; +import org.bukkit.entity.Entity; +import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; +import org.bukkit.potion.PotionEffect; +import org.bukkit.util.Vector; + +public class CraftNMSHorse extends CraftHorse { + + public CraftNMSHorse(CraftServer server, EntityNMSHorse entity) { + super(server, entity); + } + + // Disallow all the bukkit methods. + + @Override + public void remove() { + // Cannot be removed, this is the most important to override. + } + + // Methods from Horse class + @Override public void setVariant(Variant variant) { } + @Override public void setColor(Color color) { } + @Override public void setStyle(Style style) { } + @Override public void setCarryingChest(boolean chest) { } + @Override public void setDomestication(int domestication) { } + @Override public void setJumpStrength(double jump) { } + + // Methods form Ageable class + @Override public void setAge(int age) { } + @Override public void setAgeLock(boolean lock) { } + @Override public void setBreed(boolean breed) { } + @Override public void setAdult() { } + @Override public void setBaby() { } + + // Methods from LivingEntity class + @Override public boolean addPotionEffect(PotionEffect effect) { return false; } + @Override public boolean addPotionEffect(PotionEffect effect, boolean param) { return false; } + @Override public boolean addPotionEffects(Collection effects) { return false; } + @Override public void setRemoveWhenFarAway(boolean remove) { } + + // Methods from Entity + @Override public void setVelocity(Vector vel) { } + @Override public boolean teleport(Location loc) { return false; } + @Override public boolean teleport(Entity entity) { return false; } + @Override public boolean teleport(Location loc, TeleportCause cause) { return false; } + @Override public boolean teleport(Entity entity, TeleportCause cause) { return false; } + @Override public void setFireTicks(int ticks) { } + @Override public boolean setPassenger(Entity entity) { return false; } + @Override public boolean eject() { return false; } + @Override public boolean leaveVehicle() { return false; } + @Override public void playEffect(EntityEffect effect) { } + + // Methods from Tameable + @Override public void setTamed(boolean tame) { } + @Override public void setOwner(AnimalTamer owner) { } +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R3/CraftNMSItem.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R3/CraftNMSItem.java new file mode 100644 index 00000000..e2ed8994 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R3/CraftNMSItem.java @@ -0,0 +1,40 @@ +package com.gmail.filoghost.holographicdisplays.nms.v1_7_R3; + +import org.bukkit.EntityEffect; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_7_R3.CraftServer; +import org.bukkit.craftbukkit.v1_7_R3.entity.CraftItem; +import org.bukkit.entity.Entity; +import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; +import org.bukkit.inventory.ItemStack; +import org.bukkit.util.Vector; + +public class CraftNMSItem extends CraftItem { + + public CraftNMSItem(CraftServer server, EntityNMSItem entity) { + super(server, entity); + } + + // Disallow all the bukkit methods. + + @Override + public void remove() { + // Cannot be removed, this is the most important to override. + } + + // Methods from Entity + @Override public void setVelocity(Vector vel) { } + @Override public boolean teleport(Location loc) { return false; } + @Override public boolean teleport(Entity entity) { return false; } + @Override public boolean teleport(Location loc, TeleportCause cause) { return false; } + @Override public boolean teleport(Entity entity, TeleportCause cause) { return false; } + @Override public void setFireTicks(int ticks) { } + @Override public boolean setPassenger(Entity entity) { return false; } + @Override public boolean eject() { return false; } + @Override public boolean leaveVehicle() { return false; } + @Override public void playEffect(EntityEffect effect) { } + + // Methods from Item + @Override public void setItemStack(ItemStack stack) { } + @Override public void setPickupDelay(int delay) { } +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R3/CraftNMSSlime.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R3/CraftNMSSlime.java new file mode 100644 index 00000000..2fb021fc --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R3/CraftNMSSlime.java @@ -0,0 +1,47 @@ +package com.gmail.filoghost.holographicdisplays.nms.v1_7_R3; + +import java.util.Collection; + +import org.bukkit.EntityEffect; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_7_R3.CraftServer; +import org.bukkit.craftbukkit.v1_7_R3.entity.CraftSlime; +import org.bukkit.entity.Entity; +import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; +import org.bukkit.potion.PotionEffect; +import org.bukkit.util.Vector; + +public class CraftNMSSlime extends CraftSlime { + + public CraftNMSSlime(CraftServer server, EntityNMSSlime entity) { + super(server, entity); + } + + // Disallow all the bukkit methods. + + @Override + public void remove() { + // Cannot be removed, this is the most important to override. + } + + // Methods from LivingEntity class + @Override public boolean addPotionEffect(PotionEffect effect) { return false; } + @Override public boolean addPotionEffect(PotionEffect effect, boolean param) { return false; } + @Override public boolean addPotionEffects(Collection effects) { return false; } + @Override public void setRemoveWhenFarAway(boolean remove) { } + + // Methods from Entity + @Override public void setVelocity(Vector vel) { } + @Override public boolean teleport(Location loc) { return false; } + @Override public boolean teleport(Entity entity) { return false; } + @Override public boolean teleport(Location loc, TeleportCause cause) { return false; } + @Override public boolean teleport(Entity entity, TeleportCause cause) { return false; } + @Override public void setFireTicks(int ticks) { } + @Override public boolean setPassenger(Entity entity) { return false; } + @Override public boolean eject() { return false; } + @Override public boolean leaveVehicle() { return false; } + @Override public void playEffect(EntityEffect effect) { } + + // Methods from Slime + @Override public void setSize(int size) { } +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R3/CraftNMSWitherSkull.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R3/CraftNMSWitherSkull.java new file mode 100644 index 00000000..5dcfbfbd --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R3/CraftNMSWitherSkull.java @@ -0,0 +1,46 @@ +package com.gmail.filoghost.holographicdisplays.nms.v1_7_R3; + +import org.bukkit.EntityEffect; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_7_R3.CraftServer; +import org.bukkit.craftbukkit.v1_7_R3.entity.CraftWitherSkull; +import org.bukkit.entity.Entity; +import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; +import org.bukkit.util.Vector; + +public class CraftNMSWitherSkull extends CraftWitherSkull { + + public CraftNMSWitherSkull(CraftServer server, EntityNMSWitherSkull entity) { + super(server, entity); + } + + // Disallow all the bukkit methods. + + @Override + public void remove() { + // Cannot be removed, this is the most important to override. + } + + // Method from Fireball + @Override public void setDirection(Vector dir) { } + + // Method from Projectile + @Override public void setBounce(boolean bounce) { } + + // Methods from Explosive + @Override public void setYield(float yield) { } + @Override public void setIsIncendiary(boolean fire) { } + + // Methods from Entity + @Override public void setVelocity(Vector vel) { } + @Override public boolean teleport(Location loc) { return false; } + @Override public boolean teleport(Entity entity) { return false; } + @Override public boolean teleport(Location loc, TeleportCause cause) { return false; } + @Override public boolean teleport(Entity entity, TeleportCause cause) { return false; } + @Override public void setFireTicks(int ticks) { } + @Override public boolean setPassenger(Entity entity) { return false; } + @Override public boolean eject() { return false; } + @Override public boolean leaveVehicle() { return false; } + @Override public void playEffect(EntityEffect effect) { } + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R3/EntityNMSHorse.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R3/EntityNMSHorse.java new file mode 100644 index 00000000..0aa9029e --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R3/EntityNMSHorse.java @@ -0,0 +1,156 @@ +package com.gmail.filoghost.holographicdisplays.nms.v1_7_R3; + +import net.minecraft.server.v1_7_R3.EntityHorse; +import net.minecraft.server.v1_7_R3.NBTTagCompound; +import net.minecraft.server.v1_7_R3.World; + +import org.bukkit.craftbukkit.v1_7_R3.entity.CraftEntity; + +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSHorse; +import com.gmail.filoghost.holographicdisplays.object.line.CraftHologramLine; + +public class EntityNMSHorse extends EntityHorse implements NMSHorse { + + private boolean lockTick; + private CraftHologramLine parentPiece; + + public EntityNMSHorse(World world, CraftHologramLine parentPiece) { + super(world); + super.ageLocked = true; + super.persistent = true; + super.boundingBox.a = 0.0; + super.boundingBox.b = 0.0; + super.boundingBox.c = 0.0; + super.boundingBox.d = 0.0; + super.boundingBox.e = 0.0; + super.boundingBox.f = 0.0; + a(0.0F, 0.0F); + setAge(-1700000); // This is a magic value. No one will see the real horse. + this.parentPiece = parentPiece; + } + + @Override + public void h() { + // Checks every 20 ticks. + if (ticksLived % 20 == 0) { + // The horse dies without a vehicle. + if (this.vehicle == null) { + die(); + } + } + + if (!lockTick) { + super.h(); + } + } + + @Override + public void b(NBTTagCompound nbttagcompound) { + // Do not save NBT. + } + + @Override + public boolean c(NBTTagCompound nbttagcompound) { + // Do not save NBT. + return false; + } + + @Override + public boolean d(NBTTagCompound nbttagcompound) { + // Do not save NBT. + return false; + } + + @Override + public void e(NBTTagCompound nbttagcompound) { + // Do not save NBT. + } + + @Override + public boolean isInvulnerable() { + /* + * The field Entity.invulnerable is private. + * It's only used while saving NBTTags, but since the entity would be killed + * on chunk unload, we prefer to override isInvulnerable(). + */ + return true; + } + + @Override + public void setCustomName(String customName) { + // Locks the custom name. + } + + @Override + public void setCustomNameVisible(boolean visible) { + // Locks the custom name. + } + + @Override + public void makeSound(String sound, float volume, float pitch) { + // Remove sounds. + } + + @Override + public void setLockTick(boolean lock) { + lockTick = lock; + } + + @Override + public void die() { + setLockTick(false); + super.die(); + } + + @Override + public void setCustomNameNMS(String name) { + if (name != null && name.length() > 300) { + name = name.substring(0, 300); + } + super.setCustomName(name); + super.setCustomNameVisible(name != null); + } + + @Override + public CraftEntity getBukkitEntity() { + if (super.bukkitEntity == null) { + this.bukkitEntity = new CraftNMSHorse(this.world.getServer(), this); + } + return this.bukkitEntity; + } + + @Override + public boolean isDeadNMS() { + return super.dead; + } + + @Override + public String getCustomNameNMS() { + return super.getCustomName(); + } + + @Override + public void killEntityNMS() { + die(); + } + + @Override + public void setLocationNMS(double x, double y, double z) { + super.setPosition(x, y, z); + } + + @Override + public int getIdNMS() { + return this.getId(); + } + + @Override + public CraftHologramLine getHologramLine() { + return parentPiece; + } + + @Override + public org.bukkit.entity.Entity getBukkitEntityNMS() { + return getBukkitEntity(); + } +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R3/EntityNMSItem.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R3/EntityNMSItem.java new file mode 100644 index 00000000..34ca8558 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R3/EntityNMSItem.java @@ -0,0 +1,183 @@ +package com.gmail.filoghost.holographicdisplays.nms.v1_7_R3; + +import net.minecraft.server.v1_7_R3.Blocks; +import net.minecraft.server.v1_7_R3.EntityHuman; +import net.minecraft.server.v1_7_R3.EntityItem; +import net.minecraft.server.v1_7_R3.EntityPlayer; +import net.minecraft.server.v1_7_R3.ItemStack; +import net.minecraft.server.v1_7_R3.NBTTagCompound; +import net.minecraft.server.v1_7_R3.NBTTagList; +import net.minecraft.server.v1_7_R3.NBTTagString; +import net.minecraft.server.v1_7_R3.World; + +import org.bukkit.craftbukkit.v1_7_R3.entity.CraftEntity; +import org.bukkit.craftbukkit.v1_7_R3.inventory.CraftItemStack; +import org.bukkit.entity.Player; + +import com.gmail.filoghost.holographicdisplays.listener.MainListener; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSItem; +import com.gmail.filoghost.holographicdisplays.object.line.CraftItemLine; +import com.gmail.filoghost.holographicdisplays.util.ItemUtils; + +public class EntityNMSItem extends EntityItem implements NMSItem { + + private boolean lockTick; + private CraftItemLine parentPiece; + + public EntityNMSItem(World world, CraftItemLine piece) { + super(world); + super.pickupDelay = Integer.MAX_VALUE; + this.parentPiece = piece; + } + + @Override + public void h() { + // Checks every 20 ticks. + if (ticksLived % 20 == 0) { + // The item dies without a vehicle. + if (this.vehicle == null) { + die(); + } + } + + if (!lockTick) { + super.h(); + } + } + + @Override + public ItemStack getItemStack() { + // Dirty method to check if the icon is being picked up + StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace(); + if (stacktrace.length > 2 && stacktrace[2].getClassName().contains("EntityInsentient")) { + return null; // Try to pickup this, dear entity ignoring the pickupDelay! + } + + return super.getItemStack(); + } + + // Method called when a player is near. + @Override + public void b_(EntityHuman human) { + + if (parentPiece.getPickupHandler() != null && human instanceof EntityPlayer) { + MainListener.handleItemLinePickup((Player) human.getBukkitEntity(), parentPiece.getPickupHandler(), parentPiece.getParent()); + // It is never added to the inventory. + } + } + + @Override + public void b(NBTTagCompound nbttagcompound) { + // Do not save NBT. + } + + @Override + public boolean c(NBTTagCompound nbttagcompound) { + // Do not save NBT. + return false; + } + + @Override + public boolean d(NBTTagCompound nbttagcompound) { + // Do not save NBT. + return false; + } + + @Override + public void e(NBTTagCompound nbttagcompound) { + // Do not save NBT. + } + + @Override + public boolean isInvulnerable() { + /* + * The field Entity.invulnerable is private. + * It's only used while saving NBTTags, but since the entity would be killed + * on chunk unload, we prefer to override isInvulnerable(). + */ + return true; + } + + @Override + public void setLockTick(boolean lock) { + lockTick = lock; + } + + @Override + public void die() { + setLockTick(false); + super.die(); + } + + @Override + public CraftEntity getBukkitEntity() { + if (super.bukkitEntity == null) { + this.bukkitEntity = new CraftNMSItem(this.world.getServer(), this); + } + return this.bukkitEntity; + } + + @Override + public boolean isDeadNMS() { + return this.dead; + } + + @Override + public void killEntityNMS() { + die(); + } + + @Override + public void setLocationNMS(double x, double y, double z) { + super.setPosition(x, y, z); + } + + @Override + public void setItemStackNMS(org.bukkit.inventory.ItemStack stack) { + ItemStack newItem = CraftItemStack.asNMSCopy(stack); + + if (newItem == null) { + newItem = new ItemStack(Blocks.BEDROCK); + } + + if (newItem.tag == null) { + newItem.tag = new NBTTagCompound(); + } + NBTTagCompound display = newItem.tag.getCompound("display"); + + if (!newItem.tag.hasKey("display")) { + newItem.tag.set("display", display); + } + + NBTTagList tagList = new NBTTagList(); + tagList.add(new NBTTagString(ItemUtils.ANTISTACK_LORE)); // Antistack lore + + display.set("Lore", tagList); + newItem.count = 0; + setItemStack(newItem); + } + + @Override + public int getIdNMS() { + return this.getId(); + } + + @Override + public CraftItemLine getHologramLine() { + return parentPiece; + } + + @Override + public void allowPickup(boolean pickup) { + if (pickup) { + super.pickupDelay = 0; + } else { + super.pickupDelay = Integer.MAX_VALUE; + } + } + + @Override + public org.bukkit.entity.Entity getBukkitEntityNMS() { + return getBukkitEntity(); + } +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R3/EntityNMSSlime.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R3/EntityNMSSlime.java new file mode 100644 index 00000000..ee08a5e4 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R3/EntityNMSSlime.java @@ -0,0 +1,143 @@ +package com.gmail.filoghost.holographicdisplays.nms.v1_7_R3; + +import net.minecraft.server.v1_7_R3.EntitySlime; +import net.minecraft.server.v1_7_R3.NBTTagCompound; +import net.minecraft.server.v1_7_R3.World; + +import org.bukkit.craftbukkit.v1_7_R3.entity.CraftEntity; + +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSSlime; +import com.gmail.filoghost.holographicdisplays.object.line.CraftHologramLine; +import com.gmail.filoghost.holographicdisplays.object.line.CraftTouchSlimeLine; + +public class EntityNMSSlime extends EntitySlime implements NMSSlime { + + private boolean lockTick; + private CraftTouchSlimeLine parentPiece; + + public EntityNMSSlime(World world, CraftTouchSlimeLine parentPiece) { + super(world); + super.persistent = true; + super.boundingBox.a = 0.0; + super.boundingBox.b = 0.0; + super.boundingBox.c = 0.0; + super.boundingBox.d = 0.0; + super.boundingBox.e = 0.0; + super.boundingBox.f = 0.0; + a(0.0F, 0.0F); + setSize(1); + setInvisible(true); + this.parentPiece = parentPiece; + } + + @Override + public void h() { + // Checks every 20 ticks. + if (ticksLived % 20 == 0) { + // The slime dies without a vehicle. + if (this.vehicle == null) { + die(); + } + } + + if (!lockTick) { + super.h(); + } + } + + @Override + public void b(NBTTagCompound nbttagcompound) { + // Do not save NBT. + } + + @Override + public boolean c(NBTTagCompound nbttagcompound) { + // Do not save NBT. + return false; + } + + @Override + public boolean d(NBTTagCompound nbttagcompound) { + // Do not save NBT. + return false; + } + + @Override + public void e(NBTTagCompound nbttagcompound) { + // Do not save NBT. + } + + @Override + public boolean isInvulnerable() { + /* + * The field Entity.invulnerable is private. + * It's only used while saving NBTTags, but since the entity would be killed + * on chunk unload, we prefer to override isInvulnerable(). + */ + return true; + } + + @Override + public void setCustomName(String customName) { + // Locks the custom name. + } + + @Override + public void setCustomNameVisible(boolean visible) { + // Locks the custom name. + } + + @Override + public void makeSound(String sound, float volume, float pitch) { + // Remove sounds. + } + + @Override + public void setLockTick(boolean lock) { + lockTick = lock; + } + + @Override + public void die() { + setLockTick(false); + super.die(); + } + + @Override + public CraftEntity getBukkitEntity() { + if (super.bukkitEntity == null) { + this.bukkitEntity = new CraftNMSSlime(this.world.getServer(), this); + } + return this.bukkitEntity; + } + + @Override + public boolean isDeadNMS() { + return super.dead; + } + + @Override + public void killEntityNMS() { + die(); + } + + @Override + public void setLocationNMS(double x, double y, double z) { + super.setPosition(x, y, z); + } + + @Override + public int getIdNMS() { + return this.getId(); + } + + @Override + public CraftHologramLine getHologramLine() { + return parentPiece; + } + + @Override + public org.bukkit.entity.Entity getBukkitEntityNMS() { + return getBukkitEntity(); + } +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R3/EntityNMSWitherSkull.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R3/EntityNMSWitherSkull.java new file mode 100644 index 00000000..efe2ee57 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R3/EntityNMSWitherSkull.java @@ -0,0 +1,165 @@ +package com.gmail.filoghost.holographicdisplays.nms.v1_7_R3; + +import org.bukkit.craftbukkit.v1_7_R3.entity.CraftEntity; + +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSEntityBase; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSWitherSkull; +import com.gmail.filoghost.holographicdisplays.object.line.CraftHologramLine; +import com.gmail.filoghost.holographicdisplays.util.Utils; + +import net.minecraft.server.v1_7_R3.Entity; +import net.minecraft.server.v1_7_R3.EntityWitherSkull; +import net.minecraft.server.v1_7_R3.NBTTagCompound; +import net.minecraft.server.v1_7_R3.PacketPlayOutEntityTeleport; +import net.minecraft.server.v1_7_R3.World; +import net.minecraft.server.v1_7_R3.EntityPlayer; + +public class EntityNMSWitherSkull extends EntityWitherSkull implements NMSWitherSkull { + + private boolean lockTick; + private CraftHologramLine parentPiece; + + public EntityNMSWitherSkull(World world, CraftHologramLine parentPiece) { + super(world); + super.motX = 0.0; + super.motY = 0.0; + super.motZ = 0.0; + super.dirX = 0.0; + super.dirY = 0.0; + super.dirZ = 0.0; + super.boundingBox.a = 0.0; + super.boundingBox.b = 0.0; + super.boundingBox.c = 0.0; + super.boundingBox.d = 0.0; + super.boundingBox.e = 0.0; + super.boundingBox.f = 0.0; + a(0.0F, 0.0F); + this.parentPiece = parentPiece; + } + + @Override + public void b(NBTTagCompound nbttagcompound) { + // Do not save NBT. + } + + @Override + public boolean c(NBTTagCompound nbttagcompound) { + // Do not save NBT. + return false; + } + + @Override + public boolean d(NBTTagCompound nbttagcompound) { + // Do not save NBT. + return false; + } + + @Override + public void e(NBTTagCompound nbttagcompound) { + // Do not save NBT. + } + + @Override + public boolean isInvulnerable() { + /* + * The field Entity.invulnerable is private. + * It's only used while saving NBTTags, but since the entity would be killed + * on chunk unload, we prefer to override isInvulnerable(). + */ + return true; + } + + @Override + public int getId() { + + StackTraceElement[] elements = Thread.currentThread().getStackTrace(); + if (elements.length > 2 && elements[2] != null && elements[2].getFileName().equals("EntityTrackerEntry.java") && elements[2].getLineNumber() > 134 && elements[2].getLineNumber() < 144) { + // Then this method is being called when creating a new packet, we return a fake ID! + return -1; + } + + return super.getId(); + } + + @Override + public void h() { + if (!lockTick) { + super.h(); + } + } + + @Override + public void makeSound(String sound, float f1, float f2) { + // Remove sounds. + } + + @Override + public void setLockTick(boolean lock) { + lockTick = lock; + } + + @Override + public void die() { + setLockTick(false); + super.die(); + } + + @Override + public CraftEntity getBukkitEntity() { + if (super.bukkitEntity == null) { + this.bukkitEntity = new CraftNMSWitherSkull(this.world.getServer(), this); + } + return this.bukkitEntity; + } + + @Override + public void killEntityNMS() { + die(); + } + + @Override + public void setLocationNMS(double x, double y, double z) { + super.setPosition(x, y, z); + + // Send a packet near to update the position. + PacketPlayOutEntityTeleport teleportPacket = new PacketPlayOutEntityTeleport(this); + + for (Object obj : this.world.players) { + if (obj instanceof EntityPlayer) { + EntityPlayer nmsPlayer = (EntityPlayer) obj; + + double distanceSquared = Utils.square(nmsPlayer.locX - this.locX) + Utils.square(nmsPlayer.locZ - this.locZ); + if (distanceSquared < 8192 && nmsPlayer.playerConnection != null) { + nmsPlayer.playerConnection.sendPacket(teleportPacket); + } + } + } + } + + @Override + public boolean isDeadNMS() { + return this.dead; + } + + @Override + public void setPassengerNMS(NMSEntityBase passenger) { + if (passenger instanceof Entity) { + ((Entity) passenger).setPassengerOf(this); + } + } + + @Override + public int getIdNMS() { + return this.getId(); + } + + @Override + public CraftHologramLine getHologramLine() { + return parentPiece; + } + + @Override + public org.bukkit.entity.Entity getBukkitEntityNMS() { + return getBukkitEntity(); + } +} \ No newline at end of file diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R3/FancyMessageImpl.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R3/FancyMessageImpl.java new file mode 100644 index 00000000..ba16cd39 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R3/FancyMessageImpl.java @@ -0,0 +1,125 @@ +package com.gmail.filoghost.holographicdisplays.nms.v1_7_R3; + +import java.io.IOException; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.List; + +import net.minecraft.server.v1_7_R3.ChatSerializer; +import net.minecraft.server.v1_7_R3.PacketPlayOutChat; + +import org.bukkit.ChatColor; +import org.bukkit.craftbukkit.libs.com.google.gson.stream.JsonWriter; +import org.bukkit.craftbukkit.v1_7_R3.entity.CraftPlayer; +import org.bukkit.entity.Player; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.FancyMessage; + +public class FancyMessageImpl implements FancyMessage { + + private List messageParts; + + public FancyMessageImpl(String firstPartText) { + messageParts = new ArrayList(); + messageParts.add(new MessagePart(firstPartText)); + } + + @Override + public FancyMessageImpl color(ChatColor color) { + if (!color.isColor()) { + throw new IllegalArgumentException(color.name() + " is not a color"); + } + latest().color = color; + return this; + } + + @Override + public FancyMessageImpl style(ChatColor... styles) { + for (ChatColor style : styles) { + if (!style.isFormat()) { + throw new IllegalArgumentException(style.name() + " is not a style"); + } + } + latest().styles = styles; + return this; + } + + @Override + public FancyMessageImpl file(String path) { + onClick("open_file", path); + return this; + } + + @Override + public FancyMessageImpl link(String url) { + onClick("open_url", url); + return this; + } + + @Override + public FancyMessageImpl suggest(String command) { + onClick("suggest_command", command); + return this; + } + + @Override + public FancyMessageImpl command(String command) { + onClick("run_command", command); + return this; + } + + @Override + public FancyMessageImpl tooltip(String text) { + onHover("show_text", text); + return this; + } + + @Override + public FancyMessageImpl then(Object obj) { + messageParts.add(new MessagePart(obj.toString())); + return this; + } + + @Override + public String toJSONString() { + StringWriter stringWriter = new StringWriter(); + JsonWriter json = new JsonWriter(stringWriter); + + try { + if (messageParts.size() == 1) { + latest().writeJson(json); + } else { + json.beginObject().name("text").value("").name("extra").beginArray(); + for (MessagePart part : messageParts) { + part.writeJson(json); + } + json.endArray().endObject(); + } + + } catch (IOException e) { + throw new RuntimeException("invalid message"); + } + return stringWriter.toString(); + } + + @Override + public void send(Player player){ + ((CraftPlayer) player).getHandle().playerConnection.sendPacket(new PacketPlayOutChat(ChatSerializer.a(toJSONString()))); + } + + private MessagePart latest() { + return messageParts.get(messageParts.size() - 1); + } + + private void onClick(String name, String data) { + MessagePart latest = latest(); + latest.clickActionName = name; + latest.clickActionData = data; + } + + private void onHover(String name, String data) { + MessagePart latest = latest(); + latest.hoverActionName = name; + latest.hoverActionData = data; + } + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R3/NmsManagerImpl.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R3/NmsManagerImpl.java new file mode 100644 index 00000000..2ea44b71 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R3/NmsManagerImpl.java @@ -0,0 +1,127 @@ +package com.gmail.filoghost.holographicdisplays.nms.v1_7_R3; + +import net.minecraft.server.v1_7_R3.Entity; +import net.minecraft.server.v1_7_R3.EntityTypes; +import net.minecraft.server.v1_7_R3.WorldServer; + +import org.apache.commons.lang.NotImplementedException; +import org.bukkit.World; +import org.bukkit.craftbukkit.v1_7_R3.CraftWorld; +import org.bukkit.craftbukkit.v1_7_R3.entity.CraftEntity; +import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason; +import org.bukkit.inventory.ItemStack; + +import com.gmail.filoghost.holographicdisplays.nms.interfaces.FancyMessage; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.NMSManager; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSArmorStand; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSEntityBase; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSHorse; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSItem; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSWitherSkull; +import com.gmail.filoghost.holographicdisplays.object.line.CraftHologramLine; +import com.gmail.filoghost.holographicdisplays.object.line.CraftItemLine; +import com.gmail.filoghost.holographicdisplays.object.line.CraftTouchSlimeLine; +import com.gmail.filoghost.holographicdisplays.util.DebugHandler; +import com.gmail.filoghost.holographicdisplays.util.ReflectionUtils; +import com.gmail.filoghost.holographicdisplays.util.VersionUtils; + +public class NmsManagerImpl implements NMSManager { + + @Override + public void registerCustomEntities() throws Exception { + registerCustomEntity(EntityNMSHorse.class, "EntityHorse", 100); + registerCustomEntity(EntityNMSWitherSkull.class, "WitherSkull", 19); + registerCustomEntity(EntityNMSItem.class, "Item", 1); + registerCustomEntity(EntityNMSSlime.class, "Slime", 55); + } + + @SuppressWarnings("rawtypes") + public void registerCustomEntity(Class entityClass, String name, int id) throws Exception { + if (VersionUtils.isMCPCOrCauldron()) { + // MCPC+ / Cauldron entity registration. + Class entityTypesClass = Class.forName("net.minecraft.server.v1_7_R3.EntityTypes"); + ReflectionUtils.putInPrivateStaticMap(entityTypesClass, "field_75626_c", entityClass, name); + ReflectionUtils.putInPrivateStaticMap(entityTypesClass, "field_75624_e", entityClass, Integer.valueOf(id)); + } else { + // Normal entity registration. + ReflectionUtils.putInPrivateStaticMap(EntityTypes.class, "d", entityClass, name); + ReflectionUtils.putInPrivateStaticMap(EntityTypes.class, "f", entityClass, Integer.valueOf(id)); + } + } + + @Override + public NMSHorse spawnNMSHorse(org.bukkit.World world, double x, double y, double z, CraftHologramLine parentPiece) { + WorldServer nmsWorld = ((CraftWorld) world).getHandle(); + EntityNMSHorse invisibleHorse = new EntityNMSHorse(nmsWorld, parentPiece); + invisibleHorse.setLocationNMS(x, y, z); + if (!nmsWorld.addEntity(invisibleHorse, SpawnReason.CUSTOM)) { + DebugHandler.handleSpawnFail(parentPiece); + } + return invisibleHorse; + } + + @Override + public NMSWitherSkull spawnNMSWitherSkull(org.bukkit.World bukkitWorld, double x, double y, double z, CraftHologramLine parentPiece) { + WorldServer nmsWorld = ((CraftWorld) bukkitWorld).getHandle(); + EntityNMSWitherSkull staticWitherSkull = new EntityNMSWitherSkull(nmsWorld, parentPiece); + staticWitherSkull.setLocationNMS(x, y, z); + if (!nmsWorld.addEntity(staticWitherSkull, SpawnReason.CUSTOM)) { + DebugHandler.handleSpawnFail(parentPiece); + } + return staticWitherSkull; + } + + @Override + public NMSItem spawnNMSItem(org.bukkit.World bukkitWorld, double x, double y, double z, CraftItemLine parentPiece, ItemStack stack) { + WorldServer nmsWorld = ((CraftWorld) bukkitWorld).getHandle(); + EntityNMSItem customItem = new EntityNMSItem(nmsWorld, parentPiece); + customItem.setLocationNMS(x, y, z); + customItem.setItemStackNMS(stack); + if (!nmsWorld.addEntity(customItem, SpawnReason.CUSTOM)) { + DebugHandler.handleSpawnFail(parentPiece); + } + return customItem; + } + + @Override + public EntityNMSSlime spawnNMSSlime(org.bukkit.World bukkitWorld, double x, double y, double z, CraftTouchSlimeLine parentPiece) { + WorldServer nmsWorld = ((CraftWorld) bukkitWorld).getHandle(); + EntityNMSSlime touchSlime = new EntityNMSSlime(nmsWorld, parentPiece); + touchSlime.setLocationNMS(x, y, z); + if (!nmsWorld.addEntity(touchSlime, SpawnReason.CUSTOM)) { + DebugHandler.handleSpawnFail(parentPiece); + } + return touchSlime; + } + + @Override + public boolean isNMSEntityBase(org.bukkit.entity.Entity bukkitEntity) { + return ((CraftEntity) bukkitEntity).getHandle() instanceof NMSEntityBase; + } + + @Override + public NMSEntityBase getNMSEntityBase(org.bukkit.entity.Entity bukkitEntity) { + + Entity nmsEntity = ((CraftEntity) bukkitEntity).getHandle(); + if (nmsEntity instanceof NMSEntityBase) { + return ((NMSEntityBase) nmsEntity); + } + + return null; + } + + @Override + public FancyMessage newFancyMessage(String text) { + return new FancyMessageImpl(text); + } + + @Override + public boolean hasChatHoverFeature() { + return true; + } + + @Override + public NMSArmorStand spawnNMSArmorStand(World world, double x, double y, double z, CraftHologramLine parentPiece) { + throw new NotImplementedException("Method can only be used on 1.8 or higher"); + } +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R4/CraftNMSHorse.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R4/CraftNMSHorse.java new file mode 100644 index 00000000..c1c28231 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R4/CraftNMSHorse.java @@ -0,0 +1,64 @@ +package com.gmail.filoghost.holographicdisplays.nms.v1_7_R4; + +import java.util.Collection; + +import org.bukkit.EntityEffect; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_7_R4.CraftServer; +import org.bukkit.craftbukkit.v1_7_R4.entity.CraftHorse; +import org.bukkit.entity.AnimalTamer; +import org.bukkit.entity.Entity; +import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; +import org.bukkit.potion.PotionEffect; +import org.bukkit.util.Vector; + +public class CraftNMSHorse extends CraftHorse { + + public CraftNMSHorse(CraftServer server, EntityNMSHorse entity) { + super(server, entity); + } + + // Disallow all the bukkit methods. + + @Override + public void remove() { + // Cannot be removed, this is the most important to override. + } + + // Methods from Horse class + @Override public void setVariant(Variant variant) { } + @Override public void setColor(Color color) { } + @Override public void setStyle(Style style) { } + @Override public void setCarryingChest(boolean chest) { } + @Override public void setDomestication(int domestication) { } + @Override public void setJumpStrength(double jump) { } + + // Methods form Ageable class + @Override public void setAge(int age) { } + @Override public void setAgeLock(boolean lock) { } + @Override public void setBreed(boolean breed) { } + @Override public void setAdult() { } + @Override public void setBaby() { } + + // Methods from LivingEntity class + @Override public boolean addPotionEffect(PotionEffect effect) { return false; } + @Override public boolean addPotionEffect(PotionEffect effect, boolean param) { return false; } + @Override public boolean addPotionEffects(Collection effects) { return false; } + @Override public void setRemoveWhenFarAway(boolean remove) { } + + // Methods from Entity + @Override public void setVelocity(Vector vel) { } + @Override public boolean teleport(Location loc) { return false; } + @Override public boolean teleport(Entity entity) { return false; } + @Override public boolean teleport(Location loc, TeleportCause cause) { return false; } + @Override public boolean teleport(Entity entity, TeleportCause cause) { return false; } + @Override public void setFireTicks(int ticks) { } + @Override public boolean setPassenger(Entity entity) { return false; } + @Override public boolean eject() { return false; } + @Override public boolean leaveVehicle() { return false; } + @Override public void playEffect(EntityEffect effect) { } + + // Methods from Tameable + @Override public void setTamed(boolean tame) { } + @Override public void setOwner(AnimalTamer owner) { } +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R4/CraftNMSItem.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R4/CraftNMSItem.java new file mode 100644 index 00000000..1996ec7c --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R4/CraftNMSItem.java @@ -0,0 +1,40 @@ +package com.gmail.filoghost.holographicdisplays.nms.v1_7_R4; + +import org.bukkit.EntityEffect; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_7_R4.CraftServer; +import org.bukkit.craftbukkit.v1_7_R4.entity.CraftItem; +import org.bukkit.entity.Entity; +import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; +import org.bukkit.inventory.ItemStack; +import org.bukkit.util.Vector; + +public class CraftNMSItem extends CraftItem { + + public CraftNMSItem(CraftServer server, EntityNMSItem entity) { + super(server, entity); + } + + // Disallow all the bukkit methods. + + @Override + public void remove() { + // Cannot be removed, this is the most important to override. + } + + // Methods from Entity + @Override public void setVelocity(Vector vel) { } + @Override public boolean teleport(Location loc) { return false; } + @Override public boolean teleport(Entity entity) { return false; } + @Override public boolean teleport(Location loc, TeleportCause cause) { return false; } + @Override public boolean teleport(Entity entity, TeleportCause cause) { return false; } + @Override public void setFireTicks(int ticks) { } + @Override public boolean setPassenger(Entity entity) { return false; } + @Override public boolean eject() { return false; } + @Override public boolean leaveVehicle() { return false; } + @Override public void playEffect(EntityEffect effect) { } + + // Methods from Item + @Override public void setItemStack(ItemStack stack) { } + @Override public void setPickupDelay(int delay) { } +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R4/CraftNMSSlime.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R4/CraftNMSSlime.java new file mode 100644 index 00000000..f43f118e --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R4/CraftNMSSlime.java @@ -0,0 +1,47 @@ +package com.gmail.filoghost.holographicdisplays.nms.v1_7_R4; + +import java.util.Collection; + +import org.bukkit.EntityEffect; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_7_R4.CraftServer; +import org.bukkit.craftbukkit.v1_7_R4.entity.CraftSlime; +import org.bukkit.entity.Entity; +import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; +import org.bukkit.potion.PotionEffect; +import org.bukkit.util.Vector; + +public class CraftNMSSlime extends CraftSlime { + + public CraftNMSSlime(CraftServer server, EntityNMSSlime entity) { + super(server, entity); + } + + // Disallow all the bukkit methods. + + @Override + public void remove() { + // Cannot be removed, this is the most important to override. + } + + // Methods from LivingEntity class + @Override public boolean addPotionEffect(PotionEffect effect) { return false; } + @Override public boolean addPotionEffect(PotionEffect effect, boolean param) { return false; } + @Override public boolean addPotionEffects(Collection effects) { return false; } + @Override public void setRemoveWhenFarAway(boolean remove) { } + + // Methods from Entity + @Override public void setVelocity(Vector vel) { } + @Override public boolean teleport(Location loc) { return false; } + @Override public boolean teleport(Entity entity) { return false; } + @Override public boolean teleport(Location loc, TeleportCause cause) { return false; } + @Override public boolean teleport(Entity entity, TeleportCause cause) { return false; } + @Override public void setFireTicks(int ticks) { } + @Override public boolean setPassenger(Entity entity) { return false; } + @Override public boolean eject() { return false; } + @Override public boolean leaveVehicle() { return false; } + @Override public void playEffect(EntityEffect effect) { } + + // Methods from Slime + @Override public void setSize(int size) { } +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R4/CraftNMSWitherSkull.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R4/CraftNMSWitherSkull.java new file mode 100644 index 00000000..78e10f19 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R4/CraftNMSWitherSkull.java @@ -0,0 +1,46 @@ +package com.gmail.filoghost.holographicdisplays.nms.v1_7_R4; + +import org.bukkit.EntityEffect; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_7_R4.CraftServer; +import org.bukkit.craftbukkit.v1_7_R4.entity.CraftWitherSkull; +import org.bukkit.entity.Entity; +import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; +import org.bukkit.util.Vector; + +public class CraftNMSWitherSkull extends CraftWitherSkull { + + public CraftNMSWitherSkull(CraftServer server, EntityNMSWitherSkull entity) { + super(server, entity); + } + + // Disallow all the bukkit methods. + + @Override + public void remove() { + // Cannot be removed, this is the most important to override. + } + + // Method from Fireball + @Override public void setDirection(Vector dir) { } + + // Method from Projectile + @Override public void setBounce(boolean bounce) { } + + // Methods from Explosive + @Override public void setYield(float yield) { } + @Override public void setIsIncendiary(boolean fire) { } + + // Methods from Entity + @Override public void setVelocity(Vector vel) { } + @Override public boolean teleport(Location loc) { return false; } + @Override public boolean teleport(Entity entity) { return false; } + @Override public boolean teleport(Location loc, TeleportCause cause) { return false; } + @Override public boolean teleport(Entity entity, TeleportCause cause) { return false; } + @Override public void setFireTicks(int ticks) { } + @Override public boolean setPassenger(Entity entity) { return false; } + @Override public boolean eject() { return false; } + @Override public boolean leaveVehicle() { return false; } + @Override public void playEffect(EntityEffect effect) { } + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R4/EntityNMSHorse.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R4/EntityNMSHorse.java new file mode 100644 index 00000000..6c588c92 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R4/EntityNMSHorse.java @@ -0,0 +1,157 @@ +package com.gmail.filoghost.holographicdisplays.nms.v1_7_R4; + +import net.minecraft.server.v1_7_R4.EntityHorse; +import net.minecraft.server.v1_7_R4.NBTTagCompound; +import net.minecraft.server.v1_7_R4.World; + +import org.bukkit.craftbukkit.v1_7_R4.entity.CraftEntity; + +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSHorse; +import com.gmail.filoghost.holographicdisplays.object.line.CraftHologramLine; + +public class EntityNMSHorse extends EntityHorse implements NMSHorse { + + private boolean lockTick; + private CraftHologramLine parentPiece; + + public EntityNMSHorse(World world, CraftHologramLine parentPiece) { + super(world); + super.ageLocked = true; + super.persistent = true; + super.boundingBox.a = 0.0; + super.boundingBox.b = 0.0; + super.boundingBox.c = 0.0; + super.boundingBox.d = 0.0; + super.boundingBox.e = 0.0; + super.boundingBox.f = 0.0; + a(0.0F, 0.0F); + setAge(-1700000); // This is a magic value. No one will see the real horse. + this.parentPiece = parentPiece; + } + + @Override + public void h() { + // Checks every 20 ticks. + if (ticksLived % 20 == 0) { + // The horse dies without a vehicle. + if (this.vehicle == null) { + die(); + } + } + + if (!lockTick) { + super.h(); + } + } + + @Override + public void b(NBTTagCompound nbttagcompound) { + // Do not save NBT. + } + + @Override + public boolean c(NBTTagCompound nbttagcompound) { + // Do not save NBT. + return false; + } + + @Override + public boolean d(NBTTagCompound nbttagcompound) { + // Do not save NBT. + return false; + } + + @Override + public void e(NBTTagCompound nbttagcompound) { + // Do not save NBT. + } + + @Override + public boolean isInvulnerable() { + /* + * The field Entity.invulnerable is private. + * It's only used while saving NBTTags, but since the entity would be killed + * on chunk unload, we prefer to override isInvulnerable(). + */ + return true; + } + + @Override + public void setCustomName(String customName) { + // Locks the custom name. + } + + @Override + public void setCustomNameVisible(boolean visible) { + // Locks the custom name. + } + + @Override + public void makeSound(String sound, float volume, float pitch) { + // Remove sounds. + } + + @Override + public void setLockTick(boolean lock) { + lockTick = lock; + } + + @Override + public void die() { + setLockTick(false); + super.die(); + } + + @Override + public void setCustomNameNMS(String name) { + if (name != null && name.length() > 300) { + name = name.substring(0, 300); + } + super.setCustomName(name); + super.setCustomNameVisible(name != null); + } + + @Override + public CraftEntity getBukkitEntity() { + if (super.bukkitEntity == null) { + this.bukkitEntity = new CraftNMSHorse(this.world.getServer(), this); + } + return this.bukkitEntity; + } + + @Override + public boolean isDeadNMS() { + return super.dead; + } + + @Override + public String getCustomNameNMS() { + return super.getCustomName(); + } + + @Override + public void killEntityNMS() { + die(); + } + + @Override + public void setLocationNMS(double x, double y, double z) { + super.setPosition(x, y, z); + } + + @Override + public int getIdNMS() { + return this.getId(); + } + + @Override + public CraftHologramLine getHologramLine() { + return parentPiece; + } + + @Override + public org.bukkit.entity.Entity getBukkitEntityNMS() { + return getBukkitEntity(); + } + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R4/EntityNMSItem.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R4/EntityNMSItem.java new file mode 100644 index 00000000..9017ea83 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R4/EntityNMSItem.java @@ -0,0 +1,183 @@ +package com.gmail.filoghost.holographicdisplays.nms.v1_7_R4; + +import net.minecraft.server.v1_7_R4.Blocks; +import net.minecraft.server.v1_7_R4.EntityHuman; +import net.minecraft.server.v1_7_R4.EntityItem; +import net.minecraft.server.v1_7_R4.EntityPlayer; +import net.minecraft.server.v1_7_R4.ItemStack; +import net.minecraft.server.v1_7_R4.NBTTagCompound; +import net.minecraft.server.v1_7_R4.NBTTagList; +import net.minecraft.server.v1_7_R4.NBTTagString; +import net.minecraft.server.v1_7_R4.World; + +import org.bukkit.craftbukkit.v1_7_R4.entity.CraftEntity; +import org.bukkit.craftbukkit.v1_7_R4.inventory.CraftItemStack; +import org.bukkit.entity.Player; + +import com.gmail.filoghost.holographicdisplays.listener.MainListener; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSItem; +import com.gmail.filoghost.holographicdisplays.object.line.CraftItemLine; +import com.gmail.filoghost.holographicdisplays.util.ItemUtils; + +public class EntityNMSItem extends EntityItem implements NMSItem { + + private boolean lockTick; + private CraftItemLine parentPiece; + + public EntityNMSItem(World world, CraftItemLine piece) { + super(world); + super.pickupDelay = Integer.MAX_VALUE; + this.parentPiece = piece; + } + + @Override + public void h() { + // Checks every 20 ticks. + if (ticksLived % 20 == 0) { + // The item dies without a vehicle. + if (this.vehicle == null) { + die(); + } + } + + if (!lockTick) { + super.h(); + } + } + + @Override + public ItemStack getItemStack() { + // Dirty method to check if the icon is being picked up + StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace(); + if (stacktrace.length > 2 && stacktrace[2].getClassName().contains("EntityInsentient")) { + return null; // Try to pickup this, dear entity ignoring the pickupDelay! + } + + return super.getItemStack(); + } + + // Method called when a player is near. + @Override + public void b_(EntityHuman human) { + + if (parentPiece.getPickupHandler() != null && human instanceof EntityPlayer) { + MainListener.handleItemLinePickup((Player) human.getBukkitEntity(), parentPiece.getPickupHandler(), parentPiece.getParent()); + // It is never added to the inventory. + } + } + + @Override + public void b(NBTTagCompound nbttagcompound) { + // Do not save NBT. + } + + @Override + public boolean c(NBTTagCompound nbttagcompound) { + // Do not save NBT. + return false; + } + + @Override + public boolean d(NBTTagCompound nbttagcompound) { + // Do not save NBT. + return false; + } + + @Override + public void e(NBTTagCompound nbttagcompound) { + // Do not save NBT. + } + + @Override + public boolean isInvulnerable() { + /* + * The field Entity.invulnerable is private. + * It's only used while saving NBTTags, but since the entity would be killed + * on chunk unload, we prefer to override isInvulnerable(). + */ + return true; + } + + @Override + public void setLockTick(boolean lock) { + lockTick = lock; + } + + @Override + public void die() { + setLockTick(false); + super.die(); + } + + @Override + public CraftEntity getBukkitEntity() { + if (super.bukkitEntity == null) { + this.bukkitEntity = new CraftNMSItem(this.world.getServer(), this); + } + return this.bukkitEntity; + } + + @Override + public boolean isDeadNMS() { + return this.dead; + } + + @Override + public void killEntityNMS() { + die(); + } + + @Override + public void setLocationNMS(double x, double y, double z) { + super.setPosition(x, y, z); + } + + @Override + public void setItemStackNMS(org.bukkit.inventory.ItemStack stack) { + ItemStack newItem = CraftItemStack.asNMSCopy(stack); + + if (newItem == null) { + newItem = new ItemStack(Blocks.BEDROCK); + } + + if (newItem.tag == null) { + newItem.tag = new NBTTagCompound(); + } + NBTTagCompound display = newItem.tag.getCompound("display"); + + if (!newItem.tag.hasKey("display")) { + newItem.tag.set("display", display); + } + + NBTTagList tagList = new NBTTagList(); + tagList.add(new NBTTagString(ItemUtils.ANTISTACK_LORE)); // Antistack lore + + display.set("Lore", tagList); + newItem.count = 0; + setItemStack(newItem); + } + + @Override + public int getIdNMS() { + return this.getId(); + } + + @Override + public CraftItemLine getHologramLine() { + return parentPiece; + } + + @Override + public void allowPickup(boolean pickup) { + if (pickup) { + super.pickupDelay = 0; + } else { + super.pickupDelay = Integer.MAX_VALUE; + } + } + + @Override + public org.bukkit.entity.Entity getBukkitEntityNMS() { + return getBukkitEntity(); + } +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R4/EntityNMSSlime.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R4/EntityNMSSlime.java new file mode 100644 index 00000000..25a22cd0 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R4/EntityNMSSlime.java @@ -0,0 +1,143 @@ +package com.gmail.filoghost.holographicdisplays.nms.v1_7_R4; + +import net.minecraft.server.v1_7_R4.EntitySlime; +import net.minecraft.server.v1_7_R4.NBTTagCompound; +import net.minecraft.server.v1_7_R4.World; + +import org.bukkit.craftbukkit.v1_7_R4.entity.CraftEntity; + +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSSlime; +import com.gmail.filoghost.holographicdisplays.object.line.CraftHologramLine; +import com.gmail.filoghost.holographicdisplays.object.line.CraftTouchSlimeLine; + +public class EntityNMSSlime extends EntitySlime implements NMSSlime { + + private boolean lockTick; + private CraftTouchSlimeLine parentPiece; + + public EntityNMSSlime(World world, CraftTouchSlimeLine parentPiece) { + super(world); + super.persistent = true; + super.boundingBox.a = 0.0; + super.boundingBox.b = 0.0; + super.boundingBox.c = 0.0; + super.boundingBox.d = 0.0; + super.boundingBox.e = 0.0; + super.boundingBox.f = 0.0; + a(0.0F, 0.0F); + setSize(1); + setInvisible(true); + this.parentPiece = parentPiece; + } + + @Override + public void h() { + // Checks every 20 ticks. + if (ticksLived % 20 == 0) { + // The slime dies without a vehicle. + if (this.vehicle == null) { + die(); + } + } + + if (!lockTick) { + super.h(); + } + } + + @Override + public void b(NBTTagCompound nbttagcompound) { + // Do not save NBT. + } + + @Override + public boolean c(NBTTagCompound nbttagcompound) { + // Do not save NBT. + return false; + } + + @Override + public boolean d(NBTTagCompound nbttagcompound) { + // Do not save NBT. + return false; + } + + @Override + public void e(NBTTagCompound nbttagcompound) { + // Do not save NBT. + } + + @Override + public boolean isInvulnerable() { + /* + * The field Entity.invulnerable is private. + * It's only used while saving NBTTags, but since the entity would be killed + * on chunk unload, we prefer to override isInvulnerable(). + */ + return true; + } + + @Override + public void setCustomName(String customName) { + // Locks the custom name. + } + + @Override + public void setCustomNameVisible(boolean visible) { + // Locks the custom name. + } + + @Override + public void makeSound(String sound, float volume, float pitch) { + // Remove sounds. + } + + @Override + public void setLockTick(boolean lock) { + lockTick = lock; + } + + @Override + public void die() { + setLockTick(false); + super.die(); + } + + @Override + public CraftEntity getBukkitEntity() { + if (super.bukkitEntity == null) { + this.bukkitEntity = new CraftNMSSlime(this.world.getServer(), this); + } + return this.bukkitEntity; + } + + @Override + public boolean isDeadNMS() { + return super.dead; + } + + @Override + public void killEntityNMS() { + die(); + } + + @Override + public void setLocationNMS(double x, double y, double z) { + super.setPosition(x, y, z); + } + + @Override + public int getIdNMS() { + return this.getId(); + } + + @Override + public CraftHologramLine getHologramLine() { + return parentPiece; + } + + @Override + public org.bukkit.entity.Entity getBukkitEntityNMS() { + return getBukkitEntity(); + } +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R4/EntityNMSWitherSkull.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R4/EntityNMSWitherSkull.java new file mode 100644 index 00000000..74c80117 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R4/EntityNMSWitherSkull.java @@ -0,0 +1,166 @@ +package com.gmail.filoghost.holographicdisplays.nms.v1_7_R4; + +import org.bukkit.craftbukkit.v1_7_R4.entity.CraftEntity; + +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSEntityBase; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSWitherSkull; +import com.gmail.filoghost.holographicdisplays.object.line.CraftHologramLine; +import com.gmail.filoghost.holographicdisplays.util.Utils; + +import net.minecraft.server.v1_7_R4.Entity; +import net.minecraft.server.v1_7_R4.EntityWitherSkull; +import net.minecraft.server.v1_7_R4.NBTTagCompound; +import net.minecraft.server.v1_7_R4.PacketPlayOutEntityTeleport; +import net.minecraft.server.v1_7_R4.World; +import net.minecraft.server.v1_7_R4.EntityPlayer; + +public class EntityNMSWitherSkull extends EntityWitherSkull implements NMSWitherSkull { + + private boolean lockTick; + private CraftHologramLine parentPiece; + + public EntityNMSWitherSkull(World world, CraftHologramLine parentPiece) { + super(world); + super.motX = 0.0; + super.motY = 0.0; + super.motZ = 0.0; + super.dirX = 0.0; + super.dirY = 0.0; + super.dirZ = 0.0; + super.boundingBox.a = 0.0; + super.boundingBox.b = 0.0; + super.boundingBox.c = 0.0; + super.boundingBox.d = 0.0; + super.boundingBox.e = 0.0; + super.boundingBox.f = 0.0; + a(0.0F, 0.0F); + this.parentPiece = parentPiece; + } + + @Override + public void b(NBTTagCompound nbttagcompound) { + // Do not save NBT. + } + + @Override + public boolean c(NBTTagCompound nbttagcompound) { + // Do not save NBT. + return false; + } + + @Override + public boolean d(NBTTagCompound nbttagcompound) { + // Do not save NBT. + return false; + } + + @Override + public void e(NBTTagCompound nbttagcompound) { + // Do not save NBT. + } + + @Override + public boolean isInvulnerable() { + /* + * The field Entity.invulnerable is private. + * It's only used while saving NBTTags, but since the entity would be killed + * on chunk unload, we prefer to override isInvulnerable(). + */ + return true; + } + + @Override + public int getId() { + + StackTraceElement[] elements = Thread.currentThread().getStackTrace(); + if (elements.length > 2 && elements[2] != null && elements[2].getFileName().equals("EntityTrackerEntry.java") && elements[2].getLineNumber() > 134 && elements[2].getLineNumber() < 144) { + // Then this method is being called when creating a new packet, we return a fake ID! + return -1; + } + + return super.getId(); + } + + @Override + public void h() { + if (!lockTick) { + super.h(); + } + } + + @Override + public void makeSound(String sound, float f1, float f2) { + // Remove sounds. + } + + @Override + public void setLockTick(boolean lock) { + lockTick = lock; + } + + @Override + public void die() { + setLockTick(false); + super.die(); + } + + @Override + public CraftEntity getBukkitEntity() { + if (super.bukkitEntity == null) { + this.bukkitEntity = new CraftNMSWitherSkull(this.world.getServer(), this); + } + return this.bukkitEntity; + } + + @Override + public void killEntityNMS() { + die(); + } + + + @Override + public void setLocationNMS(double x, double y, double z) { + super.setPosition(x, y, z); + + // Send a packet near to update the position. + PacketPlayOutEntityTeleport teleportPacket = new PacketPlayOutEntityTeleport(this); + + for (Object obj : this.world.players) { + if (obj instanceof EntityPlayer) { + EntityPlayer nmsPlayer = (EntityPlayer) obj; + + double distanceSquared = Utils.square(nmsPlayer.locX - this.locX) + Utils.square(nmsPlayer.locZ - this.locZ); + if (distanceSquared < 8192 && nmsPlayer.playerConnection != null) { + nmsPlayer.playerConnection.sendPacket(teleportPacket); + } + } + } + } + + @Override + public boolean isDeadNMS() { + return this.dead; + } + + @Override + public void setPassengerNMS(NMSEntityBase passenger) { + if (passenger instanceof Entity) { + ((Entity) passenger).setPassengerOf(this); + } + } + + @Override + public int getIdNMS() { + return this.getId(); + } + + @Override + public CraftHologramLine getHologramLine() { + return parentPiece; + } + + @Override + public org.bukkit.entity.Entity getBukkitEntityNMS() { + return getBukkitEntity(); + } +} \ No newline at end of file diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R4/FancyMessageImpl.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R4/FancyMessageImpl.java new file mode 100644 index 00000000..5e0e6876 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R4/FancyMessageImpl.java @@ -0,0 +1,125 @@ +package com.gmail.filoghost.holographicdisplays.nms.v1_7_R4; + +import java.io.IOException; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.List; + +import net.minecraft.server.v1_7_R4.ChatSerializer; +import net.minecraft.server.v1_7_R4.PacketPlayOutChat; + +import org.bukkit.ChatColor; +import org.bukkit.craftbukkit.libs.com.google.gson.stream.JsonWriter; +import org.bukkit.craftbukkit.v1_7_R4.entity.CraftPlayer; +import org.bukkit.entity.Player; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.FancyMessage; + +public class FancyMessageImpl implements FancyMessage { + + private List messageParts; + + public FancyMessageImpl(String firstPartText) { + messageParts = new ArrayList(); + messageParts.add(new MessagePart(firstPartText)); + } + + @Override + public FancyMessageImpl color(ChatColor color) { + if (!color.isColor()) { + throw new IllegalArgumentException(color.name() + " is not a color"); + } + latest().color = color; + return this; + } + + @Override + public FancyMessageImpl style(ChatColor... styles) { + for (ChatColor style : styles) { + if (!style.isFormat()) { + throw new IllegalArgumentException(style.name() + " is not a style"); + } + } + latest().styles = styles; + return this; + } + + @Override + public FancyMessageImpl file(String path) { + onClick("open_file", path); + return this; + } + + @Override + public FancyMessageImpl link(String url) { + onClick("open_url", url); + return this; + } + + @Override + public FancyMessageImpl suggest(String command) { + onClick("suggest_command", command); + return this; + } + + @Override + public FancyMessageImpl command(String command) { + onClick("run_command", command); + return this; + } + + @Override + public FancyMessageImpl tooltip(String text) { + onHover("show_text", text); + return this; + } + + @Override + public FancyMessageImpl then(Object obj) { + messageParts.add(new MessagePart(obj.toString())); + return this; + } + + @Override + public String toJSONString() { + StringWriter stringWriter = new StringWriter(); + JsonWriter json = new JsonWriter(stringWriter); + + try { + if (messageParts.size() == 1) { + latest().writeJson(json); + } else { + json.beginObject().name("text").value("").name("extra").beginArray(); + for (MessagePart part : messageParts) { + part.writeJson(json); + } + json.endArray().endObject(); + } + + } catch (IOException e) { + throw new RuntimeException("invalid message"); + } + return stringWriter.toString(); + } + + @Override + public void send(Player player){ + ((CraftPlayer) player).getHandle().playerConnection.sendPacket(new PacketPlayOutChat(ChatSerializer.a(toJSONString()))); + } + + private MessagePart latest() { + return messageParts.get(messageParts.size() - 1); + } + + private void onClick(String name, String data) { + MessagePart latest = latest(); + latest.clickActionName = name; + latest.clickActionData = data; + } + + private void onHover(String name, String data) { + MessagePart latest = latest(); + latest.hoverActionName = name; + latest.hoverActionData = data; + } + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R4/NmsManagerImpl.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R4/NmsManagerImpl.java new file mode 100644 index 00000000..c2e71d5e --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_7_R4/NmsManagerImpl.java @@ -0,0 +1,127 @@ +package com.gmail.filoghost.holographicdisplays.nms.v1_7_R4; + +import net.minecraft.server.v1_7_R4.Entity; +import net.minecraft.server.v1_7_R4.EntityTypes; +import net.minecraft.server.v1_7_R4.WorldServer; + +import org.apache.commons.lang.NotImplementedException; +import org.bukkit.World; +import org.bukkit.craftbukkit.v1_7_R4.CraftWorld; +import org.bukkit.craftbukkit.v1_7_R4.entity.CraftEntity; +import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason; +import org.bukkit.inventory.ItemStack; + +import com.gmail.filoghost.holographicdisplays.nms.interfaces.FancyMessage; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.NMSManager; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSArmorStand; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSEntityBase; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSHorse; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSItem; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSWitherSkull; +import com.gmail.filoghost.holographicdisplays.object.line.CraftHologramLine; +import com.gmail.filoghost.holographicdisplays.object.line.CraftItemLine; +import com.gmail.filoghost.holographicdisplays.object.line.CraftTouchSlimeLine; +import com.gmail.filoghost.holographicdisplays.util.DebugHandler; +import com.gmail.filoghost.holographicdisplays.util.ReflectionUtils; +import com.gmail.filoghost.holographicdisplays.util.VersionUtils; + +public class NmsManagerImpl implements NMSManager { + + @Override + public void registerCustomEntities() throws Exception { + registerCustomEntity(EntityNMSHorse.class, "EntityHorse", 100); + registerCustomEntity(EntityNMSWitherSkull.class, "WitherSkull", 19); + registerCustomEntity(EntityNMSItem.class, "Item", 1); + registerCustomEntity(EntityNMSSlime.class, "Slime", 55); + } + + @SuppressWarnings("rawtypes") + public void registerCustomEntity(Class entityClass, String name, int id) throws Exception { + if (VersionUtils.isMCPCOrCauldron()) { + // MCPC+ / Cauldron entity registration. + Class entityTypesClass = Class.forName("net.minecraft.server.v1_7_R4.EntityTypes"); + ReflectionUtils.putInPrivateStaticMap(entityTypesClass, "field_75626_c", entityClass, name); + ReflectionUtils.putInPrivateStaticMap(entityTypesClass, "field_75624_e", entityClass, Integer.valueOf(id)); + } else { + // Normal entity registration. + ReflectionUtils.putInPrivateStaticMap(EntityTypes.class, "d", entityClass, name); + ReflectionUtils.putInPrivateStaticMap(EntityTypes.class, "f", entityClass, Integer.valueOf(id)); + } + } + + @Override + public NMSHorse spawnNMSHorse(org.bukkit.World world, double x, double y, double z, CraftHologramLine parentPiece) { + WorldServer nmsWorld = ((CraftWorld) world).getHandle(); + EntityNMSHorse invisibleHorse = new EntityNMSHorse(nmsWorld, parentPiece); + invisibleHorse.setLocationNMS(x, y, z); + if (!nmsWorld.addEntity(invisibleHorse, SpawnReason.CUSTOM)) { + DebugHandler.handleSpawnFail(parentPiece); + } + return invisibleHorse; + } + + @Override + public NMSWitherSkull spawnNMSWitherSkull(org.bukkit.World bukkitWorld, double x, double y, double z, CraftHologramLine parentPiece) { + WorldServer nmsWorld = ((CraftWorld) bukkitWorld).getHandle(); + EntityNMSWitherSkull staticWitherSkull = new EntityNMSWitherSkull(nmsWorld, parentPiece); + staticWitherSkull.setLocationNMS(x, y, z); + if (!nmsWorld.addEntity(staticWitherSkull, SpawnReason.CUSTOM)) { + DebugHandler.handleSpawnFail(parentPiece); + } + return staticWitherSkull; + } + + @Override + public NMSItem spawnNMSItem(org.bukkit.World bukkitWorld, double x, double y, double z, CraftItemLine parentPiece, ItemStack stack) { + WorldServer nmsWorld = ((CraftWorld) bukkitWorld).getHandle(); + EntityNMSItem customItem = new EntityNMSItem(nmsWorld, parentPiece); + customItem.setLocationNMS(x, y, z); + customItem.setItemStackNMS(stack); + if (!nmsWorld.addEntity(customItem, SpawnReason.CUSTOM)) { + DebugHandler.handleSpawnFail(parentPiece); + } + return customItem; + } + + @Override + public EntityNMSSlime spawnNMSSlime(org.bukkit.World bukkitWorld, double x, double y, double z, CraftTouchSlimeLine parentPiece) { + WorldServer nmsWorld = ((CraftWorld) bukkitWorld).getHandle(); + EntityNMSSlime touchSlime = new EntityNMSSlime(nmsWorld, parentPiece); + touchSlime.setLocationNMS(x, y, z); + if (!nmsWorld.addEntity(touchSlime, SpawnReason.CUSTOM)) { + DebugHandler.handleSpawnFail(parentPiece); + } + return touchSlime; + } + + @Override + public boolean isNMSEntityBase(org.bukkit.entity.Entity bukkitEntity) { + return ((CraftEntity) bukkitEntity).getHandle() instanceof NMSEntityBase; + } + + @Override + public NMSEntityBase getNMSEntityBase(org.bukkit.entity.Entity bukkitEntity) { + + Entity nmsEntity = ((CraftEntity) bukkitEntity).getHandle(); + if (nmsEntity instanceof NMSEntityBase) { + return ((NMSEntityBase) nmsEntity); + } + + return null; + } + + @Override + public FancyMessage newFancyMessage(String text) { + return new FancyMessageImpl(text); + } + + @Override + public boolean hasChatHoverFeature() { + return true; + } + + @Override + public NMSArmorStand spawnNMSArmorStand(World world, double x, double y, double z, CraftHologramLine parentPiece) { + throw new NotImplementedException("Method can only be used on 1.8 or higher"); + } +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_8_R1/CraftNMSArmorStand.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_8_R1/CraftNMSArmorStand.java new file mode 100644 index 00000000..03f01c42 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_8_R1/CraftNMSArmorStand.java @@ -0,0 +1,67 @@ +package com.gmail.filoghost.holographicdisplays.nms.v1_8_R1; + +import java.util.Collection; + +import org.bukkit.EntityEffect; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_8_R1.CraftServer; +import org.bukkit.craftbukkit.v1_8_R1.entity.CraftArmorStand; +import org.bukkit.entity.Entity; +import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; +import org.bukkit.inventory.ItemStack; +import org.bukkit.potion.PotionEffect; +import org.bukkit.util.EulerAngle; +import org.bukkit.util.Vector; + +public class CraftNMSArmorStand extends CraftArmorStand { + + public CraftNMSArmorStand(CraftServer server, EntityNMSArmorStand entity) { + super(server, entity); + } + + // Disallow all the bukkit methods. + + @Override + public void remove() { + // Cannot be removed, this is the most important to override. + } + + // Methods from Armor stand class + @Override public void setArms(boolean arms) { } + @Override public void setBasePlate(boolean basePlate) { } + @Override public void setBodyPose(EulerAngle pose) { } + @Override public void setBoots(ItemStack item) { } + @Override public void setChestplate(ItemStack item) { } + @Override public void setGravity(boolean gravity) { } + @Override public void setHeadPose(EulerAngle pose) { } + @Override public void setHelmet(ItemStack item) { } + @Override public void setItemInHand(ItemStack item) { } + @Override public void setLeftArmPose(EulerAngle pose) { } + @Override public void setLeftLegPose(EulerAngle pose) { } + @Override public void setLeggings(ItemStack item) { } + @Override public void setRightArmPose(EulerAngle pose) { } + @Override public void setRightLegPose(EulerAngle pose) { } + @Override public void setSmall(boolean small) { } + @Override public void setVisible(boolean visible) { } + + // Methods from LivingEntity class + @Override public boolean addPotionEffect(PotionEffect effect) { return false; } + @Override public boolean addPotionEffect(PotionEffect effect, boolean param) { return false; } + @Override public boolean addPotionEffects(Collection effects) { return false; } + @Override public void setRemoveWhenFarAway(boolean remove) { } + + // Methods from Entity + @Override public void setVelocity(Vector vel) { } + @Override public boolean teleport(Location loc) { return false; } + @Override public boolean teleport(Entity entity) { return false; } + @Override public boolean teleport(Location loc, TeleportCause cause) { return false; } + @Override public boolean teleport(Entity entity, TeleportCause cause) { return false; } + @Override public void setFireTicks(int ticks) { } + @Override public boolean setPassenger(Entity entity) { return false; } + @Override public boolean eject() { return false; } + @Override public boolean leaveVehicle() { return false; } + @Override public void playEffect(EntityEffect effect) { } + @Override public void setCustomName(String name) { } + @Override public void setCustomNameVisible(boolean flag) { } + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_8_R1/CraftNMSItem.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_8_R1/CraftNMSItem.java new file mode 100644 index 00000000..6de5aa1c --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_8_R1/CraftNMSItem.java @@ -0,0 +1,42 @@ +package com.gmail.filoghost.holographicdisplays.nms.v1_8_R1; + +import org.bukkit.EntityEffect; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_8_R1.CraftServer; +import org.bukkit.craftbukkit.v1_8_R1.entity.CraftItem; +import org.bukkit.entity.Entity; +import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; +import org.bukkit.inventory.ItemStack; +import org.bukkit.util.Vector; + +public class CraftNMSItem extends CraftItem { + + public CraftNMSItem(CraftServer server, EntityNMSItem entity) { + super(server, entity); + } + + // Disallow all the bukkit methods. + + @Override + public void remove() { + // Cannot be removed, this is the most important to override. + } + + // Methods from Entity + @Override public void setVelocity(Vector vel) { } + @Override public boolean teleport(Location loc) { return false; } + @Override public boolean teleport(Entity entity) { return false; } + @Override public boolean teleport(Location loc, TeleportCause cause) { return false; } + @Override public boolean teleport(Entity entity, TeleportCause cause) { return false; } + @Override public void setFireTicks(int ticks) { } + @Override public boolean setPassenger(Entity entity) { return false; } + @Override public boolean eject() { return false; } + @Override public boolean leaveVehicle() { return false; } + @Override public void playEffect(EntityEffect effect) { } + @Override public void setCustomName(String name) { } + @Override public void setCustomNameVisible(boolean flag) { } + + // Methods from Item + @Override public void setItemStack(ItemStack stack) { } + @Override public void setPickupDelay(int delay) { } +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_8_R1/CraftNMSSlime.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_8_R1/CraftNMSSlime.java new file mode 100644 index 00000000..19e7578e --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_8_R1/CraftNMSSlime.java @@ -0,0 +1,49 @@ +package com.gmail.filoghost.holographicdisplays.nms.v1_8_R1; + +import java.util.Collection; + +import org.bukkit.EntityEffect; +import org.bukkit.Location; +import org.bukkit.craftbukkit.v1_8_R1.CraftServer; +import org.bukkit.craftbukkit.v1_8_R1.entity.CraftSlime; +import org.bukkit.entity.Entity; +import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; +import org.bukkit.potion.PotionEffect; +import org.bukkit.util.Vector; + +public class CraftNMSSlime extends CraftSlime { + + public CraftNMSSlime(CraftServer server, EntityNMSSlime entity) { + super(server, entity); + } + + // Disallow all the bukkit methods. + + @Override + public void remove() { + // Cannot be removed, this is the most important to override. + } + + // Methods from LivingEntity class + @Override public boolean addPotionEffect(PotionEffect effect) { return false; } + @Override public boolean addPotionEffect(PotionEffect effect, boolean param) { return false; } + @Override public boolean addPotionEffects(Collection effects) { return false; } + @Override public void setRemoveWhenFarAway(boolean remove) { } + + // Methods from Entity + @Override public void setVelocity(Vector vel) { } + @Override public boolean teleport(Location loc) { return false; } + @Override public boolean teleport(Entity entity) { return false; } + @Override public boolean teleport(Location loc, TeleportCause cause) { return false; } + @Override public boolean teleport(Entity entity, TeleportCause cause) { return false; } + @Override public void setFireTicks(int ticks) { } + @Override public boolean setPassenger(Entity entity) { return false; } + @Override public boolean eject() { return false; } + @Override public boolean leaveVehicle() { return false; } + @Override public void playEffect(EntityEffect effect) { } + @Override public void setCustomName(String name) { } + @Override public void setCustomNameVisible(boolean flag) { } + + // Methods from Slime + @Override public void setSize(int size) { } +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_8_R1/EntityNMSArmorStand.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_8_R1/EntityNMSArmorStand.java new file mode 100644 index 00000000..26fe9811 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_8_R1/EntityNMSArmorStand.java @@ -0,0 +1,227 @@ +package com.gmail.filoghost.holographicdisplays.nms.v1_8_R1; + +import net.minecraft.server.v1_8_R1.AxisAlignedBB; +import net.minecraft.server.v1_8_R1.DamageSource; +import net.minecraft.server.v1_8_R1.Entity; +import net.minecraft.server.v1_8_R1.EntityArmorStand; +import net.minecraft.server.v1_8_R1.EntityHuman; +import net.minecraft.server.v1_8_R1.EntityPlayer; +import net.minecraft.server.v1_8_R1.ItemStack; +import net.minecraft.server.v1_8_R1.NBTTagCompound; +import net.minecraft.server.v1_8_R1.PacketPlayOutEntityTeleport; +import net.minecraft.server.v1_8_R1.Vec3D; +import net.minecraft.server.v1_8_R1.World; + +import org.bukkit.craftbukkit.v1_8_R1.entity.CraftEntity; + +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSArmorStand; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSEntityBase; +import com.gmail.filoghost.holographicdisplays.object.line.CraftHologramLine; +import com.gmail.filoghost.holographicdisplays.util.ReflectionUtils; +import com.gmail.filoghost.holographicdisplays.util.Utils; + +public class EntityNMSArmorStand extends EntityArmorStand implements NMSArmorStand { + + private boolean lockTick; + private CraftHologramLine parentPiece; + + public EntityNMSArmorStand(World world, CraftHologramLine parentPiece) { + super(world); + setInvisible(true); + setSmall(true); + setArms(false); + setGravity(true); + setBasePlate(true); + this.parentPiece = parentPiece; + try { + ReflectionUtils.setPrivateField(EntityArmorStand.class, this, "bg", Integer.MAX_VALUE); + } catch (Exception e) { + // There's still the overridden method. + } + forceSetBoundingBox(new NullBoundingBox()); + } + + + @Override + public void b(NBTTagCompound nbttagcompound) { + // Do not save NBT. + } + + @Override + public boolean c(NBTTagCompound nbttagcompound) { + // Do not save NBT. + return false; + } + + @Override + public boolean d(NBTTagCompound nbttagcompound) { + // Do not save NBT. + return false; + } + + @Override + public void e(NBTTagCompound nbttagcompound) { + // Do not save NBT. + } + + + @Override + public boolean isInvulnerable(DamageSource source) { + /* + * The field Entity.invulnerable is private. + * It's only used while saving NBTTags, but since the entity would be killed + * on chunk unload, we prefer to override isInvulnerable(). + */ + return true; + } + + @Override + public void setCustomName(String customName) { + // Locks the custom name. + } + + @Override + public void setCustomNameVisible(boolean visible) { + // Locks the custom name. + } + + @Override + public boolean a(EntityHuman human, Vec3D vec3d) { + // Prevent stand being equipped + return true; + } + + @Override + public boolean d(int i, ItemStack item) { + // Prevent stand being equipped + return false; + } + + @Override + public void setEquipment(int i, ItemStack item) { + // Prevent stand being equipped + } + + @Override + public void a(AxisAlignedBB boundingBox) { + // Do not change it! + } + + public void forceSetBoundingBox(AxisAlignedBB boundingBox) { + super.a(boundingBox); + } + + @Override + public int getId() { + + StackTraceElement[] elements = Thread.currentThread().getStackTrace(); + if (elements.length > 2 && elements[2] != null && elements[2].getFileName().equals("EntityTrackerEntry.java") && elements[2].getLineNumber() > 137 && elements[2].getLineNumber() < 147) { + // Then this method is being called when creating a new packet, we return a fake ID! + return -1; + } + + return super.getId(); + } + + @Override + public void s_() { + if (!lockTick) { + super.s_(); + } + } + + @Override + public void makeSound(String sound, float f1, float f2) { + // Remove sounds. + } + + @Override + public void setCustomNameNMS(String name) { + if (name != null && name.length() > 300) { + name = name.substring(0, 300); + } + super.setCustomName(name); + super.setCustomNameVisible(name != null); + } + + @Override + public String getCustomNameNMS() { + return super.getCustomName(); + } + + + public void callSuperTick() { + super.h(); + } + + @Override + public void setLockTick(boolean lock) { + lockTick = lock; + } + + @Override + public void die() { + setLockTick(false); + super.die(); + } + + @Override + public CraftEntity getBukkitEntity() { + if (super.bukkitEntity == null) { + this.bukkitEntity = new CraftNMSArmorStand(this.world.getServer(), this); + } + return this.bukkitEntity; + } + + @Override + public void killEntityNMS() { + die(); + } + + @Override + public void setLocationNMS(double x, double y, double z) { + super.setPosition(x, y, z); + + + // Send a packet near to update the position. + PacketPlayOutEntityTeleport teleportPacket = new PacketPlayOutEntityTeleport(this); + + for (Object obj : this.world.players) { + if (obj instanceof EntityPlayer) { + EntityPlayer nmsPlayer = (EntityPlayer) obj; + + double distanceSquared = Utils.square(nmsPlayer.locX - this.locX) + Utils.square(nmsPlayer.locZ - this.locZ); + if (distanceSquared < 8192 && nmsPlayer.playerConnection != null) { + nmsPlayer.playerConnection.sendPacket(teleportPacket); + } + } + } + } + + @Override + public boolean isDeadNMS() { + return this.dead; + } + + @Override + public void setPassengerNMS(NMSEntityBase passenger) { + if (passenger instanceof Entity) { + ((Entity) passenger).setPassengerOf(this); + } + } + + @Override + public int getIdNMS() { + return this.getId(); + } + + @Override + public CraftHologramLine getHologramLine() { + return parentPiece; + } + + @Override + public org.bukkit.entity.Entity getBukkitEntityNMS() { + return getBukkitEntity(); + } +} \ No newline at end of file diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_8_R1/EntityNMSItem.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_8_R1/EntityNMSItem.java new file mode 100644 index 00000000..58b047e8 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_8_R1/EntityNMSItem.java @@ -0,0 +1,189 @@ +package com.gmail.filoghost.holographicdisplays.nms.v1_8_R1; + +import net.minecraft.server.v1_8_R1.Blocks; +import net.minecraft.server.v1_8_R1.DamageSource; +import net.minecraft.server.v1_8_R1.EntityHuman; +import net.minecraft.server.v1_8_R1.EntityItem; +import net.minecraft.server.v1_8_R1.EntityPlayer; +import net.minecraft.server.v1_8_R1.ItemStack; +import net.minecraft.server.v1_8_R1.NBTTagCompound; +import net.minecraft.server.v1_8_R1.NBTTagList; +import net.minecraft.server.v1_8_R1.NBTTagString; +import net.minecraft.server.v1_8_R1.World; + +import org.bukkit.craftbukkit.v1_8_R1.entity.CraftEntity; +import org.bukkit.craftbukkit.v1_8_R1.inventory.CraftItemStack; +import org.bukkit.entity.Player; + +import com.gmail.filoghost.holographicdisplays.listener.MainListener; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSItem; +import com.gmail.filoghost.holographicdisplays.object.line.CraftItemLine; +import com.gmail.filoghost.holographicdisplays.util.ItemUtils; + +public class EntityNMSItem extends EntityItem implements NMSItem { + + private boolean lockTick; + private CraftItemLine parentPiece; + + public EntityNMSItem(World world, CraftItemLine piece) { + super(world); + super.pickupDelay = Integer.MAX_VALUE; + this.parentPiece = piece; + } + + @Override + public void s_() { + // Checks every 20 ticks. + if (ticksLived % 20 == 0) { + // The item dies without a vehicle. + if (this.vehicle == null) { + die(); + } + } + + if (!lockTick) { + super.h(); + } + } + + @Override + public ItemStack getItemStack() { + // Dirty method to check if the icon is being picked up + StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace(); + if (stacktrace.length > 2 && stacktrace[2].getClassName().contains("EntityInsentient")) { + return null; // Try to pickup this, dear entity ignoring the pickupDelay! + } + + return super.getItemStack(); + } + + // Method called when a player is near. + @Override + public void d(EntityHuman human) { + + if (human.locY < this.locY - 1.5 || human.locY > this.locY + 1.0) { + // Too low or too high, it's a bit weird. + return; + } + + if (parentPiece.getPickupHandler() != null && human instanceof EntityPlayer) { + MainListener.handleItemLinePickup((Player) human.getBukkitEntity(), parentPiece.getPickupHandler(), parentPiece.getParent()); + // It is never added to the inventory. + } + } + + @Override + public void b(NBTTagCompound nbttagcompound) { + // Do not save NBT. + } + + @Override + public boolean c(NBTTagCompound nbttagcompound) { + // Do not save NBT. + return false; + } + + @Override + public boolean d(NBTTagCompound nbttagcompound) { + // Do not save NBT. + return false; + } + + @Override + public void e(NBTTagCompound nbttagcompound) { + // Do not save NBT. + } + + @Override + public boolean isInvulnerable(DamageSource source) { + /* + * The field Entity.invulnerable is private. + * It's only used while saving NBTTags, but since the entity would be killed + * on chunk unload, we prefer to override isInvulnerable(). + */ + return true; + } + + @Override + public void setLockTick(boolean lock) { + lockTick = lock; + } + + @Override + public void die() { + setLockTick(false); + super.die(); + } + + @Override + public CraftEntity getBukkitEntity() { + if (super.bukkitEntity == null) { + this.bukkitEntity = new CraftNMSItem(this.world.getServer(), this); + } + return this.bukkitEntity; + } + + @Override + public boolean isDeadNMS() { + return this.dead; + } + + @Override + public void killEntityNMS() { + die(); + } + + @Override + public void setLocationNMS(double x, double y, double z) { + super.setPosition(x, y, z); + } + + @Override + public void setItemStackNMS(org.bukkit.inventory.ItemStack stack) { + ItemStack newItem = CraftItemStack.asNMSCopy(stack); + + if (newItem == null) { + newItem = new ItemStack(Blocks.BEDROCK); + } + + if (newItem.getTag() == null) { + newItem.setTag(new NBTTagCompound()); + } + NBTTagCompound display = newItem.getTag().getCompound("display"); + + if (!newItem.getTag().hasKey("display")) { + newItem.getTag().set("display", display); + } + + NBTTagList tagList = new NBTTagList(); + tagList.add(new NBTTagString(ItemUtils.ANTISTACK_LORE)); // Antistack lore + + display.set("Lore", tagList); + newItem.count = 0; + setItemStack(newItem); + } + + @Override + public int getIdNMS() { + return this.getId(); + } + + @Override + public CraftItemLine getHologramLine() { + return parentPiece; + } + + @Override + public void allowPickup(boolean pickup) { + if (pickup) { + super.pickupDelay = 0; + } else { + super.pickupDelay = Integer.MAX_VALUE; + } + } + + @Override + public org.bukkit.entity.Entity getBukkitEntityNMS() { + return getBukkitEntity(); + } +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_8_R1/EntityNMSSlime.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_8_R1/EntityNMSSlime.java new file mode 100644 index 00000000..b202b5f1 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_8_R1/EntityNMSSlime.java @@ -0,0 +1,148 @@ +package com.gmail.filoghost.holographicdisplays.nms.v1_8_R1; + +import net.minecraft.server.v1_8_R1.AxisAlignedBB; +import net.minecraft.server.v1_8_R1.DamageSource; +import net.minecraft.server.v1_8_R1.EntitySlime; +import net.minecraft.server.v1_8_R1.NBTTagCompound; +import net.minecraft.server.v1_8_R1.World; + +import org.bukkit.craftbukkit.v1_8_R1.entity.CraftEntity; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSSlime; +import com.gmail.filoghost.holographicdisplays.object.line.CraftHologramLine; +import com.gmail.filoghost.holographicdisplays.object.line.CraftTouchSlimeLine; + +public class EntityNMSSlime extends EntitySlime implements NMSSlime { + + private boolean lockTick; + private CraftTouchSlimeLine parentPiece; + + public EntityNMSSlime(World world, CraftTouchSlimeLine parentPiece) { + super(world); + super.persistent = true; + a(0.0F, 0.0F); + setSize(1); + setInvisible(true); + this.parentPiece = parentPiece; + forceSetBoundingBox(new NullBoundingBox()); + } + + @Override + public void a(AxisAlignedBB boundingBox) { + // Do not change it! + } + + public void forceSetBoundingBox(AxisAlignedBB boundingBox) { + super.a(boundingBox); + } + + @Override + public void s_() { + // Checks every 20 ticks. + if (ticksLived % 20 == 0) { + // The slime dies without a vehicle. + if (this.vehicle == null) { + die(); + } + } + + if (!lockTick) { + super.s_(); + } + } + + @Override + public void b(NBTTagCompound nbttagcompound) { + // Do not save NBT. + } + + @Override + public boolean c(NBTTagCompound nbttagcompound) { + // Do not save NBT. + return false; + } + + @Override + public boolean d(NBTTagCompound nbttagcompound) { + // Do not save NBT. + return false; + } + + @Override + public void e(NBTTagCompound nbttagcompound) { + // Do not save NBT. + } + + @Override + public boolean isInvulnerable(DamageSource source) { + /* + * The field Entity.invulnerable is private. + * It's only used while saving NBTTags, but since the entity would be killed + * on chunk unload, we prefer to override isInvulnerable(). + */ + return true; + } + + @Override + public void setCustomName(String customName) { + // Locks the custom name. + } + + @Override + public void setCustomNameVisible(boolean visible) { + // Locks the custom name. + } + + @Override + public void makeSound(String sound, float volume, float pitch) { + // Remove sounds. + } + + @Override + public void setLockTick(boolean lock) { + lockTick = lock; + } + + @Override + public void die() { + setLockTick(false); + super.die(); + } + + @Override + public CraftEntity getBukkitEntity() { + if (super.bukkitEntity == null) { + this.bukkitEntity = new CraftNMSSlime(this.world.getServer(), this); + } + return this.bukkitEntity; + } + + @Override + public boolean isDeadNMS() { + return super.dead; + } + + @Override + public void killEntityNMS() { + die(); + } + + @Override + public void setLocationNMS(double x, double y, double z) { + super.setPosition(x, y, z); + } + + @Override + public int getIdNMS() { + return this.getId(); + } + + @Override + public CraftHologramLine getHologramLine() { + return parentPiece; + } + + @Override + public org.bukkit.entity.Entity getBukkitEntityNMS() { + return getBukkitEntity(); + } +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_8_R1/FancyMessageImpl.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_8_R1/FancyMessageImpl.java new file mode 100644 index 00000000..fe8837fe --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_8_R1/FancyMessageImpl.java @@ -0,0 +1,125 @@ +package com.gmail.filoghost.holographicdisplays.nms.v1_8_R1; + +import java.io.IOException; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.List; + +import net.minecraft.server.v1_7_R4.ChatSerializer; +import net.minecraft.server.v1_7_R4.PacketPlayOutChat; + +import org.bukkit.ChatColor; +import org.bukkit.craftbukkit.libs.com.google.gson.stream.JsonWriter; +import org.bukkit.craftbukkit.v1_7_R4.entity.CraftPlayer; +import org.bukkit.entity.Player; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.FancyMessage; + +public class FancyMessageImpl implements FancyMessage { + + private List messageParts; + + public FancyMessageImpl(String firstPartText) { + messageParts = new ArrayList(); + messageParts.add(new MessagePart(firstPartText)); + } + + @Override + public FancyMessageImpl color(ChatColor color) { + if (!color.isColor()) { + throw new IllegalArgumentException(color.name() + " is not a color"); + } + latest().color = color; + return this; + } + + @Override + public FancyMessageImpl style(ChatColor... styles) { + for (ChatColor style : styles) { + if (!style.isFormat()) { + throw new IllegalArgumentException(style.name() + " is not a style"); + } + } + latest().styles = styles; + return this; + } + + @Override + public FancyMessageImpl file(String path) { + onClick("open_file", path); + return this; + } + + @Override + public FancyMessageImpl link(String url) { + onClick("open_url", url); + return this; + } + + @Override + public FancyMessageImpl suggest(String command) { + onClick("suggest_command", command); + return this; + } + + @Override + public FancyMessageImpl command(String command) { + onClick("run_command", command); + return this; + } + + @Override + public FancyMessageImpl tooltip(String text) { + onHover("show_text", text); + return this; + } + + @Override + public FancyMessageImpl then(Object obj) { + messageParts.add(new MessagePart(obj.toString())); + return this; + } + + @Override + public String toJSONString() { + StringWriter stringWriter = new StringWriter(); + JsonWriter json = new JsonWriter(stringWriter); + + try { + if (messageParts.size() == 1) { + latest().writeJson(json); + } else { + json.beginObject().name("text").value("").name("extra").beginArray(); + for (MessagePart part : messageParts) { + part.writeJson(json); + } + json.endArray().endObject(); + } + + } catch (IOException e) { + throw new RuntimeException("invalid message"); + } + return stringWriter.toString(); + } + + @Override + public void send(Player player){ + ((CraftPlayer) player).getHandle().playerConnection.sendPacket(new PacketPlayOutChat(ChatSerializer.a(toJSONString()))); + } + + private MessagePart latest() { + return messageParts.get(messageParts.size() - 1); + } + + private void onClick(String name, String data) { + MessagePart latest = latest(); + latest.clickActionName = name; + latest.clickActionData = data; + } + + private void onHover(String name, String data) { + MessagePart latest = latest(); + latest.hoverActionName = name; + latest.hoverActionData = data; + } + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_8_R1/NmsManagerImpl.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_8_R1/NmsManagerImpl.java new file mode 100644 index 00000000..7736244b --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_8_R1/NmsManagerImpl.java @@ -0,0 +1,121 @@ +package com.gmail.filoghost.holographicdisplays.nms.v1_8_R1; + +import net.minecraft.server.v1_8_R1.Entity; +import net.minecraft.server.v1_8_R1.EntityTypes; +import net.minecraft.server.v1_8_R1.WorldServer; + +import org.apache.commons.lang.NotImplementedException; +import org.bukkit.World; +import org.bukkit.craftbukkit.v1_8_R1.CraftWorld; +import org.bukkit.craftbukkit.v1_8_R1.entity.CraftEntity; +import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason; +import org.bukkit.inventory.ItemStack; + +import com.gmail.filoghost.holographicdisplays.nms.interfaces.FancyMessage; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.NMSManager; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSArmorStand; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSEntityBase; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSHorse; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSItem; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSWitherSkull; +import com.gmail.filoghost.holographicdisplays.object.line.CraftHologramLine; +import com.gmail.filoghost.holographicdisplays.object.line.CraftItemLine; +import com.gmail.filoghost.holographicdisplays.object.line.CraftTouchSlimeLine; +import com.gmail.filoghost.holographicdisplays.util.DebugHandler; +import com.gmail.filoghost.holographicdisplays.util.ReflectionUtils; +import com.gmail.filoghost.holographicdisplays.util.VersionUtils; + +public class NmsManagerImpl implements NMSManager { + + @Override + public void registerCustomEntities() throws Exception { + registerCustomEntity(EntityNMSArmorStand.class, "ArmorStand", 30); + registerCustomEntity(EntityNMSItem.class, "Item", 1); + registerCustomEntity(EntityNMSSlime.class, "Slime", 55); + } + + @SuppressWarnings("rawtypes") + public void registerCustomEntity(Class entityClass, String name, int id) throws Exception { + if (VersionUtils.isMCPCOrCauldron()) { + // MCPC+ / Cauldron entity registration. + Class entityTypesClass = Class.forName("net.minecraft.server.v1_8_R1.EntityTypes"); + ReflectionUtils.putInPrivateStaticMap(entityTypesClass, "field_75626_c", entityClass, name); + ReflectionUtils.putInPrivateStaticMap(entityTypesClass, "field_75624_e", entityClass, Integer.valueOf(id)); + } else { + // Normal entity registration. + ReflectionUtils.putInPrivateStaticMap(EntityTypes.class, "d", entityClass, name); + ReflectionUtils.putInPrivateStaticMap(EntityTypes.class, "f", entityClass, Integer.valueOf(id)); + } + } + + @Override + public NMSHorse spawnNMSHorse(org.bukkit.World world, double x, double y, double z, CraftHologramLine parentPiece) { + throw new NotImplementedException("Method can only be used on 1.7 or lower"); + } + + @Override + public NMSWitherSkull spawnNMSWitherSkull(org.bukkit.World bukkitWorld, double x, double y, double z, CraftHologramLine parentPiece) { + throw new NotImplementedException("Method can only be used on 1.7 or lower"); + } + + @Override + public NMSItem spawnNMSItem(org.bukkit.World bukkitWorld, double x, double y, double z, CraftItemLine parentPiece, ItemStack stack) { + WorldServer nmsWorld = ((CraftWorld) bukkitWorld).getHandle(); + EntityNMSItem customItem = new EntityNMSItem(nmsWorld, parentPiece); + customItem.setLocationNMS(x, y, z); + customItem.setItemStackNMS(stack); + if (!nmsWorld.addEntity(customItem, SpawnReason.CUSTOM)) { + DebugHandler.handleSpawnFail(parentPiece); + } + return customItem; + } + + @Override + public EntityNMSSlime spawnNMSSlime(org.bukkit.World bukkitWorld, double x, double y, double z, CraftTouchSlimeLine parentPiece) { + WorldServer nmsWorld = ((CraftWorld) bukkitWorld).getHandle(); + EntityNMSSlime touchSlime = new EntityNMSSlime(nmsWorld, parentPiece); + touchSlime.setLocationNMS(x, y, z); + if (!nmsWorld.addEntity(touchSlime, SpawnReason.CUSTOM)) { + DebugHandler.handleSpawnFail(parentPiece); + } + return touchSlime; + } + + @Override + public NMSArmorStand spawnNMSArmorStand(World world, double x, double y, double z, CraftHologramLine parentPiece) { + WorldServer nmsWorld = ((CraftWorld) world).getHandle(); + EntityNMSArmorStand invisibleArmorStand = new EntityNMSArmorStand(nmsWorld, parentPiece); + invisibleArmorStand.setLocationNMS(x, y, z); + if (!nmsWorld.addEntity(invisibleArmorStand, SpawnReason.CUSTOM)) { + DebugHandler.handleSpawnFail(parentPiece); + } + return invisibleArmorStand; + } + + @Override + public boolean isNMSEntityBase(org.bukkit.entity.Entity bukkitEntity) { + return ((CraftEntity) bukkitEntity).getHandle() instanceof NMSEntityBase; + } + + @Override + public NMSEntityBase getNMSEntityBase(org.bukkit.entity.Entity bukkitEntity) { + + Entity nmsEntity = ((CraftEntity) bukkitEntity).getHandle(); + if (nmsEntity instanceof NMSEntityBase) { + return ((NMSEntityBase) nmsEntity); + } + + return null; + } + + @Override + public FancyMessage newFancyMessage(String text) { + return new FancyMessageImpl(text); + } + + @Override + public boolean hasChatHoverFeature() { + return true; + } + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_8_R1/NullBoundingBox.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_8_R1/NullBoundingBox.java new file mode 100644 index 00000000..762629be --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/nms/v1_8_R1/NullBoundingBox.java @@ -0,0 +1,75 @@ +package com.gmail.filoghost.holographicdisplays.nms.v1_8_R1; + +import net.minecraft.server.v1_8_R1.AxisAlignedBB; +import net.minecraft.server.v1_8_R1.MovingObjectPosition; +import net.minecraft.server.v1_8_R1.Vec3D; + +public class NullBoundingBox extends AxisAlignedBB { + + public NullBoundingBox() { + super(0, 0, 0, 0, 0, 0); + } + + @Override + public double a() { + return 0.0; + } + + @Override + public double a(AxisAlignedBB arg0, double arg1) { + return 0.0; + } + + @Override + public AxisAlignedBB a(AxisAlignedBB arg0) { + return this; + } + + @Override + public AxisAlignedBB a(double arg0, double arg1, double arg2) { + return this; + } + + @Override + public MovingObjectPosition a(Vec3D arg0, Vec3D arg1) { + return super.a(arg0, arg1); + } + + @Override + public boolean a(Vec3D arg0) { + return false; + } + + @Override + public double b(AxisAlignedBB arg0, double arg1) { + return 0.0; + } + + @Override + public boolean b(AxisAlignedBB arg0) { + return false; + } + + @Override + public double c(AxisAlignedBB arg0, double arg1) { + return 0.0; + } + + @Override + public AxisAlignedBB c(double arg0, double arg1, double arg2) { + return this; + } + + @Override + public AxisAlignedBB grow(double arg0, double arg1, double arg2) { + return this; + } + + @Override + public AxisAlignedBB shrink(double arg0, double arg1, double arg2) { + return this; + } + + + +} \ No newline at end of file diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/object/BackendAPI.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/object/BackendAPI.java new file mode 100644 index 00000000..86b33d30 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/object/BackendAPI.java @@ -0,0 +1,67 @@ +package com.gmail.filoghost.holographicdisplays.object; + +import java.util.Collection; + +import org.bukkit.Location; +import org.bukkit.entity.Entity; +import org.bukkit.plugin.Plugin; + +import com.gmail.filoghost.holographicdisplays.HolographicDisplays; +import com.gmail.filoghost.holographicdisplays.api.Hologram; +import com.gmail.filoghost.holographicdisplays.api.placeholder.PlaceholderReplacer; +import com.gmail.filoghost.holographicdisplays.object.PluginHologram; +import com.gmail.filoghost.holographicdisplays.object.PluginHologramManager; +import com.gmail.filoghost.holographicdisplays.placeholder.Placeholder; +import com.gmail.filoghost.holographicdisplays.placeholder.PlaceholdersRegister; +import com.gmail.filoghost.holographicdisplays.util.Validator; + +public class BackendAPI { + + public static Hologram createHologram(Plugin plugin, Location source) { + Validator.notNull(plugin, "plugin"); + Validator.notNull(source, "source"); + Validator.notNull(source.getWorld(), "source's world"); + + PluginHologram hologram = new PluginHologram(source, plugin); + PluginHologramManager.addHologram(hologram); + + return hologram; + } + + public static boolean registerPlaceholder(Plugin plugin, String textPlaceholder, double refreshRate, PlaceholderReplacer replacer) { + Validator.notNull(textPlaceholder, "textPlaceholder"); + Validator.isTrue(refreshRate >= 0, "refreshRate should be positive"); + Validator.notNull(replacer, "replacer"); + + return PlaceholdersRegister.register(new Placeholder(plugin, textPlaceholder, refreshRate, replacer)); + } + + public static boolean isHologramEntity(Entity bukkitEntity) { + Validator.notNull(bukkitEntity, "bukkitEntity"); + return HolographicDisplays.getNMSManager().isNMSEntityBase(bukkitEntity); + } + + public static Collection getHolograms(Plugin plugin) { + Validator.notNull(plugin, "plugin"); + return PluginHologramManager.getHolograms(plugin); + } + + public static Collection getRegisteredPlaceholders(Plugin plugin) { + Validator.notNull(plugin, "plugin"); + return PlaceholdersRegister.getTextPlaceholdersByPlugin(plugin); + } + + public static boolean unregisterPlaceholder(Plugin plugin, String textPlaceholder) { + Validator.notNull(plugin, "plugin"); + Validator.notNull(textPlaceholder, "textPlaceholder"); + return PlaceholdersRegister.unregister(plugin, textPlaceholder); + } + + public static void unregisterPlaceholders(Plugin plugin) { + Validator.notNull(plugin, "plugin"); + for (String placeholder : getRegisteredPlaceholders(plugin)) { + unregisterPlaceholder(plugin, placeholder); + } + } + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/object/CraftHologram.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/object/CraftHologram.java new file mode 100644 index 00000000..63d0f514 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/object/CraftHologram.java @@ -0,0 +1,410 @@ +package com.gmail.filoghost.holographicdisplays.object; + +import java.util.List; + +import org.bukkit.Chunk; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.inventory.ItemStack; + +import com.gmail.filoghost.holograms.api.TouchHandler; +import com.gmail.filoghost.holograms.api.replacements.OldTouchHandlerWrapper; +import com.gmail.filoghost.holographicdisplays.api.Hologram; +import com.gmail.filoghost.holographicdisplays.api.line.TouchableLine; +import com.gmail.filoghost.holographicdisplays.disk.Configuration; +import com.gmail.filoghost.holographicdisplays.object.line.CraftHologramLine; +import com.gmail.filoghost.holographicdisplays.object.line.CraftItemLine; +import com.gmail.filoghost.holographicdisplays.object.line.CraftTextLine; +import com.gmail.filoghost.holographicdisplays.placeholder.PlaceholdersManager; +import com.gmail.filoghost.holographicdisplays.util.Utils; +import com.gmail.filoghost.holographicdisplays.util.Validator; + +/** + * This class is only used by the plugin itself. Do not attempt to use it. + * It still implements the old API, but it's temporary. + */ +@SuppressWarnings("deprecation") +public class CraftHologram implements Hologram, com.gmail.filoghost.holograms.api.Hologram { + + // Position variables. + private World world; + private double x, y, z; + private int chunkX, chunkZ; + + // The entities that represent lines. + private final List lines; + + private CraftVisibilityManager visibilityManager; + private boolean allowPlaceholders; + private long creationTimestamp; + private boolean deleted; + + public CraftHologram(Location location) { + Validator.notNull(location, "location"); + updateLocation(location.getWorld(), location.getX(), location.getY(), location.getZ()); + + lines = Utils.newList(); + allowPlaceholders = false; + creationTimestamp = System.currentTimeMillis(); + visibilityManager = new CraftVisibilityManager(this); + } + + public boolean isInChunk(Chunk chunk) { + return chunk.getX() == chunkX && chunk.getZ() == chunkZ; + } + + @Override + public World getWorld() { + return world; + } + + @Override + public double getX() { + return x; + } + + @Override + public double getY() { + return y; + } + + @Override + public double getZ() { + return z; + } + + @Override + public Location getLocation() { + return new Location(world, x, y, z); + } + + private void updateLocation(World world, double x, double y, double z) { + Validator.notNull(world, "world"); + + this.world = world; + this.x = x; + this.y = y; + this.z = z; + chunkX = Utils.floor(x) >> 4; + chunkZ = Utils.floor(z) >> 4; + } + + @Override + public boolean isDeleted() { + return deleted; + } + + @Override + public void delete() { + if (!deleted) { + deleted = true; + clearLines(); + } + } + + public List getLinesUnsafe() { + return lines; + } + + @Override + public CraftHologramLine getLine(int index) { + return lines.get(index); + } + + @Override + public CraftTextLine appendTextLine(String text) { + Validator.isTrue(!deleted, "hologram already deleted"); + + CraftTextLine line = new CraftTextLine(this, text); + lines.add(line); + refreshSingleLines(); + return line; + } + + @Override + public CraftItemLine appendItemLine(ItemStack itemStack) { + Validator.isTrue(!deleted, "hologram already deleted"); + Validator.notNull(itemStack, "itemStack"); + + CraftItemLine line = new CraftItemLine(this, itemStack); + lines.add(line); + refreshSingleLines(); + return line; + } + + @Override + public CraftTextLine insertTextLine(int index, String text) { + Validator.isTrue(!deleted, "hologram already deleted"); + + CraftTextLine line = new CraftTextLine(this, text); + lines.add(index, line); + refreshSingleLines(); + return line; + } + + @Override + public CraftItemLine insertItemLine(int index, ItemStack itemStack) { + Validator.isTrue(!deleted, "hologram already deleted"); + Validator.notNull(itemStack, "itemStack"); + + CraftItemLine line = new CraftItemLine(this, itemStack); + lines.add(index, line); + refreshSingleLines(); + return line; + } + + @Override + public void removeLine(int index) { + Validator.isTrue(!deleted, "hologram already deleted"); + + lines.remove(index).despawn(); + refreshSingleLines(); + } + + @Override + public void clearLines() { + for (CraftHologramLine line : lines) { + line.despawn(); + } + + lines.clear(); + } + + @Override + public int size() { + return lines.size(); + } + + + @Override + public CraftVisibilityManager getVisibilityManager() { + return visibilityManager; + } + + + @Override + public long getCreationTimestamp() { + return creationTimestamp; + } + + @Override + public boolean isAllowPlaceholders() { + return allowPlaceholders; + } + + @Override + public void setAllowPlaceholders(boolean allowPlaceholders) { + if (this.allowPlaceholders != allowPlaceholders) { + + if (allowPlaceholders) { + // Now allowed, previously weren't + for (CraftHologramLine line : lines) { + if (line instanceof CraftTextLine) { + PlaceholdersManager.trackIfNecessary((CraftTextLine) line); + } + } + + } else { + + // Now not allowed + for (CraftHologramLine line : lines) { + if (line instanceof CraftTextLine) { + PlaceholdersManager.untrack((CraftTextLine) line); + } + } + } + + this.allowPlaceholders = allowPlaceholders; + } + } + + + public void refreshAll() { + if (world.isChunkLoaded(chunkX, chunkZ)) { + spawnEntities(); + } + } + + public void refreshSingleLines() { + if (world.isChunkLoaded(chunkX, chunkZ)) { + + double currentY = this.y; + boolean first = true; + + for (CraftHologramLine line : lines) { + + if (first) { + first = false; + } else { + currentY -= line.getHeight(); + currentY -= Configuration.spaceBetweenLines; + } + + if (line.isSpawned()) { + line.teleport(x, currentY, z); + } else { + line.spawn(world, x, currentY, z); + if (allowPlaceholders && line instanceof CraftTextLine) { + PlaceholdersManager.trackIfNecessary((CraftTextLine) line); + } + } + } + } + } + + /** + * Forces the entities to spawn, without checking if the chunk is loaded. + */ + public void spawnEntities() { + Validator.isTrue(!deleted, "hologram already deleted"); + + despawnEntities(); + + double currentY = this.y; + boolean first = true; + + for (CraftHologramLine line : lines) { + + if (first) { + first = false; + } else { + currentY -= line.getHeight(); + currentY -= Configuration.spaceBetweenLines; + } + + line.spawn(world, x, currentY, z); + if (allowPlaceholders && line instanceof CraftTextLine) { + PlaceholdersManager.trackIfNecessary((CraftTextLine) line); + } + } + } + + /** + * Called by the PluginHologramManager when the chunk is unloaded. + */ + public void despawnEntities() { + for (CraftHologramLine piece : lines) { + piece.despawn(); + } + } + + @Override + public void teleport(Location location) { + Validator.notNull(location, "location"); + teleport(location.getWorld(), location.getX(), location.getY(), location.getZ()); + } + + @Override + public void teleport(World world, double x, double y, double z) { + Validator.isTrue(!deleted, "hologram already deleted"); + Validator.notNull(world, "world"); + + if (this.world != world) { + updateLocation(world, x, y, z); + despawnEntities(); + refreshAll(); + return; + } + + updateLocation(world, x, y, z); + + double currentY = y; + boolean first = true; + + for (CraftHologramLine line : lines) { + + if (!line.isSpawned()) { + continue; + } + + if (first) { + first = false; + } else { + currentY -= line.getHeight(); + currentY -= Configuration.spaceBetweenLines; + } + + line.teleport(x, currentY, z); + } + } + + @Override + public String toString() { + return "CraftHologram [world=" + world + ", x=" + x + ", y=" + y + ", z=" + z + ", lines=" + lines + ", deleted=" + deleted + "]"; + } + + /** + * Old API methods, will be removed soon + */ + + @Override + public boolean update() { + return true; + } + + @Override + public void hide() { + + } + + @Override + public void addLine(String text) { + appendTextLine(text); + } + + @Override + public void setLine(int index, String text) { + lines.get(index).despawn(); + lines.set(index, new CraftTextLine(this, text)); + } + + @Override + public void insertLine(int index, String text) { + insertLine(index, text); + } + + @Override + public String[] getLines() { + return null; + } + + @Override + public int getLinesLength() { + return size(); + } + + @Override + public void setLocation(Location location) { + teleport(location); + } + + @Override + public void setTouchHandler(TouchHandler handler) { + if (size() > 0) { + TouchableLine line0 = ((TouchableLine) getLine(0)); + + if (handler != null) { + line0.setTouchHandler(new OldTouchHandlerWrapper(this, handler)); + } else { + line0.setTouchHandler(null); + } + } + } + + @Override + public TouchHandler getTouchHandler() { + return null; + } + + @Override + public boolean hasTouchHandler() { + return false; + } + + /** + * About: equals() and hashcode() + * Two holograms can never be equal. Even if they have the same position and the same elements, they are still two different objects. + * The equals and hashcode methods are not overriden, two holograms are equal only if they have the same memory address. + */ + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/object/CraftVisibilityManager.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/object/CraftVisibilityManager.java new file mode 100644 index 00000000..acf74628 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/object/CraftVisibilityManager.java @@ -0,0 +1,160 @@ +package com.gmail.filoghost.holographicdisplays.object; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +import com.gmail.filoghost.holographicdisplays.HolographicDisplays; +import com.gmail.filoghost.holographicdisplays.api.VisibilityManager; +import com.gmail.filoghost.holographicdisplays.bridge.protocollib.ProtocolLibHook; +import com.gmail.filoghost.holographicdisplays.util.Validator; + +public class CraftVisibilityManager implements VisibilityManager { + + private final CraftHologram hologram; + private Map playersVisibilityMap; + private boolean visibleByDefault; + + private static final int VISIBILITY_DISTANCE_SQUARED = 64 * 64; + + public CraftVisibilityManager(CraftHologram hologram) { + Validator.notNull(hologram, "hologram"); + this.hologram = hologram; + this.visibleByDefault = true; + } + + @Override + public boolean isVisibleByDefault() { + return visibleByDefault; + } + + @SuppressWarnings("deprecation") + @Override + public void setVisibleByDefault(boolean visibleByDefault) { + if (this.visibleByDefault != visibleByDefault) { + + boolean oldVisibleByDefault = this.visibleByDefault; + this.visibleByDefault = visibleByDefault; + + for (Player player : Bukkit.getOnlinePlayers()) { + + if (playersVisibilityMap != null && playersVisibilityMap.containsKey(player.getName().toLowerCase())) { + // Has a specific value set + continue; + } + + if (oldVisibleByDefault) { + // If previously was visible, now is NOT visible by default, because the value has changed + sendDestroyPacketIfNear(player, hologram); + } else { + // Opposite case + sendCreatePacketIfNear(player, hologram); + } + } + } + } + + @Override + public void showTo(Player player) { + Validator.notNull(player, "player"); + + boolean wasVisible = isVisibleTo(player); + + if (playersVisibilityMap == null) { + // Lazy initialization + playersVisibilityMap = new ConcurrentHashMap(); + } + + playersVisibilityMap.put(player.getName().toLowerCase(), true); + + if (wasVisible == false) { + sendCreatePacketIfNear(player, hologram); + } + } + + + @Override + public void hideTo(Player player) { + Validator.notNull(player, "player"); + + boolean wasVisible = isVisibleTo(player); + + if (playersVisibilityMap == null) { + // Lazy initialization + playersVisibilityMap = new ConcurrentHashMap(); + } + + playersVisibilityMap.put(player.getName().toLowerCase(), false); + + if (wasVisible == true) { + sendDestroyPacketIfNear(player, hologram); + } + } + + @Override + public boolean isVisibleTo(Player player) { + Validator.notNull(player, "player"); + + if (playersVisibilityMap != null) { + + Boolean value = playersVisibilityMap.get(player.getName().toLowerCase()); + if (value != null) { + return value; + } + } + + return visibleByDefault; + } + + @Override + public void resetVisibility(Player player) { + Validator.notNull(player, "player"); + + if (playersVisibilityMap == null) { + return; + } + + boolean wasVisible = isVisibleTo(player); + + playersVisibilityMap.remove(player.getName().toLowerCase()); + + if (visibleByDefault && !wasVisible) { + sendCreatePacketIfNear(player, hologram); + + } else if (!visibleByDefault && wasVisible) { + sendDestroyPacketIfNear(player, hologram); + } + } + + @Override + public void resetVisibilityAll() { + if (playersVisibilityMap != null) { + playersVisibilityMap.clear(); + playersVisibilityMap = null; + } + } + + private static void sendCreatePacketIfNear(Player player, CraftHologram hologram) { + if (HolographicDisplays.useProtocolLib() && isNear(player, hologram)) { + ProtocolLibHook.sendCreateEntitiesPacket(player, hologram); + } + } + + private static void sendDestroyPacketIfNear(Player player, CraftHologram hologram) { + if (HolographicDisplays.useProtocolLib() && isNear(player, hologram)) { + ProtocolLibHook.sendDestroyEntitiesPacket(player, hologram); + } + } + + private static boolean isNear(Player player, CraftHologram hologram) { + return player.getWorld().equals(hologram.getWorld()) && player.getLocation().distanceSquared(hologram.getLocation()) < VISIBILITY_DISTANCE_SQUARED; + } + + @Override + public String toString() { + return "CraftVisibilityManager [playersMap=" + playersVisibilityMap + ", visibleByDefault=" + visibleByDefault + "]"; + } + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/object/NamedHologram.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/object/NamedHologram.java new file mode 100644 index 00000000..94024264 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/object/NamedHologram.java @@ -0,0 +1,29 @@ +package com.gmail.filoghost.holographicdisplays.object; + +import org.bukkit.Location; + +public class NamedHologram extends CraftHologram { + + private final String name; + + public NamedHologram(Location source, String name) { + super(source); + this.name = name; + } + + public String getName() { + return name; + } + + @Override + public void delete() { + super.delete(); + NamedHologramManager.removeHologram(this); + } + + @Override + public String toString() { + return "NamedHologram [name=" + name + ", super=" + super.toString() + "]"; + } + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/object/NamedHologramManager.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/object/NamedHologramManager.java new file mode 100644 index 00000000..67dbfc01 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/object/NamedHologramManager.java @@ -0,0 +1,79 @@ +package com.gmail.filoghost.holographicdisplays.object; + +import java.util.ArrayList; +import java.util.List; + +import org.bukkit.Chunk; + +import com.gmail.filoghost.holographicdisplays.util.Utils; + +/** + * This class is only used by the plugin itself. Other plugins should just use the API. + */ +public class NamedHologramManager { + + private static List pluginHolograms = Utils.newList(); + + public static void addHologram(NamedHologram hologram) { + pluginHolograms.add(hologram); + } + + public static void removeHologram(NamedHologram hologram) { + pluginHolograms.remove(hologram); + if (hologram.isDeleted()) { + hologram.delete(); + } + } + + public static List getHolograms() { + return new ArrayList(pluginHolograms); + } + + public static NamedHologram getHologram(String name) { + for (NamedHologram hologram : pluginHolograms) { + if (hologram.getName().equals(name)) { + return hologram; + } + } + return null; + } + + public static boolean isExistingHologram(String name) { + return (getHologram(name) != null); + } + + public static void onChunkLoad(Chunk chunk) { + // Load the holograms in that chunk. + for (NamedHologram hologram : pluginHolograms) { + if (hologram.isInChunk(chunk)) { + hologram.spawnEntities(); + } + } + } + + public static void onChunkUnload(Chunk chunk) { + // Hide the holograms in that chunk. + for (NamedHologram hologram : pluginHolograms) { + if (hologram.isInChunk(chunk)) { + hologram.despawnEntities(); + } + } + } + + public static void clearAll() { + List oldHolograms = new ArrayList(pluginHolograms); + pluginHolograms.clear(); + + for (NamedHologram hologram : oldHolograms) { + hologram.delete(); + } + } + + public static int size() { + return pluginHolograms.size(); + } + + public static NamedHologram get(int i) { + return pluginHolograms.get(i); + } +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/object/PluginHologram.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/object/PluginHologram.java new file mode 100644 index 00000000..62763ff7 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/object/PluginHologram.java @@ -0,0 +1,32 @@ +package com.gmail.filoghost.holographicdisplays.object; + +import org.bukkit.Location; +import org.bukkit.plugin.Plugin; + +import com.gmail.filoghost.holographicdisplays.util.Validator; + +/** + * This class is only used by the plugin itself. Do not attempt to use it. + */ +public class PluginHologram extends CraftHologram { + + private Plugin plugin; + + public PluginHologram(Location source, Plugin plugin) { + super(source); + Validator.notNull(plugin, "plugin"); + this.plugin = plugin; + setAllowPlaceholders(true); + } + + public Plugin getOwner() { + return plugin; + } + + @Override + public void delete() { + super.delete(); + PluginHologramManager.removeHologram(this); + } + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/object/PluginHologramManager.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/object/PluginHologramManager.java new file mode 100644 index 00000000..8d952b46 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/object/PluginHologramManager.java @@ -0,0 +1,74 @@ +package com.gmail.filoghost.holographicdisplays.object; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +import org.bukkit.Chunk; +import org.bukkit.plugin.Plugin; + +import com.gmail.filoghost.holographicdisplays.api.Hologram; +import com.gmail.filoghost.holographicdisplays.util.Utils; + +/** + * This class is only used by the plugin itself. Other plugins should just use the API. + */ +public class PluginHologramManager { + + private static List pluginHolograms = Utils.newList(); + + public static void addHologram(PluginHologram hologram) { + pluginHolograms.add(hologram); + } + + public static void removeHologram(PluginHologram hologram) { + pluginHolograms.remove(hologram); + if (hologram.isDeleted()) { + hologram.delete(); + } + } + + public static List getHolograms() { + return new ArrayList(pluginHolograms); + } + + public static Set getHolograms(Plugin plugin) { + Set ownedHolograms = Utils.newSet(); + + for (PluginHologram hologram : pluginHolograms) { + if (hologram.getOwner().equals(plugin)) { + ownedHolograms.add(hologram); + } + } + + return Collections.unmodifiableSet(ownedHolograms); + } + + public static void onChunkLoad(Chunk chunk) { + // Load the holograms in that chunk. + for (PluginHologram hologram : pluginHolograms) { + if (hologram.isInChunk(chunk)) { + hologram.spawnEntities(); + } + } + } + + public static void onChunkUnload(Chunk chunk) { + // Hide the holograms in that chunk. + for (PluginHologram hologram : pluginHolograms) { + if (hologram.isInChunk(chunk)) { + hologram.despawnEntities(); + } + } + } + + public static void clearAll() { + List oldHolograms = new ArrayList(pluginHolograms); + pluginHolograms.clear(); + + for (PluginHologram hologram : oldHolograms) { + hologram.delete(); + } + } +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/object/line/CraftHologramLine.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/object/line/CraftHologramLine.java new file mode 100644 index 00000000..45126c76 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/object/line/CraftHologramLine.java @@ -0,0 +1,53 @@ +package com.gmail.filoghost.holographicdisplays.object.line; + +import org.bukkit.World; + +import com.gmail.filoghost.holographicdisplays.api.line.HologramLine; +import com.gmail.filoghost.holographicdisplays.object.CraftHologram; +import com.gmail.filoghost.holographicdisplays.util.Validator; + +public abstract class CraftHologramLine implements HologramLine { + + private final double height; + private final CraftHologram parent; + + // This field is necessary for teleport. + private boolean isSpawned; + + protected CraftHologramLine(double height, CraftHologram parent) { + this.height = height; + this.parent = parent; + } + + public final double getHeight() { + return height; + } + + @Override + public final CraftHologram getParent() { + return parent; + } + + public void spawn(World world, double x, double y, double z) { + Validator.notNull(world, "world"); + + // Remove the old entities when spawning the new ones. + despawn(); + isSpawned = true; + + // Do nothing, there are no entities in this class. + } + + public void despawn() { + isSpawned = false; + } + + public final boolean isSpawned() { + return isSpawned; + } + + public abstract int[] getEntitiesIDs(); + + public abstract void teleport(double x, double y, double z); + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/object/line/CraftItemLine.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/object/line/CraftItemLine.java new file mode 100644 index 00000000..b6617de1 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/object/line/CraftItemLine.java @@ -0,0 +1,155 @@ +package com.gmail.filoghost.holographicdisplays.object.line; + +import org.apache.commons.lang.ArrayUtils; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.inventory.ItemStack; + +import com.gmail.filoghost.holographicdisplays.HolographicDisplays; +import com.gmail.filoghost.holographicdisplays.api.handler.PickupHandler; +import com.gmail.filoghost.holographicdisplays.api.handler.TouchHandler; +import com.gmail.filoghost.holographicdisplays.api.line.ItemLine; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSItem; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSRideable; +import com.gmail.filoghost.holographicdisplays.object.CraftHologram; +import com.gmail.filoghost.holographicdisplays.util.Offsets; +import com.gmail.filoghost.holographicdisplays.util.Validator; + +public class CraftItemLine extends CraftTouchableLine implements ItemLine { + + private ItemStack itemStack; + private PickupHandler pickupHandler; + + private NMSItem nmsItem; + private NMSRideable nmsVehicle; + + public CraftItemLine(CraftHologram parent, ItemStack itemStack) { + super(0.7, parent); + setItemStack(itemStack); + } + + @Override + public ItemStack getItemStack() { + return itemStack; + } + + @Override + public void setItemStack(ItemStack itemStack) { + Validator.notNull(itemStack, "itemStack"); + Validator.isTrue(itemStack.getType() != Material.AIR, "itemStack's material cannot be AIR"); + this.itemStack = itemStack; + + if (nmsItem != null) { + nmsItem.setItemStackNMS(itemStack); + } + } + + @Override + public PickupHandler getPickupHandler() { + return pickupHandler; + } + + @Override + public void setPickupHandler(PickupHandler pickupHandler) { + this.pickupHandler = pickupHandler; + } + + public void setTouchHandler(TouchHandler touchHandler) { + + if (nmsItem != null) { + + Location loc = nmsItem.getBukkitEntityNMS().getLocation(); + + if (HolographicDisplays.is1_8()) { + super.setTouchHandler(touchHandler, loc.getWorld(), loc.getX(), loc.getY() - Offsets.ARMOR_STAND_WITH_ITEM, loc.getZ()); + } else { + super.setTouchHandler(touchHandler, loc.getWorld(), loc.getX(), loc.getY() - Offsets.WITHER_SKULL_WITH_ITEM, loc.getZ()); + } + + } else { + super.setTouchHandler(touchHandler, null, 0, 0, 0); + } + } + + @Override + public void spawn(World world, double x, double y, double z) { + super.spawn(world, x, y, z); + + if (itemStack != null && itemStack.getType() != Material.AIR) { + + double offset = HolographicDisplays.is1_8() ? Offsets.ARMOR_STAND_WITH_ITEM : Offsets.WITHER_SKULL_WITH_ITEM; + + nmsItem = HolographicDisplays.getNMSManager().spawnNMSItem(world, x, y + offset, z, this, itemStack); + + if (HolographicDisplays.is1_8()) { + nmsVehicle = HolographicDisplays.getNMSManager().spawnNMSArmorStand(world, x, y + offset, z, this); + } else { + nmsVehicle = HolographicDisplays.getNMSManager().spawnNMSWitherSkull(world, x, y + offset, z, this); + } + + nmsVehicle.setPassengerNMS(nmsItem); + + nmsItem.setLockTick(true); + nmsVehicle.setLockTick(true); + } + } + + + @Override + public void despawn() { + super.despawn(); + + if (nmsVehicle != null) { + nmsVehicle.killEntityNMS(); + nmsVehicle = null; + } + + if (nmsItem != null) { + nmsItem.killEntityNMS(); + nmsItem = null; + } + } + + @Override + public void teleport(double x, double y, double z) { + super.teleport(x, y, z); + + double offset = HolographicDisplays.is1_8() ? Offsets.ARMOR_STAND_WITH_ITEM : Offsets.WITHER_SKULL_WITH_ITEM; + + if (nmsVehicle != null) { + nmsVehicle.setLocationNMS(x, y + offset, z); + } + + if (nmsItem != null) { + nmsItem.setLocationNMS(x, y + offset, z); + } + } + + @Override + public int[] getEntitiesIDs() { + if (isSpawned()) { + if (touchSlime != null) { + return ArrayUtils.addAll(new int[] {nmsVehicle.getIdNMS(), nmsItem.getIdNMS()}, touchSlime.getEntitiesIDs()); + } else { + return new int[] {nmsVehicle.getIdNMS(), nmsItem.getIdNMS()}; + } + } else { + return new int[0]; + } + } + + public NMSItem getNmsItem() { + return nmsItem; + } + + public NMSRideable getNmsVehicle() { + return nmsVehicle; + } + + @Override + public String toString() { + return "CraftItemLine [itemStack=" + itemStack + ", pickupHandler=" + pickupHandler + "]"; + } + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/object/line/CraftTextLine.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/object/line/CraftTextLine.java new file mode 100644 index 00000000..9f8177f1 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/object/line/CraftTextLine.java @@ -0,0 +1,148 @@ +package com.gmail.filoghost.holographicdisplays.object.line; + +import org.apache.commons.lang.ArrayUtils; +import org.bukkit.Location; +import org.bukkit.World; + +import com.gmail.filoghost.holographicdisplays.HolographicDisplays; +import com.gmail.filoghost.holographicdisplays.api.handler.TouchHandler; +import com.gmail.filoghost.holographicdisplays.api.line.TextLine; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSNameable; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSRideable; +import com.gmail.filoghost.holographicdisplays.object.CraftHologram; +import com.gmail.filoghost.holographicdisplays.util.Offsets; + +public class CraftTextLine extends CraftTouchableLine implements TextLine { + + private String text; + + private NMSNameable nmsNameble; + + // Legacy code for < 1.7, not used in 1.8 and greater + private NMSRideable nmsSkullVehicle; + + + public CraftTextLine(CraftHologram parent, String text) { + super(0.23, parent); + setText(text); + } + + + @Override + public String getText() { + return text; + } + + @Override + public void setText(String text) { + this.text = text; + + if (nmsNameble != null && text != null && !text.isEmpty()) { + nmsNameble.setCustomNameNMS(text); + } + } + + public void setTouchHandler(TouchHandler touchHandler) { + + if (nmsNameble != null) { + + Location loc = nmsNameble.getBukkitEntityNMS().getLocation(); + + if (HolographicDisplays.is1_8()) { + super.setTouchHandler(touchHandler, loc.getWorld(), loc.getX(), loc.getY() - Offsets.ARMOR_STAND_ALONE, loc.getZ()); + } else { + super.setTouchHandler(touchHandler, loc.getWorld(), loc.getX(), loc.getY() - Offsets.WITHER_SKULL_WITH_HORSE, loc.getZ()); + } + + } else { + super.setTouchHandler(touchHandler, null, 0, 0, 0); + } + } + + @Override + public void spawn(World world, double x, double y, double z) { + super.spawn(world, x, y, z); + + if (HolographicDisplays.is1_8()) { + nmsNameble = HolographicDisplays.getNMSManager().spawnNMSArmorStand(world, x, y + Offsets.ARMOR_STAND_ALONE, z, this); + } else { + nmsNameble = HolographicDisplays.getNMSManager().spawnNMSHorse(world, x, y + Offsets.WITHER_SKULL_WITH_HORSE, z, this); + nmsSkullVehicle = HolographicDisplays.getNMSManager().spawnNMSWitherSkull(world, x, y + Offsets.WITHER_SKULL_WITH_HORSE, z, this); + + nmsSkullVehicle.setPassengerNMS(nmsNameble); + nmsSkullVehicle.setLockTick(true); + } + + if (text != null && !text.isEmpty()) { + nmsNameble.setCustomNameNMS(text); + } + + nmsNameble.setLockTick(true); + } + + + @Override + public void despawn() { + super.despawn(); + + if (nmsSkullVehicle != null) { + nmsSkullVehicle.killEntityNMS(); + nmsSkullVehicle = null; + } + + if (nmsNameble != null) { + nmsNameble.killEntityNMS(); + nmsNameble = null; + } + } + + + @Override + public void teleport(double x, double y, double z) { + super.teleport(x, y, z); + + if (nmsSkullVehicle != null) { + nmsSkullVehicle.setLocationNMS(x, y + Offsets.WITHER_SKULL_WITH_HORSE, z); + } + + if (nmsNameble != null) { + nmsNameble.setLocationNMS(x, y + (HolographicDisplays.is1_8() ? Offsets.ARMOR_STAND_ALONE : Offsets.WITHER_SKULL_WITH_HORSE), z); + } + } + + @Override + public int[] getEntitiesIDs() { + if (isSpawned()) { + if (nmsSkullVehicle != null) { + if (touchSlime != null) { + return ArrayUtils.addAll(new int[] {nmsNameble.getIdNMS(), nmsSkullVehicle.getIdNMS()}, touchSlime.getEntitiesIDs()); + } else { + return new int[] {nmsNameble.getIdNMS(), nmsSkullVehicle.getIdNMS()}; + } + } else { + if (touchSlime != null) { + return ArrayUtils.add(touchSlime.getEntitiesIDs(), nmsNameble.getIdNMS()); + } else { + return new int[] {nmsNameble.getIdNMS()}; + } + } + } else { + return new int[0]; + } + } + + public NMSNameable getNmsNameble() { + return nmsNameble; + } + + public NMSRideable getNmsSkullVehicle() { + return nmsSkullVehicle; + } + + + @Override + public String toString() { + return "CraftTextLine [text=" + text + "]"; + } + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/object/line/CraftTouchSlimeLine.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/object/line/CraftTouchSlimeLine.java new file mode 100644 index 00000000..ffac741b --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/object/line/CraftTouchSlimeLine.java @@ -0,0 +1,106 @@ +package com.gmail.filoghost.holographicdisplays.object.line; + +import org.bukkit.World; + +import com.gmail.filoghost.holographicdisplays.HolographicDisplays; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSRideable; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSSlime; +import com.gmail.filoghost.holographicdisplays.object.CraftHologram; +import com.gmail.filoghost.holographicdisplays.util.Offsets; + +/** + * A touch slime that can be applied to a line. + */ +public class CraftTouchSlimeLine extends CraftHologramLine { + + // The touchable piece associated with this piece + private CraftTouchableLine touchablePiece; + + private NMSSlime nmsSlime; + private NMSRideable nmsVehicle; + + + protected CraftTouchSlimeLine(CraftHologram parent, CraftTouchableLine touchablePiece) { + super(0.5, parent); + this.touchablePiece = touchablePiece; + } + + public CraftTouchableLine getTouchablePiece() { + return touchablePiece; + } + + + @Override + public void spawn(World world, double x, double y, double z) { + super.spawn(world, x, y, z); + + double offset = HolographicDisplays.is1_8() ? Offsets.ARMOR_STAND_WITH_SLIME : Offsets.WITHER_SKULL_WITH_SLIME; + + nmsSlime = HolographicDisplays.getNMSManager().spawnNMSSlime(world, x, y + offset, z, this); + + if (HolographicDisplays.is1_8()) { + nmsVehicle = HolographicDisplays.getNMSManager().spawnNMSArmorStand(world, x, y + offset, z, this); + } else { + nmsVehicle = HolographicDisplays.getNMSManager().spawnNMSWitherSkull(world, x, y + offset, z, this); + } + + nmsVehicle.setPassengerNMS(nmsSlime); + + nmsSlime.setLockTick(true); + nmsVehicle.setLockTick(true); + } + + + @Override + public void despawn() { + super.despawn(); + + if (nmsSlime != null) { + nmsSlime.killEntityNMS(); + nmsSlime = null; + } + + if (nmsVehicle != null) { + nmsVehicle.killEntityNMS(); + nmsVehicle = null; + } + } + + + @Override + public void teleport(double x, double y, double z) { + + double offset = HolographicDisplays.is1_8() ? Offsets.ARMOR_STAND_WITH_SLIME : Offsets.WITHER_SKULL_WITH_SLIME; + + if (nmsVehicle != null) { + nmsVehicle.setLocationNMS(x, y + offset, z); + } + + if (nmsSlime != null) { + nmsSlime.setLocationNMS(x, y + offset, z); + } + } + + @Override + public int[] getEntitiesIDs() { + if (isSpawned()) { + return new int[] {nmsVehicle.getIdNMS(), nmsSlime.getIdNMS()}; + } else { + return new int[0]; + } + } + + public NMSSlime getNmsSlime() { + return nmsSlime; + } + + public NMSRideable getNmsVehicle() { + return nmsVehicle; + } + + @Override + public String toString() { + return "CraftTouchSlimeLine [touchablePiece=" + touchablePiece + "]"; + } + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/object/line/CraftTouchableLine.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/object/line/CraftTouchableLine.java new file mode 100644 index 00000000..4f62d122 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/object/line/CraftTouchableLine.java @@ -0,0 +1,78 @@ +package com.gmail.filoghost.holographicdisplays.object.line; + +import org.bukkit.World; + +import com.gmail.filoghost.holographicdisplays.api.handler.TouchHandler; +import com.gmail.filoghost.holographicdisplays.object.CraftHologram; + +/** + * Useful class that implements TouchablePiece. The downside is that subclasses must extend this, and cannot extend other classes. + * But all the current items are touchable. + */ +public abstract class CraftTouchableLine extends CraftHologramLine { + + protected CraftTouchSlimeLine touchSlime; + private TouchHandler touchHandler; + + + protected CraftTouchableLine(double height, CraftHologram parent) { + super(height, parent); + } + + + protected void setTouchHandler(TouchHandler touchHandler, World world, double x, double y, double z) { + this.touchHandler = touchHandler; + + if (touchHandler != null && touchSlime == null && world != null) { + // If the touch handler was null before and no entity has been spawned, spawn it now. + touchSlime = new CraftTouchSlimeLine(getParent(), this); + touchSlime.spawn(world, x, y + (getHeight() / 2.0 - touchSlime.getHeight() / 2.0), z); + + } else if (touchHandler == null && touchSlime != null) { + // Opposite case, the touch handler was not null and an entity was spawned, but now it's useless. + touchSlime.despawn(); + touchSlime = null; + } + } + + + public TouchHandler getTouchHandler() { + return this.touchHandler; + } + + + @Override + public void spawn(World world, double x, double y, double z) { + super.spawn(world, x, y, z); + + if (touchHandler != null) { + touchSlime = new CraftTouchSlimeLine(getParent(), this); + touchSlime.spawn(world, x, y + (getHeight() / 2.0 - touchSlime.getHeight() / 2.0), z); + } + } + + + @Override + public void despawn() { + super.despawn(); + + if (touchSlime != null) { + touchSlime.despawn(); + touchSlime = null; + } + } + + + @Override + public void teleport(double x, double y, double z) { + if (touchSlime != null) { + touchSlime.teleport(x, y + (getHeight() / 2.0 - touchSlime.getHeight() / 2.0), z); + } + } + + + public CraftTouchSlimeLine getTouchSlime() { + return touchSlime; + } + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/placeholder/AnimationsRegister.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/placeholder/AnimationsRegister.java new file mode 100644 index 00000000..86481491 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/placeholder/AnimationsRegister.java @@ -0,0 +1,87 @@ +package com.gmail.filoghost.holographicdisplays.placeholder; + +import java.io.File; +import java.util.List; +import java.util.Map; + +import org.bukkit.plugin.Plugin; + +import com.gmail.filoghost.holographicdisplays.HolographicDisplays; +import com.gmail.filoghost.holographicdisplays.disk.StringConverter; +import com.gmail.filoghost.holographicdisplays.util.FileUtils; +import com.gmail.filoghost.holographicdisplays.util.Utils; + +public class AnimationsRegister { + + // + private final static Map animations = Utils.newMap(); + + public static void loadAnimations(Plugin plugin) { + animations.clear(); + + File animationFolder = new File(plugin.getDataFolder(), "animations"); + if (!animationFolder.isDirectory()) { + animationFolder.mkdirs(); + plugin.saveResource("animations/example.txt", false); + return; + } + + for (File file : animationFolder.listFiles()) { + + try { + List lines = FileUtils.readLines(file); + if (lines.size() == 0) { + continue; + } + + double speed = 0.5; + boolean validSpeedFound = false; + + String firstLine = lines.get(0).trim(); + if (firstLine.toLowerCase().startsWith("speed:")) { + + // Do not consider it. + lines.remove(0); + + firstLine = firstLine.substring("speed:".length()).trim(); + + try { + speed = Double.parseDouble(firstLine); + validSpeedFound = true; + } catch (NumberFormatException e) { } + } + + if (!validSpeedFound) { + plugin.getLogger().warning("Could not find a valid 'speed: ' in the first line of the file '" + file.getName() + "'. Default speed of 0.5 seconds will be used."); + } + + if (lines.isEmpty()) { + lines.add("[No lines: " + file.getName() + "]"); + plugin.getLogger().warning("Could not find any line in '" + file.getName() + "' (excluding the speed). You should add at least one more line."); + } + + // Replace placeholders. + for (int i = 0; i < lines.size(); i++) { + lines.set(i, StringConverter.toReadableFormat(lines.get(i))); + } + + animations.put(file.getName(), new Placeholder(HolographicDisplays.getInstance(), file.getName(), speed, new CyclicPlaceholderReplacer(lines.toArray(new String[lines.size()])))); + plugin.getLogger().info("Loaded animation '" + file.getName() + "', speed " + speed + "."); + + } catch (Exception e) { + e.printStackTrace(); + plugin.getLogger().severe("Couldn't load the file '" + file.getName() + "'!"); + } + } + } + + + public static Map getAnimations() { + return animations; + } + + public static Placeholder getAnimation(String name) { + return animations.get(name); + } + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/placeholder/CyclicPlaceholderReplacer.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/placeholder/CyclicPlaceholderReplacer.java new file mode 100644 index 00000000..6ebc8c67 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/placeholder/CyclicPlaceholderReplacer.java @@ -0,0 +1,27 @@ +package com.gmail.filoghost.holographicdisplays.placeholder; + +import com.gmail.filoghost.holographicdisplays.api.placeholder.PlaceholderReplacer; + +public class CyclicPlaceholderReplacer implements PlaceholderReplacer { + + String[] frames; + private int index; + + public CyclicPlaceholderReplacer(String[] frames) { + this.frames = frames; + index = 0; + } + + @Override + public String update() { + String result = frames[index]; + + index++; + if (index >= frames.length) { + index = 0; + } + + return result; + } + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/placeholder/DynamicLineData.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/placeholder/DynamicLineData.java new file mode 100644 index 00000000..5c2199ac --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/placeholder/DynamicLineData.java @@ -0,0 +1,73 @@ +package com.gmail.filoghost.holographicdisplays.placeholder; + +import java.util.Map; +import java.util.Set; + +import com.gmail.filoghost.holographicdisplays.api.placeholder.PlaceholderReplacer; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSNameable; +import com.gmail.filoghost.holographicdisplays.util.Utils; +import com.gmail.filoghost.holographicdisplays.util.Validator; + +public class DynamicLineData { + + private final NMSNameable entity; + private final String originalName; + + private Set placeholders; + private final Map animations; + private final Map replacers; + + public DynamicLineData(NMSNameable entity, String originalName) { + Validator.notNull(entity, "entity"); + + this.entity = entity; + this.originalName = originalName; + placeholders = Utils.newSet(); + animations = Utils.newMap(); + replacers = Utils.newMap(); + } + + public NMSNameable getEntity() { + return entity; + } + + public String getOriginalName() { + return originalName; + } + + public void setPlaceholders(Set placeholders) { + this.placeholders = placeholders; + } + + public Set getPlaceholders() { + return placeholders; + } + + public Map getReplacers() { + return replacers; + } + + public Map getAnimations() { + return animations; + } + + @Override + public int hashCode() { + return entity.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + DynamicLineData other = (DynamicLineData) obj; + return this.entity == other.entity; + } + + + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/placeholder/Placeholder.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/placeholder/Placeholder.java new file mode 100644 index 00000000..586eb71f --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/placeholder/Placeholder.java @@ -0,0 +1,88 @@ +package com.gmail.filoghost.holographicdisplays.placeholder; + +import org.bukkit.plugin.Plugin; + +import com.gmail.filoghost.holographicdisplays.api.placeholder.PlaceholderReplacer; + +public class Placeholder { + + // The plugin that owns this placeholder. + private final Plugin owner; + + // The placeholder itself, something like {onlinePlayers}. Case sensitive! + private final String textPlaceholder; + + // How many tenths of second between each refresh. + private int tenthsToRefresh; + + // This is the current replacement for this placeholder. + private String currentReplacement; + + private PlaceholderReplacer replacer; + + public Placeholder(Plugin owner, String textPlaceholder, double refreshRate, PlaceholderReplacer replacer) { + this.owner = owner; + this.textPlaceholder = textPlaceholder; + this.tenthsToRefresh = refreshRate <= 0.1 ? 1 : (int) (refreshRate * 10.0); + this.replacer = replacer; + this.currentReplacement = ""; + } + + public Plugin getOwner() { + return owner; + } + + public int getTenthsToRefresh() { + return tenthsToRefresh; + } + + public void setTenthsToRefresh(int tenthsToRefresh) { + this.tenthsToRefresh = tenthsToRefresh; + } + + public String getTextPlaceholder() { + return textPlaceholder; + } + + public String getCurrentReplacement() { + return currentReplacement; + } + + public void setCurrentReplacement(String replacement) { + this.currentReplacement = replacement != null ? replacement : "null"; + } + + public PlaceholderReplacer getReplacer() { + return replacer; + } + + public void setReplacer(PlaceholderReplacer replacer) { + this.replacer = replacer; + } + + public void update() { + setCurrentReplacement(replacer.update()); + } + + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + + if (obj instanceof Placeholder) { + return ((Placeholder) obj).textPlaceholder.equals(this.textPlaceholder); + } + + return false; + } + + + @Override + public int hashCode() { + return textPlaceholder.hashCode(); + } + + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/placeholder/PlaceholdersManager.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/placeholder/PlaceholdersManager.java new file mode 100644 index 00000000..1fe79a37 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/placeholder/PlaceholdersManager.java @@ -0,0 +1,258 @@ +package com.gmail.filoghost.holographicdisplays.placeholder; + +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.bukkit.Bukkit; +import org.bukkit.plugin.Plugin; + +import com.gmail.filoghost.holographicdisplays.api.placeholder.PlaceholderReplacer; +import com.gmail.filoghost.holographicdisplays.bridge.bungeecord.BungeeServerTracker; +import com.gmail.filoghost.holographicdisplays.nms.interfaces.entity.NMSNameable; +import com.gmail.filoghost.holographicdisplays.object.line.CraftTextLine; +import com.gmail.filoghost.holographicdisplays.task.WorldPlayerCounterTask; +import com.gmail.filoghost.holographicdisplays.util.Utils; + +public class PlaceholdersManager { + + private static long elapsedTenthsOfSecond; + protected static Set linesToUpdate = Utils.newSet(); + + private static final Pattern BUNGEE_ONLINE_PATTERN = makePlaceholderWithArgsPattern("online"); + private static final Pattern ANIMATION_PATTERN = makePlaceholderWithArgsPattern("animation"); + private static final Pattern WORLD_PATTERN = makePlaceholderWithArgsPattern("world"); + + private static Pattern makePlaceholderWithArgsPattern(String prefix) { + return Pattern.compile("(\\{" + Pattern.quote(prefix) + ":)(.+?)(\\})"); + } + + private static String extractArgumentFromPlaceholder(Matcher matcher) { + return matcher.group(2).trim(); + } + + + public static void load(Plugin plugin) { + + Bukkit.getScheduler().scheduleSyncRepeatingTask(plugin, new Runnable() { + + @Override + public void run() { + + for (Placeholder placeholder : PlaceholdersRegister.getPlaceholders()) { + if (elapsedTenthsOfSecond % placeholder.getTenthsToRefresh() == 0) { + placeholder.update(); + } + } + + for (Placeholder placeholder : AnimationsRegister.getAnimations().values()) { + if (elapsedTenthsOfSecond % placeholder.getTenthsToRefresh() == 0) { + placeholder.update(); + } + } + + Iterator iter = linesToUpdate.iterator(); + DynamicLineData currentLineData; + + while (iter.hasNext()) { + currentLineData = iter.next(); + + if (currentLineData.getEntity().isDeadNMS()) { + iter.remove(); + } else { + updatePlaceholders(currentLineData); + } + } + + elapsedTenthsOfSecond++; + } + + }, 2L, 2L); + } + + + public static void untrackAll() { + linesToUpdate.clear(); + } + + public static void untrack(CraftTextLine line) { + + if (line == null || !line.isSpawned()) { + return; + } + + Iterator iter = linesToUpdate.iterator(); + while (iter.hasNext()) { + DynamicLineData data = iter.next(); + if (data.getEntity() == line.getNmsNameble()) { + iter.remove(); + data.getEntity().setCustomNameNMS(data.getOriginalName()); + } + } + } + + public static void trackIfNecessary(CraftTextLine line) { + + NMSNameable nameableEntity = line.getNmsNameble(); + String name = line.getText(); + + if (nameableEntity == null) { + return; + } + + boolean updateName = false; + + if (name == null || name.isEmpty()) { + return; + } + + // Lazy initialization. + Set normalPlaceholders = null; + + Map bungeeOnlinePlayersReplacers = null; + Map worldsOnlinePlayersReplacers = null; + Map animationsPlaceholders = null; + + Matcher matcher; + + for (Placeholder placeholder : PlaceholdersRegister.getPlaceholders()) { + + if (name.contains(placeholder.getTextPlaceholder())) { + + if (normalPlaceholders == null) { + normalPlaceholders = Utils.newSet(); + } + + normalPlaceholders.add(placeholder); + } + } + + + // Players in a world count pattern. + matcher = WORLD_PATTERN.matcher(name); + while (matcher.find()) { + + if (worldsOnlinePlayersReplacers == null) { + worldsOnlinePlayersReplacers = Utils.newMap(); + } + + final String worldName = extractArgumentFromPlaceholder(matcher); + worldsOnlinePlayersReplacers.put(matcher.group(), new PlaceholderReplacer() { + + @Override + public String update() { + return WorldPlayerCounterTask.getCount(worldName); + } + }); + } + + // BungeeCord online pattern. + matcher = BUNGEE_ONLINE_PATTERN.matcher(name); + while (matcher.find()) { + + if (bungeeOnlinePlayersReplacers == null) { + bungeeOnlinePlayersReplacers = Utils.newMap(); + } + + final String serverName = extractArgumentFromPlaceholder(matcher); + BungeeServerTracker.track(serverName); // Track this server. + + // Add it to tracked servers. + bungeeOnlinePlayersReplacers.put(matcher.group(), new PlaceholderReplacer() { + + @Override + public String update() { + return String.valueOf(BungeeServerTracker.getPlayersOnline(serverName)); + } + }); + } + + // Animation pattern. + matcher = ANIMATION_PATTERN.matcher(name); + while (matcher.find()) { + + String fileName = extractArgumentFromPlaceholder(matcher); + Placeholder animation = AnimationsRegister.getAnimation(fileName); + + // If exists... + if (animation != null) { + + if (animationsPlaceholders == null) { + animationsPlaceholders = Utils.newMap(); + } + + animationsPlaceholders.put(matcher.group(), animation); + + } else { + name = name.replace(matcher.group(), "[Animation not found: " + fileName + "]"); + updateName = true; + } + } + + if (normalPlaceholders != null || bungeeOnlinePlayersReplacers != null || worldsOnlinePlayersReplacers != null || animationsPlaceholders != null) { + DynamicLineData lineData = new DynamicLineData(nameableEntity, name); + + if (normalPlaceholders != null) { + lineData.setPlaceholders(normalPlaceholders); + } + + if (bungeeOnlinePlayersReplacers != null) { + lineData.getReplacers().putAll(bungeeOnlinePlayersReplacers); + } + + if (worldsOnlinePlayersReplacers != null) { + lineData.getReplacers().putAll(worldsOnlinePlayersReplacers); + } + + if (animationsPlaceholders != null) { + lineData.getAnimations().putAll(animationsPlaceholders); + } + + // It could be already tracked! + if (linesToUpdate.add(lineData)) { + updatePlaceholders(lineData); + } + + } else { + + // The name needs to be updated anyways. + if (updateName) { + nameableEntity.setCustomNameNMS(name); + } + } + } + + + private static void updatePlaceholders(DynamicLineData lineData) { + + String oldCustomName = lineData.getEntity().getCustomNameNMS(); + String newCustomName = lineData.getOriginalName(); + + if (!lineData.getPlaceholders().isEmpty()) { + for (Placeholder placeholder : lineData.getPlaceholders()) { + newCustomName = newCustomName.replace(placeholder.getTextPlaceholder(), placeholder.getCurrentReplacement()); + } + } + + if (!lineData.getReplacers().isEmpty()) { + for (Entry entry : lineData.getReplacers().entrySet()) { + newCustomName = newCustomName.replace(entry.getKey(), entry.getValue().update()); + } + } + + if (!lineData.getAnimations().isEmpty()) { + for (Entry entry : lineData.getAnimations().entrySet()) { + newCustomName = newCustomName.replace(entry.getKey(), entry.getValue().getCurrentReplacement()); + } + } + + // Update only if needed, don't send useless packets. + if (!oldCustomName.equals(newCustomName)) { + lineData.getEntity().setCustomNameNMS(newCustomName); + } + } + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/placeholder/PlaceholdersRegister.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/placeholder/PlaceholdersRegister.java new file mode 100644 index 00000000..d9113b08 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/placeholder/PlaceholdersRegister.java @@ -0,0 +1,115 @@ +package com.gmail.filoghost.holographicdisplays.placeholder; + +import java.util.Date; +import java.util.Iterator; +import java.util.Set; + +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.plugin.Plugin; + +import com.gmail.filoghost.holographicdisplays.HolographicDisplays; +import com.gmail.filoghost.holographicdisplays.api.placeholder.PlaceholderReplacer; +import com.gmail.filoghost.holographicdisplays.disk.Configuration; +import com.gmail.filoghost.holographicdisplays.util.Utils; + +public class PlaceholdersRegister { + + private static final Set placeholders = Utils.newSet(); + + // Register the default placeholders statically. + static { + + register(new Placeholder(HolographicDisplays.getInstance(), "{online}", 1.0, new PlaceholderReplacer() { + + @SuppressWarnings("deprecation") + @Override + public String update() { + return String.valueOf(Bukkit.getOnlinePlayers().length); + } + })); + + register(new Placeholder(HolographicDisplays.getInstance(), "{max_players}", 10.0, new PlaceholderReplacer() { + + @Override + public String update() { + return String.valueOf(Bukkit.getMaxPlayers()); + } + })); + + register(new Placeholder(HolographicDisplays.getInstance(), "{motd}", 60.0, new PlaceholderReplacer() { + + @Override + public String update() { + return Bukkit.getMotd(); + } + })); + + register(new Placeholder(HolographicDisplays.getInstance(), "{time}", 0.9, new PlaceholderReplacer() { + + @Override + public String update() { + return Configuration.timeFormat.format(new Date()); + } + })); + + register(new Placeholder(HolographicDisplays.getInstance(), "&u", 0.2, new CyclicPlaceholderReplacer(Utils.arrayToStrings( + ChatColor.RED, + ChatColor.GOLD, + ChatColor.YELLOW, + ChatColor.GREEN, + ChatColor.AQUA, + ChatColor.LIGHT_PURPLE + )))); + } + + + public static boolean register(Placeholder placeholder) { + if (placeholders.contains(placeholder)) { + return false; + } + + placeholders.add(placeholder); + return true; + } + + public static Set getTextPlaceholdersByPlugin(Plugin plugin) { + Set found = Utils.newSet(); + + for (Placeholder placeholder : placeholders) { + if (placeholder.getOwner().equals(plugin)) { + found.add(placeholder.getTextPlaceholder()); + } + } + + return found; + } + + public static boolean unregister(Plugin plugin, String textPlaceholder) { + + Iterator iter = placeholders.iterator(); + + while (iter.hasNext()) { + Placeholder placeholder = iter.next(); + + if (placeholder.getOwner().equals(plugin) && placeholder.getTextPlaceholder().equals(textPlaceholder)) { + iter.remove(); + + for (DynamicLineData data : PlaceholdersManager.linesToUpdate) { + if (data.getPlaceholders().contains(placeholder)) { + data.getPlaceholders().remove(placeholder); + } + } + + return true; + } + } + + return false; + } + + protected static Set getPlaceholders() { + return placeholders; + } + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/task/BungeeCleanupTask.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/task/BungeeCleanupTask.java new file mode 100644 index 00000000..39dd22f1 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/task/BungeeCleanupTask.java @@ -0,0 +1,29 @@ +package com.gmail.filoghost.holographicdisplays.task; + +import java.util.Iterator; +import java.util.Map.Entry; + +import com.gmail.filoghost.holographicdisplays.bridge.bungeecord.BungeeServerInfo; +import com.gmail.filoghost.holographicdisplays.bridge.bungeecord.BungeeServerTracker; + +/** + * A task to remove unused server data in the server tracker. + */ +public class BungeeCleanupTask implements Runnable { + + @Override + public void run() { + + Iterator> iter = BungeeServerTracker.getTrackedServers().entrySet().iterator(); + + while (iter.hasNext()) { + long lastRequest = iter.next().getValue().getLastRequest(); + + if (lastRequest != 0 && System.currentTimeMillis() - lastRequest > 600000) { // 10 * 60 * 1000 = 10 minutes. + // Don't track that server anymore. + iter.remove(); + } + } + } + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/task/WorldPlayerCounterTask.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/task/WorldPlayerCounterTask.java new file mode 100644 index 00000000..2f6c26ba --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/task/WorldPlayerCounterTask.java @@ -0,0 +1,27 @@ +package com.gmail.filoghost.holographicdisplays.task; + +import java.util.Map; + +import org.bukkit.Bukkit; +import org.bukkit.World; + +import com.gmail.filoghost.holographicdisplays.util.Utils; + +public class WorldPlayerCounterTask implements Runnable { + + private static Map worlds = Utils.newMap(); + + @Override + public void run() { + worlds.clear(); + + for (World world : Bukkit.getWorlds()) { + worlds.put(world.getName(), world.getPlayers().size()); + } + } + + public static String getCount(String world) { + Integer count = worlds.get(world); + return count != null ? count.toString() : "[World \"" + world + "\" not found]"; + } +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/util/DebugHandler.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/util/DebugHandler.java new file mode 100644 index 00000000..058a8951 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/util/DebugHandler.java @@ -0,0 +1,17 @@ +package com.gmail.filoghost.holographicdisplays.util; + +import com.gmail.filoghost.holographicdisplays.HolographicDisplays; +import com.gmail.filoghost.holographicdisplays.api.line.HologramLine; +import com.gmail.filoghost.holographicdisplays.disk.Configuration; + +public class DebugHandler { + + public static void handleSpawnFail(HologramLine parentPiece) { + if (Configuration.debug) { + HolographicDisplays.getInstance().getLogger().warning("[Debug] Coulnd't spawn entity for this hologram: " + parentPiece.getParent().toString()); + } + } + + + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/util/FileUtils.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/util/FileUtils.java new file mode 100644 index 00000000..5d219811 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/util/FileUtils.java @@ -0,0 +1,72 @@ +package com.gmail.filoghost.holographicdisplays.util; + +import java.awt.image.BufferedImage; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; + +import javax.imageio.ImageIO; + +import com.gmail.filoghost.holographicdisplays.exception.UnreadableImageException; + + +public class FileUtils { + + public static List readLines(File file) throws IOException, Exception { + + BufferedReader br = null; + + try { + + List lines = new ArrayList(); + + if (!file.exists()) { + throw new FileNotFoundException(); + } + + br = new BufferedReader(new FileReader(file)); + String line = br.readLine(); + + while (line != null) { + lines.add(line); + line = br.readLine(); + } + + return lines; + + } finally { + if (br != null) { + try { + br.close(); + } catch (IOException e) { } + } + } + } + + public static BufferedImage readImage(File file) throws UnreadableImageException, IOException, Exception { + + BufferedImage image = ImageIO.read(file); + + if (image == null) { + throw new UnreadableImageException(); + } + + return image; + } + + public static BufferedImage readImage(URL url) throws UnreadableImageException, IOException, Exception { + + BufferedImage image = ImageIO.read(url); + + if (image == null) { + throw new UnreadableImageException(); + } + + return image; + } +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/util/ItemUtils.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/util/ItemUtils.java new file mode 100644 index 00000000..785951d2 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/util/ItemUtils.java @@ -0,0 +1,262 @@ +package com.gmail.filoghost.holographicdisplays.util; + +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.regex.Pattern; + +import org.bukkit.ChatColor; +import org.bukkit.Material; +import com.google.common.collect.Maps; + +public class ItemUtils { + + // This is used on hologram icons, to prevent vanilla items from merging with them. + public static final String ANTISTACK_LORE = ChatColor.BLACK.toString() + Math.random(); + + // A map with formatter materials (lowercase and without dashes) for fast access. + private static Map materialMap = new HashMap(); + + private static Pattern stripSpacingSymbolsPattern = Pattern.compile("[_ \\-]+"); + + static { + // Default material names are ugly. + Map tempMap = Maps.newHashMap(); + + tempMap.put("iron bar", Material.IRON_FENCE); + tempMap.put("iron bars", Material.IRON_FENCE); + tempMap.put("glass pane", Material.THIN_GLASS); + tempMap.put("nether wart", Material.NETHER_STALK); + tempMap.put("nether warts", Material.NETHER_STALK); + tempMap.put("slab", Material.STEP); + tempMap.put("double slab", Material.DOUBLE_STEP); + tempMap.put("stone brick", Material.SMOOTH_BRICK); + tempMap.put("stone bricks", Material.SMOOTH_BRICK); + tempMap.put("stone stair", Material.SMOOTH_STAIRS); + tempMap.put("stone stairs", Material.SMOOTH_STAIRS); + tempMap.put("potato", Material.POTATO_ITEM); + tempMap.put("carrot", Material.CARROT_ITEM); + tempMap.put("brewing stand", Material.BREWING_STAND_ITEM); + tempMap.put("cauldron", Material.CAULDRON_ITEM); + tempMap.put("carrot on stick", Material.CARROT_STICK); + tempMap.put("carrot on a stick", Material.CARROT_STICK); + tempMap.put("cobblestone wall", Material.COBBLE_WALL); + tempMap.put("wood slab", Material.WOOD_STEP); + tempMap.put("double wood slab", Material.WOOD_DOUBLE_STEP); + tempMap.put("repeater", Material.DIODE); + tempMap.put("piston", Material.PISTON_BASE); + tempMap.put("sticky piston", Material.PISTON_STICKY_BASE); + tempMap.put("flower pot", Material.FLOWER_POT_ITEM); + tempMap.put("wood showel", Material.WOOD_SPADE); + tempMap.put("stone showel", Material.STONE_SPADE); + tempMap.put("gold showel", Material.GOLD_SPADE); + tempMap.put("iron showel", Material.IRON_SPADE); + tempMap.put("diamond showel", Material.DIAMOND_SPADE); + tempMap.put("steak", Material.COOKED_BEEF); + tempMap.put("cooked porkchop", Material.GRILLED_PORK); + tempMap.put("raw porkchop", Material.PORK); + tempMap.put("hardened clay", Material.HARD_CLAY); + tempMap.put("huge brown mushroom", Material.HUGE_MUSHROOM_1); + tempMap.put("huge red mushroom", Material.HUGE_MUSHROOM_2); + tempMap.put("mycelium", Material.MYCEL); + tempMap.put("poppy", Material.RED_ROSE); + tempMap.put("comparator", Material.REDSTONE_COMPARATOR); + tempMap.put("skull", Material.SKULL_ITEM); + tempMap.put("head", Material.SKULL_ITEM); + tempMap.put("redstone torch", Material.REDSTONE_TORCH_ON); + tempMap.put("redstone lamp", Material.REDSTONE_LAMP_OFF); + tempMap.put("glistering melon", Material.SPECKLED_MELON); + tempMap.put("gunpowder", Material.SULPHUR); + tempMap.put("lilypad", Material.WATER_LILY); + tempMap.put("command block", Material.COMMAND); + + for (Entry tempEntry : tempMap.entrySet()) { + materialMap.put(stripSpacingChars(tempEntry.getKey()).toLowerCase(), tempEntry.getValue()); + } + + for (Material mat : Material.values()) { + materialMap.put(stripSpacingChars(mat.toString()).toLowerCase(), mat); + } + } + + public static String stripSpacingChars(String input) { + return stripSpacingSymbolsPattern.matcher(input).replaceAll(""); + } + + public static Material matchMaterial(String input) { + return materialMap.get(stripSpacingChars(input).toLowerCase()); + } + + + // Blocks are smalled than items. + @SuppressWarnings("deprecation") + public static boolean appearsAsBlock(Material mat) { + switch (mat.getId()) { + case 1: + case 2: + case 3: + case 4: + case 5: + case 7: + case 12: + case 13: + case 14: + case 15: + case 16: + case 17: + case 18: + case 19: + case 20: + case 21: + case 22: + case 23: + case 24: + case 25: + case 26: + case 29: + case 33: + case 34: + case 35: + case 36: + case 41: + case 42: + case 43: + case 44: + case 45: + case 46: + case 47: + case 48: + case 49: + case 52: + case 53: + case 54: + case 55: + case 56: + case 57: + case 58: + case 59: + case 63: + case 64: + case 67: + case 68: + case 70: + case 71: + case 72: + case 73: + case 74: + case 75: + case 77: + case 78: + case 79: + case 80: + case 81: + case 82: + case 83: + case 84: + case 85: + case 86: + case 87: + case 88: + case 89: + case 90: + case 91: + case 92: + case 93: + case 94: + case 95: + case 96: + case 97: + case 98: + case 99: + case 100: + case 103: + case 104: + case 105: + case 107: + case 108: + case 109: + case 110: + case 112: + case 113: + case 114: + case 115: + case 116: + case 117: + case 118: + case 120: + case 121: + case 122: + case 123: + case 124: + case 125: + case 126: + case 128: + case 129: + case 130: + case 132: + case 133: + case 134: + case 135: + case 136: + case 137: + case 138: + case 139: + case 140: + case 143: + case 144: + case 145: + case 146: + case 147: + case 148: + case 149: + case 150: + case 151: + case 152: + case 153: + case 155: + case 156: + case 157: + case 158: + case 159: + case 161: + case 162: + case 163: + case 164: + case 165: + case 167: + case 168: + case 169: + case 170: + case 171: + case 172: + case 173: + case 174: + case 176: + case 177: + case 178: + case 179: + case 180: + case 181: + case 182: + case 183: + case 184: + case 185: + case 186: + case 187: + case 188: + case 189: + case 190: + case 191: + case 192: + case 193: + case 194: + case 195: + case 196: + case 197: + return true; + default: + return false; + } + } + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/util/Offsets.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/util/Offsets.java new file mode 100644 index 00000000..2df7cbcd --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/util/Offsets.java @@ -0,0 +1,23 @@ +package com.gmail.filoghost.holographicdisplays.util; + +public class Offsets { + + public static final double + + // Spawning a hologram line on top of a full block, the bottom part of the nametag should be perfectly on the surface of the block. + WITHER_SKULL_WITH_HORSE = 54.56, + + WITHER_SKULL_WITH_ITEM = -0.21, + + WITHER_SKULL_WITH_SLIME = -0.22, + + // For 1.8+, a single armor stand. As with wither skulls and horses, the bottom part of the nametag should be on the surface of the block. + ARMOR_STAND_ALONE = -1.25, + + // For 1.8+, an armor stand holding an item. + ARMOR_STAND_WITH_ITEM = -1.48, + + // For 1.8+, an armor stand holding a slime. + ARMOR_STAND_WITH_SLIME = -1.49; + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/util/ReflectionUtils.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/util/ReflectionUtils.java new file mode 100644 index 00000000..4d3ddb92 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/util/ReflectionUtils.java @@ -0,0 +1,21 @@ +package com.gmail.filoghost.holographicdisplays.util; + +import java.lang.reflect.Field; +import java.util.Map; + +public class ReflectionUtils { + + @SuppressWarnings({ "rawtypes", "unchecked" }) + public static void putInPrivateStaticMap(Class clazz, String fieldName, Object key, Object value) throws Exception { + Field field = clazz.getDeclaredField(fieldName); + field.setAccessible(true); + Map map = (Map) field.get(null); + map.put(key, value); + } + + public static void setPrivateField(Class clazz, Object handle, String fieldName, Object value) throws Exception { + Field field = clazz.getDeclaredField(fieldName); + field.setAccessible(true); + field.set(handle, value); + } +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/util/Utils.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/util/Utils.java new file mode 100644 index 00000000..5c8f4a1b --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/util/Utils.java @@ -0,0 +1,98 @@ +package com.gmail.filoghost.holographicdisplays.util; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.bukkit.ChatColor; + +public class Utils extends Object { + + /** + * Converts a generic array to an array of Strings using the method toString(). + * @param array the array to convert + * @return the new generated array of Strings + */ + public static String[] arrayToStrings(Object... array) { + String[] result = new String[array.length]; + for (int i = 0; i < array.length; i++) { + result[i] = array[i] != null ? array[i].toString() : null; + } + + return result; + } + + + /** + * Convenience method to add colors to a string. + * @param text the text to colorize + * @return the colorized text, or null if text was null + */ + public static String addColors(String text) { + if (text == null) { + return null; + } + + return ChatColor.translateAlternateColorCodes('&', text); + } + + + public static boolean containsIgnoreCase(String toCheck, String content) { + return toCheck.toLowerCase().contains(content.toLowerCase()); + } + + + public static Map newMap() { + return new HashMap(); + } + + public static List newList() { + return new ArrayList(); + } + + + public static Set newSet() { + return new HashSet(); + } + + @SuppressWarnings("unchecked") + public static T[] listToArray(List list) { + return (T[]) list.toArray(); + } + + public static int floor(double num) { + int floor = (int) num; + return floor == num ? floor : floor - (int) (Double.doubleToRawLongBits(num) >>> 63); + } + + + public static double square(double num) { + return num * num; + } + + + public static String join(String[] elements, String separator, int startIndex, int endIndex) { + Validator.isTrue(startIndex > 0 && startIndex < elements.length, "startIndex out of bounds"); + Validator.isTrue(endIndex > 0 && endIndex <= elements.length, "endIndex out of bounds"); + Validator.isTrue(startIndex <= endIndex, "startIndex lower than endIndex"); + + StringBuilder result = new StringBuilder(); + + while (startIndex < endIndex) { + if (result.length() != 0) { + result.append(separator); + } + + if (elements[startIndex] != null) { + result.append(elements[startIndex]); + } + startIndex++; + } + + return result.toString(); + } + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/util/Validator.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/util/Validator.java new file mode 100644 index 00000000..cf058012 --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/util/Validator.java @@ -0,0 +1,17 @@ +package com.gmail.filoghost.holographicdisplays.util; + +public class Validator { + + public static void notNull(Object o, String name) { + if (o == null) { + throw new NullPointerException(name + " cannot be null"); + } + } + + public static void isTrue(boolean statement, String message) { + if (!statement) { + throw new IllegalArgumentException(message); + } + } + +} diff --git a/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/util/VersionUtils.java b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/util/VersionUtils.java new file mode 100644 index 00000000..6f9e41fa --- /dev/null +++ b/HolographicDisplays/Plugin/com/gmail/filoghost/holographicdisplays/util/VersionUtils.java @@ -0,0 +1,56 @@ +package com.gmail.filoghost.holographicdisplays.util; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.bukkit.Bukkit; +import org.bukkit.entity.EntityType; + +import com.gmail.filoghost.holographicdisplays.HolographicDisplays; + +public class VersionUtils { + + /** + * This method uses a regex to get the NMS package part that changes with every update. + * Example: v1_8_R1 + * @return the NMS package part or null if not found. + */ + public static String getBukkitVersion() { + Matcher matcher = Pattern.compile("v\\d+_\\d+_R\\d+").matcher(Bukkit.getServer().getClass().getPackage().getName()); + if (matcher.find()) { + return matcher.group(); + } else { + return null; + } + } + + /** + * This method uses a regex to get the version of this Minecraft release. + * Example: 1.8.1 + * @return the version of this release or null if not found. + */ + public static String getMinecraftVersion() { + Matcher matcher = Pattern.compile("(\\(MC: )([\\d\\.]+)(\\))").matcher(Bukkit.getVersion()); + if (matcher.find()) { + return matcher.group(2); + } else { + return null; + } + } + + /** + * Checks if the server is using MCPC+ or Cauldron. + * @return true if the server software is MCPC+ or Cauldron + */ + public static boolean isMCPCOrCauldron() { + return Utils.containsIgnoreCase(Bukkit.getName(), "MCPC") || Utils.containsIgnoreCase(Bukkit.getName(), "Cauldron"); + } + + public static boolean isArmorstand(EntityType type) { + if (!HolographicDisplays.is1_8()) { + return false; + } + + return type == EntityType.ARMOR_STAND; + } +}