package org.mcsg.double0negative.tabapi;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.ProtocolManager;
import com.comphenix.protocol.events.ConnectionSide;
import com.comphenix.protocol.events.ListenerPriority;
import com.comphenix.protocol.events.PacketAdapter;
import com.comphenix.protocol.events.PacketAdapter.AdapterParameteters;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.events.PacketEvent;
import com.comphenix.protocol.events.PacketListener;
import io.github.lucaseasedup.logit.LogItCore;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.plugin.Plugin;
/**
* Provides a simple interface for adding custom text to
* display on the Minecraft tab menu on a player/plugin basis.
*
* @author Double0negative
* @modifiedby LucasEU
*/
public final class TabAPI implements Listener
{
public void onEnable()
{
protocolManager = ProtocolLibrary.getProtocolManager();
Bukkit.getServer().getPluginManager().registerEvents(this, plugin);
AdapterParameteters params = new AdapterParameteters();
params.plugin(plugin);
params.connectionSide(ConnectionSide.SERVER_SIDE);
params.listenerPriority(ListenerPriority.NORMAL);
params.types(PacketType.Play.Server.PLAYER_INFO);
packetListener = new PacketAdapter(params)
{
@Override
public void onPacketSending(PacketEvent event)
{
if (!event.getPacketType().equals(PacketType.Play.Server.PLAYER_INFO))
return;
PacketContainer container = event.getPacket();
String string = container.getStrings().read(0);
if (string.startsWith("$"))
{
container.getStrings().write(0, string.substring(1));
event.setPacket(container);
}
else
{
event.setCancelled(true);
}
}
};
protocolManager.addPacketListener(packetListener);
}
public void onDisable()
{
if (protocolManager != null)
{
protocolManager.removePacketListener(packetListener);
protocolManager = null;
}
packetListener = null;
if (tabObjects != null)
{
tabObjects.clear();
tabObjects = null;
}
if (tabHolders != null)
{
tabHolders.clear();
tabHolders = null;
}
}
private void addPacket(Player player, String msg, boolean b, int ping)
{
if (player == null)
throw new IllegalArgumentException();
PacketContainer message =
protocolManager.createPacket(PacketType.Play.Server.PLAYER_INFO);
message.getStrings().write(0, "$" + msg);
message.getBooleans().write(0, b);
message.getIntegers().write(0, ping);
List<PacketContainer> packets = cachedPackets.get(player);
if (packets == null)
{
packets = new ArrayList<>();
cachedPackets.put(player, packets);
}
packets.add(message);
}
private void flushPackets(final Player player, final TabHolder tabCopy)
{
if (player == null)
throw new IllegalArgumentException();
final List<PacketContainer> packets = cachedPackets.get(player);
Integer taskId = updateSchedules.get(player);
// prevent flickering
if (taskId != null)
{
Bukkit.getScheduler().cancelTask(taskId);
}
taskId = Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, new Runnable()
{
@Override
public void run()
{
if (player.isOnline() && packets != null)
{
for (PacketContainer packet : packets)
{
try
{
protocolManager.sendServerPacket(player, packet);
}
catch (InvocationTargetException ex)
{
ex.printStackTrace();
}
}
}
// we set this only if we really finally flush it (which is just now)
if (tabCopy != null)
{
tabHolders.put(player.getName(), tabCopy);
}
// we're done, no need to cancel this one on next run
updateSchedules.remove(player);
}
}, 5L);
// let's keep a reference to be able to cancel this (see above)
updateSchedules.put(player, taskId);
cachedPackets.remove(player);
}
private TabObject getTab(Player player)
{
TabObject tabObj = tabObjects.get(player.getName());
if (tabObj == null)
{
tabObj = new TabObject();
tabObjects.put(player.getName(), tabObj);
}
return tabObj;
}
/**
* Sets priority for a player's tab list.
*
* -2 = no longer active, remove
* -1 = background, only show if nothing else is there
* 0 = normal
* 1 = high priority
* 2 = always show, only use if MUST show
*
* @param player
* @param priority
*/
public void setPriority(Player player, int priority)
{
getTab(player).setPriority(this, priority);
}
/**
* Set the tab for a player.
*
* <p>If the plugin the tab is being set from does not have a priority,
* it will automatically be give a base priority of 0.
*
* @param player
* @param x
* @param y
* @param msg
* @param ping
*/
public void setTabString(Player player, int x, int y, String msg, int ping)
{
if (!player.isOnline())
return;
TabObject tabObj = getTab(player);
tabObj.setTab(this, x, y, msg, ping);
tabObjects.put(player.getName(), tabObj);
}
public void setTabString(Player player, int x, int y, String msg)
{
setTabString(player, x, y, msg, 0);
}
/**
* Updates a players tab.
*
* <p>A tab will be updated with the tab from the highest priority plugin.
*
* @param player
*/
public void updatePlayer(Player player)
{
if (!player.isOnline())
return;
TabObject tabObj = tabObjects.get(player.getName());
if (tabObj == null)
return;
TabHolder holder = tabObj.getTab();
if (holder == null)
return;
// need to clear the tab first
clearTab(player);
for (int i = 0; i < holder.maxv; i++)
{
for (int j = 0; j < holder.maxh; j++)
{
String msg = holder.tabs[j][i];
if (msg == null)
continue;
String truncatedMsg = msg.substring(0, Math.min(msg.length(), 16));
int ping = holder.tabPings[j][i];
addPacket(player, truncatedMsg, true, ping);
}
}
flushPackets(player, holder.copy());
}
/**
* Clear a players tab menu.
*
* @param player
*/
public void clearTab(Player player)
{
if (!player.isOnline())
return;
TabHolder holder = tabHolders.get(player.getName());
if (holder == null)
return;
for (String[] row : holder.tabs)
{
for (String msg : row)
{
if (msg == null)
continue;
addPacket(player, msg.substring(0, Math.min(msg.length(), 16)), false, 0);
}
}
}
public void updateAll()
{
for (Player player : Bukkit.getOnlinePlayers())
{
updatePlayer(player);
}
}
@EventHandler(priority = EventPriority.HIGHEST)
private void onPlayerQuit(PlayerQuitEvent event)
{
Player player = event.getPlayer();
tabObjects.remove(player.getName());
tabHolders.remove(player.getName());
cachedPackets.remove(player);
if (updateSchedules.containsKey(player))
{
int taskId = updateSchedules.get(player);
Bukkit.getScheduler().cancelTask(taskId);
}
updateSchedules.remove(player);
}
public int getVertSize()
{
return vertTabSize;
}
public int getHorizSize()
{
return horizTabSize;
}
private static final Plugin plugin = LogItCore.getInstance().getPlugin();
private static final int horizTabSize = 3;
private static final int vertTabSize = 20;
private ProtocolManager protocolManager;
private PacketListener packetListener;
private Map<String, TabObject> tabObjects = new HashMap<>();
private Map<String, TabHolder> tabHolders = new HashMap<>();
private final Map<Player, List<PacketContainer>> cachedPackets = new HashMap<>();
private final Map<Player, Integer> updateSchedules = new HashMap<>();
}