Merge changes from development branch.

This commit is contained in:
BONNe 2019-02-03 17:16:52 +02:00
commit 7ed07f48f4
15 changed files with 14697 additions and 3206 deletions

1
.gitignore vendored
View File

@ -21,3 +21,4 @@
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid* hs_err_pid*
/target/

View File

@ -13,6 +13,8 @@ import world.bentobox.bentobox.api.configuration.WorldSettings;
import world.bentobox.caveblock.commands.AdminCommand; import world.bentobox.caveblock.commands.AdminCommand;
import world.bentobox.caveblock.commands.IslandCommand; import world.bentobox.caveblock.commands.IslandCommand;
import world.bentobox.caveblock.generators.ChunkGeneratorWorld; import world.bentobox.caveblock.generators.ChunkGeneratorWorld;
import world.bentobox.caveblock.listeners.BeaconEnabler;
import world.bentobox.caveblock.listeners.CustomHeightLimitations;
public class CaveBlock extends GameModeAddon public class CaveBlock extends GameModeAddon
@ -50,6 +52,7 @@ public class CaveBlock extends GameModeAddon
{ {
super.onReload(); super.onReload();
this.loadSettings(); this.loadSettings();
this.chunkGenerator.reload();
} }
@ -107,7 +110,7 @@ public class CaveBlock extends GameModeAddon
this.islandWorld = WorldCreator.name(worldName). this.islandWorld = WorldCreator.name(worldName).
type(WorldType.FLAT). type(WorldType.FLAT).
environment(World.Environment.NORMAL). environment(World.Environment.NORMAL).
generator(new ChunkGeneratorWorld(this)). generator(this.chunkGenerator).
createWorld(); createWorld();
@ -131,7 +134,7 @@ public class CaveBlock extends GameModeAddon
{ {
this.netherWorld = WorldCreator.name(worldName + NETHER). this.netherWorld = WorldCreator.name(worldName + NETHER).
type(WorldType.FLAT). type(WorldType.FLAT).
generator(new ChunkGeneratorWorld(this)). generator(this.chunkGenerator).
environment(World.Environment.NETHER). environment(World.Environment.NETHER).
createWorld(); createWorld();
} }
@ -155,11 +158,14 @@ public class CaveBlock extends GameModeAddon
{ {
this.endWorld = WorldCreator.name(worldName + THE_END). this.endWorld = WorldCreator.name(worldName + THE_END).
type(WorldType.FLAT). type(WorldType.FLAT).
generator(new ChunkGeneratorWorld(this)). generator(this.chunkGenerator).
environment(World.Environment.THE_END). environment(World.Environment.THE_END).
createWorld(); createWorld();
} }
} }
this.getServer().getPluginManager().registerEvents(new CustomHeightLimitations(this), this.getPlugin());
this.getServer().getPluginManager().registerEvents(new BeaconEnabler(this), this.getPlugin());
} }

View File

