Custom biomes and vanilla biomes mixed

This covers the overworld. Next is to do the nether.
This commit is contained in:
tastybento 2022-12-21 11:09:16 -08:00
parent c7880b355d
commit 4a675e0433
10 changed files with 390 additions and 314 deletions

View File

@ -22,10 +22,12 @@ import world.bentobox.bentobox.api.flags.Flag;
import world.bentobox.bentobox.api.flags.Flag.Mode;
import world.bentobox.bentobox.api.flags.Flag.Type;
import world.bentobox.bentobox.managers.RanksManager;
import world.bentobox.boxed.generators.AbstractBoxedChunkGenerator;
import world.bentobox.boxed.generators.BoxedBiomeGenerator;
import world.bentobox.boxed.generators.BoxedBlockPopulator;
import world.bentobox.boxed.generators.BoxedChunkGenerator;
import world.bentobox.boxed.generators.BoxedSeedChunkGenerator;
import world.bentobox.boxed.generators.SeedBiomeGenerator;
import world.bentobox.boxed.listeners.AdvancementListener;
import world.bentobox.boxed.listeners.EnderPearlListener;
@ -48,13 +50,15 @@ public class Boxed extends GameModeAddon {
private static final String SEED = "seed";
private static final String NETHER = "_nether";
private static final String THE_END = "_the_end";
private static final String BASE = "_base";
// Settings
private Settings settings;
private BoxedChunkGenerator chunkGenerator;
private AbstractBoxedChunkGenerator chunkGenerator;
private final Config<Settings> configObject = new Config<>(this, Settings.class);
private AdvancementsManager advManager;
private BoxedChunkGenerator netherChunkGenerator;
private AbstractBoxedChunkGenerator netherChunkGenerator;
private World baseWorld;
private World seedWorld;
private World seedWorldNether;
//private World seedWorldEnd;
@ -146,22 +150,70 @@ public class Boxed extends GameModeAddon {
@Override
public void createWorlds() {
// Create seed world
String worldName = settings.getWorldName().toLowerCase();
// Create overworld
createOverWorld(worldName);
// Make the nether if it does not exist
if (settings.isNetherGenerate()) {
createNether(worldName);
}
/*
// Make the end if it does not exist
if (settings.isEndGenerate()) {
//TODO
*/
}
private void createNether(String worldName) {
log("Creating Boxed Seed Nether world ...");
seedWorldNether = WorldCreator
.name(SEED + NETHER)
.generator(new BoxedSeedChunkGenerator(this, Environment.NETHER))
.environment(Environment.NETHER)
.seed(getSettings().getSeed())
.createWorld();
seedWorldNether.setDifficulty(Difficulty.EASY); // No damage wanted in this world.
copyChunks(seedWorldNether, this.netherChunkGenerator);
if (getServer().getWorld(worldName + NETHER) == null) {
log("Creating Boxed's Nether...");
}
netherWorld = getWorld(worldName, World.Environment.NETHER);
}
private void createOverWorld(String worldName) {
// Create vanilla seed world
log("Creating Boxed Seed world ...");
// This creates a vanilla base world with biomes
AbstractBoxedChunkGenerator seedBaseGen = new BoxedSeedChunkGenerator(this, Environment.NORMAL);
baseWorld = WorldCreator
.name(SEED+BASE)
.generator(seedBaseGen)
.environment(Environment.NORMAL)
.seed(getSettings().getSeed())
.createWorld();
baseWorld.setDifficulty(Difficulty.PEACEFUL);
baseWorld.setSpawnLocation(settings.getSeedX(), 64, settings.getSeedZ());
copyChunks(baseWorld, seedBaseGen);
// Create seed world
// This copies a base world with custom biomes
log("Creating Boxed Biomed world ...");
seedWorld = WorldCreator
.name(SEED)
.generator(new BoxedSeedChunkGenerator(this, Environment.NORMAL))
.generator(new BoxedSeedChunkGenerator(this, Environment.NORMAL, new SeedBiomeGenerator(this, seedBaseGen)))
.environment(Environment.NORMAL)
.seed(getSettings().getSeed())
.createWorld();
seedWorld.setDifficulty(Difficulty.EASY);
copyChunks(seedWorld);
seedWorld.setSpawnLocation(settings.getSeedX(), 64, settings.getSeedZ());
copyChunks(seedWorld, chunkGenerator);
// Unload seed world
//Bukkit.getServer().unloadWorld("seed", false);
String worldName = settings.getWorldName().toLowerCase();
if (getServer().getWorld(worldName) == null) {
log("Creating Boxed world ...");
@ -170,70 +222,20 @@ public class Boxed extends GameModeAddon {
// Create the world if it does not exist
islandWorld = getWorld(worldName, World.Environment.NORMAL);
// Make the nether if it does not exist
if (settings.isNetherGenerate()) {
log("Creating Boxed Seed Nether world ...");
// Copy regions
/*
boolean newWorld = Bukkit.getWorld(SEED + NETHER) == null;
if (newWorld) {
// New world
File root = new File(getDataFolder(), "../../../..");
BentoBox.getInstance().logDebug("Absolute path " + root.getAbsolutePath());
this.saveResource("worlds/seed_nether/DIM-1/region/r.18.18.mca", root, false, false);
this.saveResource("worlds/seed_nether/DIM-1/region/r.18.19.mca", root, false, false);
this.saveResource("worlds/seed_nether/DIM-1/region/r.18.20.mca", root, false, false);
this.saveResource("worlds/seed_nether/DIM-1/region/r.19.18.mca", root, false, false);
this.saveResource("worlds/seed_nether/DIM-1/region/r.19.19.mca", root, false, false);
this.saveResource("worlds/seed_nether/DIM-1/region/r.19.20.mca", root, false, false);
this.saveResource("worlds/seed_nether/DIM-1/region/r.20.18.mca", root, false, false);
this.saveResource("worlds/seed_nether/DIM-1/region/r.20.19.mca", root, false, false);
this.saveResource("worlds/seed_nether/DIM-1/region/r.20.20.mca", root, false, false);
}*/
seedWorldNether = WorldCreator
.name(SEED + NETHER)
.generator(new BoxedSeedChunkGenerator(this, Environment.NETHER))
.environment(Environment.NETHER)
.seed(getSettings().getSeed())
.createWorld();
seedWorldNether.setDifficulty(Difficulty.EASY); // No damage wanted in this world.
copyChunks(seedWorldNether);
if (getServer().getWorld(worldName + NETHER) == null) {
log("Creating Boxed's Nether...");
}
netherWorld = getWorld(worldName, World.Environment.NETHER);
}
/*
// Make the end if it does not exist
if (settings.isEndGenerate()) {
if (getServer().getWorld(worldName + THE_END) == null) {
log("Creating Boxed's End World...");
}
endWorld = settings.isEndIslands() ? getWorld(worldName, World.Environment.THE_END) : getWorld(worldName, World.Environment.THE_END);
}
*/
}
/**
* Copies chunks from the seed world so they can be pasted in the game world
* @param seedWorld - source world
* @param world - source world
* @param gen - generator to store the chunks
*/
private void copyChunks(World seedWorld) {
BoxedChunkGenerator gen;
private void copyChunks(World world, AbstractBoxedChunkGenerator gen) {
int startX = 0;
int startZ = 0;
if (seedWorld.getEnvironment().equals(Environment.NORMAL)) {
gen = chunkGenerator;
if (world.getEnvironment().equals(Environment.NORMAL)) {
startX = this.settings.getSeedX() >> 4;
startZ = this.settings.getSeedZ() >> 4;
} else {
gen = netherChunkGenerator;
startX = this.settings.getNetherSeedX() >> 4;
startZ = this.settings.getNetherSeedZ() >> 4;
}
@ -245,12 +247,12 @@ public class Boxed extends GameModeAddon {
int last = 0;
for (int x = -size; x <= size; x ++) {
for (int z = -size; z <= size; z++) {
gen.setChunk(x, z, seedWorld.getChunkAt(startX + x, startZ + z));
gen.setChunk(x, z, world.getChunkAt(startX + x, startZ + z));
count++;
int p = (int) (count / percent * 100);
if (p % 10 == 0 && p != last) {
last = p;
this.log("Storing seed chunks for " + seedWorld.getEnvironment() + " " + p + "% done");
this.log("Storing seed chunks for " + world.getEnvironment() + " " + p + "% done");
}
}
@ -262,7 +264,7 @@ public class Boxed extends GameModeAddon {
* @param env - nether, normal, or end
* @return the chunkGenerator for the environment
*/
public BoxedChunkGenerator getChunkGenerator(Environment env) {
public AbstractBoxedChunkGenerator getChunkGenerator(Environment env) {
if (env.equals(Environment.NORMAL)) {
return chunkGenerator;
}

View File

@ -83,11 +83,10 @@ public abstract class AbstractBoxedBiomeProvider extends BiomeProvider {
@Override
public Biome getBiome(WorldInfo worldInfo, int x, int y, int z) {
int chunkX = (int)((double)x/16);
int chunkZ = (int)((double)z/16);
int size = (int)(dist / 16D); // Convert to chunk
chunkX = BoxedChunkGenerator.repeatCalc(chunkX, size);
chunkZ = BoxedChunkGenerator.repeatCalc(chunkZ, size);
int chunkX = x >> 4;
int chunkZ = z >> 4;
chunkX = BoxedChunkGenerator.repeatCalc(chunkX);
chunkZ = BoxedChunkGenerator.repeatCalc(chunkZ);
ChunkSnapshot c = addon.getChunkGenerator(worldInfo.getEnvironment()).getChunk(chunkX, chunkZ);
if (c != null) {

View File

@ -0,0 +1,182 @@
package world.bentobox.boxed.generators;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Random;
import org.bukkit.Chunk;
import org.bukkit.ChunkSnapshot;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Banner;
import org.bukkit.block.Block;
import org.bukkit.block.BlockState;
import org.bukkit.block.CreatureSpawner;
import org.bukkit.block.Sign;
import org.bukkit.entity.AbstractHorse;
import org.bukkit.entity.Ageable;
import org.bukkit.entity.ChestedHorse;
import org.bukkit.entity.Horse;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.entity.Tameable;
import org.bukkit.entity.Villager;
import org.bukkit.generator.BiomeProvider;
import org.bukkit.generator.BlockPopulator;
import org.bukkit.generator.ChunkGenerator;
import org.bukkit.generator.WorldInfo;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.ItemStack;
import org.bukkit.material.Attachable;
import org.bukkit.material.Colorable;
import org.bukkit.util.Vector;
import world.bentobox.bentobox.blueprints.dataobjects.BlueprintBlock;
import world.bentobox.bentobox.blueprints.dataobjects.BlueprintCreatureSpawner;
import world.bentobox.bentobox.blueprints.dataobjects.BlueprintEntity;
import world.bentobox.bentobox.util.Pair;
import world.bentobox.boxed.Boxed;
/**
* Chunk generator for all environments
* @author tastybento
*
*/
public abstract class AbstractBoxedChunkGenerator extends ChunkGenerator {
protected final Boxed addon;
protected static int size;
protected Map<Pair<Integer, Integer>, ChunkStore> chunks = new HashMap<>();
public record ChunkStore(ChunkSnapshot snapshot, List<EntityData> bpEnts, List<ChestData> chests) {};
public record EntityData(Vector relativeLoc, BlueprintEntity entity) {};
public record ChestData(Vector relativeLoc, BlueprintBlock chest) {};
//private final WorldRef wordRefNether;
public AbstractBoxedChunkGenerator(Boxed addon) {
this.addon = addon;
size = (int)(addon.getSettings().getIslandDistance() / 16D); // Size is chunks
}
/**
* Save a chunk
* @param z - chunk z coord
* @param x - chunk x coord
* @param chunk the chunk to set
*/
public void setChunk(int x, int z, Chunk chunk) {
chunks.put(new Pair<>(x, z), new ChunkStore(chunk.getChunkSnapshot(false, true, false), getEnts(chunk), getChests(chunk)));
}
protected abstract List<EntityData> getEnts(Chunk chunk);
protected abstract List<ChestData> getChests(Chunk chunk);
/**
* @param x chunk x
* @param z chunk z
* @return chunk snapshot or null if there is none
*/
public ChunkSnapshot getChunk(int x, int z) {
return chunks.get(new Pair<>(x, z)).snapshot;
}
@Override
public boolean canSpawn(World world, int x, int z)
{
return true;
}
@Override
public void generateNoise(WorldInfo worldInfo, Random r, int chunkX, int chunkZ, ChunkData cd) {
int height = worldInfo.getMaxHeight();
int minY = worldInfo.getMinHeight();
int xx = repeatCalc(chunkX);
int zz = repeatCalc(chunkZ);
Pair<Integer, Integer> coords = new Pair<>(xx, zz);
if (!chunks.containsKey(coords)) {
// This should never be needed because islands should abut each other
cd.setRegion(0, minY, 0, 16, 0, 16, Material.WATER);
return;
}
// Copy the chunk
ChunkSnapshot chunk = chunks.get(coords).snapshot;
copyChunkVerbatim(cd, chunk, minY, height);
}
private void copyChunkVerbatim(ChunkData cd, ChunkSnapshot chunk, int minY, int height) {
for (int x = 0; x < 16; x ++) {
for (int z = 0; z < 16; z++) {
for (int y = minY; y < height; y++) {
cd.setBlock(x, y, z, chunk.getBlockData(x, y, z));
}
}
}
}
/**
* Calculates the repeating value for a given size
* @param chunkCoord chunk coord
* @return mapped chunk coord
*/
public static int repeatCalc(int chunkCoord) {
int xx;
if (chunkCoord > 0) {
xx = Math.floorMod(chunkCoord + size, size*2) - size;
} else {
xx = Math.floorMod(chunkCoord - size, -size*2) + size;
}
return xx;
}
/**
* @return the chunks
*/
public Map<Pair<Integer, Integer>, ChunkStore> getChunks() {
return chunks;
}
@Override
public boolean shouldGenerateNoise() {
return false;
}
@Override
public boolean shouldGenerateSurface() {
return false;
}
@Override
public boolean shouldGenerateCaves() {
return false;
//return this.addon.getSettings().isGenerateCaves();
}
@Override
public boolean shouldGenerateDecorations() {
return false;
//return this.addon.getSettings().isGenerateDecorations();
}
@Override
public boolean shouldGenerateMobs() {
return this.addon.getSettings().isGenerateMobs();
}
@Override
public boolean shouldGenerateStructures() {
return false;
//return this.addon.getSettings().isAllowStructures();
}
}

View File

@ -33,9 +33,8 @@ public abstract class AbstractCopyBiomeProvider extends BiomeProvider {
public Biome getBiome(WorldInfo worldInfo, int x, int y, int z) {
int chunkX = (int)((double)x/16);
int chunkZ = (int)((double)z/16);
int size = (int)(dist / 16D); // Convert to chunk
chunkX = BoxedChunkGenerator.repeatCalc(chunkX, size);
chunkZ = BoxedChunkGenerator.repeatCalc(chunkZ, size);
chunkX = BoxedChunkGenerator.repeatCalc(chunkX);
chunkZ = BoxedChunkGenerator.repeatCalc(chunkZ);
ChunkSnapshot c = addon.getChunkGenerator(worldInfo.getEnvironment()).getChunk(chunkX, chunkZ);
if (c != null) {

View File

@ -4,13 +4,16 @@ import java.io.File;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.SortedMap;
import java.util.TreeMap;
import org.bukkit.ChunkSnapshot;
import org.bukkit.World.Environment;
import org.bukkit.block.Biome;
import org.bukkit.block.BlockFace;
@ -18,9 +21,13 @@ import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.generator.BiomeProvider;
import org.bukkit.generator.WorldInfo;
import org.bukkit.util.Vector;
import org.eclipse.jdt.annotation.NonNull;
import com.google.common.base.Enums;
import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.util.Pair;
import world.bentobox.bentobox.util.Util;
import world.bentobox.boxed.Boxed;
/**
@ -31,6 +38,7 @@ import world.bentobox.boxed.Boxed;
public abstract class AbstractSeedBiomeProvider extends BiomeProvider {
private static final Map<Environment, String> ENV_MAP;
private static final int DEPTH = 50;
static {
Map<Environment, String> e = new EnumMap<>(Environment.class);
@ -48,11 +56,13 @@ public abstract class AbstractSeedBiomeProvider extends BiomeProvider {
private final int offsetX;
private final int offsetZ;
protected final Map<BlockFace, SortedMap<Double, Biome>> quadrants;
private final AbstractBoxedChunkGenerator seedGen;
protected AbstractSeedBiomeProvider(Boxed boxed, Environment env, Biome defaultBiome) {
protected AbstractSeedBiomeProvider(Boxed boxed, Environment env, Biome defaultBiome, AbstractBoxedChunkGenerator seedGen) {
this.addon = boxed;
this.defaultBiome = defaultBiome;
this.seedGen = seedGen;
dist = addon.getSettings().getIslandDistance();
offsetX = addon.getSettings().getIslandXOffset();
offsetZ = addon.getSettings().getIslandZOffset();
@ -75,34 +85,78 @@ public abstract class AbstractSeedBiomeProvider extends BiomeProvider {
quadrants.put(BlockFace.SOUTH_WEST, southWest);
}
private Biome getBiome(BlockFace dir, double d) {
private Biome getQuadrantBiome(BlockFace dir, double d) {
Entry<Double, Biome> en = ((TreeMap<Double, Biome>) quadrants.get(dir)).ceilingEntry(d);
return en == null ? defaultBiome : en.getValue();
return en == null ? null : en.getValue();
}
@Override
public Biome getBiome(WorldInfo worldInfo, int x, int y, int z) {
return getMappedBiome(x,z);
// Custom biomes are not 3D yet
if (y < DEPTH) {
Biome result = getVanillaBiome(worldInfo, x, y, z);
return Objects.requireNonNull(result);
}
Biome result = getMappedBiome(x,z);
if (result == null || result.equals(Biome.CUSTOM)) {
result = getVanillaBiome(worldInfo, x, y, z);
}
return Objects.requireNonNull(result);
}
private @NonNull Biome getVanillaBiome(WorldInfo worldInfo, int x, int y, int z) {
// Vanilla biomes
int chunkX = BoxedChunkGenerator.repeatCalc(x >> 4);
int chunkZ = BoxedChunkGenerator.repeatCalc(z >> 4);
ChunkSnapshot snapshot = this.seedGen.getChunk(chunkX, chunkZ);
if (snapshot == null) {
return defaultBiome;
}
int xx = Math.floorMod(x, 16);
int zz = Math.floorMod(z, 16);
int yy = Math.max(Math.min(y * 4, worldInfo.getMaxHeight()), worldInfo.getMinHeight()); // To handle bug in Spigot
Biome b = snapshot.getBiome(xx, yy, zz);
if (y > DEPTH )
BentoBox.getInstance().logDebug("Returning vanilla biome " + b + " for " + worldInfo.getName() + " " + x + " " + y + " " + z);
return Objects.requireNonNull(b);
}
private Map<Pair<Integer, Integer>, Biome> biomeCache = new HashMap<>();
/**
* Get the mapped 2D biome at position x,z
* @param x - block coord
* @param z - block coord
* @return Biome
*/
private Biome getMappedBiome(int x, int z) {
/*
* Biomes go around the island centers
*
*/
Biome result = biomeCache.get((new Pair<Integer, Integer>(x,z)));
if (result != null) {
return result;
}
Vector s = new Vector(x, 0, z);
Vector l = getClosestIsland(s);
double dis = l.distanceSquared(s);
double d = dis / (dist * dist);
BentoBox.getInstance().logDebug("Closest island is " + Util.xyz(l));
double dis = l.distance(s);
double d = dis / dist; // Normalize
Vector direction = s.subtract(l);
if (direction.getBlockX() <= 0 && direction.getBlockZ() <= 0) {
return getBiome(BlockFace.NORTH_WEST, d);
result = getQuadrantBiome(BlockFace.NORTH_WEST, d);
} else if (direction.getBlockX() > 0 && direction.getBlockZ() <= 0) {
return getBiome(BlockFace.NORTH_EAST, d);
result = getQuadrantBiome(BlockFace.NORTH_EAST, d);
} else if (direction.getBlockX() <= 0 && direction.getBlockZ() > 0) {
return getBiome(BlockFace.SOUTH_WEST, d);
result = getQuadrantBiome(BlockFace.SOUTH_WEST, d);
} else {
result = getQuadrantBiome(BlockFace.SOUTH_EAST, d);
}
return getBiome(BlockFace.SOUTH_EAST, d);
biomeCache.put(new Pair<Integer, Integer>(x,z), result);
return result;
}
@Override
@ -111,6 +165,11 @@ public abstract class AbstractSeedBiomeProvider extends BiomeProvider {
return Arrays.stream(Biome.values()).filter(b -> !b.equals(Biome.CUSTOM)).toList();
}
/**
* Get the island center closest to this vector
* @param v - vector
* @return island center vector (no y value)
*/
private Vector getClosestIsland(Vector v) {
int d = dist * 2;
long x = Math.round((double) v.getBlockX() / d) * d + offsetX;
@ -129,8 +188,9 @@ public abstract class AbstractSeedBiomeProvider extends BiomeProvider {
try {
double d = Double.parseDouble(split[0]);
Biome biome = Enums.getIfPresent(Biome.class, split[1].toUpperCase(Locale.ENGLISH)).orNull();
if (biome == null && !split[1].toUpperCase(Locale.ENGLISH).equalsIgnoreCase("default")) {
if (biome == null) {
addon.logError(split[1].toUpperCase(Locale.ENGLISH) + " is an unknown biome on this server.");
result.put(d, Biome.CUSTOM);
} else {
// A biome of null means that no alternative biome should be applied
result.put(d, biome);

View File

@ -6,10 +6,8 @@ import java.util.Random;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Banner;
import org.bukkit.block.Block;
import org.bukkit.block.BlockState;
import org.bukkit.block.CreatureSpawner;
import org.bukkit.entity.Entity;
@ -20,13 +18,12 @@ import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.util.Vector;
import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.blueprints.dataobjects.BlueprintBlock;
import world.bentobox.bentobox.blueprints.dataobjects.BlueprintCreatureSpawner;
import world.bentobox.bentobox.util.Pair;
import world.bentobox.boxed.Boxed;
import world.bentobox.boxed.generators.BoxedChunkGenerator.ChestData;
import world.bentobox.boxed.generators.BoxedChunkGenerator.ChunkStore;
import world.bentobox.boxed.generators.AbstractBoxedChunkGenerator.ChestData;
import world.bentobox.boxed.generators.AbstractBoxedChunkGenerator.ChunkStore;
/**
* @author tastybento
@ -35,14 +32,12 @@ import world.bentobox.boxed.generators.BoxedChunkGenerator.ChunkStore;
public class BoxedBlockPopulator extends BlockPopulator {
private Boxed addon;
private int size;
/**
* @param addon
*/
public BoxedBlockPopulator(Boxed addon) {
this.addon = addon;
this.size = (int)(addon.getSettings().getIslandDistance() / 16D); // Size is chunks
}
@ -54,8 +49,8 @@ public class BoxedBlockPopulator extends BlockPopulator {
World world = Bukkit.getWorld(worldInfo.getUID());
int height = worldInfo.getMaxHeight();
int minY = worldInfo.getMinHeight();
int xx = BoxedChunkGenerator.repeatCalc(chunkX, size);
int zz = BoxedChunkGenerator.repeatCalc(chunkZ, size);
int xx = BoxedChunkGenerator.repeatCalc(chunkX);
int zz = BoxedChunkGenerator.repeatCalc(chunkZ);
Pair<Integer, Integer> coords = new Pair<>(xx, zz);
if (chunks.containsKey(coords)) {
//// BentoBox.getInstance().logDebug("Populating ");

View File

@ -43,26 +43,19 @@ import world.bentobox.bentobox.blueprints.dataobjects.BlueprintCreatureSpawner;
import world.bentobox.bentobox.blueprints.dataobjects.BlueprintEntity;
import world.bentobox.bentobox.util.Pair;
import world.bentobox.boxed.Boxed;
import world.bentobox.boxed.generators.AbstractBoxedChunkGenerator.ChestData;
import world.bentobox.boxed.generators.AbstractBoxedChunkGenerator.ChunkStore;
import world.bentobox.boxed.generators.AbstractBoxedChunkGenerator.EntityData;
/**
* Chunk generator for all environments
* @author tastybento
*
*/
public class BoxedChunkGenerator extends ChunkGenerator {
private final Boxed addon;
private final int size;
private Map<Pair<Integer, Integer>, ChunkStore> chunks = new HashMap<>();
public record ChunkStore(ChunkSnapshot snapshot, List<EntityData> bpEnts, List<ChestData> chests) {};
public record EntityData(Vector relativeLoc, BlueprintEntity entity) {};
public record ChestData(Vector relativeLoc, BlueprintBlock chest) {};
//private final WorldRef wordRefNether;
public class BoxedChunkGenerator extends AbstractBoxedChunkGenerator {
public BoxedChunkGenerator(Boxed addon) {
this.addon = addon;
this.size = (int)(addon.getSettings().getIslandDistance() / 16D); // Size is chunks
super(addon);
}
@Override
@ -76,169 +69,22 @@ public class BoxedChunkGenerator extends ChunkGenerator {
return world.getPopulators();
}
/**
* Save a chunk
* @param z - chunk z coord
* @param x - chunk x coord
* @param chunk the chunk to set
*/
public void setChunk(int x, int z, Chunk chunk) {
List<LivingEntity> ents = Arrays.stream(chunk.getEntities())
@Override
protected List<EntityData> getEnts(Chunk chunk) {
return this.setEntities(Arrays.stream(chunk.getEntities())
.filter(Objects::nonNull)
.filter(e -> !(e instanceof Player))
.filter(e -> e instanceof LivingEntity)
.map(LivingEntity.class::cast)
.toList();
// Grab entities
List<EntityData> bpEnts = this.setEntities(ents);
// Grab tile entities
List<ChestData> chests = Arrays.stream(chunk.getTileEntities()).map(t -> new ChestData(getLocInChunk(t.getLocation()), this.getBluePrintBlock(t.getBlock()))).toList();
chunks.put(new Pair<>(x, z), new ChunkStore(chunk.getChunkSnapshot(false, true, false), bpEnts, chests));
}
/**
* @param x chunk x
* @param z chunk z
* @return chunk snapshot or null if there is none
*/
public ChunkSnapshot getChunk(int x, int z) {
return chunks.get(new Pair<>(x, z)).snapshot;
.toList());
}
@Override
public boolean canSpawn(World world, int x, int z)
{
return true;
}
@Override
public void generateNoise(WorldInfo worldInfo, Random r, int chunkX, int chunkZ, ChunkData cd) {
int height = worldInfo.getMaxHeight();
int minY = worldInfo.getMinHeight();
int xx = repeatCalc(chunkX, size);
int zz = repeatCalc(chunkZ, size);
Pair<Integer, Integer> coords = new Pair<>(xx, zz);
if (!chunks.containsKey(coords)) {
// This should never be needed because islands should abut each other
cd.setRegion(0, minY, 0, 16, 0, 16, Material.WATER);
return;
}
// Copy the chunk
ChunkSnapshot chunk = chunks.get(coords).snapshot;
copyChunkVerbatim(cd, chunk, minY, height);
}
private void copyChunkVerbatim(ChunkData cd, ChunkSnapshot chunk, int minY, int height) {
for (int x = 0; x < 16; x ++) {
for (int z = 0; z < 16; z++) {
for (int y = minY; y < height; y++) {
cd.setBlock(x, y, z, chunk.getBlockData(x, y, z));
}
}
}
}
/*
private void copyChunk(ChunkData cd, ChunkSnapshot chunk, int minY, int height) {
for (int x = 0; x < 16; x ++) {
for (int z = 0; z < 16; z++) {
for (int y = minY; y < height; y++) {
Material m = chunk.getBlockType(x, y, z);
// Handle blocks that occur naturally in water
if (isInWater(m)) {
cd.setBlock(x, y, z, Material.WATER);
} else {
// Handle liquids and default blocks
switch (m) {
case WATER, LAVA, NETHERRACK, STONE, END_STONE -> cd.setBlock(x, y, z, m);
default ->
// Most other blocks
cd.setBlock(x, y, z, isGround(m) ? Material.STONE : Material.AIR);
}
}
}
}
}
}
*/
/**
* Calculates the repeating value for a given size
* @param chunkCoord chunk coord
* @param s size
* @return mapped chunk coord
*/
public static int repeatCalc(int chunkCoord, int s) {
int xx;
if (chunkCoord > 0) {
xx = Math.floorMod(chunkCoord + s, s*2) - s;
} else {
xx = Math.floorMod(chunkCoord - s, -s*2) + s;
}
return xx;
}
/**
* @return the chunks
*/
public Map<Pair<Integer, Integer>, ChunkStore> getChunks() {
return chunks;
}
/*
private static boolean isInWater(Material m) {
return switch (m) {
// Underwater plants
case KELP, KELP_PLANT, SEAGRASS, BUBBLE_COLUMN, BUBBLE_CORAL, BUBBLE_CORAL_BLOCK, BUBBLE_CORAL_FAN,
BUBBLE_CORAL_WALL_FAN, DEAD_BRAIN_CORAL, DEAD_BRAIN_CORAL_BLOCK, DEAD_BRAIN_CORAL_FAN,
DEAD_BRAIN_CORAL_WALL_FAN, DEAD_BUBBLE_CORAL, DEAD_BUBBLE_CORAL_BLOCK, DEAD_BUBBLE_CORAL_FAN,
DEAD_BUBBLE_CORAL_WALL_FAN, DEAD_BUSH, DEAD_FIRE_CORAL, DEAD_FIRE_CORAL_BLOCK, DEAD_FIRE_CORAL_FAN,
DEAD_FIRE_CORAL_WALL_FAN, DEAD_HORN_CORAL, DEAD_HORN_CORAL_BLOCK, DEAD_HORN_CORAL_FAN,
DEAD_HORN_CORAL_WALL_FAN, DEAD_TUBE_CORAL, DEAD_TUBE_CORAL_BLOCK, DEAD_TUBE_CORAL_FAN,
DEAD_TUBE_CORAL_WALL_FAN, FIRE_CORAL, FIRE_CORAL_BLOCK, FIRE_CORAL_FAN, FIRE_CORAL_WALL_FAN,
HORN_CORAL, HORN_CORAL_BLOCK, HORN_CORAL_FAN, HORN_CORAL_WALL_FAN, TUBE_CORAL, TUBE_CORAL_BLOCK,
TUBE_CORAL_FAN, TUBE_CORAL_WALL_FAN, TALL_SEAGRASS -> true;
default -> false;
};
protected List<ChestData> getChests(Chunk chunk) {
return Arrays.stream(chunk.getTileEntities()).map(t -> new ChestData(getLocInChunk(t.getLocation()), this.getBluePrintBlock(t.getBlock()))).toList();
}
private static boolean isGround(Material m) {
if (m.isAir() || m.isBurnable() || !m.isSolid()) return false;
return switch (m) {
case ANDESITE, BEDROCK, CALCITE, CLAY, COAL_ORE, COARSE_DIRT, COBBLESTONE, COPPER_ORE, DEEPSLATE,
DEEPSLATE_COAL_ORE, DEEPSLATE_COPPER_ORE, DEEPSLATE_DIAMOND_ORE, DEEPSLATE_EMERALD_ORE,
DEEPSLATE_GOLD_ORE, DEEPSLATE_IRON_ORE, DEEPSLATE_LAPIS_ORE, DEEPSLATE_REDSTONE_ORE, DIAMOND_ORE,
DIORITE, DIRT, DIRT_PATH, DRIPSTONE_BLOCK, EMERALD_ORE, END_STONE, FARMLAND, GLOWSTONE, GOLD_ORE,
GRANITE, GRASS_BLOCK, IRON_ORE, MAGMA_BLOCK, MYCELIUM, NETHERITE_BLOCK, NETHERRACK, RED_SAND,
RED_SANDSTONE, ROOTED_DIRT, SAND, SANDSTONE, SOUL_SAND, SOUL_SOIL, STONE, TERRACOTTA, AMETHYST_BLOCK,
AMETHYST_CLUSTER, AMETHYST_SHARD, BASALT, BLACKSTONE, BLACK_CONCRETE, BLACK_GLAZED_TERRACOTTA,
BLACK_TERRACOTTA, BLUE_CONCRETE, BLUE_GLAZED_TERRACOTTA, BLUE_TERRACOTTA, BONE_BLOCK, BROWN_CONCRETE,
BROWN_GLAZED_TERRACOTTA, BROWN_TERRACOTTA, BUDDING_AMETHYST, CHISELED_DEEPSLATE,
CHISELED_NETHER_BRICKS, CHISELED_POLISHED_BLACKSTONE, CHISELED_QUARTZ_BLOCK, CHISELED_RED_SANDSTONE,
CHISELED_SANDSTONE, CHISELED_STONE_BRICKS, COAL_BLOCK, COBBLED_DEEPSLATE, CRYING_OBSIDIAN,
CUT_RED_SANDSTONE, CUT_RED_SANDSTONE_SLAB, CUT_SANDSTONE, CUT_SANDSTONE_SLAB, CYAN_CONCRETE,
CYAN_GLAZED_TERRACOTTA, CYAN_TERRACOTTA, DEEPSLATE_BRICKS, DIAMOND_BLOCK, ECHO_SHARD, EMERALD_BLOCK,
GOLD_BLOCK, GRAVEL, GRAY_CONCRETE, GRAY_GLAZED_TERRACOTTA, GRAY_TERRACOTTA, GREEN_CONCRETE,
GREEN_GLAZED_TERRACOTTA, GREEN_TERRACOTTA, INFESTED_CHISELED_STONE_BRICKS, INFESTED_COBBLESTONE,
INFESTED_CRACKED_STONE_BRICKS, INFESTED_DEEPSLATE, INFESTED_MOSSY_STONE_BRICKS, INFESTED_STONE,
INFESTED_STONE_BRICKS, LAPIS_ORE, LARGE_AMETHYST_BUD, LIGHT_BLUE_CONCRETE,
LIGHT_BLUE_GLAZED_TERRACOTTA, LIGHT_BLUE_TERRACOTTA, LIGHT_GRAY_CONCRETE,
LIGHT_GRAY_GLAZED_TERRACOTTA, LIGHT_GRAY_TERRACOTTA, LIME_CONCRETE, LIME_GLAZED_TERRACOTTA,
LIME_TERRACOTTA, MAGENTA_CONCRETE, MAGENTA_GLAZED_TERRACOTTA, MAGENTA_TERRACOTTA, MOSSY_COBBLESTONE,
MUD, NETHERITE_SCRAP, NETHER_GOLD_ORE, NETHER_QUARTZ_ORE, OBSIDIAN, ORANGE_CONCRETE,
ORANGE_GLAZED_TERRACOTTA, ORANGE_TERRACOTTA, PACKED_MUD, PINK_CONCRETE, PINK_GLAZED_TERRACOTTA,
PINK_TERRACOTTA, PODZOL, POLISHED_ANDESITE, POLISHED_BASALT, POLISHED_BLACKSTONE,
POLISHED_DEEPSLATE, POLISHED_DIORITE, POLISHED_GRANITE, PURPLE_CONCRETE, PURPLE_GLAZED_TERRACOTTA,
PURPLE_TERRACOTTA, PURPUR_BLOCK, QUARTZ_BLOCK, RAW_COPPER_BLOCK, RAW_GOLD_BLOCK, RAW_IRON_BLOCK,
REDSTONE_BLOCK, REDSTONE_ORE, RED_CONCRETE, RED_GLAZED_TERRACOTTA, RED_TERRACOTTA, SMOOTH_BASALT,
SMOOTH_QUARTZ, SMOOTH_RED_SANDSTONE, SMOOTH_SANDSTONE, SMOOTH_STONE, TUFF, WARPED_HYPHAE,
WARPED_NYLIUM, WHITE_CONCRETE, WHITE_GLAZED_TERRACOTTA, WHITE_TERRACOTTA, YELLOW_CONCRETE,
YELLOW_GLAZED_TERRACOTTA, YELLOW_TERRACOTTA -> true;
default -> false;
};
}
*/
private List<EntityData> setEntities(Collection<LivingEntity> entities) {
List<EntityData> bpEnts = new ArrayList<>();
for (LivingEntity entity: entities) {
@ -350,39 +196,4 @@ public class BoxedChunkGenerator extends ChunkGenerator {
return cs;
}
@Override
public boolean shouldGenerateNoise() {
return false;
}
@Override
public boolean shouldGenerateSurface() {
return false;
}
@Override
public boolean shouldGenerateCaves() {
return false;
//return this.addon.getSettings().isGenerateCaves();
}
@Override
public boolean shouldGenerateDecorations() {
return false;
//return this.addon.getSettings().isGenerateDecorations();
}
@Override
public boolean shouldGenerateMobs() {
return this.addon.getSettings().isGenerateMobs();
}
@Override
public boolean shouldGenerateStructures() {
return false;
//return this.addon.getSettings().isAllowStructures();
}
}

View File

@ -1,5 +1,8 @@
package world.bentobox.boxed.generators;
import java.util.List;
import org.bukkit.Chunk;
import org.bukkit.World.Environment;
import org.bukkit.generator.BiomeProvider;
import org.bukkit.generator.ChunkGenerator;
@ -12,24 +15,37 @@ import world.bentobox.boxed.Boxed;
* @author tastybento
*
*/
public class BoxedSeedChunkGenerator extends ChunkGenerator {
public class BoxedSeedChunkGenerator extends AbstractBoxedChunkGenerator {
private final BiomeProvider seedBiomeProvider;
private final BiomeProvider biomeProvider;
private final Environment env;
/**
* @param env
* @param seedBiomeProvider
* @param boxed - addon
* @param env - environment
*/
public BoxedSeedChunkGenerator(Boxed boxed, Environment env) {
this.seedBiomeProvider = new SeedBiomeGenerator(boxed);
super(boxed);
this.biomeProvider = null;
this.env = env;
}
/**
* @param boxed - addon
* @param env - environment
* @param bp - biome provider
*/
public BoxedSeedChunkGenerator(Boxed boxed, Environment env, BiomeProvider bp) {
super(boxed);
this.biomeProvider = bp;
this.env = env;
}
@Override
public BiomeProvider getDefaultBiomeProvider(WorldInfo worldInfo) {
return seedBiomeProvider;
// If null then vanilla biomes are used
return biomeProvider;
}
@Override
@ -62,4 +78,16 @@ public class BoxedSeedChunkGenerator extends ChunkGenerator {
public boolean shouldGenerateStructures() {
return env.equals(Environment.NETHER); // We allow structures in the Nether
}
@Override
protected List<EntityData> getEnts(Chunk chunk) {
// These won't be stored
return null;
}
@Override
protected List<ChestData> getChests(Chunk chunk) {
// These won't be stored
return null;
}
}

View File

@ -2,6 +2,7 @@ package world.bentobox.boxed.generators;
import org.bukkit.World.Environment;
import org.bukkit.block.Biome;
import org.bukkit.generator.BiomeProvider;
import world.bentobox.boxed.Boxed;
@ -11,8 +12,8 @@ import world.bentobox.boxed.Boxed;
*/
public class SeedBiomeGenerator extends AbstractSeedBiomeProvider {
public SeedBiomeGenerator(Boxed boxed) {
super(boxed, Environment.NORMAL, Biome.OCEAN);
public SeedBiomeGenerator(Boxed boxed, AbstractBoxedChunkGenerator seedGen) {
super(boxed, Environment.NORMAL, Biome.PLAINS, seedGen);
}
}

View File

@ -2,27 +2,25 @@
distribution:
overworld:
north-east:
- 0.05:PLAINS
- 0.05:CUSTOM
- 0.1:DESERT
- 0.2:SAVANNA
- 0.5:SPARSE_JUNGLE
- 0.65:JUNGLE
- 0.8:BAMBOO_JUNGLE
- 1.0:MANGROVE_SWAMP
- 2.0:CUSTOM
south-east:
- 0.05:PLAINS
- 0.8:SUNFLOWER_PLAINS
- 0.05:CUSTOM
- 0.08:SUNFLOWER_PLAINS
- 0.2:FLOWER_FOREST
- 0.3:SAVANNA
- 0.4:BEACH
- 0.5:COLD_OCEAN
- 0.5:CUSTOM
north-west:
- 0.05:PLAINS
- 0.8:WARM_OCEAN
- 1.5:COLD_OCEAN
- 2.0:OCEAN
- 2.0:CUSTOM
south-west:
- 0.04:PLAINS
- 0.04:CUSTOM
- 0.05:DARK_FOREST
- 0.06:BIRCH_FOREST
- 0.07:FOREST
@ -34,9 +32,10 @@ distribution:
- 0.7:TAIGA
- 1.1:SNOWY_PLAINS
- 2.1:SNOWY_TAIGA
- 2.2:CUSTOM
nether:
north-east:
- 0.03:NETHER_WASTES
- 0.03:CUSTOM
- 0.14:CRIMSON_FOREST
- 0.26:NETHER_WASTES
- 0.51:WARPED_FOREST
@ -46,7 +45,7 @@ distribution:
- 1.6:BASALT_DELTAS
- 2.1:NETHER_WASTES
south-east:
- 0.03:NETHER_WASTES
- 0.03:CUSTOM
- 0.05:CRIMSON_FOREST
- 0.23:NETHER_WASTES
- 0.48:WARPED_FOREST
@ -56,7 +55,7 @@ distribution:
- 1.9:BASALT_DELTAS
- 2.0:NETHER_WASTES
north-west:
- 0.03:NETHER_WASTES
- 0.03:CUSTOM
- 0.15:CRIMSON_FOREST
- 0.20:NETHER_WASTES
- 0.3:WARPED_FOREST
@ -66,7 +65,7 @@ distribution:
- 1.5:BASALT_DELTAS
- 2.0:NETHER_WASTES
south-west:
- 0.03:NETHER_WASTES
- 0.03:CUSTOM
- 0.11:CRIMSON_FOREST
- 0.22:NETHER_WASTES
- 0.51:WARPED_FOREST