diff --git a/SubServers.Client/Bukkit/src/net/ME1312/SubServers/Client/Bukkit/SubPlugin.java b/SubServers.Client/Bukkit/src/net/ME1312/SubServers/Client/Bukkit/SubPlugin.java index a1ea4c04..20072748 100644 --- a/SubServers.Client/Bukkit/src/net/ME1312/SubServers/Client/Bukkit/SubPlugin.java +++ b/SubServers.Client/Bukkit/src/net/ME1312/SubServers/Client/Bukkit/SubPlugin.java @@ -17,11 +17,11 @@ import net.ME1312.SubData.Client.SubDataClient; import net.ME1312.SubServers.Client.Bukkit.Graphic.DefaultUIHandler; import net.ME1312.SubServers.Client.Bukkit.Graphic.UIHandler; import net.ME1312.SubServers.Client.Bukkit.Library.Compatibility.PlaceholderImpl; -import net.ME1312.SubServers.Client.Bukkit.Library.Placeholders; import net.ME1312.SubServers.Client.Bukkit.Library.ConfigUpdater; import net.ME1312.SubServers.Client.Bukkit.Library.Metrics; +import net.ME1312.SubServers.Client.Bukkit.Library.Placeholders; import net.ME1312.SubServers.Client.Bukkit.Network.SubProtocol; - +import net.ME1312.SubServers.Client.Bukkit.SubSigns.SubSigns; import org.bukkit.Bukkit; import org.bukkit.command.CommandMap; import org.bukkit.entity.Player; @@ -57,6 +57,8 @@ public final class SubPlugin extends JavaPlugin { public final Placeholders phi = new Placeholders(this); public String server_address; + private SubSigns subSigns; + private MethodHandle gson; private long resetDate = 0; private boolean reconnect = false; @@ -64,7 +66,7 @@ public final class SubPlugin extends JavaPlugin { @SuppressWarnings("ConstantConditions") public SubPlugin() throws Throwable { super(); - Class gson = Class.forName(((Try.all.get(() -> Class.forName("com.google.gson.Gson") != null, false)?"":"org.bukkit.craftbukkit.libs.")) + "com.google.gson.Gson"); + Class gson = Class.forName(((Try.all.get(() -> Class.forName("com.google.gson.Gson") != null, false) ? "" : "org.bukkit.craftbukkit.libs.")) + "com.google.gson.Gson"); this.gson = Access.shared.type(gson).method("fromJson").instance(gson.newInstance()).parameters(String.class, Class.class).returns(Object.class).handle(); version = Version.fromString(getDescription().getVersion()); subdata.put(0, null); @@ -130,7 +132,7 @@ public final class SubPlugin extends JavaPlugin { gui = new DefaultUIHandler(this); if (api.access.value > NO_COMMANDS.value && !config.get().getMap("Settings").getBoolean("API-Only-Mode", false)) { - Bukkit.getPluginManager().registerEvents(new SubSigns(this, new File(dir, "signs.dat")), this); + this.subSigns = new SubSigns(this, new File(dir, "signs.dat")); CommandMap cmd = Util.reflect(Bukkit.getServer().getClass().getDeclaredField("commandMap"), Bukkit.getServer()); cmd.register("subservers", new SubCommand(this, "subservers")); @@ -159,8 +161,10 @@ public final class SubPlugin extends JavaPlugin { updcount++; } } - if (updcount > 0) Bukkit.getLogger().info("SubServers > SubServers.Client.Bukkit v" + updversion + " is available. You are " + updcount + " version" + ((updcount == 1)?"":"s") + " behind."); - } catch (Throwable e) {} + if (updcount > 0) + Bukkit.getLogger().info("SubServers > SubServers.Client.Bukkit v" + updversion + " is available. You are " + updcount + " version" + ((updcount == 1) ? "" : "s") + " behind."); + } catch (Throwable e) { + } }, 0, TimeUnit.DAYS.toSeconds(2) * 20); } catch (Throwable e) { e.printStackTrace(); @@ -209,7 +213,7 @@ public final class SubPlugin extends JavaPlugin { Bukkit.getScheduler().runTaskLater(SubPlugin.this, this, reconnect * 20); } } - }, (disconnect == null)?0:reconnect * 20); + }, (disconnect == null) ? 0 : reconnect * 20L); } } @@ -222,15 +226,16 @@ public final class SubPlugin extends JavaPlugin { setEnabled(false); reconnect = false; - ArrayList temp = new ArrayList(); - temp.addAll(subdata.values()); - for (SubDataClient client : temp) if (client != null) { - client.close(); - client.waitFor(); + ArrayList temp = new ArrayList<>(subdata.values()); + for (SubDataClient client : temp) { + if (client != null) { + client.close(); + client.waitFor(); + } } subdata.clear(); subdata.put(0, null); - SubSigns.save(); + this.subSigns.save(); } catch (IOException | InterruptedException e) { e.printStackTrace(); } @@ -244,13 +249,13 @@ public final class SubPlugin extends JavaPlugin { */ @SuppressWarnings("unchecked") public Map parseJSON(String json) throws Throwable { - return (Map) (Object) gson.invokeExact(json, Map.class); + return (Map) gson.invokeExact(json, Map.class); } /** * Send a message to the BungeeCord Plugin Messaging Channel * - * @param player Player that will send + * @param player Player that will send * @param message Message contents */ public void pmc(Player player, String... message) { @@ -266,4 +271,8 @@ public final class SubPlugin extends JavaPlugin { player.sendPluginMessage(this, "BungeeCord", stream.toByteArray()); } + + public Pair>> getLang() { + return lang; + } } diff --git a/SubServers.Client/Bukkit/src/net/ME1312/SubServers/Client/Bukkit/SubSigns.java b/SubServers.Client/Bukkit/src/net/ME1312/SubServers/Client/Bukkit/SubSigns.java deleted file mode 100644 index 19c4dc03..00000000 --- a/SubServers.Client/Bukkit/src/net/ME1312/SubServers/Client/Bukkit/SubSigns.java +++ /dev/null @@ -1,383 +0,0 @@ -package net.ME1312.SubServers.Client.Bukkit; - -import net.ME1312.Galaxi.Library.Container.Pair; -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.Host; -import net.ME1312.SubServers.Client.Common.Network.API.Server; -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 java.io.*; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.charset.StandardCharsets; -import java.text.NumberFormat; -import java.util.*; -import java.util.Map.Entry; -import java.util.function.Supplier; - -/** - * SubServers Signs Class - */ -public class SubSigns implements Listener { - private static final HashMap data = new HashMap(); - private static final HashMap locations = 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 name = 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, name); - } else if (loaded.getBlock().getState() instanceof Sign) { - data.put(location, name); - signs.put(loaded, translate(name)); - locations.put(name.toLowerCase(), loaded); - } else { - Bukkit.getLogger().warning("SubServers > Removed invalid sign data: [\"" + loaded.getWorld().getName() + "\", " + location.x + ", " + location.y + ", " + location.z + "] -> \"" + name + '\"'); - } - 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 create(SignChangeEvent e) { - if (!e.isCancelled() && e.getLine(0).trim().equalsIgnoreCase("[SubServers]")) { - String name = e.getLine(1).trim(); - if (name.length() > 0 && plugin.lang != null) { - Player player = e.getPlayer(); - if (player.hasPermission("subservers.signs")) { - Supplier translator = translate(name); - Location location = e.getBlock().getLocation(); - - HashMap> signs = new HashMap>(SubSigns.signs); - signs.put(location, translator); - SubSigns.data.put(new OfflineBlock(location), name); - SubSigns.signs = signs; - SubSigns.locations.put(name.toLowerCase(), location); - - listen(); - refresh(e.getBlock(), translator); - Bukkit.getLogger().info("SubServers > Server sign created: [\"" + location.getWorld().getName() + "\", " + location.getBlockX() + ", " + location.getBlockY() + ", " + location.getBlockZ() + "] -> \"" + name + '\"'); - player.sendMessage(plugin.api.getLang("SubServers", "Signs.Create")); - } - } - } - } - - @SuppressWarnings({"rawtypes", "unchecked"}) - private Supplier translate(String name) { - if (name.startsWith("::") && name.length() > 2) { - final String translated = name.substring(2).toLowerCase(); - return () -> ((Map) (Map) plugin.phi.cache.getHosts()).getOrDefault(translated, name); - } else if (name.startsWith(":") && name.length() > 1) { - final String translated = name.substring(1); - return () -> { - Pair> group = plugin.phi.cache.getGroup(translated); - return (group == null)? name : group; - }; - } else { - final String translated = name.toLowerCase(); - return () -> ((Map) (Map) plugin.phi.cache.getServers()).getOrDefault(translated, name); - } - } - - 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> sign : signs.entrySet()) { - refresh(sign.getKey().getBlock(), sign.getValue()); - } - } - } - - private void refresh(SubServer server) { - if (server != null && plugin.lang != null) { - Location location = locations.get(server.getName().toLowerCase()); - if (location != null) { - refresh(location.getBlock(), () -> server); - } - for (String group : server.getGroups()) { - if ((location = locations.get(':' + group.toLowerCase())) != null) { - refresh(location.getBlock(), signs.get(location)); - } - } - if ((location = locations.get("::" + server.getHost().toLowerCase())) != null) { - refresh(location.getBlock(), signs.get(location)); - } - } - } - - private enum Text { - UNKNOWN(0, "Signs.Text.Unknown"), - OFFLINE(1, "Signs.Text.Offline"), - STARTING(3, "Signs.Text.Starting"), - ONLINE(4, "Signs.Text.Online"), - STOPPING(2, "Signs.Text.Stopping"), - ; - private final byte priority; - private final String text; - Text(int priority, String text) { - this.priority = (byte) priority; - this.text = text; - } - - private static Text determine(SubServer server) { - if (!server.isRunning()) { - return Text.OFFLINE; - } else if (server.isStopping()) { - return Text.STOPPING; - } else if (server.isOnline()) { - return Text.ONLINE; - } else { - return Text.STARTING; - } - } - - private static Text determine(Server server) { - if (server instanceof SubServer) { - return determine((SubServer) server); - } else if (server.getSubData()[0] == null) { - return Text.UNKNOWN; - } else { - return Text.ONLINE; - } - } - } - - @SuppressWarnings("unchecked") - private void refresh(Block block, Supplier translator) { - if (block.getState() instanceof Sign) { - Object object = translator.get(); - String name; - int players = 0; - - Sign sign = (Sign) block.getState(); - Text state = Text.UNKNOWN; - - if (object instanceof Server) { - Server server = (Server) object; - state = Text.determine(server); - name = server.getDisplayName(); - players = server.getRemotePlayers().size(); - - } else if (object instanceof Pair) { - Pair> group = (Pair>) object; - name = group.key(); - - Text incoming; - for (Server server : group.value()) { - players += server.getRemotePlayers().size(); - incoming = Text.determine(server); - if (incoming.priority > state.priority) - state = incoming; - } - } else if (object instanceof Host) { - Host host = (Host) object; - name = host.getDisplayName(); - - Text incoming; - for (SubServer server : host.getSubServers().values()) { - players += server.getRemotePlayers().size(); - incoming = Text.determine(server); - if (incoming.priority > state.priority) - state = incoming; - } - } else if (object instanceof String) { - name = (String) object; - } else { - return; - } - - String[] text = plugin.phi.replace(null, plugin.api.getLang("SubServers", state.text).replace("$str$", name).replace("$int$", NumberFormat.getInstance().format(players))).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); - } - } - - @SuppressWarnings("unchecked") - @EventHandler(priority = EventPriority.MONITOR) - public void interact(PlayerInteractEvent e) { - if (!e.isCancelled() && e.getClickedBlock() != null && e.getClickedBlock().getState() instanceof Sign) { - Supplier translator = signs.get(e.getClickedBlock().getLocation()); - if (translator != null && plugin.lang != null) { - Player player = e.getPlayer(); - if (player.hasPermission("subservers.teleport") && (e.getAction() == Action.RIGHT_CLICK_BLOCK || !player.hasPermission("subservers.signs"))) { - Object object = translator.get(); - - Collection servers; - if (object instanceof Server) { - servers = Collections.singleton((Server) object); - } else if (object instanceof Pair) { - servers = ((Pair>) object).value(); - } else if (object instanceof Host) { - servers = ((Host) object).getSubServers().values(); - } else { - return; - } - - Text incoming, state = Text.UNKNOWN; - List selected = new ArrayList<>(); - for (Server server : servers) { - incoming = Text.determine(server); - if (incoming != Text.STOPPING) { - if (incoming == Text.OFFLINE) { - SubServer subserver = (SubServer) server; - if (!subserver.isEnabled() || !subserver.isAvailable() || subserver.getCurrentIncompatibilities().size() != 0) continue; - } - - if (incoming.priority > state.priority) { - state = incoming; - selected.clear(); - selected.add(server); - } else if (incoming == state) { - selected.add(server); - } - } - } - - if (selected.size() > 0) { - Server server = selected.get(new Random().nextInt(selected.size())); - if (state == Text.OFFLINE) { - ((SubServer) server).start(); - } else { - 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 (player.hasPermission("subservers.signs") && plugin.lang != null) { - Location location = e.getBlock().getLocation(); - String name = data.remove(new OfflineBlock(location)); - if (name != null) locations.remove(name.toLowerCase()); - - HashMap> signs = new HashMap>(SubSigns.signs); - signs.remove(location); - SubSigns.signs = signs; - - player.sendMessage(plugin.api.getLang("SubServers", "Signs.Delete")); - } else { - e.setCancelled(true); - } - } - } - - @EventHandler(priority = EventPriority.MONITOR) - public void start(SubStartEvent e) { - refresh(plugin.phi.cache.getSubServer(e.getServer())); - } - - @EventHandler(priority = EventPriority.MONITOR) - public void started(SubStartedEvent e) { - refresh(plugin.phi.cache.getSubServer(e.getServer())); - } - - @EventHandler(priority = EventPriority.MONITOR) - public void stopping(SubStopEvent e) { - refresh(plugin.phi.cache.getSubServer(e.getServer())); - } - - @EventHandler(priority = EventPriority.MONITOR) - public void stopped(SubStoppedEvent e) { - refresh(plugin.phi.cache.getSubServer(e.getServer())); - } -} diff --git a/SubServers.Client/Bukkit/src/net/ME1312/SubServers/Client/Bukkit/SubSigns/Listeners/SignListeners.java b/SubServers.Client/Bukkit/src/net/ME1312/SubServers/Client/Bukkit/SubSigns/Listeners/SignListeners.java new file mode 100644 index 00000000..d33bd16f --- /dev/null +++ b/SubServers.Client/Bukkit/src/net/ME1312/SubServers/Client/Bukkit/SubSigns/Listeners/SignListeners.java @@ -0,0 +1,165 @@ +package net.ME1312.SubServers.Client.Bukkit.SubSigns.Listeners; + +import net.ME1312.Galaxi.Library.Container.Pair; +import net.ME1312.SubServers.Client.Bukkit.Library.Compatibility.OfflineBlock; +import net.ME1312.SubServers.Client.Bukkit.SubPlugin; +import net.ME1312.SubServers.Client.Bukkit.SubSigns.SubSigns; +import net.ME1312.SubServers.Client.Bukkit.SubSigns.Text; +import net.ME1312.SubServers.Client.Common.Network.API.Host; +import net.ME1312.SubServers.Client.Common.Network.API.Server; +import net.ME1312.SubServers.Client.Common.Network.API.SubServer; +import org.bukkit.Bukkit; +import org.bukkit.Location; +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 java.util.*; +import java.util.function.Supplier; + +public class SignListeners implements Listener { + + private final SubPlugin subPlugin; + private final SubSigns subSigns; + private final Random random = new Random(); + + public SignListeners(SubPlugin subPlugin, SubSigns subSigns) { + this.subPlugin = subPlugin; + this.subSigns = subSigns; + } + + + @EventHandler(priority = EventPriority.MONITOR) + public void create(SignChangeEvent e) { + if (e.isCancelled() || !e.getLine(0).trim().equalsIgnoreCase("[SubServers]")) { + return; + } + + String name = e.getLine(1).trim(); + + if (name.length() <= 0 || subPlugin.getLang() == null) { + return; + } + + Player player = e.getPlayer(); + + if (!player.hasPermission("subservers.signs")) { + return; + } + + Supplier translator = subSigns.translate(name); + Location location = e.getBlock().getLocation(); + + HashMap> signs = new HashMap<>(subSigns.getSigns()); + signs.put(location, translator); + subSigns.getData().put(new OfflineBlock(location), name); + subSigns.setSigns(signs); + subSigns.getLocations().put(name.toLowerCase(), location); + + subSigns.listen(); + subSigns.refresh(e.getBlock(), translator); + Bukkit.getLogger().info("SubServers > Server sign created: [\"" + location.getWorld().getName() + "\", " + location.getBlockX() + ", " + location.getBlockY() + ", " + location.getBlockZ() + "] -> \"" + name + '\"'); + player.sendMessage(subPlugin.api.getLang("SubServers", "Signs.Create")); + } + + + @SuppressWarnings("unchecked") + @EventHandler(priority = EventPriority.MONITOR) + public void interact(PlayerInteractEvent e) { + if (e.isCancelled() || e.getClickedBlock() == null || !(e.getClickedBlock().getState() instanceof Sign)) { + return; + } + + Supplier translator = subSigns.getSigns().get(e.getClickedBlock().getLocation()); + + if (translator == null || subPlugin.getLang() == null) { + return; + } + + Player player = e.getPlayer(); + + if (!player.hasPermission("subservers.teleport") || (e.getAction() != Action.RIGHT_CLICK_BLOCK && player.hasPermission("subservers.signs"))) { + return; + } + + Object object = translator.get(); + + Collection servers; + if (object instanceof Server) { + servers = Collections.singleton((Server) object); + } else if (object instanceof Pair) { + servers = ((Pair>) object).value(); + } else if (object instanceof Host) { + servers = ((Host) object).getSubServers().values(); + } else { + return; + } + + Text incoming; + Text state = Text.UNKNOWN; + List selected = new ArrayList<>(); + for (Server server : servers) { + incoming = Text.determine(server); + if (incoming == Text.STOPPING) { + continue; + } + + if (incoming == Text.OFFLINE) { + SubServer subserver = (SubServer) server; + if (!subserver.isEnabled() || !subserver.isAvailable() || subserver.getCurrentIncompatibilities().size() != 0) { + continue; + } + } + + if (incoming.getPriority() > state.getPriority()) { + state = incoming; + selected.clear(); + selected.add(server); + } else if (incoming == state) { + selected.add(server); + } + } + + if (selected.size() > 0) { + Server server = selected.get(random.nextInt(selected.size())); + if (state == Text.OFFLINE) { + ((SubServer) server).start(); + } else { + player.sendMessage(subPlugin.api.getLang("SubServers", "Command.Teleport").replace("$name$", player.getName()).replace("$str$", server.getDisplayName())); + subPlugin.pmc(player, "Connect", server.getName()); + } + } + } + + + @EventHandler(priority = EventPriority.MONITOR) + public void delete(BlockBreakEvent e) { + if (e.isCancelled() || !(e.getBlock().getState() instanceof Sign) || !subSigns.getSigns().containsKey(e.getBlock().getLocation())) { + return; + } + + Player player = e.getPlayer(); + + if (player.hasPermission("subservers.signs") && subPlugin.getLang() != null) { + Location location = e.getBlock().getLocation(); + String name = subSigns.getData().remove(new OfflineBlock(location)); + if (name != null) { + subSigns.getLocations().remove(name.toLowerCase()); + } + + HashMap> signs = new HashMap<>(subSigns.getSigns()); + signs.remove(location); + subSigns.setSigns(signs); + + player.sendMessage(subPlugin.api.getLang("SubServers", "Signs.Delete")); + } else { + e.setCancelled(true); + } + } +} diff --git a/SubServers.Client/Bukkit/src/net/ME1312/SubServers/Client/Bukkit/SubSigns/Listeners/SubServerListeners.java b/SubServers.Client/Bukkit/src/net/ME1312/SubServers/Client/Bukkit/SubSigns/Listeners/SubServerListeners.java new file mode 100644 index 00000000..f4f92af2 --- /dev/null +++ b/SubServers.Client/Bukkit/src/net/ME1312/SubServers/Client/Bukkit/SubSigns/Listeners/SubServerListeners.java @@ -0,0 +1,42 @@ +package net.ME1312.SubServers.Client.Bukkit.SubSigns.Listeners; + +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.SubPlugin; +import net.ME1312.SubServers.Client.Bukkit.SubSigns.SubSigns; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; + +public class SubServerListeners implements Listener { + + private final SubPlugin subPlugin; + private final SubSigns subSigns; + + public SubServerListeners(SubPlugin subPlugin, SubSigns subSigns) { + this.subPlugin = subPlugin; + this.subSigns = subSigns; + } + + @EventHandler(priority = EventPriority.MONITOR) + public void start(SubStartEvent e) { + subSigns.refresh(subPlugin.phi.cache.getSubServer(e.getServer())); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void started(SubStartedEvent e) { + subSigns.refresh(subPlugin.phi.cache.getSubServer(e.getServer())); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void stopping(SubStopEvent e) { + subSigns.refresh(subPlugin.phi.cache.getSubServer(e.getServer())); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void stopped(SubStoppedEvent e) { + subSigns.refresh(subPlugin.phi.cache.getSubServer(e.getServer())); + } +} diff --git a/SubServers.Client/Bukkit/src/net/ME1312/SubServers/Client/Bukkit/SubSigns/SubSigns.java b/SubServers.Client/Bukkit/src/net/ME1312/SubServers/Client/Bukkit/SubSigns/SubSigns.java new file mode 100644 index 00000000..eeb51d3e --- /dev/null +++ b/SubServers.Client/Bukkit/src/net/ME1312/SubServers/Client/Bukkit/SubSigns/SubSigns.java @@ -0,0 +1,265 @@ +package net.ME1312.SubServers.Client.Bukkit.SubSigns; + +import net.ME1312.Galaxi.Library.Container.Pair; +import net.ME1312.SubData.Client.Library.EscapedOutputStream; +import net.ME1312.SubServers.Client.Bukkit.Library.Compatibility.OfflineBlock; +import net.ME1312.SubServers.Client.Bukkit.SubPlugin; +import net.ME1312.SubServers.Client.Bukkit.SubSigns.Listeners.SignListeners; +import net.ME1312.SubServers.Client.Bukkit.SubSigns.Listeners.SubServerListeners; +import net.ME1312.SubServers.Client.Common.Network.API.Host; +import net.ME1312.SubServers.Client.Common.Network.API.Server; +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.event.Listener; + +import java.io.*; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.charset.StandardCharsets; +import java.text.NumberFormat; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.UUID; +import java.util.function.Supplier; + +/** + * SubServers Signs Class + */ +public class SubSigns implements Listener { + private final Map data = new HashMap<>(); + private final Map locations = new HashMap<>(); + private Map> signs = new HashMap<>(); + private File file; + private final SubPlugin plugin; + private boolean active = false; + + public SubSigns(SubPlugin plugin, File file) throws IOException { + this.plugin = plugin; + this.file = file; + + plugin.getServer().getPluginManager().registerEvents(new SignListeners(plugin, this), plugin); + plugin.getServer().getPluginManager().registerEvents(new SubServerListeners(plugin, this), plugin); + + load(); + } + + public void save() throws IOException { + if (data.isEmpty() && (!file.exists() || file.delete())) { + return; + } + + FileOutputStream raw = new FileOutputStream(file, false); + try (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()) { + return; + } + + try (FileInputStream in = new FileInputStream(file)) { + ByteArrayOutputStream string = new ByteArrayOutputStream(); + ByteBuffer magic = ByteBuffer.allocate(28).order(ByteOrder.BIG_ENDIAN); + + boolean escaped = false; + int b; + int 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 name = 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, name); + } else if (loaded.getBlock().getState() instanceof Sign) { + data.put(location, name); + signs.put(loaded, translate(name)); + locations.put(name.toLowerCase(), loaded); + } else { + Bukkit.getLogger().warning("SubServers > Removed invalid sign data: [\"" + loaded.getWorld().getName() + "\", " + location.x + ", " + location.y + ", " + location.z + "] -> \"" + name + '\"'); + } + 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(); + } + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + public Supplier translate(String name) { + if (name.startsWith("::") && name.length() > 2) { + final String translated = name.substring(2).toLowerCase(); + return () -> ((Map) (Map) plugin.phi.cache.getHosts()).getOrDefault(translated, name); + } else if (name.startsWith(":") && name.length() > 1) { + final String translated = name.substring(1); + return () -> { + Pair> group = plugin.phi.cache.getGroup(translated); + return (group == null) ? name : group; + }; + } else { + final String translated = name.toLowerCase(); + return () -> ((Map) (Map) plugin.phi.cache.getServers()).getOrDefault(translated, name); + } + } + + public void listen() { + if (!active && !signs.isEmpty()) { + active = true; + plugin.phi.listen(this::refresh); + plugin.phi.start(); + } + } + + private void refresh() { + if (plugin.getLang() == null) { + return; + } + + for (Entry> sign : signs.entrySet()) { + refresh(sign.getKey().getBlock(), sign.getValue()); + } + } + + public void refresh(SubServer server) { + if (server == null || plugin.getLang() == null) { + return; + } + + Location location = locations.get(server.getName().toLowerCase()); + + if (location != null) { + refresh(location.getBlock(), () -> server); + } + + for (String group : server.getGroups()) { + if ((location = locations.get(':' + group.toLowerCase())) != null) { + refresh(location.getBlock(), signs.get(location)); + } + } + + if ((location = locations.get("::" + server.getHost().toLowerCase())) != null) { + refresh(location.getBlock(), signs.get(location)); + } + } + + @SuppressWarnings("unchecked") + public void refresh(Block block, Supplier translator) { + if (!(block.getState() instanceof Sign)) { + return; + } + + Object object = translator.get(); + String name; + int players = 0; + + Sign sign = (Sign) block.getState(); + Text state = Text.UNKNOWN; + + if (object instanceof Server) { + Server server = (Server) object; + state = Text.determine(server); + name = server.getDisplayName(); + players = server.getRemotePlayers().size(); + + } else if (object instanceof Pair) { + Pair> group = (Pair>) object; + name = group.key(); + + Text incoming; + for (Server server : group.value()) { + players += server.getRemotePlayers().size(); + incoming = Text.determine(server); + if (incoming.getPriority() > state.getPriority()) + state = incoming; + } + } else if (object instanceof Host) { + Host host = (Host) object; + name = host.getDisplayName(); + + Text incoming; + for (SubServer server : host.getSubServers().values()) { + players += server.getRemotePlayers().size(); + incoming = Text.determine(server); + if (incoming.getPriority() > state.getPriority()) + state = incoming; + } + } else if (object instanceof String) { + name = (String) object; + } else { + return; + } + + String[] text = plugin.phi.replace(null, plugin.api.getLang("SubServers", state.getText()).replace("$str$", name).replace("$int$", NumberFormat.getInstance().format(players))).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); + } + + + public Map getData() { + return data; + } + + public Map getLocations() { + return locations; + } + + public Map> getSigns() { + return signs; + } + + public void setSigns(Map> signs) { + this.signs = signs; + } + + public SubPlugin getPlugin() { + return plugin; + } +} diff --git a/SubServers.Client/Bukkit/src/net/ME1312/SubServers/Client/Bukkit/SubSigns/Text.java b/SubServers.Client/Bukkit/src/net/ME1312/SubServers/Client/Bukkit/SubSigns/Text.java new file mode 100644 index 00000000..387eaba4 --- /dev/null +++ b/SubServers.Client/Bukkit/src/net/ME1312/SubServers/Client/Bukkit/SubSigns/Text.java @@ -0,0 +1,50 @@ +package net.ME1312.SubServers.Client.Bukkit.SubSigns; + +import net.ME1312.SubServers.Client.Common.Network.API.Server; +import net.ME1312.SubServers.Client.Common.Network.API.SubServer; + +public enum Text { + UNKNOWN(0, "Signs.Text.Unknown"), + OFFLINE(1, "Signs.Text.Offline"), + STARTING(3, "Signs.Text.Starting"), + ONLINE(4, "Signs.Text.Online"), + STOPPING(2, "Signs.Text.Stopping"); + + private final byte priority; + private final String text; + + Text(int priority, String text) { + this.priority = (byte) priority; + this.text = text; + } + + static Text determine(SubServer server) { + if (!server.isRunning()) { + return Text.OFFLINE; + } else if (server.isStopping()) { + return Text.STOPPING; + } else if (server.isOnline()) { + return Text.ONLINE; + } else { + return Text.STARTING; + } + } + + public static Text determine(Server server) { + if (server instanceof SubServer) { + return determine((SubServer) server); + } else if (server.getSubData()[0] == null) { + return Text.UNKNOWN; + } else { + return Text.ONLINE; + } + } + + public byte getPriority() { + return priority; + } + + public String getText() { + return text; + } +} \ No newline at end of file