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