diff --git a/src/main/java/world/bentobox/skygrid/SkyGrid.java b/src/main/java/world/bentobox/skygrid/SkyGrid.java index 9193dfd..d619664 100644 --- a/src/main/java/world/bentobox/skygrid/SkyGrid.java +++ b/src/main/java/world/bentobox/skygrid/SkyGrid.java @@ -92,7 +92,7 @@ public void createWorlds() { } BiomeProvider bp = new SkyGridBiomeProvider(); // Create the world if it does not exist - islandWorld = WorldCreator.name(worldName).environment(World.Environment.NORMAL).generator(gen) + islandWorld = WorldCreator.name(worldName).environment(World.Environment.NORMAL).generator(gen).seed(123456L) .biomeProvider(bp).createWorld(); // Make the nether if it does not exist @@ -134,4 +134,9 @@ public void saveWorldSettings() { } } + + @Override + public boolean isUsesNewChunkGeneration() { + return true; + } } diff --git a/src/main/java/world/bentobox/skygrid/generators/SkyGridBiomeProvider.java b/src/main/java/world/bentobox/skygrid/generators/SkyGridBiomeProvider.java index c4687f3..9315696 100644 --- a/src/main/java/world/bentobox/skygrid/generators/SkyGridBiomeProvider.java +++ b/src/main/java/world/bentobox/skygrid/generators/SkyGridBiomeProvider.java @@ -5,9 +5,11 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Objects; +import java.util.Random; import java.util.concurrent.ConcurrentHashMap; import org.bukkit.World; +import org.bukkit.World.Environment; import org.bukkit.block.Biome; import org.bukkit.generator.BiomeProvider; import org.bukkit.generator.WorldInfo; @@ -18,6 +20,7 @@ */ public class SkyGridBiomeProvider extends BiomeProvider { + private static final Random RAND = new Random(); private final Map temperatureGenMap = new ConcurrentHashMap<>(); private final Map rainfallGenMap = new ConcurrentHashMap<>(); @@ -25,6 +28,11 @@ public class SkyGridBiomeProvider extends BiomeProvider { @Override public Biome getBiome(WorldInfo worldInfo, int realX, int y, int realZ) { + // Handle caves + if (worldInfo.getEnvironment() == Environment.NORMAL && y < 0) { + return caveBiome(y); + } + // Make and cache the PerlinOctaveGenerator for the environment PerlinOctaveGenerator temperatureGen = temperatureGenMap.computeIfAbsent(worldInfo.getEnvironment(), wf -> { PerlinOctaveGenerator tg = new PerlinOctaveGenerator(worldInfo.getSeed(), 16); @@ -54,6 +62,13 @@ public Biome getBiome(WorldInfo worldInfo, int realX, int y, int realZ) { return Objects.requireNonNull(maxBiome).biome; } + private Biome caveBiome(int y) { + if (y < -52) { + return Biome.DEEP_DARK; + } + return RAND.nextBoolean() ? Biome.LUSH_CAVES : Biome.DRIPSTONE_CAVES; + } + @Override public List getBiomes(WorldInfo worldInfo) { return Arrays.stream(SkyGridBiomes.values()).map(SkyGridBiomes::getBiome).toList(); diff --git a/src/main/java/world/bentobox/skygrid/generators/SkyGridChunks.java b/src/main/java/world/bentobox/skygrid/generators/SkyGridChunks.java index 725bd8f..b67c9db 100644 --- a/src/main/java/world/bentobox/skygrid/generators/SkyGridChunks.java +++ b/src/main/java/world/bentobox/skygrid/generators/SkyGridChunks.java @@ -57,12 +57,35 @@ public SkyGridChunks(SkyGrid addon) { addon.log("Done making chunks"); } + public Material getBlock(Environment env) { + BlockProbability prob = addon.getWorldStyles().get(env).getProb(); + return prob.getBlock(random, false, false); + /* + // Get a random block and feed in the last block (true if cactus or cane) + Material blockMat = prob.getBlock(random, y == 0, false); + // If blockMat is not "a block" then cannot be generated + if (!blockMat.isAir() && !blockMat.isBlock()) { + blockMat = Material.STONE; + } + // Convert to deep + if (y < 0) { + if (blockMat == Material.STONE) { + blockMat = Material.DEEPSLATE; + + } else if (blockMat == Material.COBBLESTONE) { + blockMat = Material.COBBLED_DEEPSLATE; + } else { + blockMat = Enums.getIfPresent(Material.class, "DEEPSLATE_" + blockMat.name()).or(blockMat); + } + } + return new SkyGridBlock(x, y, z, blockMat);*/ + } private List getChunk(BlockProbability prob) { List result = new ArrayList<>(); for (int x = 1; x < 16; x += 4) { for (int z = 1; z < 16; z += 4) { - for (int y = 0; y <= addon.getSettings().getIslandHeight(); y += 4) { + for (int y = -64; y <= addon.getSettings().getIslandHeight(); y += 4) { setBlock(prob, x, y, z, result); } } @@ -91,6 +114,18 @@ private void setBlock(BlockProbability prob, int x, int y, int z, List { + case CACTUS, DEAD_BUSH -> { result.add(new SkyGridBlock(x, y, z, Material.SAND)); result.add(new SkyGridBlock(x, y - 1, z, Material.SANDSTONE)); result.add(new SkyGridBlock(x, y + 1, z, blockMat)); diff --git a/src/main/java/world/bentobox/skygrid/generators/SkyGridGen.java b/src/main/java/world/bentobox/skygrid/generators/SkyGridGen.java index beae04b..02d8773 100644 --- a/src/main/java/world/bentobox/skygrid/generators/SkyGridGen.java +++ b/src/main/java/world/bentobox/skygrid/generators/SkyGridGen.java @@ -1,11 +1,12 @@ package world.bentobox.skygrid.generators; -import java.util.Collections; +import java.util.ArrayList; import java.util.List; import java.util.Random; import org.bukkit.Location; import org.bukkit.Material; +import org.bukkit.Tag; import org.bukkit.World; import org.bukkit.generator.BlockPopulator; import org.bukkit.generator.ChunkGenerator; @@ -18,7 +19,6 @@ public class SkyGridGen extends ChunkGenerator { private final SkyGrid addon; private final BlockPopulator populator; - private final SkyGridChunks preMade; /** * @param addon - addon @@ -26,18 +26,74 @@ public class SkyGridGen extends ChunkGenerator { public SkyGridGen(SkyGrid addon) { this.addon = addon; this.populator = new SkyGridPop(addon); - preMade = new SkyGridChunks(addon); } @Override - public void generateNoise(WorldInfo worldInfo, Random r, int x, int z, ChunkData result) { - result.setRegion(0, worldInfo.getMinHeight(), 0, 16, worldInfo.getMaxHeight(), 16, Material.AIR); - preMade.getSkyGridChunk(worldInfo.getEnvironment()).forEach(b -> result.setBlock(b.getX(), b.getY(), b.getZ(), b.getBd())); + public void generateNoise(WorldInfo worldInfo, Random r, int chunkX, int chunkZ, ChunkData result) { + Random rand = new Random(worldInfo.getSeed()); + // Cut off anything higher than island height + result.setRegion(0, Math.min(addon.getSettings().getIslandHeight() + 1, worldInfo.getMaxHeight() - 1), 0, 16, + worldInfo.getMaxHeight(), 16, + Material.AIR); + for (int x = 0; x < 16; x++) { + for (int z = 0; z < 16; z++) { + for (int y = addon.getSettings().getIslandHeight(); y >= worldInfo.getMinHeight(); y--) { + // Check if the block is not the 4th block + if ((Math.floorMod(x, 4) != 0) || (Math.floorMod(z, 4) != 0) || (Math.floorMod(y, 4) != 0)) { + result.setBlock(x, y, z, Material.AIR); + } else { + Material m = result.getBlockData(x, y, z).getMaterial(); + if (m == Material.AIR || m == Material.WATER) { + Material nextBlock = addon.getWorldStyles().get(worldInfo.getEnvironment()).getProb() + .getBlock(rand, y == worldInfo.getMinHeight(), true); + // Check for plants, etc. below this y + checkPlants(nextBlock, x, y, z, result); + } + + } + } + } + } } + + private boolean checkPlants(Material nextBlock, int x, int y, int z, ChunkData result) { + if (Tag.SAPLINGS.isTagged(nextBlock) || Tag.FLOWERS.isTagged(nextBlock) + || Tag.ITEMS_VILLAGER_PLANTABLE_SEEDS.isTagged(nextBlock)) { + result.setBlock(x, y, z, Material.DIRT); + result.setBlock(x, y + 1, z, nextBlock); + } else { + switch (nextBlock) { + case CACTUS, DEAD_BUSH -> { + result.setBlock(x, y, z, Material.SANDSTONE); + result.setBlock(x, y, z, Material.SAND); + result.setBlock(x, y + 2, z, nextBlock); + } + case NETHER_WART -> { + result.setBlock(x, y, z, Material.SOUL_SAND); + result.setBlock(x, y + 1, z, nextBlock); + } + case END_ROD, CHORUS_PLANT -> { + result.setBlock(x, y, z, Material.END_STONE); + result.setBlock(x, y + 1, z, nextBlock); + } + + default -> { + result.setBlock(x, y, z, nextBlock); + return false; + } + } + } + return true; + } + + @Override public List getDefaultPopulators(World world) { - return Collections.singletonList(populator); + List list = new ArrayList<>(); + list.addAll(super.getDefaultPopulators(world)); + list.add(populator); + return list; } @Override @@ -45,6 +101,11 @@ public Location getFixedSpawnLocation(World world, Random random) { return new Location(world, 0, addon.getSettings().getIslandHeight() + 2D, 0); } + @Override + public boolean shouldGenerateNoise() { + return true; + } + /** * Gets if the server should generate Vanilla surface. * @return true if the server should generate Vanilla surface diff --git a/src/main/java/world/bentobox/skygrid/generators/SkyGridPop.java b/src/main/java/world/bentobox/skygrid/generators/SkyGridPop.java index 73116db..7f938f8 100644 --- a/src/main/java/world/bentobox/skygrid/generators/SkyGridPop.java +++ b/src/main/java/world/bentobox/skygrid/generators/SkyGridPop.java @@ -1,32 +1,38 @@ package world.bentobox.skygrid.generators; +import java.util.HashMap; +import java.util.Map; import java.util.Map.Entry; import java.util.NavigableMap; import java.util.Random; import java.util.TreeMap; +import java.util.UUID; -import org.bukkit.Chunk; +import org.bukkit.Bukkit; +import org.bukkit.Location; import org.bukkit.Material; +import org.bukkit.Tag; import org.bukkit.World; import org.bukkit.World.Environment; import org.bukkit.block.Biome; -import org.bukkit.block.Block; import org.bukkit.block.BlockFace; import org.bukkit.block.Chest; import org.bukkit.block.CreatureSpawner; import org.bukkit.block.data.type.EndPortalFrame; -import org.bukkit.block.data.type.Sapling; import org.bukkit.entity.EntityType; import org.bukkit.generator.BlockPopulator; +import org.bukkit.generator.LimitedRegion; +import org.bukkit.generator.WorldInfo; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; +import org.bukkit.util.Vector; import world.bentobox.skygrid.SkyGrid; public class SkyGridPop extends BlockPopulator { private static final RandomSeries slt = new RandomSeries(27); - private final int size; + private final int islandHeight; private final SkyGrid addon; private final NavigableMap chestItemsWorld = new TreeMap<>(); private final NavigableMap chestItemsNether = new TreeMap<>(); @@ -34,8 +40,8 @@ public class SkyGridPop extends BlockPopulator { private int worldTotal; private int netherTotal; private int endTotal; - private Random random; - private Chunk chunk; + private Map rand = new HashMap<>(); + private Random random = new Random(); private static final Material[] SAPLING_TYPE = { Material.ACACIA_SAPLING, @@ -50,7 +56,7 @@ public class SkyGridPop extends BlockPopulator { public SkyGridPop(SkyGrid addon) { this.addon = addon; - this.size = addon.getSettings().getIslandHeight(); + this.islandHeight = addon.getSettings().getIslandHeight(); // Load the chest items for (Entry en : addon.getSettings().getChestItemsOverworld().entrySet()) { worldTotal += en.getValue(); @@ -70,40 +76,47 @@ public SkyGridPop(SkyGrid addon) { } - @Override - @Deprecated - public void populate(World world, Random random, Chunk chunk) { - this.random = random; - this.chunk = chunk; - for (int x = 1; x < 16; x += 4) { - for (int z = 1; z < 16; z +=4) { - for (int y = 0; y <= size; y += 4) { - alterBlocks(chunk.getBlock(x, y, z)); + private Location getLoc(World world, int x, int y, int z, int chunkX, int chunkZ) { + Vector v = new Vector(x, y, z); + return v.add(new Vector(chunkX << 4, 0, chunkZ << 4)).toLocation(world); + } + + public void populate(WorldInfo worldInfo, Random random, int chunkX, int chunkZ, LimitedRegion region) { + random = rand.computeIfAbsent(worldInfo.getUID(), (b) -> new Random(worldInfo.getSeed())); + World world = Bukkit.getWorld(worldInfo.getUID()); + for (int x = 0; x < 16; x += 4) { + for (int z = 0; z < 16; z += 4) { + for (int y = worldInfo.getMinHeight(); y <= islandHeight; y += 4) { + Location loc = getLoc(world, x, y, z, chunkX, chunkZ); + alterBlocks(region, loc, worldInfo.getEnvironment()); } } } // Do an end portal check - if (addon.getSettings().isEndGenerate() && world.getEnvironment().equals(Environment.NORMAL) + if (addon.getSettings().isEndGenerate() && worldInfo.getEnvironment().equals(Environment.NORMAL) && random.nextDouble() < addon.getSettings().getEndFrameProb()) { - makeEndPortal(); + makeEndPortal(region, chunkX, chunkZ); } + } - private void alterBlocks(Block b) { + private void alterBlocks(LimitedRegion region, Location loc, Environment environment) { // Alter blocks - switch (b.getType()) { + Material m = region.getBlockData(loc).getMaterial(); + + switch (m) { case CHEST: - setChest(b); + setChest(region, loc, environment); break; case SPAWNER: - setSpawner(b); + setSpawner(region, loc, environment); break; case DIRT: - if (b.getRelative(BlockFace.UP).getBlockData() instanceof Sapling) { - if (b.getBiome().equals(Biome.DESERT)) { - b.setType(Material.SAND, false); + if (Tag.SAPLINGS.isTagged(m)) { + if (region.getBiome(loc).equals(Biome.DESERT)) { + region.setType(loc, Material.SAND); } else { - setSaplingType(b.getRelative(BlockFace.UP)); + setSaplingType(region, loc); } } break; @@ -113,21 +126,21 @@ private void alterBlocks(Block b) { } - private void makeEndPortal() { + private void makeEndPortal(LimitedRegion region, int chunkX, int chunkZ) { for (int xx = 1; xx< 6; xx++) { for (int zz = 1; zz < 6; zz++) { if (xx == zz || (xx==1 && zz==5) || (xx==5 && zz==1) || (xx>1 && xx<5 && zz>1 && zz<5)) { continue; } - setFrame(xx, zz, chunk.getBlock(xx, addon.getSettings().getEndFrameHeight(), zz)); + setFrame(region, xx + (chunkX << 4), addon.getSettings().getEndFrameHeight(), zz + (chunkZ << 4)); } } } - private void setFrame(int xx, int zz, Block frame) { - frame.setType(Material.END_PORTAL_FRAME, false); + private void setFrame(LimitedRegion region, int xx, int yy, int zz) { + region.setType(xx, yy, zz, Material.END_PORTAL_FRAME); // Cast to end frame - EndPortalFrame endFrame = (EndPortalFrame)frame.getBlockData(); + EndPortalFrame endFrame = (EndPortalFrame) region.getBlockData(xx, yy, zz); // Add the odd eye of ender endFrame.setEye(random.nextDouble() < 0.8); @@ -144,12 +157,12 @@ private void setFrame(int xx, int zz, Block frame) { // Face West endFrame.setFacing(BlockFace.WEST); } - frame.setBlockData(endFrame, false); + region.setBlockData(xx, yy, zz, endFrame); } - private void setSaplingType(Block b) { + private void setSaplingType(LimitedRegion region, Location loc) { // Set sapling type if there is one specific to this biome - Material sapling = switch (b.getBiome()) { + Material sapling = switch (region.getBiome(loc)) { case JUNGLE -> Material.JUNGLE_SAPLING; case PLAINS -> random.nextBoolean() ? Material.BIRCH_SAPLING : Material.OAK_SAPLING; case TAIGA -> Material.SPRUCE_SAPLING; @@ -163,12 +176,12 @@ private void setSaplingType(Block b) { case MUSHROOM_FIELDS -> random.nextBoolean() ? Material.RED_MUSHROOM : Material.BROWN_MUSHROOM; default -> SAPLING_TYPE[random.nextInt(6)]; }; - b.setType(sapling); + region.setType(loc.add(new Vector(0, 1, 0)), sapling); } - private void setSpawner(Block b) { - CreatureSpawner spawner = (CreatureSpawner) b.getState(); - NavigableMap spawns = addon.getWorldStyles().get(b.getWorld().getEnvironment()).getSpawns(); + private void setSpawner(LimitedRegion region, Location loc, Environment environment) { + CreatureSpawner spawner = (CreatureSpawner) region.getBlockState(loc); + NavigableMap spawns = addon.getWorldStyles().get(environment).getSpawns(); int randKey = random.nextInt(spawns.lastKey()); EntityType type = spawns.ceilingEntry(randKey).getValue(); spawner.setDelay(120); @@ -176,11 +189,11 @@ private void setSpawner(Block b) { spawner.update(true, false); } - private void setChest(Block b) { - Chest chest = (Chest) b.getState(); + private void setChest(LimitedRegion region, Location loc, Environment environment) { + Chest chest = (Chest) region.getBlockState(loc); Inventory inv = chest.getBlockInventory(); slt.reset(); - switch (b.getWorld().getEnvironment()) { + switch (environment) { case NETHER -> fillChest(inv, chestItemsNether, addon.getSettings().getChestFillNether(), netherTotal); case THE_END -> fillChest(inv, chestItemsEnd, addon.getSettings().getChestFillEnd(), endTotal); default -> fillChest(inv, chestItemsWorld, addon.getSettings().getChestFill(), worldTotal); diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index f18777d..3fb0783 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -543,11 +543,10 @@ world: COAL_BLOCK: 10 COAL_ORE: 70 COARSE_DIRT: 107 - COBBLED_DEEPSLATE: 10 - COBBLESTONE: 10 + COBBLESTONE: 50 COBWEB: 7 COMPOSTER: 10 - COPPER_BLOCK: 10 + COPPER_BLOCK: 5 COPPER_ORE: 10 CRACKED_DEEPSLATE_BRICKS: 10 CRACKED_STONE_BRICKS: 10 @@ -572,15 +571,6 @@ world: DEAD_FIRE_CORAL_BLOCK: 10 DEAD_HORN_CORAL_BLOCK: 10 DEAD_TUBE_CORAL_BLOCK: 10 - DEEPSLATE_COAL_ORE: 10 - DEEPSLATE_COPPER_ORE: 10 - DEEPSLATE_DIAMOND_ORE: 10 - DEEPSLATE_EMERALD_ORE: 10 - DEEPSLATE_GOLD_ORE: 10 - DEEPSLATE_IRON_ORE: 10 - DEEPSLATE_LAPIS_ORE: 10 - DEEPSLATE_REDSTONE_ORE: 10 - DEEPSLATE: 10 DIAMOND_ORE: 10 DIORITE: 2 DIRT_PATH: 7 @@ -854,8 +844,8 @@ world: # when player enters the end world. # Added since 1.16. create-obsidian-platform: true - # The probability of a frame being created in a chunk. Frames are always at y=0. - end-frame-probability: 1.0 + # The probability of a frame being created in a chunk. + end-frame-probability: 0.1 # Friendly name for this world. Used in admin commands. Must be a single word friendly-name: SkyGrid # Name of the world - if it does not exist then it will be generated.