Better structures

This commit is contained in:
tastybento 2023-03-04 22:35:44 -08:00
parent a359f097b9
commit 8915ef18f1
4 changed files with 141 additions and 62 deletions

17
pom.xml
View File

@ -127,6 +127,15 @@
<id>codemc-repo</id> <id>codemc-repo</id>
<url>https://repo.codemc.org/repository/maven-public/</url> <url>https://repo.codemc.org/repository/maven-public/</url>
</repository> </repository>
<repository>
<id>minecraft-repo</id>
<url>https://libraries.minecraft.net/</url>
</repository>
<!-- Spigot NMS required for world regeneration :( -->
<repository>
<id>nms-repo</id>
<url>https://repo.codemc.io/repository/nms/</url>
</repository>
</repositories> </repositories>
<dependencies> <dependencies>
@ -162,6 +171,14 @@
<version>${bentobox.version}</version> <version>${bentobox.version}</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<!-- Spigot NMS. Used for chunk deletion and pasting. -->
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot</artifactId>
<version>${spigot.version}</version>
<scope>provided</scope>
</dependency>
</dependencies> </dependencies>
<build> <build>

View File

@ -84,14 +84,19 @@ public class AdminPlaceStructureCommand extends CompositeCommand {
Location spot = new Location(user.getWorld(), x, y, z); Location spot = new Location(user.getWorld(), x, y, z);
s.place(spot, true, StructureRotation.NONE, Mirror.NONE, -1, 1, new Random()); s.place(spot, true, StructureRotation.NONE, Mirror.NONE, -1, 1, new Random());
NewAreaListener.removeJigsaw(spot, s); 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 -> { getAddon().getIslands().getIslandAt(spot).ifPresent(i -> {
int xx = x - i.getCenter().getBlockX(); int xx = spot.getBlockX() - i.getCenter().getBlockX();
int zz = z - i.getCenter().getBlockZ(); int zz = spot.getBlockZ() - i.getCenter().getBlockZ();
File structures = new File(getAddon().getDataFolder(), "structures.yml"); File structures = new File(getAddon().getDataFolder(), "structures.yml");
YamlConfiguration config = new YamlConfiguration(); YamlConfiguration config = new YamlConfiguration();
try { try {
config.load(structures); 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); config.save(structures);
} catch (IOException | InvalidConfigurationException e) { } catch (IOException | InvalidConfigurationException e) {
// TODO Auto-generated catch block // TODO Auto-generated catch block
@ -99,7 +104,6 @@ public class AdminPlaceStructureCommand extends CompositeCommand {
} }
}); });
return true;
} }
@Override @Override

View File

