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 69428310..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()); @@ -254,7 +254,6 @@ public class MMOItems extends JavaPlugin { @Override public void onDisable() { - // Support for early plugin disabling if (!hasLoadedSuccessfully) return; @@ -269,6 +268,9 @@ public class MMOItems extends JavaPlugin { for (Player player : Bukkit.getOnlinePlayers()) if (player.getOpenInventory() != null && player.getOpenInventory().getTopInventory().getHolder() instanceof PluginInventory) player.closeInventory(); + + // WorldGen + this.worldGenManager.unload(); } public String getPrefix() { 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 new file mode 100644 index 00000000..907bd3a5 --- /dev/null +++ b/MMOItems-API/src/main/java/net/Indyuce/mmoitems/listener/WorldGenerationListener.java @@ -0,0 +1,44 @@ +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 + * + * @author Roch Blondiaux (Kiwix). + */ +public class WorldGenerationListener implements Listener { + + private final WorldGenManager manager; + + public WorldGenerationListener(WorldGenManager manager) { + 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) { + 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 06d88b29..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,108 +4,91 @@ 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 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.EventHandler; -import org.bukkit.event.Listener; -import org.bukkit.event.world.ChunkLoadEvent; +import org.bukkit.event.HandlerList; +import org.jetbrains.annotations.NotNull; import java.util.HashMap; import java.util.Map; -import java.util.Random; import java.util.logging.Level; -public class WorldGenManager implements Listener, Reloadable { - private final Map templates = new HashMap<>(); +public class WorldGenManager implements Reloadable { + private final Map templates = new HashMap<>(); - /* - * maps a custom block to the world generator template so that it is later - * easier to access all the blocks which must be placed when generating a - * world. - */ - private final Map assigned = new HashMap<>(); + /* + * maps a custom block to the world generator template so that it is later + * easier to access all the blocks which must be placed when generating a + * world. + */ + private final Map assigned = new HashMap<>(); - 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; - public WorldGenManager() { + public WorldGenManager() { + /* + * load the worldGenManager even if world gen is not enabled so that if + * admins temporarily disable it, there is no console error spam saying + * MI could not find corresponding gen template in config + */ + reload(); + } - /* - * load the worldGenManager even if world gen is not enabled so that if - * admins temporarily disable it, there is no console error spam saying - * MI could not find corresponding gen template in config - */ - reload(); + public WorldGenTemplate getOrThrow(String id) { + Validate.isTrue(templates.containsKey(id), "Could not find gen template with ID '" + id + "'"); - if (MMOItems.plugin.getLanguage().worldGenEnabled) - Bukkit.getPluginManager().registerEvents(this, MMOItems.plugin); - } + return templates.get(id); + } - public WorldGenTemplate getOrThrow(String id) { - Validate.isTrue(templates.containsKey(id), "Could not find gen template with ID '" + id + "'"); + /* + * it is mandatory to call this function after registering the custom block + * if you want the custom block to be spawning in the worlds + */ + public void assign(CustomBlock block, WorldGenTemplate template) { + Validate.notNull(template, "Cannot assign a null template to a custom block"); - return templates.get(id); - } + assigned.put(block, template); + } - /* - * it is mandatory to call this function after registering the custom block - * if you want the custom block to be spawning in the worlds - */ - public void assign(CustomBlock block, WorldGenTemplate template) { - Validate.notNull(template, "Cannot assign a null template to a custom block"); + public void reload() { + // Listener + if (listener != null) + HandlerList.unregisterAll(listener); - assigned.put(block, template); - } + assigned.clear(); + templates.clear(); - @EventHandler - public void a(ChunkLoadEvent event) { - if(event.isNewChunk()) { - Bukkit.getScheduler().runTaskAsynchronously(MMOItems.plugin, () -> assigned.forEach((block, template) -> { - if(!template.canGenerateInWorld(event.getWorld())) { - return; - } - if(random.nextDouble() < template.getChunkChance()) - for(int i = 0; i < template.getVeinCount(); i++) { - int y = random.nextInt(template.getMaxDepth() - template.getMinDepth() + 1) + template.getMinDepth(); - Location generatePoint = event.getChunk().getBlock(random.nextInt(16), y, random.nextInt(16)).getLocation(); + FileConfiguration config = new ConfigFile("gen-templates").getConfig(); + for (String key : config.getKeys(false)) { + try { + WorldGenTemplate template = new WorldGenTemplate(config.getConfigurationSection(key)); + templates.put(template.getId(), template); + } catch (IllegalArgumentException exception) { + MMOItems.plugin.getLogger().log(Level.WARNING, "An error occurred when loading gen template '" + key + "': " + exception.getMessage()); + } + } - if(template.canGenerate(generatePoint)) { - Block modify = generatePoint.getWorld().getBlockAt(generatePoint); + // Listeners + if (MMOItems.plugin.getLanguage().worldGenEnabled) { + listener = new WorldGenerationListener(this); + Bukkit.getPluginManager().registerEvents(listener, MMOItems.plugin); + } + } - for(int j = 0; j < template.getVeinSize(); j++) { - if(template.canReplace(modify.getType())) { - final Block fModify = modify; - Bukkit.getScheduler().runTask(MMOItems.plugin, () -> { - fModify.setType(block.getState().getType(), false); - fModify.setBlockData(block.getState().getBlockData(), false); - }); - } + public void unload() { + if (listener != null) + HandlerList.unregisterAll(listener); + } - BlockFace nextFace = faces[random.nextInt(faces.length)]; - modify = modify.getRelative(nextFace); - } - } - } - })); - } - } + public MMOBlockPopulator populator(@NotNull World world) { + return new MMOBlockPopulator(world, this); + } - public void reload() { - assigned.clear(); - templates.clear(); - - FileConfiguration config = new ConfigFile("gen-templates").getConfig(); - for(String key : config.getKeys(false)) { - try { - WorldGenTemplate template = new WorldGenTemplate(config.getConfigurationSection(key)); - templates.put(template.getId(), template); - } catch (IllegalArgumentException exception) { - MMOItems.plugin.getLogger().log(Level.WARNING, "An error occurred when loading gen template '" + key + "': " + exception.getMessage()); - } - } - } + public Map assigned() { + return assigned; + } }