Merge pull request #49 from BentoBoxWorld/develop

Release 2.1.0
This commit is contained in:
tastybento 2023-05-14 15:42:30 -07:00 committed by GitHub
commit bc56090f46
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 442 additions and 72 deletions

79
pom.xml
View File

@ -65,7 +65,7 @@
<!-- Do not change unless you want different name for local builds. -->
<build.number>-LOCAL</build.number>
<!-- This allows to change between versions. -->
<build.version>2.0.3</build.version>
<build.version>2.1.0</build.version>
<sonar.projectKey>BentoBoxWorld_Boxed</sonar.projectKey>
<sonar.organization>bentobox-world</sonar.organization>
@ -150,7 +150,7 @@
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>4.8.0</version>
<version>3.11.1</version>
<scope>test</scope>
</dependency>
<dependency>
@ -199,6 +199,14 @@
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/resources/structures</directory>
<targetPath>./structures</targetPath>
<filtering>false</filtering>
<includes>
<include>*.nbt</include>
</includes>
</resource>
<resource>
<directory>src/main/resources/locales</directory>
<targetPath>./locales</targetPath>
@ -217,7 +225,7 @@
</includes>
</resource>
</resources>
<plugins>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-clean-plugin</artifactId>
@ -230,13 +238,14 @@
<configuration>
<nonFilteredFileExtensions>
<nonFilteredFileExtension>blu</nonFilteredFileExtension>
<nonFilteredFileExtension>nbt</nonFilteredFileExtension>
</nonFilteredFileExtensions>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<version>3.8.1</version>
<configuration>
<release>${java.version}</release>
</configuration>
@ -247,15 +256,62 @@
<version>3.0.0-M5</version>
<configuration>
<argLine>
--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
</argLine>
</configuration>
</plugin>
<plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.3.0</version>
<configuration>
<failOnError>false</failOnError>
<additionalJOption>-Xdoclint:none</additionalJOption>
<!-- To compile with Java 11, this tag may be required -->
<javadocExecutable>${java.home}/bin/javadoc</javadocExecutable>
</configuration>
<executions>
<execution>
<id>attach-javadocs</id>
<phase>install</phase>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
@ -282,7 +338,7 @@
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.3</version>
<version>0.8.7</version>
<configuration>
<append>true</append>
<excludes>
@ -293,16 +349,21 @@
</configuration>
<executions>
<execution>
<id>pre-unit-test</id>
<id>prepare-agent</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>post-unit-test</id>
<id>report</id>
<goals>
<goal>report</goal>
</goals>
<configuration>
<formats>
<format>XML</format>
</formats>
</configuration>
</execution>
</executions>
</plugin>

1
src/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/.DS_Store

View File

@ -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;
}
/**

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");
@ -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 <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) {
@ -75,7 +92,7 @@ public class AdminPlaceStructureCommand extends CompositeCommand {
// First arg must always be the structure name
List<String> 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());
}

View File

@ -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<EntityData> getEnts(Chunk chunk);
protected abstract List<ChestData> getChests(Chunk chunk);
protected abstract List<ChestData> 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;*/
}
/**

View File

@ -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());
}
}
}
});

View File

@ -72,7 +72,7 @@ public class BoxedChunkGenerator extends AbstractBoxedChunkGenerator {
}
@Override
protected List<ChestData> getChests(Chunk chunk) {
protected List<ChestData> 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));
}

View File

@ -83,7 +83,7 @@ public class BoxedSeedChunkGenerator extends AbstractBoxedChunkGenerator {
}
@Override
protected List<ChestData> getChests(Chunk chunk) {
protected List<ChestData> getTileEnts(Chunk chunk) {
// These won't be stored
return null;
}

View File

@ -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

View File

@ -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<BlockFace> CARDINALS = List.of(BlockFace.NORTH, BlockFace.SOUTH, BlockFace.EAST, BlockFace.WEST);
private static final List<String> JAR_STRUCTURES = List.of("bee", "pillager", "polar_bear", "axolotl", "allay", "parrot", "frog");
private static final List<String> 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<Item> 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<Integer, Integer> min = new Pair<Integer, Integer>(0,0);
Pair<Integer, Integer> max = new Pair<Integer, Integer>(0,0);
// Database handler for structure data
private final Database<IslandStructures> handler;
private Map<String, IslandStructures> 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<String> 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<BoundingBox, String> structures = e.getTo().getWorld().getEnvironment().equals(Environment.NETHER) ?
is.getNetherStructureBoundingBoxMap():is.getStructureBoundingBoxMap();
for (Map.Entry<BoundingBox, String> 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<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;
@ -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());
}
}
/**

View File

@ -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<BoundingBox, String> structureBoundingBoxMap = new HashMap<>();
@Expose
Map<BoundingBox, String> 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<BoundingBox, String> getStructureBoundingBoxMap() {
if (structureBoundingBoxMap == null) {
structureBoundingBoxMap = new HashMap<>();
}
return structureBoundingBoxMap;
}
public void setStructureBoundingBoxMap(Map<BoundingBox, String> 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<BoundingBox, String> getNetherStructureBoundingBoxMap() {
if (netherStructureBoundingBoxMap == null) {
netherStructureBoundingBoxMap = new HashMap<>();
}
return netherStructureBoundingBoxMap;
}
public void setNetherStructureBoundingBoxMap(Map<BoundingBox, String> netherStructureBoundingBoxMap) {
this.netherStructureBoundingBoxMap = netherStructureBoundingBoxMap;
}
}

View File

@ -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

View File

@ -23,6 +23,18 @@ boxed:
parameters: '[home number]'
sethome:
parameters: '[home number]'
boxadmin:
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,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

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -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