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.
This commit is contained in:
Nick Guy 2025-07-29 21:34:56 +01:00
parent e6f5230c58
commit c869fdefb1
14 changed files with 512 additions and 12 deletions

View file

@ -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 {
}

View file

@ -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<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
return false;
}
}

View file

@ -7,3 +7,5 @@ plugins {
id 'org.gradle.toolchains.foojay-resolver-convention' version '0.8.0' id 'org.gradle.toolchains.foojay-resolver-convention' version '0.8.0'
} }
rootProject.name = 'UsefulSkyblock' rootProject.name = 'UsefulSkyblock'
include 'annotation-processor'

View file

@ -1,7 +1,13 @@
package com.ncguy.usefulskyblock; 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 com.ncguy.usefulskyblock.world.PortalHandler;
import net.kyori.adventure.text.Component; 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.Bukkit;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.NamespacedKey; 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 static final Logger log = LoggerFactory.getLogger(UsefulSkyblock.class);
private BiomeRod biomeRodListener;
@Override @Override
public void onEnable() { public void onEnable() {
System.out.println("OM EMABL:ED");
saveDefaultConfig();
// Plugin startup logic // Plugin startup logic
PluginManager pluginManager = Bukkit.getPluginManager(); PluginManager pluginManager = Bukkit.getPluginManager();
pluginManager.registerEvents(this, this); pluginManager.registerEvents(this, this);
pluginManager.registerEvents(new PortalHandler(), 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 @Override
@ -47,12 +74,30 @@ public final class UsefulSkyblock extends JavaPlugin implements Listener {
player.sendMessage(Component.text("Hello, " + player.getName() + "!")); player.sendMessage(Component.text("Hello, " + player.getName() + "!"));
PersistentDataContainer pdc = player.getPersistentDataContainer(); PersistentDataContainer pdc = player.getPersistentDataContainer();
NamespacedKey initKey = new NamespacedKey(this, "player_init"); NamespacedKey initKey = new NamespacedKey(this, "player_init");
if (pdc.has(initKey)) // if (pdc.has(initKey))
return; // return;
NamespacedKey worldKey = new NamespacedKey(this, "void"); NamespacedKey worldKey = new NamespacedKey(this, "void");
World world = Bukkit.getWorld(worldKey); World world = Bukkit.getWorld(worldKey);
player.teleport(world.getSpawnLocation(), PlayerTeleportEvent.TeleportCause.PLUGIN); 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); pdc.set(initKey, PersistentDataType.BOOLEAN, true);
player.sendMessage(Component.text("This is your first time playing on this server.")); player.sendMessage(Component.text("This is your first time playing on this server."));
} }

View file

@ -21,6 +21,7 @@ import org.bukkit.entity.Entity;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.persistence.PersistentDataContainer; import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType; import org.bukkit.persistence.PersistentDataType;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.structure.Structure; import org.bukkit.structure.Structure;
import org.bukkit.structure.StructureManager; import org.bukkit.structure.StructureManager;
import org.bukkit.util.RayTraceResult; import org.bukkit.util.RayTraceResult;
@ -225,6 +226,9 @@ public class SkyblockAdminCommand extends AbstractSkyblockCommand {
root.requires(cmd::auth); root.requires(cmd::auth);
root.then(Commands.literal("reload").executes(cmd::executeReloadConfig));
var structures = Commands.literal("structures"); var structures = Commands.literal("structures");
structures.then(Commands.literal("list").executes(cmd::executeStructuresList)); 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)))); 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(); return root.build();
} }
private int executeReloadConfig(CommandContext<CommandSourceStack> context) {
JavaPlugin.getProvidingPlugin(SkyblockAdminCommand.class).reloadConfig();
context.getSource().getExecutor().sendMessage("Reloaded config");
return 0;
}
private boolean auth(CommandSourceStack stack) { private boolean auth(CommandSourceStack stack) {
return stack.getSender().isOp(); return stack.getSender().isOp();
} }

View file

