001package net.minecraft.server.management; 002 003import java.util.ArrayList; 004import java.util.List; 005import net.minecraft.entity.player.EntityPlayerMP; 006import net.minecraft.util.LongHashMap; 007import net.minecraft.world.ChunkCoordIntPair; 008import net.minecraft.world.WorldProvider; 009import net.minecraft.world.WorldServer; 010 011public class PlayerManager 012{ 013 private final WorldServer theWorldServer; 014 015 /** players in the current instance */ 016 private final List players = new ArrayList(); 017 018 /** 019 * A map of chunk position (two ints concatenated into a long) to PlayerInstance 020 */ 021 private final LongHashMap playerInstances = new LongHashMap(); 022 023 /** 024 * contains a PlayerInstance for every chunk they can see. the "player instance" cotains a list of all players who 025 * can also that chunk 026 */ 027 private final List chunkWatcherWithPlayers = new ArrayList(); 028 029 /** 030 * Number of chunks the server sends to the client. Valid 3<=x<=15. In server.properties. 031 */ 032 private final int playerViewRadius; 033 034 /** x, z direction vectors: east, south, west, north */ 035 private final int[][] xzDirectionsConst = new int[][] {{1, 0}, {0, 1}, { -1, 0}, {0, -1}}; 036 037 public PlayerManager(WorldServer par1WorldServer, int par2) 038 { 039 if (par2 > 15) 040 { 041 throw new IllegalArgumentException("Too big view radius!"); 042 } 043 else if (par2 < 3) 044 { 045 throw new IllegalArgumentException("Too small view radius!"); 046 } 047 else 048 { 049 this.playerViewRadius = par2; 050 this.theWorldServer = par1WorldServer; 051 } 052 } 053 054 public WorldServer getWorldServer() 055 { 056 return this.theWorldServer; 057 } 058 059 /** 060 * updates all the player instances that need to be updated 061 */ 062 public void updatePlayerInstances() 063 { 064 for (int i = 0; i < this.chunkWatcherWithPlayers.size(); ++i) 065 { 066 ((PlayerInstance)this.chunkWatcherWithPlayers.get(i)).sendChunkUpdate(); 067 } 068 069 this.chunkWatcherWithPlayers.clear(); 070 071 if (this.players.isEmpty()) 072 { 073 WorldProvider worldprovider = this.theWorldServer.provider; 074 075 if (!worldprovider.canRespawnHere()) 076 { 077 this.theWorldServer.theChunkProviderServer.unloadAllChunks(); 078 } 079 } 080 } 081 082 public PlayerInstance getOrCreateChunkWatcher(int par1, int par2, boolean par3) 083 { 084 long k = (long)par1 + 2147483647L | (long)par2 + 2147483647L << 32; 085 PlayerInstance playerinstance = (PlayerInstance)this.playerInstances.getValueByKey(k); 086 087 if (playerinstance == null && par3) 088 { 089 playerinstance = new PlayerInstance(this, par1, par2); 090 this.playerInstances.add(k, playerinstance); 091 } 092 093 return playerinstance; 094 } 095 096 /** 097 * the "PlayerInstance"/ chunkWatcher will send this chunk to all players who are in line of sight 098 */ 099 public void flagChunkForUpdate(int par1, int par2, int par3) 100 { 101 int l = par1 >> 4; 102 int i1 = par3 >> 4; 103 PlayerInstance playerinstance = this.getOrCreateChunkWatcher(l, i1, false); 104 105 if (playerinstance != null) 106 { 107 playerinstance.flagChunkForUpdate(par1 & 15, par2, par3 & 15); 108 } 109 } 110 111 /** 112 * Adds an EntityPlayerMP to the PlayerManager. 113 */ 114 public void addPlayer(EntityPlayerMP par1EntityPlayerMP) 115 { 116 int i = (int)par1EntityPlayerMP.posX >> 4; 117 int j = (int)par1EntityPlayerMP.posZ >> 4; 118 par1EntityPlayerMP.managedPosX = par1EntityPlayerMP.posX; 119 par1EntityPlayerMP.managedPosZ = par1EntityPlayerMP.posZ; 120 121 for (int k = i - this.playerViewRadius; k <= i + this.playerViewRadius; ++k) 122 { 123 for (int l = j - this.playerViewRadius; l <= j + this.playerViewRadius; ++l) 124 { 125 this.getOrCreateChunkWatcher(k, l, true).addPlayerToChunkWatchingList(par1EntityPlayerMP); 126 } 127 } 128 129 this.players.add(par1EntityPlayerMP); 130 this.filterChunkLoadQueue(par1EntityPlayerMP); 131 } 132 133 /** 134 * Removes all chunks from the given player's chunk load queue that are not in viewing range of the player. 135 */ 136 public void filterChunkLoadQueue(EntityPlayerMP par1EntityPlayerMP) 137 { 138 ArrayList arraylist = new ArrayList(par1EntityPlayerMP.loadedChunks); 139 int i = 0; 140 int j = this.playerViewRadius; 141 int k = (int)par1EntityPlayerMP.posX >> 4; 142 int l = (int)par1EntityPlayerMP.posZ >> 4; 143 int i1 = 0; 144 int j1 = 0; 145 ChunkCoordIntPair chunkcoordintpair = PlayerInstance.getChunkLocation(this.getOrCreateChunkWatcher(k, l, true)); 146 par1EntityPlayerMP.loadedChunks.clear(); 147 148 if (arraylist.contains(chunkcoordintpair)) 149 { 150 par1EntityPlayerMP.loadedChunks.add(chunkcoordintpair); 151 } 152 153 int k1; 154 155 for (k1 = 1; k1 <= j * 2; ++k1) 156 { 157 for (int l1 = 0; l1 < 2; ++l1) 158 { 159 int[] aint = this.xzDirectionsConst[i++ % 4]; 160 161 for (int i2 = 0; i2 < k1; ++i2) 162 { 163 i1 += aint[0]; 164 j1 += aint[1]; 165 chunkcoordintpair = PlayerInstance.getChunkLocation(this.getOrCreateChunkWatcher(k + i1, l + j1, true)); 166 167 if (arraylist.contains(chunkcoordintpair)) 168 { 169 par1EntityPlayerMP.loadedChunks.add(chunkcoordintpair); 170 } 171 } 172 } 173 } 174 175 i %= 4; 176 177 for (k1 = 0; k1 < j * 2; ++k1) 178 { 179 i1 += this.xzDirectionsConst[i][0]; 180 j1 += this.xzDirectionsConst[i][1]; 181 chunkcoordintpair = PlayerInstance.getChunkLocation(this.getOrCreateChunkWatcher(k + i1, l + j1, true)); 182 183 if (arraylist.contains(chunkcoordintpair)) 184 { 185 par1EntityPlayerMP.loadedChunks.add(chunkcoordintpair); 186 } 187 } 188 } 189 190 /** 191 * Removes an EntityPlayerMP from the PlayerManager. 192 */ 193 public void removePlayer(EntityPlayerMP par1EntityPlayerMP) 194 { 195 int i = (int)par1EntityPlayerMP.managedPosX >> 4; 196 int j = (int)par1EntityPlayerMP.managedPosZ >> 4; 197 198 for (int k = i - this.playerViewRadius; k <= i + this.playerViewRadius; ++k) 199 { 200 for (int l = j - this.playerViewRadius; l <= j + this.playerViewRadius; ++l) 201 { 202 PlayerInstance playerinstance = this.getOrCreateChunkWatcher(k, l, false); 203 204 if (playerinstance != null) 205 { 206 playerinstance.sendThisChunkToPlayer(par1EntityPlayerMP); 207 } 208 } 209 } 210 211 this.players.remove(par1EntityPlayerMP); 212 } 213 214 private boolean func_72684_a(int par1, int par2, int par3, int par4, int par5) 215 { 216 int j1 = par1 - par3; 217 int k1 = par2 - par4; 218 return j1 >= -par5 && j1 <= par5 ? k1 >= -par5 && k1 <= par5 : false; 219 } 220 221 /** 222 * update chunks around a player being moved by server logic (e.g. cart, boat) 223 */ 224 public void updateMountedMovingPlayer(EntityPlayerMP par1EntityPlayerMP) 225 { 226 int i = (int)par1EntityPlayerMP.posX >> 4; 227 int j = (int)par1EntityPlayerMP.posZ >> 4; 228 double d0 = par1EntityPlayerMP.managedPosX - par1EntityPlayerMP.posX; 229 double d1 = par1EntityPlayerMP.managedPosZ - par1EntityPlayerMP.posZ; 230 double d2 = d0 * d0 + d1 * d1; 231 232 if (d2 >= 64.0D) 233 { 234 int k = (int)par1EntityPlayerMP.managedPosX >> 4; 235 int l = (int)par1EntityPlayerMP.managedPosZ >> 4; 236 int i1 = this.playerViewRadius; 237 int j1 = i - k; 238 int k1 = j - l; 239 240 if (j1 != 0 || k1 != 0) 241 { 242 for (int l1 = i - i1; l1 <= i + i1; ++l1) 243 { 244 for (int i2 = j - i1; i2 <= j + i1; ++i2) 245 { 246 if (!this.func_72684_a(l1, i2, k, l, i1)) 247 { 248 this.getOrCreateChunkWatcher(l1, i2, true).addPlayerToChunkWatchingList(par1EntityPlayerMP); 249 } 250 251 if (!this.func_72684_a(l1 - j1, i2 - k1, i, j, i1)) 252 { 253 PlayerInstance playerinstance = this.getOrCreateChunkWatcher(l1 - j1, i2 - k1, false); 254 255 if (playerinstance != null) 256 { 257 playerinstance.sendThisChunkToPlayer(par1EntityPlayerMP); 258 } 259 } 260 } 261 } 262 263 this.filterChunkLoadQueue(par1EntityPlayerMP); 264 par1EntityPlayerMP.managedPosX = par1EntityPlayerMP.posX; 265 par1EntityPlayerMP.managedPosZ = par1EntityPlayerMP.posZ; 266 } 267 } 268 } 269 270 public boolean isPlayerWatchingChunk(EntityPlayerMP par1EntityPlayerMP, int par2, int par3) 271 { 272 PlayerInstance playerinstance = this.getOrCreateChunkWatcher(par2, par3, false); 273 return playerinstance == null ? false : PlayerInstance.getPlayersInChunk(playerinstance).contains(par1EntityPlayerMP) && !par1EntityPlayerMP.loadedChunks.contains(PlayerInstance.getChunkLocation(playerinstance)); 274 } 275 276 /** 277 * Get the furthest viewable block given player's view distance 278 */ 279 public static int getFurthestViewableBlock(int par0) 280 { 281 return par0 * 16 - 16; 282 } 283 284 static WorldServer getWorldServer(PlayerManager par0PlayerManager) 285 { 286 return par0PlayerManager.theWorldServer; 287 } 288 289 static LongHashMap getChunkWatchers(PlayerManager par0PlayerManager) 290 { 291 return par0PlayerManager.playerInstances; 292 } 293 294 static List getChunkWatchersWithPlayers(PlayerManager par0PlayerManager) 295 { 296 return par0PlayerManager.chunkWatcherWithPlayers; 297 } 298}