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. --> <!-- Do not change unless you want different name for local builds. -->
<build.number>-LOCAL</build.number> <build.number>-LOCAL</build.number>
<!-- This allows to change between versions. --> <!-- 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.projectKey>BentoBoxWorld_Boxed</sonar.projectKey>
<sonar.organization>bentobox-world</sonar.organization> <sonar.organization>bentobox-world</sonar.organization>
@ -150,7 +150,7 @@
<dependency> <dependency>
<groupId>org.mockito</groupId> <groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId> <artifactId>mockito-core</artifactId>
<version>4.8.0</version> <version>3.11.1</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
@ -199,6 +199,14 @@
<directory>src/main/resources</directory> <directory>src/main/resources</directory>
<filtering>true</filtering> <filtering>true</filtering>
</resource> </resource>
<resource>
<directory>src/main/resources/structures</directory>
<targetPath>./structures</targetPath>
<filtering>false</filtering>
<includes>
<include>*.nbt</include>
</includes>
</resource>
<resource> <resource>
<directory>src/main/resources/locales</directory> <directory>src/main/resources/locales</directory>
<targetPath>./locales</targetPath> <targetPath>./locales</targetPath>
@ -217,7 +225,7 @@
</includes> </includes>
</resource> </resource>
</resources> </resources>
<plugins> <plugins>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-clean-plugin</artifactId> <artifactId>maven-clean-plugin</artifactId>
@ -230,13 +238,14 @@
<configuration> <configuration>
<nonFilteredFileExtensions> <nonFilteredFileExtensions>
<nonFilteredFileExtension>blu</nonFilteredFileExtension> <nonFilteredFileExtension>blu</nonFilteredFileExtension>
<nonFilteredFileExtension>nbt</nonFilteredFileExtension>
</nonFilteredFileExtensions> </nonFilteredFileExtensions>
</configuration> </configuration>
</plugin> </plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version> <version>3.8.1</version>
<configuration> <configuration>
<release>${java.version}</release> <release>${java.version}</release>
</configuration> </configuration>
@ -247,15 +256,62 @@
<version>3.0.0-M5</version> <version>3.0.0-M5</version>
<configuration> <configuration>
<argLine> <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> </argLine>
</configuration> </configuration>
</plugin> </plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId> <artifactId>maven-jar-plugin</artifactId>
<version>3.1.0</version> <version>3.1.0</version>
</plugin> </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> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId> <artifactId>maven-source-plugin</artifactId>
@ -282,7 +338,7 @@
<plugin> <plugin>
<groupId>org.jacoco</groupId> <groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId> <artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.3</version> <version>0.8.7</version>
<configuration> <configuration>
<append>true</append> <append>true</append>
<excludes> <excludes>
@ -293,16 +349,21 @@
</configuration> </configuration>
<executions> <executions>
<execution> <execution>
<id>pre-unit-test</id> <id>prepare-agent</id>
<goals> <goals>
<goal>prepare-agent</goal> <goal>prepare-agent</goal>
</goals> </goals>
</execution> </execution>
<execution> <execution>
<id>post-unit-test</id> <id>report</id>
<goals> <goals>
<goal>report</goal> <goal>report</goal>
</goals> </goals>
<configuration>
<formats>
<format>XML</format>
</formats>
</configuration>
</execution> </execution>
</executions> </executions>
</plugin> </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); int p = (int) (count / percent * 100);
if (p % 10 == 0 && p != last) { if (p % 10 == 0 && p != last) {
last = p; 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 * @return the chunkGenerator for the environment
*/ */
public AbstractBoxedChunkGenerator getChunkGenerator(Environment env) { public AbstractBoxedChunkGenerator getChunkGenerator(Environment env) {
if (env.equals(Environment.NORMAL)) { return env.equals(Environment.NORMAL) ? chunkGenerator : netherChunkGenerator;
return chunkGenerator;
}
return netherChunkGenerator;
} }
/** /**

View File

@ -21,19 +21,35 @@ import org.bukkit.structure.Structure;
import com.google.common.base.Enums; import com.google.common.base.Enums;
import world.bentobox.bentobox.api.commands.CompositeCommand; 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.api.user.User;
import world.bentobox.bentobox.util.Util; import world.bentobox.bentobox.util.Util;
import world.bentobox.boxed.Boxed; import world.bentobox.boxed.Boxed;
import world.bentobox.boxed.listeners.NewAreaListener; import world.bentobox.boxed.listeners.NewAreaListener;
import world.bentobox.boxed.listeners.NewAreaListener.Item;
/** /**
* @author tastybento * @author tastybento
* *
*/ */
public class AdminPlaceStructureCommand extends CompositeCommand { 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 StructureRotation sr = StructureRotation.NONE;
private Mirror mirror = Mirror.NONE; private Mirror mirror = Mirror.NONE;
private boolean noMobs;
public AdminPlaceStructureCommand(CompositeCommand parent) { public AdminPlaceStructureCommand(CompositeCommand parent) {
super(parent, "place"); super(parent, "place");
@ -41,10 +57,10 @@ public class AdminPlaceStructureCommand extends CompositeCommand {
@Override @Override
public void setup() { public void setup() {
this.setPermission("boxed.admin.place"); this.setPermission("boxed.commands.boxadmin.place");
this.setOnlyPlayer(false); this.setOnlyPlayer(false);
this.setParametersHelp("boxed.admin.place.parameters"); this.setParametersHelp("boxed.commands.boxadmin.place.parameters");
this.setDescription("boxed.admin.place.description"); this.setDescription("boxed.commands.boxadmin.place.description");
} }
@ -54,10 +70,10 @@ public class AdminPlaceStructureCommand extends CompositeCommand {
// Initialize // Initialize
sr = StructureRotation.NONE; sr = StructureRotation.NONE;
mirror = Mirror.NONE; mirror = Mirror.NONE;
// Check world // Check world
if (!((Boxed)getAddon()).inWorld(getWorld())) { if (!((Boxed)getAddon()).inWorld(getWorld())) {
user.sendMessage("boxed.admin.place.wrong-world"); user.sendMessage("boxed.commands.boxadmin.place.wrong-world");
return false; return false;
} }
/* /*
@ -66,6 +82,7 @@ public class AdminPlaceStructureCommand extends CompositeCommand {
* 4. place <structure> ~ ~ ~ * 4. place <structure> ~ ~ ~
* 5. place <structure> ~ ~ ~ ROTATION * 5. place <structure> ~ ~ ~ ROTATION
* 6. place <structure> ~ ~ ~ ROTATION MIRROR * 6. place <structure> ~ ~ ~ ROTATION MIRROR
* 7. place <structure> ~ ~ ~ ROTATION MIRROR NO_MOBS
*/ */
// Format is place <structure> ~ ~ ~ or coords // Format is place <structure> ~ ~ ~ or coords
if (args.isEmpty() || args.size() == 2 || args.size() == 3 || args.size() > 6) { 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 // First arg must always be the structure name
List<String> options = Bukkit.getStructureManager().getStructures().keySet().stream().map(k -> k.getKey()).toList(); List<String> options = Bukkit.getStructureManager().getStructures().keySet().stream().map(k -> k.getKey()).toList();
if (!options.contains(args.get(0).toLowerCase(Locale.ENGLISH))) { 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; return false;
} }
// If that is all we have, we're done // 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)) if ((!args.get(1).equals("~") && !Util.isInteger(args.get(1), true))
|| (!args.get(2).equals("~") && !Util.isInteger(args.get(2), true)) || (!args.get(2).equals("~") && !Util.isInteger(args.get(2), true))
|| (!args.get(3).equals("~") && !Util.isInteger(args.get(3), 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; return false;
} }
// If that is all we have, we're done // If that is all we have, we're done
@ -96,7 +113,7 @@ public class AdminPlaceStructureCommand extends CompositeCommand {
// But there is more! // But there is more!
sr = Enums.getIfPresent(StructureRotation.class, args.get(4).toUpperCase(Locale.ENGLISH)).orNull(); sr = Enums.getIfPresent(StructureRotation.class, args.get(4).toUpperCase(Locale.ENGLISH)).orNull();
if (sr == null) { 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); Arrays.stream(StructureRotation.values()).map(StructureRotation::name).forEach(user::sendRawMessage);
return false; return false;
} }
@ -106,10 +123,18 @@ public class AdminPlaceStructureCommand extends CompositeCommand {
// But there is more! // But there is more!
mirror = Enums.getIfPresent(Mirror.class, args.get(5).toUpperCase(Locale.ENGLISH)).orNull(); mirror = Enums.getIfPresent(Mirror.class, args.get(5).toUpperCase(Locale.ENGLISH)).orNull();
if (mirror == null) { 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); Arrays.stream(Mirror.values()).map(Mirror::name).forEach(user::sendRawMessage);
return false; 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 // Syntax is okay
return true; 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 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()); 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); Location spot = new Location(user.getWorld(), x, y, z);
s.place(spot, true, sr, mirror, -1, 1, new Random()); s.place(spot, true, sr, mirror, PALETTE, INTEGRITY, new Random());
NewAreaListener.removeJigsaw(spot, s, sr, tag.getKey()); NewAreaListener.removeJigsaw(new Item(tag.getKey(), s, spot, sr, mirror, noMobs));
saveStructure(spot, tag, user, sr, mirror); boolean result = saveStructure(spot, tag, user, sr, mirror);
return true; 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) { private boolean saveStructure(Location spot, NamespacedKey tag, User user, StructureRotation sr2, Mirror mirror2) {
getAddon().getIslands().getIslandAt(spot).ifPresent(i -> { return getAddon().getIslands().getIslandAt(spot).map(i -> {
int xx = spot.getBlockX() - i.getCenter().getBlockX(); int xx = spot.getBlockX() - i.getCenter().getBlockX();
int zz = spot.getBlockZ() - i.getCenter().getBlockZ(); 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(); YamlConfiguration config = new YamlConfiguration();
try { try {
config.load(structures); config.load(structures);
String value = tag.getKey() + "," + sr2.name() + "," + mirror2.name(); StringBuilder v = new StringBuilder();
config.set(spot.getWorld().getEnvironment().name().toLowerCase(Locale.ENGLISH) + "." + xx + "," + spot.getBlockY() + "," + zz, value); 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); config.save(structures);
} catch (IOException | InvalidConfigurationException e) { } catch (IOException | InvalidConfigurationException e) {
// TODO Auto-generated catch block // TODO Auto-generated catch block
e.printStackTrace(); 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()); return Optional.of(Arrays.stream(StructureRotation.values()).map(StructureRotation::name).toList());
} else if (args.size() == 7) { } else if (args.size() == 7) {
return Optional.of(Arrays.stream(Mirror.values()).map(Mirror::name).toList()); 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()); 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<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. * 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 * @return mapped chunk coord
*/ */
public static int repeatCalc(int chunkCoord) { public static int repeatCalc(int chunkCoord) {
return Math.floorMod(chunkCoord + size, size*2) - size;
/*
int xx; int xx;
if (chunkCoord > 0) { if (chunkCoord > 0) {
xx = Math.floorMod(chunkCoord + size, size*2) - size; xx = Math.floorMod(chunkCoord + size, size*2) - size;
} else { } else {
xx = Math.floorMod(chunkCoord - size, -size*2) + size; 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); ChunkStore data = chunks.get(coords);
// Paste entities // Paste entities
data.bpEnts().forEach(e -> { data.bpEnts().forEach(e -> {
Location l = getLoc(world, e.relativeLoc().clone(), chunkX, chunkZ); Location l = getLoc(world, e.relativeLoc().clone(), chunkX, chunkZ);
if (limitedRegion.isInRegion(l)) { if (limitedRegion.isInRegion(l)) {
Entity ent = limitedRegion.spawnEntity(l, e.entity().getType()); Entity ent = limitedRegion.spawnEntity(l, e.entity().getType());
@ -61,10 +60,14 @@ public class BoxedBlockPopulator extends BlockPopulator {
}); });
// Fill chests // Fill chests
limitedRegion.getTileEntities().forEach(te -> { limitedRegion.getTileEntities().forEach(te -> {
for (ChestData cd : data.chests()) { int teX = BoxedChunkGenerator.repeatCalc(te.getX() >> 4);
Location chestLoc = getLoc(world, cd.relativeLoc().clone(), chunkX, chunkZ); int teZ = BoxedChunkGenerator.repeatCalc(te.getZ() >> 4);
if (limitedRegion.isInRegion(chestLoc) && te.getLocation().equals(chestLoc)) { if (teX == xx && teZ == zz) {
this.setBlockState(te, cd.chest()); 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 @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(); 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; return bpEnts;
} }
// Get the location in the chunk
private Vector getLocInChunk(Location l) { 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 @Override
protected List<ChestData> getChests(Chunk chunk) { protected List<ChestData> getTileEnts(Chunk chunk) {
// These won't be stored // These won't be stored
return null; 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( return StreamSupport.stream(
Spliterators.spliteratorUnknownSize(Bukkit.advancementIterator(), Spliterator.ORDERED), false) Spliterators.spliteratorUnknownSize(Bukkit.advancementIterator(), Spliterator.ORDERED), false)
.filter(a -> a.getKey().toString().equals(string)) .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()) { if (adv != null && !player.getAdvancementProgress(adv).isDone()) {
adv.getCriteria().forEach(player.getAdvancementProgress(adv)::awardCriteria); adv.getCriteria().forEach(player.getAdvancementProgress(adv)::awardCriteria);
} }
@ -199,6 +200,17 @@ public class AdvancementListener implements Listener {
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void onPlayerJoin(PlayerJoinEvent e) { 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()); User user = User.getInstance(e.getPlayer());
if (Util.sameWorld(addon.getOverWorld(), e.getPlayer().getWorld())) { if (Util.sameWorld(addon.getOverWorld(), e.getPlayer().getWorld())) {
// Set advancements to same as island // Set advancements to same as island

View File

@ -1,7 +1,9 @@
package world.bentobox.boxed.listeners; package world.bentobox.boxed.listeners;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
@ -25,10 +27,13 @@ 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_R3.CraftWorld; import org.bukkit.craftbukkit.v1_19_R3.CraftWorld;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType; import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
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.event.player.PlayerMoveEvent;
import org.bukkit.loot.LootTables; import org.bukkit.loot.LootTables;
import org.bukkit.structure.Structure; import org.bukkit.structure.Structure;
import org.bukkit.util.BoundingBox; 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.BentoBoxReadyEvent;
import world.bentobox.bentobox.api.events.island.IslandCreatedEvent; import world.bentobox.bentobox.api.events.island.IslandCreatedEvent;
import world.bentobox.bentobox.api.events.island.IslandResettedEvent; 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.database.objects.Island;
import world.bentobox.bentobox.util.Pair; import world.bentobox.bentobox.util.Pair;
import world.bentobox.bentobox.util.Util; import world.bentobox.bentobox.util.Util;
import world.bentobox.boxed.Boxed; import world.bentobox.boxed.Boxed;
import world.bentobox.boxed.objects.BoxedJigsawBlock; import world.bentobox.boxed.objects.BoxedJigsawBlock;
import world.bentobox.boxed.objects.BoxedStructureBlock; import world.bentobox.boxed.objects.BoxedStructureBlock;
import world.bentobox.boxed.objects.IslandStructures;
/** /**
* @author tastybento * @author tastybento
@ -59,15 +66,28 @@ import world.bentobox.boxed.objects.BoxedStructureBlock;
public class NewAreaListener implements Listener { 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<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 final Boxed addon;
private File structureFile; private File structureFile;
private Queue<Item> itemsToBuild = new LinkedList<>(); private Queue<Item> itemsToBuild = new LinkedList<>();
private static Random rand = new Random(); private static Random rand = new Random();
private boolean pasting; private boolean pasting;
private static Gson gson = new Gson(); 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> 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);
// 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); 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");
// Get database ready
handler = new Database<>(addon, IslandStructures.class);
// Try to build something every second // Try to build something every second
Bukkit.getScheduler().runTaskTimer(addon.getPlugin(), () -> BuildItem(), 20, 20); 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() { private void BuildItem() {
@ -96,7 +131,7 @@ public class NewAreaListener implements Listener {
* Build a list of structures * Build a list of structures
* @param event event * @param event event
*/ */
@EventHandler() @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void onBentoBoxReady(BentoBoxReadyEvent event) { public void onBentoBoxReady(BentoBoxReadyEvent event) {
addon.saveResource("templates.yml", false); addon.saveResource("templates.yml", false);
File templateFile = new File(addon.getDataFolder(), "templates.yml"); File templateFile = new File(addon.getDataFolder(), "templates.yml");
@ -104,15 +139,73 @@ public class NewAreaListener implements Listener {
YamlConfiguration loader = YamlConfiguration.loadConfiguration(templateFile); YamlConfiguration loader = YamlConfiguration.loadConfiguration(templateFile);
List<String> list = loader.getStringList("templates"); List<String> list = loader.getStringList("templates");
for (String struct : list) { for (String struct : list) {
Structure s = Bukkit.getStructureManager().loadStructure(NamespacedKey.fromString(struct)); if (!struct.endsWith("/")) {
if (s == null) { Bukkit.getStructureManager().loadStructure(NamespacedKey.fromString(struct));
//addon.log("Now loading group from: " + 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) @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void onIslandCreated(IslandCreatedEvent event) { public void onIslandCreated(IslandCreatedEvent event) {
setUpIsland(event.getIsland()); setUpIsland(event.getIsland());
@ -148,6 +241,7 @@ public class NewAreaListener implements Listener {
for (String vector : section.getKeys(false)) { for (String vector : section.getKeys(false)) {
StructureRotation rot = StructureRotation.NONE; StructureRotation rot = StructureRotation.NONE;
Mirror mirror = Mirror.NONE; Mirror mirror = Mirror.NONE;
boolean noMobs = false;
String name = section.getString(vector); String name = section.getString(vector);
// Check for rotation // Check for rotation
String[] split = name.split(","); String[] split = name.split(",");
@ -158,7 +252,10 @@ public class NewAreaListener implements Listener {
} }
if (split.length == 3) { if (split.length == 3) {
// Mirror // 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 // Load Structure
Structure s = Bukkit.getStructureManager().loadStructure(NamespacedKey.fromString("minecraft:" + name)); 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 y = Integer.valueOf(value[1].strip());
int z = Integer.valueOf(value[2].strip()) + center.getBlockZ(); int z = Integer.valueOf(value[2].strip()) + center.getBlockZ();
Location l = new Location(world, x, y, z); 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 { } else {
addon.logError("Structure file syntax error: " + vector + ": " + value); 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); 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())); addon.log(item.name() + " placed at " + item.location().getWorld().getName() + " " + Util.xyz(item.location().toVector()));
// Find it // 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; pasting = false;
} }
/** /**
* Removes Jigsaw blocks from a placed structure. Fills underwater ruins with water. * Removes Jigsaw blocks from a placed structure. Fills underwater ruins with water.
* @param loc - location where the structure was placed * @param item - record of what's required
* @param structure - structure that was placed * @return the resulting bounding box of the structure
* @param structureRotation - rotation of structure
* @param key
*/ */
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) { Location otherCorner = switch (structureRotation) {
case CLOCKWISE_180 -> loc.clone().add(new Vector(-structure.getSize().getX(), structure.getSize().getY(), -structure.getSize().getZ())); 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); Block b = loc.getWorld().getBlockAt(x, y, z);
if (b.getType().equals(Material.JIGSAW)) { if (b.getType().equals(Material.JIGSAW)) {
// I would like to read the data from the block and do something with it! // 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)) { } else if (b.getType().equals(Material.STRUCTURE_BLOCK)) {
processStructureBlock(b); 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 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); String data = nmsData(b);
BoxedJigsawBlock bjb = gson.fromJson(data, BoxedJigsawBlock.class); 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); String finalState = correctDirection(bjb.getFinal_state(), structureRotation);
//BentoBox.getInstance().logDebug("FinalState after rotation: " + finalState);
BlockData bd = Bukkit.createBlockData(finalState); BlockData bd = Bukkit.createBlockData(finalState);
b.setBlockData(bd); 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 = EntityType type =
switch (bjb.getPool()) { switch (bjb.getPool()) {
case "minecraft:bastion/mobs/piglin" -> EntityType.PIGLIN; 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)); case "minecraft:village/common/animals" -> BUTCHER_ANIMALS.get(rand.nextInt(3));
default -> null; 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")) { if (bjb.getPool().contains("zombie/villagers")) {
type = EntityType.ZOMBIE_VILLAGER; type = EntityType.ZOMBIE_VILLAGER;
} else if (bjb.getPool().contains("villagers")) { } else if (bjb.getPool().contains("villagers")) {
type = EntityType.VILLAGER; type = EntityType.VILLAGER;
} }
//if (type == null) {
// BentoBox.getInstance().logDebug(bjb.getPool());
//}
// Spawn it // 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()); //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/jungle_in': 0
'exploration/write_book': 0 'exploration/write_book': 0
'forestry/sapling' : 0 'forestry/sapling' : 0
 'adventure/adventuring_time': 13 'adventure/adventuring_time': 13
'adventure/arbalistic': 11 'adventure/arbalistic': 11
'adventure/avoid_vibration': 13 'adventure/avoid_vibration': 13
'adventure/bullseye': 5 'adventure/bullseye': 5

View File

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

View File

@ -1,3 +1,4 @@
# This file is written by the /boxadmin place command
normal: normal:
0,64,80: village/plains/houses/plains_masons_house_1,CLOCKWISE_90 0,64,80: village/plains/houses/plains_masons_house_1,CLOCKWISE_90
-38,63,20: ruined_portal/portal_5 -38,63,20: ruined_portal/portal_5
@ -15,6 +16,8 @@ normal:
34,69,-29: village/common/iron_golem 34,69,-29: village/common/iron_golem
41,69,-26: village/common/animals/cat_calico 41,69,-26: village/common/animals/cat_calico
35,69,-22: 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 99,72,118: pillager_outpost/feature_cage1
33,72,100: village/desert/houses/desert_farm_1 33,72,100: village/desert/houses/desert_farm_1
26,72,100: village/desert/houses/desert_medium_house_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 -52,72,33: village/snowy/houses/snowy_farm_1,COUNTERCLOCKWISE_90,NONE
-28,63,47: village/snowy/snowy_lamp_post_01,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 -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: nether:
16,32,0: bastion/bridge/starting_pieces/entrance 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: 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_connector
- ancient_city/city/entrance/entrance_path_1 - ancient_city/city/entrance/entrance_path_1
- ancient_city/city/entrance/entrance_path_2 - ancient_city/city/entrance/entrance_path_2