/FileExtraction/Java_unseen/Wind-Development_WindSpigot/WindSpigot-Server/src/main/java/net/minecraft/server/PlayerList.java
package net.minecraft.server;

import java.io.File;
import java.net.SocketAddress;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.TravelAgent;
// CraftBukkit start
import org.bukkit.craftbukkit.CraftServer;
import org.bukkit.craftbukkit.CraftWorld;
import org.bukkit.craftbukkit.chunkio.ChunkIOExecutor;
import org.bukkit.craftbukkit.inventory.CraftInventoryView;
import org.bukkit.craftbukkit.util.CraftChatMessage;
import org.bukkit.entity.Player;
import org.bukkit.event.player.PlayerChangedWorldEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerLoginEvent;
import org.bukkit.event.player.PlayerPortalEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.event.player.PlayerRespawnEvent;
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
import org.bukkit.util.Vector;
import org.spigotmc.event.player.PlayerSpawnLocationEvent;
// CraftBukkit end

import com.eatthepath.uuid.FastUUID;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.mojang.authlib.GameProfile;

import ga.windpvp.windspigot.WindSpigot;
import ga.windpvp.windspigot.async.AsyncUtil;
import ga.windpvp.windspigot.config.WindSpigotConfig;
import io.netty.buffer.Unpooled;
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap;

public abstract class PlayerList {

	public static final File a = new File("banned-players.json");
	public static final File b = new File("banned-ips.json");
	public static final File c = new File("ops.json");
	public static final File d = new File("whitelist.json");
	private static final Logger f = LogManager.getLogger();
	private static final SimpleDateFormat g = new SimpleDateFormat("yyyy-MM-dd 'at' HH:mm:ss z");
	private final MinecraftServer server;
	public final List<EntityPlayer> players = new java.util.concurrent.CopyOnWriteArrayList<>(); // CraftBukkit -
																									// ArrayList ->
																									// CopyOnWriteArrayList:
																									// Iterator safety
	// private final Map<UUID, EntityPlayer> j = Maps.newHashMap();
	// PaperSpigot start - Player lookup improvements
	public final Map<String, EntityPlayer> playerMap = new Object2ObjectArrayMap<String, EntityPlayer>() {
		
		private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
		
		@Override
		public EntityPlayer put(String key, EntityPlayer value) {
			lock.writeLock().lock();
			// WindSpigot - synchronize player map
			try {
				return super.put(key.toLowerCase(), value);
			} finally {
				lock.writeLock().unlock();
			}
		}

		@Override
		public EntityPlayer get(Object key) {
			// WindSpigot - synchronize player map
			lock.readLock().lock();
			try {
				// put the .playerConnection check done in other places here
				EntityPlayer player = super.get(key instanceof String ? ((String) key).toLowerCase() : key);
				return (player != null && player.playerConnection != null) ? player : null;
			} finally {
				lock.readLock().unlock();
			}
		}

		@Override
		public boolean containsKey(Object key) {
			return get(key) != null;
		}

		@Override
		public EntityPlayer remove(Object key) {
			// WindSpigot - synchronize player map
			lock.writeLock().lock();
			try {
				return super.remove(key instanceof String ? ((String) key).toLowerCase() : key);
			} finally {
				lock.writeLock().unlock();
			}
		}
	};
	public final Map<UUID, EntityPlayer> uuidMap = new Object2ObjectArrayMap<UUID, EntityPlayer>() {
		
		private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

		@Override
		public EntityPlayer get(Object key) {
			// WindSpigot - synchronize uuid map
			lock.readLock().lock();
			try {
				// put the .playerConnection check done in other places here
				EntityPlayer player = super.get(key instanceof String ? ((String) key).toLowerCase() : key);
				return (player != null && player.playerConnection != null) ? player : null;
			} finally {
				lock.readLock().unlock();
			}
		}
		
		@Override
		public EntityPlayer put(UUID key, EntityPlayer value) {
			lock.writeLock().lock();
			// WindSpigot - synchronize uuid map
			try {
				return super.put(key, value);
			} finally {
				lock.writeLock().unlock();
			}
		}

		@Override
		public boolean containsKey(Object key) {
			// WindSpigot - synchronize uuid map
			return get(key) != null;
		}

		@Override
		public EntityPlayer remove(Object key) {
			// WindSpigot - synchronize uuid map
			lock.writeLock().lock();
			try {
				return super.remove(key);
			} finally {
				lock.writeLock().unlock();
			}
		}
	};
	// PaperSpigot end
	private final GameProfileBanList k;
	private final IpBanList l;
	private final OpList operators;
	private final Set<UUID> fastOperator = new HashSet<>();
	private final WhiteList whitelist;
	private final Map<UUID, ServerStatisticManager> o;
	public IPlayerFileData playerFileData;
	private boolean hasWhitelist;
	protected int maxPlayers;
	private int r;
	private WorldSettings.EnumGamemode s;
	private boolean t;
	private int u;

	// CraftBukkit start
	private final CraftServer cserver;
	private final Map<String, EntityPlayer> playersByName = new org.spigotmc.CaseInsensitiveMap<>();

	public PlayerList(MinecraftServer minecraftserver) {
		this.cserver = minecraftserver.server = new CraftServer(minecraftserver, this);
		minecraftserver.console = org.bukkit.craftbukkit.command.ColouredConsoleSender.getInstance();
		minecraftserver.reader
				.addCompleter(new org.bukkit.craftbukkit.command.ConsoleCommandCompleter(minecraftserver.server));
		// CraftBukkit end

		this.k = new GameProfileBanList(PlayerList.a);
		this.l = new IpBanList(PlayerList.b);
		this.operators = new OpList(PlayerList.c);
		// [Nacho-0037] Add toggle for "Faster Operator"
		if (WindSpigotConfig.useFastOperators) {
			for (OpListEntry value : this.operators.getValues()) {
				this.fastOperator.add(value.getKey().getId());
			}
		}
		this.whitelist = new WhiteList(PlayerList.d);
		this.o = Maps.newHashMap();
		this.server = minecraftserver;
		this.k.a(false);
		this.l.a(false);
		this.maxPlayers = 8;
	}

