mirror of
https://github.com/BentoBoxWorld/Boxed.git
synced 2024-11-17 10:45:42 +01:00
commit
470f893bbf
9
.github/workflows/build.yml
vendored
9
.github/workflows/build.yml
vendored
@ -11,21 +11,22 @@ jobs:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v1
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: 'adopt'
|
||||
java-version: 17
|
||||
- name: Cache SonarCloud packages
|
||||
uses: actions/cache@v1
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.sonar/cache
|
||||
key: ${{ runner.os }}-sonar
|
||||
restore-keys: ${{ runner.os }}-sonar
|
||||
- name: Cache Maven packages
|
||||
uses: actions/cache@v1
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.m2
|
||||
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
|
||||
|
12
README.md
12
README.md
@ -4,14 +4,16 @@ A game mode where you are boxed into a tiny space that only expands by completin
|
||||
|
||||
## BentoBox Requirements
|
||||
|
||||
* Requires BentoBox 1.16.0 or later (Snapshots can be downloaded here: [https://ci.bentobox.world](https://ci.bentobox.world))
|
||||
* Requires BentoBox 1.23.0 or later (Snapshots can be downloaded here: [https://ci.bentobox.world](https://ci.bentobox.world))
|
||||
* InvSwitcher - keeps advancements, inventory, etc. separate between worlds on a server.
|
||||
* Border - shows the box
|
||||
|
||||
## Required Plugins
|
||||
|
||||
* Requires WorldGeneratorAPI plugin. [Download the correct one for your server here.](https://github.com/rutgerkok/WorldGeneratorApi/releases)
|
||||
* Border requires WorldBorderAPI by default. [Download it here.](https://github.com/yannicklamprecht/WorldBorderAPI/releases)
|
||||
### Warning!!
|
||||
Boxed requires **a lot of RAM** and can take up to **10 minutes** to boot up for the first time as it pre-generates the worlds. After the initial start, it will start up much quicker. With 12GB of RAM running on a fast ARM-based system, it takes ~ 8 minutes for the first boot. If you do not have enough RAM then weird things will happen to you server including strange errors about chunks and things like that. To dedicate enough RAM to your JVM, use the correct flags during startup. Here is my `start.sh` for running on Paper 1.19.4:
|
||||
```
|
||||
#!/bin/sh
|
||||
java -Xms12G -Xmx12G -XX:+UseG1GC -XX:+ParallelRefProcEnabled -XX:MaxGCPauseMillis=200 -XX:+UnlockExperimentalVMOptions -XX:+DisableExplicitGC -XX:+AlwaysPreTouch -XX:G1HeapWastePercent=5 -XX:G1MixedGCCountTarget=4 -XX:G1MixedGCLiveThresholdPercent=90 -XX:G1RSetUpdatingPauseTimePercent=5 -XX:SurvivorRatio=32 -XX:+PerfDisableSharedMem -XX:MaxTenuringThreshold=1 -XX:G1NewSizePercent=30 -XX:G1MaxNewSizePercent=40 -XX:G1HeapRegionSize=8M -XX:G1ReservePercent=20 -XX:InitiatingHeapOccupancyPercent=15 -Dusing.aikars.flags=https://mcflags.emc.gs -Daikars.new.flags=true -jar paper-1.19.4.jar nogui
|
||||
```
|
||||
|
||||
## How to install
|
||||
|
||||
|
9
pom.xml
9
pom.xml
@ -58,14 +58,14 @@
|
||||
<!-- Non-minecraft related dependencies -->
|
||||
<powermock.version>2.0.9</powermock.version>
|
||||
<!-- More visible way how to change dependency versions -->
|
||||
<spigot.version>1.19.4-R0.1-SNAPSHOT</spigot.version>
|
||||
<bentobox.version>1.23.0</bentobox.version>
|
||||
<spigot.version>1.20.1-R0.1-SNAPSHOT</spigot.version>
|
||||
<bentobox.version>1.24.0</bentobox.version>
|
||||
<!-- Revision variable removes warning about dynamic version -->
|
||||
<revision>${build.version}-SNAPSHOT</revision>
|
||||
<!-- 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.1.1</build.version>
|
||||
<build.version>2.3.0</build.version>
|
||||
|
||||
<sonar.projectKey>BentoBoxWorld_Boxed</sonar.projectKey>
|
||||
<sonar.organization>bentobox-world</sonar.organization>
|
||||
@ -254,6 +254,7 @@
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>3.0.0-M5</version>
|
||||
<!--suppress MavenModelInspection -->
|
||||
<configuration>
|
||||
<argLine>
|
||||
${argLine}
|
||||
@ -338,7 +339,7 @@
|
||||
<plugin>
|
||||
<groupId>org.jacoco</groupId>
|
||||
<artifactId>jacoco-maven-plugin</artifactId>
|
||||
<version>0.8.7</version>
|
||||
<version>0.8.10</version>
|
||||
<configuration>
|
||||
<append>true</append>
|
||||
<excludes>
|
||||
|
1
src/main/.gitignore
vendored
Normal file
1
src/main/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/.DS_Store
|
@ -20,7 +20,6 @@ import world.bentobox.bentobox.api.events.island.IslandEvent;
|
||||
import world.bentobox.bentobox.database.Database;
|
||||
import world.bentobox.bentobox.database.objects.Island;
|
||||
import world.bentobox.bentobox.managers.RanksManager;
|
||||
import world.bentobox.bentobox.util.Util;
|
||||
import world.bentobox.boxed.objects.IslandAdvancements;
|
||||
|
||||
/**
|
||||
@ -34,7 +33,7 @@ public class AdvancementsManager {
|
||||
// Database handler for level data
|
||||
private final Database<IslandAdvancements> handler;
|
||||
// A cache of island levels.
|
||||
private final Map<String, IslandAdvancements> cache;
|
||||
private final Map<String, IslandAdvancements> cache = new HashMap<>();
|
||||
private final YamlConfiguration advConfig;
|
||||
private int unknownAdvChange;
|
||||
private int unknownRecipeChange;
|
||||
@ -48,8 +47,6 @@ public class AdvancementsManager {
|
||||
// Set up the database handler to store and retrieve data
|
||||
// Note that these are saved by the BentoBox database
|
||||
handler = new Database<>(addon, IslandAdvancements.class);
|
||||
// Initialize the cache
|
||||
cache = new HashMap<>();
|
||||
// Advancement score sheet
|
||||
addon.saveResource("advancements.yml", false);
|
||||
advConfig = new YamlConfiguration();
|
||||
@ -185,7 +182,7 @@ public class AdvancementsManager {
|
||||
* @return score for advancement. 0 if the advancement was not added.
|
||||
*/
|
||||
public int addAdvancement(Player p, Advancement advancement) {
|
||||
if (!addon.getOverWorld().equals(Util.getWorld(p.getWorld()))) {
|
||||
if (!addon.inWorld(p.getWorld())) {
|
||||
// Wrong world
|
||||
return 0;
|
||||
}
|
||||
@ -240,7 +237,7 @@ public class AdvancementsManager {
|
||||
/**
|
||||
* Get the score for this advancement
|
||||
* @param a - advancement
|
||||
* @return score of advancement, or 0 if cannot be worked out
|
||||
* @return score of advancement, or 0 if it cannot be worked out
|
||||
*/
|
||||
public int getScore(Advancement a) {
|
||||
String adv = "advancements." + a.getKey().getKey();
|
||||
@ -255,8 +252,7 @@ public class AdvancementsManager {
|
||||
if (!a.getKey().getKey().contains("recipes") && a.getDisplay() != null) {
|
||||
float x = a.getDisplay().getX();
|
||||
float y = a.getDisplay().getY();
|
||||
int score = (int) Math.round(Math.sqrt(x * x + y * y));
|
||||
return score;
|
||||
return (int) Math.round(Math.sqrt(x * x + y * y));
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,7 +1,11 @@
|
||||
package world.bentobox.boxed;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Difficulty;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.World;
|
||||
@ -9,8 +13,8 @@ import org.bukkit.World.Environment;
|
||||
import org.bukkit.WorldCreator;
|
||||
import org.bukkit.entity.SpawnCategory;
|
||||
import org.bukkit.generator.BiomeProvider;
|
||||
import org.bukkit.generator.BlockPopulator;
|
||||
import org.bukkit.generator.ChunkGenerator;
|
||||
import org.eclipse.jdt.annotation.NonNull;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
import world.bentobox.bentobox.api.addons.GameModeAddon;
|
||||
@ -21,13 +25,14 @@ import world.bentobox.bentobox.api.configuration.WorldSettings;
|
||||
import world.bentobox.bentobox.api.flags.Flag;
|
||||
import world.bentobox.bentobox.api.flags.Flag.Mode;
|
||||
import world.bentobox.bentobox.api.flags.Flag.Type;
|
||||
import world.bentobox.bentobox.api.hooks.Hook;
|
||||
import world.bentobox.bentobox.hooks.WorldManagementHook;
|
||||
import world.bentobox.bentobox.managers.RanksManager;
|
||||
import world.bentobox.boxed.commands.AdminPlaceStructureCommand;
|
||||
import world.bentobox.boxed.generators.biomes.BoxedBiomeGenerator;
|
||||
import world.bentobox.boxed.generators.biomes.NetherSeedBiomeGenerator;
|
||||
import world.bentobox.boxed.generators.biomes.SeedBiomeGenerator;
|
||||
import world.bentobox.boxed.generators.chunks.AbstractBoxedChunkGenerator;
|
||||
import world.bentobox.boxed.generators.chunks.BoxedBlockPopulator;
|
||||
import world.bentobox.boxed.generators.chunks.BoxedChunkGenerator;
|
||||
import world.bentobox.boxed.generators.chunks.BoxedSeedChunkGenerator;
|
||||
import world.bentobox.boxed.listeners.AdvancementListener;
|
||||
@ -61,13 +66,10 @@ public class Boxed extends GameModeAddon {
|
||||
private final Config<Settings> configObject = new Config<>(this, Settings.class);
|
||||
private AdvancementsManager advManager;
|
||||
private AbstractBoxedChunkGenerator netherChunkGenerator;
|
||||
private World baseWorld;
|
||||
private World baseWorldNether;
|
||||
private World seedWorld;
|
||||
private World seedWorldNether;
|
||||
private final Map<World, ChunkGenerator> generatorMap = new HashMap<>();
|
||||
//private World seedWorldEnd;
|
||||
private BiomeProvider boxedBiomeProvider;
|
||||
private BlockPopulator boxedBlockPopulator;
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
@ -180,29 +182,32 @@ public class Boxed extends GameModeAddon {
|
||||
log("Creating Boxed Seed Nether world ...");
|
||||
// This creates a vanilla base world with biomes
|
||||
AbstractBoxedChunkGenerator seedBaseGen = new BoxedSeedChunkGenerator(this, Environment.NETHER);
|
||||
baseWorldNether = WorldCreator
|
||||
.name(worldName + "/" + SEED+NETHER+BASE)
|
||||
World baseWorldNether = WorldCreator
|
||||
.name(worldName + "/" + SEED + NETHER + BASE)
|
||||
.generator(seedBaseGen)
|
||||
.environment(Environment.NETHER)
|
||||
.seed(getSettings().getSeed())
|
||||
.createWorld();
|
||||
baseWorldNether.setDifficulty(Difficulty.PEACEFUL);
|
||||
baseWorldNether.setSpawnLocation(settings.getSeedX(), 64, settings.getSeedZ());
|
||||
generatorMap.put(baseWorldNether, seedBaseGen);
|
||||
getPlugin().getIWM().addWorld(baseWorldNether, this);
|
||||
copyChunks(baseWorldNether, seedBaseGen);
|
||||
// Create seed world
|
||||
// This copies a base world with custom biomes
|
||||
log("Creating Boxed Biomed Nether world ...");
|
||||
|
||||
seedWorldNether = WorldCreator
|
||||
.name(worldName + "/" + SEED+NETHER)
|
||||
.generator(new BoxedSeedChunkGenerator(this, Environment.NETHER, new NetherSeedBiomeGenerator(this, seedBaseGen)))
|
||||
BoxedSeedChunkGenerator seedWorldNetherGenerator = new BoxedSeedChunkGenerator(this, Environment.NETHER, new NetherSeedBiomeGenerator(this, seedBaseGen));
|
||||
World seedWorldNether = WorldCreator
|
||||
.name(worldName + "/" + SEED + NETHER)
|
||||
.generator(seedWorldNetherGenerator)
|
||||
.environment(Environment.NETHER)
|
||||
.seed(getSettings().getSeed())
|
||||
.createWorld();
|
||||
seedWorldNether.setDifficulty(Difficulty.EASY);
|
||||
|
||||
seedWorldNether.setSpawnLocation(settings.getNetherSeedX(), 64, settings.getNetherSeedZ());
|
||||
|
||||
generatorMap.put(seedWorldNether, seedWorldNetherGenerator);
|
||||
getPlugin().getIWM().addWorld(seedWorldNether, this);
|
||||
copyChunks(seedWorldNether, netherChunkGenerator);
|
||||
|
||||
if (getServer().getWorld(worldName + NETHER) == null) {
|
||||
@ -216,22 +221,24 @@ public class Boxed extends GameModeAddon {
|
||||
log("Creating Boxed Seed world ...");
|
||||
// This creates a vanilla base world with biomes
|
||||
AbstractBoxedChunkGenerator seedBaseGen = new BoxedSeedChunkGenerator(this, Environment.NORMAL);
|
||||
baseWorld = WorldCreator
|
||||
.name(worldName + "/" + SEED+BASE)
|
||||
World baseWorld = WorldCreator
|
||||
.name(worldName + "/" + SEED + BASE)
|
||||
.generator(seedBaseGen)
|
||||
.environment(Environment.NORMAL)
|
||||
.seed(getSettings().getSeed())
|
||||
.createWorld();
|
||||
baseWorld.setDifficulty(Difficulty.PEACEFUL);
|
||||
baseWorld.setSpawnLocation(settings.getSeedX(), 64, settings.getSeedZ());
|
||||
generatorMap.put(baseWorld, seedBaseGen);
|
||||
getPlugin().getIWM().addWorld(baseWorld, this);
|
||||
copyChunks(baseWorld, seedBaseGen);
|
||||
// Create seed world
|
||||
// This copies a base world with custom biomes
|
||||
log("Creating Boxed Biomed world ...");
|
||||
|
||||
BoxedSeedChunkGenerator seedWorldGenerator = new BoxedSeedChunkGenerator(this, Environment.NORMAL, new SeedBiomeGenerator(this, seedBaseGen));
|
||||
seedWorld = WorldCreator
|
||||
.name(worldName + "/" + SEED)
|
||||
.generator(new BoxedSeedChunkGenerator(this, Environment.NORMAL, new SeedBiomeGenerator(this, seedBaseGen)))
|
||||
.generator(seedWorldGenerator)
|
||||
.environment(Environment.NORMAL)
|
||||
.seed(getSettings().getSeed())
|
||||
.createWorld();
|
||||
@ -239,6 +246,8 @@ public class Boxed extends GameModeAddon {
|
||||
|
||||
seedWorld.setSpawnLocation(settings.getSeedX(), 64, settings.getSeedZ());
|
||||
|
||||
generatorMap.put(seedWorld, seedWorldGenerator);
|
||||
getPlugin().getIWM().addWorld(seedWorld, this);
|
||||
copyChunks(seedWorld, chunkGenerator);
|
||||
|
||||
|
||||
@ -251,9 +260,28 @@ public class Boxed extends GameModeAddon {
|
||||
islandWorld = getWorld(worldName, World.Environment.NORMAL);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a world with world management plugins
|
||||
*
|
||||
* @param world the World to register
|
||||
*/
|
||||
private void registerToWorldManagementPlugins(@NonNull World world) {
|
||||
if (getPlugin().getHooks() != null) {
|
||||
for (Hook hook : getPlugin().getHooks().getHooks()) {
|
||||
if (hook instanceof final WorldManagementHook worldManagementHook) {
|
||||
if (Bukkit.isPrimaryThread()) {
|
||||
worldManagementHook.registerWorld(world, true);
|
||||
} else {
|
||||
Bukkit.getScheduler().runTask(getPlugin(), () -> worldManagementHook.registerWorld(world, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies chunks from the seed world so they can be pasted in the game world
|
||||
* Copies chunks from the seed world, so they can be pasted in the game world
|
||||
* @param world - source world
|
||||
* @param gen - generator to store the chunks
|
||||
*/
|
||||
@ -307,10 +335,9 @@ public class Boxed extends GameModeAddon {
|
||||
worldName2 = env.equals(World.Environment.NETHER) ? worldName2 + NETHER : worldName2;
|
||||
worldName2 = env.equals(World.Environment.THE_END) ? worldName2 + THE_END : worldName2;
|
||||
boxedBiomeProvider = new BoxedBiomeGenerator(this);
|
||||
boxedBlockPopulator = new BoxedBlockPopulator(this);
|
||||
World w = WorldCreator
|
||||
.name(worldName2)
|
||||
.generator(env.equals(World.Environment.NETHER) ? netherChunkGenerator : chunkGenerator)
|
||||
.generator(getChunkGenerator(env))
|
||||
.environment(env)
|
||||
.seed(seedWorld.getSeed()) // For development
|
||||
.createWorld();
|
||||
@ -318,6 +345,8 @@ public class Boxed extends GameModeAddon {
|
||||
if (w != null) {
|
||||
setSpawnRates(w);
|
||||
}
|
||||
// Store main generators
|
||||
generatorMap.put(w, getChunkGenerator(env));
|
||||
return w;
|
||||
|
||||
}
|
||||
@ -357,7 +386,12 @@ public class Boxed extends GameModeAddon {
|
||||
|
||||
@Override
|
||||
public @Nullable ChunkGenerator getDefaultWorldGenerator(String worldName, String id) {
|
||||
return worldName.endsWith(NETHER) ? netherChunkGenerator : chunkGenerator;
|
||||
for (Entry<World, ChunkGenerator> en : generatorMap.entrySet()) {
|
||||
if (en.getKey().getName().equalsIgnoreCase(worldName)) {
|
||||
return en.getValue();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -374,6 +408,9 @@ public class Boxed extends GameModeAddon {
|
||||
public void allLoaded() {
|
||||
// Save settings. This will occur after all addons have loaded
|
||||
this.saveWorldSettings();
|
||||
// Register generators for worlds with multiverse etc.
|
||||
this.log("Registering Boxed worlds with other plugins (if applicable)...");
|
||||
generatorMap.keySet().forEach(this::registerToWorldManagementPlugins);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -383,13 +420,6 @@ public class Boxed extends GameModeAddon {
|
||||
return advManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the boxedBlockPopulator
|
||||
*/
|
||||
public BlockPopulator getBoxedBlockPopulator() {
|
||||
return boxedBlockPopulator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUsesNewChunkGeneration() {
|
||||
return true;
|
||||
|
@ -26,9 +26,10 @@ 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;
|
||||
import world.bentobox.boxed.listeners.NewAreaListener.StructureRecord;
|
||||
|
||||
/**
|
||||
* Enables admins to place templates in a Box and have them recorded for future boxes.
|
||||
* @author tastybento
|
||||
*
|
||||
*/
|
||||
@ -74,11 +75,11 @@ public class AdminPlaceStructureCommand extends CompositeCommand {
|
||||
// Check world
|
||||
if (!((Boxed)getAddon()).inWorld(getWorld())) {
|
||||
user.sendMessage("boxed.commands.boxadmin.place.wrong-world");
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
/*
|
||||
* Acceptable syntax with number of args:
|
||||
* 1. place <structure>
|
||||
* 1. place <structure>
|
||||
* 4. place <structure> ~ ~ ~
|
||||
* 5. place <structure> ~ ~ ~ ROTATION
|
||||
* 6. place <structure> ~ ~ ~ ROTATION MIRROR
|
||||
@ -90,7 +91,7 @@ public class AdminPlaceStructureCommand extends CompositeCommand {
|
||||
return false;
|
||||
}
|
||||
// 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(NamespacedKey::getKey).toList();
|
||||
if (!options.contains(args.get(0).toLowerCase(Locale.ENGLISH))) {
|
||||
user.sendMessage("boxed.commands.boxadmin.place.unknown-structure");
|
||||
return false;
|
||||
@ -101,10 +102,10 @@ public class AdminPlaceStructureCommand extends CompositeCommand {
|
||||
}
|
||||
// Next come the coordinates - there must be at least 3 of them
|
||||
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))) {
|
||||
user.sendMessage("boxed.commands.boxadmin.place.use-integers");
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
// If that is all we have, we're done
|
||||
if (args.size() == 4) {
|
||||
@ -115,7 +116,7 @@ public class AdminPlaceStructureCommand extends CompositeCommand {
|
||||
if (sr == null) {
|
||||
user.sendMessage("boxed.commands.boxadmin.place.unknown-rotation");
|
||||
Arrays.stream(StructureRotation.values()).map(StructureRotation::name).forEach(user::sendRawMessage);
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
if (args.size() == 5) {
|
||||
return true;
|
||||
@ -125,11 +126,11 @@ public class AdminPlaceStructureCommand extends CompositeCommand {
|
||||
if (mirror == null) {
|
||||
user.sendMessage("boxed.commands.boxadmin.place.unknown-mirror");
|
||||
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;
|
||||
noMobs = true;
|
||||
} else {
|
||||
user.sendMessage("boxed.commands.boxadmin.place.unknown", TextVariables.LABEL, args.get(6).toUpperCase(Locale.ENGLISH));
|
||||
return false;
|
||||
@ -143,13 +144,13 @@ public class AdminPlaceStructureCommand extends CompositeCommand {
|
||||
public boolean execute(User user, String label, List<String> args) {
|
||||
NamespacedKey tag = NamespacedKey.fromString(args.get(0).toLowerCase(Locale.ENGLISH));
|
||||
Structure s = Bukkit.getStructureManager().loadStructure(tag);
|
||||
int x = args.size() == 1 || args.get(1).equals("~") ? user.getLocation().getBlockX() : Integer.valueOf(args.get(1).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 x = args.size() == 1 || args.get(1).equals("~") ? user.getLocation().getBlockX() : Integer.parseInt(args.get(1).trim());
|
||||
int y = args.size() == 1 || args.get(2).equals("~") ? user.getLocation().getBlockY() : Integer.parseInt(args.get(2).trim());
|
||||
int z = args.size() == 1 || args.get(3).equals("~") ? user.getLocation().getBlockZ() : Integer.parseInt(args.get(3).trim());
|
||||
Location spot = new Location(user.getWorld(), x, y, z);
|
||||
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);
|
||||
NewAreaListener.removeJigsaw(new StructureRecord(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 {
|
||||
@ -167,14 +168,14 @@ public class AdminPlaceStructureCommand extends CompositeCommand {
|
||||
try {
|
||||
config.load(structures);
|
||||
StringBuilder v = new StringBuilder();
|
||||
v.append(tag.getKey() + "," + sr2.name() + "," + mirror2.name());
|
||||
v.append(tag.getKey()).append(",").append(sr2.name()).append(",").append(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;
|
||||
}
|
||||
@ -188,7 +189,7 @@ public class AdminPlaceStructureCommand extends CompositeCommand {
|
||||
{
|
||||
String lastArg = !args.isEmpty() ? args.get(args.size() - 1) : "";
|
||||
if (args.size() == 2) {
|
||||
return Optional.of(Util.tabLimit(Bukkit.getStructureManager().getStructures().keySet().stream().map(k -> k.getKey()).toList(), lastArg));
|
||||
return Optional.of(Util.tabLimit(Bukkit.getStructureManager().getStructures().keySet().stream().map(NamespacedKey::getKey).toList(), lastArg));
|
||||
} else if (args.size() == 3) {
|
||||
return Optional.of(List.of(String.valueOf(user.getLocation().getBlockX()), "~"));
|
||||
} else if (args.size() == 4) {
|
||||
|
@ -14,8 +14,8 @@ import com.google.common.base.Enums;
|
||||
|
||||
import world.bentobox.bentobox.BentoBox;
|
||||
import world.bentobox.boxed.Boxed;
|
||||
import world.bentobox.boxed.generators.chunks.AbstractBoxedChunkGenerator;
|
||||
import world.bentobox.boxed.generators.chunks.AbstractBoxedChunkGenerator.ChunkStore;
|
||||
import world.bentobox.boxed.generators.chunks.BoxedChunkGenerator;
|
||||
|
||||
/**
|
||||
* Copies biomes from seed world
|
||||
@ -39,15 +39,14 @@ public abstract class AbstractCopyBiomeProvider extends BiomeProvider {
|
||||
public Biome getBiome(WorldInfo worldInfo, int x, int y, int z) {
|
||||
int chunkX = x >> 4;
|
||||
int chunkZ = z >> 4;
|
||||
chunkX = BoxedChunkGenerator.repeatCalc(chunkX);
|
||||
chunkZ = BoxedChunkGenerator.repeatCalc(chunkZ);
|
||||
chunkX = AbstractBoxedChunkGenerator.repeatCalc(chunkX);
|
||||
chunkZ = AbstractBoxedChunkGenerator.repeatCalc(chunkZ);
|
||||
@Nullable ChunkStore c = addon.getChunkGenerator(worldInfo.getEnvironment()).getChunk(chunkX, chunkZ);
|
||||
|
||||
if (c != null) {
|
||||
int xx = Math.floorMod(x, 16);
|
||||
int zz = Math.floorMod(z, 16);
|
||||
Biome biome = c.chunkBiomes().getOrDefault(new Vector(xx, y, zz), defaultBiome);
|
||||
return biome;
|
||||
return c.chunkBiomes().getOrDefault(new Vector(xx, y, zz), defaultBiome);
|
||||
} else {
|
||||
BentoBox.getInstance().logWarning("Snapshot at " + chunkX + " " + chunkZ + " is not stored");
|
||||
return defaultBiome;
|
||||
|
@ -26,7 +26,6 @@ import com.google.common.base.Enums;
|
||||
import world.bentobox.boxed.Boxed;
|
||||
import world.bentobox.boxed.generators.chunks.AbstractBoxedChunkGenerator;
|
||||
import world.bentobox.boxed.generators.chunks.AbstractBoxedChunkGenerator.ChunkStore;
|
||||
import world.bentobox.boxed.generators.chunks.BoxedChunkGenerator;
|
||||
|
||||
/**
|
||||
* Generates the biomes for the seed world. A seed world is the template for the chunks that
|
||||
@ -115,8 +114,8 @@ public abstract class AbstractSeedBiomeProvider extends BiomeProvider {
|
||||
@NonNull
|
||||
private Biome getVanillaBiome(WorldInfo worldInfo, int x, int y, int z) {
|
||||
// Get the chunk coordinates
|
||||
int chunkX = BoxedChunkGenerator.repeatCalc(x >> 4);
|
||||
int chunkZ = BoxedChunkGenerator.repeatCalc(z >> 4);
|
||||
int chunkX = AbstractBoxedChunkGenerator.repeatCalc(x >> 4);
|
||||
int chunkZ = AbstractBoxedChunkGenerator.repeatCalc(z >> 4);
|
||||
// Get the stored snapshot
|
||||
ChunkStore snapshot = this.seedGen.getChunk(chunkX, chunkZ);
|
||||
if (snapshot == null) {
|
||||
@ -200,7 +199,7 @@ public abstract class AbstractSeedBiomeProvider extends BiomeProvider {
|
||||
* Loads the custom biomes from the config file
|
||||
* @param config - Yaml configuration object
|
||||
* @param sector - the direction section to load
|
||||
* @return
|
||||
* @return sorted map of the biomes and their probabilities as keys
|
||||
*/
|
||||
private SortedMap<Double, Biome> loadQuad(YamlConfiguration config, String sector) {
|
||||
SortedMap<Double, Biome> result = new TreeMap<>();
|
||||
|
@ -6,6 +6,7 @@ import org.bukkit.block.Biome;
|
||||
import world.bentobox.boxed.Boxed;
|
||||
|
||||
/**
|
||||
* Generator for the over world
|
||||
* @author tastybento
|
||||
*
|
||||
*/
|
||||
|
@ -6,6 +6,7 @@ import org.bukkit.block.Biome;
|
||||
import world.bentobox.boxed.Boxed;
|
||||
|
||||
/**
|
||||
* Generator for the nether world
|
||||
* @author tastybento
|
||||
*
|
||||
*/
|
||||
|
@ -7,6 +7,7 @@ import world.bentobox.boxed.Boxed;
|
||||
import world.bentobox.boxed.generators.chunks.AbstractBoxedChunkGenerator;
|
||||
|
||||
/**
|
||||
* Generator for the Nether seed world
|
||||
* @author tastybento
|
||||
*
|
||||
*/
|
||||
|
@ -7,6 +7,7 @@ import world.bentobox.boxed.Boxed;
|
||||
import world.bentobox.boxed.generators.chunks.AbstractBoxedChunkGenerator;
|
||||
|
||||
/**
|
||||
* Generator for the seed world
|
||||
* @author tastybento
|
||||
*
|
||||
*/
|
||||
|
@ -1 +1,5 @@
|
||||
/**
|
||||
* Generators to make the worlds with the custom biomes
|
||||
* @author tastybento
|
||||
*/
|
||||
package world.bentobox.boxed.generators.biomes;
|
@ -26,10 +26,12 @@ public abstract class AbstractBoxedChunkGenerator extends ChunkGenerator {
|
||||
|
||||
protected final Boxed addon;
|
||||
protected static int size;
|
||||
protected Map<Pair<Integer, Integer>, ChunkStore> chunks = new HashMap<>();
|
||||
public record ChunkStore(ChunkSnapshot snapshot, List<EntityData> bpEnts, List<ChestData> chests, Map<Vector, Biome> chunkBiomes) {};
|
||||
public record EntityData(Vector relativeLoc, BlueprintEntity entity) {};
|
||||
public record ChestData(Vector relativeLoc, BlueprintBlock chest) {};
|
||||
protected final Map<Pair<Integer, Integer>, ChunkStore> chunks = new HashMap<>();
|
||||
public record ChunkStore(ChunkSnapshot snapshot, List<EntityData> bpEnts, List<ChestData> chests, Map<Vector, Biome> chunkBiomes) {}
|
||||
|
||||
public record EntityData(Vector relativeLoc, BlueprintEntity entity) {}
|
||||
|
||||
public record ChestData(Vector relativeLoc, BlueprintBlock chest) {}
|
||||
|
||||
//private final WorldRef wordRefNether;
|
||||
|
||||
|
@ -31,10 +31,10 @@ import world.bentobox.boxed.generators.chunks.AbstractBoxedChunkGenerator.ChunkS
|
||||
*/
|
||||
public class BoxedBlockPopulator extends BlockPopulator {
|
||||
|
||||
private Boxed addon;
|
||||
private final Boxed addon;
|
||||
|
||||
/**
|
||||
* @param addon
|
||||
* @param addon Boxed
|
||||
*/
|
||||
public BoxedBlockPopulator(Boxed addon) {
|
||||
this.addon = addon;
|
||||
@ -82,7 +82,7 @@ public class BoxedBlockPopulator extends BlockPopulator {
|
||||
/**
|
||||
* Handles signs, chests and mob spawner blocks
|
||||
*
|
||||
* @param block - block
|
||||
* @param bs - block state
|
||||
* @param bpBlock - config
|
||||
*/
|
||||
public void setBlockState(BlockState bs, BlueprintBlock bpBlock) {
|
||||
|
@ -45,9 +45,12 @@ import world.bentobox.boxed.Boxed;
|
||||
*
|
||||
*/
|
||||
public class BoxedChunkGenerator extends AbstractBoxedChunkGenerator {
|
||||
|
||||
private final BlockPopulator boxedBlockPopulator;
|
||||
|
||||
public BoxedChunkGenerator(Boxed addon) {
|
||||
super(addon);
|
||||
boxedBlockPopulator = new BoxedBlockPopulator(addon);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -57,7 +60,10 @@ public class BoxedChunkGenerator extends AbstractBoxedChunkGenerator {
|
||||
|
||||
@Override
|
||||
public List<BlockPopulator> getDefaultPopulators(World world) {
|
||||
world.getPopulators().add(addon.getBoxedBlockPopulator());
|
||||
// Only add it once
|
||||
if (!world.getPopulators().contains(boxedBlockPopulator)) {
|
||||
world.getPopulators().add(boxedBlockPopulator);
|
||||
}
|
||||
return world.getPopulators();
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
/**
|
||||
* Generators and populators to make the base world
|
||||
* @author tastybento
|
||||
*
|
||||
*/
|
||||
|
@ -36,7 +36,6 @@ import world.bentobox.bentobox.api.events.team.TeamLeaveEvent;
|
||||
import world.bentobox.bentobox.api.localization.TextVariables;
|
||||
import world.bentobox.bentobox.api.user.User;
|
||||
import world.bentobox.bentobox.database.objects.Island;
|
||||
import world.bentobox.bentobox.managers.RanksManager;
|
||||
import world.bentobox.bentobox.util.Util;
|
||||
import world.bentobox.boxed.Boxed;
|
||||
|
||||
@ -66,22 +65,31 @@ public class AdvancementListener implements Listener {
|
||||
}
|
||||
|
||||
|
||||
public static Advancement getAdvancement(String string) {
|
||||
/**
|
||||
* Get Advancement given the namespaced key for it
|
||||
* @param key namespaced key name for Advancement
|
||||
* @return Advancement or null if none found
|
||||
*/
|
||||
public static Advancement getAdvancement(String key) {
|
||||
return StreamSupport.stream(
|
||||
Spliterators.spliteratorUnknownSize(Bukkit.advancementIterator(), Spliterator.ORDERED), false)
|
||||
.filter(a -> a.getKey().toString().equals(string))
|
||||
.filter(a -> a.getKey().toString().equals(key))
|
||||
.findFirst().orElse(null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Awards a bigger box when an advancement is done. Removes advancements if they are not valid.
|
||||
* @param e PlayerAdvancementDoneEvent
|
||||
*/
|
||||
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
|
||||
public void onAdvancement(PlayerAdvancementDoneEvent e) {
|
||||
// Ignore if player is not in survival
|
||||
if (!e.getPlayer().getGameMode().equals(GameMode.SURVIVAL)) {
|
||||
return;
|
||||
}
|
||||
if (Util.sameWorld(e.getPlayer().getWorld(), addon.getOverWorld())) {
|
||||
|
||||
// Check if player is in the Boxed worlds
|
||||
if (addon.inWorld(e.getPlayer().getWorld())) {
|
||||
// Only allow members or higher to get advancements in a box
|
||||
if (addon.getSettings().isDenyVisitorAdvancements() && !addon.getIslands().getIslandAt(e.getPlayer().getLocation()).map(i -> i.getMemberSet().contains(e.getPlayer().getUniqueId())).orElse(false)) {
|
||||
// Remove advancement from player
|
||||
@ -93,13 +101,12 @@ public class AdvancementListener implements Listener {
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Add the advancement to the island
|
||||
int score = addon.getAdvManager().addAdvancement(e.getPlayer(), e.getAdvancement());
|
||||
// Tell other team players one tick after it occurs if it is something that has a score
|
||||
if (score != 0) {
|
||||
User user = User.getInstance(e.getPlayer());
|
||||
if (user != null) {
|
||||
Bukkit.getScheduler().runTask(addon.getPlugin(), () -> tellTeam(user, e.getAdvancement().getKey(), score));
|
||||
}
|
||||
Bukkit.getScheduler().runTask(addon.getPlugin(), () -> tellTeam(user, e.getAdvancement().getKey(), score));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -107,11 +114,10 @@ public class AdvancementListener implements Listener {
|
||||
private void tellTeam(User user, NamespacedKey key, int score) {
|
||||
Island island = addon.getIslands().getIsland(addon.getOverWorld(), user);
|
||||
if (island == null) {
|
||||
// Something went wrong here
|
||||
return;
|
||||
}
|
||||
island.getMemberSet(RanksManager.MEMBER_RANK).stream()
|
||||
.map(User::getInstance)
|
||||
.filter(User::isOnline)
|
||||
island.getMemberSet().stream().map(User::getInstance).filter(User::isOnline)
|
||||
.forEach(u -> {
|
||||
informPlayer(u, key, score);
|
||||
// Sync
|
||||
@ -126,15 +132,15 @@ public class AdvancementListener implements Listener {
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronize the player's advancements to that of the island.
|
||||
* Player's advancements should be cleared before calling this otherwise they will get add the island ones as well.
|
||||
* Synchronize the player's advancements to that of the box.
|
||||
* Player's advancements should be cleared before calling this otherwise they will get add the box ones as well.
|
||||
* @param user - user
|
||||
*/
|
||||
public void syncAdvancements(User user) {
|
||||
Island island = addon.getIslands().getIsland(addon.getOverWorld(), user);
|
||||
if (island != null) {
|
||||
grantAdv(user, addon.getAdvManager().getIsland(island).getAdvancements());
|
||||
int diff = addon.getAdvManager().checkIslandSize(island);
|
||||
Island box = addon.getIslands().getIsland(addon.getOverWorld(), user);
|
||||
if (box != null) {
|
||||
grantAdv(user, addon.getAdvManager().getIsland(box).getAdvancements());
|
||||
int diff = addon.getAdvManager().checkIslandSize(box);
|
||||
if (diff > 0) {
|
||||
user.sendMessage("boxed.size-changed", TextVariables.NUMBER, String.valueOf(diff));
|
||||
user.getPlayer().playSound(Objects.requireNonNull(user.getLocation()), Sound.ENTITY_PLAYER_LEVELUP, 1F, 2F);
|
||||
@ -161,9 +167,14 @@ public class AdvancementListener implements Listener {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Special case Advancement awarding
|
||||
* Awards the nether and end advancements when they use a portal for the first time.
|
||||
* @param e PlayerPortalEvent
|
||||
*/
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
public void onPortal(PlayerPortalEvent e) {
|
||||
if (!Util.sameWorld(e.getPlayer().getWorld(), addon.getOverWorld()) || !e.getPlayer().getGameMode().equals(GameMode.SURVIVAL)) {
|
||||
if (!addon.inWorld(e.getPlayer().getWorld()) || !e.getPlayer().getGameMode().equals(GameMode.SURVIVAL)) {
|
||||
return;
|
||||
}
|
||||
if (e.getCause().equals(TeleportCause.NETHER_PORTAL)) {
|
||||
@ -177,47 +188,50 @@ public class AdvancementListener implements Listener {
|
||||
}
|
||||
|
||||
/**
|
||||
* Special case Advancement awarding
|
||||
* Looks for certain blocks, and if they are found then awards an advancement
|
||||
* @param e - PlayerMoveEvent
|
||||
*/
|
||||
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
|
||||
public void onMove(PlayerMoveEvent e) {
|
||||
if (!Util.sameWorld(e.getPlayer().getWorld(), addon.getNetherWorld())) {
|
||||
if (!addon.getSettings().isNetherGenerate() || !Util.sameWorld(e.getPlayer().getWorld(), addon.getNetherWorld())) {
|
||||
return;
|
||||
}
|
||||
// Nether fortress advancement
|
||||
if (e.getTo().getBlock().getRelative(BlockFace.DOWN).getType().equals(Material.NETHER_BRICKS)) {
|
||||
giveAdv(e.getPlayer(), netherFortressAdvancement);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Give player an advancement
|
||||
* @param player - player
|
||||
* @param adv - Advancement
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync advancements when player joins server if they are in the Boxed world
|
||||
* @param e PlayerJoinEvent
|
||||
*/
|
||||
@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())) {
|
||||
if (addon.inWorld(e.getPlayer().getWorld())) {
|
||||
// Set advancements to same as island
|
||||
syncAdvancements(user);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync advancements when player enters the Boxed world
|
||||
* @param e PlayerChangedWorldEvent
|
||||
*/
|
||||
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
|
||||
public void onPlayerEnterWorld(PlayerChangedWorldEvent e) {
|
||||
User user = User.getInstance(e.getPlayer());
|
||||
@ -227,6 +241,10 @@ public class AdvancementListener implements Listener {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear and sync advancements for a player when they join a team if the settings require it
|
||||
* @param e TeamJoinedEvent
|
||||
*/
|
||||
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
|
||||
public void onTeamJoinTime(TeamJoinedEvent e) {
|
||||
User user = User.getInstance(e.getPlayerUUID());
|
||||
@ -239,6 +257,10 @@ public class AdvancementListener implements Listener {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear player's advancements when they leave a team if the setting requires it
|
||||
* @param e TeamLeaveEvent
|
||||
*/
|
||||
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
|
||||
public void onTeamLeaveTime(TeamLeaveEvent e) {
|
||||
User user = User.getInstance(e.getPlayerUUID());
|
||||
@ -250,6 +272,10 @@ public class AdvancementListener implements Listener {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear player's advancements when they start an island for the first time.
|
||||
* @param e IslandNewIslandEvent
|
||||
*/
|
||||
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
|
||||
public void onFirstTime(IslandNewIslandEvent e) {
|
||||
User user = User.getInstance(e.getPlayerUUID());
|
||||
@ -259,6 +285,12 @@ public class AdvancementListener implements Listener {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Clear and set advancements for user. Will not do anything if the user is offline
|
||||
* @param user - user
|
||||
* @param clear - whether to clear advacements for this user or not
|
||||
* @param list - list of advacements (namespaced keys) to grant to user
|
||||
*/
|
||||
private void clearAndSetAdv(User user, boolean clear, List<String> list) {
|
||||
if (!user.isOnline()) {
|
||||
return;
|
||||
@ -288,10 +320,8 @@ public class AdvancementListener implements Listener {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void clearAdv(User user) {
|
||||
// Clear stats
|
||||
// Statistics
|
||||
// Clear Statistics
|
||||
Arrays.stream(Statistic.values()).forEach(s -> resetStats(user, s));
|
||||
// Clear advancements
|
||||
Iterator<Advancement> it = Bukkit.advancementIterator();
|
||||
@ -303,36 +333,12 @@ public class AdvancementListener implements Listener {
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private void resetStats(User user, Statistic s) {
|
||||
switch(s.getType()) {
|
||||
case BLOCK:
|
||||
for (Material m: Material.values()) {
|
||||
if (m.isBlock() && !m.isLegacy()) {
|
||||
user.getPlayer().setStatistic(s, m, 0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ITEM:
|
||||
for (Material m: Material.values()) {
|
||||
if (m.isItem() && !m.isLegacy()) {
|
||||
user.getPlayer().setStatistic(s, m, 0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ENTITY:
|
||||
for (EntityType en: EntityType.values()) {
|
||||
if (en.isAlive()) {
|
||||
user.getPlayer().setStatistic(s, en, 0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case UNTYPED:
|
||||
user.getPlayer().setStatistic(s, 0);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
case BLOCK -> Arrays.stream(Material.values()).filter(Material::isBlock).forEach(m -> user.getPlayer().setStatistic(s, m, 0));
|
||||
case ITEM -> Arrays.stream(Material.values()).filter(Material::isItem).forEach(m -> user.getPlayer().setStatistic(s, m, 0));
|
||||
case ENTITY -> Arrays.stream(EntityType.values()).filter(EntityType::isAlive).forEach(m -> user.getPlayer().setStatistic(s, m, 0));
|
||||
case UNTYPED -> user.getPlayer().setStatistic(s, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -47,13 +47,12 @@ public class EnderPearlListener implements Listener {
|
||||
}
|
||||
|
||||
User u = User.getInstance(e.getPlayer());
|
||||
// If the to is outside the box, cancel it
|
||||
// If the to-location is outside the box, cancel it
|
||||
if (e.getTo() != null) {
|
||||
Island i = addon.getIslands().getIsland(e.getFrom().getWorld(), u);
|
||||
if (i == null || !i.onIsland(e.getTo())) {
|
||||
u.sendMessage("boxed.general.errors.no-teleport-outside");
|
||||
e.setCancelled(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -92,19 +91,16 @@ public class EnderPearlListener implements Listener {
|
||||
// Moving is allowed
|
||||
moveBox(u, fromIsland, l);
|
||||
Util.teleportAsync(player, l);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// Different box. This is never allowed. Cancel the throw
|
||||
e.setCancelled(true);
|
||||
u.sendMessage("boxed.general.errors.no-teleport-outside");
|
||||
return;
|
||||
}
|
||||
}, () -> {
|
||||
// No box. This is never allowed. Cancel the throw
|
||||
e.setCancelled(true);
|
||||
u.sendMessage("boxed.general.errors.no-teleport-outside");
|
||||
return;
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -3,13 +3,7 @@ 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;
|
||||
import java.util.Map;
|
||||
import java.util.Queue;
|
||||
import java.util.Random;
|
||||
import java.util.*;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
@ -26,7 +20,7 @@ import org.bukkit.block.structure.Mirror;
|
||||
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.craftbukkit.v1_20_R1.CraftWorld;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.entity.Player;
|
||||
@ -65,6 +59,19 @@ import world.bentobox.boxed.objects.IslandStructures;
|
||||
*/
|
||||
public class NewAreaListener implements Listener {
|
||||
|
||||
/**
|
||||
* Structure record contains the name of the structure, the structure itself, where it was placed and
|
||||
* enums for rotation, mirror, and a flag to paste mobs or not.
|
||||
* @param name - name of structure
|
||||
* @param structure - Structure object
|
||||
* @param location - location where it has been placed
|
||||
* @param rot - rotation
|
||||
* @param mirror - mirror setting
|
||||
* @param noMobs - if false, mobs not pasted
|
||||
*/
|
||||
public record StructureRecord(String name, Structure structure, Location location, StructureRotation rot, Mirror mirror, Boolean noMobs) {}
|
||||
|
||||
private static final Map<Integer, EntityType> BUTCHER_ANIMALS = Map.of(0, EntityType.COW, 1, EntityType.SHEEP, 2, EntityType.PIG);
|
||||
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",
|
||||
@ -76,17 +83,16 @@ public class NewAreaListener implements Listener {
|
||||
"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 final File structureFile;
|
||||
private final Queue<StructureRecord> itemsToBuild = new LinkedList<>();
|
||||
private static final Random rand = new Random();
|
||||
private boolean pasting;
|
||||
private static Gson gson = new Gson();
|
||||
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);
|
||||
private static final Gson gson = new Gson();
|
||||
Pair<Integer, Integer> min = new Pair<>(0, 0);
|
||||
Pair<Integer, Integer> max = new Pair<>(0, 0);
|
||||
// Database handler for structure data
|
||||
private final Database<IslandStructures> handler;
|
||||
private Map<String, IslandStructures> islandStructureCache = new HashMap<>();
|
||||
private final Map<String, IslandStructures> islandStructureCache = new HashMap<>();
|
||||
|
||||
|
||||
|
||||
@ -95,14 +101,18 @@ public class NewAreaListener implements Listener {
|
||||
*/
|
||||
public NewAreaListener(Boxed addon) {
|
||||
this.addon = addon;
|
||||
// Save the default structures file from the jar
|
||||
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
|
||||
runStructurePrinter(addon);
|
||||
}
|
||||
|
||||
private void runStructurePrinter(Boxed addon2) {
|
||||
Bukkit.getScheduler().runTaskTimer(addon.getPlugin(), this::buildStructure, 20, 20);
|
||||
for (String js : JAR_STRUCTURES) {
|
||||
addon.saveResource("structures/" + js + ".nbt", false);
|
||||
File structureFile = new File(addon.getDataFolder(), "structures/" + js + ".nbt");
|
||||
@ -112,24 +122,29 @@ public class NewAreaListener implements Listener {
|
||||
addon.log("Loaded " + js + ".nbt");
|
||||
} catch (IOException e) {
|
||||
addon.logError("Error trying to load " + structureFile.getAbsolutePath());
|
||||
e.printStackTrace();
|
||||
addon.getPlugin().logStacktrace(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void BuildItem() {
|
||||
/**
|
||||
* Build something in the queue
|
||||
*/
|
||||
private void buildStructure() {
|
||||
// Only kick off a build if there is something to build and something isn't already being built
|
||||
if (!pasting && !itemsToBuild.isEmpty()) {
|
||||
// Build item
|
||||
Item item = itemsToBuild.poll();
|
||||
LoadChunksAsync(item);
|
||||
StructureRecord item = itemsToBuild.poll();
|
||||
placeStructure(item);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a list of structures
|
||||
* @param event event
|
||||
* Load known structures from the templates file.
|
||||
* This makes them available for admins to use in the boxed place file.
|
||||
* If this is not done, then no structures (templates) will be available.
|
||||
* @param event BentoBoxReadyEvent
|
||||
*/
|
||||
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
|
||||
public void onBentoBoxReady(BentoBoxReadyEvent event) {
|
||||
@ -147,6 +162,10 @@ public class NewAreaListener implements Listener {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Track if a place has entered a structure.
|
||||
* @param e PlayerMoveEvent
|
||||
*/
|
||||
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
|
||||
public void onPlayerMove(PlayerMoveEvent e) {
|
||||
// Ignore head movements
|
||||
@ -195,6 +214,11 @@ public class NewAreaListener implements Listener {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the known island structures for this island
|
||||
* @param islandId - island ID
|
||||
* @return IslandStructures
|
||||
*/
|
||||
private IslandStructures getIslandStructData(String islandId) {
|
||||
// Return from cache if it exists
|
||||
if (islandStructureCache.containsKey(islandId)) {
|
||||
@ -229,13 +253,13 @@ public class NewAreaListener implements Listener {
|
||||
if (e == null) {
|
||||
addon.logError("Error in structures.yml - unknown environment " + env);
|
||||
} else {
|
||||
place("structure",config.getConfigurationSection(env), center, e);
|
||||
place(config.getConfigurationSection(env), center, e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void place(String string, ConfigurationSection section, Location center, Environment env) {
|
||||
private void place(ConfigurationSection section, Location center, Environment env) {
|
||||
World world = env.equals(Environment.NORMAL) ? addon.getOverWorld() : addon.getNetherWorld();
|
||||
// Loop through the structures in the file - there could be more than one
|
||||
for (String vector : section.getKeys(false)) {
|
||||
@ -266,24 +290,26 @@ public class NewAreaListener implements Listener {
|
||||
// Extract coords
|
||||
String[] value = vector.split(",");
|
||||
if (value.length > 2) {
|
||||
int x = Integer.valueOf(value[0].strip()) + center.getBlockX();
|
||||
int y = Integer.valueOf(value[1].strip());
|
||||
int z = Integer.valueOf(value[2].strip()) + center.getBlockZ();
|
||||
int x = Integer.parseInt(value[0].strip()) + center.getBlockX();
|
||||
int y = Integer.parseInt(value[1].strip());
|
||||
int z = Integer.parseInt(value[2].strip()) + center.getBlockZ();
|
||||
Location l = new Location(world, x, y, z);
|
||||
itemsToBuild.add(new Item(name, s, l, rot, mirror, noMobs));
|
||||
itemsToBuild.add(new StructureRecord(name, s, l, rot, mirror, noMobs));
|
||||
} else {
|
||||
addon.logError("Structure file syntax error: " + vector + ": " + value);
|
||||
addon.logError("Structure file syntax error: " + vector + ": " + Arrays.toString(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadChunksAsync(Item item) {
|
||||
private void placeStructure(StructureRecord item) {
|
||||
// Set the semaphore - only paste one at a time
|
||||
pasting = true;
|
||||
// Place the structure
|
||||
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
|
||||
// Remove any jigsaw artifacts
|
||||
BoundingBox bb = removeJigsaw(item);
|
||||
// Store it
|
||||
// Store it for future reference
|
||||
addon.getIslands().getIslandAt(item.location()).map(Island::getUniqueId).ifPresent(id -> {
|
||||
addon.log("Saved " + item.name());
|
||||
if (item.location().getWorld().getEnvironment().equals(Environment.NETHER)) {
|
||||
@ -293,7 +319,7 @@ public class NewAreaListener implements Listener {
|
||||
}
|
||||
handler.saveObjectAsync(getIslandStructData(id));
|
||||
});
|
||||
|
||||
// Clear the semaphore
|
||||
pasting = false;
|
||||
}
|
||||
|
||||
@ -302,7 +328,7 @@ public class NewAreaListener implements Listener {
|
||||
* @param item - record of what's required
|
||||
* @return the resulting bounding box of the structure
|
||||
*/
|
||||
public static BoundingBox removeJigsaw(Item item) {
|
||||
public static BoundingBox removeJigsaw(StructureRecord item) {
|
||||
Location loc = item.location();
|
||||
Structure structure = item.structure();
|
||||
StructureRotation structureRotation = item.rot();
|
||||
@ -342,8 +368,15 @@ public class NewAreaListener implements Listener {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Process a structure block. Sets it to a structure void at a minimum.
|
||||
* If the structure block has metadata indicating it is a chest, then it will fill
|
||||
* the chest with a buried treasure loot. If it is waterlogged, then it will change
|
||||
* the void to water.
|
||||
* @param b structure block
|
||||
*/
|
||||
private static void processStructureBlock(Block b) {
|
||||
// I would like to read the data from the block an do something with it!
|
||||
// I would like to read the data from the block and do something with it!
|
||||
String data = nmsData(b);
|
||||
BoxedStructureBlock bsb = gson.fromJson(data, BoxedStructureBlock.class);
|
||||
b.setType(Material.STRUCTURE_VOID);
|
||||
@ -365,7 +398,6 @@ 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, boolean pasteMobs) {
|
||||
String data = nmsData(b);
|
||||
BoxedJigsawBlock bjb = gson.fromJson(data, BoxedJigsawBlock.class);
|
||||
@ -390,9 +422,8 @@ public class NewAreaListener implements Listener {
|
||||
case "minecraft:village/common/pigs" -> EntityType.PIG;
|
||||
case "minecraft:village/common/cows" -> EntityType.COW;
|
||||
case "minecraft:village/common/iron_golem" -> EntityType.IRON_GOLEM;
|
||||
case "minecraft:village/common/butcher_animals" -> BUTCHER_ANIMALS.get(rand.nextInt(3));
|
||||
case "minecraft:village/common/animals" -> BUTCHER_ANIMALS.get(rand.nextInt(3));
|
||||
default -> null;
|
||||
case "minecraft:village/common/butcher_animals", "minecraft:village/common/animals" -> BUTCHER_ANIMALS.get(rand.nextInt(3));
|
||||
default -> null;
|
||||
};
|
||||
// Boxed
|
||||
if (type == null && bjb.getPool().startsWith("minecraft:boxed/")) {
|
||||
|
@ -2,8 +2,13 @@ package world.bentobox.boxed.objects;
|
||||
|
||||
import com.google.gson.annotations.Expose;
|
||||
|
||||
/**
|
||||
* This replicates a jigsaw block.
|
||||
* @author tastybento
|
||||
*
|
||||
*/
|
||||
public class BoxedJigsawBlock {
|
||||
// final_state:"minecraft:polished_blackstone_bricks",joint:"aligned",name:"minecraft:empty",pool:"minecraft:bastion/bridge/legs",target:"minecraft:leg_connector"
|
||||
// final_state:"minecraft:polished_blackstone_bricks",joint:"aligned",name:"minecraft:empty",pool:"minecraft:bastion/bridge/legs",target:"minecraft:leg_connector"
|
||||
@Expose
|
||||
private String final_state;
|
||||
@Expose
|
||||
|
@ -7,11 +7,12 @@ import org.bukkit.block.structure.StructureRotation;
|
||||
import com.google.gson.annotations.Expose;
|
||||
|
||||
/**
|
||||
* This represents a Structure Block
|
||||
* @author tastybento
|
||||
*
|
||||
*/
|
||||
public class BoxedStructureBlock {
|
||||
//{author:"LadyAgnes",ignoreEntities:1b,integrity:1.0f,metadata:"drowned",mirror:"NONE",mode:"DATA",name:"",posX:0,posY:1,posZ:0,powered:0b,rotation:"NONE",seed:0L,showair:0b
|
||||
//{author:"LadyAgnes",ignoreEntities:1b,integrity:1.0f,metadata:"drowned",mirror:"NONE",mode:"DATA",name:"",posX:0,posY:1,posZ:0,powered:0b,rotation:"NONE",seed:0L,showair:0b
|
||||
//,showboundingbox:1b,sizeX:0,sizeY:0,sizeZ:0}
|
||||
@Expose
|
||||
private String author;
|
||||
@ -168,7 +169,7 @@ public class BoxedStructureBlock {
|
||||
+ seed + ", showair=" + showair + ", showboundingbox=" + showboundingbox + ", sizeX=" + sizeX
|
||||
+ ", sizeY=" + sizeY + ", sizeZ=" + sizeZ + "]";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
@ -10,6 +10,13 @@ import com.google.gson.annotations.Expose;
|
||||
import world.bentobox.bentobox.database.objects.DataObject;
|
||||
import world.bentobox.bentobox.database.objects.Table;
|
||||
|
||||
/**
|
||||
* Stores all the structures placed in the box when it is made.
|
||||
* These are used later to identify when a player enters such a structure and
|
||||
* trigger an Advancement
|
||||
* @author tastybento
|
||||
*
|
||||
*/
|
||||
@Table(name = "IslandStructures")
|
||||
public class IslandStructures implements DataObject {
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
name: Boxed
|
||||
main: world.bentobox.boxed.Boxed
|
||||
version: ${version}${build.number}
|
||||
api-version: 1.22
|
||||
api-version: 1.24
|
||||
metrics: true
|
||||
icon: "COMPOSTER"
|
||||
repository: "BentoBoxWorld/Boxed"
|
||||
|
@ -9,11 +9,11 @@ boxed:
|
||||
admin: boxadmin
|
||||
# The default action for new player command call.
|
||||
# Sub-command of main player command that will be run on first player command call.
|
||||
# By default it is sub-command 'create'.
|
||||
# By default, it is sub-command 'create'.
|
||||
new-player-action: create
|
||||
# The default action for player command.
|
||||
# Sub-command of main player command that will be run on each player command call.
|
||||
# By default it is sub-command 'go'.
|
||||
# By default, it is sub-command 'go'.
|
||||
default-action: go
|
||||
# Announce advancements. We recommend you set the game rule `/gamerule announceAdvancements false`
|
||||
# but that blocks all new advancement announcements. This setting tells Boxed to broadcast new advancements.
|
||||
|
339
src/test/java/world/bentobox/boxed/AdvancementsManagerTest.java
Normal file
339
src/test/java/world/bentobox/boxed/AdvancementsManagerTest.java
Normal file
@ -0,0 +1,339 @@
|
||||
package world.bentobox.boxed;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.beans.IntrospectionException;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Comparator;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.advancement.Advancement;
|
||||
import org.bukkit.advancement.AdvancementDisplay;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.eclipse.jdt.annotation.NonNull;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.powermock.api.mockito.PowerMockito;
|
||||
import org.powermock.core.classloader.annotations.PrepareForTest;
|
||||
import org.powermock.modules.junit4.PowerMockRunner;
|
||||
import org.powermock.reflect.Whitebox;
|
||||
|
||||
import world.bentobox.bentobox.BentoBox;
|
||||
import world.bentobox.bentobox.Settings;
|
||||
import world.bentobox.bentobox.api.user.User;
|
||||
import world.bentobox.bentobox.database.AbstractDatabaseHandler;
|
||||
import world.bentobox.bentobox.database.DatabaseSetup;
|
||||
import world.bentobox.bentobox.database.DatabaseSetup.DatabaseType;
|
||||
import world.bentobox.bentobox.database.objects.Island;
|
||||
import world.bentobox.bentobox.managers.IslandsManager;
|
||||
import world.bentobox.bentobox.managers.RanksManager;
|
||||
import world.bentobox.bentobox.util.Util;
|
||||
import world.bentobox.boxed.objects.IslandAdvancements;
|
||||
|
||||
/**
|
||||
* @author tastybento
|
||||
*
|
||||
*/
|
||||
@RunWith(PowerMockRunner.class)
|
||||
@PrepareForTest({Bukkit.class, BentoBox.class, DatabaseSetup.class, Util.class})
|
||||
public class AdvancementsManagerTest {
|
||||
|
||||
private static AbstractDatabaseHandler<Object> h;
|
||||
@Mock
|
||||
private BentoBox plugin;
|
||||
@Mock
|
||||
private Settings pluginSettings;
|
||||
|
||||
|
||||
@Mock
|
||||
private Boxed addon;
|
||||
private AdvancementsManager am;
|
||||
private File dataFolder;
|
||||
@Mock
|
||||
private Island island;
|
||||
@Mock
|
||||
private Player player;
|
||||
@Mock
|
||||
private Advancement advancement;
|
||||
@Mock
|
||||
private World world;
|
||||
@Mock
|
||||
private IslandsManager im;
|
||||
@Mock
|
||||
private AdvancementDisplay display;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@BeforeClass
|
||||
public static void beforeClass() throws IllegalAccessException, InvocationTargetException, IntrospectionException {
|
||||
// This has to be done beforeClass otherwise the tests will interfere with each other
|
||||
h = mock(AbstractDatabaseHandler.class);
|
||||
// Database
|
||||
PowerMockito.mockStatic(DatabaseSetup.class);
|
||||
DatabaseSetup dbSetup = mock(DatabaseSetup.class);
|
||||
when(DatabaseSetup.getDatabase()).thenReturn(dbSetup);
|
||||
when(dbSetup.getHandler(any())).thenReturn(h);
|
||||
when(h.saveObject(any())).thenReturn(CompletableFuture.completedFuture(true));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @throws java.lang.Exception
|
||||
*/
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
when(addon.getPlugin()).thenReturn(plugin);
|
||||
// Set up plugin
|
||||
Whitebox.setInternalState(BentoBox.class, "instance", plugin);
|
||||
|
||||
// The database type has to be created one line before the thenReturn() to work!
|
||||
DatabaseType value = DatabaseType.JSON;
|
||||
when(plugin.getSettings()).thenReturn(pluginSettings);
|
||||
when(pluginSettings.getDatabaseType()).thenReturn(value);
|
||||
// Addon
|
||||
dataFolder = new File("dataFolder");
|
||||
dataFolder.mkdirs();
|
||||
when(addon.getDataFolder()).thenReturn(dataFolder);
|
||||
Files.copy(Path.of("src/main/resources/advancements.yml"), Path.of("dataFolder/advancements.yml"));
|
||||
when(addon.inWorld(world)).thenReturn(true);
|
||||
when(addon.getOverWorld()).thenReturn(world);
|
||||
|
||||
// Island
|
||||
when(island.getUniqueId()).thenReturn("uniqueId");
|
||||
|
||||
// Player
|
||||
when(player.getWorld()).thenReturn(world);
|
||||
UUID uuid = UUID.randomUUID();
|
||||
when(player.getUniqueId()).thenReturn(uuid);
|
||||
|
||||
|
||||
NamespacedKey key = NamespacedKey.fromString("adventure/honey_block_slide");
|
||||
// Advancement
|
||||
when(advancement.getKey()).thenReturn(key);
|
||||
when(display.getX()).thenReturn(9F);
|
||||
when(display.getY()).thenReturn(0F);
|
||||
when(advancement.getDisplay()).thenReturn(display);
|
||||
|
||||
// Bukkit
|
||||
PowerMockito.mockStatic(Bukkit.class, Mockito.RETURNS_MOCKS);
|
||||
when(Bukkit.getAdvancement(any(NamespacedKey.class))).thenReturn(advancement);
|
||||
|
||||
// Island
|
||||
when(addon.getIslands()).thenReturn(im);
|
||||
when(im.getIsland(world, uuid)).thenReturn(island);
|
||||
when(island.getRank(uuid)).thenReturn(RanksManager.MEMBER_RANK);
|
||||
when(island.getProtectionRange()).thenReturn(5);
|
||||
|
||||
|
||||
am = new AdvancementsManager(addon);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws java.lang.Exception - exception
|
||||
*/
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
deleteAll(new File("database"));
|
||||
deleteAll(dataFolder);
|
||||
User.clearUsers();
|
||||
Mockito.framework().clearInlineMocks();
|
||||
}
|
||||
|
||||
private static void deleteAll(File file) throws IOException {
|
||||
if (file.exists()) {
|
||||
Files.walk(file.toPath())
|
||||
.sorted(Comparator.reverseOrder())
|
||||
.map(Path::toFile)
|
||||
.forEach(File::delete);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.boxed.AdvancementsManager#AdvancementsManager(world.bentobox.boxed.Boxed)}.
|
||||
* @throws Exception
|
||||
*/
|
||||
@Test
|
||||
public void testAdvancementsManagerNoFile() throws Exception {
|
||||
tearDown();
|
||||
am = new AdvancementsManager(addon);
|
||||
verify(addon).logError("advancements.yml cannot be found!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.boxed.AdvancementsManager#AdvancementsManager(world.bentobox.boxed.Boxed)}.
|
||||
* @throws IOException
|
||||
*/
|
||||
@Test
|
||||
public void testAdvancementsManager() throws IOException {
|
||||
verify(addon).saveResource("advancements.yml", false);
|
||||
verify(addon, never()).logError(anyString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.boxed.AdvancementsManager#getIsland(world.bentobox.bentobox.database.objects.Island)}.
|
||||
*/
|
||||
@Test
|
||||
public void testGetIsland() {
|
||||
@NonNull
|
||||
IslandAdvancements adv = am.getIsland(island);
|
||||
assertEquals("uniqueId", adv.getUniqueId());
|
||||
assertTrue(adv.getAdvancements().isEmpty());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.boxed.AdvancementsManager#saveIsland(world.bentobox.bentobox.database.objects.Island)}.
|
||||
* @throws IntrospectionException
|
||||
* @throws InvocationTargetException
|
||||
* @throws IllegalAccessException
|
||||
*/
|
||||
@Test
|
||||
public void testSaveIslandNotInCache() throws IllegalAccessException, InvocationTargetException, IntrospectionException {
|
||||
am.removeFromCache(island);
|
||||
am.saveIsland(island);
|
||||
verify(island, times(2)).getUniqueId(); // 2x
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.boxed.AdvancementsManager#saveIsland(world.bentobox.bentobox.database.objects.Island)}.
|
||||
* @throws IntrospectionException
|
||||
* @throws InvocationTargetException
|
||||
* @throws IllegalAccessException
|
||||
*/
|
||||
@Test
|
||||
public void testSaveIslandInCache() throws IllegalAccessException, InvocationTargetException, IntrospectionException {
|
||||
testGetIsland();
|
||||
am.saveIsland(island);
|
||||
verify(island, times(3)).getUniqueId(); // 3x
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.boxed.AdvancementsManager#save()}.
|
||||
* @throws IntrospectionException
|
||||
* @throws InvocationTargetException
|
||||
* @throws IllegalAccessException
|
||||
*/
|
||||
@Test
|
||||
public void testSaveNothingToSave() throws IllegalAccessException, InvocationTargetException, IntrospectionException {
|
||||
am.removeFromCache(island);
|
||||
am.save();
|
||||
verify(island).getUniqueId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.boxed.AdvancementsManager#save()}.
|
||||
* @throws IntrospectionException
|
||||
* @throws InvocationTargetException
|
||||
* @throws IllegalAccessException
|
||||
*/
|
||||
@Test
|
||||
public void testSave() throws IllegalAccessException, InvocationTargetException, IntrospectionException {
|
||||
testGetIsland();
|
||||
am.save();
|
||||
verify(island).getUniqueId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.boxed.AdvancementsManager#addAdvancement(world.bentobox.bentobox.database.objects.Island, java.lang.String)}.
|
||||
*/
|
||||
@Test
|
||||
public void testAddAdvancementIslandString() {
|
||||
assertTrue(am.addAdvancement(island, "advancement"));
|
||||
assertFalse(am.addAdvancement(island, "advancement")); // Second time should fail
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.boxed.AdvancementsManager#removeAdvancement(world.bentobox.bentobox.database.objects.Island, java.lang.String)}.
|
||||
*/
|
||||
@Test
|
||||
public void testRemoveAdvancement() {
|
||||
assertTrue(am.addAdvancement(island, "advancement"));
|
||||
am.removeAdvancement(island, "advancement");
|
||||
assertTrue(am.addAdvancement(island, "advancement")); // Should work because it was removed
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.boxed.AdvancementsManager#hasAdvancement(world.bentobox.bentobox.database.objects.Island, java.lang.String)}.
|
||||
*/
|
||||
@Test
|
||||
public void testHasAdvancement() {
|
||||
assertFalse(am.hasAdvancement(island, "advancement"));
|
||||
am.addAdvancement(island, "advancement");
|
||||
assertTrue(am.hasAdvancement(island, "advancement"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.boxed.AdvancementsManager#checkIslandSize(world.bentobox.bentobox.database.objects.Island)}.
|
||||
*/
|
||||
@Test
|
||||
public void testCheckIslandSize() {
|
||||
// Island protection size is set to 5, but after checking, the size is reduced by 4
|
||||
assertEquals(-4, am.checkIslandSize(island));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.boxed.AdvancementsManager#addAdvancement(org.bukkit.entity.Player, org.bukkit.advancement.Advancement)}.
|
||||
*/
|
||||
@Test
|
||||
public void testAddAdvancementPlayerAdvancementWrongWorld() {
|
||||
when(addon.inWorld(world)).thenReturn(false);
|
||||
assertEquals(0, am.addAdvancement(player, advancement));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.boxed.AdvancementsManager#addAdvancement(org.bukkit.entity.Player, org.bukkit.advancement.Advancement)}.
|
||||
*/
|
||||
@Test
|
||||
public void testAddAdvancementPlayerAdvancement() {
|
||||
assertEquals(9, am.addAdvancement(player, advancement));
|
||||
verify(island).setProtectionRange(14); // (9 + 5)
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.boxed.AdvancementsManager#addAdvancement(org.bukkit.entity.Player, org.bukkit.advancement.Advancement)}.
|
||||
*/
|
||||
@Test
|
||||
public void testAddAdvancementPlayerAdvancementZeroScore() {
|
||||
when(display.getX()).thenReturn(0F);
|
||||
assertEquals(0, am.addAdvancement(player, advancement));
|
||||
verify(island, never()).setProtectionRange(anyInt());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.boxed.AdvancementsManager#getScore(java.lang.String)}.
|
||||
*/
|
||||
@Test
|
||||
public void testGetScoreString() {
|
||||
assertEquals(9, am.getScore("adventure/lightning_rod_with_villager_no_fire"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link world.bentobox.boxed.AdvancementsManager#getScore(org.bukkit.advancement.Advancement)}.
|
||||
*/
|
||||
@Test
|
||||
public void testGetScoreAdvancement() {
|
||||
assertEquals(9, am.getScore(advancement));
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user