@ -1,12 +1,18 @@
package world.bentobox.caveblock; package world.bentobox.caveblock;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.bukkit.Difficulty; import org.bukkit.Difficulty;
import org.bukkit.GameMode; import org.bukkit.GameMode;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.block.Biome; import org.bukkit.block.Biome;
import org.bukkit.entity.EntityType; import org.bukkit.entity.EntityType;
import java.util.*;
import world.bentobox.bentobox.api.configuration.ConfigComment; import world.bentobox.bentobox.api.configuration.ConfigComment;
import world.bentobox.bentobox.api.configuration.ConfigEntry; import world.bentobox.bentobox.api.configuration.ConfigEntry;
@ -26,8 +32,8 @@ import world.bentobox.bentobox.database.objects.adapters.FlagSerializer2;
@StoreAt(filename="config.yml", path="addons/CaveBlock") @StoreAt(filename="config.yml", path="addons/CaveBlock")
@ConfigComment("CaveBlock Configuration [version]") @ConfigComment("CaveBlock Configuration [version]")
@ConfigComment("This config file is dynamic and saved when the server is shutdown.") @ConfigComment("This config file is dynamic and saved when the server is shutdown.")
@ConfigComment("You cannot edit it while the server is running because changes will") @ConfigComment("If you edit it while the server is running use /cbadmin reload")
@ConfigComment("be lost! Use in-game settings GUI or edit when server is offline.") @ConfigComment("otherwise your settings will be lost.")
public class Settings implements DataObject, WorldSettings public class Settings implements DataObject, WorldSettings
{ {
// --------------------------------------------------------------------- // ---------------------------------------------------------------------
@ -173,7 +179,7 @@ public class Settings implements DataObject, WorldSettings
@Override @Override
public int getSeaHeight() public int getSeaHeight()
{ {
return seaHeight; return 0;
} }
@ -791,6 +797,37 @@ public class Settings implements DataObject, WorldSettings
return numberOfBlockGenerationTries; return numberOfBlockGenerationTries;
} }
/**
* This method returns the skyWalking object.
* @return the skyWalking object.
*/
public boolean isSkyWalking()
{
return skyWalking;
}
/**
* This method returns the alternativeTeleports object.
* @return the alternativeTeleports object.
*/
public boolean isAlternativeTeleports()
{
return alternativeTeleports;
}
/**
* This method returns the beaconAllowed object.
* @return the beaconAllowed object.
*/
public boolean isBeaconAllowed()
{
return beaconAllowed;
}
// --------------------------------------------------------------------- // ---------------------------------------------------------------------
// Section: Setters // Section: Setters
// --------------------------------------------------------------------- // ---------------------------------------------------------------------
@ -927,18 +964,6 @@ public class Settings implements DataObject, WorldSettings
this.useOwnGenerator = useOwnGenerator; this.useOwnGenerator = useOwnGenerator;
} }
/**
* This method sets the seaHeight object value.
* @param seaHeight the seaHeight object new value.
*
*/
public void setSeaHeight(int seaHeight)
{
this.seaHeight = seaHeight;
}
/** /**
* This method sets the maxIslands object value. * This method sets the maxIslands object value.
* @param maxIslands the maxIslands object new value. * @param maxIslands the maxIslands object new value.
@ -1556,11 +1581,58 @@ public class Settings implements DataObject, WorldSettings
} }
/**
* This method sets the skyWalking object value.
* @param skyWalking the skyWalking object new value.
*
*/
public void setSkyWalking(boolean skyWalking)
{
this.skyWalking = skyWalking;
}
/**
* This method sets the alternativeTeleports object value.
* @param alternativeTeleports the alternativeTeleports object new value.
*
*/
public void setAlternativeTeleports(boolean alternativeTeleports)
{
this.alternativeTeleports = alternativeTeleports;
}
/**
* This method sets the beaconAllowed object value.
* @param beaconAllowed the beaconAllowed object new value.
*
*/
public void setBeaconAllowed(boolean beaconAllowed)
{
this.beaconAllowed = beaconAllowed;
}
/**
* @return the debug
*/
public boolean isDebug() {
return debug;
}
/**
* @param debug the debug to set
*/
public void setDebug(boolean debug) {
this.debug = debug;
}
// --------------------------------------------------------------------- // ---------------------------------------------------------------------
// Section: Variables // Section: Variables
// --------------------------------------------------------------------- // ---------------------------------------------------------------------
/* WORLD */ /* WORLD */
@ConfigComment("Friendly name for this world. Used in admin commands. Must be a single word") @ConfigComment("Friendly name for this world. Used in admin commands. Must be a single word")
@ConfigEntry(path = "world.friendly-name") @ConfigEntry(path = "world.friendly-name")
@ -1576,11 +1648,11 @@ public class Settings implements DataObject, WorldSettings
@ConfigEntry(path = "world.difficulty") @ConfigEntry(path = "world.difficulty")
private Difficulty difficulty = Difficulty.NORMAL; private Difficulty difficulty = Difficulty.NORMAL;
@ConfigComment("Radius of island in blocks. (So distance between islands is twice this)") @ConfigComment("Radius of cave in blocks. (So distance between caves is twice this)")
@ConfigComment("Will be rounded up to the nearest 16 blocks.") @ConfigComment("Will be rounded up to the nearest 16 blocks.")
@ConfigComment("It is the same for every dimension : Overworld, Nether and End.") @ConfigComment("It is the same for every dimension : Overworld, Nether and End.")
@ConfigComment("This value cannot be changed mid-game and the plugin will not start if it is different.") @ConfigComment("This value cannot be changed mid-game and the plugin will not start if it is different.")
@ConfigEntry(path = "world.distance-between-islands", needsReset = true) @ConfigEntry(path = "world.distance-between-caves", needsReset = true)
private int islandDistance = 100; private int islandDistance = 100;
@ConfigComment("Default protection range radius in blocks. Cannot be larger than distance.") @ConfigComment("Default protection range radius in blocks. Cannot be larger than distance.")
@ -1605,9 +1677,9 @@ public class Settings implements DataObject, WorldSettings
@ConfigEntry(path = "world.offset-z") @ConfigEntry(path = "world.offset-z")
private int islandZOffset; private int islandZOffset;
@ConfigComment("Island height - Lowest is 5.") @ConfigComment("Cave height - Lowest is 5.")
@ConfigComment("It is the y coordinate of the bedrock block in the schem.") @ConfigComment("It is the y coordinate of the bedrock block in the schem.")
@ConfigEntry(path = "world.island-height") @ConfigEntry(path = "world.cave-height")
private int islandHeight = 60; private int islandHeight = 60;
@ConfigComment("Use your own world generator for this world.") @ConfigComment("Use your own world generator for this world.")
@ -1615,13 +1687,6 @@ public class Settings implements DataObject, WorldSettings
@ConfigEntry(path = "world.use-own-generator", experimental = true) @ConfigEntry(path = "world.use-own-generator", experimental = true)
private boolean useOwnGenerator = true; private boolean useOwnGenerator = true;
@ConfigComment("Sea height (don't changes this mid-game unless you delete the world)")
@ConfigComment("Minimum is 0, which means you are playing CaveBlock!")
@ConfigComment("If sea height is less than about 10, then players will drop right through it")
@ConfigComment("if it exists. Makes for an interesting variation on caveblock.")
@ConfigEntry(path = "world.sea-height", needsReset = true)
private int seaHeight = 0;
@ConfigComment("Maximum number of islands in the world. Set to -1 or 0 for unlimited.") @ConfigComment("Maximum number of islands in the world. Set to -1 or 0 for unlimited.")
@ConfigComment("If the number of islands is greater than this number, it will stop players from creating islands.") @ConfigComment("If the number of islands is greater than this number, it will stop players from creating islands.")
@ConfigEntry(path = "world.max-islands") @ConfigEntry(path = "world.max-islands")
@ -1652,6 +1717,26 @@ public class Settings implements DataObject, WorldSettings
@ConfigEntry(path = "world.generation-tries", needsReset = true) @ConfigEntry(path = "world.generation-tries", needsReset = true)
private int numberOfBlockGenerationTries = 1; private int numberOfBlockGenerationTries = 1;
@ConfigComment("")
@ConfigComment("Allows to walk over the world roof.")
@ConfigEntry(path = "world.sky-walking")
private boolean skyWalking;
@ConfigComment("Enables different ways how to get to other worlds.")
@ConfigComment("If players fall into void, then they will be teleported:")
@ConfigComment(" - to nether if falls into void from over world")
@ConfigComment(" - to the end if falls into void from nether")
@ConfigComment(" - to over world if falls into void from the end")
@ConfigEntry(path = "world.alternative-teleports")
private boolean alternativeTeleports;
@ConfigComment("Enables ability to use beacon, if world roof is made of Bedrock. It will replace")
@ConfigComment("bedrock with black stained glass and on beacon placing, and replace it with bedrock if")
@ConfigComment("beacon is destroyed.")
@ConfigComment("This will not do anything, if roof is not made of bedrock.")
@ConfigEntry(path = "world.allow-beacon")
private boolean beaconAllowed;
@ConfigComment("") @ConfigComment("")
@ConfigComment("Make over world roof of bedrock, if false, it will be made from stone") @ConfigComment("Make over world roof of bedrock, if false, it will be made from stone")
@ConfigEntry(path = "world.normal.roof", needsReset = true) @ConfigEntry(path = "world.normal.roof", needsReset = true)
@ -1672,7 +1757,7 @@ public class Settings implements DataObject, WorldSettings
@ConfigComment("Entities spawned via generator are not protected from despawing.") @ConfigComment("Entities spawned via generator are not protected from despawing.")
@ConfigComment("Working only with 2 high mobs currently.") @ConfigComment("Working only with 2 high mobs currently.")
@ConfigComment("Example:") @ConfigComment("Example:")
@ConfigComment("MATERIAL:DIAMOND:100:5 - means there is 100% chace of spawing diamonds") @ConfigComment("MATERIAL:DIAMOND_ORE:100:5 - means there is 100% chace of spawing diamonds")
@ConfigComment("where max amount in pack are 5 per each subchunk!") @ConfigComment("where max amount in pack are 5 per each subchunk!")
@ConfigEntry(path = "world.normal.blocks", needsReset = true) @ConfigEntry(path = "world.normal.blocks", needsReset = true)
private List<String> normalBlocks = new ArrayList<>(); private List<String> normalBlocks = new ArrayList<>();
@ -1721,7 +1806,7 @@ public class Settings implements DataObject, WorldSettings
@ConfigComment("Entities spawned via generator are not protected from despawing.") @ConfigComment("Entities spawned via generator are not protected from despawing.")
@ConfigComment("Working only with 2 high mobs currently.") @ConfigComment("Working only with 2 high mobs currently.")
@ConfigComment("Example:") @ConfigComment("Example:")
@ConfigComment("MATERIAL:DIAMOND:100:5 - means there is 100% chace of spawing diamonds") @ConfigComment("MATERIAL:DIAMOND_ORE:100:5 - means there is 100% chace of spawing diamonds")
@ConfigComment("where max amount in pack are 5 per each subchunk!") @ConfigComment("where max amount in pack are 5 per each subchunk!")
@ConfigEntry(path = "world.nether.blocks", needsReset = true) @ConfigEntry(path = "world.nether.blocks", needsReset = true)
private List<String> netherBlocks = new ArrayList<>(); private List<String> netherBlocks = new ArrayList<>();
@ -1755,7 +1840,7 @@ public class Settings implements DataObject, WorldSettings
@ConfigComment("Entities spawned via generator are not protected from despawing.") @ConfigComment("Entities spawned via generator are not protected from despawing.")
@ConfigComment("Working only with 2 high mobs currently.") @ConfigComment("Working only with 2 high mobs currently.")
@ConfigComment("Example:") @ConfigComment("Example:")
@ConfigComment("MATERIAL:DIAMOND:100:5 - means there is 100% chace of spawing diamonds") @ConfigComment("MATERIAL:DIAMOND_ORE:100:5 - means there is 100% chace of spawing diamonds")
@ConfigComment("where max amount in pack are 5 per each subchunk!") @ConfigComment("where max amount in pack are 5 per each subchunk!")
@ConfigEntry(path = "world.end.blocks", needsReset = true) @ConfigEntry(path = "world.end.blocks", needsReset = true)
private List<String> endBlocks = new ArrayList<>(); private List<String> endBlocks = new ArrayList<>();
@ -1918,6 +2003,7 @@ public class Settings implements DataObject, WorldSettings
@ConfigComment("These settings should not be edited") @ConfigComment("These settings should not be edited")
@ConfigEntry(path = "do-not-edit-these-settings.reset-epoch") @ConfigEntry(path = "do-not-edit-these-settings.reset-epoch")
private long resetEpoch = 0; private long resetEpoch = 0;
private boolean debug;
private String uniqueId = "config"; private String uniqueId = "config";
} }

View File

@ -1,11 +1,14 @@
package world.bentobox.caveblock.generators; package world.bentobox.caveblock.generators;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.generator.BlockPopulator; import org.bukkit.generator.BlockPopulator;
import org.bukkit.generator.ChunkGenerator; import org.bukkit.generator.ChunkGenerator;
import java.util.*;
import world.bentobox.caveblock.CaveBlock; import world.bentobox.caveblock.CaveBlock;
import world.bentobox.caveblock.Settings; import world.bentobox.caveblock.Settings;
@ -35,10 +38,7 @@ public class ChunkGeneratorWorld extends ChunkGenerator
this.addon = addon; this.addon = addon;
this.settings = addon.getSettings(); this.settings = addon.getSettings();
this.blockPopulators = new ArrayList<>(1); reload();
this.blockPopulators.add(new MaterialPopulator(this.addon));
this.blockPopulators.add(new EntitiesPopulator(this.addon));
} }
@ -187,6 +187,16 @@ public class ChunkGeneratorWorld extends ChunkGenerator
return this.blockPopulators; return this.blockPopulators;
} }
/**
* Called when config is reloaded
*/
public void reload() {
this.blockPopulators = new ArrayList<>(2);
this.blockPopulators.add(new MaterialPopulator(this.addon));
this.blockPopulators.add(new EntitiesPopulator(this.addon));
}
// --------------------------------------------------------------------- // ---------------------------------------------------------------------
// Section: Variables // Section: Variables
@ -207,4 +217,6 @@ public class ChunkGeneratorWorld extends ChunkGenerator
* This list contains block populators that will be applied after chunk is generated. * This list contains block populators that will be applied after chunk is generated.
*/ */
private List<BlockPopulator> blockPopulators; private List<BlockPopulator> blockPopulators;
} }

View File

