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