package net.minecraft.server.management;
import java.util.ArrayList;
import java.util.List;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.util.LongHashMap;
import net.minecraft.world.ChunkCoordIntPair;
import net.minecraft.world.WorldProvider;
import net.minecraft.world.WorldServer;
public class PlayerManager
{
private final WorldServer theWorldServer;
/** players in the current instance */
private final List players = new ArrayList();
/**
* A map of chunk position (two ints concatenated into a long) to PlayerInstance
*/
private final LongHashMap playerInstances = new LongHashMap();
/**
* contains a PlayerInstance for every chunk they can see. the "player instance" cotains a list of all players who
* can also that chunk
*/
private final List chunkWatcherWithPlayers = new ArrayList();
/**
* Number of chunks the server sends to the client. Valid 3<=x<=15. In server.properties.
*/
private final int playerViewRadius;
/** x, z direction vectors: east, south, west, north */
private final int[][] xzDirectionsConst = new int[][] {{1, 0}, {0, 1}, { -1, 0}, {0, -1}};
public PlayerManager(WorldServer par1WorldServer, int par2)
{
if (par2 > 15)
{
throw new IllegalArgumentException("Too big view radius!");
}
else if (par2 < 3)
{
throw new IllegalArgumentException("Too small view radius!");
}
else
{
this.playerViewRadius = par2;
this.theWorldServer = par1WorldServer;
}
}
public WorldServer getWorldServer()
{
return this.theWorldServer;
}
/**
* updates all the player instances that need to be updated
*/
public void updatePlayerInstances()
{
for (int i = 0; i < this.chunkWatcherWithPlayers.size(); ++i)
{
((PlayerInstance)this.chunkWatcherWithPlayers.get(i)).sendChunkUpdate();
}
this.chunkWatcherWithPlayers.clear();
if (this.players.isEmpty())
{
WorldProvider worldprovider = this.theWorldServer.provider;
if (!worldprovider.canRespawnHere())
{
this.theWorldServer.theChunkProviderServer.unloadAllChunks();
}
}
}
public PlayerInstance getOrCreateChunkWatcher(int par1, int par2, boolean par3)
{
long k = (long)par1 + 2147483647L | (long)par2 + 2147483647L << 32;
PlayerInstance playerinstance = (PlayerInstance)this.playerInstances.getValueByKey(k);
if (playerinstance == null && par3)
{
playerinstance = new PlayerInstance(this, par1, par2);
this.playerInstances.add(k, playerinstance);
}
return playerinstance;
}
/**
* the "PlayerInstance"/ chunkWatcher will send this chunk to all players who are in line of sight
*/
public void flagChunkForUpdate(int par1, int par2, int par3)
{
int l = par1 >> 4;
int i1 = par3 >> 4;
PlayerInstance playerinstance = this.getOrCreateChunkWatcher(l, i1, false);
if (playerinstance != null)
{
playerinstance.flagChunkForUpdate(par1 & 15, par2, par3 & 15);
}
}
/**
* Adds an EntityPlayerMP to the PlayerManager.
*/
public void addPlayer(EntityPlayerMP par1EntityPlayerMP)
{
int i = (int)par1EntityPlayerMP.posX >> 4;
int j = (int)par1EntityPlayerMP.posZ >> 4;
par1EntityPlayerMP.managedPosX = par1EntityPlayerMP.posX;
par1EntityPlayerMP.managedPosZ = par1EntityPlayerMP.posZ;
for (int k = i - this.playerViewRadius; k <= i + this.playerViewRadius; ++k)
{
for (int l = j - this.playerViewRadius; l <= j + this.playerViewRadius; ++l)
{
this.getOrCreateChunkWatcher(k, l, true).addPlayerToChunkWatchingList(par1EntityPlayerMP);
}
}
this.players.add(par1EntityPlayerMP);
this.filterChunkLoadQueue(par1EntityPlayerMP);
}
/**
* Removes all chunks from the given player's chunk load queue that are not in viewing range of the player.
*/
public void filterChunkLoadQueue(EntityPlayerMP par1EntityPlayerMP)
{
ArrayList arraylist = new ArrayList(par1EntityPlayerMP.loadedChunks);
int i = 0;
int j = this.playerViewRadius;
int k = (int)par1EntityPlayerMP.posX >> 4;
int l = (int)par1EntityPlayerMP.posZ >> 4;
int i1 = 0;
int j1 = 0;
ChunkCoordIntPair chunkcoordintpair = PlayerInstance.getChunkLocation(this.getOrCreateChunkWatcher(k, l, true));
par1EntityPlayerMP.loadedChunks.clear();
if (arraylist.contains(chunkcoordintpair))
{
par1EntityPlayerMP.loadedChunks.add(chunkcoordintpair);
}
int k1;
for (k1 = 1; k1 <= j * 2; ++k1)
{
for (int l1 = 0; l1 < 2; ++l1)
{
int[] aint = this.xzDirectionsConst[i++ % 4];
for (int i2 = 0; i2 < k1; ++i2)
{
i1 += aint[0];
j1 += aint[1];
chunkcoordintpair = PlayerInstance.getChunkLocation(this.getOrCreateChunkWatcher(k + i1, l + j1, true));
if (arraylist.contains(chunkcoordintpair))
{
par1EntityPlayerMP.loadedChunks.add(chunkcoordintpair);
}
}
}
}
i %= 4;
for (k1 = 0; k1 < j * 2; ++k1)
{
i1 += this.xzDirectionsConst[i][0];
j1 += this.xzDirectionsConst[i][1];
chunkcoordintpair = PlayerInstance.getChunkLocation(this.getOrCreateChunkWatcher(k + i1, l + j1, true));
if (arraylist.contains(chunkcoordintpair))
{
par1EntityPlayerMP.loadedChunks.add(chunkcoordintpair);
}
}
}
/**
* Removes an EntityPlayerMP from the PlayerManager.
*/
public void removePlayer(EntityPlayerMP par1EntityPlayerMP)
{
int i = (int)par1EntityPlayerMP.managedPosX >> 4;
int j = (int)par1EntityPlayerMP.managedPosZ >> 4;
for (int k = i - this.playerViewRadius; k <= i + this.playerViewRadius; ++k)
{
for (int l = j - this.playerViewRadius; l <= j + this.playerViewRadius; ++l)
{
PlayerInstance playerinstance = this.getOrCreateChunkWatcher(k, l, false);
if (playerinstance != null)
{
playerinstance.sendThisChunkToPlayer(par1EntityPlayerMP);
}
}
}
this.players.remove(par1EntityPlayerMP);
}
private boolean func_72684_a(int par1, int par2, int par3, int par4, int par5)
{
int j1 = par1 - par3;
int k1 = par2 - par4;
return j1 >= -par5 && j1 <= par5 ? k1 >= -par5 && k1 <= par5 : false;
}
/**
* update chunks around a player being moved by server logic (e.g. cart, boat)
*/
public void updateMountedMovingPlayer(EntityPlayerMP par1EntityPlayerMP)
{
int i = (int)par1EntityPlayerMP.posX >> 4;
int j = (int)par1EntityPlayerMP.posZ >> 4;
double d0 = par1EntityPlayerMP.managedPosX - par1EntityPlayerMP.posX;
double d1 = par1EntityPlayerMP.managedPosZ - par1EntityPlayerMP.posZ;
double d2 = d0 * d0 + d1 * d1;
if (d2 >= 64.0D)
{
int k = (int)par1EntityPlayerMP.managedPosX >> 4;
int l = (int)par1EntityPlayerMP.managedPosZ >> 4;
int i1 = this.playerViewRadius;
int j1 = i - k;
int k1 = j - l;
if (j1 != 0 || k1 != 0)
{
for (int l1 = i - i1; l1 <= i + i1; ++l1)
{
for (int i2 = j - i1; i2 <= j + i1; ++i2)
{
if (!this.func_72684_a(l1, i2, k, l, i1))
{
this.getOrCreateChunkWatcher(l1, i2, true).addPlayerToChunkWatchingList(par1EntityPlayerMP);
}
if (!this.func_72684_a(l1 - j1, i2 - k1, i, j, i1))
{
PlayerInstance playerinstance = this.getOrCreateChunkWatcher(l1 - j1, i2 - k1, false);
if (playerinstance != null)
{
playerinstance.sendThisChunkToPlayer(par1EntityPlayerMP);
}
}
}
}
this.filterChunkLoadQueue(par1EntityPlayerMP);
par1EntityPlayerMP.managedPosX = par1EntityPlayerMP.posX;
par1EntityPlayerMP.managedPosZ = par1EntityPlayerMP.posZ;
}
}
}
public boolean isPlayerWatchingChunk(EntityPlayerMP par1EntityPlayerMP, int par2, int par3)
{
PlayerInstance playerinstance = this.getOrCreateChunkWatcher(par2, par3, false);
return playerinstance == null ? false : PlayerInstance.getPlayersInChunk(playerinstance).contains(par1EntityPlayerMP) && !par1EntityPlayerMP.loadedChunks.contains(PlayerInstance.getChunkLocation(playerinstance));
}
/**
* Get the furthest viewable block given player's view distance
*/
public static int getFurthestViewableBlock(int par0)
{
return par0 * 16 - 16;
}
static WorldServer getWorldServer(PlayerManager par0PlayerManager)
{
return par0PlayerManager.theWorldServer;
}
static LongHashMap getChunkWatchers(PlayerManager par0PlayerManager)
{
return par0PlayerManager.playerInstances;
}
static List getChunkWatchersWithPlayers(PlayerManager par0PlayerManager)
{
return par0PlayerManager.chunkWatcherWithPlayers;
}
}