diff --git a/pom.xml b/pom.xml index 3ca78f8..bf70763 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 xyz.webmc wlib - 1.2.9-SNAPSHOT + 1.3.1-SNAPSHOT github diff --git a/src/main/java/xyz/webmc/wlib/api/WLIB.java b/src/main/java/xyz/webmc/wlib/api/WLIB.java index a972d02..3db2823 100644 --- a/src/main/java/xyz/webmc/wlib/api/WLIB.java +++ b/src/main/java/xyz/webmc/wlib/api/WLIB.java @@ -28,7 +28,7 @@ import org.bukkit.plugin.Plugin; import org.semver4j.Semver; -@SuppressWarnings({ "NonConstantLogger" }) +@SuppressWarnings({ "NonConstantLogger" , "null"}) public final class WLIB { private static final StackWalker stackWalker = StackWalker.getInstance(Option.RETAIN_CLASS_REFERENCE); private static final List plugins = new ArrayList<>(); diff --git a/src/main/java/xyz/webmc/wlib/api/structure/AbstractBaseStructure.java b/src/main/java/xyz/webmc/wlib/api/structure/AbstractBaseStructure.java index d84fd41..9a5a2d1 100644 --- a/src/main/java/xyz/webmc/wlib/api/structure/AbstractBaseStructure.java +++ b/src/main/java/xyz/webmc/wlib/api/structure/AbstractBaseStructure.java @@ -13,114 +13,86 @@ package xyz.webmc.wlib.api.structure; -import xyz.webmc.wlib.api.util.SchemUtil; +import xyz.webmc.wlib.api.WLIB; import java.io.File; -import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; -import com.cryptomorin.xseries.XMaterial; -import dev.colbster937.reflect.MirrorSafe; import net.sandrohc.schematic4j.exception.ParsingException; -import net.sandrohc.schematic4j.schematic.Schematic; -import net.sandrohc.schematic4j.schematic.types.SchematicBlock; -import net.sandrohc.schematic4j.schematic.types.SchematicBlockPos; import org.bukkit.Location; - -@SuppressWarnings({ "unchecked" }) public abstract class AbstractBaseStructure { - private static final Map, AbstractBaseStructure> INSTANCES = new HashMap<>(); - - private final List blocks = new ArrayList<>(); + private static final List> STRUCTURES = new ArrayList<>(); private final String name; protected AbstractBaseStructure(final String name) { this.name = name; } - public final void place(final Location loc) { - final Location offset = loc.clone().add(this.getOffsetX(), this.getOffsetY(), this.getOffsetZ()); - for (final BlockRelative blk : blocks) { - blk.place(offset); + public final String getName() { + return this.name; + } + + public abstract PlaceableStructure build(); + + public static final List> getStructures() { + return STRUCTURES; + } + + public static final void registerStructure(final Class clazz) { + if (clazz != null && !STRUCTURES.contains(clazz)) { + STRUCTURES.add(clazz); } } - public final String getName() { - return this.name; + @Deprecated(forRemoval = true) + public void place(final Location loc) { + WLIB.warnDeprecatedUsage(); + this.build().place(loc); } + @Deprecated(forRemoval = true) public int getOffsetX() { - return 0; + WLIB.warnDeprecatedUsage(); + throw new UnsupportedOperationException(); } + @Deprecated(forRemoval = true) public int getOffsetY() { - return 0; + WLIB.warnDeprecatedUsage(); + throw new UnsupportedOperationException(); } + @Deprecated(forRemoval = true) public int getOffsetZ() { - return 0; + WLIB.warnDeprecatedUsage(); + throw new UnsupportedOperationException(); } + @Deprecated(forRemoval = true) protected final void addBlock(final BlockRelative blk) { - this.blocks.add(blk); + WLIB.warnDeprecatedUsage(); + throw new UnsupportedOperationException(); } + @Deprecated(forRemoval = true) protected final void loadSchematic(final InputStream is) throws IOException, ParsingException { - try (is) { - final Schematic schematic = SchemUtil.readSchematic(is); - - final int width = schematic.width(); - final int height = schematic.height(); - final int length = schematic.length(); - - final SchematicBlockPos offset = schematic.offset(); - - for (int y = 0; y < height; y++) { - for (int z = 0; z < length; z++) { - for (int x = 0; x < width; x++) { - final SchematicBlock block = schematic.block(x, y, z); - if (block != SchematicBlock.AIR) { - final XMaterial mat = XMaterial.matchXMaterial(block.block().replace("minecraft:", "")).orElse(XMaterial.AIR); - if (mat != null) { - final StringBuilder sb = new StringBuilder(); - - for (final Map.Entry entry : block.states().entrySet()) { - if (sb.length() > 0) { - sb.append(","); - } - - sb.append(entry.getKey()); - sb.append("="); - sb.append(entry.getValue()); - } - - this.addBlock(new BlockRelative(x - offset.x, y - offset.y, z - offset.z, mat, "[" + sb.toString() + "]")); - } - } - } - } - } - } + WLIB.warnDeprecatedUsage(); + throw new UnsupportedOperationException(); } + @Deprecated(forRemoval = true) protected final void loadSchematic(final File file) throws IOException, ParsingException { - loadSchematic(new FileInputStream(file)); + WLIB.warnDeprecatedUsage(); + throw new UnsupportedOperationException(); } + @Deprecated(forRemoval = true) public static final T getInstance(final Class clazz, final Object... params) { - AbstractBaseStructure structure = INSTANCES.get(clazz); - - if (structure == null) { - structure = MirrorSafe.invokeConstructor(clazz, params); - INSTANCES.put(clazz, structure); - } - - return (T) structure; + WLIB.warnDeprecatedUsage(); + throw new UnsupportedOperationException(); } } diff --git a/src/main/java/xyz/webmc/wlib/api/structure/AbstractGenerableStructure.java b/src/main/java/xyz/webmc/wlib/api/structure/AbstractGenerableStructure.java new file mode 100644 index 0000000..0bd9c6f --- /dev/null +++ b/src/main/java/xyz/webmc/wlib/api/structure/AbstractGenerableStructure.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2026 Colbster937 + * + * SPDX-License-Identifier: AGPL-3.0-or-later + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * See the LICENSE file for details. + */ + +package xyz.webmc.wlib.api.structure; + +import xyz.webmc.wlib.api.WLIB; + +import org.bukkit.Location; +import org.bukkit.World; + +public abstract class AbstractGenerableStructure extends AbstractBaseStructure { + static final int SEARCH_RADIUS = 100; + + protected AbstractGenerableStructure(final String name) { + super(name); + } + + public abstract boolean canGenerateAt(final int chunkX, final int chunkZ, final long worldSeed); + + public abstract long getGenerationSeed(final int chunkX, final int chunkZ, final long worldSeed); + + public abstract PlaceableStructure build(final long seed); + + @Override + public final PlaceableStructure build() { + return this.build(0); + } + + public final Location locateNearest(final Location pos, final long worldSeed) { + Location ret = null; + + if (pos != null && pos.getWorld() != null) { + final int centerChunkX = pos.getChunk().getX(); + final int centerChunkZ = pos.getChunk().getZ(); + final World world = pos.getWorld(); + + search: + for (int radius = 0; radius <= SEARCH_RADIUS; radius++) { + final int minX = centerChunkX - radius; + final int maxX = centerChunkX + radius; + final int minZ = centerChunkZ - radius; + final int maxZ = centerChunkZ + radius; + + for (int chunkX = minX; chunkX <= maxX; chunkX++) { + for (int chunkZ : new int[] { minZ, maxZ }) { + if (this.canGenerateAt(chunkX, chunkZ, worldSeed)) { + ret = this.createLocation(world, chunkX, chunkZ); + break search; + } + } + } + + for (int chunkZ = minZ + 1; chunkZ < maxZ; chunkZ++) { + for (int chunkX : new int[] { minX, maxX }) { + if (this.canGenerateAt(chunkX, chunkZ, worldSeed)) { + ret = this.createLocation(world, chunkX, chunkZ); + break search; + } + } + } + } + } + + return ret; + } + + @Deprecated(forRemoval = true) + public final void place(final Location loc) { + WLIB.warnDeprecatedUsage(); + this.build(getGenerationSeed(loc.getChunk().getX(), loc.getChunk().getZ(), loc.getWorld().getSeed())).place(loc); + } + + private Location createLocation(final World world, final int chunkX, final int chunkZ) { + return new Location(world, chunkX * 16 + 8, 65, chunkZ * 16 + 8); + } +} diff --git a/src/main/java/xyz/webmc/wlib/api/structure/BlockRelative.java b/src/main/java/xyz/webmc/wlib/api/structure/BlockRelative.java index d6880eb..bab27ed 100644 --- a/src/main/java/xyz/webmc/wlib/api/structure/BlockRelative.java +++ b/src/main/java/xyz/webmc/wlib/api/structure/BlockRelative.java @@ -71,7 +71,7 @@ public void place(final Location loc) { final Material _mat = this.mat.parseMaterial(); if (mat != null) { - if (WLIB.getIsModernServer() && this.dataModern != null) { + if (WLIB.getIsModernServer() && this.dataModern != null && _mat != null) { final Object data = MirrorSafe.invokeMethod(Bukkit.class, "createBlockData", new Object[] { "minecraft:" + _mat.name().toLowerCase() + this.dataModern }); MirrorSafe.invokeMethod(Block.class, blk, "setBlockData", data, false); } else { @@ -110,4 +110,8 @@ public final String getDataModern() { public final byte getDataLegacy() { return dataLegacy; } + + public final BlockRelative offset(final int x, final int y, final int z) { + return new BlockRelative(this.x + x, this.y + y, this.z + z, this.mat, this.dataModern, this.dataLegacy); + } } diff --git a/src/main/java/xyz/webmc/wlib/api/structure/PlaceableStructure.java b/src/main/java/xyz/webmc/wlib/api/structure/PlaceableStructure.java new file mode 100644 index 0000000..14236a2 --- /dev/null +++ b/src/main/java/xyz/webmc/wlib/api/structure/PlaceableStructure.java @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2026 Colbster937 + * + * SPDX-License-Identifier: AGPL-3.0-or-later + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * See the LICENSE file for details. + */ + +package xyz.webmc.wlib.api.structure; + +import xyz.webmc.wlib.api.util.SchemUtil; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import com.cryptomorin.xseries.XMaterial; +import net.sandrohc.schematic4j.exception.ParsingException; +import net.sandrohc.schematic4j.schematic.Schematic; +import net.sandrohc.schematic4j.schematic.types.SchematicBlock; +import net.sandrohc.schematic4j.schematic.types.SchematicBlockPos; +import org.bukkit.Chunk; +import org.bukkit.Location; + +public final class PlaceableStructure { + private final List blocks = new ArrayList<>(); + private final String name; + + public PlaceableStructure(final String name) { + this.name = name; + } + + public final String getName() { + return this.name; + } + + public final List getBlocks() { + return Collections.unmodifiableList(this.blocks); + } + + public final void addBlock(final BlockRelative block) { + if (block != null) { + this.blocks.add(block); + } + } + + public final void addBlocks(final Collection blocks) { + if (blocks != null) { + this.blocks.addAll(blocks); + } + } + + public final void addBlocks(final BlockRelative... blocks) { + if (blocks != null) { + Collections.addAll(this.blocks, blocks); + } + } + + public final void place(final Location loc) { + final Location origin = loc.clone(); + for (final BlockRelative block : this.blocks) { + block.place(origin); + } + } + + public final void place(final Chunk chunk) { + final PlaceableStructure chunkData = this.getChunk(0, 0); + final Location origin = new Location(chunk.getWorld(), chunk.getX() * 16, 65, chunk.getZ() * 16); + chunkData.place(origin); + } + + public final PlaceableStructure getChunk(final int chunkX, final int chunkZ) { + final int minX = chunkX * 16; + final int maxX = minX + 15; + final int minZ = chunkZ * 16; + final int maxZ = minZ + 15; + + final PlaceableStructure chunk = new PlaceableStructure(this.name + "_chunk_" + chunkX + "_" + chunkZ); + for (final BlockRelative block : this.blocks) { + if (block.getX() >= minX && block.getX() <= maxX && block.getZ() >= minZ && block.getZ() <= maxZ) { + chunk.addBlock(block.offset(-minX, 0, -minZ)); + } + } + + return chunk; + } + + public final void loadSchematic(final InputStream is) throws IOException, ParsingException { + this.loadSchematic(is, 0, 0, 0); + } + + public final void loadSchematic(final InputStream is, final int offsetX, final int offsetY, final int offsetZ) throws IOException, ParsingException { + try (is) { + final Schematic schematic = SchemUtil.readSchematic(is); + + final int width = schematic.width(); + final int height = schematic.height(); + final int length = schematic.length(); + final SchematicBlockPos offset = schematic.offset(); + + for (int y = 0; y < height; y++) { + for (int z = 0; z < length; z++) { + for (int x = 0; x < width; x++) { + final SchematicBlock block = schematic.block(x, y, z); + if (block != SchematicBlock.AIR) { + final XMaterial mat = XMaterial.matchXMaterial(block.block().replace("minecraft:", "")).orElse(XMaterial.AIR); + if (mat != null) { + final StringBuilder sb = new StringBuilder(); + + for (final Map.Entry entry : block.states().entrySet()) { + if (sb.length() > 0) { + sb.append(","); + } + + sb.append(entry.getKey()); + sb.append("="); + sb.append(entry.getValue()); + } + + this.addBlock(new BlockRelative(x - offset.x + offsetX, y - offset.y + offsetY, z - offset.z + offsetZ, mat, "[" + sb.toString() + "]")); + } + } + } + } + } + } + } + + public final void loadSchematic(final File file) throws IOException, ParsingException { + this.loadSchematic(file, 0, 0, 0); + } + + public final void loadSchematic(final File file, final int offsetX, final int offsetY, final int offsetZ) throws IOException, ParsingException { + this.loadSchematic(new FileInputStream(file), offsetX, offsetY, offsetZ); + } +} diff --git a/src/main/java/xyz/webmc/wlib/api/structure/WeightedStructureTable.java b/src/main/java/xyz/webmc/wlib/api/structure/WeightedStructureTable.java index 5e777cc..55070c7 100644 --- a/src/main/java/xyz/webmc/wlib/api/structure/WeightedStructureTable.java +++ b/src/main/java/xyz/webmc/wlib/api/structure/WeightedStructureTable.java @@ -13,6 +13,7 @@ package xyz.webmc.wlib.api.structure; +import xyz.webmc.wlib.api.WLIB; import xyz.webmc.wlib.api.util.RNGUtil; import java.util.ArrayList; @@ -22,6 +23,7 @@ import dev.colbster937.util.WeightedObjectTable; import org.bukkit.Location; +@SuppressWarnings({ "removal" }) public final class WeightedStructureTable extends WeightedObjectTable { public WeightedStructureTable(final long seed, final WeightedStructure... structures) { super(seed, structures); @@ -41,7 +43,9 @@ public WeightedStructureTable(final Class... st this(weigh(structures)); } + @Deprecated(forRemoval = true) public void place(final Location loc) { + WLIB.warnDeprecatedUsage(); this.computeRandomObject().place(loc); } diff --git a/src/main/java/xyz/webmc/wlib/internal/command/WLIBCommand.java b/src/main/java/xyz/webmc/wlib/internal/command/WLIBCommand.java index 1aa962e..1ad9552 100644 --- a/src/main/java/xyz/webmc/wlib/internal/command/WLIBCommand.java +++ b/src/main/java/xyz/webmc/wlib/internal/command/WLIBCommand.java @@ -16,6 +16,7 @@ import xyz.webmc.wlib.api.WLIB; import xyz.webmc.wlib.api.command.WCommand; import xyz.webmc.wlib.api.structure.AbstractBaseStructure; +import xyz.webmc.wlib.api.structure.AbstractGenerableStructure; import xyz.webmc.wlib.api.util.PermissionUtil; import xyz.webmc.wlib.api.util.SchedulerUtil; import xyz.webmc.wlib.api.util.TextUtil; @@ -73,29 +74,37 @@ public final boolean run(final CommandSender sender, final String label, final S if (act.equals("throw")) { throw new CommandException(); } else if (act.equals("place")) { - bool1 = false; - if (sender instanceof Player plr) { - final String struct = args[2].trim(); - Class clazz = null; - - if (struct.equals("shrine")) { - clazz = HerobrineShrineTestStructure.class; - } else if (struct.equals("rick")) { - clazz = RickQRCodeTestSchemStructure.class; - } - - if (clazz != null) { - final AbstractBaseStructure structure = AbstractBaseStructure.getInstance(clazz); - final Location loc = plr.getLocation(); - structure.place(loc.clone()); - SchedulerUtil.teleportAsync(plr, loc.clone().add(1, 1, 0).getBlock().getLocation().clone().add(0.5D, 0, 0.5D)); - sender.sendMessage(ChatColor.GREEN + "Placed structure " + structure.getName()); + if (args.length >= 3) { + bool1 = false; + if (sender instanceof Player plr) { + final String struct = args[2].trim(); + final AbstractBaseStructure structure; + + switch (struct) { + case "shrine" -> structure = new HerobrineShrineTestStructure(); + case "rick" -> structure = new RickQRCodeTestSchemStructure(); + default -> { + bool1 = true; + bool2 = false; + structure = null; + } + } + + if (structure != null) { + final Location loc = plr.getLocation(); + if (structure instanceof AbstractGenerableStructure generable) { + final long seed = generable.getGenerationSeed(loc.getChunk().getX(), loc.getChunk().getZ(), plr.getWorld().getSeed()); + generable.build(seed).place(loc.clone()); + } else { + structure.build().place(loc.clone()); + } + SchedulerUtil.teleportAsync(plr, loc.clone().add(0, 1, 0).getBlock().getLocation().clone().add(0.5D, 0, 0.5D)); + sender.sendMessage(ChatColor.GREEN + "Placed structure " + structure.getName()); + } } else { - bool1 = true; - bool2 = false; + sendOnlyPlayersMessage(sender); + return true; } - } else { - sendOnlyPlayersMessage(sender); } } } @@ -115,10 +124,10 @@ public final boolean run(final CommandSender sender, final String label, final S @Override public final List tab(final CommandSender sender, final String label, final String[] args) { - if (args.length > 0) { - final List ret = new ArrayList<>(); + final List ret = new ArrayList<>(); - if (args.length == 1) { + switch (args.length) { + case 1 -> { if (sender.hasPermission("wlib.plugins")) { ret.add("plugins"); } @@ -134,21 +143,23 @@ public final List tab(final CommandSender sender, final String label, fi if (sender.hasPermission("wlib.debug")) { ret.add("debug"); } - } else if (args.length == 2) { + } + + case 2 -> { if (args[0].equals("debug") && sender.hasPermission("wlib.debug")) { ret.add("throw"); ret.add("place"); } - } else if (args.length == 3) { + } + + case 3 -> { if (args[0].equals("debug") && args[1].equals("place") && sender.hasPermission("wlib.debug")) { ret.add("shrine"); ret.add("rick"); } } - - return ret; - } else { - return List.of(); } + + return ret; } } diff --git a/src/main/java/xyz/webmc/wlib/internal/structure/HerobrineShrineTestStructure.java b/src/main/java/xyz/webmc/wlib/internal/structure/HerobrineShrineTestStructure.java index 0c08197..f8e2d96 100644 --- a/src/main/java/xyz/webmc/wlib/internal/structure/HerobrineShrineTestStructure.java +++ b/src/main/java/xyz/webmc/wlib/internal/structure/HerobrineShrineTestStructure.java @@ -13,35 +13,49 @@ package xyz.webmc.wlib.internal.structure; -import xyz.webmc.wlib.api.structure.AbstractBaseStructure; +import xyz.webmc.wlib.api.structure.AbstractGenerableStructure; import xyz.webmc.wlib.api.structure.BlockRelative; +import xyz.webmc.wlib.api.structure.PlaceableStructure; import com.cryptomorin.xseries.XMaterial; -public final class HerobrineShrineTestStructure extends AbstractBaseStructure { +public final class HerobrineShrineTestStructure extends AbstractGenerableStructure { public HerobrineShrineTestStructure() { super("herobrine_shrine"); + } + + @Override + public PlaceableStructure build(final long seed) { + final PlaceableStructure structure = new PlaceableStructure(this.getName()); for (int y = 0; y < 3; y++) { for (int x = -1; x < 2; x++) { for (int z = -1; z < 2; z++) { if (y == 0) { - super.addBlock(new BlockRelative(x, y, z, XMaterial.GOLD_BLOCK)); + structure.addBlock(new BlockRelative(x, y, z, XMaterial.GOLD_BLOCK)); } else if (y == 1) { if (x != z && x + z != 0) { - super.addBlock(new BlockRelative(x, y, z, XMaterial.REDSTONE_TORCH)); + structure.addBlock(new BlockRelative(x, y, z, XMaterial.REDSTONE_TORCH)); } else if (x == 0 && z == 0) { - super.addBlock(new BlockRelative(x, y, z, XMaterial.NETHERRACK)); + structure.addBlock(new BlockRelative(x, y, z, XMaterial.NETHERRACK)); } } else if (y == 2 && x == 0 && z == 0) { - super.addBlock(new BlockRelative(x, y, z, XMaterial.FIRE)); + structure.addBlock(new BlockRelative(x, y, z, XMaterial.FIRE)); } } } } + + return structure; + } + + @Override + public boolean canGenerateAt(final int chunkX, final int chunkZ, final long worldSeed) { + return (chunkX + chunkZ) % 10 == 0; } - public static HerobrineShrineTestStructure getInstance() { - return getInstance(HerobrineShrineTestStructure.class); + @Override + public long getGenerationSeed(final int chunkX, final int chunkZ, final long worldSeed) { + return worldSeed + (chunkX * 31L) + (chunkZ * 17L); } } diff --git a/src/main/java/xyz/webmc/wlib/internal/structure/RickQRCodeTestSchemStructure.java b/src/main/java/xyz/webmc/wlib/internal/structure/RickQRCodeTestSchemStructure.java index 21f7d24..4e061d9 100644 --- a/src/main/java/xyz/webmc/wlib/internal/structure/RickQRCodeTestSchemStructure.java +++ b/src/main/java/xyz/webmc/wlib/internal/structure/RickQRCodeTestSchemStructure.java @@ -14,6 +14,7 @@ package xyz.webmc.wlib.internal.structure; import xyz.webmc.wlib.api.structure.AbstractBaseStructure; +import xyz.webmc.wlib.api.structure.PlaceableStructure; import java.io.IOException; import java.io.InputStream; @@ -21,14 +22,20 @@ import net.sandrohc.schematic4j.exception.ParsingException; public final class RickQRCodeTestSchemStructure extends AbstractBaseStructure { - public RickQRCodeTestSchemStructure() throws IOException, ParsingException { + public RickQRCodeTestSchemStructure() { super("rick_qr"); + } + + @Override + public PlaceableStructure build() { + final PlaceableStructure structure = new PlaceableStructure(this.getName()); + try (InputStream is = RickQRCodeTestSchemStructure.class.getResourceAsStream("/schematics/rick.schem")) { - super.loadSchematic(is); + structure.loadSchematic(is); + } catch (final IOException | ParsingException ex) { + throw new RuntimeException(ex); } - } - public static RickQRCodeTestSchemStructure getInstance() { - return getInstance(RickQRCodeTestSchemStructure.class); + return structure; } }