package net.minecraft.client.multiplayer; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; import java.util.HashSet; import java.util.Iterator; import java.util.Random; import java.util.Set; import net.minecraft.block.Block; import net.minecraft.client.Minecraft; import net.minecraft.client.particle.EntityFireworkStarterFX; import net.minecraft.crash.CrashReport; import net.minecraft.crash.CrashReportCategory; import net.minecraft.entity.Entity; import net.minecraft.entity.item.EntityMinecart; import net.minecraft.entity.item.SoundUpdaterMinecart; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.network.packet.Packet255KickDisconnect; import net.minecraft.profiler.Profiler; import net.minecraft.server.gui.IUpdatePlayerListBox; import net.minecraft.util.IntHashMap; import net.minecraft.world.ChunkCoordIntPair; import net.minecraft.world.World; import net.minecraft.world.WorldProvider; import net.minecraft.world.WorldSettings; import net.minecraft.world.chunk.Chunk; import net.minecraft.world.chunk.IChunkProvider; import net.minecraft.world.storage.SaveHandlerMP; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.event.world.WorldEvent; @SideOnly(Side.CLIENT) public class WorldClient extends World { /** The packets that need to be sent to the server. */ private NetClientHandler sendQueue; /** The ChunkProviderClient instance */ private ChunkProviderClient clientChunkProvider; /** * The hash set of entities handled by this client. Uses the entity's ID as the hash set's key. */ private IntHashMap entityHashSet = new IntHashMap(); /** Contains all entities for this client, both spawned and non-spawned. */ private Set entityList = new HashSet(); /** * Contains all entities for this client that were not spawned due to a non-present chunk. The game will attempt to * spawn up to 10 pending entities with each subsequent tick until the spawn queue is empty. */ private Set entitySpawnQueue = new HashSet(); private final Minecraft mc = Minecraft.getMinecraft(); private final Set previousActiveChunkSet = new HashSet(); public WorldClient(NetClientHandler par1NetClientHandler, WorldSettings par2WorldSettings, int par3, int par4, Profiler par5Profiler) { super(new SaveHandlerMP(), "MpServer", WorldProvider.getProviderForDimension(par3), par2WorldSettings, par5Profiler); this.sendQueue = par1NetClientHandler; this.difficultySetting = par4; this.mapStorage = par1NetClientHandler.mapStorage; this.isRemote = true; finishSetup(); this.setSpawnLocation(8, 64, 8); MinecraftForge.EVENT_BUS.post(new WorldEvent.Load(this)); } /** * Runs a single tick for the world */ public void tick() { super.tick(); this.func_82738_a(this.getTotalWorldTime() + 1L); this.setWorldTime(this.getWorldTime() + 1L); this.theProfiler.startSection("reEntryProcessing"); for (int var1 = 0; var1 < 10 && !this.entitySpawnQueue.isEmpty(); ++var1) { Entity var2 = (Entity)this.entitySpawnQueue.iterator().next(); this.entitySpawnQueue.remove(var2); if (!this.loadedEntityList.contains(var2)) { this.spawnEntityInWorld(var2); } } this.theProfiler.endStartSection("connection"); this.sendQueue.processReadPackets(); this.theProfiler.endStartSection("chunkCache"); this.clientChunkProvider.unload100OldestChunks(); this.theProfiler.endStartSection("tiles"); this.tickBlocksAndAmbiance(); this.theProfiler.endSection(); } /** * Invalidates an AABB region of blocks from the receive queue, in the event that the block has been modified * client-side in the intervening 80 receive ticks. */ public void invalidateBlockReceiveRegion(int par1, int par2, int par3, int par4, int par5, int par6) {} /** * Creates the chunk provider for this world. Called in the constructor. Retrieves provider from worldProvider? */ protected IChunkProvider createChunkProvider() { this.clientChunkProvider = new ChunkProviderClient(this); return this.clientChunkProvider; } /** * plays random cave ambient sounds and runs updateTick on random blocks within each chunk in the vacinity of a * player */ protected void tickBlocksAndAmbiance() { super.tickBlocksAndAmbiance(); this.previousActiveChunkSet.retainAll(this.activeChunkSet); if (this.previousActiveChunkSet.size() == this.activeChunkSet.size()) { this.previousActiveChunkSet.clear(); } int var1 = 0; Iterator var2 = this.activeChunkSet.iterator(); while (var2.hasNext()) { ChunkCoordIntPair var3 = (ChunkCoordIntPair)var2.next(); if (!this.previousActiveChunkSet.contains(var3)) { int var4 = var3.chunkXPos * 16; int var5 = var3.chunkZPos * 16; this.theProfiler.startSection("getChunk"); Chunk var6 = this.getChunkFromChunkCoords(var3.chunkXPos, var3.chunkZPos); this.moodSoundAndLightCheck(var4, var5, var6); this.theProfiler.endSection(); this.previousActiveChunkSet.add(var3); ++var1; if (var1 >= 10) { return; } } } } public void doPreChunk(int par1, int par2, boolean par3) { if (par3) { this.clientChunkProvider.loadChunk(par1, par2); } else { this.clientChunkProvider.unloadChunk(par1, par2); } if (!par3) { this.markBlockRangeForRenderUpdate(par1 * 16, 0, par2 * 16, par1 * 16 + 15, 256, par2 * 16 + 15); } } /** * Called to place all entities as part of a world */ public boolean spawnEntityInWorld(Entity par1Entity) { boolean var2 = super.spawnEntityInWorld(par1Entity); this.entityList.add(par1Entity); if (!var2) { this.entitySpawnQueue.add(par1Entity); } return var2; } /** * Dismounts the entity (and anything riding the entity), sets the dead flag, and removes the player entity from the * player entity list. Called by the playerLoggedOut function. */ public void setEntityDead(Entity par1Entity) { super.setEntityDead(par1Entity); this.entityList.remove(par1Entity); } /** * Start the skin for this entity downloading, if necessary, and increment its reference counter */ protected void obtainEntitySkin(Entity par1Entity) { super.obtainEntitySkin(par1Entity); if (this.entitySpawnQueue.contains(par1Entity)) { this.entitySpawnQueue.remove(par1Entity); } } /** * Decrement the reference counter for this entity's skin image data */ public void releaseEntitySkin(Entity par1Entity) { super.releaseEntitySkin(par1Entity); if (this.entityList.contains(par1Entity)) { if (par1Entity.isEntityAlive()) { this.entitySpawnQueue.add(par1Entity); } else { this.entityList.remove(par1Entity); } } } /** * Add an ID to Entity mapping to entityHashSet */ public void addEntityToWorld(int par1, Entity par2Entity) { Entity var3 = this.getEntityByID(par1); if (var3 != null) { this.setEntityDead(var3); } this.entityList.add(par2Entity); par2Entity.entityId = par1; if (!this.spawnEntityInWorld(par2Entity)) { this.entitySpawnQueue.add(par2Entity); } this.entityHashSet.addKey(par1, par2Entity); } /** * Returns the Entity with the given ID, or null if it doesn't exist in this World. */ public Entity getEntityByID(int par1) { return (Entity)(par1 == this.mc.thePlayer.entityId ? this.mc.thePlayer : (Entity)this.entityHashSet.lookup(par1)); } public Entity removeEntityFromWorld(int par1) { Entity var2 = (Entity)this.entityHashSet.removeObject(par1); if (var2 != null) { this.entityList.remove(var2); this.setEntityDead(var2); } return var2; } public boolean setBlockAndMetadataAndInvalidate(int par1, int par2, int par3, int par4, int par5) { this.invalidateBlockReceiveRegion(par1, par2, par3, par1, par2, par3); return super.setBlockAndMetadataWithNotify(par1, par2, par3, par4, par5); } /** * If on MP, sends a quitting packet. */ public void sendQuittingDisconnectingPacket() { this.sendQueue.quitWithPacket(new Packet255KickDisconnect("Quitting")); } public IUpdatePlayerListBox func_82735_a(EntityMinecart par1EntityMinecart) { return new SoundUpdaterMinecart(this.mc.sndManager, par1EntityMinecart, this.mc.thePlayer); } /** * Updates all weather states. */ protected void updateWeather() { super.updateWeather(); } @Override public void updateWeatherBody() { if (!this.provider.hasNoSky) { this.prevRainingStrength = this.rainingStrength; if (this.worldInfo.isRaining()) { this.rainingStrength = (float)((double)this.rainingStrength + 0.01D); } else { this.rainingStrength = (float)((double)this.rainingStrength - 0.01D); } if (this.rainingStrength < 0.0F) { this.rainingStrength = 0.0F; } if (this.rainingStrength > 1.0F) { this.rainingStrength = 1.0F; } this.prevThunderingStrength = this.thunderingStrength; if (this.worldInfo.isThundering()) { this.thunderingStrength = (float)((double)this.thunderingStrength + 0.01D); } else { this.thunderingStrength = (float)((double)this.thunderingStrength - 0.01D); } if (this.thunderingStrength < 0.0F) { this.thunderingStrength = 0.0F; } if (this.thunderingStrength > 1.0F) { this.thunderingStrength = 1.0F; } } } public void func_73029_E(int par1, int par2, int par3) { byte var4 = 16; Random var5 = new Random(); for (int var6 = 0; var6 < 1000; ++var6) { int var7 = par1 + this.rand.nextInt(var4) - this.rand.nextInt(var4); int var8 = par2 + this.rand.nextInt(var4) - this.rand.nextInt(var4); int var9 = par3 + this.rand.nextInt(var4) - this.rand.nextInt(var4); int var10 = this.getBlockId(var7, var8, var9); if (var10 == 0 && this.rand.nextInt(8) > var8 && this.provider.getWorldHasVoidParticles()) { this.spawnParticle("depthsuspend", (double)((float)var7 + this.rand.nextFloat()), (double)((float)var8 + this.rand.nextFloat()), (double)((float)var9 + this.rand.nextFloat()), 0.0D, 0.0D, 0.0D); } else if (var10 > 0) { Block.blocksList[var10].randomDisplayTick(this, var7, var8, var9, var5); } } } /** * also releases skins. */ public void removeAllEntities() { this.loadedEntityList.removeAll(this.unloadedEntityList); int var1; Entity var2; int var3; int var4; for (var1 = 0; var1 < this.unloadedEntityList.size(); ++var1) { var2 = (Entity)this.unloadedEntityList.get(var1); var3 = var2.chunkCoordX; var4 = var2.chunkCoordZ; if (var2.addedToChunk && this.chunkExists(var3, var4)) { this.getChunkFromChunkCoords(var3, var4).removeEntity(var2); } } for (var1 = 0; var1 < this.unloadedEntityList.size(); ++var1) { this.releaseEntitySkin((Entity)this.unloadedEntityList.get(var1)); } this.unloadedEntityList.clear(); for (var1 = 0; var1 < this.loadedEntityList.size(); ++var1) { var2 = (Entity)this.loadedEntityList.get(var1); if (var2.ridingEntity != null) { if (!var2.ridingEntity.isDead && var2.ridingEntity.riddenByEntity == var2) { continue; } var2.ridingEntity.riddenByEntity = null; var2.ridingEntity = null; } if (var2.isDead) { var3 = var2.chunkCoordX; var4 = var2.chunkCoordZ; if (var2.addedToChunk && this.chunkExists(var3, var4)) { this.getChunkFromChunkCoords(var3, var4).removeEntity(var2); } this.loadedEntityList.remove(var1--); this.releaseEntitySkin(var2); } } } /** * Adds some basic stats of the world to the given crash report. */ public CrashReportCategory addWorldInfoToCrashReport(CrashReport par1CrashReport) { CrashReportCategory var2 = super.addWorldInfoToCrashReport(par1CrashReport); var2.addCrashSectionCallable("Forced entities", new CallableMPL1(this)); var2.addCrashSectionCallable("Retry entities", new CallableMPL2(this)); return var2; } /** * par8 is loudness, all pars passed to minecraftInstance.sndManager.playSound */ public void playSound(double par1, double par3, double par5, String par7Str, float par8, float par9, boolean par10) { float var11 = 16.0F; if (par8 > 1.0F) { var11 *= par8; } double var12 = this.mc.renderViewEntity.getDistanceSq(par1, par3, par5); if (var12 < (double)(var11 * var11)) { if (par10 && var12 > 100.0D) { double var14 = Math.sqrt(var12) / 40.0D; this.mc.sndManager.func_92070_a(par7Str, (float)par1, (float)par3, (float)par5, par8, par9, (int)Math.round(var14 * 20.0D)); } else { this.mc.sndManager.playSound(par7Str, (float)par1, (float)par3, (float)par5, par8, par9); } } } public void func_92088_a(double par1, double par3, double par5, double par7, double par9, double par11, NBTTagCompound par13NBTTagCompound) { this.mc.effectRenderer.addEffect(new EntityFireworkStarterFX(this, par1, par3, par5, par7, par9, par11, this.mc.effectRenderer, par13NBTTagCompound)); } static Set getEntityList(WorldClient par0WorldClient) { return par0WorldClient.entityList; } static Set getEntitySpawnQueue(WorldClient par0WorldClient) { return par0WorldClient.entitySpawnQueue; } }