	public void a(NetworkManager networkmanager, EntityPlayer entityplayer) {
		GameProfile gameprofile = entityplayer.getProfile();
		UserCache usercache = this.server.getUserCache();
		GameProfile gameprofile1 = usercache.a(gameprofile.getId());
		String s = gameprofile1 == null ? gameprofile.getName() : gameprofile1.getName();

		usercache.a(gameprofile);
		NBTTagCompound nbttagcompound = this.a(entityplayer);
		// CraftBukkit start - Better rename detection
		if (nbttagcompound != null && nbttagcompound.hasKey("bukkit")) {
			NBTTagCompound bukkit = nbttagcompound.getCompound("bukkit");
			s = bukkit.hasKeyOfType("lastKnownName", 8) ? bukkit.getString("lastKnownName") : s;
		}
		// CraftBukkit end

		// PaperSpigot start - support PlayerInitialSpawnEvent
		Location originalLoc = new Location(entityplayer.world.getWorld(), entityplayer.locX, entityplayer.locY,
				entityplayer.locZ, entityplayer.yaw, entityplayer.pitch);
		org.bukkit.event.player.PlayerInitialSpawnEvent event = new org.bukkit.event.player.PlayerInitialSpawnEvent(
				entityplayer.getBukkitEntity(), originalLoc);
		this.server.server.getPluginManager().callEvent(event);

		Location newLoc = event.getSpawnLocation();
		entityplayer.world = ((CraftWorld) newLoc.getWorld()).getHandle();
		entityplayer.locX = newLoc.getX();
		entityplayer.locY = newLoc.getY();
		entityplayer.locZ = newLoc.getZ();
		entityplayer.yaw = newLoc.getYaw();
		entityplayer.pitch = newLoc.getPitch();
		entityplayer.dimension = ((CraftWorld) newLoc.getWorld()).getHandle().dimension;
		entityplayer.spawnWorld = entityplayer.world.worldData.getName();
		// PaperSpigot end

		entityplayer.spawnIn(this.server.getWorldServer(entityplayer.dimension));
		entityplayer.playerInteractManager.a((WorldServer) entityplayer.world);
		String s1 = "local";

		if (networkmanager.getSocketAddress() != null) {
			s1 = networkmanager.getSocketAddress().toString();
		}

		// Spigot start - spawn location event
		Player bukkitPlayer = entityplayer.getBukkitEntity();
		PlayerSpawnLocationEvent ev = new PlayerSpawnLocationEvent(bukkitPlayer, bukkitPlayer.getLocation());
		Bukkit.getPluginManager().callEvent(ev);

		Location loc = ev.getSpawnLocation();
		WorldServer world = ((CraftWorld) loc.getWorld()).getHandle();

		entityplayer.spawnIn(world);
		entityplayer.setPosition(loc.getX(), loc.getY(), loc.getZ());
		entityplayer.setYawPitch(loc.getYaw(), loc.getPitch());
		// Spigot end

		// CraftBukkit - Moved message to after join
		// PlayerList.f.info(entityplayer.getName() + "[" + s1 + "] logged in with
		// entity id " + entityplayer.getId() + " at (" + entityplayer.locX + ", " +
		// entityplayer.locY + ", " + entityplayer.locZ + ")");
		WorldServer worldserver = this.server.getWorldServer(entityplayer.dimension);
		WorldData worlddata = worldserver.getWorldData();
		BlockPosition blockposition = worldserver.getSpawn();

		this.a(entityplayer, null, worldserver);
		PlayerConnection playerconnection = new PlayerConnection(this.server, networkmanager, entityplayer);

		playerconnection.sendPacket(
				new PacketPlayOutLogin(entityplayer.getId(), entityplayer.playerInteractManager.getGameMode(),
						worlddata.isHardcore(), worldserver.worldProvider.getDimension(), worldserver.getDifficulty(),
						Math.min(this.getMaxPlayers(), 60), worlddata.getType(),
						worldserver.getGameRules().getBoolean("reducedDebugInfo"))); // CraftBukkit - cap player list to
																						// 60
		entityplayer.getBukkitEntity().sendSupportedChannels(); // CraftBukkit
		playerconnection.sendPacket(new PacketPlayOutCustomPayload("MC|Brand",
				(new PacketDataSerializer(Unpooled.buffer())).a(this.getServer().getServerModName())));
		playerconnection.sendPacket(
				new PacketPlayOutServerDifficulty(worlddata.getDifficulty(), worlddata.isDifficultyLocked()));
		playerconnection.sendPacket(new PacketPlayOutSpawnPosition(blockposition));
		playerconnection.sendPacket(new PacketPlayOutAbilities(entityplayer.abilities));
		playerconnection.sendPacket(new PacketPlayOutHeldItemSlot(entityplayer.inventory.itemInHandIndex));
		entityplayer.getStatisticManager().d();
		entityplayer.getStatisticManager().updateStatistics(entityplayer);
		this.sendScoreboard((ScoreboardServer) worldserver.getScoreboard(), entityplayer);
		this.server.aH();
		// CraftBukkit start - login message is handled in the event
		// ChatMessage chatmessage;

		String joinMessage;
		if (!entityplayer.getName().equalsIgnoreCase(s)) {
			// chatmessage = new ChatMessage("multiplayer.player.joined.renamed", new
			// Object[] { entityplayer.getScoreboardDisplayName(), s});
			joinMessage = "\u00A7e" + LocaleI18n.a("multiplayer.player.joined.renamed", entityplayer.getName(), s);
		} else {
			// chatmessage = new ChatMessage("multiplayer.player.joined", new Object[] {
			// entityplayer.getScoreboardDisplayName()});
			joinMessage = "\u00A7e" + LocaleI18n.a("multiplayer.player.joined", entityplayer.getName());
		}

		// chatmessage.getChatModifier().setColor(EnumChatFormat.YELLOW);
		// this.sendMessage(chatmessage);
		this.onPlayerJoin(entityplayer, joinMessage);
		// CraftBukkit end
		worldserver = server.getWorldServer(entityplayer.dimension); // CraftBukkit - Update in case join event changed
																		// it
		playerconnection.a(entityplayer.locX, entityplayer.locY, entityplayer.locZ, entityplayer.yaw,
				entityplayer.pitch);
		this.b(entityplayer, worldserver);
		if (this.server.getResourcePack().length() > 0) {
			entityplayer.setResourcePack(this.server.getResourcePack(), this.server.getResourcePackHash());
		}

		for (MobEffect mobeffect : entityplayer.getEffects()) {
			playerconnection.sendPacket(new PacketPlayOutEntityEffect(entityplayer.getId(), mobeffect));
		}

		entityplayer.syncInventory();
		if (nbttagcompound != null && nbttagcompound.hasKeyOfType("Riding", 10)) {
			Entity entity = EntityTypes.a(nbttagcompound.getCompound("Riding"), worldserver);

			if (entity != null) {
				entity.attachedToPlayer = true;
				worldserver.addEntity(entity);
				entityplayer.mount(entity);
				entity.attachedToPlayer = false;
			}
		}
		
		// WindSpigot start - toggleable player ip hiding
		if (!WindSpigotConfig.showPlayerIps) {
			// Replace the address before the ":port" part of the string
			String port = s1.split(":") [1];
			s1 = "hidden-address:" + port;
		}
		// WindSpigot end

		// CraftBukkit - Moved from above, added world
		PlayerList.f.info(entityplayer.getName() + "[" + s1 + "] logged in with entity id " + entityplayer.getId()
				+ " at ([" + entityplayer.world.worldData.getName() + "]" + entityplayer.locX + ", " + entityplayer.locY
				+ ", " + entityplayer.locZ + ")");
	}

	public void sendScoreboard(ScoreboardServer scoreboardserver, EntityPlayer player) {
		HashSet<ScoreboardObjective> objectives = Sets.newHashSet();
		for (ScoreboardTeam scoreboardteam : scoreboardserver.getTeams()) {
			player.playerConnection.sendPacket(new PacketPlayOutScoreboardTeam(scoreboardteam, 0));
		}
		for (int i = 0; i < 19; ++i) {
			ScoreboardObjective objective = scoreboardserver.getObjectiveForSlot(i);
			if (objective != null && !objectives.contains(objective)) {
				List<Packet<PacketListenerPlayOut>> packets = scoreboardserver
						.getScoreboardScorePacketsForObjective(objective);
				for (Packet<PacketListenerPlayOut> packet : packets) {
					player.playerConnection.sendPacket(packet);
				}
				objectives.add(objective);
			}
		}

	}

	public void setPlayerFileData(WorldServer[] aworldserver) {
		if (playerFileData != null) {
			return; // CraftBukkit
		}
		this.playerFileData = aworldserver[0].getDataManager().getPlayerFileData();
		aworldserver[0].getWorldBorder().a(new IWorldBorderListener() {
			@Override
			public void a(WorldBorder worldborder, double d0) {
				PlayerList.this.sendAll(new PacketPlayOutWorldBorder(worldborder,
						PacketPlayOutWorldBorder.EnumWorldBorderAction.SET_SIZE));
			}

			@Override
			public void a(WorldBorder worldborder, double d0, double d1, long i) {
				PlayerList.this.sendAll(new PacketPlayOutWorldBorder(worldborder,
						PacketPlayOutWorldBorder.EnumWorldBorderAction.LERP_SIZE));
			}

			@Override
			public void a(WorldBorder worldborder, double d0, double d1) {
				PlayerList.this.sendAll(new PacketPlayOutWorldBorder(worldborder,
						PacketPlayOutWorldBorder.EnumWorldBorderAction.SET_CENTER));
			}

			@Override
			public void a(WorldBorder worldborder, int i) {
				PlayerList.this.sendAll(new PacketPlayOutWorldBorder(worldborder,
						PacketPlayOutWorldBorder.EnumWorldBorderAction.SET_WARNING_TIME));
			}

			@Override
			public void b(WorldBorder worldborder, int i) {
				PlayerList.this.sendAll(new PacketPlayOutWorldBorder(worldborder,
						PacketPlayOutWorldBorder.EnumWorldBorderAction.SET_WARNING_BLOCKS));
			}

			@Override
			public void b(WorldBorder worldborder, double d0) {
			}

			@Override
			public void c(WorldBorder worldborder, double d0) {
			}
		});
	}

