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:
parent
e6f5230c58
commit
c869fdefb1
14 changed files with 512 additions and 12 deletions
|
@ -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 {
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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'
|
|
@ -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."));
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
234
src/main/java/com/ncguy/usefulskyblock/recipe/BiomeRod.java
Normal file
234
src/main/java/com/ncguy/usefulskyblock/recipe/BiomeRod.java
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
|
||||||
|
}
|
12
src/main/java/com/ncguy/usefulskyblock/utils/Remark.java
Normal file
12
src/main/java/com/ncguy/usefulskyblock/utils/Remark.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
11
src/main/java/com/ncguy/usefulskyblock/utils/RemarkSet.java
Normal file
11
src/main/java/com/ncguy/usefulskyblock/utils/RemarkSet.java
Normal 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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();
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
17
src/main/resources/config.yml
Normal file
17
src/main/resources/config.yml
Normal 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"
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
"type": "flat",
|
"type": "flat",
|
||||||
"settings":{
|
"settings":{
|
||||||
"layers": [],
|
"layers": [],
|
||||||
"biome": "minecraft:void",
|
"biome": "minecraft:hell",
|
||||||
"structure_overrides": []
|
"structure_overrides": []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue