From c869fdefb1bc065aa0c6780b3e41057a2634ada9 Mon Sep 17 00:00:00 2001 From: Nick Guy Date: Tue, 29 Jul 2025 21:34:56 +0100 Subject: [PATCH] Add BiomeRod feature. - Implemented BiomeRod for biome transformation with configurable radius. - Updated configuration files and resource handling. - Integrated reload command for dynamic biome mapping updates. --- .../com/ncguy/annotations/Remarkable.java | 11 + .../ncguy/processors/RemarkableProcessor.java | 25 ++ settings.gradle | 2 + .../ncguy/usefulskyblock/UsefulSkyblock.java | 49 +++- .../command/SkyblockAdminCommand.java | 11 + .../command/SkyblockGenCommand.java | 53 +++- .../ncguy/usefulskyblock/recipe/BiomeRod.java | 234 ++++++++++++++++++ .../utils/BossBarProgressMonitor.java | 67 +++++ .../utils/IProgressMonitor.java | 13 + .../ncguy/usefulskyblock/utils/Remark.java | 12 + .../ncguy/usefulskyblock/utils/RemarkSet.java | 11 + .../usefulskyblock/world/PortalHandler.java | 17 +- src/main/resources/config.yml | 17 ++ .../data/minecraft/dimension/the_nether.json | 2 +- 14 files changed, 512 insertions(+), 12 deletions(-) create mode 100644 annotation-processor/src/main/java/com/ncguy/annotations/Remarkable.java create mode 100644 annotation-processor/src/main/java/com/ncguy/processors/RemarkableProcessor.java create mode 100644 src/main/java/com/ncguy/usefulskyblock/recipe/BiomeRod.java create mode 100644 src/main/java/com/ncguy/usefulskyblock/utils/BossBarProgressMonitor.java create mode 100644 src/main/java/com/ncguy/usefulskyblock/utils/IProgressMonitor.java create mode 100644 src/main/java/com/ncguy/usefulskyblock/utils/Remark.java create mode 100644 src/main/java/com/ncguy/usefulskyblock/utils/RemarkSet.java create mode 100644 src/main/resources/config.yml diff --git a/annotation-processor/src/main/java/com/ncguy/annotations/Remarkable.java b/annotation-processor/src/main/java/com/ncguy/annotations/Remarkable.java new file mode 100644 index 0000000..35ee21d --- /dev/null +++ b/annotation-processor/src/main/java/com/ncguy/annotations/Remarkable.java @@ -0,0 +1,11 @@ +package com.ncguy.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.SOURCE) +public @interface Remarkable { +} diff --git a/annotation-processor/src/main/java/com/ncguy/processors/RemarkableProcessor.java b/annotation-processor/src/main/java/com/ncguy/processors/RemarkableProcessor.java new file mode 100644 index 0000000..2f4e87a --- /dev/null +++ b/annotation-processor/src/main/java/com/ncguy/processors/RemarkableProcessor.java @@ -0,0 +1,25 @@ +package com.ncguy.processors; + +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.RoundEnvironment; +import javax.annotation.processing.SupportedAnnotationTypes; +import javax.annotation.processing.SupportedSourceVersion; +import javax.lang.model.SourceVersion; +import javax.lang.model.element.Element; +import javax.lang.model.element.TypeElement; +import javax.tools.JavaFileObject; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +@SupportedAnnotationTypes("com.ncguy.annotations.Remarkable") +@SupportedSourceVersion(SourceVersion.RELEASE_21) +public class RemarkableProcessor extends AbstractProcessor { + + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + return false; + } + +} diff --git a/settings.gradle b/settings.gradle index 651edba..df924b5 100644 --- a/settings.gradle +++ b/settings.gradle @@ -7,3 +7,5 @@ plugins { id 'org.gradle.toolchains.foojay-resolver-convention' version '0.8.0' } rootProject.name = 'UsefulSkyblock' + +include 'annotation-processor' \ No newline at end of file diff --git a/src/main/java/com/ncguy/usefulskyblock/UsefulSkyblock.java b/src/main/java/com/ncguy/usefulskyblock/UsefulSkyblock.java index 8360e5d..989da38 100644 --- a/src/main/java/com/ncguy/usefulskyblock/UsefulSkyblock.java +++ b/src/main/java/com/ncguy/usefulskyblock/UsefulSkyblock.java @@ -1,7 +1,13 @@ package com.ncguy.usefulskyblock; +import com.destroystokyo.paper.event.player.PlayerPostRespawnEvent; +import com.ncguy.usefulskyblock.recipe.BiomeRod; +import com.ncguy.usefulskyblock.utils.Remark; +import com.ncguy.usefulskyblock.utils.RemarkSet; import com.ncguy.usefulskyblock.world.PortalHandler; import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.Style; +import net.kyori.adventure.text.format.TextColor; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.NamespacedKey; @@ -28,12 +34,33 @@ public final class UsefulSkyblock extends JavaPlugin implements Listener { private static final Logger log = LoggerFactory.getLogger(UsefulSkyblock.class); + private BiomeRod biomeRodListener; + @Override public void onEnable() { + + System.out.println("OM EMABL:ED"); + + saveDefaultConfig(); + // Plugin startup logic PluginManager pluginManager = Bukkit.getPluginManager(); pluginManager.registerEvents(this, this); pluginManager.registerEvents(new PortalHandler(), this); + biomeRodListener = new BiomeRod(); + pluginManager.registerEvents(biomeRodListener, this); + BiomeRod.Init(Bukkit.getServer()); + } + + @Override + public void reloadConfig() { + super.reloadConfig(); + if(biomeRodListener == null) + return; + RemarkSet remarks = biomeRodListener.reloadBiomeMap(); + for (Remark remark : remarks) { + Bukkit.getServer().broadcast(Component.text(remark.domain, Style.style(TextColor.fromHexString("#777"))).appendSpace().append(Component.text(remark.message, Style.style(TextColor.fromHexString("#fff"))))); + } } @Override @@ -47,12 +74,30 @@ public final class UsefulSkyblock extends JavaPlugin implements Listener { player.sendMessage(Component.text("Hello, " + player.getName() + "!")); PersistentDataContainer pdc = player.getPersistentDataContainer(); NamespacedKey initKey = new NamespacedKey(this, "player_init"); - if (pdc.has(initKey)) - return; +// if (pdc.has(initKey)) +// return; NamespacedKey worldKey = new NamespacedKey(this, "void"); World world = Bukkit.getWorld(worldKey); player.teleport(world.getSpawnLocation(), PlayerTeleportEvent.TeleportCause.PLUGIN); + player.setRespawnLocation(world.getSpawnLocation()); + pdc.set(initKey, PersistentDataType.BOOLEAN, true); + player.sendMessage(Component.text("This is your first time playing on this server.")); + } + + @EventHandler + public void onPlayerPostRespawn(PlayerPostRespawnEvent event) { + Player player = event.getPlayer(); + player.sendMessage(Component.text("Hello, " + player.getName() + "!")); + PersistentDataContainer pdc = player.getPersistentDataContainer(); + NamespacedKey initKey = new NamespacedKey(this, "player_init"); +// if (pdc.has(initKey)) +// return; + + NamespacedKey worldKey = new NamespacedKey(this, "void"); + World world = Bukkit.getWorld(worldKey); + player.teleport(world.getSpawnLocation(), PlayerTeleportEvent.TeleportCause.PLUGIN); + player.setRespawnLocation(world.getSpawnLocation()); pdc.set(initKey, PersistentDataType.BOOLEAN, true); player.sendMessage(Component.text("This is your first time playing on this server.")); } diff --git a/src/main/java/com/ncguy/usefulskyblock/command/SkyblockAdminCommand.java b/src/main/java/com/ncguy/usefulskyblock/command/SkyblockAdminCommand.java index 8533908..6e185ec 100644 --- a/src/main/java/com/ncguy/usefulskyblock/command/SkyblockAdminCommand.java +++ b/src/main/java/com/ncguy/usefulskyblock/command/SkyblockAdminCommand.java @@ -21,6 +21,7 @@ import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import org.bukkit.persistence.PersistentDataContainer; import org.bukkit.persistence.PersistentDataType; +import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.structure.Structure; import org.bukkit.structure.StructureManager; import org.bukkit.util.RayTraceResult; @@ -225,6 +226,9 @@ public class SkyblockAdminCommand extends AbstractSkyblockCommand { root.requires(cmd::auth); + + root.then(Commands.literal("reload").executes(cmd::executeReloadConfig)); + var structures = Commands.literal("structures"); structures.then(Commands.literal("list").executes(cmd::executeStructuresList)); structures.then(Commands.literal("save").then(Commands.argument("radius", IntegerArgumentType.integer()).then(Commands.argument("name", StringArgumentType.word()).executes(cmd::executeSave)))); @@ -240,6 +244,13 @@ public class SkyblockAdminCommand extends AbstractSkyblockCommand { return root.build(); } + private int executeReloadConfig(CommandContext context) { + JavaPlugin.getProvidingPlugin(SkyblockAdminCommand.class).reloadConfig(); + + context.getSource().getExecutor().sendMessage("Reloaded config"); + return 0; + } + private boolean auth(CommandSourceStack stack) { return stack.getSender().isOp(); } diff --git a/src/main/java/com/ncguy/usefulskyblock/command/SkyblockGenCommand.java b/src/main/java/com/ncguy/usefulskyblock/command/SkyblockGenCommand.java index 428e885..11bccf4 100644 --- a/src/main/java/com/ncguy/usefulskyblock/command/SkyblockGenCommand.java +++ b/src/main/java/com/ncguy/usefulskyblock/command/SkyblockGenCommand.java @@ -7,8 +7,11 @@ import com.ncguy.usefulskyblock.utils.MathsUtils; import io.papermc.paper.command.brigadier.CommandSourceStack; import io.papermc.paper.command.brigadier.Commands; import org.bukkit.Location; +import org.bukkit.Material; import org.bukkit.NamespacedKey; import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.block.BlockState; import org.bukkit.block.structure.Mirror; import org.bukkit.block.structure.StructureRotation; import org.bukkit.entity.Entity; @@ -23,6 +26,7 @@ import org.bukkit.util.Vector; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.Optional; import java.util.Random; import java.util.function.Supplier; @@ -82,11 +86,33 @@ public class SkyblockGenCommand extends AbstractSkyblockCommand { private static float T2_ISLAND_SPACING = T2_ISLAND_SLOTS << 3; private static float T3_ISLAND_SPACING = T3_ISLAND_SLOTS << 3; - private Location placeStructureAtLocation(Structure structure, Location loc) { + private Location placeStructureAtLocation(Structure structure, Location loc, boolean canRotate, boolean canMirror) { Vector extents = structure.getSize().clone().multiply(0.5); loc.subtract(extents); - structure.place(loc, true, randomEnum(StructureRotation.class), randomEnum(Mirror.class), 0, 1, new Random()); + StructureRotation rotation = canRotate ? randomEnum(StructureRotation.class) : StructureRotation.NONE; + Mirror mirror = canMirror ? randomEnum(Mirror.class) : Mirror.NONE; + structure.place(loc, true, rotation, mirror, 0, 1, new Random()); + +// Optional bedrock2 = structure.getPalettes().getFirst().getBlocks().stream().filter(b -> b.getBlockData().getMaterial() == Material.BEDROCK).findFirst(); + + Optional bedrock = Optional.empty(); + for(int x = loc.getBlockX(); x < loc.getBlockX() + structure.getSize().getBlockX(); x++) { + for(int y = loc.getBlockY(); y < loc.getBlockY() + structure.getSize().getBlockY(); y++) { + for(int z = loc.getBlockZ(); z < loc.getBlockZ() + structure.getSize().getBlockZ(); z++) { + Block b = loc.getWorld().getBlockAt(x, y, z); + if(b.getType() == Material.BEDROCK) { + bedrock = Optional.of(b.getState()); + } + } + } + } + + if(bedrock.isPresent()) { + loc = bedrock.get().getLocation(); + loc.add(0, 2, 0); + } + return loc; } @@ -101,7 +127,7 @@ public class SkyblockGenCommand extends AbstractSkyblockCommand { } private Location generateIslandNetwork(Location origin) { - Location centralIslandSpawnLoc = placeStructureAtLocation(randomElement(centralIslands), origin.clone()); + Location centralIslandSpawnLoc = placeStructureAtLocation(randomElement(centralIslands), origin.clone(), false, false); int[] t1Slots = MathsUtils.sampleUniqueInts(T1_ISLAND_SLOTS, t1Islands.length); int[] t2Slots = MathsUtils.sampleUniqueInts(T2_ISLAND_SLOTS, t2Islands.length); @@ -127,7 +153,7 @@ public class SkyblockGenCommand extends AbstractSkyblockCommand { double z = Math.sin(angle) * islandSpacing; double y = yNoiseFunc.get(); Location pos = origin.clone().add(x, y, z); - placeStructureAtLocation(island, pos); + placeStructureAtLocation(island, pos, true, true); } } @@ -148,9 +174,21 @@ public class SkyblockGenCommand extends AbstractSkyblockCommand { executor.sendMessage("Not part of a team, can't generate skyblock world"); return 0; } + // TODO Sanitize playerTeam.getName() + String playerTeamName = playerTeam.getName(); + + PersistentDataContainer worldPDC = overworld.getPersistentDataContainer(); +// worldPDC.set(key("skyblock.team." + playerTeam.getName()), PersistentDataType.INTEGER_ARRAY, new int[]{tpLoc.getBlockX(), tpLoc.getBlockY(), tpLoc.getBlockZ()}); + if(worldPDC.has(key("skyblock.team." + playerTeamName), PersistentDataType.INTEGER_ARRAY)) { + int[] ints = worldPDC.get(key("skyblock.team." + playerTeamName), PersistentDataType.INTEGER_ARRAY); + Location loc = new Location(overworld, ints[0], ints[1], ints[2]); + executor.teleport(loc); + pdc.set(key("island.home.loc"), PersistentDataType.INTEGER_ARRAY, ints); + executor.sendMessage("Teleported to island home"); + return 0; + } executor.sendMessage("Generating skyblock world for " + playerTeam.getName() + "..."); - PersistentDataContainer worldPDC = overworld.getPersistentDataContainer(); int count = 0; if(worldPDC.has(key("skyblock.team.count"), PersistentDataType.INTEGER)) { @@ -167,7 +205,7 @@ public class SkyblockGenCommand extends AbstractSkyblockCommand { int numSlots = MathsUtils.getSlotsInRing(ring); - float step = numSlots / 360f; + float step = 360f / numSlots; float angle = slot * step; float x = (float) Math.cos(angle) * playerIslandSpacing; float y = (float) Math.sin(angle) * playerIslandSpacing; @@ -176,8 +214,7 @@ public class SkyblockGenCommand extends AbstractSkyblockCommand { Location tpLoc = generateIslandNetwork(originLoc); - // TODO Sanitize playerTeam.getName() - worldPDC.set(key("skyblock.team." + playerTeam.getName()), PersistentDataType.INTEGER_ARRAY, new int[]{tpLoc.getBlockX(), tpLoc.getBlockY(), tpLoc.getBlockZ()}); + worldPDC.set(key("skyblock.team." + playerTeamName), PersistentDataType.INTEGER_ARRAY, new int[]{tpLoc.getBlockX(), tpLoc.getBlockY(), tpLoc.getBlockZ()}); worldPDC.set(key("skyblock.team.count"), PersistentDataType.INTEGER, count + 1); executor.teleport(tpLoc); diff --git a/src/main/java/com/ncguy/usefulskyblock/recipe/BiomeRod.java b/src/main/java/com/ncguy/usefulskyblock/recipe/BiomeRod.java new file mode 100644 index 0000000..64e632a --- /dev/null +++ b/src/main/java/com/ncguy/usefulskyblock/recipe/BiomeRod.java @@ -0,0 +1,234 @@ +package com.ncguy.usefulskyblock.recipe; + +import com.ncguy.usefulskyblock.UsefulSkyblock; +import com.ncguy.usefulskyblock.utils.BossBarProgressMonitor; +import com.ncguy.usefulskyblock.utils.IProgressMonitor; +import com.ncguy.usefulskyblock.utils.Remark; +import com.ncguy.usefulskyblock.utils.RemarkSet; +import io.papermc.paper.registry.RegistryAccess; +import io.papermc.paper.registry.RegistryKey; +import io.papermc.paper.util.Tick; +import net.kyori.adventure.bossbar.BossBar; +import net.kyori.adventure.text.Component; +import org.bukkit.*; +import org.bukkit.block.Biome; +import org.bukkit.block.Block; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.entity.BlockDisplay; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.ShapedRecipe; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.persistence.PersistentDataContainer; +import org.bukkit.persistence.PersistentDataType; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.java.JavaPlugin; +import org.bukkit.scheduler.BukkitScheduler; +import org.bukkit.util.BlockVector; +import org.bukkit.util.Transformation; +import org.joml.AxisAngle4f; +import org.joml.Vector3f; + +import java.time.Duration; +import java.util.*; + +public class BiomeRod implements Listener { + + private static String Namespace = "usefulskyblock"; + + private static NamespacedKey rodkey = new NamespacedKey(Namespace, "biomerod"); + private static NamespacedKey flag = new NamespacedKey(Namespace, "flag_biomerod"); + + public static void Init(Server server) { + + ItemStack sign = ItemStack.of(Material.END_ROD); + ItemMeta meta = sign.getItemMeta(); + PersistentDataContainer pdc = meta.getPersistentDataContainer(); + pdc.set(flag, PersistentDataType.STRING, flag.asString()); + meta.setMaxStackSize(1); + meta.customName(Component.text("Biome Rod")); + meta.lore(Arrays.stream(new String[]{ + "A mystical tool allowing manipulation of the world." + }).map(Component::text).toList()); + sign.setItemMeta(meta); + + ShapedRecipe signRecipe = new ShapedRecipe(rodkey, sign); + signRecipe.shape(" ", "DSD", " "); + signRecipe.setIngredient('D', Material.DIAMOND); + signRecipe.setIngredient('S', Material.STICK); + + server.addRecipe(signRecipe); + } + + public BiomeRod() { + reloadBiomeMap(); + } + + public RemarkSet reloadBiomeMap() { + RemarkSet set = new RemarkSet(); + Plugin plugin = Bukkit.getPluginManager().getPlugin("UsefulSkyblock"); + FileConfiguration config = plugin.getConfig(); + List> o = (List>) config.getList("skyblock.biomes.biome-map"); + + Registry biomeRegistry = RegistryAccess.registryAccess().getRegistry(RegistryKey.BIOME); + + biomeMap.clear(); + + for(Map m : Objects.requireNonNull(o)) { + String materialName = m.get("material"); + String biomeName = m.get("biome"); + Material material = Material.getMaterial(materialName); + Biome biome = biomeRegistry.get(Objects.requireNonNull(NamespacedKey.fromString(biomeName))); + + if(material == null || biome == null) { + System.err.println("Invalid biome rod material or biome: " + materialName + " " + biomeName); + if(material == null) + set.add("biome-map", "Invalid biome rod material: " + materialName); + if(biome == null) + set.add("biome-map", "Invalid biome rod biome: " + biomeName); + continue; + } + + biomeMap.put(material, biome); + } + return set; + } + + private boolean isItemStackBiomeSign(ItemStack itemStack) { + if(itemStack == null) + return false; + PersistentDataContainer pdc = itemStack.getItemMeta().getPersistentDataContainer(); + return pdc.has(flag, PersistentDataType.STRING); + } + + private Map biomeMap = new HashMap<>(); + + private boolean isBlockValidBiomeKey(Block block) { + return biomeMap.containsKey(block.getType()); + } + + @EventHandler + public void onBiomeSignPlace(BlockPlaceEvent event) { + ItemStack itemInHand = event.getItemInHand(); + if(!isItemStackBiomeSign(itemInHand)) + return; + if(!isBlockValidBiomeKey(event.getBlockAgainst())) { + event.setBuild(false); + Player player = event.getPlayer(); + player.sendMessage(Component.text(event.getBlockAgainst().getType().name() + " is not a valid biome anchor.")); + return; + } + + final World world = event.getBlockAgainst().getWorld(); + + List biomeLocations = new ArrayList<>(); + + JavaPlugin plugin = JavaPlugin.getPlugin(UsefulSkyblock.class); + FileConfiguration config = plugin.getConfig(); + final int radius = config.getInt("skyblock.biomes.rod-radius", 4); + + System.out.println("Modifying biomes with radius: " + radius); + + BlockVector center = new BlockVector(event.getBlockAgainst().getX(), event.getBlockAgainst().getY(), event.getBlockAgainst().getZ()); + + for(int x = -radius; x <= radius; x++) { + for(int y = -radius; y <= radius; y++) { + for(int z = -radius; z <= radius; z++) { + BlockVector vec = new BlockVector(x, y, z); + vec.add(center); + if(vec.distance(center) <= radius) + biomeLocations.add(new Location(world, vec.getX(), vec.getY(), vec.getZ())); + } + } + } + Biome biome = biomeMap.get(event.getBlockAgainst().getType()); + event.getPlayer().sendMessage(Component.text("Converting to biome " + biome + " with radius " + radius + " at " + center + ". Blocks to change: " + biomeLocations.size())); + + Queue locationQueue = new LinkedList<>(biomeLocations.stream().sorted((a, b) -> (int) Math.round(a.toVector().distanceSquared(center) - b.toVector().distanceSquared(center))).toList()); + BukkitScheduler scheduler = Bukkit.getServer().getScheduler(); + + BossBarProgressMonitor progress = new BossBarProgressMonitor(BossBar.bossBar(Component.text(""), 0, BossBar.Color.GREEN, BossBar.Overlay.PROGRESS), event.getPlayer()); + progress.setTitle(Component.text("Converting biome to").appendSpace().append(Component.translatable(biome.translationKey()))); + progress.setMaxProgress(biomeLocations.size()); + progress.setInverseProgress(true); + + int batchSize = biomeLocations.size() / sinkDurationTicks; + modifyBiomeAsync(world, locationQueue, biome, plugin, scheduler, batchSize, progress); + sinkRodIntoBlock(event.getBlockPlaced(), plugin, scheduler); + } + + private static int sinkDurationTicks = Tick.tick().fromDuration(Duration.ofSeconds(2)); + private static void sinkRodIntoBlock(Block block, JavaPlugin plugin, BukkitScheduler scheduler) { + World world = block.getWorld(); + world.spawn(block.getLocation(), BlockDisplay.class, e -> { + e.setBlock(block.getBlockData()); + e.setTransformation(new Transformation( + new Vector3f(0, 0, 0), + new AxisAngle4f(0, 0, 0, 1), + new Vector3f(1, 1, 1), + new AxisAngle4f(0, 0, 0, 1) + )); + + e.setPersistent(false); + sinkRodIntoBlock(e, plugin, scheduler, sinkDurationTicks); + }); + block.setType(Material.AIR); + } + private static void sinkRodIntoBlock(BlockDisplay display, JavaPlugin plugin, BukkitScheduler scheduler, int remainder) { + if(remainder == 0) { + display.remove(); + return; + } + + float progress = (float) remainder / sinkDurationTicks; + float scale = 1.0f - progress; + + float meshScale = (progress * 0.5f) + 0.5f; + float meshOffset = scale * 0.5f; + + display.setTransformation(new Transformation( + new Vector3f(0, -meshOffset, 0), + new AxisAngle4f(0, 0, 0, 1), + new Vector3f(1, meshScale, 1), + new AxisAngle4f(0, 0, 0, 1) + )); + + scheduler.runTaskLater(plugin, () -> sinkRodIntoBlock(display, plugin, scheduler, remainder - 1), 1); + } + + private static boolean modifyBiomeAsync(World world, Queue locations, Biome biome, JavaPlugin plugin, BukkitScheduler scheduler, int batchSize, IProgressMonitor progress) { + if(locations.isEmpty()) { + progress.finish(); + return true; + } + + Set chunksToRefresh = new HashSet<>(); + + for(int i = 0; i < Math.min(batchSize, locations.size()); i++) { + Location loc = locations.remove(); + if(loc == null) { + progress.finish(); + return true; + } + world.setBiome(loc, biome); + Chunk chunkAt = world.getChunkAt(loc); + chunksToRefresh.add(chunkAt); + } + + progress.setProgress(locations.size()); + + chunksToRefresh.forEach(x -> world.refreshChunk(x.getX(), x.getZ())); + + if(locations.isEmpty()) { + progress.finish(); + return true; + } + + scheduler.runTaskLater(plugin, () -> modifyBiomeAsync(world, locations, biome, plugin, scheduler, batchSize, progress), 1); + return false; + } + +} diff --git a/src/main/java/com/ncguy/usefulskyblock/utils/BossBarProgressMonitor.java b/src/main/java/com/ncguy/usefulskyblock/utils/BossBarProgressMonitor.java new file mode 100644 index 0000000..ff3e307 --- /dev/null +++ b/src/main/java/com/ncguy/usefulskyblock/utils/BossBarProgressMonitor.java @@ -0,0 +1,67 @@ +package com.ncguy.usefulskyblock.utils; + +import net.kyori.adventure.audience.Audience; +import net.kyori.adventure.bossbar.BossBar; +import net.kyori.adventure.text.Component; + +public class BossBarProgressMonitor implements IProgressMonitor { + + private BossBar bar; + private int progress = 1; + private int maxProgress = 1; + private boolean inverseProgress = false; + private Audience[] viewers; + + public BossBarProgressMonitor(BossBar bar, Audience... viewers) { + this.bar = bar; + this.viewers = viewers; + + for (Audience viewer : viewers) { + viewer.showBossBar(bar); + } + } + + private void updateBar() { + float prg = progress / (float) maxProgress; + if(inverseProgress) + prg = 1 - prg; + + prg = Math.clamp(prg, 0, 1); + bar.progress(prg); + } + + public void setTitle(Component component) { + bar.name(component); + } + public void setTitle(String title) { + bar.name(Component.text(title)); + } + + @Override + public void setMessage(String message) { + // Not implemented + } + + @Override + public void setProgress(int progress) { + this.progress = progress; + updateBar(); + } + + @Override + public void setMaxProgress(int maxProgress) { + this.maxProgress = maxProgress; + updateBar(); + } + + @Override + public void setInverseProgress(boolean inverseProgress) { + this.inverseProgress = inverseProgress; + } + + @Override + public void finish() { + for (Audience viewer : viewers) + bar.removeViewer(viewer); + } +} diff --git a/src/main/java/com/ncguy/usefulskyblock/utils/IProgressMonitor.java b/src/main/java/com/ncguy/usefulskyblock/utils/IProgressMonitor.java new file mode 100644 index 0000000..b8987f7 --- /dev/null +++ b/src/main/java/com/ncguy/usefulskyblock/utils/IProgressMonitor.java @@ -0,0 +1,13 @@ +package com.ncguy.usefulskyblock.utils; + +public interface IProgressMonitor { + + void setTitle(String title); + void setMessage(String message); + void setProgress(int progress); + void setMaxProgress(int maxProgress); + void setInverseProgress(boolean inverseProgress); + + void finish(); + +} diff --git a/src/main/java/com/ncguy/usefulskyblock/utils/Remark.java b/src/main/java/com/ncguy/usefulskyblock/utils/Remark.java new file mode 100644 index 0000000..e3eefe4 --- /dev/null +++ b/src/main/java/com/ncguy/usefulskyblock/utils/Remark.java @@ -0,0 +1,12 @@ +package com.ncguy.usefulskyblock.utils; + +public class Remark { + + public String domain; + public String message; + + public Remark(String domain, String message) { + this.domain = domain; + this.message = message; + } +} diff --git a/src/main/java/com/ncguy/usefulskyblock/utils/RemarkSet.java b/src/main/java/com/ncguy/usefulskyblock/utils/RemarkSet.java new file mode 100644 index 0000000..6594533 --- /dev/null +++ b/src/main/java/com/ncguy/usefulskyblock/utils/RemarkSet.java @@ -0,0 +1,11 @@ +package com.ncguy.usefulskyblock.utils; + +import java.util.HashSet; + +public class RemarkSet extends HashSet { + + public void add(String domain, String message) { + add(new Remark(domain, message)); + } + +} diff --git a/src/main/java/com/ncguy/usefulskyblock/world/PortalHandler.java b/src/main/java/com/ncguy/usefulskyblock/world/PortalHandler.java index 9576e94..5a22e62 100644 --- a/src/main/java/com/ncguy/usefulskyblock/world/PortalHandler.java +++ b/src/main/java/com/ncguy/usefulskyblock/world/PortalHandler.java @@ -1,20 +1,35 @@ package com.ncguy.usefulskyblock.world; +import io.papermc.paper.math.BlockPosition; +import org.bukkit.block.BlockState; import org.bukkit.entity.Entity; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.world.PortalCreateEvent; +import org.bukkit.util.BoundingBox; + +import java.util.List; public class PortalHandler implements Listener { @EventHandler public void onPortalCreate(PortalCreateEvent event) { - if(event.getReason() != PortalCreateEvent.CreateReason.FIRE) + if(event.getReason() != PortalCreateEvent.CreateReason.NETHER_PAIR) return; Entity entity = event.getEntity(); if(entity == null) return; + + BoundingBox bb = new BoundingBox(); + List blocks = event.getBlocks(); + bb.shift(blocks.getFirst().getLocation()); + for (int i = 1; i < blocks.size(); i++) + bb.expand(blocks.get(i).getLocation().toVector()); + + boolean isXForward = bb.getWidthX() < bb.getWidthZ(); + + } } diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml new file mode 100644 index 0000000..417332b --- /dev/null +++ b/src/main/resources/config.yml @@ -0,0 +1,17 @@ +skyblock: + biomes: + rod-radius: 16 + biome-map: + - material: "GRASS_BLOCK" + biome: "minecraft:plains" + - material: "SAND" + biome: "minecraft:desert" + - material: "MYCELIUM" + biome: "minecraft:mushroom_fields" + - material: "NETHERRACK" + biome: "minecraft:nether_wastes" + - material: "OAK_LOG" + biome: "minecraft:forest" + - material: "SNOW_BLOCK" + biome: "minecraft:snowy_plains" + diff --git a/src/main/resources/datapacks/usefulskyblock/data/minecraft/dimension/the_nether.json b/src/main/resources/datapacks/usefulskyblock/data/minecraft/dimension/the_nether.json index f47e112..39b74be 100644 --- a/src/main/resources/datapacks/usefulskyblock/data/minecraft/dimension/the_nether.json +++ b/src/main/resources/datapacks/usefulskyblock/data/minecraft/dimension/the_nether.json @@ -4,7 +4,7 @@ "type": "flat", "settings":{ "layers": [], - "biome": "minecraft:void", + "biome": "minecraft:hell", "structure_overrides": [] } }