Fixed bugs with structure placing and added more of them

This commit is contained in:
tastybento 2023-04-30 09:39:26 -07:00
parent 36162fe293
commit 628068eb5d
4 changed files with 114 additions and 32 deletions

View File

@ -21,19 +21,35 @@ import org.bukkit.structure.Structure;
import com.google.common.base.Enums;
import world.bentobox.bentobox.api.commands.CompositeCommand;
import world.bentobox.bentobox.api.localization.TextVariables;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.util.Util;
import world.bentobox.boxed.Boxed;
import world.bentobox.boxed.listeners.NewAreaListener;
import world.bentobox.boxed.listeners.NewAreaListener.Item;
/**
* @author tastybento
*
*/
public class AdminPlaceStructureCommand extends CompositeCommand {
private static final String STRUCTURE_FILE = "structures.yml";
/**
* Integrity determines how damaged the building should look by randomly skipping blocks to place.
* This value can range from 0 to 1. With 0 removing all blocks and 1 spawning the structure in pristine condition.
*/
private static final float INTEGRITY = 1;
/**
* The palette index of the structure to use, starting at 0, or -1 to pick a random palette.
*/
private static final int PALETTE = -1;
private StructureRotation sr = StructureRotation.NONE;
private Mirror mirror = Mirror.NONE;
private boolean noMobs;
public AdminPlaceStructureCommand(CompositeCommand parent) {
super(parent, "place");
@ -54,7 +70,7 @@ public class AdminPlaceStructureCommand extends CompositeCommand {
// Initialize
sr = StructureRotation.NONE;
mirror = Mirror.NONE;
// Check world
if (!((Boxed)getAddon()).inWorld(getWorld())) {
user.sendMessage("boxed.admin.place.wrong-world");
@ -66,6 +82,7 @@ public class AdminPlaceStructureCommand extends CompositeCommand {
* 4. place <structure> ~ ~ ~
* 5. place <structure> ~ ~ ~ ROTATION
* 6. place <structure> ~ ~ ~ ROTATION MIRROR
* 7. place <structure> ~ ~ ~ ROTATION MIRROR NO_MOBS
*/
// Format is place <structure> ~ ~ ~ or coords
if (args.isEmpty() || args.size() == 2 || args.size() == 3 || args.size() > 6) {
@ -110,6 +127,14 @@ public class AdminPlaceStructureCommand extends CompositeCommand {
Arrays.stream(Mirror.values()).map(Mirror::name).forEach(user::sendRawMessage);
return false;
}
if (args.size() == 7) {
if (args.get(6).toUpperCase(Locale.ENGLISH).equals("NO_MOBS")) {
noMobs = true;
} else {
user.sendMessage("boxed.admin.place.unknown", TextVariables.LABEL, args.get(6).toUpperCase(Locale.ENGLISH));
return false;
}
}
// Syntax is okay
return true;
}
@ -122,28 +147,39 @@ public class AdminPlaceStructureCommand extends CompositeCommand {
int y = args.size() == 1 || args.get(2).equals("~") ? user.getLocation().getBlockY() : Integer.valueOf(args.get(2).trim());
int z = args.size() == 1 || args.get(3).equals("~") ? user.getLocation().getBlockZ() : Integer.valueOf(args.get(3).trim());
Location spot = new Location(user.getWorld(), x, y, z);
s.place(spot, true, sr, mirror, -1, 1, new Random());
NewAreaListener.removeJigsaw(spot, s, sr, tag.getKey());
saveStructure(spot, tag, user, sr, mirror);
return true;
s.place(spot, true, sr, mirror, PALETTE, INTEGRITY, new Random());
NewAreaListener.removeJigsaw(new Item(tag.getKey(), s, spot, sr, mirror, noMobs));
boolean result = saveStructure(spot, tag, user, sr, mirror);
if (result) {
user.sendMessage("boxed.admin.place.saved");
} else {
user.sendMessage("boxed.admin.place.failed");
}
return result;
}
private void saveStructure(Location spot, NamespacedKey tag, User user, StructureRotation sr2, Mirror mirror2) {
getAddon().getIslands().getIslandAt(spot).ifPresent(i -> {
private boolean saveStructure(Location spot, NamespacedKey tag, User user, StructureRotation sr2, Mirror mirror2) {
return getAddon().getIslands().getIslandAt(spot).map(i -> {
int xx = spot.getBlockX() - i.getCenter().getBlockX();
int zz = spot.getBlockZ() - i.getCenter().getBlockZ();
File structures = new File(getAddon().getDataFolder(), "structures.yml");
File structures = new File(getAddon().getDataFolder(), STRUCTURE_FILE);
YamlConfiguration config = new YamlConfiguration();
try {
config.load(structures);
String value = tag.getKey() + "," + sr2.name() + "," + mirror2.name();
config.set(spot.getWorld().getEnvironment().name().toLowerCase(Locale.ENGLISH) + "." + xx + "," + spot.getBlockY() + "," + zz, value);
StringBuilder v = new StringBuilder();
v.append(tag.getKey() + "," + sr2.name() + "," + mirror2.name());
if (noMobs) {
v.append(" NO_MOBS");
}
config.set(spot.getWorld().getEnvironment().name().toLowerCase(Locale.ENGLISH) + "." + xx + "," + spot.getBlockY() + "," + zz, v.toString());
config.save(structures);
} catch (IOException | InvalidConfigurationException e) {
// TODO Auto-generated catch block
// TODO Auto-generated catch block
e.printStackTrace();
return false;
}
});
return true;
}).orElse(false);
}
@ -163,6 +199,8 @@ public class AdminPlaceStructureCommand extends CompositeCommand {
return Optional.of(Arrays.stream(StructureRotation.values()).map(StructureRotation::name).toList());
} else if (args.size() == 7) {
return Optional.of(Arrays.stream(Mirror.values()).map(Mirror::name).toList());
}else if (args.size() == 8) {
return Optional.of(List.of("NO_MOBS"));
}
return Optional.of(Collections.emptyList());
}

View File

@ -79,7 +79,7 @@ public class NewAreaListener implements Listener {
private static Random rand = new Random();
private boolean pasting;
private static Gson gson = new Gson();
private record Item(String name, Structure structure, Location location, StructureRotation rot, Mirror mirror) {};
public record Item(String name, Structure structure, Location location, StructureRotation rot, Mirror mirror, Boolean noMobs) {};
Pair<Integer, Integer> min = new Pair<Integer, Integer>(0,0);
Pair<Integer, Integer> max = new Pair<Integer, Integer>(0,0);
// Database handler for structure data
@ -181,7 +181,14 @@ public class NewAreaListener implements Listener {
}
private IslandStructures getIslandStructData(String islandId) {
return this.islandStructureCache.computeIfAbsent(islandId, k -> Objects.requireNonNullElse(handler.loadObject(k), new IslandStructures(islandId)));
// Return from cache if it exists
if (islandStructureCache.containsKey(islandId)) {
return islandStructureCache.get(islandId);
}
// Get from database
IslandStructures struct = handler.objectExists(islandId) ? handler.loadObject(islandId) : new IslandStructures(islandId);
this.islandStructureCache.put(islandId, struct);
return struct;
}
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
@ -219,6 +226,7 @@ public class NewAreaListener implements Listener {
for (String vector : section.getKeys(false)) {
StructureRotation rot = StructureRotation.NONE;
Mirror mirror = Mirror.NONE;
boolean noMobs = false;
String name = section.getString(vector);
// Check for rotation
String[] split = name.split(",");
@ -229,7 +237,10 @@ public class NewAreaListener implements Listener {
}
if (split.length == 3) {
// Mirror
mirror = Enums.getIfPresent(Mirror.class, split[1].strip().toUpperCase(Locale.ENGLISH)).or(Mirror.NONE);
mirror = Enums.getIfPresent(Mirror.class, split[2].strip().toUpperCase(Locale.ENGLISH)).or(Mirror.NONE);
}
if (split.length == 4) {
noMobs = split[3].strip().toUpperCase(Locale.ENGLISH).equals("NO_MOBS");
}
// Load Structure
Structure s = Bukkit.getStructureManager().loadStructure(NamespacedKey.fromString("minecraft:" + name));
@ -244,7 +255,7 @@ public class NewAreaListener implements Listener {
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(name, s, l, rot, mirror));
itemsToBuild.add(new Item(name, s, l, rot, mirror, noMobs));
} else {
addon.logError("Structure file syntax error: " + vector + ": " + value);
}
@ -256,7 +267,7 @@ public class NewAreaListener implements Listener {
item.structure().place(item.location(), true, item.rot(), item.mirror(), -1, 1, rand);
addon.log(item.name() + " placed at " + item.location().getWorld().getName() + " " + Util.xyz(item.location().toVector()));
// Find it
BoundingBox bb = removeJigsaw(item.location(), item.structure(), item.rot(), item.name());
BoundingBox bb = removeJigsaw(item);
// Store it
addon.getIslands().getIslandAt(item.location()).map(Island::getUniqueId).ifPresent(id -> {
addon.log("Saved " + item.name());
@ -273,13 +284,15 @@ public class NewAreaListener implements Listener {
/**
* Removes Jigsaw blocks from a placed structure. Fills underwater ruins with water.
* @param loc - location where the structure was placed
* @param structure - structure that was placed
* @param structureRotation - rotation of structure
* @param key
* @param item - record of what's required
* @return the resulting bounding box of the structure
*/
public static BoundingBox removeJigsaw(Location loc, Structure structure, StructureRotation structureRotation, String key) {
public static BoundingBox removeJigsaw(Item item) {
Location loc = item.location();
Structure structure = item.structure();
StructureRotation structureRotation = item.rot();
String key = item.name();
Location otherCorner = switch (structureRotation) {
case CLOCKWISE_180 -> loc.clone().add(new Vector(-structure.getSize().getX(), structure.getSize().getY(), -structure.getSize().getZ()));
@ -299,7 +312,7 @@ public class NewAreaListener implements Listener {
Block b = loc.getWorld().getBlockAt(x, y, z);
if (b.getType().equals(Material.JIGSAW)) {
// I would like to read the data from the block and do something with it!
processJigsaw(b, structureRotation);
processJigsaw(b, structureRotation, !item.noMobs());
} else if (b.getType().equals(Material.STRUCTURE_BLOCK)) {
processStructureBlock(b);
}
@ -338,15 +351,19 @@ public class NewAreaListener implements Listener {
}
private static final Map<Integer, EntityType> BUTCHER_ANIMALS = Map.of(0, EntityType.COW, 1, EntityType.SHEEP, 2, EntityType.PIG);
private static void processJigsaw(Block b, StructureRotation structureRotation) {
private static void processJigsaw(Block b, StructureRotation structureRotation, boolean pasteMobs) {
String data = nmsData(b);
BoxedJigsawBlock bjb = gson.fromJson(data, BoxedJigsawBlock.class);
//BentoBox.getInstance().logDebug("Jigsaw: " + bjb);
//BentoBox.getInstance().logDebug("FinalState: " + bjb.getFinal_state());
String finalState = correctDirection(bjb.getFinal_state(), structureRotation);
//BentoBox.getInstance().logDebug("FinalState after rotation: " + finalState);
BlockData bd = Bukkit.createBlockData(finalState);
b.setBlockData(bd);
if (!bjb.getPool().equalsIgnoreCase("minecraft:empty") && pasteMobs) {
spawnMob(b, bjb);
}
}
private static void spawnMob(Block b, BoxedJigsawBlock bjb) {
// bjb.getPool contains a lot more than just mobs, so we have to filter it to see if any mobs are in there. This list may need to grow in the future
EntityType type =
switch (bjb.getPool()) {
case "minecraft:bastion/mobs/piglin" -> EntityType.PIGLIN;
@ -362,16 +379,19 @@ public class NewAreaListener implements Listener {
case "minecraft:village/common/animals" -> BUTCHER_ANIMALS.get(rand.nextInt(3));
default -> null;
};
// Villagers
if (bjb.getPool().contains("zombie/villagers")) {
type = EntityType.ZOMBIE_VILLAGER;
} else if (bjb.getPool().contains("villagers")) {
type = EntityType.VILLAGER;
}
if (type == null) {
BentoBox.getInstance().logDebug(bjb.getPool());
}
// Spawn it
if (type != null && b.getWorld().spawnEntity(b.getRelative(BlockFace.UP).getLocation(), type) != null) {
//BentoBox.getInstance().logDebug("Spawned a " + type + " at " + b.getRelative(BlockFace.UP).getLocation());
}
}
}
/**

View File

@ -23,6 +23,18 @@ boxed:
parameters: '[home number]'
sethome:
parameters: '[home number]'
admin:
place:
description: "Place an area structure"
parameters: "<x> <y> <z> <rotation> <mirror> <no mobs>"
use-integers: "&c Coordinated must be integers"
wrong-world: "&c This command can only be used in a Boxed world"
unknown-structure: "&c Cannot place: Unknown structure"
unknown-rotation: "&c Cannot place: Unknown rotation type"
unknown-mirror: "&c Cannot place: Unknown mirror type"
saved: "&a Placed and saved to structures.yml"
failed: "&c Could not be saved to structures.yml. Check console for error"
unknown: "&c Unknown parameter: [label]"
island:
go:
parameters: '[home number]'

View File

@ -1,4 +1,5 @@
normal:
# This file is written by the /boxadmin place command
ormal:
0,64,80: village/plains/houses/plains_masons_house_1,CLOCKWISE_90
-38,63,20: ruined_portal/portal_5
3,58,-60: shipwreck/rightsideup_backhalf
@ -15,6 +16,8 @@ normal:
34,69,-29: village/common/iron_golem
41,69,-26: village/common/animals/cat_calico
35,69,-22: village/common/animals/cat_calico
32,70,65: village/savanna/houses/savanna_temple_1,NONE,NONE
25,70,72: village/savanna/houses/savanna_small_house_7,COUNTERCLOCKWISE_90,NONE,NO_MOBS
99,72,118: pillager_outpost/feature_cage1
33,72,100: village/desert/houses/desert_farm_1
26,72,100: village/desert/houses/desert_medium_house_1
@ -33,7 +36,16 @@ normal:
-52,72,33: village/snowy/houses/snowy_farm_1,COUNTERCLOCKWISE_90,NONE
-28,63,47: village/snowy/snowy_lamp_post_01,COUNTERCLOCKWISE_90,NONE
-6,64,52: village/snowy/houses/snowy_small_house_3,COUNTERCLOCKWISE_90,NONE
40,73,-3: village/savanna/houses/savanna_small_house_1,CLOCKWISE_180
60,81,88: village/desert/camel_spawn,NONE,NONE
64,81,93: village/desert/houses/desert_animal_pen_1,NONE,NONE
49,81,94: village/desert/houses/desert_large_farm_1,NONE,NONE
-106,45,68: underwater_ruin/big_brick_1,NONE,NONE
-117,45,88: underwater_ruin/big_brick_2,NONE,NONE
64,78,49: village/common/animals/horses_1,NONE,NONE
65,78,51: village/common/animals/horses_2,NONE,NONE
67,78,52: village/common/animals/horses_3,NONE,NONE
57,70,-90: village/common/animals/horses_5,NONE,NONE
62,70,-88: village/common/animals/horses_5,NONE,NONE
nether:
16,32,0: bastion/bridge/starting_pieces/entrance