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    }