@ -1,9 +1,8 @@
package world.bentobox.boxed.listeners; package world.bentobox.boxed.listeners;
import java.io.File; import java.io.File;
import java.util.ArrayList; import java.lang.reflect.Field;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Queue; import java.util.Queue;
import java.util.Random; import java.util.Random;
@ -15,20 +14,33 @@ import org.bukkit.NamespacedKey;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.World.Environment; import org.bukkit.World.Environment;
import org.bukkit.block.Block; 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.Mirror;
import org.bukkit.block.structure.StructureRotation; import org.bukkit.block.structure.StructureRotation;
import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration; 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.EventHandler;
import org.bukkit.event.EventPriority; import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.inventory.ItemStack;
import org.bukkit.structure.Structure; import org.bukkit.structure.Structure;
import org.bukkit.util.BoundingBox; 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.BentoBox;
import world.bentobox.bentobox.api.events.island.IslandCreatedEvent; import world.bentobox.bentobox.api.events.island.IslandCreatedEvent;
import world.bentobox.bentobox.database.objects.Island; import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.util.Pair; import world.bentobox.bentobox.util.Pair;
import world.bentobox.bentobox.util.Util;
import world.bentobox.boxed.Boxed; import world.bentobox.boxed.Boxed;
/** /**
@ -41,7 +53,7 @@ public class NewAreaListener implements Listener {
private File structureFile; private File structureFile;
private Queue<Item> itemsToBuild = new LinkedList<>(); private Queue<Item> itemsToBuild = new LinkedList<>();
private boolean pasting; private boolean pasting;
private record Item(World w, List<Pair<Integer, Integer>> cs, Structure structure, Location location) {}; private record Item(String name, Structure structure, Location location) {};
Pair<Integer, Integer> min = new Pair<Integer, Integer>(0,0); Pair<Integer, Integer> min = new Pair<Integer, Integer>(0,0);
Pair<Integer, Integer> max = new Pair<Integer, Integer>(0,0); Pair<Integer, Integer> max = new Pair<Integer, Integer>(0,0);
@ -51,14 +63,11 @@ public class NewAreaListener implements Listener {
*/ */
public NewAreaListener(Boxed addon) { public NewAreaListener(Boxed addon) {
this.addon = addon; this.addon = addon;
addon.saveResource("structures.yml", false);
// Load the config // Load the config
structureFile = new File(addon.getDataFolder(), "structures.yml"); structureFile = new File(addon.getDataFolder(), "structures.yml");
// Check if it exists and if not, save it from the jar // Try to build something every second
if (!structureFile.exists()) { Bukkit.getScheduler().runTaskTimer(addon.getPlugin(), () -> BuildItem(), 20, 20);
addon.saveResource("structures.yml", true);
}
// Try to build something every 5 seconds
Bukkit.getScheduler().runTaskTimer(addon.getPlugin(), () -> BuildItem(), 20, 100);
} }
private void BuildItem() { private void BuildItem() {
@ -68,60 +77,50 @@ public class NewAreaListener implements Listener {
Item item = itemsToBuild.poll(); Item item = itemsToBuild.poll();
LoadChunksAsync(item); LoadChunksAsync(item);
} }
} }
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) @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 // Load the latest config so that admins can change it on the fly without reloading
YamlConfiguration config = YamlConfiguration.loadConfiguration(structureFile); 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(); Location center = island.getProtectionCenter();
for (String env : config.getKeys(false)) {
ConfigurationSection section = config.getConfigurationSection("structures.new"); Environment e = Enums.getIfPresent(Environment.class, env.toUpperCase(Locale.ENGLISH)).orNull();
if (section == null) { if (e == null) {
addon.log("structures.new not found"); addon.logError("Error in structures.yml - unknown environment " + env);
} else { } else {
place("structure",section, center); 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 // Loop through the structures in the file - there could be more than one
for (String structure : section.getKeys(false)) { for (String vector : section.getKeys(false)) {
addon.log(structure); String name = section.getString(vector);
String key = section.getString(structure,""); // Load Structure
Structure s = Bukkit.getStructureManager().loadStructure(NamespacedKey.fromString("minecraft:" + name));
if (s == null) {
BentoBox.getInstance().logError("Could not load " + name);
return;
}
// Extract coords // Extract coords
String[] value = key.split(","); String[] value = vector.split(",");
if (value.length == 4) { if (value.length == 3) {
Environment env = Environment.valueOf(value[0].toUpperCase(Locale.ENGLISH).strip()); int x = Integer.valueOf(value[0].strip()) + center.getBlockX();
World world = env.equals(Environment.NORMAL) ? addon.getOverWorld() : addon.getNetherWorld(); int y = Integer.valueOf(value[1].strip());
int x = Integer.valueOf(value[1].strip()) + center.getBlockX(); int z = Integer.valueOf(value[2].strip()) + center.getBlockZ();
int y = Integer.valueOf(value[2].strip());
int z = Integer.valueOf(value[3].strip()) + center.getBlockZ();
List<Pair<Integer, Integer>> 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;
}
Location l = new Location(world, x, y, z); Location l = new Location(world, x, y, z);
itemsToBuild.add(new Item(world, cs, s, l)); itemsToBuild.add(new Item(name, s, l));
} else { } 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) { private void LoadChunksAsync(Item item) {
pasting = true; pasting = true;
item.structure().place(item.location(), true, StructureRotation.NONE, Mirror.NONE, -1, 1, new Random()); 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 // Find it
removeJigsaw(item.location(), item.structure()); removeJigsaw(item.location(), item.structure());
pasting = false; pasting = false;
@ -151,7 +150,36 @@ public class NewAreaListener implements Listener {
b.setType(Material.STRUCTURE_VOID); b.setType(Material.STRUCTURE_VOID);
} else if (b.getType().equals(Material.STRUCTURE_BLOCK)) { } else if (b.getType().equals(Material.STRUCTURE_BLOCK)) {
// I would like to read the data from the block an do something with it! // I would like to read the data from the block an do something with it!
String data = nmsData(b);
b.setType(Material.STRUCTURE_VOID); 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);
}
}
}
}
}
} }
} }
} }
@ -160,5 +188,22 @@ public class NewAreaListener implements Listener {
} }
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";
}
} }

View File

@ -1,6 +1,19 @@
tructures: normal:
new: 0,64,80: village/plains/houses/plains_masons_house_1
village/plains/houses/plains_masons_house_1: "normal, 0 ,64, 80" -38,63,20: ruined_portal/portal_5
ruined_portal/portal_5: "normal, -38, 66, 20" 3,58,-60: shipwreck/rightsideup_backhalf
shipwreck/rightsideup_backhalf: "normal, 3, 59, -60" 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