@ -1,25 +1,35 @@
package world.bentobox.caveblock.generators.populators; package world.bentobox.caveblock.generators.populators;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.stream.Collectors;
import org.bukkit.Chunk; import org.bukkit.Chunk;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.World.Environment;
import org.bukkit.block.Block; import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType; import org.bukkit.entity.EntityType;
import org.bukkit.entity.LivingEntity;
import org.bukkit.generator.BlockPopulator; import org.bukkit.generator.BlockPopulator;
import java.util.*; import org.bukkit.util.BoundingBox;
import java.util.stream.Collectors;
import world.bentobox.bentobox.util.Pair; import world.bentobox.bentobox.util.Pair;
import world.bentobox.caveblock.CaveBlock; import world.bentobox.caveblock.CaveBlock;
import world.bentobox.caveblock.Settings;
/** /**
* This class populates generated chunk with enitites by random chance. * This class populates generated chunk with entites by random chance.
*/ */
public class EntitiesPopulator extends BlockPopulator public class EntitiesPopulator extends BlockPopulator
{ {
/** /**
* This is default constructor * This is default constructor
* @param addon CaveBlock addon. * @param addon CaveBlock addon.
@ -27,7 +37,21 @@ public class EntitiesPopulator extends BlockPopulator
public EntitiesPopulator(CaveBlock addon) public EntitiesPopulator(CaveBlock addon)
{ {
this.addon = addon; this.addon = addon;
this.settings = addon.getSettings(); loadSettings();
}
public void loadSettings() {
// Set up chances
chances = new HashMap<>();
// Normal
chances.put(Environment.NORMAL, new Chances(this.getEntityMap(addon.getSettings().getNormalBlocks()), addon.getSettings().getNormalMainBlock()));
// Nether
chances.put(Environment.NETHER, new Chances(this.getEntityMap(addon.getSettings().getNetherBlocks()), addon.getSettings().getNetherMainBlock()));
// End
chances.put(Environment.THE_END, new Chances(this.getEntityMap(addon.getSettings().getEndBlocks()), addon.getSettings().getEndMainBlock()));
// Other settings
worldHeight = addon.getSettings().getWorldDepth() - 1;
} }
@ -40,42 +64,16 @@ public class EntitiesPopulator extends BlockPopulator
@Override @Override
public void populate(World world, Random random, Chunk chunk) public void populate(World world, Random random, Chunk chunk)
{ {
Map<EntityType, Pair<Integer, Integer>> entityChanceMap; for (Map.Entry<EntityType, Pair<Double, Integer>> entry : chances.get(world.getEnvironment()).entityChanceMap.entrySet())
Material mainMaterial;
if (world.getEnvironment().equals(World.Environment.NETHER))
{
entityChanceMap = this.getEntityMap(this.settings.getNetherBlocks());
mainMaterial = this.settings.getNetherMainBlock();
}
else if (world.getEnvironment().equals(World.Environment.THE_END))
{
entityChanceMap = this.getEntityMap(this.settings.getEndBlocks());
mainMaterial = this.settings.getEndMainBlock();
}
else
{
entityChanceMap = this.getEntityMap(this.settings.getNormalBlocks());
mainMaterial = this.settings.getNormalMainBlock();
}
final int generationTry = this.settings.getNumberOfBlockGenerationTries();
final int worldHeight = this.settings.getWorldDepth() - 1;
for (Map.Entry<EntityType, Pair<Integer, Integer>> entry : entityChanceMap.entrySet())
{ {
for (int subY = 0; subY < worldHeight; subY += 16) for (int subY = 0; subY < worldHeight; subY += 16)
{ {
for (int tries = 0; tries < generationTry; tries++) // Use double so chance can be < 1
if (random.nextDouble() * 100 < entry.getValue().x)
{ {
if (random.nextInt(100) < entry.getValue().x)
{
int x = random.nextInt(15);
int z = random.nextInt(15);
int y = Math.min(worldHeight - 2, subY + random.nextInt(15)); int y = Math.min(worldHeight - 2, subY + random.nextInt(15));
// Spawn only in middle of chunk because bounding box will grow out from here
this.tryToPlaceEntity(world, chunk.getBlock(x, y, z), entry.getKey(), x, z, mainMaterial); this.tryToPlaceEntity(world, chunk.getBlock(7, y, 7), entry.getKey(), chances.get(world.getEnvironment()).mainMaterial);
}
} }
} }
} }
@ -87,9 +85,9 @@ public class EntitiesPopulator extends BlockPopulator
* @param objectList List with objects that contains data. * @param objectList List with objects that contains data.
* @return Map that contains entity, its rarity and pack size. * @return Map that contains entity, its rarity and pack size.
*/ */
private Map<EntityType, Pair<Integer, Integer>> getEntityMap(List<String> objectList) private Map<EntityType, Pair<Double, Integer>> getEntityMap(List<String> objectList)
{ {
Map<EntityType, Pair<Integer, Integer>> entityMap = new HashMap<>(objectList.size()); Map<EntityType, Pair<Double, Integer>> entityMap = new HashMap<>(objectList.size());
Map<String, EntityType> entityTypeMap = Arrays.stream(EntityType.values()). Map<String, EntityType> entityTypeMap = Arrays.stream(EntityType.values()).
collect(Collectors.toMap(Enum::name, collect(Collectors.toMap(Enum::name,
@ -108,150 +106,54 @@ public class EntitiesPopulator extends BlockPopulator
if (entity != null) if (entity != null)
{ {
entityMap.put(entity, entityMap.put(entity,
new Pair<>(Integer.parseInt(splitString[2]), Integer.parseInt(splitString[3]))); new Pair<>(Double.parseDouble(splitString[2]), Integer.parseInt(splitString[3])));
} }
}); });
return entityMap; return entityMap;
} }
/** /**
* This method checks if all chunks around given block is generated. * Places entities if there is room for them.
* @param world World in which block is located
* @param block Block that must be checked.
* @param x Block x-index in chunk
* @param z Block z-index in chunk
* @return true, if all chunks around given block are generated.
*/
private boolean isValidBlock(World world, Block block, int x, int z)
{
return x > 0 && x < 15 && z > 0 && z < 15 ||
world.isChunkGenerated(block.getX() + 1, block.getZ()) &&
world.isChunkGenerated(block.getX() - 1, block.getZ()) &&
world.isChunkGenerated(block.getX(), block.getZ() - 1) &&
world.isChunkGenerated(block.getX(), block.getZ() + 1);
}
/**
* This method is not completed. It must reserve space for entities to spawn, but
* current implementation just allows to spawn 2 high mobs that can be in single
* place.
* @param world - World were mob must be spawned. * @param world - World were mob must be spawned.
* @param block - Block that was choosed by random. * @param block - Block that was chosen by random.
* @param entity - Entity that must be spawned. * @param entity - Entity that must be spawned.
* @param x - ChunkX coordinate. * @param originalMaterial - replacement material.
* @param z - ChunkY coordinate.
* @param originalMaterial - replacement manterial.
*/ */
private void tryToPlaceEntity(World world, Block block, EntityType entity, int x, int z, Material originalMaterial) private void tryToPlaceEntity(World world, Block block, EntityType entity, Material originalMaterial)
{ {
if (this.isValidBlock(world, block, x, z) && block.getType().equals(originalMaterial)) if (block.getType().equals(originalMaterial)) {
{ // Spawn entity
if (entity.isAlive()) Entity e = world.spawnEntity(block.getLocation().add(0.5, 0, 0.5), entity);
{ if (e instanceof LivingEntity) {
int height = 0; // Do not despawn
int width = 0; ((LivingEntity)e).setRemoveWhenFarAway(false);
int length = 0;
boolean water = false;
switch (entity)
{
case SPIDER:
width = 1;
length = 1;
break;
case SLIME:
case ELDER_GUARDIAN:
case GHAST:
case MAGMA_CUBE:
case WITHER:
height = 2;
width = 2;
length = 2;
break;
case ENDERMAN:
case IRON_GOLEM:
height = 2;
break;
case WITHER_SKELETON:
case STRAY:
case HUSK:
case ZOMBIE_VILLAGER:
case EVOKER:
case VINDICATOR:
case ILLUSIONER:
case CREEPER:
case SKELETON:
case ZOMBIE:
case BLAZE:
case SNOWMAN:
case VILLAGER:
case PIG_ZOMBIE:
case WITCH:
case SHULKER:
case SHEEP:
case COW:
case MUSHROOM_COW:
height = 12;
break;
case SKELETON_HORSE:
case ZOMBIE_HORSE:
case DONKEY:
case MULE:
case HORSE:
case POLAR_BEAR:
case LLAMA:
height = 1;
width = 1;
break;
case GUARDIAN:
case SQUID:
case COD:
case SALMON:
case PUFFERFISH:
case TROPICAL_FISH:
water = true;
break;
case DROWNED:
case DOLPHIN:
water = true;
height = 1;
break;
} }
// Make space for entity based on the entity's size
if (block.getY() > 1 && block.getY() < world.getMaxHeight() - 2) BoundingBox bb = e.getBoundingBox();
{ for (int x = (int) bb.getMinX(); x < bb.getMaxX(); x++) {
Block otherBlock = world.getBlockAt(block.getX(), block.getY() + 1, block.getZ()); for (int z = (int) bb.getMinZ(); z < bb.getMaxZ(); z++) {
int y = (int) bb.getMinY();
if (!otherBlock.getType().equals(originalMaterial)) Block b = world.getBlockAt(x, y, z);
{ for (; y < bb.getMaxY(); y++) {
otherBlock = world.getBlockAt(block.getX(), block.getY() - 1, block.getZ()); if (addon.getSettings().isDebug()) {
addon.log("DEBUG: Entity spawn: " + world.getName() + " " + x + " " + y + " " + z + " " + e.getType());
} }
b = world.getBlockAt(x, y, z);
if (otherBlock.getType().equals(originalMaterial)) if (!b.getType().equals(originalMaterial)) {
{ // Cannot place entity
block.setType(Material.CAVE_AIR); e.remove();
otherBlock.setType(Material.CAVE_AIR); return;
if (otherBlock.getY() < block.getY())
{
world.spawnEntity(otherBlock.getLocation(), entity);
} }
else b.setType(WATER_ENTITIES.contains(entity) ? Material.WATER : Material.CAVE_AIR);
{ }
world.spawnEntity(block.getLocation(), entity); // Add air block on top for all water entities (required for dolphin, okay for others)
if (WATER_ENTITIES.contains(entity) && b.getRelative(BlockFace.UP).getType().equals(originalMaterial)) {
b.getRelative(BlockFace.UP).setType(Material.CAVE_AIR);
} }
} }
} }
} }
else
{
block.setType(Material.CAVE_AIR);
world.spawnEntity(block.getLocation(), entity);
}
}
} }
@ -265,8 +167,34 @@ public class EntitiesPopulator extends BlockPopulator
*/ */
private CaveBlock addon; private CaveBlock addon;
private Map<Environment, Chances> chances;
private int worldHeight;
private final static List<EntityType> WATER_ENTITIES = Arrays.asList(EntityType.GUARDIAN,
EntityType.SQUID,
EntityType.COD,
EntityType.SALMON,
EntityType.PUFFERFISH,
EntityType.TROPICAL_FISH,
EntityType.DROWNED,
EntityType.DOLPHIN);
/** /**
* CaveBlock settings. * Chances class to store chances for environments and main material
*
*/ */
private Settings settings; private class Chances {
final Map<EntityType, Pair<Double, Integer>> entityChanceMap;
final Material mainMaterial;
/**
* @param materialChanceMap
* @param mainMaterial
*/
public Chances(Map<EntityType, Pair<Double, Integer>> entityChanceMap, Material mainMaterial) {
this.entityChanceMap = entityChanceMap;
this.mainMaterial = mainMaterial;
}
}
} }

