diff --git a/pom.xml b/pom.xml
index 410f118..10d06d3 100644
--- a/pom.xml
+++ b/pom.xml
@@ -65,7 +65,7 @@
-LOCAL
- 2.0.3
+ 2.1.0
BentoBoxWorld_Boxed
bentobox-world
@@ -150,7 +150,7 @@
org.mockito
mockito-core
- 4.8.0
+ 3.11.1
test
@@ -199,6 +199,14 @@
src/main/resources
true
+
+ src/main/resources/structures
+ ./structures
+ false
+
+ *.nbt
+
+
src/main/resources/locales
./locales
@@ -217,7 +225,7 @@
-
+
org.apache.maven.plugins
maven-clean-plugin
@@ -230,13 +238,14 @@
blu
+ nbt
org.apache.maven.plugins
maven-compiler-plugin
- 3.7.0
+ 3.8.1
${java.version}
@@ -247,15 +256,62 @@
3.0.0-M5
- --illegal-access=permit
+ ${argLine}
+ --add-opens java.base/java.lang=ALL-UNNAMED
+ --add-opens java.base/java.math=ALL-UNNAMED
+ --add-opens java.base/java.io=ALL-UNNAMED
+ --add-opens java.base/java.util=ALL-UNNAMED
+ --add-opens
+ java.base/java.util.stream=ALL-UNNAMED
+ --add-opens java.base/java.text=ALL-UNNAMED
+ --add-opens
+ java.base/java.util.regex=ALL-UNNAMED
+ --add-opens
+ java.base/java.nio.channels.spi=ALL-UNNAMED
+ --add-opens java.base/sun.nio.ch=ALL-UNNAMED
+ --add-opens java.base/java.net=ALL-UNNAMED
+ --add-opens
+ java.base/java.util.concurrent=ALL-UNNAMED
+ --add-opens java.base/sun.nio.fs=ALL-UNNAMED
+ --add-opens java.base/sun.nio.cs=ALL-UNNAMED
+ --add-opens java.base/java.nio.file=ALL-UNNAMED
+ --add-opens
+ java.base/java.nio.charset=ALL-UNNAMED
+ --add-opens
+ java.base/java.lang.reflect=ALL-UNNAMED
+ --add-opens
+ java.logging/java.util.logging=ALL-UNNAMED
+ --add-opens java.base/java.lang.ref=ALL-UNNAMED
+ --add-opens java.base/java.util.jar=ALL-UNNAMED
+ --add-opens java.base/java.util.zip=ALL-UNNAMED
-
+
org.apache.maven.plugins
maven-jar-plugin
3.1.0
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+ 3.3.0
+
+ false
+ -Xdoclint:none
+
+ ${java.home}/bin/javadoc
+
+
+
+ attach-javadocs
+ install
+
+ jar
+
+
+
+
org.apache.maven.plugins
maven-source-plugin
@@ -282,7 +338,7 @@
org.jacoco
jacoco-maven-plugin
- 0.8.3
+ 0.8.7
true
@@ -293,16 +349,21 @@
- pre-unit-test
+ prepare-agent
prepare-agent
- post-unit-test
+ report
report
+
+
+ XML
+
+
diff --git a/src/.gitignore b/src/.gitignore
new file mode 100644
index 0000000..9bb88d3
--- /dev/null
+++ b/src/.gitignore
@@ -0,0 +1 @@
+/.DS_Store
diff --git a/src/main/java/world/bentobox/boxed/Boxed.java b/src/main/java/world/bentobox/boxed/Boxed.java
index bb6d325..352b966 100644
--- a/src/main/java/world/bentobox/boxed/Boxed.java
+++ b/src/main/java/world/bentobox/boxed/Boxed.java
@@ -280,7 +280,7 @@ public class Boxed extends GameModeAddon {
int p = (int) (count / percent * 100);
if (p % 10 == 0 && p != last) {
last = p;
- this.log("Storing seed chunks for " + world.getEnvironment() + " " + p + "% done");
+ this.log("Pregenrating seed chunks for " + world.getName() + "'s " + world.getEnvironment() + " " + p + "% done");
}
}
@@ -293,10 +293,7 @@ public class Boxed extends GameModeAddon {
* @return the chunkGenerator for the environment
*/
public AbstractBoxedChunkGenerator getChunkGenerator(Environment env) {
- if (env.equals(Environment.NORMAL)) {
- return chunkGenerator;
- }
- return netherChunkGenerator;
+ return env.equals(Environment.NORMAL) ? chunkGenerator : netherChunkGenerator;
}
/**
diff --git a/src/main/java/world/bentobox/boxed/commands/AdminPlaceStructureCommand.java b/src/main/java/world/bentobox/boxed/commands/AdminPlaceStructureCommand.java
index 44b4fae..8a59f51 100644
--- a/src/main/java/world/bentobox/boxed/commands/AdminPlaceStructureCommand.java
+++ b/src/main/java/world/bentobox/boxed/commands/AdminPlaceStructureCommand.java
@@ -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");
@@ -41,10 +57,10 @@ public class AdminPlaceStructureCommand extends CompositeCommand {
@Override
public void setup() {
- this.setPermission("boxed.admin.place");
+ this.setPermission("boxed.commands.boxadmin.place");
this.setOnlyPlayer(false);
- this.setParametersHelp("boxed.admin.place.parameters");
- this.setDescription("boxed.admin.place.description");
+ this.setParametersHelp("boxed.commands.boxadmin.place.parameters");
+ this.setDescription("boxed.commands.boxadmin.place.description");
}
@@ -54,10 +70,10 @@ 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");
+ user.sendMessage("boxed.commands.boxadmin.place.wrong-world");
return false;
}
/*
@@ -66,6 +82,7 @@ public class AdminPlaceStructureCommand extends CompositeCommand {
* 4. place ~ ~ ~
* 5. place ~ ~ ~ ROTATION
* 6. place ~ ~ ~ ROTATION MIRROR
+ * 7. place ~ ~ ~ ROTATION MIRROR NO_MOBS
*/
// Format is place ~ ~ ~ or coords
if (args.isEmpty() || args.size() == 2 || args.size() == 3 || args.size() > 6) {
@@ -75,7 +92,7 @@ public class AdminPlaceStructureCommand extends CompositeCommand {
// First arg must always be the structure name
List options = Bukkit.getStructureManager().getStructures().keySet().stream().map(k -> k.getKey()).toList();
if (!options.contains(args.get(0).toLowerCase(Locale.ENGLISH))) {
- user.sendMessage("boxed.admin.place.unknown-structure");
+ user.sendMessage("boxed.commands.boxadmin.place.unknown-structure");
return false;
}
// If that is all we have, we're done
@@ -86,7 +103,7 @@ public class AdminPlaceStructureCommand extends CompositeCommand {
if ((!args.get(1).equals("~") && !Util.isInteger(args.get(1), true))
|| (!args.get(2).equals("~") && !Util.isInteger(args.get(2), true))
|| (!args.get(3).equals("~") && !Util.isInteger(args.get(3), true))) {
- user.sendMessage("boxed.admin.place.use-integers");
+ user.sendMessage("boxed.commands.boxadmin.place.use-integers");
return false;
}
// If that is all we have, we're done
@@ -96,7 +113,7 @@ public class AdminPlaceStructureCommand extends CompositeCommand {
// But there is more!
sr = Enums.getIfPresent(StructureRotation.class, args.get(4).toUpperCase(Locale.ENGLISH)).orNull();
if (sr == null) {
- user.sendMessage("boxed.admin.place.unknown-rotation");
+ user.sendMessage("boxed.commands.boxadmin.place.unknown-rotation");
Arrays.stream(StructureRotation.values()).map(StructureRotation::name).forEach(user::sendRawMessage);
return false;
}
@@ -106,10 +123,18 @@ public class AdminPlaceStructureCommand extends CompositeCommand {
// But there is more!
mirror = Enums.getIfPresent(Mirror.class, args.get(5).toUpperCase(Locale.ENGLISH)).orNull();
if (mirror == null) {
- user.sendMessage("boxed.admin.place.unknown-mirror");
+ user.sendMessage("boxed.commands.boxadmin.place.unknown-mirror");
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.commands.boxadmin.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.commands.boxadmin.place.saved");
+ } else {
+ user.sendMessage("boxed.commands.boxadmin.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());
}
diff --git a/src/main/java/world/bentobox/boxed/generators/chunks/AbstractBoxedChunkGenerator.java b/src/main/java/world/bentobox/boxed/generators/chunks/AbstractBoxedChunkGenerator.java
index c5d6290..77d0407 100644
--- a/src/main/java/world/bentobox/boxed/generators/chunks/AbstractBoxedChunkGenerator.java
+++ b/src/main/java/world/bentobox/boxed/generators/chunks/AbstractBoxedChunkGenerator.java
@@ -53,12 +53,12 @@ public abstract class AbstractBoxedChunkGenerator extends ChunkGenerator {
}
}
}
- chunks.put(new Pair<>(x, z), new ChunkStore(chunk.getChunkSnapshot(), getEnts(chunk), getChests(chunk), chunkBiomes));
+ chunks.put(new Pair<>(x, z), new ChunkStore(chunk.getChunkSnapshot(), getEnts(chunk), getTileEnts(chunk), chunkBiomes));
}
protected abstract List getEnts(Chunk chunk);
- protected abstract List getChests(Chunk chunk);
+ protected abstract List getTileEnts(Chunk chunk);
/**
* Get the chunk store for these chunk coords or null if there is none.
@@ -84,13 +84,15 @@ public abstract class AbstractBoxedChunkGenerator extends ChunkGenerator {
* @return mapped chunk coord
*/
public static int repeatCalc(int chunkCoord) {
+ return Math.floorMod(chunkCoord + size, size*2) - size;
+ /*
int xx;
if (chunkCoord > 0) {
xx = Math.floorMod(chunkCoord + size, size*2) - size;
} else {
xx = Math.floorMod(chunkCoord - size, -size*2) + size;
}
- return xx;
+ return xx;*/
}
/**
diff --git a/src/main/java/world/bentobox/boxed/generators/chunks/BoxedBlockPopulator.java b/src/main/java/world/bentobox/boxed/generators/chunks/BoxedBlockPopulator.java
index b36cb7f..811fd79 100644
--- a/src/main/java/world/bentobox/boxed/generators/chunks/BoxedBlockPopulator.java
+++ b/src/main/java/world/bentobox/boxed/generators/chunks/BoxedBlockPopulator.java
@@ -52,7 +52,6 @@ public class BoxedBlockPopulator extends BlockPopulator {
ChunkStore data = chunks.get(coords);
// Paste entities
data.bpEnts().forEach(e -> {
-
Location l = getLoc(world, e.relativeLoc().clone(), chunkX, chunkZ);
if (limitedRegion.isInRegion(l)) {
Entity ent = limitedRegion.spawnEntity(l, e.entity().getType());
@@ -61,10 +60,14 @@ public class BoxedBlockPopulator extends BlockPopulator {
});
// Fill chests
limitedRegion.getTileEntities().forEach(te -> {
- for (ChestData cd : data.chests()) {
- Location chestLoc = getLoc(world, cd.relativeLoc().clone(), chunkX, chunkZ);
- if (limitedRegion.isInRegion(chestLoc) && te.getLocation().equals(chestLoc)) {
- this.setBlockState(te, cd.chest());
+ int teX = BoxedChunkGenerator.repeatCalc(te.getX() >> 4);
+ int teZ = BoxedChunkGenerator.repeatCalc(te.getZ() >> 4);
+ if (teX == xx && teZ == zz) {
+ for (ChestData cd : data.chests()) {
+ Location chestLoc = getLoc(world, cd.relativeLoc().clone(), chunkX, chunkZ);
+ if (limitedRegion.isInRegion(chestLoc) && te.getLocation().equals(chestLoc)) {
+ this.setBlockState(te, cd.chest());
+ }
}
}
});
diff --git a/src/main/java/world/bentobox/boxed/generators/chunks/BoxedChunkGenerator.java b/src/main/java/world/bentobox/boxed/generators/chunks/BoxedChunkGenerator.java
index c462e39..e4ca1f5 100644
--- a/src/main/java/world/bentobox/boxed/generators/chunks/BoxedChunkGenerator.java
+++ b/src/main/java/world/bentobox/boxed/generators/chunks/BoxedChunkGenerator.java
@@ -72,7 +72,7 @@ public class BoxedChunkGenerator extends AbstractBoxedChunkGenerator {
}
@Override
- protected List getChests(Chunk chunk) {
+ protected List getTileEnts(Chunk chunk) {
return Arrays.stream(chunk.getTileEntities()).map(t -> new ChestData(getLocInChunk(t.getLocation()), this.getBluePrintBlock(t.getBlock()))).toList();
}
@@ -120,8 +120,10 @@ public class BoxedChunkGenerator extends AbstractBoxedChunkGenerator {
return bpEnts;
}
+ // Get the location in the chunk
private Vector getLocInChunk(Location l) {
- return new Vector(l.getBlockX() % 16, l.getBlockY(), l.getBlockZ() % 16);
+ // Have to use Math function because java % doesn't work correctly IMO with negatives
+ return new Vector(Math.floorMod(l.getBlockX(),16), l.getBlockY(), Math.floorMod(l.getBlockZ(), 16));
}
diff --git a/src/main/java/world/bentobox/boxed/generators/chunks/BoxedSeedChunkGenerator.java b/src/main/java/world/bentobox/boxed/generators/chunks/BoxedSeedChunkGenerator.java
index 81e40f2..6ba8dab 100644
--- a/src/main/java/world/bentobox/boxed/generators/chunks/BoxedSeedChunkGenerator.java
+++ b/src/main/java/world/bentobox/boxed/generators/chunks/BoxedSeedChunkGenerator.java
@@ -83,7 +83,7 @@ public class BoxedSeedChunkGenerator extends AbstractBoxedChunkGenerator {
}
@Override
- protected List getChests(Chunk chunk) {
+ protected List getTileEnts(Chunk chunk) {
// These won't be stored
return null;
}
diff --git a/src/main/java/world/bentobox/boxed/listeners/AdvancementListener.java b/src/main/java/world/bentobox/boxed/listeners/AdvancementListener.java
index 444a761..d65bf1a 100644
--- a/src/main/java/world/bentobox/boxed/listeners/AdvancementListener.java
+++ b/src/main/java/world/bentobox/boxed/listeners/AdvancementListener.java
@@ -66,7 +66,7 @@ public class AdvancementListener implements Listener {
}
- private Advancement getAdvancement(String string) {
+ public static Advancement getAdvancement(String string) {
return StreamSupport.stream(
Spliterators.spliteratorUnknownSize(Bukkit.advancementIterator(), Spliterator.ORDERED), false)
.filter(a -> a.getKey().toString().equals(string))
@@ -191,7 +191,8 @@ public class AdvancementListener implements Listener {
}
- private void giveAdv(Player player, Advancement adv) {
+ public static void giveAdv(Player player, Advancement adv) {
+ //BentoBox.getInstance().logDebug("Give Adv " + adv.getKey() + " done status " + player.getAdvancementProgress(adv).isDone());
if (adv != null && !player.getAdvancementProgress(adv).isDone()) {
adv.getCriteria().forEach(player.getAdvancementProgress(adv)::awardCriteria);
}
@@ -199,6 +200,17 @@ public class AdvancementListener implements Listener {
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void onPlayerJoin(PlayerJoinEvent e) {
+ /*
+ StreamSupport.stream(
+ Spliterators.spliteratorUnknownSize(Bukkit.advancementIterator(), Spliterator.ORDERED), false).forEach(a-> {
+ AdvancementProgress progress = e.getPlayer().getAdvancementProgress(a);
+ BentoBox.getInstance().logDebug(a.getKey() + " " + progress.isDone());
+ BentoBox.getInstance().logDebug("Awarded");
+ progress.getAwardedCriteria().forEach(c -> BentoBox.getInstance().logDebug(c));
+ BentoBox.getInstance().logDebug("Remaining");
+ progress.getRemainingCriteria().forEach(c -> BentoBox.getInstance().logDebug(c));
+ });
+ */
User user = User.getInstance(e.getPlayer());
if (Util.sameWorld(addon.getOverWorld(), e.getPlayer().getWorld())) {
// Set advancements to same as island
diff --git a/src/main/java/world/bentobox/boxed/listeners/NewAreaListener.java b/src/main/java/world/bentobox/boxed/listeners/NewAreaListener.java
index 77c3acd..8280583 100644
--- a/src/main/java/world/bentobox/boxed/listeners/NewAreaListener.java
+++ b/src/main/java/world/bentobox/boxed/listeners/NewAreaListener.java
@@ -1,7 +1,9 @@
package world.bentobox.boxed.listeners;
import java.io.File;
+import java.io.IOException;
import java.lang.reflect.Field;
+import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
@@ -25,10 +27,13 @@ import org.bukkit.block.structure.StructureRotation;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.craftbukkit.v1_19_R3.CraftWorld;
+import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
+import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
+import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.loot.LootTables;
import org.bukkit.structure.Structure;
import org.bukkit.util.BoundingBox;
@@ -45,12 +50,14 @@ import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.api.events.BentoBoxReadyEvent;
import world.bentobox.bentobox.api.events.island.IslandCreatedEvent;
import world.bentobox.bentobox.api.events.island.IslandResettedEvent;
+import world.bentobox.bentobox.database.Database;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.util.Pair;
import world.bentobox.bentobox.util.Util;
import world.bentobox.boxed.Boxed;
import world.bentobox.boxed.objects.BoxedJigsawBlock;
import world.bentobox.boxed.objects.BoxedStructureBlock;
+import world.bentobox.boxed.objects.IslandStructures;
/**
* @author tastybento
@@ -59,15 +66,28 @@ import world.bentobox.boxed.objects.BoxedStructureBlock;
public class NewAreaListener implements Listener {
private static final List CARDINALS = List.of(BlockFace.NORTH, BlockFace.SOUTH, BlockFace.EAST, BlockFace.WEST);
+ private static final List JAR_STRUCTURES = List.of("bee", "pillager", "polar_bear", "axolotl", "allay", "parrot", "frog");
+ private static final List STRUCTURES = List.of("ancient_city", "bastion_remnant", "bastion",
+ "buried_treasure", "desert_pyramid", "end_city",
+ "fortress", "igloo", "jungle_pyramid", "mansion", "mineshaft", "mineshaft_mesa",
+ "monument", "nether_fossil", "ocean_ruin_cold", "ocean_ruin_warm", "pillager_outpost",
+ "ruined_portal_desert", "ruined_portal_jungle", "ruined_portal_mountain", "ruined_portal_nether",
+ "ruined_portal_ocean", "ruined_portal_swamp", "ruined_portal", "shipwreck_beached",
+ "shipwreck", "stronghold", "swamp_hut", "village_desert", "village_plains",
+ "village_savanna", "village_snowy", "village_taiga");
private final Boxed addon;
private File structureFile;
private Queue- itemsToBuild = new LinkedList<>();
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 min = new Pair(0,0);
Pair max = new Pair(0,0);
+ // Database handler for structure data
+ private final Database handler;
+ private Map islandStructureCache = new HashMap<>();
+
/**
@@ -78,9 +98,24 @@ public class NewAreaListener implements Listener {
addon.saveResource("structures.yml", false);
// Load the config
structureFile = new File(addon.getDataFolder(), "structures.yml");
-
+ // Get database ready
+ handler = new Database<>(addon, IslandStructures.class);
// Try to build something every second
Bukkit.getScheduler().runTaskTimer(addon.getPlugin(), () -> BuildItem(), 20, 20);
+ // Experiment: TODO: read all files in from the structure folder including the ones saved from the jar file
+ for (String js : JAR_STRUCTURES) {
+ addon.saveResource("structures/" + js + ".nbt", false);
+ File structureFile = new File(addon.getDataFolder(), "structures/" + js + ".nbt");
+ try {
+ Structure s = Bukkit.getStructureManager().loadStructure(structureFile);
+ Bukkit.getStructureManager().registerStructure(NamespacedKey.fromString("minecraft:boxed/" + js), s);
+ addon.log("Loaded " + js + ".nbt");
+ } catch (IOException e) {
+ addon.logError("Error trying to load " + structureFile.getAbsolutePath());
+ e.printStackTrace();
+ }
+
+ }
}
private void BuildItem() {
@@ -96,7 +131,7 @@ public class NewAreaListener implements Listener {
* Build a list of structures
* @param event event
*/
- @EventHandler()
+ @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void onBentoBoxReady(BentoBoxReadyEvent event) {
addon.saveResource("templates.yml", false);
File templateFile = new File(addon.getDataFolder(), "templates.yml");
@@ -104,15 +139,73 @@ public class NewAreaListener implements Listener {
YamlConfiguration loader = YamlConfiguration.loadConfiguration(templateFile);
List list = loader.getStringList("templates");
for (String struct : list) {
- Structure s = Bukkit.getStructureManager().loadStructure(NamespacedKey.fromString(struct));
- if (s == null) {
- //addon.log("Now loading group from: " + struct);
+ if (!struct.endsWith("/")) {
+ Bukkit.getStructureManager().loadStructure(NamespacedKey.fromString(struct));
}
}
}
}
+ @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
+ public void onPlayerMove(PlayerMoveEvent e) {
+ // Ignore head movements
+ if (!addon.inWorld(e.getFrom().getWorld()) || e.getFrom().toVector().equals(e.getTo().toVector())) {
+ return;
+ }
+ // Check where the player is
+ addon.getIslands().getIslandAt(e.getTo()).ifPresent(island -> {
+ // See if island is in cache
+ final String islandId = island.getUniqueId();
+ IslandStructures is = getIslandStructData(islandId);
+ // Check if player is in any of the structures
+ Map structures = e.getTo().getWorld().getEnvironment().equals(Environment.NETHER) ?
+ is.getNetherStructureBoundingBoxMap():is.getStructureBoundingBoxMap();
+ for (Map.Entry en:structures.entrySet()) {
+ if (en.getKey().contains(e.getTo().toVector())) {
+ for (String s: STRUCTURES) {
+ if (en.getValue().startsWith(s)) {
+ giveAdvFromCriteria(e.getPlayer(), s);
+ }
+ }
+ //STRUCTURES.stream().filter(en.getValue()::startsWith).forEach(s -> giveAdvFromCriteria(e.getPlayer(), s));
+ }
+ }
+ });
+ }
+
+ /**
+ * Gives a player all the advancements that have string as a named criteria
+ * @param player - player
+ * @param string - criteria
+ */
+ private void giveAdvFromCriteria(Player player, String string) {
+ // Give every advancement that requires a bastion
+ Bukkit.advancementIterator().forEachRemaining(ad -> {
+ if (!player.getAdvancementProgress(ad).isDone()) {
+ for (String crit: ad.getCriteria()) {
+ if (crit.equals(string)) {
+ // Set the criteria (it may not complete the advancement completely
+ player.getAdvancementProgress(ad).awardCriteria(crit);
+ break;
+ }
+ }
+ }
+ });
+
+ }
+
+ private IslandStructures getIslandStructData(String 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)
public void onIslandCreated(IslandCreatedEvent event) {
setUpIsland(event.getIsland());
@@ -148,6 +241,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(",");
@@ -158,7 +252,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));
@@ -173,7 +270,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);
}
@@ -185,18 +282,32 @@ 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
- 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());
+ if (item.location().getWorld().getEnvironment().equals(Environment.NETHER)) {
+ getIslandStructData(id).addNetherStructure(bb, item.name());
+ } else {
+ getIslandStructData(id).addStructure(bb, item.name());
+ }
+ handler.saveObjectAsync(getIslandStructData(id));
+ });
+
pasting = false;
}
/**
* 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 void 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()));
@@ -216,7 +327,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);
}
@@ -227,7 +338,7 @@ public class NewAreaListener implements Listener {
}
}
}
-
+ return bb;
}
@@ -255,15 +366,19 @@ public class NewAreaListener implements Listener {
}
private static final Map 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;
@@ -279,16 +394,28 @@ public class NewAreaListener implements Listener {
case "minecraft:village/common/animals" -> BUTCHER_ANIMALS.get(rand.nextInt(3));
default -> null;
};
+ // Boxed
+ if (type == null && bjb.getPool().startsWith("minecraft:boxed/")) {
+ String entString = bjb.getPool().toUpperCase(Locale.ENGLISH).substring(16, bjb.getPool().length());
+ type = Enums.getIfPresent(EntityType.class, entString).orNull();
+ }
+ // 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) {
+ if (type != null) {
+ Entity e = b.getWorld().spawnEntity(b.getRelative(BlockFace.UP).getLocation(), type);
+ if (e != null) {
+ e.setPersistent(true);
+ }
//BentoBox.getInstance().logDebug("Spawned a " + type + " at " + b.getRelative(BlockFace.UP).getLocation());
}
-
}
/**
diff --git a/src/main/java/world/bentobox/boxed/objects/IslandStructures.java b/src/main/java/world/bentobox/boxed/objects/IslandStructures.java
new file mode 100644
index 0000000..a60203d
--- /dev/null
+++ b/src/main/java/world/bentobox/boxed/objects/IslandStructures.java
@@ -0,0 +1,76 @@
+package world.bentobox.boxed.objects;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.bukkit.util.BoundingBox;
+
+import com.google.gson.annotations.Expose;
+
+import world.bentobox.bentobox.database.objects.DataObject;
+import world.bentobox.bentobox.database.objects.Table;
+
+@Table(name = "IslandStructures")
+public class IslandStructures implements DataObject {
+
+ @Expose
+ String uniqueId;
+ @Expose
+ Map structureBoundingBoxMap = new HashMap<>();
+ @Expose
+ Map netherStructureBoundingBoxMap = new HashMap<>();
+
+ public IslandStructures(String islandId) {
+ this.uniqueId = islandId;
+ }
+
+ @Override
+ public String getUniqueId() {
+ return uniqueId;
+ }
+
+ @Override
+ public void setUniqueId(String uniqueId) {
+ this.uniqueId = uniqueId;
+ }
+
+ /**
+ * Add a structure for this island
+ * @param bb - bounding box of the structure
+ * @param key - structure namespace key
+ */
+ public void addStructure(BoundingBox bb, String key) {
+ getStructureBoundingBoxMap().put(bb, key);
+ }
+
+ public Map getStructureBoundingBoxMap() {
+ if (structureBoundingBoxMap == null) {
+ structureBoundingBoxMap = new HashMap<>();
+ }
+ return structureBoundingBoxMap;
+ }
+
+ public void setStructureBoundingBoxMap(Map structureBoundingBoxMap) {
+ this.structureBoundingBoxMap = structureBoundingBoxMap;
+ }
+
+ /**
+ * Add a structure for this island
+ * @param bb - bounding box of the structure
+ * @param key - structure namespace key
+ */
+ public void addNetherStructure(BoundingBox bb, String key) {
+ getNetherStructureBoundingBoxMap().put(bb, key);
+ }
+
+ public Map getNetherStructureBoundingBoxMap() {
+ if (netherStructureBoundingBoxMap == null) {
+ netherStructureBoundingBoxMap = new HashMap<>();
+ }
+ return netherStructureBoundingBoxMap;
+ }
+
+ public void setNetherStructureBoundingBoxMap(Map netherStructureBoundingBoxMap) {
+ this.netherStructureBoundingBoxMap = netherStructureBoundingBoxMap;
+ }
+}
diff --git a/src/main/resources/advancements.yml b/src/main/resources/advancements.yml
index 71c1d1a..1b25253 100644
--- a/src/main/resources/advancements.yml
+++ b/src/main/resources/advancements.yml
@@ -9,7 +9,7 @@ advancements:
'exploration/jungle_in': 0
'exploration/write_book': 0
'forestry/sapling' : 0
- 'adventure/adventuring_time': 13
+ 'adventure/adventuring_time': 13
'adventure/arbalistic': 11
'adventure/avoid_vibration': 13
'adventure/bullseye': 5
diff --git a/src/main/resources/locales/en-US.yml b/src/main/resources/locales/en-US.yml
index 104a5c9..4c603c4 100755
--- a/src/main/resources/locales/en-US.yml
+++ b/src/main/resources/locales/en-US.yml
@@ -23,6 +23,18 @@ boxed:
parameters: '[home number]'
sethome:
parameters: '[home number]'
+ boxadmin:
+ place:
+ description: Place an area structure
+ parameters:
+ 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]'
diff --git a/src/main/resources/structures.yml b/src/main/resources/structures.yml
index ef77edd..1e2fc65 100644
--- a/src/main/resources/structures.yml
+++ b/src/main/resources/structures.yml
@@ -1,3 +1,4 @@
+# This file is written by the /boxadmin place command
normal:
0,64,80: village/plains/houses/plains_masons_house_1,CLOCKWISE_90
-38,63,20: ruined_portal/portal_5
@@ -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,36 @@ 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
+ -2,66,10: boxed/frog,NONE,NONE
+ 87,85,120: boxed/pillager,NONE,NONE
+ 85,85,122: boxed/pillager,NONE,NONE
+ 87,85,118: boxed/pillager,NONE,NONE
+ 87,85,123: boxed/pillager,NONE,NONE
+ 89,81,122: boxed/pillager,NONE,NONE
+ 87,77,120: boxed/pillager,NONE,NONE
+ 94,71,111: boxed/pillager,NONE,NONE
+ 90,71,111: boxed/pillager,NONE,NONE
+ 100,72,110: pillager_outpost/feature_tent1,NONE,NONE
+ -20,63,-24: boxed/parrot,NONE,NONE
+ -21,65,-21: boxed/parrot,NONE,NONE
+ 1,69,-12: boxed/frog,NONE,NONE
+ 2,67,10: boxed/parrot,NONE,NONE
+ -58,64,13: boxed/polar_bear,NONE,NONE
+ -90,63,57: boxed/polar_bear,NONE,NONE
+ -87,63,63: boxed/polar_bear,NONE,NONE
+ -33,69,58: boxed/polar_bear,NONE,NONE
+ 137,-8,11: boxed/axolotl,NONE,NONE
+ 137,-8,9: boxed/axolotl,NONE,NONE
nether:
16,32,0: bastion/bridge/starting_pieces/entrance
diff --git a/src/main/resources/structures/allay.nbt b/src/main/resources/structures/allay.nbt
new file mode 100644
index 0000000..a204ba2
Binary files /dev/null and b/src/main/resources/structures/allay.nbt differ
diff --git a/src/main/resources/structures/axolotl.nbt b/src/main/resources/structures/axolotl.nbt
new file mode 100644
index 0000000..f367743
Binary files /dev/null and b/src/main/resources/structures/axolotl.nbt differ
diff --git a/src/main/resources/structures/bee.nbt b/src/main/resources/structures/bee.nbt
new file mode 100644
index 0000000..fc343eb
Binary files /dev/null and b/src/main/resources/structures/bee.nbt differ
diff --git a/src/main/resources/structures/frog.nbt b/src/main/resources/structures/frog.nbt
new file mode 100644
index 0000000..141307c
Binary files /dev/null and b/src/main/resources/structures/frog.nbt differ
diff --git a/src/main/resources/structures/parrot.nbt b/src/main/resources/structures/parrot.nbt
new file mode 100644
index 0000000..0556701
Binary files /dev/null and b/src/main/resources/structures/parrot.nbt differ
diff --git a/src/main/resources/structures/pillager.nbt b/src/main/resources/structures/pillager.nbt
new file mode 100644
index 0000000..f90628c
Binary files /dev/null and b/src/main/resources/structures/pillager.nbt differ
diff --git a/src/main/resources/structures/polar_bear.nbt b/src/main/resources/structures/polar_bear.nbt
new file mode 100644
index 0000000..5448180
Binary files /dev/null and b/src/main/resources/structures/polar_bear.nbt differ
diff --git a/src/main/resources/templates.yml b/src/main/resources/templates.yml
index 847844d..2745582 100644
--- a/src/main/resources/templates.yml
+++ b/src/main/resources/templates.yml
@@ -1,4 +1,11 @@
templates:
+- boxed/frog
+- boxed/allay
+- boxed/bee
+- boxed/axolotl
+- boxed/pillager
+- boxed/polar_bear
+- boxed/parrot
- ancient_city/city/entrance/entrance_connector
- ancient_city/city/entrance/entrance_path_1
- ancient_city/city/entrance/entrance_path_2