	public void a(EntityPlayer entityplayer, WorldServer worldserver) {
		WorldServer worldserver1 = entityplayer.u();

		if (worldserver != null) {
			worldserver.getPlayerChunkMap().removePlayer(entityplayer);
		}

		worldserver1.getPlayerChunkMap().addPlayer(entityplayer);
		worldserver1.chunkProviderServer.getChunkAt((int) entityplayer.locX >> 4, (int) entityplayer.locZ >> 4);
	}

	public int d() {
		return PlayerChunkMap.getFurthestViewableBlock(this.s());
	}

	public NBTTagCompound a(EntityPlayer entityplayer) {
		NBTTagCompound nbttagcompound = this.server.worlds.get(0).getWorldData().i(); // CraftBukkit
		NBTTagCompound nbttagcompound1;

		if (entityplayer.getName().equals(this.server.S()) && nbttagcompound != null) {
			entityplayer.f(nbttagcompound);
			nbttagcompound1 = nbttagcompound;
			PlayerList.f.debug("loading single player");
		} else {
			nbttagcompound1 = this.playerFileData.load(entityplayer);
		}

		return nbttagcompound1;
	}

	protected void savePlayerFile(EntityPlayer entityplayer) {
		this.playerFileData.save(entityplayer);
		ServerStatisticManager serverstatisticmanager = this.o.get(entityplayer.getUniqueID());

		if (serverstatisticmanager != null) {
			serverstatisticmanager.b();
		}

	}

	public void onPlayerJoin(EntityPlayer entityplayer, String joinMessage) { // CraftBukkit added param
		this.players.add(entityplayer);
		this.playersByName.put(entityplayer.getName(), entityplayer); // Spigot
		this.playerMap.put(entityplayer.getName(), entityplayer); // PaperSpigot
		this.uuidMap.put(entityplayer.getUniqueID(), entityplayer); // PaperSpigot
		// this.sendAll(new
		// PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.ADD_PLAYER,
		// new EntityPlayer[] { entityplayer})); // CraftBukkit - replaced with loop
		// below
		WorldServer worldserver = this.server.getWorldServer(entityplayer.dimension);

		// CraftBukkit start
		PlayerJoinEvent playerJoinEvent = new PlayerJoinEvent(cserver.getPlayer(entityplayer), joinMessage);
		cserver.getPluginManager().callEvent(playerJoinEvent);

		joinMessage = playerJoinEvent.getJoinMessage();

		if (joinMessage != null && joinMessage.length() > 0) {
			for (IChatBaseComponent line : org.bukkit.craftbukkit.util.CraftChatMessage.fromString(joinMessage)) {
				server.getPlayerList().sendAll(new PacketPlayOutChat(line));
			}
		}

		ChunkIOExecutor.adjustPoolSize(getPlayerCount());
		// CraftBukkit end

		// CraftBukkit start - sendAll above replaced with this loop
		PacketPlayOutPlayerInfo packet = new PacketPlayOutPlayerInfo(
				PacketPlayOutPlayerInfo.EnumPlayerInfoAction.ADD_PLAYER, entityplayer);

		for (int i = 0; i < this.players.size(); ++i) {
			EntityPlayer entityplayer1 = this.players.get(i);

			if (entityplayer1.getBukkitEntity().canSee(entityplayer.getBukkitEntity())) {
				entityplayer1.playerConnection.sendPacket(packet);
			}

			if (!entityplayer.getBukkitEntity().canSee(entityplayer1.getBukkitEntity())) {
				continue;
			}

			entityplayer.playerConnection.sendPacket(new PacketPlayOutPlayerInfo(
					PacketPlayOutPlayerInfo.EnumPlayerInfoAction.ADD_PLAYER, new EntityPlayer[] { entityplayer1 }));
		}
		// CraftBukkit end

		// CraftBukkit start - Only add if the player wasn't moved in the event
		if (entityplayer.world == worldserver && !worldserver.players.contains(entityplayer)) {
			worldserver.addEntity(entityplayer);
			this.a(entityplayer, (WorldServer) null);
		}
		// CraftBukkit end
	}

	public void d(EntityPlayer entityplayer) {
		entityplayer.u().getPlayerChunkMap().movePlayer(entityplayer);
		entityplayer.world.playerMap.move(entityplayer);
	}

	public String disconnect(EntityPlayer entityplayer) { // CraftBukkit - return string
		entityplayer.b(StatisticList.f);

		// CraftBukkit start - Quitting must be before we do final save of data, in case
		// plugins need to modify it
		org.bukkit.craftbukkit.event.CraftEventFactory.handleInventoryCloseEvent(entityplayer);

		Player bukkit = cserver.getPlayer(entityplayer); // KigPaper
		PlayerQuitEvent playerQuitEvent = new PlayerQuitEvent(bukkit,
				"\u00A7e" + entityplayer.getName() + " left the game.");
		cserver.getPluginManager().callEvent(playerQuitEvent);
		entityplayer.getBukkitEntity().disconnect(playerQuitEvent.getQuitMessage());
		// CraftBukkit end

		this.savePlayerFile(entityplayer);
		WorldServer worldserver = entityplayer.u();

		if (entityplayer.vehicle != null && !(entityplayer.vehicle instanceof EntityPlayer)) { // CraftBukkit - Don't
																								// remove players
			worldserver.removeEntity(entityplayer.vehicle);
			PlayerList.f.debug("removing player mount");
		}

		worldserver.kill(entityplayer);
		worldserver.getPlayerChunkMap().removePlayer(entityplayer);
		this.players.remove(entityplayer);
		this.uuidMap.remove(entityplayer.getUniqueID()); // PaperSpigot
		this.playerMap.remove(entityplayer.getName()); // PaperSpigot
		this.playersByName.remove(entityplayer.getName()); // Spigot
		UUID uuid = entityplayer.getUniqueID();
		EntityPlayer entityplayer1 = this.uuidMap.get(uuid);

		if (entityplayer1 == entityplayer) {
			this.uuidMap.remove(uuid);
			this.playerMap.remove(entityplayer.getName()); // PaperSpigot
			this.o.remove(uuid);
		}

		// CraftBukkit start
		// this.sendAll(new PacketPlayOutPlayerInfo(EnumPlayerInfoAction.REMOVE_PLAYER,
		// new EntityPlayer[] { entityplayer}));
		PacketPlayOutPlayerInfo packet = new PacketPlayOutPlayerInfo(
				PacketPlayOutPlayerInfo.EnumPlayerInfoAction.REMOVE_PLAYER, entityplayer);
		for (int i = 0; i < players.size(); i++) {
			EntityPlayer entityplayer2 = this.players.get(i);

			if (entityplayer2.getBukkitEntity().canSee(entityplayer.getBukkitEntity())) {
				entityplayer2.playerConnection.sendPacket(packet);
			} else {
				entityplayer2.getBukkitEntity().removeDisconnectingPlayer(entityplayer.getBukkitEntity());
			}
		}
		// This removes the scoreboard (and player reference) for the specific player in
		// the manager
		cserver.getScoreboardManager().removePlayer(entityplayer.getBukkitEntity());
		// CraftBukkit end

		ChunkIOExecutor.adjustPoolSize(this.getPlayerCount()); // CraftBukkit

		// KigPaper start - fix memory leak
		CraftingManager craftingManager = CraftingManager.getInstance();
		CraftInventoryView lastView = (CraftInventoryView) craftingManager.lastCraftView;
		if (lastView != null && lastView.getHandle() instanceof ContainerPlayer && lastView.getPlayer() == bukkit) {
			craftingManager.lastCraftView = null;
			// KigPaper end
		}

		WindSpigot.getInstance().getLagCompensator().clearCache(bukkit); // Nacho
		
		return playerQuitEvent.getQuitMessage(); // CraftBukkit
	}