View File

@ -2,19 +2,20 @@
package world.bentobox.caveblock.generators.populators; package world.bentobox.caveblock.generators.populators;
import org.bukkit.Chunk;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.generator.BlockPopulator;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Random; import java.util.Random;
import org.bukkit.Chunk;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.World.Environment;
import org.bukkit.block.Block;
import org.bukkit.generator.BlockPopulator;
import world.bentobox.bentobox.util.Pair; import world.bentobox.bentobox.util.Pair;
import world.bentobox.caveblock.CaveBlock; import world.bentobox.caveblock.CaveBlock;
import world.bentobox.caveblock.Settings;
/** /**
@ -22,6 +23,7 @@ import world.bentobox.caveblock.Settings;
*/ */
public class MaterialPopulator extends BlockPopulator public class MaterialPopulator extends BlockPopulator
{ {
/** /**
* This is default constructor * This is default constructor
* @param addon CaveBlock addon. * @param addon CaveBlock addon.
@ -29,7 +31,25 @@ public class MaterialPopulator extends BlockPopulator
public MaterialPopulator(CaveBlock addon) public MaterialPopulator(CaveBlock addon)
{ {
this.addon = addon; this.addon = addon;
this.settings = addon.getSettings(); // Load settings
loadSettings();
}
/**
* Loads chances for Material Populator
*/
public void loadSettings() {
// Set up chances
chances = new HashMap<>();
// Normal
chances.put(Environment.NORMAL, new Chances(this.getMaterialMap(addon.getSettings().getNormalBlocks()), addon.getSettings().getNormalMainBlock()));
// Nether
chances.put(Environment.NETHER, new Chances(this.getMaterialMap(addon.getSettings().getNetherBlocks()), addon.getSettings().getNetherMainBlock()));
// End
chances.put(Environment.THE_END, new Chances(this.getMaterialMap(addon.getSettings().getEndBlocks()), addon.getSettings().getEndMainBlock()));
// Other settings
worldHeight = addon.getSettings().getWorldDepth() - 1;
} }
@ -42,44 +62,26 @@ public class MaterialPopulator extends BlockPopulator
@Override @Override
public void populate(World world, Random random, Chunk chunk) public void populate(World world, Random random, Chunk chunk)
{ {
Map<Material, Pair<Integer, Integer>> materialChanceMap; for (Map.Entry<Material, Pair<Double, Integer>> entry : chances.get(world.getEnvironment()).materialChanceMap.entrySet())
Material mainMaterial; {
for (int subY = 1; subY < worldHeight; subY += 16)
{
if (random.nextDouble() * 100 < entry.getValue().x)
{
if (world.getEnvironment().equals(World.Environment.NETHER)) // Blocks must be 1 away from edge to avoid adjacent chunk loading
{ int x = random.nextInt(13) + 1;
materialChanceMap = this.getMaterialMap(this.settings.getNetherBlocks()); int z = random.nextInt(13) + 1;
mainMaterial = this.settings.getNetherMainBlock();
}
else if (world.getEnvironment().equals(World.Environment.THE_END))
{
materialChanceMap = this.getMaterialMap(this.settings.getEndBlocks());
mainMaterial = this.settings.getEndMainBlock();
}
else
{
materialChanceMap = this.getMaterialMap(this.settings.getNormalBlocks());
mainMaterial = this.settings.getNormalMainBlock();
}
final int generationTry = this.settings.getNumberOfBlockGenerationTries();
final int worldHeight = this.settings.getWorldDepth() - 1;
for (Map.Entry<Material, Pair<Integer, Integer>> entry : materialChanceMap.entrySet())
{
for (int subY = 0; subY < worldHeight; subY += 16)
{
for (int tries = 0; tries < generationTry; tries++)
{
if (random.nextInt(100) < entry.getValue().x)
{
int x = random.nextInt(15);
int z = random.nextInt(15);
int y = Math.min(worldHeight - 2, subY + random.nextInt(15)); int y = Math.min(worldHeight - 2, subY + random.nextInt(15));
/*
* TODO: remove
if (addon.getSettings().isDebug()) {
addon.log("DEBUG: Material: " + world.getName() + " " + x + " " + y + " " + z + " " + entry.getKey());
}
*/
Block block = chunk.getBlock(x, y, z); Block block = chunk.getBlock(x, y, z);
if (block.getType().equals(mainMaterial) && if (block.getType().equals(chances.get(world.getEnvironment()).mainMaterial))
this.isValidBlock(world, block, x, z))
{ {
int packSize = random.nextInt(entry.getValue().z); int packSize = random.nextInt(entry.getValue().z);
@ -118,9 +120,8 @@ public class MaterialPopulator extends BlockPopulator
block = chunk.getBlock(x, y, z); block = chunk.getBlock(x, y, z);
continuePlacing = this.isValidBlock(world, block, x, z) && continuePlacing = packSize > 0 &&
packSize > 0 && (block.getType().equals(chances.get(world.getEnvironment()).mainMaterial) ||
(block.getType().equals(mainMaterial) ||
block.getType().equals(entry.getKey())); block.getType().equals(entry.getKey()));
} }
} }
@ -128,35 +129,15 @@ public class MaterialPopulator extends BlockPopulator
} }
} }
} }
}
/**
* This method checks if all chunks around given block is generated.
* @param world World in which block is located
* @param block Block that must be checked.
* @param x Block x-index in chunk
* @param z Block z-index in chunk
* @return true, if all chunks around given block are generated.
*/
private boolean isValidBlock(World world, Block block, int x, int z)
{
return x > 0 && x < 15 && z > 0 && z < 15 ||
world.isChunkGenerated(block.getX() + 1, block.getZ()) &&
world.isChunkGenerated(block.getX() - 1, block.getZ()) &&
world.isChunkGenerated(block.getX(), block.getZ() - 1) &&
world.isChunkGenerated(block.getX(), block.getZ() + 1);
}
/** /**
* This method returns material frequently and pack size map. * This method returns material frequently and pack size map.
* @param objectList List with objects that contains data. * @param objectList List with objects that contains data.
* @return Map that contains material, its rarity and pack size. * @return Map that contains material, its rarity and pack size.
*/ */
private Map<Material, Pair<Integer, Integer>> getMaterialMap(List<String> objectList) private Map<Material, Pair<Double, Integer>> getMaterialMap(List<String> objectList)
{ {
Map<Material, Pair<Integer, Integer>> materialMap = new HashMap<>(objectList.size()); Map<Material, Pair<Double, Integer>> materialMap = new HashMap<>(objectList.size());
// wrong material object. // wrong material object.
objectList.stream(). objectList.stream().
@ -169,7 +150,7 @@ public class MaterialPopulator extends BlockPopulator
if (material != null) if (material != null)
{ {
materialMap.put(material, materialMap.put(material,
new Pair<>(Integer.parseInt(splitString[2]), Integer.parseInt(splitString[3]))); new Pair<>(Double.parseDouble(splitString[2]), Integer.parseInt(splitString[3])));
} }
}); });
@ -187,8 +168,25 @@ public class MaterialPopulator extends BlockPopulator
*/ */
private CaveBlock addon; private CaveBlock addon;
private Map<Environment, Chances> chances;
private int worldHeight;
/** /**
* CaveBlock settings. * Chances class to store chances for environments and main material
*
*/ */
private Settings settings; private class Chances {
final Map<Material, Pair<Double, Integer>> materialChanceMap;
final Material mainMaterial;
/**
* @param materialChanceMap
* @param mainMaterial
*/
public Chances(Map<Material, Pair<Double, Integer>> materialChanceMap, Material mainMaterial) {
this.materialChanceMap = materialChanceMap;
this.mainMaterial = mainMaterial;
}
}
} }

