001 package net.minecraft.server.management;
002
003 import java.util.ArrayList;
004 import java.util.List;
005 import net.minecraft.entity.player.EntityPlayerMP;
006 import net.minecraft.util.LongHashMap;
007 import net.minecraft.world.ChunkCoordIntPair;
008 import net.minecraft.world.WorldProvider;
009 import net.minecraft.world.WorldServer;
010
011 public 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 var1 = 0; var1 < this.chunkWatcherWithPlayers.size(); ++var1)
065 {
066 ((PlayerInstance)this.chunkWatcherWithPlayers.get(var1)).sendChunkUpdate();
067 }
068
069 this.chunkWatcherWithPlayers.clear();
070
071 if (this.players.isEmpty())
072 {
073 WorldProvider var2 = this.theWorldServer.provider;
074
075 if (!var2.canRespawnHere())
076 {
077 this.theWorldServer.theChunkProviderServer.unloadAllChunks();
078 }
079 }
080 }
081
082 public PlayerInstance getOrCreateChunkWatcher(int par1, int par2, boolean par3)
083 {
084 long var4 = (long)par1 + 2147483647L | (long)par2 + 2147483647L << 32;
085 PlayerInstance var6 = (PlayerInstance)this.playerInstances.getValueByKey(var4);
086
087 if (var6 == null && par3)
088 {
089 var6 = new PlayerInstance(this, par1, par2);
090 this.playerInstances.add(var4, var6);
091 }
092
093 return var6;
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 var4 = par1 >> 4;
102 int var5 = par3 >> 4;
103 PlayerInstance var6 = this.getOrCreateChunkWatcher(var4, var5, false);
104
105 if (var6 != null)
106 {
107 var6.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 var2 = (int)par1EntityPlayerMP.posX >> 4;
117 int var3 = (int)par1EntityPlayerMP.posZ >> 4;
118 par1EntityPlayerMP.managedPosX = par1EntityPlayerMP.posX;
119 par1EntityPlayerMP.managedPosZ = par1EntityPlayerMP.posZ;
120
121 for (int var4 = var2 - this.playerViewRadius; var4 <= var2 + this.playerViewRadius; ++var4)
122 {
123 for (int var5 = var3 - this.playerViewRadius; var5 <= var3 + this.playerViewRadius; ++var5)
124 {
125 this.getOrCreateChunkWatcher(var4, var5, 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 var2 = new ArrayList(par1EntityPlayerMP.loadedChunks);
139 int var3 = 0;
140 int var4 = this.playerViewRadius;
141 int var5 = (int)par1EntityPlayerMP.posX >> 4;
142 int var6 = (int)par1EntityPlayerMP.posZ >> 4;
143 int var7 = 0;
144 int var8 = 0;
145 ChunkCoordIntPair var9 = PlayerInstance.getChunkLocation(this.getOrCreateChunkWatcher(var5, var6, true));
146 par1EntityPlayerMP.loadedChunks.clear();
147
148 if (var2.contains(var9))
149 {
150 par1EntityPlayerMP.loadedChunks.add(var9);
151 }
152
153 int var10;
154
155 for (var10 = 1; var10 <= var4 * 2; ++var10)
156 {
157 for (int var11 = 0; var11 < 2; ++var11)
158 {
159 int[] var12 = this.xzDirectionsConst[var3++ % 4];
160
161 for (int var13 = 0; var13 < var10; ++var13)
162 {
163 var7 += var12[0];
164 var8 += var12[1];
165 var9 = PlayerInstance.getChunkLocation(this.getOrCreateChunkWatcher(var5 + var7, var6 + var8, true));
166
167 if (var2.contains(var9))
168 {
169 par1EntityPlayerMP.loadedChunks.add(var9);
170 }
171 }
172 }
173 }
174
175 var3 %= 4;
176
177 for (var10 = 0; var10 < var4 * 2; ++var10)
178 {
179 var7 += this.xzDirectionsConst[var3][0];
180 var8 += this.xzDirectionsConst[var3][1];
181 var9 = PlayerInstance.getChunkLocation(this.getOrCreateChunkWatcher(var5 + var7, var6 + var8, true));
182
183 if (var2.contains(var9))
184 {
185 par1EntityPlayerMP.loadedChunks.add(var9);
186 }
187 }
188 }
189
190 /**
191 * Removes an EntityPlayerMP from the PlayerManager.
192 */
193 public void removePlayer(EntityPlayerMP par1EntityPlayerMP)
194 {
195 int var2 = (int)par1EntityPlayerMP.managedPosX >> 4;
196 int var3 = (int)par1EntityPlayerMP.managedPosZ >> 4;
197
198 for (int var4 = var2 - this.playerViewRadius; var4 <= var2 + this.playerViewRadius; ++var4)
199 {
200 for (int var5 = var3 - this.playerViewRadius; var5 <= var3 + this.playerViewRadius; ++var5)
201 {
202 PlayerInstance var6 = this.getOrCreateChunkWatcher(var4, var5, false);
203
204 if (var6 != null)
205 {
206 var6.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 var6 = par1 - par3;
217 int var7 = par2 - par4;
218 return var6 >= -par5 && var6 <= par5 ? var7 >= -par5 && var7 <= 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 var2 = (int)par1EntityPlayerMP.posX >> 4;
227 int var3 = (int)par1EntityPlayerMP.posZ >> 4;
228 double var4 = par1EntityPlayerMP.managedPosX - par1EntityPlayerMP.posX;
229 double var6 = par1EntityPlayerMP.managedPosZ - par1EntityPlayerMP.posZ;
230 double var8 = var4 * var4 + var6 * var6;
231
232 if (var8 >= 64.0D)
233 {
234 int var10 = (int)par1EntityPlayerMP.managedPosX >> 4;
235 int var11 = (int)par1EntityPlayerMP.managedPosZ >> 4;
236 int var12 = this.playerViewRadius;
237 int var13 = var2 - var10;
238 int var14 = var3 - var11;
239
240 if (var13 != 0 || var14 != 0)
241 {
242 for (int var15 = var2 - var12; var15 <= var2 + var12; ++var15)
243 {
244 for (int var16 = var3 - var12; var16 <= var3 + var12; ++var16)
245 {
246 if (!this.func_72684_a(var15, var16, var10, var11, var12))
247 {
248 this.getOrCreateChunkWatcher(var15, var16, true).addPlayerToChunkWatchingList(par1EntityPlayerMP);
249 }
250
251 if (!this.func_72684_a(var15 - var13, var16 - var14, var2, var3, var12))
252 {
253 PlayerInstance var17 = this.getOrCreateChunkWatcher(var15 - var13, var16 - var14, false);
254
255 if (var17 != null)
256 {
257 var17.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 var4 = this.getOrCreateChunkWatcher(par2, par3, false);
273 return var4 == null ? false : PlayerInstance.getPlayersInChunk(var4).contains(par1EntityPlayerMP) && !par1EntityPlayerMP.loadedChunks.contains(PlayerInstance.getChunkLocation(var4));
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 }