	// CraftBukkit start - Whole method, SocketAddress to LoginListener, added
	// hostname to signature, return EntityPlayer
	public EntityPlayer attemptLogin(LoginListener loginlistener, GameProfile gameprofile, String hostname) {
		// Moved from processLogin
		UUID uuid = EntityHuman.a(gameprofile);
		ArrayList<EntityPlayer> arraylist = Lists.newArrayList();

		EntityPlayer entityplayer;

		for (EntityPlayer entityPlayer : this.players) {
			entityplayer = entityPlayer;
			if (entityplayer.getUniqueID().equals(uuid)) {
				arraylist.add(entityplayer);
			}
		}

		for (EntityPlayer value : arraylist) {
			entityplayer = value;
			savePlayerFile(entityplayer); // CraftBukkit - Force the player's inventory to be saved
			entityplayer.playerConnection.disconnect("You logged in from another location");
		}

		// Instead of kicking then returning, we need to store the kick reason
		// in the event, check with plugins to see if it's ok, and THEN kick
		// depending on the outcome.
		SocketAddress socketaddress = loginlistener.networkManager.getSocketAddress();

		EntityPlayer entity = new EntityPlayer(server, server.getWorldServer(0), gameprofile,
				new PlayerInteractManager(server.getWorldServer(0)));
		Player player = entity.getBukkitEntity();
		PlayerLoginEvent event = new PlayerLoginEvent(player, hostname,
				((java.net.InetSocketAddress) socketaddress).getAddress(),
				((java.net.InetSocketAddress) loginlistener.networkManager.getRawAddress()).getAddress());
		String s;

		if (getProfileBans().isBanned(gameprofile) && !getProfileBans().get(gameprofile).hasExpired()) {
			GameProfileBanEntry gameprofilebanentry = this.k.get(gameprofile);

			s = "You are banned from this server!\nReason: " + gameprofilebanentry.getReason();
			if (gameprofilebanentry.getExpires() != null) {
				s = s + "\nYour ban will be removed on " + PlayerList.g.format(gameprofilebanentry.getExpires());
			}

			// return s;
			if (!gameprofilebanentry.hasExpired()) {
				event.disallow(PlayerLoginEvent.Result.KICK_BANNED, s); // Spigot
			}
		} else if (!this.isWhitelisted(gameprofile)) {
			// return "You are not white-listed on this server!";
			event.disallow(PlayerLoginEvent.Result.KICK_WHITELIST, org.spigotmc.SpigotConfig.whitelistMessage); // Spigot
		} else if (getIPBans().isBanned(socketaddress) && !getIPBans().get(socketaddress).hasExpired()) {
			IpBanEntry ipbanentry = this.l.get(socketaddress);

			s = "Your IP address is banned from this server!\nReason: " + ipbanentry.getReason();
			if (ipbanentry.getExpires() != null) {
				s = s + "\nYour ban will be removed on " + PlayerList.g.format(ipbanentry.getExpires());
			}

			// return s;
			event.disallow(PlayerLoginEvent.Result.KICK_BANNED, s);
		} else // return this.players.size() >= this.maxPlayers && !this.f(gameprofile) ? "The
		// server is full!" : null;
		if (this.players.size() >= this.maxPlayers && !this.f(gameprofile)) {
			event.disallow(PlayerLoginEvent.Result.KICK_FULL, org.spigotmc.SpigotConfig.serverFullMessage); // Spigot
		}

		cserver.getPluginManager().callEvent(event);
		if (event.getResult() != PlayerLoginEvent.Result.ALLOWED) {
			loginlistener.disconnect(event.getKickMessage());
			return null;
		}
		return entity;
	}

	public EntityPlayer processLogin(GameProfile gameprofile, EntityPlayer player) { // CraftBukkit - added EntityPlayer
		/*
		 * CraftBukkit startMoved up UUID uuid = EntityHuman.a(gameprofile); ArrayList
		 * arraylist = Lists.newArrayList();
		 * 
		 * for (int i = 0; i < this.players.size(); ++i) { EntityPlayer entityplayer =
		 * (EntityPlayer) this.players.get(i);
		 * 
		 * if (entityplayer.getUniqueID().equals(uuid)) { arraylist.add(entityplayer); }
		 * }
		 * 
		 * EntityPlayer entityplayer1 = (EntityPlayer) this.j.get(gameprofile.getId());
		 * 
		 * if (entityplayer1 != null && !arraylist.contains(entityplayer1)) {
		 * arraylist.add(entityplayer1); }
		 * 
		 * Iterator iterator = arraylist.iterator();
		 * 
		 * while (iterator.hasNext()) { EntityPlayer entityplayer2 = (EntityPlayer)
		 * iterator.next();
		 * 
		 * entityplayer2.playerConnection.
		 * disconnect("You logged in from another location"); }
		 * 
		 * Object object;
		 * 
		 * if (this.server.X()) { object = new
		 * DemoPlayerInteractManager(this.server.getWorldServer(0)); } else { object =
		 * new PlayerInteractManager(this.server.getWorldServer(0)); }
		 * 
		 * return new EntityPlayer(this.server, this.server.getWorldServer(0),
		 * gameprofile, (PlayerInteractManager) object);
		 */
		return player;
		// CraftBukkit end
	}

	// CraftBukkit start
	public EntityPlayer moveToWorld(EntityPlayer entityplayer, int i, boolean flag) {
		return this.moveToWorld(entityplayer, i, flag, null, true);
	}

	public EntityPlayer moveToWorld(EntityPlayer entityplayer, int i, boolean flag, Location location,
			boolean avoidSuffocation) {
		entityplayer.u().getTracker().untrackPlayer(entityplayer);
		// entityplayer.u().getTracker().untrackEntity(entityplayer); // CraftBukkit
		entityplayer.u().getPlayerChunkMap().removePlayer(entityplayer);
		this.players.remove(entityplayer);
		this.playersByName.remove(entityplayer.getName()); // Spigot
		this.server.getWorldServer(entityplayer.dimension).removeEntity(entityplayer);
		BlockPosition blockposition = entityplayer.getBed();
		boolean flag1 = entityplayer.isRespawnForced();

		/*
		 * CraftBukkit start entityplayer.dimension = i; Object object;
		 * 
		 * if (this.server.X()) { object = new
		 * DemoPlayerInteractManager(this.server.getWorldServer(entityplayer.dimension))
		 * ; } else { object = new
		 * PlayerInteractManager(this.server.getWorldServer(entityplayer.dimension)); }
		 * 
		 * EntityPlayer entityplayer1 = new EntityPlayer(this.server,
		 * this.server.getWorldServer(entityplayer.dimension),
		 * entityplayer.getProfile(), (PlayerInteractManager) object); //
		 */
		EntityPlayer entityplayer1 = entityplayer;
		org.bukkit.World fromWorld = entityplayer.getBukkitEntity().getWorld();
		entityplayer.viewingCredits = false;
		// CraftBukkit end

		entityplayer1.playerConnection = entityplayer.playerConnection;
		entityplayer1.copyTo(entityplayer, flag);
		entityplayer1.d(entityplayer.getId());
		entityplayer1.o(entityplayer);
		// WorldServer worldserver = this.server.getWorldServer(entityplayer.dimension);
		// // CraftBukkit - handled later

		// this.a(entityplayer1, entityplayer, worldserver); // CraftBukkit - removed
		BlockPosition blockposition1;

		// CraftBukkit start - fire PlayerRespawnEvent
		if (location == null) {
			boolean isBedSpawn = false;
			CraftWorld cworld = (CraftWorld) this.server.server.getWorld(entityplayer.spawnWorld);
			if (cworld != null && blockposition != null) {
				blockposition1 = EntityHuman.getBed(cworld.getHandle(), blockposition, flag1);
				if (blockposition1 != null) {
					isBedSpawn = true;
					location = new Location(cworld, blockposition1.getX() + 0.5, blockposition1.getY(),
							blockposition1.getZ() + 0.5);
				} else {
					entityplayer1.setRespawnPosition(null, true);
					entityplayer1.playerConnection.sendPacket(new PacketPlayOutGameStateChange(0, 0.0F));
				}
			}

			if (location == null) {
				cworld = (CraftWorld) this.server.server.getWorlds().get(0);
				blockposition = cworld.getHandle().getSpawn();
				location = new Location(cworld, blockposition.getX() + 0.5, blockposition.getY(),
						blockposition.getZ() + 0.5);
			}

			Player respawnPlayer = cserver.getPlayer(entityplayer1);
			
			PlayerRespawnEvent respawnEvent = new PlayerRespawnEvent(respawnPlayer, location, isBedSpawn);
			cserver.getPluginManager().callEvent(respawnEvent);
			// Spigot Start
			if (entityplayer.playerConnection.isDisconnected()) {
				return entityplayer;
			}
			// Spigot End

            WindSpigot.getInstance().getLagCompensator().clearCache(respawnPlayer); // Nacho
			location = respawnEvent.getRespawnLocation();
			entityplayer.reset();
		} else {
			location.setWorld(server.getWorldServer(i).getWorld());
		}
		WorldServer worldserver = ((CraftWorld) location.getWorld()).getHandle();
		entityplayer1.setLocation(location.getX(), location.getY(), location.getZ(), location.getYaw(),
				location.getPitch());
		// CraftBukkit end

		worldserver.chunkProviderServer.getChunkAt((int) entityplayer1.locX >> 4, (int) entityplayer1.locZ >> 4);

		while (avoidSuffocation && !worldserver.getCubes(entityplayer1, entityplayer1.getBoundingBox()).isEmpty()
				&& entityplayer1.locY < 256.0D) {
			entityplayer1.setPosition(entityplayer1.locX, entityplayer1.locY + 1.0D, entityplayer1.locZ);
		}
		// CraftBukkit start
		byte actualDimension = (byte) (worldserver.getWorld().getEnvironment().getId());
		// Force the client to refresh their chunk cache
		if (fromWorld.getEnvironment() == worldserver.getWorld().getEnvironment()) {
			entityplayer1.playerConnection.sendPacket(
					new PacketPlayOutRespawn((byte) (actualDimension >= 0 ? -1 : 0), worldserver.getDifficulty(),
							worldserver.getWorldData().getType(), entityplayer.playerInteractManager.getGameMode()));
		}
		entityplayer1.playerConnection.sendPacket(new PacketPlayOutRespawn(actualDimension, worldserver.getDifficulty(),
				worldserver.getWorldData().getType(), entityplayer1.playerInteractManager.getGameMode()));
		entityplayer1.spawnIn(worldserver);
		entityplayer1.dead = false;
		entityplayer1.playerConnection.teleport(new Location(worldserver.getWorld(), entityplayer1.locX,
				entityplayer1.locY, entityplayer1.locZ, entityplayer1.yaw, entityplayer1.pitch));
		entityplayer1.setSneaking(false);
		blockposition1 = worldserver.getSpawn();
		// entityplayer1.playerConnection.a(entityplayer1.locX, entityplayer1.locY,
		// entityplayer1.locZ, entityplayer1.yaw, entityplayer1.pitch);
		entityplayer1.playerConnection.sendPacket(new PacketPlayOutSpawnPosition(blockposition1));
		entityplayer1.playerConnection.sendPacket(
				new PacketPlayOutExperience(entityplayer1.exp, entityplayer1.expTotal, entityplayer1.expLevel));
		this.b(entityplayer1, worldserver);

		if (!entityplayer.playerConnection.isDisconnected()) {
			// WindSpigot start - safe cross world player teleports
			if (Thread.currentThread() != MinecraftServer.getServer().primaryThread) {
				// Schedule this to run sync one tick later
				AsyncUtil.runPostTick(() -> {
					worldserver.getPlayerChunkMap().addPlayer(entityplayer1);
					worldserver.addEntity(entityplayer1);
				});
			} else {
				worldserver.getPlayerChunkMap().addPlayer(entityplayer1);
				worldserver.addEntity(entityplayer1);
			}
			// WindSpigot end
			this.players.add(entityplayer1);
			this.playersByName.put(entityplayer1.getName(), entityplayer1); // Spigot
			this.playerMap.put(entityplayer1.getName(), entityplayer1); // PaperSpigot
			this.uuidMap.put(entityplayer1.getUniqueID(), entityplayer1);
		}
		// Added from changeDimension
		updateClient(entityplayer); // Update health, etc...
		entityplayer.updateAbilities();
		for (MobEffect o1 : entityplayer.getEffects()) {
			MobEffect mobEffect = o1;
			entityplayer.playerConnection.sendPacket(new PacketPlayOutEntityEffect(entityplayer.getId(), mobEffect));
		}
		// entityplayer1.syncInventory();
		// CraftBukkit end
		entityplayer1.setHealth(entityplayer1.getHealth());

		// CraftBukkit start
		// Don't fire on respawn
		if (fromWorld != location.getWorld()) {
			// Nacho
            WindSpigot.getInstance().getLagCompensator().registerMovement(entityplayer.getBukkitEntity(), entityplayer.getBukkitEntity().getLocation());
			PlayerChangedWorldEvent event = new PlayerChangedWorldEvent(entityplayer.getBukkitEntity(), fromWorld);
			server.server.getPluginManager().callEvent(event);
		}

		// Save player file again if they were disconnected
		if (entityplayer.playerConnection.isDisconnected()) {
			this.savePlayerFile(entityplayer);
		}
		// CraftBukkit end
		return entityplayer1;
	}

	// CraftBukkit start - Replaced the standard handling of portals with a more
	// customised method.
	public void changeDimension(EntityPlayer entityplayer, int i, TeleportCause cause) {
		WorldServer exitWorld = null;
		if (entityplayer.dimension < CraftWorld.CUSTOM_DIMENSION_OFFSET) { // plugins must specify exit from custom
																			// Bukkit worlds
																			// only target existing worlds (compensate
																			// for allow-nether/allow-end as false)
			for (WorldServer world : this.server.worlds) {
				if (world.dimension == i) {
					exitWorld = world;
				}
			}
		}

		Location enter = entityplayer.getBukkitEntity().getLocation();
		Location exit = null;
		boolean useTravelAgent = false; // don't use agent for custom worlds or return from THE_END
		if (exitWorld != null) {
			if ((cause == TeleportCause.END_PORTAL) && (i == 0)) {
				// THE_END -> NORMAL; use bed if available, otherwise default spawn
				exit = entityplayer.getBukkitEntity().getBedSpawnLocation();
				if (exit == null || ((CraftWorld) exit.getWorld()).getHandle().dimension != 0) {
					exit = exitWorld.getWorld().getSpawnLocation();
				}
			} else {
				// NORMAL <-> NETHER or NORMAL -> THE_END
				exit = this.calculateTarget(enter, exitWorld);
				useTravelAgent = true;
			}
		}

		TravelAgent agent = exit != null ? (TravelAgent) ((CraftWorld) exit.getWorld()).getHandle().getTravelAgent()
				: org.bukkit.craftbukkit.CraftTravelAgent.DEFAULT; // return arbitrary TA to compensate for
																	// implementation dependent plugins
		agent.setCanCreatePortal(cause != TeleportCause.END_PORTAL); // PaperSpigot - Configurable end credits, don't
																		// allow End Portals to create portals

		PlayerPortalEvent event = new PlayerPortalEvent(entityplayer.getBukkitEntity(), enter, exit, agent, cause);
		event.useTravelAgent(useTravelAgent);
		Bukkit.getServer().getPluginManager().callEvent(event);
		if (event.isCancelled() || event.getTo() == null) {
			return;
		}

		// PaperSpigot - Configurable end credits, if a plugin sets to use a travel
		// agent even if the cause is an end portal, ignore it
		exit = cause != TeleportCause.END_PORTAL && event.useTravelAgent()
				? event.getPortalTravelAgent().findOrCreate(event.getTo())
				: event.getTo();
		if (exit == null) {
			return;
		}
		exitWorld = ((CraftWorld) exit.getWorld()).getHandle();
		
		WindSpigot.getInstance().getLagCompensator().registerMovement(entityplayer.getBukkitEntity(), exit); // Nacho
		
		org.bukkit.event.player.PlayerTeleportEvent tpEvent = new org.bukkit.event.player.PlayerTeleportEvent(
				entityplayer.getBukkitEntity(), enter, exit, cause);
		Bukkit.getServer().getPluginManager().callEvent(tpEvent);
		if (tpEvent.isCancelled() || tpEvent.getTo() == null) {
			return;
		}

		Vector velocity = entityplayer.getBukkitEntity().getVelocity();
		boolean before = exitWorld.chunkProviderServer.forceChunkLoad;
		exitWorld.chunkProviderServer.forceChunkLoad = true;
		exitWorld.getTravelAgent().adjustExit(entityplayer, exit, velocity);
		exitWorld.chunkProviderServer.forceChunkLoad = before;

		this.moveToWorld(entityplayer, exitWorld.dimension, true, exit, false); // Vanilla doesn't check for suffocation
																				// when handling portals, so neither
																				// should we
		if (entityplayer.motX != velocity.getX() || entityplayer.motY != velocity.getY()
				|| entityplayer.motZ != velocity.getZ()) {
			entityplayer.getBukkitEntity().setVelocity(velocity);
		}
	}

