Skip to content

Instantly share code, notes, and snippets.

@boy0001
Created July 2, 2015 06:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save boy0001/e12ff4a80ad06a15ff24 to your computer and use it in GitHub Desktop.
Save boy0001/e12ff4a80ad06a15ff24 to your computer and use it in GitHub Desktop.
PlotGen
package com.boydti.gen;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import org.bukkit.World;
import org.bukkit.block.Biome;
import org.bukkit.generator.BlockPopulator;
import org.bukkit.generator.ChunkGenerator;
public class Gen extends ChunkGenerator {
public final int PLOT_HEIGHT = 64; // Plot height of 64
public final int PLOT_WIDTH = 42; // Plot width of 42
public final int ROAD_WIDTH = 7; // Road width of 7
public final short ROAD_BLOCK = 155; // Quartz
public final short MAIN_BLOCK = 1; // Stone
public final short WALL_BLOCK = 7; // Bedrock
public final short BORDER_BLOCK = 44; // Stone slab
public final short[] FLOOR_BLOCK = new short[] {3, 19}; // Grass and sponge
/**
* I'm using my PseudoRandom class as it's more efficient and we don't need secure randomness
*/
public final PseudoRandom RANDOM = new PseudoRandom();
/**
* This cache maps the indexes of the x,y,z to the chunk blocks
* - It's not necessary but speeds things up
*/
public short[][][] CACHE_I = null;
public short[][][] CACHE_J = null;
// These variables will be calculated based on the configuration above ^
public final int total_width;
public final int road_width_lower;
public final int road_width_upper;
/**
* The base template used for all chunks
*/
private short[][] base_result;
public Gen() {
initCache();
total_width = PLOT_WIDTH + ROAD_WIDTH;
// Calculating the bottom and top road portions (this is for a PlotSquared compatible generator, but you can have any offset you want)
if ((ROAD_WIDTH % 2) == 0) {
road_width_lower = ROAD_WIDTH / 2 - 1;
} else {
road_width_lower = ROAD_WIDTH / 2;
}
road_width_upper = road_width_lower + PLOT_WIDTH + 1;
// Since all plots will have a bedrock base, we can have a partially made template that we use for each chunk
base_result = new short[1 + PLOT_HEIGHT / 16][];
for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {
setBlock(base_result, x, 0, z, (short) 7);
}
}
}
/**
* This method will cache a map the x,y,z coordinates to the chunk blocks (chunks are broken into blocks of 16x16)
*/
public void initCache() {
if (CACHE_I == null) {
CACHE_I = new short[256][16][16];
CACHE_J = new short[256][16][16];
for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {
for (int y = 0; y <= PLOT_HEIGHT; y++) {
short i = (short) (y >> 4);
short j = (short) (((y & 0xF) << 8) | (z << 4) | x);
CACHE_I[y][x][z] = i;
CACHE_J[y][x][z] = j;
}
}
}
}
}
@Override
public List<BlockPopulator> getDefaultPopulators(World world) {
// Here is a good place to set any world settings such as mob spawning:
world.setSpawnFlags(false, false);
world.setAmbientSpawnLimit(0);
world.setAnimalSpawnLimit(0);
world.setMonsterSpawnLimit(0);
world.setWaterAnimalSpawnLimit(0);
// Here we would return our populator if we had one...
return new ArrayList<>();
}
/**
* This part here is where your generation goes
*/
@Override
public short[][] generateExtBlockSections(World world, Random r, int cx, int cz, BiomeGrid biomes) {
short[][] result = new short[1 + PLOT_HEIGHT / 16][];;
try {
// If you have any random elements to your generation, you will want to set the state of the random class
RANDOM.state = pair(cx, cz);
// Copy over our template array which has the bedrock already there
result[0] = new short[4096];
System.arraycopy( base_result[0], 0, result[0], 0, base_result[0].length );
// We want all plots to be the same
// To do this we will need to reduce the coordinates to the same base location
// To get the world coord from a chunk coord we multiply by 16 `cx << 4`
// Then we find the remainder of that `(cx << 4) % total_width`
// We don't want negative numbers though so add the `total_width` if the remainder is less than 0
// We have to do this as the plot size will not necessarily have to be a multiple of 16, and so it won't always align to the chunk
// If the total width is a multiple of 16, you can in fact make some neat optimizations, see PlotSquaredMG source for more info
int bx = (cx << 4) % total_width + (cx < 0 ? total_width : 0);
int bz = (cz << 4) % total_width + (cz < 0 ? total_width : 0);
// This is our main loop where we go over all the columns in the chunk and set the blocks
for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {
biomes.setBiome(x, z, Biome.FOREST);
// Getting the reduced coordinate
int xx = (x + bx);
int zz = (z + bz);
// If it's greater than the total width, we need to reduce it
// Although we reduced the chunk coordinates before, that only means the base coordinate of the chunk is reduced
// The top coordinate could still be outside our desired range
if (xx >= total_width) xx -= total_width;
if (zz >= total_width) zz -= total_width;
// ROAD
if (xx < road_width_lower || zz < road_width_lower || xx > road_width_upper || zz > road_width_upper) {
for (int y = 0; y < PLOT_HEIGHT; y++) setBlock(result, x, y, z, ROAD_BLOCK);
}
// WALL
else if (xx == road_width_lower || zz == road_width_lower || xx == road_width_upper || zz == road_width_upper) {
// Set the wall block
for (int y = 0; y < PLOT_HEIGHT; y++) setBlock(result, x, y, z, WALL_BLOCK);
// Set the border block (on top of the wall)
setBlock(result, x, PLOT_HEIGHT, z, BORDER_BLOCK);
}
// PLOT
else {
// Set the main plot block
for (int y = 0; y < PLOT_HEIGHT - 1; y++) setBlock(result, x, y, z, MAIN_BLOCK);
// Set the plot floor
setBlock(result, x, PLOT_HEIGHT - 1, z, FLOOR_BLOCK);
}
}
}
}
catch (Exception e) {
// Normally if something goes wrong in your code it will fail silently with world generators
// Having this try/catch will help recover the exception
e.printStackTrace();
}
return result;
}
/**
* This simple pairing function is used for the seed for each chunk,
* - This is useful if you want generation to appear random, but be the same each time
* - You could also use a simple hash function like `return x + y * 31` - but this looks fancier
* @param x
* @param y
* @return
*/
public int pair(int x, int y) {
long hash;
if (x >= 0) {
if (y >= 0) {
hash = (x * x) + (3 * x) + (2 * x * y) + y + (y * y) + 2;
} else {
final int y1 = -y;
hash = (x * x) + (3 * x) + (2 * x * y1) + y1 + (y1 * y1) + 1;
}
} else {
final int x1 = -x;
if (y >= 0) {
hash = -((x1 * x1) + (3 * x1) + (2 * x1 * y) + y + (y * y));
} else {
final int y1 = -y;
hash = -((x1 * x1) + (3 * x1) + (2 * x1 * y1) + y1 + (y1 * y1) + 1);
}
}
return (int) (hash % Integer.MAX_VALUE);
}
public void setBlock(final short[][] result, final int x, final int y, final int z, final short blkid) {
if (result[CACHE_I[y][x][z]] == null) {
result[CACHE_I[y][x][z]] = new short[4096];
}
result[CACHE_I[y][x][z]][CACHE_J[y][x][z]] = blkid;
}
public void setBlock(final short[][] result, final int x, final int y, final int z, final short[] blkid) {
if (blkid.length == 1) {
setBlock(result, x, y, z, blkid[0]);
}
short id = blkid[RANDOM.random(blkid.length)];
if (result[CACHE_I[y][x][z]] == null) {
result[CACHE_I[y][x][z]] = new short[4096];
}
result[CACHE_I[y][x][z]][CACHE_J[y][x][z]] = id;
}
}
package com.boydti.gen;
import org.bukkit.generator.ChunkGenerator;
import org.bukkit.plugin.java.JavaPlugin;
public class Main extends JavaPlugin {
@Override
public ChunkGenerator getDefaultWorldGenerator(String worldName, String args) {
// If we want to do anything fancy, we can accept arguments
// e.g. with Multiverse /mv create <world> normal -g <generator>:<args>
// We could then pass these arguments to the generator e.g. if we wanted different blocks for the road
return new Gen();
}
}
package com.boydti.gen;
public class PseudoRandom {
public long state = 1;
public long nextLong() {
final long a = state;
state = xorShift64(a);
return a;
}
public long xorShift64(long a) {
a ^= (a << 21);
a ^= (a >>> 35);
a ^= (a << 4);
return a;
}
public int random(final int n) {
if (n == 1) {
return 0;
}
final long r = ((nextLong() >>> 32) * n) >> 32;
return (int) r;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment