diff --git a/src/main/java/world/bentobox/boxed/Boxed.java b/src/main/java/world/bentobox/boxed/Boxed.java index 977b813..8baba9c 100644 --- a/src/main/java/world/bentobox/boxed/Boxed.java +++ b/src/main/java/world/bentobox/boxed/Boxed.java @@ -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 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; } diff --git a/src/main/java/world/bentobox/boxed/generators/AbstractBoxedBiomeProvider.java b/src/main/java/world/bentobox/boxed/generators/AbstractBoxedBiomeProvider.java index ce7da0f..9ea9eb3 100644 --- a/src/main/java/world/bentobox/boxed/generators/AbstractBoxedBiomeProvider.java +++ b/src/main/java/world/bentobox/boxed/generators/AbstractBoxedBiomeProvider.java @@ -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) { diff --git a/src/main/java/world/bentobox/boxed/generators/AbstractBoxedChunkGenerator.java b/src/main/java/world/bentobox/boxed/generators/AbstractBoxedChunkGenerator.java new file mode 100644 index 0000000..349856c --- /dev/null +++ b/src/main/java/world/bentobox/boxed/generators/AbstractBoxedChunkGenerator.java @@ -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, ChunkStore> chunks = new HashMap<>(); + public record ChunkStore(ChunkSnapshot snapshot, List bpEnts, List 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 getEnts(Chunk chunk); + + protected abstract List 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 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, 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(); + } + +} diff --git a/src/main/java/world/bentobox/boxed/generators/AbstractCopyBiomeProvider.java b/src/main/java/world/bentobox/boxed/generators/AbstractCopyBiomeProvider.java index 23c3840..d011239 100644 --- a/src/main/java/world/bentobox/boxed/generators/AbstractCopyBiomeProvider.java +++ b/src/main/java/world/bentobox/boxed/generators/AbstractCopyBiomeProvider.java @@ -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) { diff --git a/src/main/java/world/bentobox/boxed/generators/AbstractSeedBiomeProvider.java b/src/main/java/world/bentobox/boxed/generators/AbstractSeedBiomeProvider.java index 52c0ad1..d675c80 100644 --- a/src/main/java/world/bentobox/boxed/generators/AbstractSeedBiomeProvider.java +++ b/src/main/java/world/bentobox/boxed/generators/AbstractSeedBiomeProvider.java @@ -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 ENV_MAP; + private static final int DEPTH = 50; static { Map 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> 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 en = ((TreeMap) 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, 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(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(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); diff --git a/src/main/java/world/bentobox/boxed/generators/BoxedBlockPopulator.java b/src/main/java/world/bentobox/boxed/generators/BoxedBlockPopulator.java index 8cf3c4c..cf34c60 100644 --- a/src/main/java/world/bentobox/boxed/generators/BoxedBlockPopulator.java +++ b/src/main/java/world/bentobox/boxed/generators/BoxedBlockPopulator.java @@ -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 coords = new Pair<>(xx, zz); if (chunks.containsKey(coords)) { //// BentoBox.getInstance().logDebug("Populating "); diff --git a/src/main/java/world/bentobox/boxed/generators/BoxedChunkGenerator.java b/src/main/java/world/bentobox/boxed/generators/BoxedChunkGenerator.java index 0163a51..f4b70c4 100644 --- a/src/main/java/world/bentobox/boxed/generators/BoxedChunkGenerator.java +++ b/src/main/java/world/bentobox/boxed/generators/BoxedChunkGenerator.java @@ -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, ChunkStore> chunks = new HashMap<>(); - public record ChunkStore(ChunkSnapshot snapshot, List bpEnts, List 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 ents = Arrays.stream(chunk.getEntities()) + @Override + protected List 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 bpEnts = this.setEntities(ents); - // Grab tile entities - List 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 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, 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 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 setEntities(Collection entities) { List 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(); - } - } diff --git a/src/main/java/world/bentobox/boxed/generators/BoxedSeedChunkGenerator.java b/src/main/java/world/bentobox/boxed/generators/BoxedSeedChunkGenerator.java index 965de77..c5c5154 100644 --- a/src/main/java/world/bentobox/boxed/generators/BoxedSeedChunkGenerator.java +++ b/src/main/java/world/bentobox/boxed/generators/BoxedSeedChunkGenerator.java @@ -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 getEnts(Chunk chunk) { + // These won't be stored + return null; + } + + @Override + protected List getChests(Chunk chunk) { + // These won't be stored + return null; + } } diff --git a/src/main/java/world/bentobox/boxed/generators/SeedBiomeGenerator.java b/src/main/java/world/bentobox/boxed/generators/SeedBiomeGenerator.java index f4bcf25..d257e0a 100644 --- a/src/main/java/world/bentobox/boxed/generators/SeedBiomeGenerator.java +++ b/src/main/java/world/bentobox/boxed/generators/SeedBiomeGenerator.java @@ -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); } } \ No newline at end of file diff --git a/src/main/resources/biomes.yml b/src/main/resources/biomes.yml index 5ffeab7..619ba94 100644 --- a/src/main/resources/biomes.yml +++ b/src/main/resources/biomes.yml @@ -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