View File

@ -0,0 +1,203 @@
package world.bentobox.caveblock.listeners;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.block.BlockDamageEvent;
import org.bukkit.event.block.BlockExplodeEvent;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.entity.EntityExplodeEvent;
import world.bentobox.bentobox.util.Util;
import world.bentobox.caveblock.CaveBlock;
import world.bentobox.caveblock.Settings;
/**
* This class allows to enable beacon in CaveBlock, if cave roof is made of bedrock.
* It will replace Bedrock with black glass.
*/
public class BeaconEnabler implements Listener
{
/**
* Constructor BeaconEnabler creates a new BeaconEnabler instance.
*
* @param addon of type CaveBlock
*/
public BeaconEnabler(CaveBlock addon)
{
this.addon = addon;
this.settings = addon.getSettings();
}
/**
* Method onBlockPlacement detects if beacon is placed and replace roof bedrock with black glass.
*
* @param event of type BlockPlaceEvent
*/
@EventHandler(priority = EventPriority.LOWEST)
public void onBlockPlacement(BlockPlaceEvent event)
{
World world = event.getPlayer().getWorld();
if (!Util.sameWorld(this.addon.getOverWorld(), world) ||
!this.settings.isBeaconAllowed() ||
!this.isRoofEnabled(world) ||
!event.getBlock().getType().equals(Material.BEACON))
{
// This should work only if it is cave block world or world has roof from bedrock. Otherwise,
// players can dig till top themself.
return;
}
Block roofBlock = world.getBlockAt(event.getBlock().getX(), this.settings.getWorldDepth() - 1, event.getBlock().getZ());
if (roofBlock.getType().equals(Material.BEDROCK))
{
// Replace only bedrock.
roofBlock.setType(Material.BLACK_STAINED_GLASS);
}
}
/**
* Method onBlockBreak detects if beacon is destroyed and replace roof black glass with bedrock.
*
* @param event of type BlockBreakEvent
*/
@EventHandler(priority = EventPriority.LOWEST)
public void onBlockBreak(BlockBreakEvent event)
{
World world = event.getPlayer().getWorld();
if (!Util.sameWorld(this.addon.getOverWorld(), world) ||
!this.isRoofEnabled(world) ||
!this.settings.isBeaconAllowed() ||
!event.getBlock().getType().equals(Material.BEACON))
{
// This should work only if it is cave block world or world has roof from bedrock.
return;
}
Block roofBlock = world.getBlockAt(event.getBlock().getX(), this.settings.getWorldDepth() - 1, event.getBlock().getZ());
if (roofBlock.getType().equals(Material.BLACK_STAINED_GLASS))
{
// Replace only black glass.
roofBlock.setType(Material.BEDROCK);
}
}
/**
* Method onBlockDamage detects if user tries to destroy black glass on roof and disable it.
*
* @param event of type BlockDamageEvent
*/
@EventHandler(priority = EventPriority.LOWEST)
public void onBlockDamage(BlockDamageEvent event)
{
World world = event.getPlayer().getWorld();
if (!Util.sameWorld(this.addon.getOverWorld(), world) ||
!this.isRoofEnabled(world) ||
!this.settings.isBeaconAllowed() ||
event.getBlock().getY() != this.settings.getWorldDepth() - 1)
{
// This should work only if it is cave block world or world has roof from bedrock.
return;
}
// Cancel break event if it is black glass.
event.setCancelled(event.getBlock().getType().equals(Material.BLACK_STAINED_GLASS));
}
/**
* Method onBlockExplode detects if explosion tries to destroy black glass on roof and disable it.
*
* @param event of type BlockExplodeEvent
*/
@EventHandler(priority = EventPriority.LOWEST)
public void onBlockExplode(BlockExplodeEvent event)
{
World world = event.getBlock().getWorld();
if (!Util.sameWorld(this.addon.getOverWorld(), world) ||
!this.isRoofEnabled(world) ||
!this.settings.isBeaconAllowed() ||
event.getBlock().getY() < this.settings.getWorldDepth() - 9)
{
// This should work only if it is cave block world or world has roof from bedrock.
return;
}
final int blockY = this.settings.getWorldDepth() - 1;
// Remove all black stained glass from explosion block list if it is on the roof.
event.blockList().removeIf(block ->
block.getY() == blockY && block.getType().equals(Material.BLACK_STAINED_GLASS));
}
/**
* Method onEntityExplode detects if explosion tries to destroy black glass on roof and disable it.
*
* @param event of type EntityExplodeEvent
*/
@EventHandler(priority = EventPriority.LOWEST)
public void onEntityExplode(EntityExplodeEvent event)
{
World world = event.getLocation().getWorld();
if (!Util.sameWorld(this.addon.getOverWorld(), world) ||
!this.isRoofEnabled(world) ||
!this.settings.isBeaconAllowed() ||
event.getLocation().getY() < this.settings.getWorldDepth() - 9)
{
// This should work only if it is cave block world or world has roof from bedrock.
return;
}
final int blockY = this.settings.getWorldDepth() - 1;
// Remove all black stained glass from explosion block list if it is on the roof.
event.blockList().removeIf(block ->
block.getY() == blockY && block.getType().equals(Material.BLACK_STAINED_GLASS));
}
/**
* This method checks if in given world bedrock roof is enabled.
* @param world World that must be checked.
* @return <code>true</code> - bedrock roof is enabled, otherwise <code>false</code>
*/
private boolean isRoofEnabled(World world)
{
return world.getEnvironment().equals(World.Environment.NORMAL) && this.settings.isNormalRoof() ||
world.getEnvironment().equals(World.Environment.NETHER) && this.settings.isNetherRoof() ||
world.getEnvironment().equals(World.Environment.THE_END) && this.settings.isEndRoof();
}
// ---------------------------------------------------------------------
// Section: Variables
// ---------------------------------------------------------------------
/**
* CaveBlock addon.
*/
private CaveBlock addon;
/**
* Addon settings.
*/
private Settings settings;
}

View File

@ -0,0 +1,192 @@
package world.bentobox.caveblock.listeners;
import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.event.player.PlayerTeleportEvent;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.util.Util;
import world.bentobox.bentobox.util.teleport.SafeSpotTeleport;
import world.bentobox.caveblock.CaveBlock;
/**
* This listener checks player movement. If enabled, players will be deny to get over world depth limit and
* if alternative teleports is enabled, then falling in void also will be processed.
*/
public class CustomHeightLimitations implements Listener
{
/**
* Simple constructor
* @param addon
*/
public CustomHeightLimitations(CaveBlock addon)
{
this.addon = addon;
this.worldHeight = addon.getSettings().getWorldDepth() - 1;
}
/**
* Method onPlayerMove disables movement if user tries to get on top of the world.
* It allows movement only downwards.
*
* @param event of type PlayerMoveEvent
*/
@EventHandler(priority = EventPriority.LOWEST)
public void onPlayerMove(PlayerMoveEvent event)
{
Player player = event.getPlayer();
final double nextY = event.getTo().getY();
if (this.addon.getSettings().isSkyWalking() ||
player.isOp() ||
player.isDead() ||
player.getGameMode().equals(GameMode.CREATIVE) ||
player.getGameMode().equals(GameMode.SPECTATOR) ||
this.addon.getPlayers().isInTeleport(player.getUniqueId()) ||
player.hasPermission("caveblock.skywalker") ||
!Util.sameWorld(this.addon.getOverWorld(), player.getWorld()) ||
nextY > 0 && nextY < this.worldHeight ||
// Next check will allow to go down, but never up.
event.getFrom().getBlockY() <= event.getFrom().getBlockY() &&
event.getFrom().getBlockX() == event.getTo().getBlockX() &&
event.getFrom().getBlockZ() == event.getTo().getBlockZ())
{
// interested only in movements that is below 0 or above height limit.
return;
}
// Use custom teleport to different world
if (this.addon.getSettings().isAlternativeTeleports() && nextY <= 0)
{
switch (player.getWorld().getEnvironment())
{
case NORMAL:
{
// From normal world users will get to nether.
Location to = this.addon.getIslands().getIslandAt(event.getFrom()).
map(i -> i.getSpawnPoint(World.Environment.NETHER)).
orElse(event.getFrom().toVector().toLocation(this.addon.getNetherWorld()));
event.setCancelled(true);
new SafeSpotTeleport.Builder(this.addon.getPlugin()).
entity(event.getPlayer()).
location(to).
portal().
build();
break;
}
case NETHER:
{
// From nether world users will get to the end.
Location to = this.addon.getIslands().getIslandAt(event.getFrom()).
map(i -> i.getSpawnPoint(World.Environment.THE_END)).
orElse(event.getFrom().toVector().toLocation(this.addon.getEndWorld()));
event.setCancelled(true);
new SafeSpotTeleport.Builder(this.addon.getPlugin()).
entity(event.getPlayer()).
location(to).
portal().
build();
break;
}
case THE_END:
{
// From the end users will get to over world.
Location to = this.addon.getIslands().getIslandAt(event.getFrom()).
map(i -> i.getSpawnPoint(World.Environment.NORMAL)).
orElse(event.getFrom().toVector().toLocation(this.addon.getOverWorld()));
event.setCancelled(true);
new SafeSpotTeleport.Builder(this.addon.getPlugin()).
entity(event.getPlayer()).
location(to).
portal().
build();
break;
}
default:
break;
}
return;
}
// Prevent to get over world height
if (nextY >= this.worldHeight)
{
User.getInstance(player).sendMessage("caveblock.general.errors.cave-limit-reached");
event.setCancelled(true);
}
}
/**
* Method onPlayerTeleport disables all teleports that involves moving on top of the world.
*
* @param event of type PlayerTeleportEvent
*/
@EventHandler(priority = EventPriority.LOWEST)
public void onPlayerTeleport(PlayerTeleportEvent event)
{
Player player = event.getPlayer();
final double nextY = event.getTo().getY();
if (this.addon.getSettings().isSkyWalking() ||
player.isOp() ||
player.isDead() ||
player.getGameMode().equals(GameMode.CREATIVE) ||
player.getGameMode().equals(GameMode.SPECTATOR) ||
this.addon.getPlayers().isInTeleport(player.getUniqueId()) ||
player.hasPermission("caveblock.skywalker") ||
!Util.sameWorld(this.addon.getOverWorld(), player.getWorld()) ||
nextY > 0 && nextY < this.worldHeight ||
// Next check will allow to go down, but never up.
event.getFrom().getBlockY() <= event.getFrom().getBlockY() &&
event.getFrom().getBlockX() == event.getTo().getBlockX() &&
event.getFrom().getBlockZ() == event.getTo().getBlockZ())
{
// interested only in movements that is below 0 or above height limit.
return;
}
// Prevent to get over world height
if (nextY >= this.worldHeight)
{
User.getInstance(player).sendMessage("caveblock.general.errors.cave-limit-reached");
event.setCancelled(true);
}
}
// ---------------------------------------------------------------------
// Section: Variables
// ---------------------------------------------------------------------
/**
* CaveBlock addon
*/
private CaveBlock addon;
/**
* This variable store world height.
*/
private int worldHeight;
}

