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