	public void changeWorld(Entity entity, int i, WorldServer worldserver, WorldServer worldserver1) {
		// CraftBukkit start - Split into modular functions
		Location exit = calculateTarget(entity.getBukkitEntity().getLocation(), worldserver1);
		repositionEntity(entity, exit, true);
	}

	// Copy of original changeWorld(Entity, int, WorldServer, WorldServer) method
	// with only location calculation logic
	public Location calculateTarget(Location enter, World target) {
		WorldServer worldserver = ((CraftWorld) enter.getWorld()).getHandle();
		WorldServer worldserver1 = target.getWorld().getHandle();
		int i = worldserver.dimension;

		double y = enter.getY();
		float yaw = enter.getYaw();
		float pitch = enter.getPitch();
		double d0 = enter.getX();
		double d1 = enter.getZ();
		double d2 = 8.0D;
		/*
		 * double d0 = entity.locX; double d1 = entity.locZ; double d2 = 8.0D; float f =
		 * entity.yaw;
		 * 
		 * worldserver.methodProfiler.a("moving");
		 */
		if (worldserver1.dimension == -1) {
			d0 = MathHelper.a(d0 / d2, worldserver1.getWorldBorder().b() + 16.0D,
					worldserver1.getWorldBorder().d() - 16.0D);
			d1 = MathHelper.a(d1 / d2, worldserver1.getWorldBorder().c() + 16.0D,
					worldserver1.getWorldBorder().e() - 16.0D);
			/*
			 * entity.setPositionRotation(d0, entity.locY, d1, entity.yaw, entity.pitch); if
			 * (entity.isAlive()) { worldserver.entityJoinedWorld(entity, false); }
			 */
		} else if (worldserver1.dimension == 0) {
			d0 = MathHelper.a(d0 * d2, worldserver1.getWorldBorder().b() + 16.0D,
					worldserver1.getWorldBorder().d() - 16.0D);
			d1 = MathHelper.a(d1 * d2, worldserver1.getWorldBorder().c() + 16.0D,
					worldserver1.getWorldBorder().e() - 16.0D);
			/*
			 * entity.setPositionRotation(d0, entity.locY, d1, entity.yaw, entity.pitch); if
			 * (entity.isAlive()) { worldserver.entityJoinedWorld(entity, false); }
			 */
		} else {
			BlockPosition blockposition;

			if (i == 1) {
				// use default NORMAL world spawn instead of target
				worldserver1 = this.server.worlds.get(0);
				blockposition = worldserver1.getSpawn();
			} else {
				blockposition = worldserver1.getDimensionSpawn();
			}

			d0 = blockposition.getX();
			y = blockposition.getY();
			d1 = blockposition.getZ();
			/*
			 * entity.setPositionRotation(d0, entity.locY, d1, 90.0F, 0.0F); if
			 * (entity.isAlive()) { worldserver.entityJoinedWorld(entity, false); }
			 */
		}

		// worldserver.methodProfiler.b();
		if (i != 1) {
			worldserver.methodProfiler.a("placing");
			d0 = MathHelper.clamp((int) d0, -29999872, 29999872);
			d1 = MathHelper.clamp((int) d1, -29999872, 29999872);
			/*
			 * if (entity.isAlive()) { entity.setPositionRotation(d0, entity.locY, d1,
			 * entity.yaw, entity.pitch); worldserver1.getTravelAgent().a(entity, f);
			 * worldserver1.addEntity(entity); worldserver1.entityJoinedWorld(entity,
			 * false); }
			 * 
			 * worldserver.methodProfiler.b();
			 */
		}

		// entity.spawnIn(worldserver1);
		return new Location(worldserver1.getWorld(), d0, y, d1, yaw, pitch);
	}

	// copy of original a(Entity, int, WorldServer, WorldServer) method with only
	// entity repositioning logic
	public void repositionEntity(Entity entity, Location exit, boolean portal) {
		WorldServer worldserver = (WorldServer) entity.world;
		WorldServer worldserver1 = ((CraftWorld) exit.getWorld()).getHandle();
		int i = worldserver.dimension;

		/*
		 * double d0 = entity.locX; double d1 = entity.locZ; double d2 = 8.0D; float f =
		 * entity.yaw;
		 * 
		 * worldserver.methodProfiler.a("moving");
		 */
		entity.setPositionRotation(exit.getX(), exit.getY(), exit.getZ(), exit.getYaw(), exit.getPitch());
		if (entity.isAlive()) {
			worldserver.entityJoinedWorld(entity, false);
		}
		/*
		 * if (entity.dimension == -1) { d0 = MathHelper.a(d0 / d2,
		 * worldserver1.getWorldBorder().b() + 16.0D, worldserver1.getWorldBorder().d()
		 * - 16.0D); d1 = MathHelper.a(d1 / d2, worldserver1.getWorldBorder().c() +
		 * 16.0D, worldserver1.getWorldBorder().e() - 16.0D);
		 * entity.setPositionRotation(d0, entity.locY, d1, entity.yaw, entity.pitch); if
		 * (entity.isAlive()) { worldserver.entityJoinedWorld(entity, false); } } else
		 * if (entity.dimension == 0) { d0 = MathHelper.a(d0 * d2,
		 * worldserver1.getWorldBorder().b() + 16.0D, worldserver1.getWorldBorder().d()
		 * - 16.0D); d1 = MathHelper.a(d1 * d2, worldserver1.getWorldBorder().c() +
		 * 16.0D, worldserver1.getWorldBorder().e() - 16.0D);
		 * entity.setPositionRotation(d0, entity.locY, d1, entity.yaw, entity.pitch); if
		 * (entity.isAlive()) { worldserver.entityJoinedWorld(entity, false); } } else {
		 * BlockPosition blockposition;
		 * 
		 * if (i == 1) { // use default NORMAL world spawn instead of target
		 * worldserver1 = this.server.worlds.get(0); blockposition =
		 * worldserver1.getSpawn(); } else { blockposition =
		 * worldserver1.getDimensionSpawn(); }
		 * 
		 * d0 = (double) blockposition.getX(); entity.locY = (double)
		 * blockposition.getY(); d1 = (double) blockposition.getZ();
		 * entity.setPositionRotation(d0, entity.locY, d1, 90.0F, 0.0F); if
		 * (entity.isAlive()) { worldserver.entityJoinedWorld(entity, false); } }
		 */

		worldserver.methodProfiler.b();
		if (i != 1) {
			worldserver.methodProfiler.a("placing");
			/*
			 * d0 = (double) MathHelper.clamp((int) d0, -29999872, 29999872); d1 = (double)
			 * MathHelper.clamp((int) d1, -29999872, 29999872);
			 */
			if (entity.isAlive()) {
				// entity.setPositionRotation(d0, entity.locY, d1, entity.yaw, entity.pitch);
				// worldserver1.getTravelAgent().a(entity, f);
				if (portal) {
					Vector velocity = entity.getBukkitEntity().getVelocity();
					worldserver1.getTravelAgent().adjustExit(entity, exit, velocity);
					entity.setPositionRotation(exit.getX(), exit.getY(), exit.getZ(), exit.getYaw(), exit.getPitch());
					if (entity.motX != velocity.getX() || entity.motY != velocity.getY()
							|| entity.motZ != velocity.getZ()) {
						entity.getBukkitEntity().setVelocity(velocity);
					}
				}
				worldserver1.addEntity(entity);
				worldserver1.entityJoinedWorld(entity, false);
			}

			worldserver.methodProfiler.b();
		}

		entity.spawnIn(worldserver1);
		// CraftBukkit end
	}