@ -7,8 +7,11 @@ import com.ncguy.usefulskyblock.utils.MathsUtils;
import io.papermc.paper.command.brigadier.CommandSourceStack; import io.papermc.paper.command.brigadier.CommandSourceStack;
import io.papermc.paper.command.brigadier.Commands; import io.papermc.paper.command.brigadier.Commands;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.NamespacedKey; import org.bukkit.NamespacedKey;
import org.bukkit.World; 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.Mirror;
import org.bukkit.block.structure.StructureRotation; import org.bukkit.block.structure.StructureRotation;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
@ -23,6 +26,7 @@ import org.bukkit.util.Vector;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.util.Optional;
import java.util.Random; import java.util.Random;
import java.util.function.Supplier; 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 T2_ISLAND_SPACING = T2_ISLAND_SLOTS << 3;
private static float T3_ISLAND_SPACING = T3_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); Vector extents = structure.getSize().clone().multiply(0.5);
loc.subtract(extents); 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<BlockState> bedrock2 = structure.getPalettes().getFirst().getBlocks().stream().filter(b -> b.getBlockData().getMaterial() == Material.BEDROCK).findFirst();
Optional<BlockState> 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; return loc;
} }
@ -101,7 +127,7 @@ public class SkyblockGenCommand extends AbstractSkyblockCommand {
} }
private Location generateIslandNetwork(Location origin) { 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[] t1Slots = MathsUtils.sampleUniqueInts(T1_ISLAND_SLOTS, t1Islands.length);
int[] t2Slots = MathsUtils.sampleUniqueInts(T2_ISLAND_SLOTS, t2Islands.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 z = Math.sin(angle) * islandSpacing;
double y = yNoiseFunc.get(); double y = yNoiseFunc.get();
Location pos = origin.clone().add(x, y, z); 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"); executor.sendMessage("Not part of a team, can't generate skyblock world");
return 0; 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() + "..."); executor.sendMessage("Generating skyblock world for " + playerTeam.getName() + "...");
PersistentDataContainer worldPDC = overworld.getPersistentDataContainer();
int count = 0; int count = 0;
if(worldPDC.has(key("skyblock.team.count"), PersistentDataType.INTEGER)) { if(worldPDC.has(key("skyblock.team.count"), PersistentDataType.INTEGER)) {
@ -167,7 +205,7 @@ public class SkyblockGenCommand extends AbstractSkyblockCommand {
int numSlots = MathsUtils.getSlotsInRing(ring); int numSlots = MathsUtils.getSlotsInRing(ring);
float step = numSlots / 360f; float step = 360f / numSlots;
float angle = slot * step; float angle = slot * step;
float x = (float) Math.cos(angle) * playerIslandSpacing; float x = (float) Math.cos(angle) * playerIslandSpacing;
float y = (float) Math.sin(angle) * playerIslandSpacing; float y = (float) Math.sin(angle) * playerIslandSpacing;
@ -176,8 +214,7 @@ public class SkyblockGenCommand extends AbstractSkyblockCommand {
Location tpLoc = generateIslandNetwork(originLoc); Location tpLoc = generateIslandNetwork(originLoc);
// TODO Sanitize playerTeam.getName() worldPDC.set(key("skyblock.team." + playerTeamName), PersistentDataType.INTEGER_ARRAY, new int[]{tpLoc.getBlockX(), tpLoc.getBlockY(), tpLoc.getBlockZ()});
worldPDC.set(key("skyblock.team." + playerTeam.getName()), PersistentDataType.INTEGER_ARRAY, new int[]{tpLoc.getBlockX(), tpLoc.getBlockY(), tpLoc.getBlockZ()});
worldPDC.set(key("skyblock.team.count"), PersistentDataType.INTEGER, count + 1); worldPDC.set(key("skyblock.team.count"), PersistentDataType.INTEGER, count + 1);
executor.teleport(tpLoc); executor.teleport(tpLoc);

View file

@ -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<Map<String, String>> o = (List<Map<String, String>>) config.getList("skyblock.biomes.biome-map");
Registry<Biome> biomeRegistry = RegistryAccess.registryAccess().getRegistry(RegistryKey.BIOME);
biomeMap.clear();
for(Map<String, String> 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<Material, Biome> 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<Location> 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<Location> 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<Location> locations, Biome biome, JavaPlugin plugin, BukkitScheduler scheduler, int batchSize, IProgressMonitor progress) {
if(locations.isEmpty()) {
progress.finish();
return true;
}
Set<Chunk> 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;
}
}

View file

@ -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);
}
}

View file

@ -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();
}

View file

@ -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;
}
}

View file

@ -0,0 +1,11 @@
package com.ncguy.usefulskyblock.utils;
import java.util.HashSet;
public class RemarkSet extends HashSet<Remark> {
public void add(String domain, String message) {
add(new Remark(domain, message));
}
}

View file

@ -1,20 +1,35 @@
package com.ncguy.usefulskyblock.world; package com.ncguy.usefulskyblock.world;
import io.papermc.paper.math.BlockPosition;
import org.bukkit.block.BlockState;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.world.PortalCreateEvent; import org.bukkit.event.world.PortalCreateEvent;
import org.bukkit.util.BoundingBox;
import java.util.List;
public class PortalHandler implements Listener { public class PortalHandler implements Listener {
@EventHandler @EventHandler
public void onPortalCreate(PortalCreateEvent event) { public void onPortalCreate(PortalCreateEvent event) {
if(event.getReason() != PortalCreateEvent.CreateReason.FIRE) if(event.getReason() != PortalCreateEvent.CreateReason.NETHER_PAIR)
return; return;
Entity entity = event.getEntity(); Entity entity = event.getEntity();
if(entity == null) if(entity == null)
return; return;
BoundingBox bb = new BoundingBox();
List<BlockState> 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();
} }
} }

View file

@ -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"

View file

@ -4,7 +4,7 @@
"type": "flat", "type": "flat",
"settings":{ "settings":{
"layers": [], "layers": [],
"biome": "minecraft:void", "biome": "minecraft:hell",
"structure_overrides": [] "structure_overrides": []
} }
} }