    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 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}