	public void tick() {
		if (++this.u > 600) {
			this.sendAll(new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.UPDATE_LATENCY,
					this.players));
			this.u = 0;
		}

	}

	public void sendAll(Packet packet) {
		for (int i = 0; i < this.players.size(); ++i) {
			this.players.get(i).playerConnection.sendPacket(packet);
		}

	}

	// CraftBukkit start - add a world/entity limited version
	public void sendAll(Packet packet, EntityHuman entityhuman) {
		for (int i = 0; i < this.players.size(); ++i) {
			EntityPlayer entityplayer = this.players.get(i);
			if (entityhuman != null && entityhuman instanceof EntityPlayer
					&& !entityplayer.getBukkitEntity().canSee(((EntityPlayer) entityhuman).getBukkitEntity())) {
				continue;
			}
			this.players.get(i).playerConnection.sendPacket(packet);
		}
	}

	public void sendAll(Packet packet, World world) {
		for (int i = 0; i < world.players.size(); ++i) {
			((EntityPlayer) world.players.get(i)).playerConnection.sendPacket(packet);
		}

	}
	// CraftBukkit end

	public void a(Packet packet, int i) {
		for (int j = 0; j < this.players.size(); ++j) {
			EntityPlayer entityplayer = this.players.get(j);

			if (entityplayer.dimension == i) {
				entityplayer.playerConnection.sendPacket(packet);
			}
		}

	}

	public void a(EntityHuman entityhuman, IChatBaseComponent ichatbasecomponent) {
		ScoreboardTeamBase scoreboardteambase = entityhuman.getScoreboardTeam();

		if (scoreboardteambase != null) {
			Collection collection = scoreboardteambase.getPlayerNameSet();
			Iterator iterator = collection.iterator();

			while (iterator.hasNext()) {
				String s = (String) iterator.next();
				EntityPlayer entityplayer = this.getPlayer(s);

				if (entityplayer != null && entityplayer != entityhuman) {
					entityplayer.sendMessage(ichatbasecomponent);
				}
			}

		}
	}

	public void b(EntityHuman entityhuman, IChatBaseComponent ichatbasecomponent) {
		ScoreboardTeamBase scoreboardteambase = entityhuman.getScoreboardTeam();

		if (scoreboardteambase == null) {
			this.sendMessage(ichatbasecomponent);
		} else {
			for (int i = 0; i < this.players.size(); ++i) {
				EntityPlayer entityplayer = this.players.get(i);

				if (entityplayer.getScoreboardTeam() != scoreboardteambase) {
					entityplayer.sendMessage(ichatbasecomponent);
				}
			}

		}
	}

	public String b(boolean flag) {
		StringBuilder s = new StringBuilder();
		ArrayList arraylist = Lists.newArrayList(this.players);

		for (int i = 0; i < arraylist.size(); ++i) {
			if (i > 0) {
				s.append(", ");
			}

			s.append(((EntityPlayer) arraylist.get(i)).getName());
			if (flag) {
				s.append(" (").append(((EntityPlayer) arraylist.get(i)).getUniqueID().toString()).append(")");
			}
		}

		return s.toString();
	}

	public String[] f() {
		String[] astring = new String[this.players.size()];

		for (int i = 0; i < this.players.size(); ++i) {
			astring[i] = this.players.get(i).getName();
		}

		return astring;
	}

	public GameProfile[] g() {
		GameProfile[] agameprofile = new GameProfile[this.players.size()];

		for (int i = 0; i < this.players.size(); ++i) {
			agameprofile[i] = this.players.get(i).getProfile();
		}

		return agameprofile;
	}

	public GameProfileBanList getProfileBans() {
		return this.k;
	}

	public IpBanList getIPBans() {
		return this.l;
	}

	public void addOp(GameProfile gameprofile) {
		this.operators.add(new OpListEntry(gameprofile, this.server.p(), this.operators.b(gameprofile)));
		// [Nacho-0037] Add toggle for "Faster Operator"
		if (WindSpigotConfig.useFastOperators) {
			this.fastOperator.add(gameprofile.getId());
		}
		// CraftBukkit start
		Player player = server.server.getPlayer(gameprofile.getId());
		if (player != null) {
			player.recalculatePermissions();
		}
		// CraftBukkit end
	}

	public void removeOp(GameProfile gameprofile) {
		this.operators.remove(gameprofile);
		// [Nacho-0037] Add toggle for "Faster Operator"
		if (WindSpigotConfig.useFastOperators) {
			this.fastOperator.remove(gameprofile.getId());
		}

		// CraftBukkit start
		Player player = server.server.getPlayer(gameprofile.getId());
		if (player != null) {
			player.recalculatePermissions();
		}
		// CraftBukkit end
	}

	public boolean isWhitelisted(GameProfile gameprofile) {
		// [Nacho-0037] Add toggle for "Faster Operator"
		return !this.hasWhitelist || (WindSpigotConfig.useFastOperators ? this.fastOperator.contains(gameprofile.getId())
				: this.operators.d(gameprofile)) || this.whitelist.d(gameprofile);
	}

	public boolean isOp(GameProfile gameprofile) {
		// [Nacho-0037] Add toggle for "Faster Operator"
		return (WindSpigotConfig.useFastOperators ? this.fastOperator.contains(gameprofile.getId())
				: this.operators.d(gameprofile))
				|| this.server.T() && this.server.worlds.get(0).getWorldData().v()
						&& this.server.S().equalsIgnoreCase(gameprofile.getName())
				|| this.t; // CraftBukkit
	}

	public EntityPlayer getPlayer(String s) {
		return this.playerMap.get(s); // Spigot
	}

	public void sendPacketNearby(double d0, double d1, double d2, double d3, int i, Packet packet) {
		this.sendPacketNearby((EntityHuman) null, d0, d1, d2, d3, i, packet);
	}

	public void sendPacketNearby(EntityHuman entityhuman, double d0, double d1, double d2, double d3, int i,
			Packet<?> packet) {
//        for (int j = 0; j < this.players.size(); ++j) {
//            EntityPlayer entityplayer = (EntityPlayer) this.players.get(j);
		sendPacketNearby(entityhuman, d0, d1, d2, d3, i, null, packet); // Retained for compatibility
	}

	public void sendPacketNearby(EntityHuman entityhuman, double d0, double d1, double d2, double d3, WorldServer world,
			Packet<?> packet) {
		sendPacketNearby(entityhuman, d0, d1, d2, d3, world.dimension, world, packet);
	}

	public void sendPacketNearby(EntityHuman entityhuman, double d0, double d1, double d2, double d3, int i,
			WorldServer world, Packet<?> packet) {
		if (world == null && entityhuman != null && entityhuman.world instanceof WorldServer) {
			world = (WorldServer) entityhuman.world;
		}

		double squared = d3 * d3;
		List<? extends EntityHuman> players1 = world == null ? players : world.players;
		for (int j = 0; j < players1.size(); ++j) {
			EntityHuman entity = players1.get(j);
			if (!(entity instanceof EntityPlayer)) {
				continue;
			}
			EntityPlayer entityplayer = (EntityPlayer) players1.get(j);
			// CraftBukkit start - Test if player receiving packet can see the source of the
			// packet
			if (entityhuman != null && entityhuman instanceof EntityPlayer
					&& !entityplayer.getBukkitEntity().canSee(((EntityPlayer) entityhuman).getBukkitEntity())) {
				continue;
			}
			// CraftBukkit end

			if (entityplayer != entityhuman && (world != null || entityplayer.dimension == i)) { // Paper
				double d4 = d0 - entityplayer.locX;
				double d5 = d1 - entityplayer.locY;
				double d6 = d2 - entityplayer.locZ;

				if (d4 * d4 + d5 * d5 + d6 * d6 < squared) {
					entityplayer.playerConnection.sendPacket(packet);
				}
			}
		}

	}

	public void sendPacketNearbyIncludingSelf(EntityHuman entityhuman, double d0, double d1, double d2, double d3,
			int i, Packet packet) { // Saith
		for (int j = 0; j < this.players.size(); ++j) {
			EntityPlayer entityplayer = this.players.get(j);
			if (entityhuman != null && !entityplayer.getBukkitEntity().canSee(entityhuman.getBukkitEntity())) {
				continue;
			}

			if (entityplayer.dimension == i) {
				double d4 = d0 - entityplayer.locX;
				double d5 = d1 - entityplayer.locY;
				double d6 = d2 - entityplayer.locZ;

				if (d4 * d4 + d5 * d5 + d6 * d6 < d3 * d3) {
					entityplayer.playerConnection.sendPacket(packet);
				}
			}
		}
	}

	public void savePlayers() {
		for (int i = 0; i < this.players.size(); ++i) {
			this.savePlayerFile(this.players.get(i));
		}

	}

	public void addWhitelist(GameProfile gameprofile) {
		this.whitelist.add(new WhiteListEntry(gameprofile));
	}

	public void removeWhitelist(GameProfile gameprofile) {
		this.whitelist.remove(gameprofile);
	}

	public WhiteList getWhitelist() {
		return this.whitelist;
	}

	public String[] getWhitelisted() {
		return this.whitelist.getEntries();
	}

	public OpList getOPs() {
		return this.operators;
	}

	public String[] n() {
		return this.operators.getEntries();
	}

	public void reloadWhitelist() {
	}

	public void b(EntityPlayer entityplayer, WorldServer worldserver) {
		WorldBorder worldborder = entityplayer.world.getWorldBorder(); // CraftBukkit

		entityplayer.playerConnection.sendPacket(
				new PacketPlayOutWorldBorder(worldborder, PacketPlayOutWorldBorder.EnumWorldBorderAction.INITIALIZE));
		entityplayer.playerConnection.sendPacket(new PacketPlayOutUpdateTime(worldserver.getTime(),
				worldserver.getDayTime(), worldserver.getGameRules().getBoolean("doDaylightCycle")));
		if (worldserver.S()) {
			// CraftBukkit start - handle player weather
			// entityplayer.playerConnection.sendPacket(new PacketPlayOutGameStateChange(1,
			// 0.0F));
			// entityplayer.playerConnection.sendPacket(new PacketPlayOutGameStateChange(7,
			// worldserver.j(1.0F)));
			// entityplayer.playerConnection.sendPacket(new PacketPlayOutGameStateChange(8,
			// worldserver.h(1.0F)));
			entityplayer.setPlayerWeather(org.bukkit.WeatherType.DOWNFALL, false);
			entityplayer.updateWeather(-worldserver.p, worldserver.p, -worldserver.r, worldserver.r);
			// CraftBukkit end
		}

	}

	public void updateClient(EntityPlayer entityplayer) {
		entityplayer.updateInventory(entityplayer.defaultContainer);
		// entityplayer.triggerHealthUpdate();
		entityplayer.getBukkitEntity().updateScaledHealth(); // CraftBukkit - Update scaled health on respawn and
																// worldchange
		entityplayer.playerConnection.sendPacket(new PacketPlayOutHeldItemSlot(entityplayer.inventory.itemInHandIndex));
	}

	public int getPlayerCount() {
		return this.players.size();
	}

	public int getMaxPlayers() {
		return this.maxPlayers;
	}

	public void setMaxPlayers(int maxPlayerCount) {
		this.maxPlayers = maxPlayerCount;
	}

	public String[] getSeenPlayers() {
		return this.server.worlds.get(0).getDataManager().getPlayerFileData().getSeenPlayers(); // CraftBukkit
	}

	public boolean getHasWhitelist() {
		return this.hasWhitelist;
	}

	public void setHasWhitelist(boolean flag) {
		this.hasWhitelist = flag;
	}

	public List<EntityPlayer> b(String s) {
		ArrayList arraylist = Lists.newArrayList();
		Iterator iterator = this.players.iterator();

		while (iterator.hasNext()) {
			EntityPlayer entityplayer = (EntityPlayer) iterator.next();

			if (entityplayer.w().equals(s)) {
				arraylist.add(entityplayer);
			}
		}

		return arraylist;
	}

	public int s() {
		return this.r;
	}

	public MinecraftServer getServer() {
		return this.server;
	}

	public NBTTagCompound t() {
		return null;
	}

	private void a(EntityPlayer entityplayer, EntityPlayer entityplayer1, World world) {
		if (entityplayer1 != null) {
			entityplayer.playerInteractManager.setGameMode(entityplayer1.playerInteractManager.getGameMode());
		} else if (this.s != null) {
			entityplayer.playerInteractManager.setGameMode(this.s);
		}

		entityplayer.playerInteractManager.b(world.getWorldData().getGameType());
	}

	public void u() {
		for (int i = 0; i < this.players.size(); ++i) {
			this.players.get(i).playerConnection.disconnect(this.server.server.getShutdownMessage()); // CraftBukkit
																										// -
																										// add
																										// custom
																										// shutdown
																										// message
		}

	}

	// CraftBukkit start
	public void sendMessage(IChatBaseComponent[] iChatBaseComponents) {
		for (IChatBaseComponent component : iChatBaseComponents) {
			sendMessage(component, true);
		}
	}
	// CraftBukkit end

	public void sendMessage(IChatBaseComponent ichatbasecomponent, boolean flag) {
		this.server.sendMessage(ichatbasecomponent);
		int i = flag ? 1 : 0;

		// CraftBukkit start - we run this through our processor first so we can get web
		// links etc
		this.sendAll(new PacketPlayOutChat(CraftChatMessage.fixComponent(ichatbasecomponent), (byte) i));
		// CraftBukkit end
	}

	public void sendMessage(IChatBaseComponent ichatbasecomponent) {
		this.sendMessage(ichatbasecomponent, true);
	}

	public ServerStatisticManager a(EntityHuman entityhuman) {
		UUID uuid = entityhuman.getUniqueID();
		ServerStatisticManager serverstatisticmanager = uuid == null ? null : (ServerStatisticManager) this.o.get(uuid);

		if (serverstatisticmanager == null) {
			File file = new File(this.server.getWorldServer(0).getDataManager().getDirectory(), "stats");
			File file1 = new File(file, FastUUID.toString(uuid) + ".json");

			if (!file1.exists()) {
				File file2 = new File(file, entityhuman.getName() + ".json");

				if (file2.exists() && file2.isFile()) {
					file2.renameTo(file1);
				}
			}

			serverstatisticmanager = new ServerStatisticManager(this.server, file1);
			serverstatisticmanager.a();
			this.o.put(uuid, serverstatisticmanager);
		}

		return serverstatisticmanager;
	}

	public void a(int i) {
		this.r = i;
		if (this.server.worldServer != null) {
			WorldServer[] aworldserver = this.server.worldServer;
			int j = aworldserver.length;

			// CraftBukkit start
			for (int k = 0; k < server.worlds.size(); ++k) {
				WorldServer worldserver = server.worlds.get(0);
				// CraftBukkit end

				if (worldserver != null) {
					worldserver.getPlayerChunkMap().a(i);
				}
			}

		}
	}

	public List<EntityPlayer> v() {
		return this.players;
	}

	public EntityPlayer a(UUID uuid) {
		return this.uuidMap.get(uuid);
	}

	public boolean f(GameProfile gameprofile) {
		return false;
	}
}