View File

@ -6,25 +6,25 @@ authors: BONNe
permissions: permissions:
caveblock.island: caveblock.island:
description: Allow island command usage description: Allow cave command usage
default: true default: true
caveblock.island.create: caveblock.island.create:
description: Allow island creation description: Allow cave creation
default: true default: true
caveblock.island.home: caveblock.island.home:
description: Allow teleporting to player island description: Allow teleporting to player cave
default: true default: true
caveblock.island.sethome: caveblock.island.sethome:
description: Let the player use the sethome command description: Let the player use the sethome command
default: true default: true
caveblock.island.info: caveblock.island.info:
description: Let the player check their island level description: Let the player check their cave level
default: true default: true
caveblock.island.sethome: caveblock.island.sethome:
description: Let the player set their island teleport point description: Let the player set their cave teleport point
default: true default: true
caveblock.island.lock: caveblock.island.lock:
description: Allows island locking description: Allows cave locking
default: false default: false
caveblock.island.expel: caveblock.island.expel:
description: Allows expelling of visitors description: Allows expelling of visitors
@ -39,13 +39,13 @@ permissions:
description: Player can select a language description: Player can select a language
default: true default: true
caveblock.island.name: caveblock.island.name:
description: Player can set the name of their island description: Player can set the name of their cave
default: true default: true
caveblock.island.spawn: caveblock.island.spawn:
description: Player can use the island spawn command if spawn exists description: Player can use the cave spawn command if spawn exists
default: true default: true
caveblock.island.reset: caveblock.island.reset:
description: Player can use the island reset or restart command description: Player can use the cave reset or restart command
default: true default: true
caveblock.island.team: caveblock.island.team:
description: Let a player use team commands description: Let a player use team commands
@ -57,7 +57,7 @@ permissions:
description: Let a player use team coop commands description: Let a player use team coop commands
default: true default: true
caveblock.settings.*: caveblock.settings.*:
description: Allow use of settings on island description: Allow use of settings on cave
default: true default: true
caveblock.mod.info: caveblock.mod.info:
description: Let a moderator see info on a player description: Let a moderator see info on a player
@ -66,71 +66,74 @@ permissions:
description: Allows setting or reseting of a player's home position description: Allows setting or reseting of a player's home position
default: op default: op
caveblock.mod.clearreset: caveblock.mod.clearreset:
description: Allow clearing of island reset limit description: Allow clearing of cave reset limit
default: false default: false
caveblock.mod.tp: caveblock.mod.tp:
description: Allows teleport to an island description: Allows teleport to an cave
default: op default: op
caveblock.mod.bypasscooldowns: caveblock.mod.bypasscooldowns:
description: Allow moderator to bypass cooldowns description: Allow moderator to bypass cooldowns
default: op default: op
caveblock.mod.bypassprotect: caveblock.mod.bypassprotect:
description: Allow moderator to bypass island protection description: Allow moderator to bypass cave protection
default: op default: op
caveblock.mod.bypassexpel: caveblock.mod.bypassexpel:
description: Allow moderator to bypass island expulsion description: Allow moderator to bypass cave expulsion
default: op default: op
caveblock.mod.lock: caveblock.mod.lock:
description: Locks or unlocks an island description: Locks or unlocks an cave
default: op default: op
caveblock.mod.bypasslock: caveblock.mod.bypasslock:
description: Bypasses an island lock description: Bypasses an cave lock
default: op default: op
caveblock.mod.team: caveblock.mod.team:
description: Enables modification of teams via kick and add commands description: Enables modification of teams via kick and add commands
default: false default: false
caveblock.mod.name: caveblock.mod.name:
description: Enables naming of player's islands description: Enables naming of player's caves
default: false default: false
caveblock.mod.resetname: caveblock.mod.resetname:
description: Enables reset of player's island names description: Enables reset of player's cave names
default: false default: false
caveblock.admin.clearresetall: caveblock.admin.clearresetall:
description: Allow clearing of island reset limit of all players description: Allow clearing of cave reset limit of all players
default: op default: op
caveblock.admin.reload: caveblock.admin.reload:
description: Reload the config.yml description: Reload the config.yml
default: op default: op
caveblock.admin.delete: caveblock.admin.delete:
description: Let a player completely remove a player (including island) description: Let a player completely remove a player (including cave)
default: op default: op
caveblock.admin.deleteisland: caveblock.admin.deleteisland:
description: Let a player completely remove the island the player is on description: Let a player completely remove the cave the player is on
default: op default: op
caveblock.admin.register: caveblock.admin.register:
description: Let a player register the nearest island to another player. description: Let a player register the nearest cave to another player.
default: op default: op
caveblock.admin.unregister: caveblock.admin.unregister:
description: Removes a player from an island without deleting the island blocks. description: Removes a player from an cave without deleting the cave blocks.
default: op default: op
caveblock.admin.purge: caveblock.admin.purge:
description: Let a player purge old islands. description: Let a player purge old caves.
default: op default: op
caveblock.admin.setspawn: caveblock.admin.setspawn:
description: Allows use of spawn tools description: Allows use of spawn tools
default: op default: op
caveblock.admin.setrange: caveblock.admin.setrange:
description: Allows setting of island protection range description: Allows setting of cave protection range
default: op default: op
caveblock.admin.reserve: caveblock.admin.reserve:
description: Reserves an empty spot for a player's next island description: Reserves an empty spot for a player's next cave
default: op default: op
caveblock.admin.settingsreset: caveblock.admin.settingsreset:
description: Resets all the islands to default protection settings description: Resets all the caves to default protection settings
default: op default: op
caveblock.admin.noban: caveblock.admin.noban:
description: Player cannot be banned from an island description: Player cannot be banned from an cave
default: op default: op
caveblock.admin.setlanguage: caveblock.admin.setlanguage:
description: Resets all player languages and sets the default language description: Resets all player languages and sets the default language
default: op default: op
caveblock.skywalker:
description: Allows player to walk over the heigh limit.
default: op

View File

