From fedcbdf256e29df3300639e4a9fa97fd04cd4108 Mon Sep 17 00:00:00 2001
From: Dinnerbone <dinnerbone@dinnerbone.com>
Date: Mon, 6 Jun 2011 14:52:02 +0100
Subject: [PATCH] Implemented custom chunk generators and block populators

---
 .../minecraft/server/ChunkProviderServer.java | 16 ++++
 .../net/minecraft/server/MinecraftServer.java |  4 +-
 .../server/SecondaryWorldServer.java          |  6 +-
 src/main/java/net/minecraft/server/World.java | 21 +++++-
 .../net/minecraft/server/WorldServer.java     | 29 +++++++-
 .../org/bukkit/craftbukkit/CraftServer.java   | 19 ++++-
 .../org/bukkit/craftbukkit/CraftWorld.java    | 15 +++-
 .../generator/CustomChunkGenerator.java       | 73 +++++++++++++++++++
 .../generator/InternalChunkGenerator.java     |  9 +++
 .../generator/NetherChunkGenerator.java       | 12 +++
 .../generator/NormalChunkGenerator.java       | 27 +++++++
 .../generator/SkyLandsChunkGenerator.java     | 12 +++
 12 files changed, 231 insertions(+), 12 deletions(-)
 create mode 100644 src/main/java/org/bukkit/craftbukkit/generator/CustomChunkGenerator.java
 create mode 100644 src/main/java/org/bukkit/craftbukkit/generator/InternalChunkGenerator.java
 create mode 100644 src/main/java/org/bukkit/craftbukkit/generator/NetherChunkGenerator.java
 create mode 100644 src/main/java/org/bukkit/craftbukkit/generator/NormalChunkGenerator.java
 create mode 100644 src/main/java/org/bukkit/craftbukkit/generator/SkyLandsChunkGenerator.java

diff --git a/src/main/java/net/minecraft/server/ChunkProviderServer.java b/src/main/java/net/minecraft/server/ChunkProviderServer.java
index 8d790184f4..40a2b91deb 100644
--- a/src/main/java/net/minecraft/server/ChunkProviderServer.java
+++ b/src/main/java/net/minecraft/server/ChunkProviderServer.java
@@ -6,6 +6,7 @@ import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Random;
 import java.util.Set;
 
 // CraftBukkit start
@@ -16,6 +17,7 @@ import org.bukkit.craftbukkit.util.LongHashset;
 import org.bukkit.craftbukkit.util.LongHashtable;
 import org.bukkit.event.world.ChunkLoadEvent;
 import org.bukkit.event.world.ChunkUnloadEvent;
