package net.knarcraft.stargate.utility; import net.knarcraft.stargate.Stargate; import net.knarcraft.stargate.portal.Portal; import net.knarcraft.stargate.portal.PortalHandler; import net.knarcraft.stargate.portal.teleporter.PlayerTeleporter; import net.md_5.bungee.api.ChatColor; import org.bukkit.entity.Player; import org.bukkit.event.player.PlayerMoveEvent; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.UUID; /** * This class contains helpful functions to help with sending and receiving BungeeCord plugin messages */ public final class BungeeHelper { private final static String bungeeSubChannel = "SGBungee"; private final static String bungeeChannel = "BungeeCord"; private final static String teleportMessageDelimiter = "#@#"; private final static Map bungeeQueue = new HashMap<>(); private BungeeHelper() { } /** * Get the plugin message channel use for BungeeCord messages * * @return

The bungee plugin channel

*/ public static String getBungeeChannel() { return bungeeChannel; } /** * Removes a player from the queue of players teleporting through BungeeCord * *

Whenever a BungeeCord teleportation message is received and the player is not currently connected to this * server, it'll be added to this queue. Once the player joins this server, the player should be removed from the * queue and teleported to the destination.

* * @param playerUUID

The UUID of the player to remove

* @return

The name of the destination portal the player should be teleported to

*/ public static String removeFromQueue(UUID playerUUID) { return bungeeQueue.remove(playerUUID); } /** * Sends a plugin message to BungeeCord allowing the target server to catch it * * @param player

The teleporting player

* @param entrancePortal

The portal the player is teleporting from

* @return

True if the message was successfully sent

*/ public static boolean sendTeleportationMessage(Player player, Portal entrancePortal) { try { //Build the teleportation message, format is delimiter String message = player.getUniqueId() + teleportMessageDelimiter + entrancePortal.getDestinationName(); ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); DataOutputStream dataOutputStream = new DataOutputStream(byteArrayOutputStream); //Build the message data and send it over the SGBungee BungeeCord channel dataOutputStream.writeUTF("Forward"); //Send the message to the server defined in the entrance portal's network line dataOutputStream.writeUTF(stripColor(entrancePortal.getNetwork())); //Specify the sub-channel/tag to make it recognizable on arrival dataOutputStream.writeUTF(bungeeSubChannel); //Write the length of the message dataOutputStream.writeShort(message.length()); //Write the actual message dataOutputStream.writeBytes(message); //Send the plugin message player.sendPluginMessage(Stargate.getInstance(), bungeeChannel, byteArrayOutputStream.toByteArray()); } catch (IOException ex) { Stargate.logSevere("Error sending BungeeCord teleport packet"); ex.printStackTrace(); return false; } return true; } /** * Sends the bungee message necessary to make a player connect to another server * * @param player

The player to teleport

* @param entrancePortal

The bungee portal the player is teleporting from

* @return

True if the plugin message was sent successfully

*/ public static boolean changeServer(Player player, Portal entrancePortal) { try { ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); DataOutputStream dataOutputStream = new DataOutputStream(byteArrayOutputStream); //Send a connect-message to connect the player to the server defined in the entrance portal's network line dataOutputStream.writeUTF("Connect"); dataOutputStream.writeUTF(stripColor(entrancePortal.getNetwork())); //Send the plugin message player.sendPluginMessage(Stargate.getInstance(), bungeeChannel, byteArrayOutputStream.toByteArray()); } catch (IOException ex) { Stargate.logSevere("Error sending BungeeCord connect packet"); ex.printStackTrace(); return false; } return true; } /** * Reads a plugin message byte array to a string if it's sent from another stargate plugin * * @param message

The byte array to read

* @return

The message contained in the byte array, or null on failure

*/ public static String readPluginMessage(byte[] message) { byte[] data; try { DataInputStream dataInputStream = new DataInputStream(new ByteArrayInputStream(message)); String subChannel = dataInputStream.readUTF(); //Only listen for the SGBungee channel if (!subChannel.equals(bungeeSubChannel)) { return null; } //Get the length of the contained message short dataLength = dataInputStream.readShort(); //Prepare a byte array for the sent message data = new byte[dataLength]; //Read the message to the prepared array dataInputStream.readFully(data); } catch (IOException ex) { Stargate.logSevere("Error receiving BungeeCord message"); ex.printStackTrace(); return null; } return new String(data); } /** * Handles the receival of a teleport message * * @param receivedMessage

The received teleport message

*/ public static void handleTeleportMessage(String receivedMessage) { //Get the player id and destination from the message String[] messageParts = receivedMessage.split(teleportMessageDelimiter); UUID playerUUID = UUID.fromString(messageParts[0]); String destination = messageParts[1]; //Check if the player is online, if so, teleport, otherwise, queue Player player = Stargate.getInstance().getServer().getPlayer(playerUUID); if (player == null) { bungeeQueue.put(playerUUID, destination); } else { Portal destinationPortal = PortalHandler.getBungeePortal(destination); //If teleporting to an invalid portal, let the server decide where the player arrives if (destinationPortal == null) { Stargate.logInfo(String.format("Bungee portal %s does not exist", destination)); return; } new PlayerTeleporter(destinationPortal, player).teleport(destinationPortal, null); } } /** * Teleports a player to a bungee gate * * @param player

The player to teleport

* @param entrancePortal

The gate the player is entering from

* @param event

The event causing the teleportation

* @return

True if the teleportation was successful

*/ public static boolean bungeeTeleport(Player player, Portal entrancePortal, PlayerMoveEvent event) { //Check if bungee is actually enabled if (!Stargate.getGateConfig().enableBungee()) { if (!entrancePortal.getOptions().isSilent()) { Stargate.getMessageSender().sendErrorMessage(player, Stargate.getString("bungeeDisabled")); } entrancePortal.getPortalOpener().closePortal(false); return false; } //Teleport the player back to this gate, for sanity's sake new PlayerTeleporter(entrancePortal, player).teleport(entrancePortal, event); //Send the SGBungee packet first, it will be queued by BC if required if (!BungeeHelper.sendTeleportationMessage(player, entrancePortal)) { Stargate.debug("bungeeTeleport", "Unable to send teleportation message"); return false; } //Send the connect-message to make the player change server if (!BungeeHelper.changeServer(player, entrancePortal)) { Stargate.debug("bungeeTeleport", "Unable to change server"); return false; } Stargate.debug("bungeeTeleport", "Teleported player to another server"); return true; } /** * Strips all color tags from a string * * @param string

The string to strip color from

* @return

The string without color codes

*/ private static String stripColor(String string) { return ChatColor.stripColor(ChatColor.translateAlternateColorCodes('&', string)); } }