@ -1,7 +1,7 @@
# CaveBlock Configuration ${version} # CaveBlock Configuration ${version}
# This config file is dynamic and saved when the server is shutdown. # This config file is dynamic and saved when the server is shutdown.
# You cannot edit it while the server is running because changes will # If you edit it while the server is running use /cbadmin reload
# be lost! Use in-game settings GUI or edit when server is offline. # otherwise your settings will be lost.
world: world:
# Friendly name for this world. Used in admin commands. Must be a single word # Friendly name for this world. Used in admin commands. Must be a single word
friendly-name: CaveBlock friendly-name: CaveBlock
@ -11,11 +11,11 @@ world:
# World difficulty setting - PEACEFUL, EASY, NORMAL, HARD # World difficulty setting - PEACEFUL, EASY, NORMAL, HARD
# Other plugins may override this setting # Other plugins may override this setting
difficulty: NORMAL difficulty: NORMAL
# Radius of island in blocks. (So distance between islands is twice this) # Radius of cave in blocks. (So distance between caves is twice this)
# Will be rounded up to the nearest 16 blocks. # Will be rounded up to the nearest 16 blocks.
# It is the same for every dimension : Overworld, Nether and End. # It is the same for every dimension : Overworld, Nether and End.
# This value cannot be changed mid-game and the plugin will not start if it is different. # This value cannot be changed mid-game and the plugin will not start if it is different.
distance-between-islands: 64 distance-between-caves: 64
# Default protection range radius in blocks. Cannot be larger than distance. # Default protection range radius in blocks. Cannot be larger than distance.
# Admins can change protection sizes for players individually using /cbadmin range set <player> <new range> # Admins can change protection sizes for players individually using /cbadmin range set <player> <new range>
# or set this permission: caveblock.island.range.<number> # or set this permission: caveblock.island.range.<number>
@ -29,18 +29,13 @@ world:
start-z: 0 start-z: 0
offset-x: 0 offset-x: 0
offset-z: 0 offset-z: 0
# Island height - Lowest is 5. # Cave height - Lowest is 5.
# It is the y coordinate of the bedrock block in the schem. # It is the y coordinate of the bedrock block in the schem.
island-height: 60 cave-height: 60
# Use your own world generator for this world. # Use your own world generator for this world.
# In this case, the plugin will not generate anything. # In this case, the plugin will not generate anything.
# /!\ This feature is experimental and might not work as expected or might not work at all. # /!\ This feature is experimental and might not work as expected or might not work at all.
use-own-generator: true use-own-generator: true
# Sea height (don't changes this mid-game unless you delete the world)
# Minimum is 0, which means you are playing CaveBlock!
# If sea height is less than about 10, then players will drop right through it
# if it exists. Makes for an interesting variation on caveblock.
sea-height: 0
# Maximum number of islands in the world. Set to -1 or 0 for unlimited. # Maximum number of islands in the world. Set to -1 or 0 for unlimited.
# If the number of islands is greater than this number, it will stop players from creating islands. # If the number of islands is greater than this number, it will stop players from creating islands.
max-islands: 0 max-islands: 0
@ -50,7 +45,7 @@ world:
# The default biome for the overworld # The default biome for the overworld
default-biome: MOUNTAINS default-biome: MOUNTAINS
# The maximum number of players a player can ban at any one time in this game mode. # The maximum number of players a player can ban at any one time in this game mode.
# The permission acidisland.ban.maxlimit.X where X is a number can also be used per player # The permission caveblock.ban.maxlimit.X where X is a number can also be used per player
# -1 = unlimited # -1 = unlimited
ban-limit: -1 ban-limit: -1
# #
@ -59,6 +54,20 @@ world:
world-depth: 256 world-depth: 256
# This indicate how many times block should be tried to generate. # This indicate how many times block should be tried to generate.
generation-tries: 2 generation-tries: 2
#
# Allows to walk over the world roof.
sky-walking: false
# Enables different ways how to get to other worlds.
# If players fall into void, then they will be teleported:
# - to nether if falls into void from over world
# - to the end if falls into void from nether
# - to over world if falls into void from the end
alternative-teleports: true
# Enables ability to use beacon, if world roof is made of Bedrock. It will replace
# bedrock with black stained glass and on beacon placing, and replace it with bedrock if
# beacon is destroyed.
# This will not do anything, if roof is not made of bedrock.
allow-beacon: false
normal: normal:
# #
# Make over world roof of bedrock, if false, it will be made from stone # Make over world roof of bedrock, if false, it will be made from stone
@ -74,11 +83,11 @@ world:
# Entities spawned via generator are not protected from despawing. # Entities spawned via generator are not protected from despawing.
# Working only with 2 high mobs currently. # Working only with 2 high mobs currently.
# Example: # Example:
# MATERIAL:DIAMOND:100:5 - means there is 100% chace of spawing diamonds # MATERIAL:DIAMOND_ORE:100:5 - means there is 100% chace of spawing diamonds
# where max amount in pack are 5 per each subchunk! # where max amount in pack are 5 per each subchunk!
blocks: blocks:
- MATERIAL:DIAMOND_ORE:1:3 - MATERIAL:DIAMOND_ORE:1:5
- MATERIAL:GOLD_ORE:5:4 - MATERIAL:GOLD_ORE:1:4
- MATERIAL:IRON_ORE:5:4 - MATERIAL:IRON_ORE:5:4
- MATERIAL:COAL_ORE:10:6 - MATERIAL:COAL_ORE:10:6
- MATERIAL:EMERALD_ORE:1:1 - MATERIAL:EMERALD_ORE:1:1
@ -88,6 +97,9 @@ world:
- MATERIAL:GRANITE:20:10 - MATERIAL:GRANITE:20:10
- MATERIAL:ANDESITE:20:10 - MATERIAL:ANDESITE:20:10
- MATERIAL:DIORITE:30:8 - MATERIAL:DIORITE:30:8
- ENTITY:ZOMBIE:1:1
- ENTITY:DOLPHIN:0.1:1
- ENTITY:CAVE_SPIDER:1:1
nether: nether:
# Generate Nether - if this is false, the nether world will not be made and access to # Generate Nether - if this is false, the nether world will not be made and access to
# the nether will not occur. Other plugins may still enable portal usage. # the nether will not occur. Other plugins may still enable portal usage.
@ -118,14 +130,19 @@ world:
# Entities spawned via generator are not protected from despawing. # Entities spawned via generator are not protected from despawing.
# Working only with 2 high mobs currently. # Working only with 2 high mobs currently.
# Example: # Example:
# MATERIAL:DIAMOND:100:5 - means there is 100% chace of spawing diamonds # MATERIAL:DIAMOND_ORE:100:5 - means there is 100% chace of spawing diamonds
# where max amount in pack are 5 per each subchunk! # where max amount in pack are 5 per each subchunk!
blocks: blocks:
- MATERIAL:QUARTZ_ORE:30:5 - MATERIAL:QUARTZ_ORE:30:5
- MATERIAL:SOUL_SAND:40:10 - MATERIAL:SOUL_SAND:40:10
- MATERIAL:MAGMA_BLOCK:10:3 - MATERIAL:MAGMA_BLOCK:10:3
- MATERIAL:GLOWSTONE:20:8 - MATERIAL:GLOWSTONE:20:8
- MATERIAL:NETHER_BRICK:10:5
- MATERIAL:LAVA:10:1 - MATERIAL:LAVA:10:1
- ENTITY:MAGMA_CUBE:0.5:1
- ENTITY:GHAST:0.1:1
- ENTITY:WITHER_SKELETON:0.1:1
- MATERIAL:FIRE:10:1
end: end:
generate: true generate: true
islands: true islands: true
@ -144,14 +161,16 @@ world:
# Entities spawned via generator are not protected from despawing. # Entities spawned via generator are not protected from despawing.
# Working only with 2 high mobs currently. # Working only with 2 high mobs currently.
# Example: # Example:
# MATERIAL:DIAMOND:100:5 - means there is 100% chace of spawing diamonds # MATERIAL:DIAMOND_ORE:100:5 - means there is 100% chace of spawing diamonds
# where max amount in pack are 5 per each subchunk! # where max amount in pack are 5 per each subchunk!
blocks: blocks:
- ENTITY:SHULKER:1:1 - ENTITY:SHULKER:0.2:1
- MATERIAL:OBSIDIAN:1:1
- MATERIAL:CHORUS_FRUIT:1:3
# Mob white list - these mobs will NOT be removed when logging in or doing /cave # Mob white list - these mobs will NOT be removed when logging in or doing /cave
remove-mobs-whitelist: remove-mobs-whitelist:
- ZOMBIE_VILLAGER
- WITHER - WITHER
- ZOMBIE_VILLAGER
- PIG_ZOMBIE - PIG_ZOMBIE
- ENDERMAN - ENDERMAN
# World flags. These are boolean settings for various flags for this world # World flags. These are boolean settings for various flags for this world
@ -193,8 +212,8 @@ world:
FROST_WALKER: 500 FROST_WALKER: 500
COLLECT_LAVA: 500 COLLECT_LAVA: 500
LEVER: 500 LEVER: 500
HURT_MONSTERS: 0
RIDING: 500 RIDING: 500
HURT_MONSTERS: 0
NAME_TAG: 500 NAME_TAG: 500
ARMOR_STAND: 500 ARMOR_STAND: 500
TRADING: 0 TRADING: 0
@ -202,8 +221,8 @@ world:
ITEM_DROP: 0 ITEM_DROP: 0
NOTE_BLOCK: 0 NOTE_BLOCK: 0
NETHER_PORTAL: 500 NETHER_PORTAL: 500
ITEM_PICKUP: 0
CROP_TRAMPLE: 500 CROP_TRAMPLE: 500
ITEM_PICKUP: 0
BREWING: 500 BREWING: 500
DROPPER: 500 DROPPER: 500
COLLECT_WATER: 500 COLLECT_WATER: 500
@ -215,26 +234,27 @@ world:
PLACE_BLOCKS: 500 PLACE_BLOCKS: 500
ITEM_FRAME: 500 ITEM_FRAME: 500
CRAFTING: 0 CRAFTING: 0
SHEARING: 500
ENCHANTING: 0 ENCHANTING: 0
SPAWN_EGGS: 500 SHEARING: 500
BED: 500 BED: 500
SPAWN_EGGS: 500
MILKING: 0 MILKING: 0
DISPENSER: 500 DISPENSER: 500
GATE: 0 GATE: 0
EXPERIENCE_PICKUP: 500 EXPERIENCE_PICKUP: 500
HOPPER: 500 HOPPER: 500
LEASH: 500 LEASH: 500
MOUNT_INVENTORY: 500
BREAK_BLOCKS: 500 BREAK_BLOCKS: 500
MOUNT_INVENTORY: 500
CHORUS_FRUIT: 500 CHORUS_FRUIT: 500
CONTAINER: 500 CONTAINER: 500
POTION_THROWING: 500
JUKEBOX: 500 JUKEBOX: 500
# These are the default settings for new islands # These are the default settings for new islands
default-island-settings: default-island-settings:
PVP_END: false PVP_END: false
ANIMAL_SPAWN: true
PVP_NETHER: false PVP_NETHER: false
ANIMAL_SPAWN: true
MONSTER_SPAWN: true MONSTER_SPAWN: true
FIRE_SPREAD: true FIRE_SPREAD: true
PVP_OVERWORLD: false PVP_OVERWORLD: false
@ -350,4 +370,5 @@ panel:
do-not-edit-these-settings: do-not-edit-these-settings:
# These settings should not be edited # These settings should not be edited
reset-epoch: 0 reset-epoch: 0
debug: false
uniqueId: config uniqueId: config

