package net.ME1312.SubServers.Client.Bukkit; import net.ME1312.SubData.Client.Library.EscapedOutputStream; import net.ME1312.SubServers.Client.Bukkit.Event.SubStartEvent; import net.ME1312.SubServers.Client.Bukkit.Event.SubStartedEvent; import net.ME1312.SubServers.Client.Bukkit.Event.SubStopEvent; import net.ME1312.SubServers.Client.Bukkit.Event.SubStoppedEvent; import net.ME1312.SubServers.Client.Bukkit.Library.Compatibility.OfflineBlock; import net.ME1312.SubServers.Client.Common.Network.API.SubServer; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.block.Block; import org.bukkit.block.Sign; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.block.Action; import org.bukkit.event.block.BlockBreakEvent; import org.bukkit.event.block.SignChangeEvent; import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.world.WorldLoadEvent; import java.io.*; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.HashMap; import java.util.Map.Entry; import java.util.UUID; /** * SubServers Signs Class */ public class SubSigns implements Listener { private static final HashMap data = new HashMap(); private static HashMap signs = new HashMap(); private static File file; private final SubPlugin plugin; private boolean active = false; SubSigns(SubPlugin plugin, File file) throws IOException { this.plugin = plugin; SubSigns.file = file; load(); } public static void save() throws IOException { if (!data.isEmpty() || (file.exists() && !file.delete())) { FileOutputStream raw = new FileOutputStream(file, false); EscapedOutputStream escaped = new EscapedOutputStream(raw, '\u001B', '\u0003'); for (Entry sign : data.entrySet()) { raw.write(ByteBuffer.allocate(28).order(ByteOrder.BIG_ENDIAN) .putLong(sign.getKey().world.getMostSignificantBits()) .putLong(sign.getKey().world.getLeastSignificantBits()) .putInt(sign.getKey().x) .putInt(sign.getKey().y) .putInt(sign.getKey().z) .array() ); escaped.write(sign.getValue().getBytes(StandardCharsets.UTF_8)); escaped.control('\u0003'); } } } private void load() throws IOException { if (file.exists()) { FileInputStream in = new FileInputStream(file); ByteArrayOutputStream string = new ByteArrayOutputStream(); ByteBuffer magic = ByteBuffer.allocate(28).order(ByteOrder.BIG_ENDIAN); boolean escaped = false; int b, i = 0; while ((b = in.read()) != -1) { if (i < 28) { magic.put((byte) b); ++i; } else if (escaped) { switch (b) { case '\u001B': // [ESC] (Escape character) string.write('\u001B'); break; case '\u0003': // [ETX] (End of String character) magic.position(0); String server = string.toString(StandardCharsets.UTF_8.name()); OfflineBlock location = new OfflineBlock(new UUID(magic.getLong(), magic.getLong()), magic.getInt(), magic.getInt(), magic.getInt()); Location loaded = location.load(); if (loaded == null) { data.put(location, server); } else if (loaded.getBlock().getState() instanceof Sign) { data.put(location, server); signs.put(loaded, server); } else { Bukkit.getLogger().warning("SubServers > Removed invalid sign data: [\"" + loaded.getWorld().getName() + "\", " + location.x + ", " + location.y + ", " + location.z + "] -> \"" + server + '\"'); } magic.clear(); string.reset(); i = 0; break; default: string.write('\u001B'); string.write(b); break; } escaped = false; } else if (b == '\u001B') { escaped = true; } else { string.write(b); } } listen(); } } @EventHandler(priority = EventPriority.MONITOR) public void load(WorldLoadEvent e) { UUID wid = e.getWorld().getUID(); ArrayList removals = new ArrayList(); HashMap signs = new HashMap(SubSigns.signs); for (Entry sign : data.entrySet()) { if (wid == sign.getKey().world) { OfflineBlock location = sign.getKey(); Location loaded = location.load(); if (loaded.getBlock().getState() instanceof Sign) { signs.put(loaded, sign.getValue()); } else { removals.add(sign.getKey()); Bukkit.getLogger().warning("SubServers > Removed invalid sign data: [\"" + loaded.getWorld().getName() + "\", " + location.x + ", " + location.y + ", " + location.z + "] -> \"" + sign.getValue() + '\"'); } } } SubSigns.signs = signs; for (OfflineBlock location : removals) { data.remove(location); } } @EventHandler(priority = EventPriority.MONITOR) public void create(SignChangeEvent e) { if (!e.isCancelled() && plugin.lang != null && e.getLine(0).trim().equalsIgnoreCase("[SubServers]")) { Player player = e.getPlayer(); String server = e.getLine(1).trim(); if (server.length() > 0 && player.hasPermission("subservers.signs")) { Location pos = e.getBlock().getLocation(); if (pos.getBlock().getState() instanceof Sign) { HashMap signs = new HashMap(SubSigns.signs); signs.put(pos, server); SubSigns.signs = signs; data.put(new OfflineBlock(pos), server); listen(); refresh(e.getBlock(), server, null); Bukkit.getLogger().info("SubServers > Server sign created: [\"" + pos.getWorld().getName() + "\", " + pos.getBlockX() + ", " + pos.getBlockY() + ", " + pos.getBlockZ() + "] -> \"" + server + '\"'); player.sendMessage(plugin.api.getLang("SubServers", "Signs.Create")); } } } } private void listen() { if (!active && !signs.isEmpty()) { active = true; plugin.phi.listen(this::refresh); plugin.phi.start(); } } private void refresh() { if (plugin.lang != null) { for (Entry pos : signs.entrySet()) { refresh(pos.getKey().getBlock(), pos.getValue(), null); } } } private void refresh(String name, String lang) { if (plugin.lang != null) { for (Entry pos : signs.entrySet()) { if (pos.getValue().equalsIgnoreCase(name)) refresh(pos.getKey().getBlock(), pos.getValue(), lang); } } } private void refresh(Block block, String name, String lang) { if (block.getState() instanceof Sign) { Sign sign = (Sign) block.getState(); SubServer server = plugin.phi.cache.getSubServer(name); if (lang == null) { if (server == null) { lang = "Signs.Text.Error"; } else if (server.isStopping()) { lang = "Signs.Text.Stopping"; } else if (server.isOnline()) { lang = "Signs.Text.Online"; } else if (server.isRunning()) { lang = "Signs.Text.Starting"; } else { lang = "Signs.Text.Offline"; } } String[] text = plugin.phi.replace(null, plugin.api.getLang("SubServers", lang).replace("$str$", name)).split("\n", 4); for (int i = 0; i < 4; ++i) if (i < text.length) { sign.setLine(i, text[i]); } else { sign.setLine(i, ""); } Bukkit.getScheduler().runTask(plugin, sign::update); } } @EventHandler(priority = EventPriority.MONITOR) public void interact(PlayerInteractEvent e) { if (!e.isCancelled() && e.getClickedBlock() != null && plugin.lang != null && plugin.api.getSubDataNetwork()[0] != null && !plugin.api.getSubDataNetwork()[0].isClosed() && signs.containsKey(e.getClickedBlock().getLocation())) { Player player = e.getPlayer(); if ((e.getAction() == Action.RIGHT_CLICK_BLOCK || !player.hasPermission("subservers.signs")) && player.hasPermission("subservers.teleport")) { SubServer server = plugin.phi.cache.getSubServer(signs.get(e.getClickedBlock().getLocation())); if (server != null) { if (!server.isRunning()) { if (server.isEnabled() && server.isAvailable() && server.getCurrentIncompatibilities().size() == 0) server.start(); } else if (!server.isStopping()) { player.sendMessage(plugin.api.getLang("SubServers", "Command.Teleport").replace("$name$", player.getName()).replace("$str$", server.getDisplayName())); plugin.pmc(player, "Connect", server.getName()); } } } } } @EventHandler(priority = EventPriority.MONITOR) public void delete(BlockBreakEvent e) { if (!e.isCancelled() && e.getBlock().getState() instanceof Sign && signs.containsKey(e.getBlock().getLocation())) { Player player = e.getPlayer(); if (plugin.lang != null && player.hasPermission("subservers.signs")) { Location pos = e.getBlock().getLocation(); HashMap signs = new HashMap(SubSigns.signs); signs.remove(pos); SubSigns.signs = signs; data.remove(new OfflineBlock(pos)); player.sendMessage(plugin.api.getLang("SubServers", "Signs.Delete")); } else { e.setCancelled(true); } } } @EventHandler public void start(SubStartEvent e) { refresh(e.getServer(), "Signs.Text.Starting"); } @EventHandler public void started(SubStartedEvent e) { refresh(e.getServer(), "Signs.Text.Online"); } @EventHandler public void stopping(SubStopEvent e) { refresh(e.getServer(), "Signs.Text.Stopping"); } @EventHandler public void stopped(SubStoppedEvent e) { refresh(e.getServer(), "Signs.Text.Offline"); } }