001    package net.minecraft.server.management;
002    
003    import java.util.ArrayList;
004    import java.util.List;
005    import net.minecraft.entity.player.EntityPlayerMP;
006    import net.minecraft.network.packet.Packet;
007    import net.minecraft.network.packet.Packet51MapChunk;
008    import net.minecraft.network.packet.Packet52MultiBlockChange;
009    import net.minecraft.network.packet.Packet53BlockChange;
010    import net.minecraft.tileentity.TileEntity;
011    import net.minecraft.world.ChunkCoordIntPair;
012    
013    import net.minecraftforge.common.MinecraftForge;
014    import net.minecraftforge.event.world.ChunkWatchEvent;
015    
016    public 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    }