package in.twizmwaz.cardinal.tabList;
import com.google.common.base.Optional;
import com.google.common.collect.Lists;
import in.twizmwaz.cardinal.Cardinal;
import in.twizmwaz.cardinal.event.CycleCompleteEvent;
import in.twizmwaz.cardinal.event.PlayerChangeTeamEvent;
import in.twizmwaz.cardinal.event.PlayerNameUpdateEvent;
import in.twizmwaz.cardinal.event.RankChangeEvent;
import in.twizmwaz.cardinal.event.TeamNameChangeEvent;
import in.twizmwaz.cardinal.module.modules.team.TeamModule;
import in.twizmwaz.cardinal.tabList.entries.EmptyTabEntry;
import in.twizmwaz.cardinal.tabList.entries.PlayerTabEntry;
import in.twizmwaz.cardinal.tabList.entries.SkinTabEntry;
import in.twizmwaz.cardinal.tabList.entries.TabEntry;
import in.twizmwaz.cardinal.tabList.entries.TeamTabEntry;
import in.twizmwaz.cardinal.util.PacketUtils;
import in.twizmwaz.cardinal.util.Teams;
import net.minecraft.server.Packet;
import net.minecraft.server.PacketPlayOutPlayerInfo;
import net.minecraft.server.PacketPlayOutScoreboardTeam;
import org.bukkit.Bukkit;
import org.bukkit.Skin;
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.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.event.player.PlayerSkinPartsChangeEvent;
import org.bukkit.scheduler.BukkitScheduler;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
public class TabList implements Listener {
private static long TAB_UPDATE_TIME = 5L;
public static int columnsPerTeam = 0;
// Entries
private static Map<UUID, PlayerTabEntry> fakePlayer = new HashMap<>();
private static Map<String, TeamTabEntry> teamTitles = new HashMap<>();
private static List<EmptyTabEntry> emptyPlayers = new ArrayList<>();
// Views
private static Map<Player, TabView> playerView = new HashMap<>();
// Update handling
private static boolean scheduledUpdate = false;
private static Set<TabEntry> updateEntries = new HashSet<>();
private static Set<TeamModule> teamNeedUpdate = new HashSet<>();
public TabList() {
BukkitScheduler scheduler = Bukkit.getServer().getScheduler();
scheduler.scheduleSyncRepeatingTask(Cardinal.getInstance(), new Runnable() {
@Override
public void run() {
PacketPlayOutPlayerInfo listPacket =
new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.UPDATE_LATENCY);
for (PlayerTabEntry entry : fakePlayer.values()) {
listPacket.add(entry.getPlayerInfo(null, listPacket));
}
PacketUtils.broadcastPacket(listPacket);
}
}, 0L, 600L);
scheduler.scheduleSyncRepeatingTask(Cardinal.getInstance(), new Runnable() {
@Override
public void run() {
for (SkinTabEntry entry : teamTitles.values()) {
entry.setHat(true);
}
}
}, 0L, 20L);
scheduler.scheduleSyncRepeatingTask(Cardinal.getInstance(), new Runnable() {
@Override
public void run() {
for (SkinTabEntry entry : teamTitles.values()) {
entry.setHat(false);
}
}
}, 5L, 20L);
}
@EventHandler (priority = EventPriority.LOWEST)
public void beforeCycleComplete(CycleCompleteEvent event) {
List<String> names = Lists.newArrayList();
for (Player player : Bukkit.getOnlinePlayers()) {
names.add(player.getName());
}
PacketUtils.broadcastPacket(getTeamPacket(names, 80, 3));
}
@EventHandler (priority = EventPriority.HIGHEST)
public void onCycleComplete(CycleCompleteEvent event) {
columnsPerTeam = Math.max(4 / (Teams.getTeams().size() - 1), 1);
resetTeams();
updateAll();
for (PlayerTabEntry entry : fakePlayer.values()) {
entry.broadcastCreateSkinParts();
}
}
@EventHandler (priority = EventPriority.LOWEST)
public void onPlayerJoin(PlayerJoinEvent event) {
playerView.put(event.getPlayer(), new TabView(event.getPlayer()));
}
@EventHandler (priority = EventPriority.HIGHEST)
public void onPlayerLeave(PlayerQuitEvent event) {
playerView.remove(event.getPlayer());
removePlayer(event.getPlayer());
updateAll();
}
@EventHandler (priority = EventPriority.HIGHEST)
public void onTeamChange(PlayerChangeTeamEvent event) {
addUpdateTeam(event.getNewTeam());
renderAllTeamTitles();
updateAll();
}
@EventHandler
public void onTeamChangeName(TeamNameChangeEvent event) {
renderTeamTitle(event.getTeam());
}
@EventHandler (priority = EventPriority.HIGHEST)
public void onDisplayNameChange(PlayerNameUpdateEvent event) {
if (!playerView.containsKey(event.getPlayer())) return;
addUpdateName(getPlayer(event.getPlayer()));
}
@EventHandler
public void onRankChange(RankChangeEvent event) {
if (!event.isOnline()) return;
addUpdateTeam(Teams.getTeamByPlayer(event.getPlayer()));
updateAll();
}
@EventHandler
public void onPlayerChangeSkinParts(PlayerSkinPartsChangeEvent event) {
getPlayer(event.getPlayer()).setHat(event.getPlayer().getSkinParts().contains(Skin.Part.HAT));
}
private static void updateAll() {
if (!scheduledUpdate) {
scheduledUpdate = true;
Bukkit.getScheduler().runTaskLaterAsynchronously(Cardinal.getInstance(), new Runnable() {
@Override
public void run() {
scheduledUpdate = false;
try {
for (TabView view : playerView.values()) view.update();
} catch (Exception e) {
e.printStackTrace();
}
}
}, TAB_UPDATE_TIME);
}
}
private static void addUpdateName(TabEntry entry) {
if (updateEntries.size() == 0) {
Bukkit.getScheduler().scheduleSyncDelayedTask(Cardinal.getInstance(), new Runnable() {
@Override
public void run() {
try {
Set<TabEntry> entries = new HashSet<>(updateEntries);
updateEntries.clear();
PacketPlayOutPlayerInfo listPacket = new PacketPlayOutPlayerInfo(
PacketPlayOutPlayerInfo.EnumPlayerInfoAction.UPDATE_DISPLAY_NAME);
boolean compact = false;
for (TabEntry entry : entries) {
if (entry instanceof PlayerTabEntry) {
entry.broadcastTabListPacket(
PacketPlayOutPlayerInfo.EnumPlayerInfoAction.UPDATE_DISPLAY_NAME);
} else {
compact = true;
listPacket.add(entry.getPlayerInfo(null, listPacket));
}
}
if (compact) PacketUtils.broadcastPacket(listPacket);
} catch (Exception e) {
e.printStackTrace();
}
}
}, TAB_UPDATE_TIME);
}
updateEntries.add(entry);
}
private static void addUpdateTeam(Optional<TeamModule> team) {
if (team != null && team.isPresent()) {
teamNeedUpdate.add(team.get());
}
}
public static boolean updateTeam(TeamModule team) {
if (teamNeedUpdate.contains(team)) {
teamNeedUpdate.remove(team);
return true;
}
return false;
}
public static void renderAllTeamTitles() {
for (TabEntry entry : teamTitles.values()) {
addUpdateName(entry);
}
}
public static void renderTeamTitle(TeamModule team) {
if (team.isObserver()) return;
addUpdateName(getTeam(team));
}
public static List<TabEntry> getTabEntries() {
List<TabEntry> result = Lists.newArrayList();
result.addAll(fakePlayer.values());
result.addAll(teamTitles.values());
result.addAll(emptyPlayers);
return result;
}
public static Collection<TabView> getTabViews() {
return playerView.values();
}
public static PlayerTabEntry getPlayer(Player player) {
if (!fakePlayer.containsKey(player.getUniqueId())) {
fakePlayer.put(player.getUniqueId(), new PlayerTabEntry(player));
}
return fakePlayer.get(player.getUniqueId());
}
private static void removePlayer(Player player) {
if (!fakePlayer.containsKey(player.getUniqueId())) return;
TabEntry entry = getPlayer(player);
entry.destroy();
fakePlayer.remove(player.getUniqueId());
}
public static TeamTabEntry getTeam(TeamModule team) {
if (!teamTitles.containsKey(team.getId()))
teamTitles.put(team.getId(), new TeamTabEntry(team));
return teamTitles.get(team.getId());
}
private static void resetTeams() {
if (teamTitles.isEmpty()) return;
for (TabEntry entry : teamTitles.values()) {
entry.destroy();
}
teamTitles.clear();
}
public static EmptyTabEntry getFakePlayer(TabView view) {
for (EmptyTabEntry emptyPlayer : emptyPlayers) {
if (view.getEmptyPlayers().contains(emptyPlayer)) continue;
return emptyPlayer;
}
EmptyTabEntry fakePlayer = new EmptyTabEntry();
emptyPlayers.add(fakePlayer);
return fakePlayer;
}
public static Packet getTeamPacket(String player, int slot, int action) {
return getTeamPacket(player == null ? Collections.<String>emptyList() : Collections.singletonList(player),
slot, action);
}
public static Packet getTeamPacket(Collection<String> players, int slot, int action) {
String team = "\000TabView" + (slot < 10 ? "0" + slot : slot);
return new PacketPlayOutScoreboardTeam(action, team, team, "", "", -1, "never", "never", 0, players);
}
}