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 thePlayerManager; 028 029 public PlayerInstance(PlayerManager par1PlayerManager, int par2, int par3) 030 { 031 this.thePlayerManager = 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.thePlayerManager).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.thePlayerManager).remove(i); 069 070 if (this.numberOfTilesToUpdate > 0) 071 { 072 PlayerManager.getChunkWatchersWithPlayers(this.thePlayerManager).remove(this); 073 } 074 075 this.thePlayerManager.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.thePlayerManager).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.thePlayerManager))); 136 137 if (PlayerManager.getWorldServer(this.thePlayerManager).blockHasTileEntity(i, j, k)) 138 { 139 this.sendTileToAllPlayersWatchingChunk(PlayerManager.getWorldServer(this.thePlayerManager).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.thePlayerManager).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.thePlayerManager).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.thePlayerManager))); 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.thePlayerManager).blockHasTileEntity(j, k, l)) 181 { 182 this.sendTileToAllPlayersWatchingChunk(PlayerManager.getWorldServer(this.thePlayerManager).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}