    001package net.minecraft.server.management;
    002
    003import java.util.ArrayList;
    004import java.util.Arrays;
    005import java.util.List;
    006import net.minecraft.entity.player.EntityPlayerMP;
    007import net.minecraft.network.packet.Packet;
    008import net.minecraft.network.packet.Packet51MapChunk;
    009import net.minecraft.network.packet.Packet52MultiBlockChange;
    010import net.minecraft.network.packet.Packet53BlockChange;
    011import net.minecraft.tileentity.TileEntity;
    012import net.minecraft.world.ChunkCoordIntPair;
    013import net.minecraftforge.common.ForgeDummyContainer;
    014import net.minecraftforge.common.MinecraftForge;
    015import net.minecraftforge.event.world.ChunkWatchEvent;
    016
    017public class PlayerInstance
    018{
    019    private final List playersInChunk;
    020
    021    /** note: this is final */
    022    private final ChunkCoordIntPair chunkLocation;
    023    private short[] locationOfBlockChange;
    024    private int numberOfTilesToUpdate;
    025    private int field_73260_f;
    026
    027    final PlayerManager myManager;
    028
    029    public PlayerInstance(PlayerManager par1PlayerManager, int par2, int par3)
    030    {
    031        this.myManager = par1PlayerManager;
    032        this.playersInChunk = new ArrayList();
    033        this.locationOfBlockChange = new short[64];
    034        this.numberOfTilesToUpdate = 0;
    035        this.chunkLocation = new ChunkCoordIntPair(par2, par3);
    036        par1PlayerManager.getWorldServer().theChunkProviderServer.loadChunk(par2, par3);
    037    }
    038
    039    /**
    040     * called for all chunks within the visible radius of the player
    041     */
    042    public void addPlayerToChunkWatchingList(EntityPlayerMP par1EntityPlayerMP)
    043    {
    044        if (this.playersInChunk.contains(par1EntityPlayerMP))
    045        {
    046            throw new IllegalStateException("Failed to add player. " + par1EntityPlayerMP + " already is in chunk " + this.chunkLocation.chunkXPos + ", " + this.chunkLocation.chunkZPos);
    047        }
    048        else
    049        {
    050            this.playersInChunk.add(par1EntityPlayerMP);
    051            par1EntityPlayerMP.loadedChunks.add(this.chunkLocation);
    052        }
    053    }
    054
    055    public void sendThisChunkToPlayer(EntityPlayerMP par1EntityPlayerMP)
    056    {
    057        if (this.playersInChunk.contains(par1EntityPlayerMP))
    058        {
    059            par1EntityPlayerMP.playerNetServerHandler.sendPacketToPlayer(new Packet51MapChunk(PlayerManager.getWorldServer(this.myManager).getChunkFromChunkCoords(this.chunkLocation.chunkXPos, this.chunkLocation.chunkZPos), true, 0));
    060            this.playersInChunk.remove(par1EntityPlayerMP);
    061            par1EntityPlayerMP.loadedChunks.remove(this.chunkLocation);
    062
    063            MinecraftForge.EVENT_BUS.post(new ChunkWatchEvent.UnWatch(chunkLocation, par1EntityPlayerMP));
    064
    065            if (this.playersInChunk.isEmpty())
    066            {
    067                long i = (long)this.chunkLocation.chunkXPos + 2147483647L | (long)this.chunkLocation.chunkZPos + 2147483647L << 32;
    068                PlayerManager.getChunkWatchers(this.myManager).remove(i);
    069
    070                if (this.numberOfTilesToUpdate > 0)
    071                {
    072                    PlayerManager.getChunkWatchersWithPlayers(this.myManager).remove(this);
    073                }
    074
    075                this.myManager.getWorldServer().theChunkProviderServer.unloadChunksIfNotNearSpawn(this.chunkLocation.chunkXPos, this.chunkLocation.chunkZPos);
    076            }
    077        }
    078    }
    079
    080    public void flagChunkForUpdate(int par1, int par2, int par3)
    081    {
    082        if (this.numberOfTilesToUpdate == 0)
    083        {
    084            PlayerManager.getChunkWatchersWithPlayers(this.myManager).add(this);
    085        }
    086
    087        this.field_73260_f |= 1 << (par2 >> 4);
    088
    089        //if (this.numberOfTilesToUpdate < 64) //Forge; Cache everything, so always run
    090        {
    091            short short1 = (short)(par1 << 12 | par3 << 8 | par2);
    092
    093            for (int l = 0; l < this.numberOfTilesToUpdate; ++l)
    094            {
    095                if (this.locationOfBlockChange[l] == short1)
    096                {
    097                    return;
    098                }
    099            }
    100
    101            if (numberOfTilesToUpdate == locationOfBlockChange.length)
    102            {
    103                locationOfBlockChange = Arrays.copyOf(locationOfBlockChange, locationOfBlockChange.length << 1);
    104            }
    105            this.locationOfBlockChange[this.numberOfTilesToUpdate++] = short1;
    106        }
    107    }
    108
    109    public void sendToAllPlayersWatchingChunk(Packet par1Packet)
    110    {
    111        for (int i = 0; i < this.playersInChunk.size(); ++i)
    112        {
    113            EntityPlayerMP entityplayermp = (EntityPlayerMP)this.playersInChunk.get(i);
    114
    115            if (!entityplayermp.loadedChunks.contains(this.chunkLocation))
    116            {
    117                entityplayermp.playerNetServerHandler.sendPacketToPlayer(par1Packet);
    118            }
    119        }
    120    }
    121
    122    public void sendChunkUpdate()
    123    {
    124        if (this.numberOfTilesToUpdate != 0)
    125        {
    126            int i;
    127            int j;
    128            int k;
    129
    130            if (this.numberOfTilesToUpdate == 1)
    131            {
    132                i = this.chunkLocation.chunkXPos * 16 + (this.locationOfBlockChange[0] >> 12 & 15);
    133                j = this.locationOfBlockChange[0] & 255;
    134                k = this.chunkLocation.chunkZPos * 16 + (this.locationOfBlockChange[0] >> 8 & 15);
    135                this.sendToAllPlayersWatchingChunk(new Packet53BlockChange(i, j, k, PlayerManager.getWorldServer(this.myManager)));
    136
    137                if (PlayerManager.getWorldServer(this.myManager).blockHasTileEntity(i, j, k))
    138                {
    139                    this.sendTileToAllPlayersWatchingChunk(PlayerManager.getWorldServer(this.myManager).getBlockTileEntity(i, j, k));
    140                }
    141            }
    142            else
    143            {
    144                int l;
    145
    146                if (this.numberOfTilesToUpdate >= ForgeDummyContainer.clumpingThreshold)
    147                {
    148                    i = this.chunkLocation.chunkXPos * 16;
    149                    j = this.chunkLocation.chunkZPos * 16;
    150                    this.sendToAllPlayersWatchingChunk(new Packet51MapChunk(PlayerManager.getWorldServer(this.myManager).getChunkFromChunkCoords(this.chunkLocation.chunkXPos, this.chunkLocation.chunkZPos), false, this.field_73260_f));
    151
    152                    /* Forge: Grabs ALL tile entities is costly on a modded server, only send needed ones
    153                    for (k = 0; k < 16; ++k)
    154                    {
    155                        if ((this.field_73260_f & 1 << k) != 0)
    156                        {
    157                            l = k << 4;
    158                            List list = PlayerManager.getWorldServer(this.myManager).getAllTileEntityInBox(i, l, j, i + 16, l + 16, j + 16);
    159
    160                            for (int i1 = 0; i1 < list.size(); ++i1)
    161                            {
    162                                this.sendTileToAllPlayersWatchingChunk((TileEntity)list.get(i1));
    163                            }
    164                        }
    165                    }
    166                    */
    167                }
    168                else
    169                {
    170                    this.sendToAllPlayersWatchingChunk(new Packet52MultiBlockChange(this.chunkLocation.chunkXPos, this.chunkLocation.chunkZPos, this.locationOfBlockChange, this.numberOfTilesToUpdate, PlayerManager.getWorldServer(this.myManager)));
    171                }
    172
    173                { //Forge: Send only the tile entities that are updated, Adding this brace lets us keep the indent and the patch small
    174                    for (i = 0; i < this.numberOfTilesToUpdate; ++i)
    175                    {
    176                        j = this.chunkLocation.chunkXPos * 16 + (this.locationOfBlockChange[i] >> 12 & 15);
    177                        k = this.locationOfBlockChange[i] & 255;
    178                        l = this.chunkLocation.chunkZPos * 16 + (this.locationOfBlockChange[i] >> 8 & 15);
    179
    180                        if (PlayerManager.getWorldServer(this.myManager).blockHasTileEntity(j, k, l))
    181                        {
    182                            this.sendTileToAllPlayersWatchingChunk(PlayerManager.getWorldServer(this.myManager).getBlockTileEntity(j, k, l));
    183                        }
    184                    }
    185                }
    186            }
    187
    188            this.numberOfTilesToUpdate = 0;
    189            this.field_73260_f = 0;
    190        }
    191    }
    192
    193    private void sendTileToAllPlayersWatchingChunk(TileEntity par1TileEntity)
    194    {
    195        if (par1TileEntity != null)
    196        {
    197            Packet packet = par1TileEntity.getDescriptionPacket();
    198
    199            if (packet != null)
    200            {
    201                this.sendToAllPlayersWatchingChunk(packet);
    202            }
    203        }
    204    }
    205
    206    static ChunkCoordIntPair getChunkLocation(PlayerInstance par0PlayerInstance)
    207    {
    208        return par0PlayerInstance.chunkLocation;
    209    }
    210
    211    static List getPlayersInChunk(PlayerInstance par0PlayerInstance)
    212    {
    213        return par0PlayerInstance.playersInChunk;
    214    }
    215}