package com.bergerkiller.bukkit.common.bases;
import java.util.Random;
import org.bukkit.World;
import org.bukkit.event.world.ChunkPopulateEvent;
import org.bukkit.generator.BlockPopulator;
import com.bergerkiller.bukkit.common.conversion.Conversion;
import com.bergerkiller.bukkit.common.reflection.classes.ChunkProviderServerRef;
import com.bergerkiller.bukkit.common.reflection.classes.WorldServerRef;
import com.bergerkiller.bukkit.common.utils.MathUtil;
import net.minecraft.server.BlockSand;
import net.minecraft.server.Chunk;
import net.minecraft.server.ChunkProviderServer;
import net.minecraft.server.CrashReport;
import net.minecraft.server.CrashReportSystemDetails;
import net.minecraft.server.IChunkLoader;
import net.minecraft.server.IChunkProvider;
import net.minecraft.server.ReportedException;
import net.minecraft.server.WorldServer;
public class ChunkProviderServerBase extends ChunkProviderServer {
public final org.bukkit.World world;
public ChunkProviderServerBase(World world) {
this(WorldServerRef.chunkProviderServer.get(Conversion.toWorldHandle.convert(world)));
}
public ChunkProviderServerBase(Object chunkProviderServer) {
super(getWorld(chunkProviderServer), getLoader(chunkProviderServer), getGenerator(chunkProviderServer));
ChunkProviderServerRef.TEMPLATE.transfer(chunkProviderServer, this);
this.world = super.world.getWorld();
}
private static WorldServer getWorld(Object chunkProviderServer) {
return ((ChunkProviderServer) chunkProviderServer).world;
}
private static IChunkLoader getLoader(Object chunkProviderServer) {
return (IChunkLoader) ChunkProviderServerRef.chunkLoader.get(chunkProviderServer);
}
private static IChunkProvider getGenerator(Object chunkProviderServer) {
return ((ChunkProviderServer) chunkProviderServer).chunkProvider;
}
private void checkGenerator() {
if (this.chunkProvider == null) {
throw new RuntimeException("Chunk provider has no generator set: " + (world == null ? "null" : world.getName()));
}
}
/**
* Reverts the previous creation of this Chunk Provider, creating the default ChunkProviderServer instance
*
* @return ChunkProviderServer instance this base was made from
*/
public Object revert() {
ChunkProviderServer chunkProvider = new ChunkProviderServer(super.world, null, null);
ChunkProviderServerRef.TEMPLATE.transfer(this, chunkProvider);
return chunkProvider;
}
/**
* @deprecated Use {@link #loadBukkitChunk(int, int) loadBukkitChunk(x, z)} instead
*/
@Override
@Deprecated
public Chunk loadChunk(int x, int z) {
return (Chunk) Conversion.toChunkHandle.convert(loadBukkitChunk(x, z));
}
/**
* Called when a chunk is loaded from file
*
* @param x - coordinate of the chunk
* @param z - coordinate of the chunk
* @return the newly loaded Chunk
*/
public org.bukkit.Chunk loadBukkitChunk(int x, int z) {
return Conversion.toChunk.convert(super.loadChunk(x, z));
}
/**
* @deprecated Use {@link #getBukkitChunkAt(int, int, Runnable) getBukkitChunkAt(x, z, taskWhenFinished)} instead
*/
@Override
@Deprecated
public Chunk getChunkAt(int i, int j, Runnable runnable) {
return (Chunk) Conversion.toChunkHandle.convert(getBukkitChunkAt(i, j, runnable));
}
/**
* Called when a chunk is requested from this provider
*
* @param x - coordinate of the chunk
* @param z - coordinate of the chunk
* @param taskWhenFinished to execute when the chunk is successfully obtained
* @return the loaded or obtained Bukkit chunk
*/
public org.bukkit.Chunk getBukkitChunkAt(int x, int z, Runnable taskWhenFinished) {
checkGenerator();
return Conversion.toChunk.convert(super.getChunkAt(x, z, taskWhenFinished));
}
/**
* Called when a Block Populator is populating a chunk during generation
*
* @param chunk to be populated
* @param populator used
* @param random used by the populator
*/
public void onPopulate(org.bukkit.Chunk chunk, BlockPopulator populator, Random random) {
populator.populate(world, random, chunk);
}
/**
* Orders the underlying chunk provider to generate a new chunk
*
* @param x - coordinate of the chunk to generate
* @param z - coordinate of the chunk to generate
* @return generated chunk
*/
public org.bukkit.Chunk generateChunk(int x, int z) {
checkGenerator();
return Conversion.toChunk.convert(this.chunkProvider.getOrCreateChunk(x, z));
}
@Override
public boolean unloadChunks() {
return super.unloadChunks();
}
/**
* @deprecated Use {@link #onPopulate(Chunk, BlockPopulator, Random)} to handle populators instead
*/
@Override
@Deprecated
public void getChunkAt(IChunkProvider ichunkprovider, int i, int j) {
checkGenerator();
Chunk chunk = this.getOrCreateChunk(i, j);
if (!chunk.done) {
chunk.done = true;
this.chunkProvider.getChunkAt(ichunkprovider, i, j);
// CraftBukkit start
BlockSand.instaFall = true;
final Random random = new Random();
random.setSeed(world.getSeed());
long xRand = random.nextLong() / 2L * 2L + 1L;
long zRand = random.nextLong() / 2L * 2L + 1L;
random.setSeed((long) i * xRand + (long) j * zRand ^ world.getSeed());
if (world != null) {
for (BlockPopulator populator : world.getPopulators()) {
onPopulate(chunk.bukkitChunk, populator, random);
}
}
BlockSand.instaFall = false;
super.world.getServer().getPluginManager().callEvent(new ChunkPopulateEvent(chunk.bukkitChunk));
// CraftBukkit end
chunk.e();
}
}
/**
* Handles an error that occurred during chunk generation
*
* @param throwable that was thrown
* @param chunkX coordinate of the chunk
* @param chunkZ coordinate of the chunk
*/
public void handleGeneratorError(Throwable throwable, int chunkX, int chunkZ) {
CrashReport crashreport = CrashReport.a(throwable, "Exception generating new chunk");
CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Chunk to be generated");
crashreportsystemdetails.a("Location", String.format("%d,%d", chunkX, chunkZ));
crashreportsystemdetails.a("Position hash", Long.valueOf(MathUtil.longHashToLong(chunkX, chunkZ)));
crashreportsystemdetails.a("Generator", this.chunkProvider.getName());
throw new ReportedException(crashreport);
}
}