View File

@ -10,3 +10,233 @@ caveblock:
line2: "[name]" line2: "[name]"
line3: "Start digging! &c<3" line3: "Start digging! &c<3"
informational:
to-nether: "So unlucky to fall into nether."
to-the-end: "You have reached the end."
to-normal: "Back to your cave."
# Override BentoBox default command strings
# General strings
general:
errors:
no-island: "&cYou do not have a cave!"
player-has-island: "&cPlayer already has a cave!"
player-has-no-island: "&cThat player has no cave!"
already-have-island: "&cYou already have a cave!"
no-safe-location: "&cNo safe location found!"
not-owner: "&cYou are not the owner of your team!"
cave-limit-reached: "&cYou have reached the top of your cave. You cannot get higher!"
commands:
# Override BentoBox default island command strings
island:
info:
description: "display info about your cave or the player's cave"
go:
description: "teleport you to your cave"
teleport: "&aTeleporting you to your cave."
create:
description: "create a cave, using optional schem (requires permission)"
too-many-islands: "&cThere are too many caves in this world: there isn't enough room for yours to be created."
unable-create-island: "&cYour cave could not be generated, please contact an administrator."
creating-island: "&aCreating your cave, please wait a moment..."
reset:
description: "restart your cave from scratch"
parameters: ""
must-remove-members: "&cYou must remove all team players before you can restart (/[label] team kick <player>)."
sethome:
must-be-on-your-island: "&cYou must be in your cave to set home!"
home-set: "&6Your home has been set to your current location."
setname:
description: "set a name for your cave"
resetname:
description: "reset your cave name"
team:
coop:
description: "make a player coop rank"
uncoop:
you-are-no-longer-a-coop-member: "&cYou are no longer a coop member of [name]'s cave"
all-members-logged-off: "&cAll team members logged off so you are no longer a coop member of [name]'s cave"
trust:
description: "give a player trusted rank"
invite:
description: "invite a player to join your team"
name-has-invited-you: "&a[name] has invited you to join their team."
to-accept-or-reject: "&aDo /[label] team accept to accept, or /[label] team reject to reject"
you-will-lose-your-island: "&cWARNING! You will lose your our cave if you accept!"
errors:
island-is-full: "&cYour team is full, you can't invite anyone else."
accept:
you-joined-island: "&aYou joined a team! Use /[label] team info to see the other members."
name-joined-your-island: "&a[name] joined your team!"
confirmation: |-
&cAre you sure you want to accept this invite?
&c&lThis will &nDESTORY &r&c&lyour current cave!
reject:
you-rejected-invite: "&aYou rejected the invitation to join a team."
name-rejected-your-invite: "&c[name] rejected your invite!"
cancel:
description: "cancel the pending invite to join your team"
leave:
description: "leave your team"
left-your-island: "&c[name] &cleft your team"
kick:
description: "remove a team member"
owner-kicked: "&cThe owner kicked you from the team!"
demote:
description: "demote a player one rank"
promote:
description: "promote a player one rank"
setowner:
description: "transfer team ownership to a member"
errors:
target-is-not-member: "&cThat player is not part of your team!"
name-is-the-owner: "&a[name] is now the cave owner!"
you-are-the-owner: "&aYou are now the cave owner!"
ban:
description: "ban a player from your cave"
cannot-ban-more-players: "&cYou reached the ban limit, you cannot ban any more players."
owner-banned-you: "&b[name]&c banned you from their cave!"
you-are-banned: "&bYou are banned from this cave!"
unban:
description: "unban a player from your cave"
you-are-unbanned: "&b[name]&a unbanned you from their cave!"
banlist:
noone: "&aNo one is banned on this cave"
settings:
description: "display cave settings"
# Admin commands
admin:
team:
add:
name-has-island: "&c[name] has a cave. Unregister or delete them first!"
setowner:
description: "transfers cave ownership to the player"
already-owner: "&cPlayer is already the owner of this cave!"
range:
description: "Admin cave range command"
display:
description: "Show/hide cave range indicators"
hint: |-
&cRed Barrier icons &fshow the current protected range limit.
&7Gray Particles &fshow the max limit.
&aGreen Particles &fshow the default protected range if the protection range differs from it.
set:
description: "Sets the cave protected range"
reset:
description: "Resets the protected range to the world default"
register:
parameters: "<player>"
description: "register player to unowned cave you are in"
registered-island: "&aRegistered player to cave at [xyz]."
already-owned: "&ccave is already owned by another player!"
no-island-here: "&cThere is no player cave here. Confirm to make one."
in-deletion: "&cThis space is currently being regenerated. Try later."
unregister:
description: "unregister owner from a cave, but keep cave blocks as-is"
unregistered-island: "&aUnregistered player from cave at [xyz]."
info:
parameters: "<player>"
description: "get info on where you are or on player"
no-island: "&cYou are not in a registered cave right now..."
title: "========== Cave Info ============"
owner: "Owner: [owner] ([uuid])"
last-login: "Last login: [date]"
deaths: "Deaths: [number]"
resets-left: "Resets: [number] (Max: [total])"
team-members-title: "Team members:"
team-owner-format: "&a[name] [rank]"
team-member-format: "&b[name] [rank]"
island-location: "Cave location: [xyz]"
island-coords: "Cave coordinates: [xz1] to [xz2]"
protection-range: "Protection range: [range]"
max-protection-range: "Largest historical protection range: [range]"
protection-coords: "Protection coordinates: [xz1] to [xz2]"
is-spawn: "Cave is a spawn cave"
banned-players: "Banned players:"
banned-format: "&c[name]"
unowned: "&cUnowned"
setrange:
description: "set the range of player's cave"
range-updated: "Cave range updated to [number]"
tp:
parameters: "<player>"
description: "teleport to a player's cave"
getrank:
description: "get a player's rank in their cave"
rank-is: "&aRank is [rank] in their cave."
setrank:
description: "set a player's rank in their cave"
setspawn:
description: "set a cave as spawn for this world"
already-spawn: "&cThis cave is already a spawn!"
no-island-here: "&cThere is no registered cave here."
confirmation: "&cAre you sure you want to set this cave as the spawn for this world?"
delete:
parameters: ""
description: "deletes a player and regenerates their cave"
cannot-delete-owner: "&cAll team members must be kicked before deleting."
deleted-island: "&aCave at &e[xyz] &ahas been successfully regenerated."
protection:
flags:
ELYTRA:
description: "Toggle use"
ENDERMAN_GRIEFING:
description: |-
&aEndermen can remove
&ablocks
ENTER_EXIT_MESSAGES:
description: "Display entry and exit messages"
island: "[name]'s protected cave"
name: "Enter/Exit messages"
now-entering: "&bNow entering [name]"
now-leaving: "&bNow leaving [name]"
GEO_LIMIT_MOBS:
description: |-
&aRemove mobs that go
&aoutside protected
&aplayer space
name: "&eLimit mobs to player cave"
ISLAND_RESPAWN:
description: |-
&aPlayers respawn
&ain their cave
name: "Cave respawn"
LOCK:
name: "Lock player cave"
OFFLINE_REDSTONE:
description: |-
&aWhen disabled, redstone
&awill not operate in caves
&awhere all members are offline.
&aMay help reduce lag.
PISTON_PUSH:
description: |-
&aAllow pistons to push
&ablocks outside a player's cave
PVP_OVERWORLD:
description: |-
&cEnable/Disable PVP
&cin protected cave.
REMOVE_MOBS:
description: |-
&aRemove monsters when
&ateleporting to a cave
PREVENT_TELEPORT_WHEN_FALLING:
description: |-
&aPrevent players from teleporting
&aif they are falling.
hint: "&cYou cannot teleport while you are falling!"
locked: "&cThis cave is locked!"
protected: "&ccave protected: [description]"
panel:
PROTECTION:
title: "&6Protection"
description: |-
&aProtection settings
&afor this cave
SETTING:
description: |-
&aGeneral settings
&afor this cave

View File

@ -578,7 +578,7 @@ blocks:
'24': '24':
==: org.bukkit.inventory.ItemStack ==: org.bukkit.inventory.ItemStack
v: 1631 v: 1631
type: BEETROOT type: beetroot_seeds
'26': '26':
==: org.bukkit.inventory.ItemStack ==: org.bukkit.inventory.ItemStack
v: 1631 v: 1631

File diff suppressed because it is too large Load Diff