+import org.bukkit.generator.BlockPopulator;
 // CraftBukkit end
 
 public class ChunkProviderServer implements IChunkProvider {
@@ -172,6 +174,20 @@ public class ChunkProviderServer implements IChunkProvider {
             chunk.done = true;
             if (this.chunkProvider != null) {
                 this.chunkProvider.getChunkAt(ichunkprovider, i, j);
+
+                // Craftbukkit start
+                BlockSand.a = true;
+                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());
+                for (BlockPopulator populator : world.getWorld().getPopulators()) {
+                    populator.populate(world.getWorld(), random, chunk.bukkitChunk);
+                }
+                BlockSand.a = false;
+                // Craftbukkit end
+
                 chunk.f();
             }
         }
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
index cfd527efa6..98221345b7 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -166,10 +166,10 @@ public class MinecraftServer implements Runnable, ICommandListener {
             int dimension = j == 0 ? 0 : -1;
 
             if (j == 0) {
-                world = new WorldServer(this, new ServerNBTManager(new File("."), s, true), s, dimension, i, org.bukkit.World.Environment.getEnvironment(dimension)); // CraftBukkit
+                world = new WorldServer(this, new ServerNBTManager(new File("."), s, true), s, dimension, i, org.bukkit.World.Environment.getEnvironment(dimension), null); // CraftBukkit
             } else {
                 String name = s + "_" + Environment.getEnvironment(dimension).toString().toLowerCase();
-                world = new SecondaryWorldServer(this, new ServerNBTManager(new File("."), name, true), name, dimension, i, worlds.get(0), org.bukkit.World.Environment.getEnvironment(dimension)); // CraftBukkit
+                world = new SecondaryWorldServer(this, new ServerNBTManager(new File("."), name, true), name, dimension, i, worlds.get(0), org.bukkit.World.Environment.getEnvironment(dimension), null); // CraftBukkit
             }
 
             world.tracker = new EntityTracker(this, dimension);
diff --git a/src/main/java/net/minecraft/server/SecondaryWorldServer.java b/src/main/java/net/minecraft/server/SecondaryWorldServer.java
index 06159cda14..d36af3f826 100644
--- a/src/main/java/net/minecraft/server/SecondaryWorldServer.java
+++ b/src/main/java/net/minecraft/server/SecondaryWorldServer.java
@@ -1,9 +1,11 @@
 package net.minecraft.server;
 
+import org.bukkit.generator.ChunkGenerator;
+
 public class SecondaryWorldServer extends WorldServer {
     // CraftBukkit start
-    public SecondaryWorldServer(MinecraftServer minecraftserver, IDataManager idatamanager, String s, int i, long j, WorldServer worldserver, org.bukkit.World.Environment env) {
-        super(minecraftserver, idatamanager, s, i, j, env);
+    public SecondaryWorldServer(MinecraftServer minecraftserver, IDataManager idatamanager, String s, int i, long j, WorldServer worldserver, org.bukkit.World.Environment env, ChunkGenerator gen) {
+        super(minecraftserver, idatamanager, s, i, j, env, gen);
         // CraftBukkit end
         this.z = worldserver.z;
     }
diff --git a/src/main/java/net/minecraft/server/World.java b/src/main/java/net/minecraft/server/World.java
index a5f954b2db..259ccdcfe6 100644
--- a/src/main/java/net/minecraft/server/World.java
+++ b/src/main/java/net/minecraft/server/World.java
@@ -9,6 +9,7 @@ import java.util.Set;
 import java.util.TreeSet;
 
 // CraftBukkit start
+import org.bukkit.generator.ChunkGenerator;
 import org.bukkit.craftbukkit.CraftServer;
 import org.bukkit.craftbukkit.CraftWorld;
 import org.bukkit.craftbukkit.event.CraftEventFactory;
@@ -65,12 +66,17 @@ public class World implements IBlockAccess {
     private int O;
     private List P;
     public boolean isStatic;
+    public ChunkGenerator generator; // Craftbukkit
 
     public WorldChunkManager getWorldChunkManager() {
         return this.worldProvider.b;
     }
 
-    public World(IDataManager idatamanager, String s, long i, WorldProvider worldprovider) {
+    // Craftbukkit start - changed signature
+    public World(IDataManager idatamanager, String s, long i, WorldProvider worldprovider, ChunkGenerator gen) {
+        this.generator = gen;
+    // Craftbukkit end
+
         this.O = this.random.nextInt(12000);
         this.P = new ArrayList();
         this.isStatic = false;
@@ -118,7 +124,8 @@ public class World implements IBlockAccess {
 
         int j;
 
-        for (j = 0; !this.worldProvider.a(i, j); j += this.random.nextInt(64) - this.random.nextInt(64)) {
+        // Craftbukkit
+        for (j = 0; !canSpawn(i, j); j += this.random.nextInt(64) - this.random.nextInt(64)) {
             i += this.random.nextInt(64) - this.random.nextInt(64);
         }
 
@@ -126,6 +133,16 @@ public class World implements IBlockAccess {
         this.isLoading = false;
     }
 
+    // Craftbukkit start
+    private boolean canSpawn(int x, int z) {
+        if (generator != null) {
+            return this.generator.canSpawn(((WorldServer)this).getWorld(), x, z);
+        } else {
+            return this.worldProvider.a(x, z);
+        }
+    }
+    // Craftbukkit end
+
     public int a(int i, int j) {
         int k;
 
diff --git a/src/main/java/net/minecraft/server/WorldServer.java b/src/main/java/net/minecraft/server/WorldServer.java
index cc933aeb50..14006ee8b6 100644
--- a/src/main/java/net/minecraft/server/WorldServer.java
+++ b/src/main/java/net/minecraft/server/WorldServer.java
@@ -5,8 +5,14 @@ import java.util.List;
 
 // CraftBukkit start
 import org.bukkit.BlockChangeDelegate;
+import org.bukkit.generator.ChunkGenerator;
 import org.bukkit.craftbukkit.CraftServer;
 import org.bukkit.craftbukkit.CraftWorld;
+import org.bukkit.craftbukkit.generator.CustomChunkGenerator;
+import org.bukkit.craftbukkit.generator.InternalChunkGenerator;
+import org.bukkit.craftbukkit.generator.NetherChunkGenerator;
+import org.bukkit.craftbukkit.generator.NormalChunkGenerator;
+import org.bukkit.craftbukkit.generator.SkyLandsChunkGenerator;
 import org.bukkit.event.weather.LightningStrikeEvent;
 
 public class WorldServer extends World implements BlockChangeDelegate {
@@ -19,13 +25,13 @@ public class WorldServer extends World implements BlockChangeDelegate {
     private EntityList G = new EntityList();
 
     // CraftBukkit start - change signature
-    public WorldServer(MinecraftServer minecraftserver, IDataManager idatamanager, String s, int i, long j, org.bukkit.World.Environment env) {
-        super(idatamanager, s, j, WorldProvider.a(env.getId()));
+    public WorldServer(MinecraftServer minecraftserver, IDataManager idatamanager, String s, int i, long j, org.bukkit.World.Environment env, ChunkGenerator gen) {
+        super(idatamanager, s, j, WorldProvider.a(env.getId()), gen);
         this.server = minecraftserver;
 
         this.dimension = i;
         this.cserver = minecraftserver.server;
-        this.world = new CraftWorld(this);
+        this.world = new CraftWorld(this, gen);
         this.pvpMode = minecraftserver.pvpMode;
         this.manager = new PlayerManager(minecraftserver, dimension, minecraftserver.propertyManager.getInt("view-distance", 10));
     }
@@ -64,7 +70,22 @@ public class WorldServer extends World implements BlockChangeDelegate {
     protected IChunkProvider b() {
         IChunkLoader ichunkloader = this.w.a(this.worldProvider);
 
-        this.chunkProviderServer = new ChunkProviderServer(this, ichunkloader, this.worldProvider.b());
+        // Craftbukkit start
+        InternalChunkGenerator gen;
+
+        if (this.generator != null) {
+            gen = new CustomChunkGenerator(this, this.getSeed(), this.generator);
+        } else if (this.worldProvider instanceof WorldProviderHell) {
+            gen = new NetherChunkGenerator(this, this.getSeed());
+        } else if (this.worldProvider instanceof WorldProviderSky) {
+            gen = new SkyLandsChunkGenerator(this, this.getSeed());
+        } else {
+            gen = new NormalChunkGenerator(this, this.getSeed());
+        }
+
+        this.chunkProviderServer = new ChunkProviderServer(this, ichunkloader, gen);
+        // Craftbukkit end
+
         return this.chunkProviderServer;
     }
 
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
index dd35d2453a..7696462131 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -1,10 +1,12 @@
 package org.bukkit.craftbukkit;
 
+import org.bukkit.generator.ChunkGenerator;
 import com.avaje.ebean.config.DataSourceConfig;
 import com.avaje.ebean.config.ServerConfig;
 import com.avaje.ebean.config.dbplatform.SQLitePlatform;
 import com.avaje.ebeaninternal.server.lib.sql.TransactionIsolation;
 import net.minecraft.server.IWorldAccess;
+import org.bukkit.World.Environment;
 import org.bukkit.command.*;
 import org.bukkit.entity.Player;
 import org.bukkit.event.world.WorldLoadEvent;
@@ -54,6 +56,7 @@ import org.bukkit.craftbukkit.command.ServerCommandListener;
 import org.bukkit.scheduler.BukkitWorker;
 import org.bukkit.scheduler.BukkitTask;
 import org.bukkit.craftbukkit.scheduler.CraftScheduler;
+import org.bukkit.event.world.WorldInitEvent;
 import org.bukkit.util.config.Configuration;
 
 public final class CraftServer implements Server {
@@ -349,6 +352,14 @@ public final class CraftServer implements Server {
     }
 
     public World createWorld(String name, World.Environment environment, long seed) {
+        return createWorld(name, environment, seed, null);
+    }
+
+    public World createWorld(String name, Environment environment, ChunkGenerator generator) {
+        return createWorld(name, environment, (new Random()).nextLong(), generator);
+    }
+
+    public World createWorld(String name, Environment environment, long seed, ChunkGenerator generator) {
         File folder = new File(name);
         World world = getWorld(name);
 
@@ -367,7 +378,7 @@ public final class CraftServer implements Server {
         }
 
         int dimension = 200 + console.worlds.size();
-        WorldServer internal = new WorldServer(console, new ServerNBTManager(new File("."), name, true), name, dimension, seed, environment);
+        WorldServer internal = new WorldServer(console, new ServerNBTManager(new File("."), name, true), name, dimension, seed, environment, generator);
         internal.z = console.worlds.get(0).z;
 
         internal.tracker = new EntityTracker(console, dimension);
@@ -376,6 +387,12 @@ public final class CraftServer implements Server {
         internal.setSpawnFlags(true, true);
         console.worlds.add(internal);
 
+        if (generator != null) {
+            internal.getWorld().getPopulators().addAll(generator.getDefaultPopulators(internal.getWorld()));
+        }
+
+        pluginManager.callEvent(new WorldInitEvent(internal.getWorld()));
+
         short short1 = 196;
         long i = System.currentTimeMillis();
         for (int j = -short1; j <= short1; j += 16) {
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
index a942e743b2..33c7a95530 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
@@ -20,9 +20,11 @@ import org.bukkit.Chunk;
 import org.bukkit.inventory.ItemStack;
 import org.bukkit.util.Vector;
 import org.bukkit.BlockChangeDelegate;
+import org.bukkit.generator.ChunkGenerator;
 import org.bukkit.Location;
 import org.bukkit.TreeType;
 import org.bukkit.World;
+import org.bukkit.generator.BlockPopulator;
 
 public class CraftWorld implements World {
     private final WorldServer world;
@@ -30,13 +32,16 @@ public class CraftWorld implements World {
     private final CraftServer server;
     private final ChunkProviderServer provider;
     private HashMap<Integer,CraftChunk> unloadedChunks = new HashMap<Integer, CraftChunk>();
+    private final ChunkGenerator generator;
+    private final List<BlockPopulator> populators = new ArrayList<BlockPopulator>();
 
     private static final Random rand = new Random();
 
-    public CraftWorld(WorldServer world) {
+    public CraftWorld(WorldServer world, ChunkGenerator gen) {
         this.world = world;
         this.server = world.getServer();
         this.provider = world.chunkProviderServer;
+        this.generator = gen;
         
         environment = Environment.getEnvironment(world.worldProvider.dimension);
 
@@ -463,6 +468,14 @@ public class CraftWorld implements World {
         return getChunkAt(location.getBlockX() >> 4, location.getBlockZ() >> 4);
     }
 
+    public ChunkGenerator getGenerator() {
+        return generator;
+    }
+
+    public List<BlockPopulator> getPopulators() {
+        return populators;
+    }
+
     private final class ChunkCoordinate {
         public final int x;
         public final int z;
diff --git a/src/main/java/org/bukkit/craftbukkit/generator/CustomChunkGenerator.java b/src/main/java/org/bukkit/craftbukkit/generator/CustomChunkGenerator.java
new file mode 100644
index 0000000000..3103c5e9b9
--- /dev/null
+++ b/src/main/java/org/bukkit/craftbukkit/generator/CustomChunkGenerator.java
@@ -0,0 +1,73 @@
+package org.bukkit.craftbukkit.generator;
+
+import java.util.List;
+import java.util.Random;
+import net.minecraft.server.Chunk;
+import net.minecraft.server.IChunkProvider;
+import net.minecraft.server.IProgressUpdate;
+import net.minecraft.server.World;
+import net.minecraft.server.WorldServer;
+import org.bukkit.generator.BlockPopulator;
+import org.bukkit.generator.ChunkGenerator;
+
+public class CustomChunkGenerator implements InternalChunkGenerator {
+    private final ChunkGenerator generator;
+    private final WorldServer world;
+    private final long seed;
+    private final Random random;
+
+    public CustomChunkGenerator(World world, long seed, ChunkGenerator generator) {
+        this.world = (WorldServer)world;
+        this.seed = seed;
+        this.generator = generator;
+
+        this.random = new Random(seed);
+    }
+
+    public boolean isChunkLoaded(int x, int z) {
+        return true;
+    }
+
+    public Chunk getOrCreateChunk(int x, int z) {
+        random.setSeed((long)x * 341873128712L + (long)z * 132897987541L);
+        byte[] types = generator.generate(world.getWorld(), random, x, z);
+
+        Chunk chunk = new Chunk(world, types, x, z);
+
+        chunk.b();
+
+        return chunk;
+    }
+
+    public void getChunkAt(IChunkProvider icp, int i, int i1) {
+        // Nothing!
+    }
+
+    public boolean saveChunks(boolean bln, IProgressUpdate ipu) {
+        return true;
+    }
+
+    public boolean unloadChunks() {
+        return false;
+    }
+
+    public boolean b() {
+        return true;
+    }
+
+    public byte[] generate(org.bukkit.World world, Random random, int x, int z) {
+        return generator.generate(world, random, x, z);
+    }
+
+    public Chunk getChunkAt(int x, int z) {
+        return getOrCreateChunk(x, z);
+    }
+
+    public boolean canSpawn(org.bukkit.World world, int x, int z) {
+        return generator.canSpawn(world, x, z);
+    }
+
+    public List<BlockPopulator> getDefaultPopulators(org.bukkit.World world) {
+        return generator.getDefaultPopulators(world);
+    }
+}
diff --git a/src/main/java/org/bukkit/craftbukkit/generator/InternalChunkGenerator.java b/src/main/java/org/bukkit/craftbukkit/generator/InternalChunkGenerator.java
new file mode 100644
index 0000000000..d98c5e6672
--- /dev/null
+++ b/src/main/java/org/bukkit/craftbukkit/generator/InternalChunkGenerator.java
@@ -0,0 +1,9 @@
+
+package org.bukkit.craftbukkit.generator;
+
+import net.minecraft.server.IChunkProvider;
+import org.bukkit.generator.ChunkGenerator;
+
+public interface InternalChunkGenerator extends ChunkGenerator, IChunkProvider {
+
+}
diff --git a/src/main/java/org/bukkit/craftbukkit/generator/NetherChunkGenerator.java b/src/main/java/org/bukkit/craftbukkit/generator/NetherChunkGenerator.java
new file mode 100644
index 0000000000..59d7bbec55
--- /dev/null
+++ b/src/main/java/org/bukkit/craftbukkit/generator/NetherChunkGenerator.java
@@ -0,0 +1,12 @@
+package org.bukkit.craftbukkit.generator;
+
+import net.minecraft.server.World;
+
+/**
+ * This class is useless. Just fyi.
+ */
+public class NetherChunkGenerator extends NormalChunkGenerator {
+    public NetherChunkGenerator(World world, long seed) {
+        super(world, seed);
+    }
+}
diff --git a/src/main/java/org/bukkit/craftbukkit/generator/NormalChunkGenerator.java b/src/main/java/org/bukkit/craftbukkit/generator/NormalChunkGenerator.java
new file mode 100644
index 0000000000..2e83fa5c3e
--- /dev/null
+++ b/src/main/java/org/bukkit/craftbukkit/generator/NormalChunkGenerator.java
@@ -0,0 +1,27 @@
+package org.bukkit.craftbukkit.generator;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+import net.minecraft.server.ChunkProviderGenerate;
+import net.minecraft.server.World;
+import org.bukkit.craftbukkit.CraftWorld;
+import org.bukkit.generator.BlockPopulator;
+
+public class NormalChunkGenerator extends ChunkProviderGenerate implements InternalChunkGenerator {
+    public NormalChunkGenerator(World world, long seed) {
+        super(world, seed);
+    }
+
+    public byte[] generate(org.bukkit.World world, Random random, int x, int z) {
+        throw new UnsupportedOperationException("Not supported.");
+    }
+
+    public boolean canSpawn(org.bukkit.World world, int x, int z) {
+        return ((CraftWorld)world).getHandle().worldProvider.a(x, z);
+    }
+
+    public List<BlockPopulator> getDefaultPopulators(org.bukkit.World world) {
+        return new ArrayList<BlockPopulator>();
+    }
+}
diff --git a/src/main/java/org/bukkit/craftbukkit/generator/SkyLandsChunkGenerator.java b/src/main/java/org/bukkit/craftbukkit/generator/SkyLandsChunkGenerator.java
new file mode 100644
index 0000000000..e327996943
--- /dev/null
+++ b/src/main/java/org/bukkit/craftbukkit/generator/SkyLandsChunkGenerator.java
@@ -0,0 +1,12 @@
+package org.bukkit.craftbukkit.generator;
+
+import net.minecraft.server.World;
+
+/**
+ * This class is useless. Just fyi.
+ */
+public class SkyLandsChunkGenerator extends NormalChunkGenerator {
+    public SkyLandsChunkGenerator(World world, long seed) {
+        super(world, seed);
+    }
+}