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