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}