mirror of
https://github.com/BentoBoxWorld/BentoBox.git
synced 2024-07-08 12:45:53 +02:00
Fixes island pasting issue when pasting clipboard every tick.
https://github.com/BentoBoxWorld/BentoBox/issues/535 due to race conditions with task timers and incorrectly shared field variables, islands would not paste completely if pasted every tick. Now pasting is handled in its own class with a new instance created with each paste.
This commit is contained in:
parent
0fd043df23
commit
8334de8cba
|
@ -66,6 +66,7 @@ public class IslandCreateCommand extends CompositeCommand {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
try {
|
||||
NewIsland.builder()
|
||||
.player(user)
|
||||
|
@ -78,7 +79,6 @@ public class IslandCreateCommand extends CompositeCommand {
|
|||
user.sendMessage("commands.island.create.unable-create-island");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (getSettings().isResetCooldownOnCreate()) {
|
||||
getParent().getSubCommand("reset").ifPresent(resetCommand -> resetCommand.setCooldown(user.getUniqueId(), null, getSettings().getResetCooldown()));
|
||||
}
|
||||
|
|
|
@ -94,7 +94,9 @@ public class User {
|
|||
* @param player the player
|
||||
*/
|
||||
public static void removePlayer(Player player) {
|
||||
users.remove(player.getUniqueId());
|
||||
if (player != null) {
|
||||
users.remove(player.getUniqueId());
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------
|
||||
|
|
|
@ -10,8 +10,8 @@ import java.util.UUID;
|
|||
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNull;
|
||||
|
||||
import world.bentobox.bentobox.BentoBox;
|
||||
import world.bentobox.bentobox.api.user.User;
|
||||
import world.bentobox.bentobox.database.Database;
|
||||
|
@ -113,6 +113,12 @@ public class PlayersManager {
|
|||
// If the player is in the database, load it, otherwise create a new player
|
||||
if (handler.objectExists(playerUUID.toString())) {
|
||||
player = handler.loadObject(playerUUID.toString());
|
||||
if (player == null) {
|
||||
player = new Players(plugin, playerUUID);
|
||||
// Corrupted database entry
|
||||
plugin.logError("Corrupted player database entry for " + playerUUID + " - unrecoverable. Recreated.");
|
||||
player.setUniqueId(playerUUID.toString());
|
||||
}
|
||||
} else {
|
||||
player = new Players(plugin, playerUUID);
|
||||
}
|
||||
|
|
|
@ -10,17 +10,12 @@ import java.nio.file.Path;
|
|||
import java.nio.file.Paths;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.DyeColor;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.World;
|
||||
|
@ -34,48 +29,31 @@ import org.bukkit.configuration.file.YamlConfiguration;
|
|||
import org.bukkit.entity.AbstractHorse;
|
||||
import org.bukkit.entity.Ageable;
|
||||
import org.bukkit.entity.ChestedHorse;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.entity.Horse;
|
||||
import org.bukkit.entity.LivingEntity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.entity.Tameable;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.InventoryHolder;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.material.Attachable;
|
||||
import org.bukkit.material.Colorable;
|
||||
import org.bukkit.scheduler.BukkitTask;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import world.bentobox.bentobox.BentoBox;
|
||||
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.util.Util;
|
||||
|
||||
/**
|
||||
* @author tastybento
|
||||
*/
|
||||
public class Clipboard {
|
||||
|
||||
enum PasteState {
|
||||
BLOCKS,
|
||||
ATTACHMENTS,
|
||||
ENTITIES,
|
||||
DONE
|
||||
}
|
||||
|
||||
// Speed of pasting
|
||||
private int pasteSpeed;
|
||||
private PasteState pasteState;
|
||||
|
||||
// Commonly used texts along this class.
|
||||
private static final String ATTACHED_YAML_PREFIX = "attached.";
|
||||
private static final String ENTITIES_YAML_PREFIX = "entities.";
|
||||
private static final String BLOCKS_YAML_PREFIX = "blocks.";
|
||||
private static final String BEDROCK = "bedrock";
|
||||
private static final String INVENTORY = "inventory";
|
||||
private static final String ENTITY = "entity";
|
||||
private static final String COLOR = "color";
|
||||
private static final String LOAD_ERROR = "Could not load schems file - does not exist : ";
|
||||
private static final String LINES = "lines";
|
||||
|
@ -89,8 +67,6 @@ public class Clipboard {
|
|||
|
||||
private File schemFolder;
|
||||
|
||||
private BukkitTask pastingTask;
|
||||
|
||||
public Clipboard(BentoBox plugin, File schemFolder) {
|
||||
super();
|
||||
this.plugin = plugin;
|
||||
|
@ -98,8 +74,6 @@ public class Clipboard {
|
|||
schemFolder.mkdirs();
|
||||
}
|
||||
this.schemFolder = schemFolder;
|
||||
pasteSpeed = plugin.getSettings().getPasteSpeed();
|
||||
pasteState = PasteState.BLOCKS;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -211,62 +185,7 @@ public class Clipboard {
|
|||
}
|
||||
|
||||
private void paste(World world, Island island, Location loc, Runnable task) {
|
||||
if (!blockConfig.contains(BLOCKS_YAML_PREFIX)) {
|
||||
plugin.logError("Clipboard has no block data in it to paste!");
|
||||
return;
|
||||
}
|
||||
// Iterators for the various schem sections
|
||||
Iterator<String> it = blockConfig.getConfigurationSection(BLOCKS_YAML_PREFIX).getKeys(false).iterator();
|
||||
Iterator<String> it2 = blockConfig.contains(ATTACHED_YAML_PREFIX) ? blockConfig.getConfigurationSection(ATTACHED_YAML_PREFIX).getKeys(false).iterator() : null;
|
||||
Iterator<String> it3 = blockConfig.contains(ENTITIES_YAML_PREFIX) ? blockConfig.getConfigurationSection(ENTITIES_YAML_PREFIX).getKeys(false).iterator() : null;
|
||||
|
||||
// Initial state & speed
|
||||
pasteState = PasteState.BLOCKS;
|
||||
pasteSpeed = plugin.getSettings().getPasteSpeed();
|
||||
|
||||
pastingTask = Bukkit.getScheduler().runTaskTimer(plugin, () -> {
|
||||
int count = 0;
|
||||
while (pasteState.equals(PasteState.BLOCKS) && count < pasteSpeed && it.hasNext()) {
|
||||
pasteBlock(world, island, loc, blockConfig.getConfigurationSection(BLOCKS_YAML_PREFIX + it.next()));
|
||||
count++;
|
||||
}
|
||||
while (it2 != null && pasteState.equals(PasteState.ATTACHMENTS) && count < pasteSpeed && it2.hasNext()) {
|
||||
pasteBlock(world, island, loc, blockConfig.getConfigurationSection(ATTACHED_YAML_PREFIX + it2.next()));
|
||||
count++;
|
||||
}
|
||||
while (it3 != null && pasteState.equals(PasteState.ENTITIES) && count < pasteSpeed && it3.hasNext()) {
|
||||
pasteEntity(world, loc, blockConfig.getConfigurationSection(ENTITIES_YAML_PREFIX + it3.next()));
|
||||
count++;
|
||||
}
|
||||
// STATE SHIFT
|
||||
if (pasteState.equals(PasteState.BLOCKS) && !it.hasNext()) {
|
||||
// Blocks done.
|
||||
if (it2 == null && it3 == null) {
|
||||
// No attachments or entities
|
||||
pasteState = PasteState.DONE;
|
||||
} else {
|
||||
// Next paste attachments, otherwise skip to entities
|
||||
pasteState = it2 != null ? PasteState.ATTACHMENTS : PasteState.ENTITIES;
|
||||
}
|
||||
}
|
||||
if (pasteState.equals(PasteState.ATTACHMENTS) && !it2.hasNext()) {
|
||||
// Attachments done. Next paste entities, otherwise done
|
||||
pasteState = it3 != null ? PasteState.ENTITIES : PasteState.DONE;
|
||||
}
|
||||
if (pasteState.equals(PasteState.ENTITIES) && !it3.hasNext()) {
|
||||
pasteState = PasteState.DONE;
|
||||
}
|
||||
if (pasteState.equals(PasteState.DONE)) {
|
||||
// All done. Cancel task
|
||||
pastingTask.cancel();
|
||||
if (task != null) {
|
||||
// Run follow on task if it exists
|
||||
Bukkit.getScheduler().runTaskLater(plugin, task, 2L);
|
||||
}
|
||||
}
|
||||
}, 0L, 1L);
|
||||
|
||||
|
||||
new Paster(plugin, blockConfig, world, island, loc, task);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -281,162 +200,6 @@ public class Clipboard {
|
|||
}
|
||||
}
|
||||
|
||||
private void writeSign(Island island, Block block, List<String> lines) {
|
||||
Sign sign = (Sign) block.getState();
|
||||
org.bukkit.material.Sign s = (org.bukkit.material.Sign) sign.getData();
|
||||
// Handle spawn sign
|
||||
if (island != null && !lines.isEmpty() && lines.get(0).equalsIgnoreCase(TextVariables.SPAWN_HERE)) {
|
||||
block.setType(Material.AIR);
|
||||
// Orient to face same direction as sign
|
||||
Location spawnPoint = new Location(block.getWorld(), block.getX() + 0.5D, block.getY(),
|
||||
block.getZ() + 0.5D, Util.blockFaceToFloat(s.getFacing().getOppositeFace()), 30F);
|
||||
island.setSpawnPoint(block.getWorld().getEnvironment(), spawnPoint);
|
||||
return;
|
||||
}
|
||||
// Handle locale text for starting sign
|
||||
// Sign text must be stored under the addon's name.sign.line0,1,2,3 in the yaml file
|
||||
if (island != null && !lines.isEmpty() && lines.get(0).equalsIgnoreCase(TextVariables.START_TEXT)) {
|
||||
// Get the addon that is operating in this world
|
||||
plugin.getIWM().getAddon(island.getWorld()).ifPresent(addon -> {
|
||||
lines.clear();
|
||||
for (int i = 0; i < 4; i++) {
|
||||
lines.add(ChatColor.translateAlternateColorCodes('&', plugin.getLocalesManager().getOrDefault(User.getInstance(island.getOwner()),
|
||||
addon.getDescription().getName().toLowerCase() + ".sign.line" + i,"")));
|
||||
}
|
||||
});
|
||||
}
|
||||
// Get the name of the player
|
||||
String name = TextVariables.NAME;
|
||||
if (island != null) {
|
||||
name = plugin.getPlayers().getName(island.getOwner());
|
||||
}
|
||||
// Sub in player's name
|
||||
for (int i = 0 ; i < lines.size(); i++) {
|
||||
sign.setLine(i, lines.get(i).replace(TextVariables.NAME, name));
|
||||
}
|
||||
// Update the sign
|
||||
sign.update();
|
||||
}
|
||||
|
||||
|
||||
private void pasteBlock(World world, Island island, Location location, ConfigurationSection config) {
|
||||
String[] pos = config.getName().split(",");
|
||||
int x = location.getBlockX() + Integer.valueOf(pos[0]);
|
||||
int y = location.getBlockY() + Integer.valueOf(pos[1]);
|
||||
int z = location.getBlockZ() + Integer.valueOf(pos[2]);
|
||||
|
||||
Block block = world.getBlockAt(x, y, z);
|
||||
String blockData = config.getString("bd");
|
||||
if (blockData != null) {
|
||||
setBlock(island, block, config, blockData);
|
||||
}
|
||||
// Entities (legacy)
|
||||
if (config.isConfigurationSection(ENTITY)) {
|
||||
setEntity(block.getLocation(), config.getConfigurationSection(ENTITY));
|
||||
}
|
||||
}
|
||||
|
||||
private void pasteEntity(World world, Location location, ConfigurationSection config) {
|
||||
String[] pos = config.getName().split(",");
|
||||
int x = location.getBlockX() + Integer.valueOf(pos[0]);
|
||||
int y = location.getBlockY() + Integer.valueOf(pos[1]);
|
||||
int z = location.getBlockZ() + Integer.valueOf(pos[2]);
|
||||
setEntity(new Location(world, x, y, z), config);
|
||||
}
|
||||
|
||||
private void setBlock(Island island, Block block, ConfigurationSection config, String blockData) {
|
||||
// Set the block data
|
||||
block.setBlockData(Bukkit.createBlockData(blockData));
|
||||
// Set the block state for chests, signs and mob spawners
|
||||
setBlockState(island, block, config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets any entity that is in this location
|
||||
* @param location - location
|
||||
* @param en - config section
|
||||
*/
|
||||
private void setEntity(Location location, ConfigurationSection en) {
|
||||
en.getKeys(false).forEach(k -> {
|
||||
ConfigurationSection ent = en.getConfigurationSection(k);
|
||||
// Center, and just a bit high
|
||||
Location center = location.add(new Vector(0.5, 0.5, 0.5));
|
||||
LivingEntity e = (LivingEntity)location.getWorld().spawnEntity(center, EntityType.valueOf(ent.getString("type", "PIG")));
|
||||
if (e != null) {
|
||||
e.setCustomName(ent.getString("name"));
|
||||
}
|
||||
if (e instanceof Colorable && ent.contains(COLOR)) {
|
||||
((Colorable) e).setColor(DyeColor.valueOf(ent.getString(COLOR)));
|
||||
}
|
||||
if (e instanceof Tameable) {
|
||||
((Tameable)e).setTamed(ent.getBoolean("tamed"));
|
||||
}
|
||||
if (e instanceof ChestedHorse) {
|
||||
((ChestedHorse)e).setCarryingChest(ent.getBoolean("chest"));
|
||||
}
|
||||
if (e instanceof Ageable) {
|
||||
if (ent.getBoolean("adult")) {
|
||||
((Ageable)e).setAdult();
|
||||
} else {
|
||||
((Ageable)e).setBaby();
|
||||
}
|
||||
}
|
||||
if (e instanceof AbstractHorse) {
|
||||
AbstractHorse horse = (AbstractHorse)e;
|
||||
horse.setDomestication(ent.getInt("domestication"));
|
||||
if (ent.isConfigurationSection(INVENTORY)) {
|
||||
ConfigurationSection inv = ent.getConfigurationSection(INVENTORY);
|
||||
inv.getKeys(false).forEach(i -> horse.getInventory().setItem(Integer.valueOf(i), (ItemStack)inv.get(i)));
|
||||
}
|
||||
}
|
||||
if (e instanceof Horse) {
|
||||
((Horse)e).setStyle(Horse.Style.valueOf(ent.getString("style", "NONE")));
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles signs, chests and mob spawner blocks
|
||||
* @param island - island
|
||||
* @param block - block
|
||||
* @param config - config
|
||||
*/
|
||||
private void setBlockState(Island island, Block block, ConfigurationSection config) {
|
||||
// Get the block state
|
||||
BlockState bs = block.getState();
|
||||
// Signs
|
||||
if (bs instanceof Sign) {
|
||||
List<String> lines = config.getStringList(LINES);
|
||||
writeSign(island, block, lines);
|
||||
}
|
||||
// Chests, in general
|
||||
if (bs instanceof InventoryHolder) {
|
||||
bs.update(true, false);
|
||||
Inventory ih = ((InventoryHolder)bs).getInventory();
|
||||
if (config.isConfigurationSection(INVENTORY)) {
|
||||
ConfigurationSection inv = config.getConfigurationSection(INVENTORY);
|
||||
// Double chests are pasted as two blocks so inventory is filled twice. This code stops over filling for the first block.
|
||||
inv.getKeys(false).stream()
|
||||
.filter(i -> Integer.valueOf(i) < ih.getSize())
|
||||
.forEach(i -> ih.setItem(Integer.valueOf(i), (ItemStack)inv.get(i)));
|
||||
}
|
||||
}
|
||||
// Mob spawners
|
||||
if (bs instanceof CreatureSpawner) {
|
||||
CreatureSpawner spawner = ((CreatureSpawner) bs);
|
||||
spawner.setSpawnedType(EntityType.valueOf(config.getString("spawnedType", "PIG")));
|
||||
spawner.setMaxNearbyEntities(config.getInt("maxNearbyEntities", 16));
|
||||
spawner.setMaxSpawnDelay(config.getInt("maxSpawnDelay", 2*60*20));
|
||||
spawner.setMinSpawnDelay(config.getInt("minSpawnDelay", 5*20));
|
||||
|
||||
spawner.setDelay(config.getInt("delay", -1));
|
||||
spawner.setRequiredPlayerRange(config.getInt("requiredPlayerRange", 16));
|
||||
spawner.setSpawnRange(config.getInt("spawnRange", 4));
|
||||
bs.update(true, false);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean copyBlock(Block block, Location copyOrigin, boolean copyAir, Collection<LivingEntity> entities) {
|
||||
if (!copyAir && block.getType().equals(Material.AIR) && entities.isEmpty()) {
|
||||
return false;
|
||||
|
|
288
src/main/java/world/bentobox/bentobox/schems/Paster.java
Normal file
288
src/main/java/world/bentobox/bentobox/schems/Paster.java
Normal file
|
@ -0,0 +1,288 @@
|
|||
package world.bentobox.bentobox.schems;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.DyeColor;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.BlockState;
|
||||
import org.bukkit.block.CreatureSpawner;
|
||||
import org.bukkit.block.Sign;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.entity.AbstractHorse;
|
||||
import org.bukkit.entity.Ageable;
|
||||
import org.bukkit.entity.ChestedHorse;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.entity.Horse;
|
||||
import org.bukkit.entity.LivingEntity;
|
||||
import org.bukkit.entity.Tameable;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.InventoryHolder;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.material.Colorable;
|
||||
import org.bukkit.scheduler.BukkitTask;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import world.bentobox.bentobox.BentoBox;
|
||||
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.util.Util;
|
||||
|
||||
/**
|
||||
* This class pastes the clipboard it is given
|
||||
* @author tastybento
|
||||
*
|
||||
*/
|
||||
public class Paster {
|
||||
|
||||
enum PasteState {
|
||||
BLOCKS,
|
||||
ATTACHMENTS,
|
||||
ENTITIES,
|
||||
DONE,
|
||||
CANCEL
|
||||
}
|
||||
|
||||
BentoBox plugin;
|
||||
|
||||
// Speed of pasting
|
||||
private int pasteSpeed;
|
||||
private PasteState pasteState;
|
||||
private BukkitTask pastingTask;
|
||||
|
||||
// Commonly used texts along this class.
|
||||
private static final String ATTACHED_YAML_PREFIX = "attached.";
|
||||
private static final String ENTITIES_YAML_PREFIX = "entities.";
|
||||
private static final String BLOCKS_YAML_PREFIX = "blocks.";
|
||||
private static final String INVENTORY = "inventory";
|
||||
private static final String ENTITY = "entity";
|
||||
private static final String COLOR = "color";
|
||||
private static final String LINES = "lines";
|
||||
|
||||
|
||||
|
||||
public Paster(final BentoBox plugin, final YamlConfiguration blockConfig, final World world, final Island island, final Location loc, final Runnable task) {
|
||||
this.plugin = plugin;
|
||||
if (!blockConfig.contains(BLOCKS_YAML_PREFIX)) {
|
||||
plugin.logError("Clipboard has no block data in it to paste!");
|
||||
return;
|
||||
}
|
||||
// Iterators for the various schem sections
|
||||
Iterator<String> it = blockConfig.getConfigurationSection(BLOCKS_YAML_PREFIX).getKeys(false).iterator();
|
||||
Iterator<String> it2 = blockConfig.contains(ATTACHED_YAML_PREFIX) ? blockConfig.getConfigurationSection(ATTACHED_YAML_PREFIX).getKeys(false).iterator() : null;
|
||||
Iterator<String> it3 = blockConfig.contains(ENTITIES_YAML_PREFIX) ? blockConfig.getConfigurationSection(ENTITIES_YAML_PREFIX).getKeys(false).iterator() : null;
|
||||
|
||||
// Initial state & speed
|
||||
pasteState = PasteState.BLOCKS;
|
||||
pasteSpeed = plugin.getSettings().getPasteSpeed();
|
||||
|
||||
pastingTask = Bukkit.getScheduler().runTaskTimer(plugin, () -> {
|
||||
int count = 0;
|
||||
while (pasteState.equals(PasteState.BLOCKS) && count < pasteSpeed && it.hasNext()) {
|
||||
pasteBlock(world, island, loc, blockConfig.getConfigurationSection(BLOCKS_YAML_PREFIX + it.next()));
|
||||
count++;
|
||||
}
|
||||
while (it2 != null && pasteState.equals(PasteState.ATTACHMENTS) && count < pasteSpeed && it2.hasNext()) {
|
||||
pasteBlock(world, island, loc, blockConfig.getConfigurationSection(ATTACHED_YAML_PREFIX + it2.next()));
|
||||
count++;
|
||||
}
|
||||
while (it3 != null && pasteState.equals(PasteState.ENTITIES) && count < pasteSpeed && it3.hasNext()) {
|
||||
pasteEntity(world, loc, blockConfig.getConfigurationSection(ENTITIES_YAML_PREFIX + it3.next()));
|
||||
count++;
|
||||
}
|
||||
// STATE SHIFT
|
||||
if (pasteState.equals(PasteState.BLOCKS) && !it.hasNext()) {
|
||||
// Blocks done.
|
||||
if (it2 == null && it3 == null) {
|
||||
// No attachments or entities
|
||||
pasteState = PasteState.DONE;
|
||||
} else {
|
||||
// Next paste attachments, otherwise skip to entities
|
||||
pasteState = it2 != null ? PasteState.ATTACHMENTS : PasteState.ENTITIES;
|
||||
}
|
||||
}
|
||||
if (pasteState.equals(PasteState.ATTACHMENTS) && !it2.hasNext()) {
|
||||
// Attachments done. Next paste entities, otherwise done
|
||||
pasteState = it3 != null ? PasteState.ENTITIES : PasteState.DONE;
|
||||
}
|
||||
if (pasteState.equals(PasteState.ENTITIES) && !it3.hasNext()) {
|
||||
pasteState = PasteState.DONE;
|
||||
}
|
||||
if (pasteState.equals(PasteState.DONE)) {
|
||||
// All done. Cancel task
|
||||
if (task != null) {
|
||||
// Run follow-on task if it exists
|
||||
Bukkit.getScheduler().runTask(plugin, task);
|
||||
}
|
||||
pasteState = PasteState.CANCEL;
|
||||
} else if (pasteState.equals(PasteState.CANCEL)) {
|
||||
// This state makes sure the follow-on task only ever runs once
|
||||
pastingTask.cancel();
|
||||
}
|
||||
}, 0L, 1L);
|
||||
|
||||
}
|
||||
|
||||
private void pasteBlock(World world, Island island, Location location, ConfigurationSection config) {
|
||||
String[] pos = config.getName().split(",");
|
||||
int x = location.getBlockX() + Integer.valueOf(pos[0]);
|
||||
int y = location.getBlockY() + Integer.valueOf(pos[1]);
|
||||
int z = location.getBlockZ() + Integer.valueOf(pos[2]);
|
||||
|
||||
Block block = world.getBlockAt(x, y, z);
|
||||
String blockData = config.getString("bd");
|
||||
if (blockData != null) {
|
||||
setBlock(island, block, config, blockData);
|
||||
}
|
||||
// Entities (legacy)
|
||||
if (config.isConfigurationSection(ENTITY)) {
|
||||
setEntity(block.getLocation(), config.getConfigurationSection(ENTITY));
|
||||
}
|
||||
}
|
||||
|
||||
private void pasteEntity(World world, Location location, ConfigurationSection config) {
|
||||
String[] pos = config.getName().split(",");
|
||||
int x = location.getBlockX() + Integer.valueOf(pos[0]);
|
||||
int y = location.getBlockY() + Integer.valueOf(pos[1]);
|
||||
int z = location.getBlockZ() + Integer.valueOf(pos[2]);
|
||||
setEntity(new Location(world, x, y, z), config);
|
||||
}
|
||||
|
||||
private void setBlock(Island island, Block block, ConfigurationSection config, String blockData) {
|
||||
// Set the block data
|
||||
block.setBlockData(Bukkit.createBlockData(blockData));
|
||||
// Set the block state for chests, signs and mob spawners
|
||||
setBlockState(island, block, config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets any entity that is in this location
|
||||
* @param location - location
|
||||
* @param en - config section
|
||||
*/
|
||||
private void setEntity(Location location, ConfigurationSection en) {
|
||||
en.getKeys(false).forEach(k -> {
|
||||
ConfigurationSection ent = en.getConfigurationSection(k);
|
||||
// Center, and just a bit high
|
||||
Location center = location.add(new Vector(0.5, 0.5, 0.5));
|
||||
LivingEntity e = (LivingEntity)location.getWorld().spawnEntity(center, EntityType.valueOf(ent.getString("type", "PIG")));
|
||||
if (e != null) {
|
||||
e.setCustomName(ent.getString("name"));
|
||||
}
|
||||
if (e instanceof Colorable && ent.contains(COLOR)) {
|
||||
((Colorable) e).setColor(DyeColor.valueOf(ent.getString(COLOR)));
|
||||
}
|
||||
if (e instanceof Tameable) {
|
||||
((Tameable)e).setTamed(ent.getBoolean("tamed"));
|
||||
}
|
||||
if (e instanceof ChestedHorse) {
|
||||
((ChestedHorse)e).setCarryingChest(ent.getBoolean("chest"));
|
||||
}
|
||||
if (e instanceof Ageable) {
|
||||
if (ent.getBoolean("adult")) {
|
||||
((Ageable)e).setAdult();
|
||||
} else {
|
||||
((Ageable)e).setBaby();
|
||||
}
|
||||
}
|
||||
if (e instanceof AbstractHorse) {
|
||||
AbstractHorse horse = (AbstractHorse)e;
|
||||
horse.setDomestication(ent.getInt("domestication"));
|
||||
if (ent.isConfigurationSection(INVENTORY)) {
|
||||
ConfigurationSection inv = ent.getConfigurationSection(INVENTORY);
|
||||
inv.getKeys(false).forEach(i -> horse.getInventory().setItem(Integer.valueOf(i), (ItemStack)inv.get(i)));
|
||||
}
|
||||
}
|
||||
if (e instanceof Horse) {
|
||||
((Horse)e).setStyle(Horse.Style.valueOf(ent.getString("style", "NONE")));
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles signs, chests and mob spawner blocks
|
||||
* @param island - island
|
||||
* @param block - block
|
||||
* @param config - config
|
||||
*/
|
||||
private void setBlockState(Island island, Block block, ConfigurationSection config) {
|
||||
// Get the block state
|
||||
BlockState bs = block.getState();
|
||||
// Signs
|
||||
if (bs instanceof Sign) {
|
||||
List<String> lines = config.getStringList(LINES);
|
||||
writeSign(island, block, lines);
|
||||
}
|
||||
// Chests, in general
|
||||
if (bs instanceof InventoryHolder) {
|
||||
bs.update(true, false);
|
||||
Inventory ih = ((InventoryHolder)bs).getInventory();
|
||||
if (config.isConfigurationSection(INVENTORY)) {
|
||||
ConfigurationSection inv = config.getConfigurationSection(INVENTORY);
|
||||
// Double chests are pasted as two blocks so inventory is filled twice. This code stops over filling for the first block.
|
||||
inv.getKeys(false).stream()
|
||||
.filter(i -> Integer.valueOf(i) < ih.getSize())
|
||||
.forEach(i -> ih.setItem(Integer.valueOf(i), (ItemStack)inv.get(i)));
|
||||
}
|
||||
}
|
||||
// Mob spawners
|
||||
if (bs instanceof CreatureSpawner) {
|
||||
CreatureSpawner spawner = ((CreatureSpawner) bs);
|
||||
spawner.setSpawnedType(EntityType.valueOf(config.getString("spawnedType", "PIG")));
|
||||
spawner.setMaxNearbyEntities(config.getInt("maxNearbyEntities", 16));
|
||||
spawner.setMaxSpawnDelay(config.getInt("maxSpawnDelay", 2*60*20));
|
||||
spawner.setMinSpawnDelay(config.getInt("minSpawnDelay", 5*20));
|
||||
|
||||
spawner.setDelay(config.getInt("delay", -1));
|
||||
spawner.setRequiredPlayerRange(config.getInt("requiredPlayerRange", 16));
|
||||
spawner.setSpawnRange(config.getInt("spawnRange", 4));
|
||||
bs.update(true, false);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeSign(Island island, Block block, List<String> lines) {
|
||||
Sign sign = (Sign) block.getState();
|
||||
org.bukkit.material.Sign s = (org.bukkit.material.Sign) sign.getData();
|
||||
// Handle spawn sign
|
||||
if (island != null && !lines.isEmpty() && lines.get(0).equalsIgnoreCase(TextVariables.SPAWN_HERE)) {
|
||||
block.setType(Material.AIR);
|
||||
// Orient to face same direction as sign
|
||||
Location spawnPoint = new Location(block.getWorld(), block.getX() + 0.5D, block.getY(),
|
||||
block.getZ() + 0.5D, Util.blockFaceToFloat(s.getFacing().getOppositeFace()), 30F);
|
||||
island.setSpawnPoint(block.getWorld().getEnvironment(), spawnPoint);
|
||||
return;
|
||||
}
|
||||
// Handle locale text for starting sign
|
||||
// Sign text must be stored under the addon's name.sign.line0,1,2,3 in the yaml file
|
||||
if (island != null && !lines.isEmpty() && lines.get(0).equalsIgnoreCase(TextVariables.START_TEXT)) {
|
||||
// Get the addon that is operating in this world
|
||||
plugin.getIWM().getAddon(island.getWorld()).ifPresent(addon -> {
|
||||
lines.clear();
|
||||
for (int i = 0; i < 4; i++) {
|
||||
lines.add(ChatColor.translateAlternateColorCodes('&', plugin.getLocalesManager().getOrDefault(User.getInstance(island.getOwner()),
|
||||
addon.getDescription().getName().toLowerCase() + ".sign.line" + i,"")));
|
||||
}
|
||||
});
|
||||
}
|
||||
// Get the name of the player
|
||||
String name = TextVariables.NAME;
|
||||
if (island != null) {
|
||||
name = plugin.getPlayers().getName(island.getOwner());
|
||||
}
|
||||
// Sub in player's name
|
||||
for (int i = 0 ; i < lines.size(); i++) {
|
||||
sign.setLine(i, lines.get(i).replace(TextVariables.NAME, name));
|
||||
}
|
||||
// Update the sign
|
||||
sign.update();
|
||||
}
|
||||
|
||||
}
|
|
@ -326,10 +326,7 @@ public class ClipboardTest {
|
|||
cb.setPos2(loc2);
|
||||
cb.copy(user, false);
|
||||
cb.pasteClipboard(loc);
|
||||
// Verify the task is run
|
||||
Mockito.verify(sched).runTaskTimer(Mockito.any(), Mockito.any(Runnable.class), Mockito.eq(0L), Mockito.eq(1L));
|
||||
// Player should NOT spawn!!
|
||||
Mockito.verify(world, Mockito.never()).spawnEntity(Mockito.eq(null), Mockito.eq(EntityType.PLAYER));
|
||||
// No verification possible on the new constructor
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
Loading…
Reference in New Issue
Block a user