package net.minecraft.server.management; import com.google.common.collect.Lists; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.network.Packet; import net.minecraft.network.play.server.S21PacketChunkData; import net.minecraft.network.play.server.S22PacketMultiBlockChange; import net.minecraft.network.play.server.S23PacketBlockChange; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.BlockPos; import net.minecraft.util.LongHashMap; import net.minecraft.util.MathHelper; import net.minecraft.world.ChunkCoordIntPair; import net.minecraft.world.WorldProvider; import net.minecraft.world.WorldServer; import net.minecraft.world.chunk.Chunk; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; public class PlayerManager { private static final Logger field_152627_a = LogManager.getLogger(); private final WorldServer theWorldServer; /** players in the current instance */ private final List players = Lists.newArrayList(); /** the hash of all playerInstances created */ private final LongHashMap playerInstances = new LongHashMap(); /** the playerInstances(chunks) that need to be updated */ private final List playerInstancesToUpdate = Lists.newArrayList(); /** This field is using when chunk should be processed (every 8000 ticks) */ private final List playerInstanceList = Lists.newArrayList(); /** * Number of chunks the server sends to the client. Valid 3<=x<=15. In server.properties. */ private int playerViewRadius; /** time what is using to check if InhabitedTime should be calculated */ private long previousTotalWorldTime; /** x, z direction vectors: east, south, west, north */ private final int[][] xzDirectionsConst = new int[][] {{1, 0}, {0, 1}, { -1, 0}, {0, -1}}; private static final String __OBFID = "CL_00001434"; public PlayerManager(WorldServer p_i1176_1_) { this.theWorldServer = p_i1176_1_; this.func_152622_a(p_i1176_1_.func_73046_m().getConfigurationManager().getViewDistance()); } /** * Returns the MinecraftServer associated with the PlayerManager. */ public WorldServer getMinecraftServer() { return this.theWorldServer; } /** * updates all the player instances that need to be updated */ public void updatePlayerInstances() { long var1 = this.theWorldServer.getTotalWorldTime(); int var3; PlayerManager.PlayerInstance var4; if (var1 - this.previousTotalWorldTime > 8000L) { this.previousTotalWorldTime = var1; for (var3 = 0; var3 < this.playerInstanceList.size(); ++var3) { var4 = (PlayerManager.PlayerInstance)this.playerInstanceList.get(var3); var4.onUpdate(); var4.processChunk(); } } else { for (var3 = 0; var3 < this.playerInstancesToUpdate.size(); ++var3) { var4 = (PlayerManager.PlayerInstance)this.playerInstancesToUpdate.get(var3); var4.onUpdate(); } } this.playerInstancesToUpdate.clear(); if (this.players.isEmpty()) { WorldProvider var5 = this.theWorldServer.provider; if (!var5.canRespawnHere()) { this.theWorldServer.theChunkProviderServer.unloadAllChunks(); } } } public boolean func_152621_a(int p_152621_1_, int p_152621_2_) { long var3 = (long)p_152621_1_ + 2147483647L | (long)p_152621_2_ + 2147483647L << 32; return this.playerInstances.getValueByKey(var3) != null; } /** * passi n the chunk x and y and a flag as to whether or not the instance should be made if it doesnt exist */ private PlayerManager.PlayerInstance getPlayerInstance(int p_72690_1_, int p_72690_2_, boolean p_72690_3_) { long var4 = (long)p_72690_1_ + 2147483647L | (long)p_72690_2_ + 2147483647L << 32; PlayerManager.PlayerInstance var6 = (PlayerManager.PlayerInstance)this.playerInstances.getValueByKey(var4); if (var6 == null && p_72690_3_) { var6 = new PlayerManager.PlayerInstance(p_72690_1_, p_72690_2_); this.playerInstances.add(var4, var6); this.playerInstanceList.add(var6); } return var6; } public void func_180244_a(BlockPos p_180244_1_) { int var2 = p_180244_1_.getX() >> 4; int var3 = p_180244_1_.getZ() >> 4; PlayerManager.PlayerInstance var4 = this.getPlayerInstance(var2, var3, false); if (var4 != null) { var4.flagChunkForUpdate(p_180244_1_.getX() & 15, p_180244_1_.getY(), p_180244_1_.getZ() & 15); } } /** * Adds an EntityPlayerMP to the PlayerManager and to all player instances within player visibility */ public void addPlayer(EntityPlayerMP p_72683_1_) { int var2 = (int)p_72683_1_.posX >> 4; int var3 = (int)p_72683_1_.posZ >> 4; p_72683_1_.managedPosX = p_72683_1_.posX; p_72683_1_.managedPosZ = p_72683_1_.posZ; for (int var4 = var2 - this.playerViewRadius; var4 <= var2 + this.playerViewRadius; ++var4) { for (int var5 = var3 - this.playerViewRadius; var5 <= var3 + this.playerViewRadius; ++var5) { this.getPlayerInstance(var4, var5, true).addPlayer(p_72683_1_); } } this.players.add(p_72683_1_); this.filterChunkLoadQueue(p_72683_1_); } /** * Removes all chunks from the given player's chunk load queue that are not in viewing range of the player. */ public void filterChunkLoadQueue(EntityPlayerMP p_72691_1_) { ArrayList var2 = Lists.newArrayList(p_72691_1_.loadedChunks); int var3 = 0; int var4 = this.playerViewRadius; int var5 = (int)p_72691_1_.posX >> 4; int var6 = (int)p_72691_1_.posZ >> 4; int var7 = 0; int var8 = 0; ChunkCoordIntPair var9 = this.getPlayerInstance(var5, var6, true).currentChunk; p_72691_1_.loadedChunks.clear(); if (var2.contains(var9)) { p_72691_1_.loadedChunks.add(var9); } int var10; for (var10 = 1; var10 <= var4 * 2; ++var10) { for (int var11 = 0; var11 < 2; ++var11) { int[] var12 = this.xzDirectionsConst[var3++ % 4]; for (int var13 = 0; var13 < var10; ++var13) { var7 += var12[0]; var8 += var12[1]; var9 = this.getPlayerInstance(var5 + var7, var6 + var8, true).currentChunk; if (var2.contains(var9)) { p_72691_1_.loadedChunks.add(var9); } } } } var3 %= 4; for (var10 = 0; var10 < var4 * 2; ++var10) { var7 += this.xzDirectionsConst[var3][0]; var8 += this.xzDirectionsConst[var3][1]; var9 = this.getPlayerInstance(var5 + var7, var6 + var8, true).currentChunk; if (var2.contains(var9)) { p_72691_1_.loadedChunks.add(var9); } } } /** * Removes an EntityPlayerMP from the PlayerManager. */ public void removePlayer(EntityPlayerMP p_72695_1_) { int var2 = (int)p_72695_1_.managedPosX >> 4; int var3 = (int)p_72695_1_.managedPosZ >> 4; for (int var4 = var2 - this.playerViewRadius; var4 <= var2 + this.playerViewRadius; ++var4) { for (int var5 = var3 - this.playerViewRadius; var5 <= var3 + this.playerViewRadius; ++var5) { PlayerManager.PlayerInstance var6 = this.getPlayerInstance(var4, var5, false); if (var6 != null) { var6.removePlayer(p_72695_1_); } } } this.players.remove(p_72695_1_); } /** * Determine if two rectangles centered at the given points overlap for the provided radius. Arguments: x1, z1, x2, * z2, radius. */ private boolean overlaps(int p_72684_1_, int p_72684_2_, int p_72684_3_, int p_72684_4_, int p_72684_5_) { int var6 = p_72684_1_ - p_72684_3_; int var7 = p_72684_2_ - p_72684_4_; return var6 >= -p_72684_5_ && var6 <= p_72684_5_ ? var7 >= -p_72684_5_ && var7 <= p_72684_5_ : false; } /** * update chunks around a player being moved by server logic (e.g. cart, boat) */ public void updateMountedMovingPlayer(EntityPlayerMP p_72685_1_) { int var2 = (int)p_72685_1_.posX >> 4; int var3 = (int)p_72685_1_.posZ >> 4; double var4 = p_72685_1_.managedPosX - p_72685_1_.posX; double var6 = p_72685_1_.managedPosZ - p_72685_1_.posZ; double var8 = var4 * var4 + var6 * var6; if (var8 >= 64.0D) { int var10 = (int)p_72685_1_.managedPosX >> 4; int var11 = (int)p_72685_1_.managedPosZ >> 4; int var12 = this.playerViewRadius; int var13 = var2 - var10; int var14 = var3 - var11; if (var13 != 0 || var14 != 0) { for (int var15 = var2 - var12; var15 <= var2 + var12; ++var15) { for (int var16 = var3 - var12; var16 <= var3 + var12; ++var16) { if (!this.overlaps(var15, var16, var10, var11, var12)) { this.getPlayerInstance(var15, var16, true).addPlayer(p_72685_1_); } if (!this.overlaps(var15 - var13, var16 - var14, var2, var3, var12)) { PlayerManager.PlayerInstance var17 = this.getPlayerInstance(var15 - var13, var16 - var14, false); if (var17 != null) { var17.removePlayer(p_72685_1_); } } } } this.filterChunkLoadQueue(p_72685_1_); p_72685_1_.managedPosX = p_72685_1_.posX; p_72685_1_.managedPosZ = p_72685_1_.posZ; } } } public boolean isPlayerWatchingChunk(EntityPlayerMP p_72694_1_, int p_72694_2_, int p_72694_3_) { PlayerManager.PlayerInstance var4 = this.getPlayerInstance(p_72694_2_, p_72694_3_, false); return var4 != null && var4.playersWatchingChunk.contains(p_72694_1_) && !p_72694_1_.loadedChunks.contains(var4.currentChunk); } public void func_152622_a(int p_152622_1_) { p_152622_1_ = MathHelper.clamp_int(p_152622_1_, 3, 32); if (p_152622_1_ != this.playerViewRadius) { int var2 = p_152622_1_ - this.playerViewRadius; ArrayList var3 = Lists.newArrayList(this.players); Iterator var4 = var3.iterator(); while (var4.hasNext()) { EntityPlayerMP var5 = (EntityPlayerMP)var4.next(); int var6 = (int)var5.posX >> 4; int var7 = (int)var5.posZ >> 4; int var8; int var9; if (var2 > 0) { for (var8 = var6 - p_152622_1_; var8 <= var6 + p_152622_1_; ++var8) { for (var9 = var7 - p_152622_1_; var9 <= var7 + p_152622_1_; ++var9) { PlayerManager.PlayerInstance var10 = this.getPlayerInstance(var8, var9, true); if (!var10.playersWatchingChunk.contains(var5)) { var10.addPlayer(var5); } } } } else { for (var8 = var6 - this.playerViewRadius; var8 <= var6 + this.playerViewRadius; ++var8) { for (var9 = var7 - this.playerViewRadius; var9 <= var7 + this.playerViewRadius; ++var9) { if (!this.overlaps(var8, var9, var6, var7, p_152622_1_)) { this.getPlayerInstance(var8, var9, true).removePlayer(var5); } } } } } this.playerViewRadius = p_152622_1_; } } /** * Get the furthest viewable block given player's view distance */ public static int getFurthestViewableBlock(int p_72686_0_) { return p_72686_0_ * 16 - 16; } class PlayerInstance { private final List playersWatchingChunk = Lists.newArrayList(); private final ChunkCoordIntPair currentChunk; private short[] locationOfBlockChange = new short[64]; private int numBlocksToUpdate; private int flagsYAreasToUpdate; private long previousWorldTime; private static final String __OBFID = "CL_00001435"; public PlayerInstance(int p_i1518_2_, int p_i1518_3_) { this.currentChunk = new ChunkCoordIntPair(p_i1518_2_, p_i1518_3_); PlayerManager.this.getMinecraftServer().theChunkProviderServer.loadChunk(p_i1518_2_, p_i1518_3_); } public void addPlayer(EntityPlayerMP p_73255_1_) { if (this.playersWatchingChunk.contains(p_73255_1_)) { PlayerManager.field_152627_a.debug("Failed to add player. {} already is in chunk {}, {}", new Object[] {p_73255_1_, Integer.valueOf(this.currentChunk.chunkXPos), Integer.valueOf(this.currentChunk.chunkZPos)}); } else { if (this.playersWatchingChunk.isEmpty()) { this.previousWorldTime = PlayerManager.this.theWorldServer.getTotalWorldTime(); } this.playersWatchingChunk.add(p_73255_1_); p_73255_1_.loadedChunks.add(this.currentChunk); } } public void removePlayer(EntityPlayerMP p_73252_1_) { if (this.playersWatchingChunk.contains(p_73252_1_)) { Chunk var2 = PlayerManager.this.theWorldServer.getChunkFromChunkCoords(this.currentChunk.chunkXPos, this.currentChunk.chunkZPos); if (var2.isPopulated()) { p_73252_1_.playerNetServerHandler.sendPacket(new S21PacketChunkData(var2, true, 0)); } this.playersWatchingChunk.remove(p_73252_1_); p_73252_1_.loadedChunks.remove(this.currentChunk); if (this.playersWatchingChunk.isEmpty()) { long var3 = (long)this.currentChunk.chunkXPos + 2147483647L | (long)this.currentChunk.chunkZPos + 2147483647L << 32; this.increaseInhabitedTime(var2); PlayerManager.this.playerInstances.remove(var3); PlayerManager.this.playerInstanceList.remove(this); if (this.numBlocksToUpdate > 0) { PlayerManager.this.playerInstancesToUpdate.remove(this); } PlayerManager.this.getMinecraftServer().theChunkProviderServer.dropChunk(this.currentChunk.chunkXPos, this.currentChunk.chunkZPos); } } } public void processChunk() { this.increaseInhabitedTime(PlayerManager.this.theWorldServer.getChunkFromChunkCoords(this.currentChunk.chunkXPos, this.currentChunk.chunkZPos)); } private void increaseInhabitedTime(Chunk p_111196_1_) { p_111196_1_.setInhabitedTime(p_111196_1_.getInhabitedTime() + PlayerManager.this.theWorldServer.getTotalWorldTime() - this.previousWorldTime); this.previousWorldTime = PlayerManager.this.theWorldServer.getTotalWorldTime(); } public void flagChunkForUpdate(int p_151253_1_, int p_151253_2_, int p_151253_3_) { if (this.numBlocksToUpdate == 0) { PlayerManager.this.playerInstancesToUpdate.add(this); } this.flagsYAreasToUpdate |= 1 << (p_151253_2_ >> 4); if (this.numBlocksToUpdate < 64) { short var4 = (short)(p_151253_1_ << 12 | p_151253_3_ << 8 | p_151253_2_); for (int var5 = 0; var5 < this.numBlocksToUpdate; ++var5) { if (this.locationOfBlockChange[var5] == var4) { return; } } this.locationOfBlockChange[this.numBlocksToUpdate++] = var4; } } public void sendToAllPlayersWatchingChunk(Packet p_151251_1_) { for (int var2 = 0; var2 < this.playersWatchingChunk.size(); ++var2) { EntityPlayerMP var3 = (EntityPlayerMP)this.playersWatchingChunk.get(var2); if (!var3.loadedChunks.contains(this.currentChunk)) { var3.playerNetServerHandler.sendPacket(p_151251_1_); } } } public void onUpdate() { if (this.numBlocksToUpdate != 0) { int var1; int var2; int var3; if (this.numBlocksToUpdate == 1) { var1 = (this.locationOfBlockChange[0] >> 12 & 15) + this.currentChunk.chunkXPos * 16; var2 = this.locationOfBlockChange[0] & 255; var3 = (this.locationOfBlockChange[0] >> 8 & 15) + this.currentChunk.chunkZPos * 16; BlockPos var4 = new BlockPos(var1, var2, var3); this.sendToAllPlayersWatchingChunk(new S23PacketBlockChange(PlayerManager.this.theWorldServer, var4)); if (PlayerManager.this.theWorldServer.getBlockState(var4).getBlock().hasTileEntity()) { this.sendTileToAllPlayersWatchingChunk(PlayerManager.this.theWorldServer.getTileEntity(var4)); } } else { int var7; if (this.numBlocksToUpdate == 64) { var1 = this.currentChunk.chunkXPos * 16; var2 = this.currentChunk.chunkZPos * 16; this.sendToAllPlayersWatchingChunk(new S21PacketChunkData(PlayerManager.this.theWorldServer.getChunkFromChunkCoords(this.currentChunk.chunkXPos, this.currentChunk.chunkZPos), false, this.flagsYAreasToUpdate)); for (var3 = 0; var3 < 16; ++var3) { if ((this.flagsYAreasToUpdate & 1 << var3) != 0) { var7 = var3 << 4; List var5 = PlayerManager.this.theWorldServer.func_147486_a(var1, var7, var2, var1 + 16, var7 + 16, var2 + 16); for (int var6 = 0; var6 < var5.size(); ++var6) { this.sendTileToAllPlayersWatchingChunk((TileEntity)var5.get(var6)); } } } } else { this.sendToAllPlayersWatchingChunk(new S22PacketMultiBlockChange(this.numBlocksToUpdate, this.locationOfBlockChange, PlayerManager.this.theWorldServer.getChunkFromChunkCoords(this.currentChunk.chunkXPos, this.currentChunk.chunkZPos))); for (var1 = 0; var1 < this.numBlocksToUpdate; ++var1) { var2 = (this.locationOfBlockChange[var1] >> 12 & 15) + this.currentChunk.chunkXPos * 16; var3 = this.locationOfBlockChange[var1] & 255; var7 = (this.locationOfBlockChange[var1] >> 8 & 15) + this.currentChunk.chunkZPos * 16; BlockPos var8 = new BlockPos(var2, var3, var7); if (PlayerManager.this.theWorldServer.getBlockState(var8).getBlock().hasTileEntity()) { this.sendTileToAllPlayersWatchingChunk(PlayerManager.this.theWorldServer.getTileEntity(var8)); } } } } this.numBlocksToUpdate = 0; this.flagsYAreasToUpdate = 0; } } private void sendTileToAllPlayersWatchingChunk(TileEntity p_151252_1_) { if (p_151252_1_ != null) { Packet var2 = p_151252_1_.getDescriptionPacket(); if (var2 != null) { this.sendToAllPlayersWatchingChunk(var2); } } } } }