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