package com.supaham.commons.bukkit;
import com.google.common.base.Preconditions;
import com.supaham.commons.bukkit.modules.CommonModule;
import com.supaham.commons.bukkit.modules.ModuleContainer;
import com.supaham.commons.state.State;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import javax.annotation.Nonnull;
/**
* Utility class for working with Bungee. This class contains methods such as
* {@link #sendPlayerTo(Player, String)}, and more.
*
* @since 0.1
*/
public class Bungee extends CommonModule {
private final Map<UUID, Long> connecting = new HashMap<>();
private final int connectCooldown;
private TickerTask cooldownRemover = new CooldownRemover();
/**
* Constructs a {@link Bungee} instance with a {@link Plugin} as the owner and a 40 tick
* connecting cooldown.
* <br />
* If the {@code connectCooldown} is <b>40</b> and a player is to be sent to a server, then
* <b>30</b> ticks later the same player is asked to leave the server again and still hasn't left
* the code terminates.
*
* @param container module container to own this module.
*
* @see #Bungee(ModuleContainer, int)
*/
public Bungee(@Nonnull ModuleContainer container) {
this(container, 40);
}
/**
* Constructs a {@link Bungee} instance with a {@link Plugin} as the owner.
* <br />
* If the {@code connectCooldown} is <b>40</b> and a player is to be sent to a server, then
* <b>30</b> ticks later the same player is asked to leave the server again and still hasn't left
* the code terminates.
*
* @param container module container to own this module.
* @param connectCooldown cooldown in ticks that a player should be able to connect to a server.
*/
public Bungee(@Nonnull ModuleContainer container, int connectCooldown) {
super(container);
this.plugin.getServer().getMessenger().registerOutgoingPluginChannel(this.plugin, "BungeeCord");
this.connectCooldown = connectCooldown;
registerTask(this.cooldownRemover);
}
public boolean setState(@Nonnull State state) throws UnsupportedOperationException {
boolean change = super.setState(state);
if (change) {
switch (state) {
case STOPPED:
this.connecting.clear();
break;
}
}
return change;
}
/**
* Sends all online players to a specific bungee server.
*
* @param serverName server to send all online players to
*
* @see #sendPlayerTo(Player, String)
*/
public void sendAllTo(String serverName) {
for (Player player : this.plugin.getServer().getOnlinePlayers()) {
sendPlayerTo(player, serverName);
}
}
/**
* Sends a {@link Player} to a specific bungee server.
* <br />
* The one and only case where the returned boolean would be false is if the {@code player} was
* already being sent within the {@link #getConnectCooldown()} ticks.
*
* @param player player to send
* @param serverName bungee server to send player to
*
* @return whether the player is being sent to the specified {@code serverName}.
*/
public boolean sendPlayerTo(final Player player, String serverName) {
Preconditions.checkState(getState().equals(State.ACTIVE), "Bungee module not active.");
Long previous = this.connecting.get(player.getUniqueId());
// below, connectCooldown is being converted from minecraft ticks to milliseconds
if (previous != null && previous < System.currentTimeMillis() + (this.connectCooldown * 50)) {
return false;
}
ByteArrayOutputStream bout = new ByteArrayOutputStream();
try (DataOutputStream out = new DataOutputStream(bout)) {
out.writeUTF("Connect");
out.writeUTF(serverName);
} catch (IOException e) {
throw new RuntimeException(e);
}
player.sendPluginMessage(this.plugin, "BungeeCord", bout.toByteArray());
this.connecting.put(player.getUniqueId(), System.currentTimeMillis());
return true;
}
public int getConnectCooldown() {
return connectCooldown;
}
public Set<UUID> getConnecting() {
return Collections.unmodifiableSet(this.connecting.keySet());
}
private final class CooldownRemover extends TickerTask {
public CooldownRemover() {
super(Bungee.this.plugin, 0, 1);
}
@Override public void run() {
Iterator<Long> it = Bungee.this.connecting.values().iterator();
while (it.hasNext()) {
if (it.next() <= System.currentTimeMillis()) {
it.remove();
}
}
}
}
}