From 8915ef18f139a9d19729f0cafbb7dd9821c17ede Mon Sep 17 00:00:00 2001 From: tastybento Date: Sat, 4 Mar 2023 22:35:44 -0800 Subject: [PATCH] Better structures --- pom.xml | 17 ++ .../commands/AdminPlaceStructureCommand.java | 14 +- .../boxed/listeners/NewAreaListener.java | 147 ++++++++++++------ src/main/resources/structures.yml | 25 ++- 4 files changed, 141 insertions(+), 62 deletions(-) diff --git a/pom.xml b/pom.xml index 9486668..f9c6282 100644 --- a/pom.xml +++ b/pom.xml @@ -127,6 +127,15 @@ codemc-repo https://repo.codemc.org/repository/maven-public/ + + minecraft-repo + https://libraries.minecraft.net/ + + + + nms-repo + https://repo.codemc.io/repository/nms/ + @@ -162,6 +171,14 @@ ${bentobox.version} provided + + + org.spigotmc + spigot + ${spigot.version} + provided + + diff --git a/src/main/java/world/bentobox/boxed/commands/AdminPlaceStructureCommand.java b/src/main/java/world/bentobox/boxed/commands/AdminPlaceStructureCommand.java index abc1eee..dae24e0 100644 --- a/src/main/java/world/bentobox/boxed/commands/AdminPlaceStructureCommand.java +++ b/src/main/java/world/bentobox/boxed/commands/AdminPlaceStructureCommand.java @@ -84,22 +84,26 @@ public class AdminPlaceStructureCommand extends CompositeCommand { Location spot = new Location(user.getWorld(), x, y, z); s.place(spot, true, StructureRotation.NONE, Mirror.NONE, -1, 1, new Random()); NewAreaListener.removeJigsaw(spot, s); + saveStructure(spot, tag, user); + return true; + } + + private void saveStructure(Location spot, NamespacedKey tag, User user) { getAddon().getIslands().getIslandAt(spot).ifPresent(i -> { - int xx = x - i.getCenter().getBlockX(); - int zz = z - i.getCenter().getBlockZ(); + int xx = spot.getBlockX() - i.getCenter().getBlockX(); + int zz = spot.getBlockZ() - i.getCenter().getBlockZ(); File structures = new File(getAddon().getDataFolder(), "structures.yml"); YamlConfiguration config = new YamlConfiguration(); try { config.load(structures); - config.set("structures.new." + tag.getKey(), user.getWorld().getEnvironment().name().toLowerCase() + ", " + xx + ", " + y + ", " + zz); + config.set(spot.getWorld().getEnvironment().name().toLowerCase(Locale.ENGLISH) + "." + xx + "," + spot.getBlockY() + "," + zz, tag.getKey()); config.save(structures); } catch (IOException | InvalidConfigurationException e) { // TODO Auto-generated catch block e.printStackTrace(); } }); - - return true; + } @Override diff --git a/src/main/java/world/bentobox/boxed/listeners/NewAreaListener.java b/src/main/java/world/bentobox/boxed/listeners/NewAreaListener.java index a3cbfcd..5f628b2 100644 --- a/src/main/java/world/bentobox/boxed/listeners/NewAreaListener.java +++ b/src/main/java/world/bentobox/boxed/listeners/NewAreaListener.java @@ -1,9 +1,8 @@ package world.bentobox.boxed.listeners; import java.io.File; -import java.util.ArrayList; +import java.lang.reflect.Field; import java.util.LinkedList; -import java.util.List; import java.util.Locale; import java.util.Queue; import java.util.Random; @@ -15,20 +14,33 @@ import org.bukkit.NamespacedKey; import org.bukkit.World; import org.bukkit.World.Environment; import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.block.Chest; +import org.bukkit.block.data.Waterlogged; import org.bukkit.block.structure.Mirror; import org.bukkit.block.structure.StructureRotation; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.craftbukkit.v1_19_R2.CraftWorld; +import org.bukkit.entity.EntityType; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; +import org.bukkit.inventory.ItemStack; import org.bukkit.structure.Structure; import org.bukkit.util.BoundingBox; +import com.google.common.base.Enums; + +import net.minecraft.core.BlockPosition; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.network.protocol.game.PacketPlayOutTileEntityData; +import net.minecraft.world.level.block.entity.TileEntity; import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.api.events.island.IslandCreatedEvent; import world.bentobox.bentobox.database.objects.Island; import world.bentobox.bentobox.util.Pair; +import world.bentobox.bentobox.util.Util; import world.bentobox.boxed.Boxed; /** @@ -41,7 +53,7 @@ public class NewAreaListener implements Listener { private File structureFile; private Queue itemsToBuild = new LinkedList<>(); private boolean pasting; - private record Item(World w, List> cs, Structure structure, Location location) {}; + private record Item(String name, Structure structure, Location location) {}; Pair min = new Pair(0,0); Pair max = new Pair(0,0); @@ -51,14 +63,11 @@ public class NewAreaListener implements Listener { */ public NewAreaListener(Boxed addon) { this.addon = addon; + addon.saveResource("structures.yml", false); // Load the config structureFile = new File(addon.getDataFolder(), "structures.yml"); - // Check if it exists and if not, save it from the jar - if (!structureFile.exists()) { - addon.saveResource("structures.yml", true); - } - // Try to build something every 5 seconds - Bukkit.getScheduler().runTaskTimer(addon.getPlugin(), () -> BuildItem(), 20, 100); + // Try to build something every second + Bukkit.getScheduler().runTaskTimer(addon.getPlugin(), () -> BuildItem(), 20, 20); } private void BuildItem() { @@ -68,60 +77,50 @@ public class NewAreaListener implements Listener { Item item = itemsToBuild.poll(); LoadChunksAsync(item); } - } @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) - public void onIslandCreated(IslandCreatedEvent e) { + public void onIslandCreated(IslandCreatedEvent event) { + Island island = event.getIsland(); + // Check if this island is in this game + if (!(addon.inWorld(island.getWorld()))) { + return; + } // Load the latest config so that admins can change it on the fly without reloading YamlConfiguration config = YamlConfiguration.loadConfiguration(structureFile); - //addon.log(e.getEventName()); - Island island = e.getIsland(); - addon.getPlugin().getIWM().getAddon(island.getWorld()).ifPresent(gma -> addon.log(gma.getDescription().getName())); - // Check if this island is in this game - Location center = island.getProtectionCenter(); - - ConfigurationSection section = config.getConfigurationSection("structures.new"); - if (section == null) { - addon.log("structures.new not found"); - } else { - place("structure",section, center); + for (String env : config.getKeys(false)) { + Environment e = Enums.getIfPresent(Environment.class, env.toUpperCase(Locale.ENGLISH)).orNull(); + if (e == null) { + addon.logError("Error in structures.yml - unknown environment " + env); + } else { + place("structure",config.getConfigurationSection(env), center, e); + } } - } - private void place(String string, ConfigurationSection section, Location center) { + private void place(String string, ConfigurationSection section, Location center, Environment env) { + World world = env.equals(Environment.NORMAL) ? addon.getOverWorld() : addon.getNetherWorld(); // Loop through the structures in the file - there could be more than one - for (String structure : section.getKeys(false)) { - addon.log(structure); - String key = section.getString(structure,""); + for (String vector : section.getKeys(false)) { + String name = section.getString(vector); + // Load Structure + Structure s = Bukkit.getStructureManager().loadStructure(NamespacedKey.fromString("minecraft:" + name)); + if (s == null) { + BentoBox.getInstance().logError("Could not load " + name); + return; + } // Extract coords - String[] value = key.split(","); - if (value.length == 4) { - Environment env = Environment.valueOf(value[0].toUpperCase(Locale.ENGLISH).strip()); - World world = env.equals(Environment.NORMAL) ? addon.getOverWorld() : addon.getNetherWorld(); - int x = Integer.valueOf(value[1].strip()) + center.getBlockX(); - int y = Integer.valueOf(value[2].strip()); - int z = Integer.valueOf(value[3].strip()) + center.getBlockZ(); - List> cs = new ArrayList<>(); - int size = 10; - for (int cx = (x >> 4) - size; cx < (x >>4) + size; cx++) { - for (int cz = (z >> 4) - size; cz < (z >>4) + size; cz++) { - cs.add(new Pair<>(cx, cz)); - } - } - // Load Structure - Structure s = Bukkit.getStructureManager().loadStructure(NamespacedKey.fromString("minecraft:" + structure)); - if (s == null) { - BentoBox.getInstance().logError("Could not load " + structure); - return; - } + String[] value = vector.split(","); + if (value.length == 3) { + int x = Integer.valueOf(value[0].strip()) + center.getBlockX(); + int y = Integer.valueOf(value[1].strip()); + int z = Integer.valueOf(value[2].strip()) + center.getBlockZ(); Location l = new Location(world, x, y, z); - itemsToBuild.add(new Item(world, cs, s, l)); + itemsToBuild.add(new Item(name, s, l)); } else { - addon.logError("Structure file syntax error: " + structure + " " + key); + addon.logError("Structure file syntax error: " + name + " " + vector); } } } @@ -130,7 +129,7 @@ public class NewAreaListener implements Listener { private void LoadChunksAsync(Item item) { pasting = true; item.structure().place(item.location(), true, StructureRotation.NONE, Mirror.NONE, -1, 1, new Random()); - addon.log("Structure placed at " + item.location); + addon.log(item.name() + " placed at " + item.location().getWorld().getName() + " " + Util.xyz(item.location().toVector())); // Find it removeJigsaw(item.location(), item.structure()); pasting = false; @@ -151,14 +150,60 @@ public class NewAreaListener implements Listener { b.setType(Material.STRUCTURE_VOID); } else if (b.getType().equals(Material.STRUCTURE_BLOCK)) { // I would like to read the data from the block an do something with it! + String data = nmsData(b); b.setType(Material.STRUCTURE_VOID); + BentoBox.getInstance().logDebug(data); + int index = data.indexOf("metadata:"); + if (index > -1) { + data = data.substring(index + 10, data.length()); + index = data.indexOf("\""); + data = data.substring(0, index); + BentoBox.getInstance().logDebug(data); + EntityType type = Enums.getIfPresent(EntityType.class, data.toUpperCase(Locale.ENGLISH)).orNull(); + if (type != null) { + if (loc.getWorld().spawnEntity(loc, type) != null) { + BentoBox.getInstance().logDebug("Spawned a " + type); + } + } + if (data.contains("chest")) { + Block downBlock = b.getRelative(BlockFace.DOWN); + if (downBlock.getType().equals(Material.CHEST)) { + Chest chest = (Chest)downBlock.getState(); + chest.getInventory().addItem(new ItemStack(Material.GOLD_INGOT, 3)); + if (chest.getBlockData() instanceof Waterlogged wl) { + if (wl.isWaterlogged()) { + b.setType(Material.WATER); + } + } + } + } + + } + } } } } - + } + private static String nmsData(Block block) { + Location w = block.getLocation(); + CraftWorld cw = (CraftWorld) w.getWorld(); // CraftWorld is NMS one + // for 1.13+ (we have use WorldServer) + TileEntity te = cw.getHandle().c_(new BlockPosition(w.getBlockX(), w.getBlockY(), w.getBlockZ())); + try { + PacketPlayOutTileEntityData packet = ((PacketPlayOutTileEntityData) te.h()); // get update packet from NMS object + // here we should use reflection because "c" field isn't accessible + Field f = packet.getClass().getDeclaredField("c"); // get field + f.setAccessible(true); // make it available + NBTTagCompound nbtTag = (NBTTagCompound) f.get(packet); + return nbtTag.toString(); // this will show what you want + } catch (Exception exc) { + exc.printStackTrace(); + } + return "Nothing"; + } } diff --git a/src/main/resources/structures.yml b/src/main/resources/structures.yml index 3a67bed..ea5bc42 100644 --- a/src/main/resources/structures.yml +++ b/src/main/resources/structures.yml @@ -1,6 +1,19 @@ -tructures: - new: - village/plains/houses/plains_masons_house_1: "normal, 0 ,64, 80" - ruined_portal/portal_5: "normal, -38, 66, 20" - shipwreck/rightsideup_backhalf: "normal, 3, 59, -60" - +normal: + 0,64,80: village/plains/houses/plains_masons_house_1 + -38,63,20: ruined_portal/portal_5 + 3,58,-60: shipwreck/rightsideup_backhalf + 16,67,23: village/plains/houses/plains_cartographer_1 + -66,63,17: igloo/top + -34,52,-35: underwater_ruin/warm_5 + 80,71,113: pillager_outpost/watchtower + 33,70,-34: village/savanna/town_centers/savanna_meeting_point_1 + 34,70,-29: village/common/iron_golem + 41,70,-26: village/common/animals/cat_calico + 35,70,-22: village/common/animals/cat_calico +nether: + 62,33,-17: bastion/hoglin_stable/walls/wall_base + 68,32,-13: bastion/hoglin_stable/large_stables/inner_0 + 79,33,-10: bastion/hoglin_stable/ramparts/ramparts_1 + 75,34,-4: bastion/hoglin_stable/ramparts/ramparts_3 + 66,39,-7: bastion/hoglin_stable/large_stables/inner_3 + 73,24,-8: bastion/hoglin_stable/stairs/stairs_3_3