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}