From b138c0dcc9fa1c511743a948c19dc225f8208f62 Mon Sep 17 00:00:00 2001 From: Roch Blonndiaux Date: Tue, 14 Mar 2023 15:47:16 +0100 Subject: [PATCH] Some more optimization & runnable -> block populator --- .../java/net/Indyuce/mmoitems/MMOItems.java | 2 +- .../mmoitems/api/block/WorldGenTemplate.java | 259 ++++++++---------- .../mmoitems/api/world/MMOBlockPopulator.java | 75 +++++ .../listener/WorldGenerationListener.java | 21 +- .../mmoitems/manager/WorldGenManager.java | 76 +---- .../tasks/CustomBlocksPopulateTask.java | 72 ----- 6 files changed, 222 insertions(+), 283 deletions(-) create mode 100644 MMOItems-API/src/main/java/net/Indyuce/mmoitems/api/world/MMOBlockPopulator.java delete mode 100644 MMOItems-API/src/main/java/net/Indyuce/mmoitems/tasks/CustomBlocksPopulateTask.java diff --git a/MMOItems-API/src/main/java/net/Indyuce/mmoitems/MMOItems.java b/MMOItems-API/src/main/java/net/Indyuce/mmoitems/MMOItems.java index 64617b2e..c4b542fa 100644 --- a/MMOItems-API/src/main/java/net/Indyuce/mmoitems/MMOItems.java +++ b/MMOItems-API/src/main/java/net/Indyuce/mmoitems/MMOItems.java @@ -168,7 +168,7 @@ public class MMOItems extends JavaPlugin { dropTableManager = new DropTableManager(); worldGenManager = new WorldGenManager(); blockManager = new BlockManager(); - statManager.reload(false); + statManager.reload(false); PluginUtils.hookDependencyIfPresent("Vault", u -> vaultSupport = new VaultSupport()); diff --git a/MMOItems-API/src/main/java/net/Indyuce/mmoitems/api/block/WorldGenTemplate.java b/MMOItems-API/src/main/java/net/Indyuce/mmoitems/api/block/WorldGenTemplate.java index 52497a54..25255e39 100644 --- a/MMOItems-API/src/main/java/net/Indyuce/mmoitems/api/block/WorldGenTemplate.java +++ b/MMOItems-API/src/main/java/net/Indyuce/mmoitems/api/block/WorldGenTemplate.java @@ -1,171 +1,138 @@ package net.Indyuce.mmoitems.api.block; -import java.util.ArrayList; -import java.util.List; - import org.apache.commons.lang.Validate; import org.bukkit.Location; import org.bukkit.Material; -import org.bukkit.World; import org.bukkit.block.Biome; +import org.bukkit.block.Block; import org.bukkit.block.BlockFace; import org.bukkit.configuration.ConfigurationSection; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + public class WorldGenTemplate { - private final String id; - private final double chunkChance; - private final int minDepth, maxDepth, veinSize, veinCount; + private final String id; + private final double chunkChance; + private final int minDepth, maxDepth, veinSize, veinCount; - private final List replaceable = new ArrayList<>(); - private final List bordering = new ArrayList<>(); - private final List notBordering = new ArrayList<>(); - private final List worldWhitelist = new ArrayList<>(), worldBlacklist = new ArrayList<>(); - private final List biomeWhitelist = new ArrayList<>(), biomeBlacklist = new ArrayList<>(); - private final boolean slimeChunk; + private final List replaceable = new ArrayList<>(); + private final List bordering = new ArrayList<>(); + private final List notBordering = new ArrayList<>(); + private final List worldWhitelist = new ArrayList<>(), worldBlacklist = new ArrayList<>(); + private final List biomeWhitelist = new ArrayList<>(), biomeBlacklist = new ArrayList<>(); + private final boolean slimeChunk; - public WorldGenTemplate(ConfigurationSection config) { - Validate.notNull(config, "Could not read gen template config"); + public WorldGenTemplate(ConfigurationSection config) { + Validate.notNull(config, "Could not read gen template config"); - id = config.getName().toLowerCase().replace(" ", "-").replace("_", "-"); - config.getStringList("replace").forEach(str -> replaceable.add(Material.valueOf(str.toUpperCase().replace("-", "_").replace(" ", "_")))); - config.getStringList("bordering").forEach(str -> bordering.add(Material.valueOf(str.toUpperCase().replace("-", "_").replace(" ", "_")))); - config.getStringList("not-bordering").forEach(str -> notBordering.add(Material.valueOf(str.toUpperCase().replace("-", "_").replace(" ", "_")))); - - for (String world : config.getStringList("worlds")) { - (world.startsWith("!") ? worldBlacklist : worldWhitelist).add(world.toLowerCase().replace("_", "-")); - } - for (String biome : config.getStringList("biomes")) { - (biome.startsWith("!") ? biomeBlacklist : biomeWhitelist).add(biome.toUpperCase().replace("-", "_").replace(" ", "_")); - } - chunkChance = config.getDouble("chunk-chance"); - slimeChunk = config.getBoolean("slime-chunk", false); + id = config.getName().toLowerCase().replace(" ", "-").replace("_", "-"); + config.getStringList("replace").forEach(str -> replaceable.add(Material.valueOf(str.toUpperCase().replace("-", "_").replace(" ", "_")))); + config.getStringList("bordering").forEach(str -> bordering.add(Material.valueOf(str.toUpperCase().replace("-", "_").replace(" ", "_")))); + config.getStringList("not-bordering").forEach(str -> notBordering.add(Material.valueOf(str.toUpperCase().replace("-", "_").replace(" ", "_")))); - String[] depth = config.getString("depth").split("="); - minDepth = Integer.parseInt(depth[0]); - maxDepth = Integer.parseInt(depth[1]); - - //Validate.isTrue(minDepth >= 0, "Min depth must be greater than 0"); - //Validate.isTrue(maxDepth < 256, "Max depth must be at most 255"); - - veinSize = config.getInt("vein-size"); - veinCount = config.getInt("vein-count"); - - Validate.isTrue(veinSize > 0 && veinCount > 0, "Vein size and count must be at least 1"); - } - - public String getId() { - return id; - } - - public double getChunkChance() { - return chunkChance; - } - - public int getVeinSize() { - return veinSize; - } - - public int getVeinCount() { - return veinCount; - } - - public int getMinDepth() { - return minDepth; - } - - public int getMaxDepth() { - return maxDepth; - } - - public boolean canGenerateInWorld(World w) { - // check world list - String world = w.getName().toLowerCase().replace("_", "-"); - if (!worldWhitelist.isEmpty() && !worldWhitelist.contains(world)) { - return false; + for (String world : config.getStringList("worlds")) { + (world.startsWith("!") ? worldBlacklist : worldWhitelist).add(world.toLowerCase().replace("_", "-")); } - if (!worldBlacklist.isEmpty() && worldBlacklist.contains(world)) { - return false; + for (String biome : config.getStringList("biomes")) { + (biome.startsWith("!") ? biomeBlacklist : biomeWhitelist).add(biome.toUpperCase().replace("-", "_").replace(" ", "_")); } + chunkChance = config.getDouble("chunk-chance"); + slimeChunk = config.getBoolean("slime-chunk", false); + + String[] depth = config.getString("depth").split("="); + minDepth = Integer.parseInt(depth[0]); + maxDepth = Integer.parseInt(depth[1]); + + //Validate.isTrue(minDepth >= 0, "Min depth must be greater than 0"); + //Validate.isTrue(maxDepth < 256, "Max depth must be at most 255"); + + veinSize = config.getInt("vein-size"); + veinCount = config.getInt("vein-count"); + + Validate.isTrue(veinSize > 0 && veinCount > 0, "Vein size and count must be at least 1"); + } + + public String getId() { + return id; + } + + public double getChunkChance() { + return chunkChance; + } + + public int getVeinSize() { + return veinSize; + } + + public int getVeinCount() { + return veinCount; + } + + public int getMinDepth() { + return minDepth; + } + + public int getMaxDepth() { + return maxDepth; + } + + public boolean canGenerateInWorld(String worldName) { + // check world list + String world = worldName.toLowerCase().replace("_", "-"); + if (!worldWhitelist.isEmpty() && !worldWhitelist.contains(world)) + return false; + return worldBlacklist.isEmpty() || !worldBlacklist.contains(world); + } + + public boolean canGenerate(Location pos) { + + // check biome list + Biome biome = pos.getWorld().getBiome(pos.getBlockX(), pos.getBlockY(), pos.getBlockZ()); + if ((!biomeWhitelist.isEmpty() && !biomeWhitelist.contains(biome.name())) + || (!biomeBlacklist.isEmpty() && biomeBlacklist.contains(biome.name()))) + return false; + + // check extra options + if (slimeChunk && !pos.getChunk().isSlimeChunk()) + return false; + + if (!bordering.isEmpty()) { + if (!checkIfBorderingBlocks(pos)) + return false; + } + + if (!notBordering.isEmpty()) + return checkIfNotBorderingBlocks(pos); + + // can generate if no restrictions applied return true; - } - - public boolean canGenerate(Location pos) { + } - // check biome list - Biome biome = pos.getWorld().getBiome(pos.getBlockX(), pos.getBlockY(), pos.getBlockZ()); - if (!biomeWhitelist.isEmpty() && !biomeWhitelist.contains(biome.name())) { - return false; - } - if (!biomeBlacklist.isEmpty() && biomeBlacklist.contains(biome.name())) { - return false; - } - - // check extra options - if (slimeChunk && !pos.getChunk().isSlimeChunk()) { - return false; - } - - if(!bordering.isEmpty()) { - if(!checkIfBorderingBlocks(pos)) { - return false; - } - } - - if(!notBordering.isEmpty()) { - return checkIfNotBorderingBlocks(pos); - } - - // can generate if no restrictions applied - return true; - } + public boolean canReplace(Material type) { + return replaceable.isEmpty() || replaceable.contains(type); + } - public boolean canReplace(Material type) { - return replaceable.isEmpty() || replaceable.contains(type); - } - - public boolean canBorder(Material type) { + public boolean canBorder(Material type) { return bordering.isEmpty() || bordering.contains(type); } - + public boolean checkIfBorderingBlocks(Location pos) { - if(!canBorder(pos.getBlock().getRelative(BlockFace.NORTH).getType())) { - return false; - } - if(!canBorder(pos.getBlock().getRelative(BlockFace.EAST).getType())) { - return false; - } - if(!canBorder(pos.getBlock().getRelative(BlockFace.SOUTH).getType())) { - return false; - } - if(!canBorder(pos.getBlock().getRelative(BlockFace.WEST).getType())) { - return false; - } - if(!canBorder(pos.getBlock().getRelative(BlockFace.UP).getType())) { - return false; - } - return canBorder(pos.getBlock().getRelative(BlockFace.DOWN).getType()); - } - - public boolean canNotBorder(Material type) { + return Arrays.stream(BlockFace.values()) + .map(pos.getBlock()::getRelative) + .map(Block::getType) + .allMatch(this::canBorder); + } + + public boolean canNotBorder(Material type) { return !notBordering.isEmpty() && notBordering.contains(type); } - - public boolean checkIfNotBorderingBlocks(Location pos) { - if(canNotBorder(pos.getBlock().getRelative(BlockFace.NORTH).getType())) { - return false; - } - if(canNotBorder(pos.getBlock().getRelative(BlockFace.EAST).getType())) { - return false; - } - if(canNotBorder(pos.getBlock().getRelative(BlockFace.SOUTH).getType())) { - return false; - } - if(canNotBorder(pos.getBlock().getRelative(BlockFace.WEST).getType())) { - return false; - } - if(canNotBorder(pos.getBlock().getRelative(BlockFace.UP).getType())) { - return false; - } - return !canNotBorder(pos.getBlock().getRelative(BlockFace.DOWN).getType()); - } + + public boolean checkIfNotBorderingBlocks(Location pos) { + return Arrays.stream(BlockFace.values()) + .map(pos.getBlock()::getRelative) + .map(Block::getType) + .allMatch(this::canNotBorder); + } } diff --git a/MMOItems-API/src/main/java/net/Indyuce/mmoitems/api/world/MMOBlockPopulator.java b/MMOItems-API/src/main/java/net/Indyuce/mmoitems/api/world/MMOBlockPopulator.java new file mode 100644 index 00000000..74139abf --- /dev/null +++ b/MMOItems-API/src/main/java/net/Indyuce/mmoitems/api/world/MMOBlockPopulator.java @@ -0,0 +1,75 @@ +package net.Indyuce.mmoitems.api.world; + +import net.Indyuce.mmoitems.api.block.CustomBlock; +import net.Indyuce.mmoitems.api.block.WorldGenTemplate; +import net.Indyuce.mmoitems.manager.WorldGenManager; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.generator.BlockPopulator; +import org.bukkit.generator.LimitedRegion; +import org.bukkit.generator.WorldInfo; +import org.jetbrains.annotations.NotNull; + +import java.util.Map; +import java.util.Random; + +/** + * mmoitems + * 14/03/2023 + * + * @author Roch Blondiaux (Kiwix). + */ +public class MMOBlockPopulator extends BlockPopulator { + + private static final BlockFace[] faces = {BlockFace.NORTH, BlockFace.SOUTH, BlockFace.WEST, BlockFace.EAST, BlockFace.DOWN, BlockFace.UP}; + + private final WorldGenManager manager; + private final World world; + + public MMOBlockPopulator(World world, WorldGenManager manager) { + this.manager = manager; + this.world = world; + } + + @Override + public void populate(@NotNull WorldInfo worldInfo, @NotNull Random random, int chunkX, int chunkZ, @NotNull LimitedRegion limitedRegion) { + final Map assigned = manager.assigned(); + assigned.entrySet() + .stream() + .filter(entry -> entry.getValue().canGenerateInWorld(worldInfo.getName())) + .filter(entry -> entry.getValue().getMinDepth() >= worldInfo.getMinHeight()) + .filter(entry -> entry.getValue().getMaxDepth() <= worldInfo.getMaxHeight()) + .forEach(entry -> { + final CustomBlock block = entry.getKey(); + final WorldGenTemplate template = entry.getValue(); + if (random.nextDouble() > template.getChunkChance()) + return; + + for (int i = 0; i < template.getVeinCount(); i++) { + int x = chunkX * 16 + random.nextInt(16); + int y = random.nextInt(template.getMaxDepth() - template.getMinDepth() + 1) + template.getMinDepth(); + int z = chunkZ * 16 + random.nextInt(16); + Location generatePoint = new Location(world, x, y, z); + + if (!template.canGenerate(generatePoint) || generatePoint.getWorld() == null) + continue; + Block modify = generatePoint.getWorld().getBlockAt(generatePoint); + + // MMOItems.log("Generating " + block.getId() + " at x: " + generatePoint.getBlockX() + " y: " + generatePoint.getBlockY() + " z: " + generatePoint.getBlockZ()); + for (int j = 0; j < template.getVeinSize(); j++) { + if (!limitedRegion.isInRegion(modify.getLocation())) + continue; + if (template.canReplace(limitedRegion.getType(modify.getLocation()))) { + limitedRegion.setType(modify.getLocation(), block.getState().getType()); + limitedRegion.setBlockData(modify.getLocation(), block.getState().getBlockData()); + } + BlockFace nextFace = faces[random.nextInt(faces.length)]; + modify = modify.getRelative(nextFace); + } + } + }); + } + +} diff --git a/MMOItems-API/src/main/java/net/Indyuce/mmoitems/listener/WorldGenerationListener.java b/MMOItems-API/src/main/java/net/Indyuce/mmoitems/listener/WorldGenerationListener.java index 165e3b20..907bd3a5 100644 --- a/MMOItems-API/src/main/java/net/Indyuce/mmoitems/listener/WorldGenerationListener.java +++ b/MMOItems-API/src/main/java/net/Indyuce/mmoitems/listener/WorldGenerationListener.java @@ -1,10 +1,15 @@ package net.Indyuce.mmoitems.listener; +import net.Indyuce.mmoitems.api.world.MMOBlockPopulator; import net.Indyuce.mmoitems.manager.WorldGenManager; +import org.bukkit.World; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.world.ChunkLoadEvent; +import java.util.HashMap; +import java.util.Map; + /** * mmoitems * 13/03/2023 @@ -19,9 +24,21 @@ public class WorldGenerationListener implements Listener { this.manager = manager; } +// @EventHandler +// public void onWorldInit(WorldInitEvent e) { +// MMOItems.log("Initializing world " + e.getWorld().getName()); +// final World world = e.getWorld(); +// world.getPopulators().add(manager.populator(world)); +// } + + private final Map populatorMap = new HashMap<>(); + @EventHandler public void onChunkLoad(ChunkLoadEvent e) { - if (!e.isNewChunk()) return; - manager.populate(e); + final World world = e.getWorld(); + if (!e.isNewChunk() || populatorMap.containsKey(world.getName())) return; + MMOBlockPopulator populator = manager.populator(world); + world.getPopulators().add(populator); + populatorMap.put(world.getName(), populator); } } diff --git a/MMOItems-API/src/main/java/net/Indyuce/mmoitems/manager/WorldGenManager.java b/MMOItems-API/src/main/java/net/Indyuce/mmoitems/manager/WorldGenManager.java index 7fd41ebd..ef17ec6c 100644 --- a/MMOItems-API/src/main/java/net/Indyuce/mmoitems/manager/WorldGenManager.java +++ b/MMOItems-API/src/main/java/net/Indyuce/mmoitems/manager/WorldGenManager.java @@ -4,25 +4,17 @@ import net.Indyuce.mmoitems.MMOItems; import net.Indyuce.mmoitems.api.ConfigFile; import net.Indyuce.mmoitems.api.block.CustomBlock; import net.Indyuce.mmoitems.api.block.WorldGenTemplate; +import net.Indyuce.mmoitems.api.world.MMOBlockPopulator; import net.Indyuce.mmoitems.listener.WorldGenerationListener; -import net.Indyuce.mmoitems.tasks.CustomBlocksPopulateTask; -import net.Indyuce.mmoitems.util.Pair; import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; -import org.bukkit.Location; -import org.bukkit.block.Block; -import org.bukkit.block.BlockFace; +import org.bukkit.World; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.event.HandlerList; -import org.bukkit.event.world.ChunkLoadEvent; -import org.jetbrains.annotations.Blocking; import org.jetbrains.annotations.NotNull; import java.util.HashMap; import java.util.Map; -import java.util.Queue; -import java.util.Random; -import java.util.concurrent.ConcurrentLinkedQueue; import java.util.logging.Level; public class WorldGenManager implements Reloadable { @@ -34,13 +26,8 @@ public class WorldGenManager implements Reloadable { * world. */ private final Map assigned = new HashMap<>(); - private final Queue> modificationsQueue = new ConcurrentLinkedQueue<>(); - - private static final BlockFace[] faces = {BlockFace.NORTH, BlockFace.SOUTH, BlockFace.WEST, BlockFace.EAST, BlockFace.DOWN, BlockFace.UP}; - private static final Random random = new Random(); private WorldGenerationListener listener; - private CustomBlocksPopulateTask task; public WorldGenManager() { /* @@ -67,48 +54,6 @@ public class WorldGenManager implements Reloadable { assigned.put(block, template); } - public void populate(@NotNull ChunkLoadEvent e) { - Bukkit.getScheduler().runTaskAsynchronously(MMOItems.plugin, () -> { - preprocess(e); - if (task != null && task.isRunning()) - return; - task = new CustomBlocksPopulateTask(this); - task.start(); - }); - } - - private @Blocking void preprocess(ChunkLoadEvent e) { - assigned.entrySet() - .stream() - .filter(entry -> entry.getValue().canGenerateInWorld(e.getWorld())) - .forEach(entry -> { - final CustomBlock block = entry.getKey(); - final WorldGenTemplate template = entry.getValue(); - if (random.nextDouble() > template.getChunkChance()) - return; - - for (int i = 0; i < template.getVeinCount(); i++) { - int y = random.nextInt(template.getMaxDepth() - template.getMinDepth() + 1) + template.getMinDepth(); - Location generatePoint = e.getChunk().getBlock(random.nextInt(16), y, random.nextInt(16)).getLocation(); - - if (!template.canGenerate(generatePoint) || generatePoint.getWorld() == null) - continue; - Block modify = generatePoint.getWorld().getBlockAt(generatePoint); - - for (int j = 0; j < template.getVeinSize(); j++) { - if (template.canReplace(modify.getType())) - this.modificationsQueue.add(Pair.of(modify.getLocation(), block)); - BlockFace nextFace = faces[random.nextInt(faces.length)]; - modify = modify.getRelative(nextFace); - } - } - }); - } - - public Queue> getModificationsQueue() { - return modificationsQueue; - } - public void reload() { // Listener if (listener != null) @@ -116,7 +61,6 @@ public class WorldGenManager implements Reloadable { assigned.clear(); templates.clear(); - modificationsQueue.clear(); FileConfiguration config = new ConfigFile("gen-templates").getConfig(); for (String key : config.getKeys(false)) { @@ -129,14 +73,22 @@ public class WorldGenManager implements Reloadable { } // Listeners - if (MMOItems.plugin.getLanguage().worldGenEnabled) - Bukkit.getPluginManager().registerEvents(listener = new WorldGenerationListener(this), MMOItems.plugin); + if (MMOItems.plugin.getLanguage().worldGenEnabled) { + listener = new WorldGenerationListener(this); + Bukkit.getPluginManager().registerEvents(listener, MMOItems.plugin); + } } public void unload() { if (listener != null) HandlerList.unregisterAll(listener); - if (task != null) - task.stop(); + } + + public MMOBlockPopulator populator(@NotNull World world) { + return new MMOBlockPopulator(world, this); + } + + public Map assigned() { + return assigned; } } diff --git a/MMOItems-API/src/main/java/net/Indyuce/mmoitems/tasks/CustomBlocksPopulateTask.java b/MMOItems-API/src/main/java/net/Indyuce/mmoitems/tasks/CustomBlocksPopulateTask.java deleted file mode 100644 index d9630e05..00000000 --- a/MMOItems-API/src/main/java/net/Indyuce/mmoitems/tasks/CustomBlocksPopulateTask.java +++ /dev/null @@ -1,72 +0,0 @@ -package net.Indyuce.mmoitems.tasks; - -import net.Indyuce.mmoitems.MMOItems; -import net.Indyuce.mmoitems.api.block.CustomBlock; -import net.Indyuce.mmoitems.manager.WorldGenManager; -import net.Indyuce.mmoitems.util.Pair; -import org.bukkit.Bukkit; -import org.bukkit.Location; -import org.bukkit.block.Block; -import org.bukkit.scheduler.BukkitRunnable; - -import java.util.Queue; - -/** - * mmoitems - * 13/03/2023 - * - * @author Roch Blondiaux (Kiwix). - */ -public class CustomBlocksPopulateTask extends BukkitRunnable { - - private final WorldGenManager manager; - private boolean running = false; - - public CustomBlocksPopulateTask(WorldGenManager manager) { - this.manager = manager; - } - - @Override - public void run() { - final Queue> modificationsQueue = manager.getModificationsQueue(); - final Pair pair = modificationsQueue.poll(); - - // If the queue is empty, cancel the task - if (pair == null) { - this.stop(); - return; - } - - // If the chunk is not loaded, skip it - if (!pair.getKey().getChunk().isLoaded()) - return; - - // If the block is already modified, skip it - if (pair.getKey().getBlock().getBlockData().equals(pair.getValue().getState().getBlockData())) - return; - - // Change the block - Bukkit.getScheduler().runTask(MMOItems.plugin, () -> setBlockData(pair.getKey().getBlock(), pair.getValue())); - } - - private void setBlockData(Block fModify, CustomBlock block) { - fModify.setType(block.getState().getType(), false); - fModify.setBlockData(block.getState().getBlockData(), false); - } - - public void start() { - if (running) return; - running = true; - this.runTaskTimerAsynchronously(MMOItems.plugin, 0, 1); - } - - public void stop() { - if (!running) return; - running = false; - this.cancel(); - } - - public boolean isRunning() { - return running; - } -}