mirror of
https://github.com/BentoBoxWorld/BentoBox.git
synced 2025-01-10 02:19:30 +01:00
More abstract on Blueprint Paster (#1970)
* more abstract on Paster * comment and stuff * How about Impl * ok, PasteHandler * PasteUtil * don't check other BlockState if there is one matched * world as an argument * forgot the impl * createBlockData from BlueprintBlock * fix remaining conflicts Co-authored-by: tastybento <tastybento@users.noreply.github.com>
This commit is contained in:
parent
4ab579f2cd
commit
0f815d8175
@ -1,49 +1,27 @@
|
||||
package world.bentobox.bentobox.blueprints;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Banner;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.BlockFace;
|
||||
import org.bukkit.block.BlockState;
|
||||
import org.bukkit.block.CreatureSpawner;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.block.data.type.Sign;
|
||||
import org.bukkit.block.data.type.WallSign;
|
||||
import org.bukkit.entity.LivingEntity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.InventoryHolder;
|
||||
import org.bukkit.scheduler.BukkitTask;
|
||||
import org.bukkit.util.Vector;
|
||||
import org.eclipse.jdt.annotation.NonNull;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
import world.bentobox.bentobox.BentoBox;
|
||||
import world.bentobox.bentobox.api.localization.TextVariables;
|
||||
import world.bentobox.bentobox.api.user.User;
|
||||
import world.bentobox.bentobox.blueprints.dataobjects.BlueprintBlock;
|
||||
import world.bentobox.bentobox.blueprints.dataobjects.BlueprintCreatureSpawner;
|
||||
import world.bentobox.bentobox.blueprints.dataobjects.BlueprintEntity;
|
||||
import world.bentobox.bentobox.database.objects.Island;
|
||||
import world.bentobox.bentobox.nms.PasteHandler;
|
||||
import world.bentobox.bentobox.util.Util;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
* This class pastes the clipboard it is given
|
||||
* @author tastybento
|
||||
@ -68,11 +46,9 @@ public class BlueprintPaster {
|
||||
*/
|
||||
private static long chunkLoadTime = 0;
|
||||
|
||||
private static final String MINECRAFT = "minecraft:";
|
||||
|
||||
private static final Map<String, String> BLOCK_CONVERSION = ImmutableMap.of("sign", "oak_sign", "wall_sign", "oak_wall_sign");
|
||||
|
||||
private final BentoBox plugin;
|
||||
private final PasteHandler paster = Util.getPasteHandler();
|
||||
private final World world;
|
||||
// The minimum block position (x,y,z)
|
||||
private Location pos1;
|
||||
// The maximum block position (x,y,z)
|
||||
@ -80,6 +56,7 @@ public class BlueprintPaster {
|
||||
private PasteState pasteState;
|
||||
private BukkitTask pastingTask;
|
||||
private BlueprintClipboard clipboard;
|
||||
private CompletableFuture<Void> currentTask = CompletableFuture.completedFuture(null);
|
||||
|
||||
/**
|
||||
* The Blueprint to paste.
|
||||
@ -111,6 +88,7 @@ public class BlueprintPaster {
|
||||
// Calculate location for pasting
|
||||
this.blueprint = Objects.requireNonNull(clipboard.getBlueprint(), "Clipboard cannot have a null Blueprint");
|
||||
this.location = location;
|
||||
this.world = location.getWorld();
|
||||
this.island = null;
|
||||
|
||||
// Paste
|
||||
@ -128,6 +106,7 @@ public class BlueprintPaster {
|
||||
this.plugin = plugin;
|
||||
this.blueprint = bp;
|
||||
this.island = island;
|
||||
this.world = world;
|
||||
// Offset due to bedrock
|
||||
Vector off = bp.getBedrock() != null ? bp.getBedrock() : new Vector(0,0,0);
|
||||
// Calculate location for pasting
|
||||
@ -157,7 +136,8 @@ public class BlueprintPaster {
|
||||
// If this is an island OVERWORLD paste, get the island owner.
|
||||
final Optional<User> owner = Optional.ofNullable(island)
|
||||
.filter(i -> location.getWorld().getEnvironment().equals(World.Environment.NORMAL))
|
||||
.map(i -> User.getInstance(i.getOwner()));
|
||||
.map(Island::getOwner)
|
||||
.map(User::getInstance);
|
||||
// Tell the owner we're pasting blocks and how much time it might take
|
||||
owner.ifPresent(user -> tellOwner(user, blocks.size(), attached.size(), entities.size(), plugin.getSettings().getPasteSpeed()));
|
||||
Bits bits = new Bits(blocks, attached, entities,
|
||||
@ -169,6 +149,8 @@ public class BlueprintPaster {
|
||||
}
|
||||
|
||||
private void pasterTask(CompletableFuture<Boolean> result, Optional<User> owner, Bits bits) {
|
||||
if (!currentTask.isDone()) return;
|
||||
|
||||
final int pasteSpeed = plugin.getSettings().getPasteSpeed();
|
||||
|
||||
long timer = System.currentTimeMillis();
|
||||
@ -176,7 +158,7 @@ public class BlueprintPaster {
|
||||
if (pasteState.equals(PasteState.CHUNK_LOAD)) {
|
||||
pasteState = PasteState.CHUNK_LOADING;
|
||||
// Load chunk
|
||||
Util.getChunkAtAsync(location).thenRun(() -> {
|
||||
currentTask = Util.getChunkAtAsync(location).thenRun(() -> {
|
||||
pasteState = PasteState.BLOCKS;
|
||||
long duration = System.currentTimeMillis() - timer;
|
||||
if (duration > chunkLoadTime) {
|
||||
@ -184,34 +166,65 @@ public class BlueprintPaster {
|
||||
}
|
||||
});
|
||||
}
|
||||
while (pasteState.equals(PasteState.BLOCKS) && count < pasteSpeed && bits.it.hasNext()) {
|
||||
pasteBlock(location, bits.it.next());
|
||||
count++;
|
||||
}
|
||||
while (pasteState.equals(PasteState.ATTACHMENTS) && count < pasteSpeed && bits.it2.hasNext()) {
|
||||
pasteBlock(location, bits.it2.next());
|
||||
count++;
|
||||
}
|
||||
while (pasteState.equals(PasteState.ENTITIES) && count < pasteSpeed && bits.it3.hasNext()) {
|
||||
pasteEntity(location, bits.it3.next());
|
||||
count++;
|
||||
}
|
||||
// STATE SHIFT
|
||||
if (pasteState.equals(PasteState.BLOCKS) && !bits.it.hasNext()) {
|
||||
// Blocks done
|
||||
// Next paste attachments
|
||||
pasteState = PasteState.ATTACHMENTS;
|
||||
}
|
||||
else if (pasteState.equals(PasteState.ATTACHMENTS) && !bits.it2.hasNext()) {
|
||||
// Attachments done. Next paste entities
|
||||
pasteState = PasteState.ENTITIES;
|
||||
if (bits.entities.size() != 0) {
|
||||
owner.ifPresent(user -> user.sendMessage("commands.island.create.pasting.entities", TextVariables.NUMBER, String.valueOf(bits.entities.size())));
|
||||
else if (pasteState.equals(PasteState.BLOCKS) || pasteState.equals(PasteState.ATTACHMENTS)) {
|
||||
Iterator<Entry<Vector, BlueprintBlock>> it = pasteState.equals(PasteState.BLOCKS) ? bits.it : bits.it2;
|
||||
if (it.hasNext()) {
|
||||
Map<Location, BlueprintBlock> blockMap = new HashMap<>();
|
||||
// Paste blocks
|
||||
while (count < pasteSpeed) {
|
||||
if (!it.hasNext()) {
|
||||
break;
|
||||
}
|
||||
Entry<Vector, BlueprintBlock> entry = it.next();
|
||||
Location pasteTo = location.clone().add(entry.getKey());
|
||||
// pos1 and pos2 update
|
||||
updatePos(pasteTo);
|
||||
|
||||
BlueprintBlock block = entry.getValue();
|
||||
blockMap.put(pasteTo, block);
|
||||
count++;
|
||||
}
|
||||
if (!blockMap.isEmpty()) {
|
||||
currentTask = paster.pasteBlocks(island, world, blockMap);
|
||||
}
|
||||
} else {
|
||||
if (pasteState.equals(PasteState.BLOCKS)) {
|
||||
// Blocks done
|
||||
// Next paste attachments
|
||||
pasteState = PasteState.ATTACHMENTS;
|
||||
} else {
|
||||
// Attachments done. Next paste entities
|
||||
pasteState = PasteState.ENTITIES;
|
||||
if (bits.entities.size() != 0) {
|
||||
owner.ifPresent(user -> user.sendMessage("commands.island.create.pasting.entities", TextVariables.NUMBER, String.valueOf(bits.entities.size())));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (pasteState.equals(PasteState.ENTITIES) && !bits.it3.hasNext()) {
|
||||
pasteState = PasteState.DONE;
|
||||
owner.ifPresent(user -> user.sendMessage("commands.island.create.pasting.done"));
|
||||
else if (pasteState.equals(PasteState.ENTITIES)) {
|
||||
if (bits.it3().hasNext()) {
|
||||
Map<Location, List<BlueprintEntity>> entityMap = new HashMap<>();
|
||||
// Paste entities
|
||||
while (count < pasteSpeed) {
|
||||
if (!bits.it3().hasNext()) {
|
||||
break;
|
||||
}
|
||||
Entry<Vector, List<BlueprintEntity>> entry = bits.it3().next();
|
||||
int x = location.getBlockX() + entry.getKey().getBlockX();
|
||||
int y = location.getBlockY() + entry.getKey().getBlockY();
|
||||
int z = location.getBlockZ() + entry.getKey().getBlockZ();
|
||||
Location center = new Location(world, x, y, z).add(new Vector(0.5, 0.5, 0.5));
|
||||
List<BlueprintEntity> entities = entry.getValue();
|
||||
entityMap.put(center, entities);
|
||||
count++;
|
||||
}
|
||||
if (!entityMap.isEmpty()) {
|
||||
currentTask = paster.pasteEntities(island, world, entityMap);
|
||||
}
|
||||
} else {
|
||||
pasteState = PasteState.DONE;
|
||||
owner.ifPresent(user -> user.sendMessage("commands.island.create.pasting.done"));
|
||||
}
|
||||
}
|
||||
else if (pasteState.equals(PasteState.DONE)) {
|
||||
// All done. Cancel task
|
||||
@ -238,135 +251,6 @@ public class BlueprintPaster {
|
||||
user.sendMessage("commands.island.create.pasting.blocks", TextVariables.NUMBER, String.valueOf(blocksSize + attachedSize));
|
||||
}
|
||||
|
||||
private void pasteBlock(Location location, Entry<Vector, BlueprintBlock> entry) {
|
||||
World world = location.getWorld();
|
||||
Location pasteTo = location.clone().add(entry.getKey());
|
||||
BlueprintBlock bpBlock = entry.getValue();
|
||||
Util.getChunkAtAsync(pasteTo).thenRun(() -> {
|
||||
Block block = pasteTo.getBlock();
|
||||
// Set the block data - default is AIR
|
||||
BlockData bd;
|
||||
try {
|
||||
bd = Bukkit.createBlockData(bpBlock.getBlockData());
|
||||
} catch (Exception e) {
|
||||
bd = convertBlockData(world, bpBlock);
|
||||
}
|
||||
block.setBlockData(bd, false);
|
||||
setBlockState(block, bpBlock);
|
||||
// Set biome
|
||||
if (bpBlock.getBiome() != null) {
|
||||
block.setBiome(bpBlock.getBiome());
|
||||
}
|
||||
// pos1 and pos2 update
|
||||
updatePos(block.getLocation());
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to convert the BlockData to a newer version, and logs a warning if it fails to do so.
|
||||
* @return the converted BlockData or a default AIR BlockData.
|
||||
* @since 1.6.0
|
||||
*/
|
||||
private BlockData convertBlockData(World world, BlueprintBlock block) {
|
||||
BlockData blockData = Bukkit.createBlockData(Material.AIR);
|
||||
try {
|
||||
for (Entry<String, String> en : BLOCK_CONVERSION.entrySet()) {
|
||||
if (block.getBlockData().startsWith(MINECRAFT + en.getKey())) {
|
||||
blockData = Bukkit.createBlockData(block.getBlockData().replace(MINECRAFT + en.getKey(), MINECRAFT + en.getValue()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
// This may happen if the block type is no longer supported by the server
|
||||
plugin.logWarning("Blueprint references materials not supported on this server version.");
|
||||
plugin.logWarning("Load blueprint manually, check and save to fix for this server version.");
|
||||
plugin.logWarning("World: " + world.getName() + "; Failed block data: " + block.getBlockData());
|
||||
}
|
||||
return blockData;
|
||||
}
|
||||
|
||||
private void pasteEntity(Location location, Entry<Vector, List<BlueprintEntity>> entry) {
|
||||
int x = location.getBlockX() + entry.getKey().getBlockX();
|
||||
int y = location.getBlockY() + entry.getKey().getBlockY();
|
||||
int z = location.getBlockZ() + entry.getKey().getBlockZ();
|
||||
setEntity(new Location(location.getWorld(), x, y, z), entry.getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles signs, chests and mob spawner blocks
|
||||
* @param block - block
|
||||
* @param bpBlock - config
|
||||
*/
|
||||
private void setBlockState(Block block, BlueprintBlock bpBlock) {
|
||||
// Get the block state
|
||||
BlockState bs = block.getState();
|
||||
// Signs
|
||||
if (bs instanceof org.bukkit.block.Sign sign) {
|
||||
writeSign(block, bpBlock.getSignLines(), bpBlock.isGlowingText());
|
||||
}
|
||||
// Chests, in general
|
||||
if (bs instanceof InventoryHolder holder) {
|
||||
Inventory ih = holder.getInventory();
|
||||
// Double chests are pasted as two blocks so inventory is filled twice.
|
||||
// This code stops over-filling for the first block.
|
||||
bpBlock.getInventory().forEach(ih::setItem);
|
||||
}
|
||||
// Mob spawners
|
||||
if (bs instanceof CreatureSpawner spawner) {
|
||||
setSpawner(spawner, bpBlock.getCreatureSpawner());
|
||||
}
|
||||
// Banners
|
||||
if (bs instanceof Banner banner && bpBlock.getBannerPatterns() != null) {
|
||||
bpBlock.getBannerPatterns().removeIf(Objects::isNull);
|
||||
banner.setPatterns(bpBlock.getBannerPatterns());
|
||||
banner.update(true, false);
|
||||
}
|
||||
}
|
||||
|
||||
private void setSpawner(CreatureSpawner spawner, BlueprintCreatureSpawner s) {
|
||||
spawner.setSpawnedType(s.getSpawnedType());
|
||||
spawner.setMaxNearbyEntities(s.getMaxNearbyEntities());
|
||||
spawner.setMaxSpawnDelay(s.getMaxSpawnDelay());
|
||||
spawner.setMinSpawnDelay(s.getMinSpawnDelay());
|
||||
spawner.setDelay(s.getDelay());
|
||||
spawner.setRequiredPlayerRange(s.getRequiredPlayerRange());
|
||||
spawner.setSpawnRange(s.getSpawnRange());
|
||||
spawner.update(true, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets any entity that is in this location
|
||||
* @param location - location
|
||||
* @param list - list of entities to paste
|
||||
*/
|
||||
private void setEntity(Location location, List<BlueprintEntity> list) {
|
||||
list.stream().filter(k -> k.getType() != null).forEach(k -> {
|
||||
// Center, and just a bit high
|
||||
Location center = location.add(new Vector(0.5, 0.5, 0.5));
|
||||
Util.getChunkAtAsync(center).thenRun(() -> {
|
||||
LivingEntity e = (LivingEntity)location.getWorld().spawnEntity(center, k.getType());
|
||||
if (k.getCustomName() != null) {
|
||||
String customName = k.getCustomName();
|
||||
|
||||
if (island != null) {
|
||||
// Parse any placeholders in the entity's name, if the owner's connected (he should)
|
||||
Player owner = User.getInstance(island.getOwner()).getPlayer();
|
||||
if (owner != null) {
|
||||
// Parse for the player's name first (in case placeholders might need it)
|
||||
customName = customName.replace(TextVariables.NAME, owner.getName());
|
||||
// Now parse the placeholders
|
||||
customName = plugin.getPlaceholdersManager().replacePlaceholders(owner, customName);
|
||||
}
|
||||
}
|
||||
|
||||
// Actually set the custom name
|
||||
e.setCustomName(customName);
|
||||
}
|
||||
k.configureEntity(e);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Tracks the minimum and maximum block positions
|
||||
* @param l - location of block pasted
|
||||
@ -397,50 +281,4 @@ public class BlueprintPaster {
|
||||
pos2.setZ(l.getBlockZ());
|
||||
}
|
||||
}
|
||||
|
||||
private void writeSign(final Block block, final List<String> lines, boolean glow) {
|
||||
BlockFace bf;
|
||||
if (block.getType().name().contains("WALL_SIGN")) {
|
||||
WallSign wallSign = (WallSign)block.getBlockData();
|
||||
bf = wallSign.getFacing();
|
||||
} else {
|
||||
Sign sign = (Sign)block.getBlockData();
|
||||
bf = sign.getRotation();
|
||||
}
|
||||
// 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(bf.getOppositeFace()), 30F);
|
||||
island.setSpawnPoint(block.getWorld().getEnvironment(), spawnPoint);
|
||||
return;
|
||||
}
|
||||
// Get the name of the player
|
||||
String name = "";
|
||||
if (island != null) {
|
||||
name = plugin.getPlayers().getName(island.getOwner());
|
||||
}
|
||||
// Handle locale text for starting sign
|
||||
org.bukkit.block.Sign s = (org.bukkit.block.Sign)block.getState();
|
||||
// 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
|
||||
String addonName = plugin.getIWM().getAddon(island.getWorld()).map(addon -> addon.getDescription().getName().toLowerCase(Locale.ENGLISH)).orElse("");
|
||||
if (island.getOwner() != null) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
s.setLine(i, Util.translateColorCodes(plugin.getLocalesManager().getOrDefault(User.getInstance(island.getOwner()),
|
||||
addonName + ".sign.line" + i,"").replace(TextVariables.NAME, name)));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Just paste
|
||||
for (int i = 0; i < 4; i++) {
|
||||
s.setLine(i, lines.get(i));
|
||||
}
|
||||
}
|
||||
s.setGlowingText(glow);
|
||||
// Update the sign
|
||||
s.update();
|
||||
}
|
||||
}
|
||||
|
36
src/main/java/world/bentobox/bentobox/nms/PasteHandler.java
Normal file
36
src/main/java/world/bentobox/bentobox/nms/PasteHandler.java
Normal file
@ -0,0 +1,36 @@
|
||||
package world.bentobox.bentobox.nms;
|
||||
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import world.bentobox.bentobox.blueprints.dataobjects.BlueprintBlock;
|
||||
import world.bentobox.bentobox.blueprints.dataobjects.BlueprintEntity;
|
||||
import world.bentobox.bentobox.database.objects.Island;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
* A helper class for {@link world.bentobox.bentobox.blueprints.BlueprintPaster}
|
||||
*/
|
||||
public interface PasteHandler {
|
||||
/**
|
||||
* Create a future to paste the blocks
|
||||
*
|
||||
* @param island the island
|
||||
* @param world the world
|
||||
* @param blockMap the block map
|
||||
* @return the future
|
||||
*/
|
||||
CompletableFuture<Void> pasteBlocks(Island island, World world, Map<Location, BlueprintBlock> blockMap);
|
||||
|
||||
/**
|
||||
* Create a future to paste the entities
|
||||
*
|
||||
* @param island the island
|
||||
* @param world the world
|
||||
* @param entityMap the entities map
|
||||
* @return the future
|
||||
*/
|
||||
CompletableFuture<Void> pasteEntities(Island island, World world, Map<Location, List<BlueprintEntity>> entityMap);
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package world.bentobox.bentobox.nms.fallback;
|
||||
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import world.bentobox.bentobox.blueprints.dataobjects.BlueprintBlock;
|
||||
import world.bentobox.bentobox.blueprints.dataobjects.BlueprintEntity;
|
||||
import world.bentobox.bentobox.database.objects.Island;
|
||||
import world.bentobox.bentobox.nms.PasteHandler;
|
||||
import world.bentobox.bentobox.util.DefaultPasteUtil;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class PasteHandlerImpl implements PasteHandler {
|
||||
@Override
|
||||
public CompletableFuture<Void> pasteBlocks(Island island, World world, Map<Location, BlueprintBlock> blockMap) {
|
||||
blockMap.forEach((location, block) -> DefaultPasteUtil.setBlock(island, location, block));
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> pasteEntities(Island island, World world, Map<Location, List<BlueprintEntity>> entityMap) {
|
||||
entityMap.forEach((location, blueprintEntities) -> DefaultPasteUtil.setEntity(island, location, blueprintEntities));
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
}
|
237
src/main/java/world/bentobox/bentobox/util/DefaultPasteUtil.java
Normal file
237
src/main/java/world/bentobox/bentobox/util/DefaultPasteUtil.java
Normal file
@ -0,0 +1,237 @@
|
||||
package world.bentobox.bentobox.util;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.*;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.block.data.type.WallSign;
|
||||
import org.bukkit.entity.LivingEntity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.InventoryHolder;
|
||||
import world.bentobox.bentobox.BentoBox;
|
||||
import world.bentobox.bentobox.api.localization.TextVariables;
|
||||
import world.bentobox.bentobox.api.user.User;
|
||||
import world.bentobox.bentobox.blueprints.dataobjects.BlueprintBlock;
|
||||
import world.bentobox.bentobox.blueprints.dataobjects.BlueprintCreatureSpawner;
|
||||
import world.bentobox.bentobox.blueprints.dataobjects.BlueprintEntity;
|
||||
import world.bentobox.bentobox.database.objects.Island;
|
||||
import world.bentobox.bentobox.nms.PasteHandler;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* A utility class for {@link PasteHandler}
|
||||
*
|
||||
* @author tastybento
|
||||
*/
|
||||
public class DefaultPasteUtil {
|
||||
private static final String MINECRAFT = "minecraft:";
|
||||
private static final Map<String, String> BLOCK_CONVERSION = Map.of("sign", "oak_sign", "wall_sign", "oak_wall_sign");
|
||||
private static final BentoBox plugin;
|
||||
|
||||
static {
|
||||
plugin = BentoBox.getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the block to the location
|
||||
*
|
||||
* @param island - island
|
||||
* @param location - location
|
||||
* @param bpBlock - blueprint block
|
||||
*/
|
||||
public static void setBlock(Island island, Location location, BlueprintBlock bpBlock) {
|
||||
Util.getChunkAtAsync(location).thenRun(() -> {
|
||||
Block block = location.getBlock();
|
||||
// Set the block data - default is AIR
|
||||
BlockData bd = createBlockData(bpBlock);
|
||||
block.setBlockData(bd, false);
|
||||
setBlockState(island, block, bpBlock);
|
||||
// Set biome
|
||||
if (bpBlock.getBiome() != null) {
|
||||
block.setBiome(bpBlock.getBiome());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a block data from the blueprint
|
||||
*
|
||||
* @param block - blueprint block
|
||||
* @return the block data
|
||||
*/
|
||||
public static BlockData createBlockData(BlueprintBlock block) {
|
||||
try {
|
||||
return Bukkit.createBlockData(block.getBlockData());
|
||||
} catch (Exception e) {
|
||||
return convertBlockData(block);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the blueprint to block data
|
||||
*
|
||||
* @param block - the blueprint block
|
||||
* @return the block data
|
||||
*/
|
||||
public static BlockData convertBlockData(BlueprintBlock block) {
|
||||
BlockData blockData = Bukkit.createBlockData(Material.AIR);
|
||||
try {
|
||||
for (Map.Entry<String, String> en : BLOCK_CONVERSION.entrySet()) {
|
||||
if (block.getBlockData().startsWith(MINECRAFT + en.getKey())) {
|
||||
blockData = Bukkit.createBlockData(block.getBlockData().replace(MINECRAFT + en.getKey(), MINECRAFT + en.getValue()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
// This may happen if the block type is no longer supported by the server
|
||||
plugin.logWarning("Blueprint references materials not supported on this server version.");
|
||||
plugin.logWarning("Load blueprint manually, check and save to fix for this server version.");
|
||||
plugin.logWarning("Failed block data: " + block.getBlockData());
|
||||
}
|
||||
return blockData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles signs, chests and mob spawner blocks
|
||||
*
|
||||
* @param island - island
|
||||
* @param block - block
|
||||
* @param bpBlock - config
|
||||
*/
|
||||
public static void setBlockState(Island island, Block block, BlueprintBlock bpBlock) {
|
||||
// Get the block state
|
||||
BlockState bs = block.getState();
|
||||
// Signs
|
||||
if (bs instanceof Sign) {
|
||||
writeSign(island, block, bpBlock.getSignLines(), bpBlock.isGlowingText());
|
||||
}
|
||||
// Chests, in general
|
||||
else if (bs instanceof InventoryHolder holder) {
|
||||
Inventory ih = holder.getInventory();
|
||||
// Double chests are pasted as two blocks so inventory is filled twice.
|
||||
// This code stops over-filling for the first block.
|
||||
bpBlock.getInventory().forEach(ih::setItem);
|
||||
}
|
||||
// Mob spawners
|
||||
else if (bs instanceof CreatureSpawner spawner) {
|
||||
setSpawner(spawner, bpBlock.getCreatureSpawner());
|
||||
}
|
||||
// Banners
|
||||
else if (bs instanceof Banner banner && bpBlock.getBannerPatterns() != null) {
|
||||
bpBlock.getBannerPatterns().removeIf(Objects::isNull);
|
||||
banner.setPatterns(bpBlock.getBannerPatterns());
|
||||
banner.update(true, false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the spawner setting from the blueprint
|
||||
*
|
||||
* @param spawner - spawner
|
||||
* @param s - blueprint spawner
|
||||
*/
|
||||
public static void setSpawner(CreatureSpawner spawner, BlueprintCreatureSpawner s) {
|
||||
spawner.setSpawnedType(s.getSpawnedType());
|
||||
spawner.setMaxNearbyEntities(s.getMaxNearbyEntities());
|
||||
spawner.setMaxSpawnDelay(s.getMaxSpawnDelay());
|
||||
spawner.setMinSpawnDelay(s.getMinSpawnDelay());
|
||||
spawner.setDelay(s.getDelay());
|
||||
spawner.setRequiredPlayerRange(s.getRequiredPlayerRange());
|
||||
spawner.setSpawnRange(s.getSpawnRange());
|
||||
spawner.update(true, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Spawn the blueprint entities to the location
|
||||
*
|
||||
* @param island - island
|
||||
* @param location - location
|
||||
* @param list - blueprint entities
|
||||
*/
|
||||
public static void setEntity(Island island, Location location, List<BlueprintEntity> list) {
|
||||
World world = location.getWorld();
|
||||
assert world != null;
|
||||
Util.getChunkAtAsync(location).thenRun(() -> list.stream().filter(k -> k.getType() != null).forEach(k -> {
|
||||
LivingEntity e = (LivingEntity) location.getWorld().spawnEntity(location, k.getType());
|
||||
if (k.getCustomName() != null) {
|
||||
String customName = k.getCustomName();
|
||||
|
||||
if (island != null) {
|
||||
// Parse any placeholders in the entity's name, if the owner's connected (he should)
|
||||
Optional<Player> owner = Optional.ofNullable(island.getOwner())
|
||||
.map(User::getInstance)
|
||||
.map(User::getPlayer);
|
||||
if (owner.isPresent()) {
|
||||
// Parse for the player's name first (in case placeholders might need it)
|
||||
customName = customName.replace(TextVariables.NAME, owner.get().getName());
|
||||
// Now parse the placeholders
|
||||
customName = plugin.getPlaceholdersManager().replacePlaceholders(owner.get(), customName);
|
||||
}
|
||||
}
|
||||
|
||||
// Actually set the custom name
|
||||
e.setCustomName(customName);
|
||||
}
|
||||
k.configureEntity(e);
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the lines to the sign at the block
|
||||
*
|
||||
* @param island - island
|
||||
* @param block - block
|
||||
* @param lines - lines
|
||||
* @param glow - is sign glowing?
|
||||
*/
|
||||
public static void writeSign(Island island, final Block block, final List<String> lines, boolean glow) {
|
||||
BlockFace bf;
|
||||
if (block.getType().name().contains("WALL_SIGN")) {
|
||||
WallSign wallSign = (WallSign) block.getBlockData();
|
||||
bf = wallSign.getFacing();
|
||||
} else {
|
||||
org.bukkit.block.data.type.Sign sign = (org.bukkit.block.data.type.Sign) block.getBlockData();
|
||||
bf = sign.getRotation();
|
||||
}
|
||||
// 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(bf.getOppositeFace()), 30F);
|
||||
island.setSpawnPoint(block.getWorld().getEnvironment(), spawnPoint);
|
||||
return;
|
||||
}
|
||||
// Get the name of the player
|
||||
String name = "";
|
||||
if (island != null) {
|
||||
name = plugin.getPlayers().getName(island.getOwner());
|
||||
}
|
||||
// Handle locale text for starting sign
|
||||
org.bukkit.block.Sign s = (org.bukkit.block.Sign) block.getState();
|
||||
// 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
|
||||
String addonName = plugin.getIWM().getAddon(island.getWorld()).map(addon -> addon.getDescription().getName().toLowerCase(Locale.ENGLISH)).orElse("");
|
||||
Optional<User> user = Optional.ofNullable(island.getOwner()).map(User::getInstance);
|
||||
if (user.isPresent()) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
s.setLine(i, Util.translateColorCodes(plugin.getLocalesManager().getOrDefault(user.get(),
|
||||
addonName + ".sign.line" + i, "").replace(TextVariables.NAME, name)));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Just paste
|
||||
for (int i = 0; i < 4; i++) {
|
||||
s.setLine(i, lines.get(i));
|
||||
}
|
||||
}
|
||||
s.setGlowingText(glow);
|
||||
// Update the sign
|
||||
s.update();
|
||||
}
|
||||
}
|
@ -47,8 +47,10 @@ import io.papermc.lib.PaperLib;
|
||||
import io.papermc.lib.features.blockstatesnapshot.BlockStateSnapshotResult;
|
||||
import world.bentobox.bentobox.BentoBox;
|
||||
import world.bentobox.bentobox.api.user.User;
|
||||
import world.bentobox.bentobox.nms.PasteHandler;
|
||||
import world.bentobox.bentobox.nms.WorldRegenerator;
|
||||
|
||||
|
||||
/**
|
||||
* A set of utility methods
|
||||
*
|
||||
@ -64,6 +66,7 @@ public class Util {
|
||||
private static final String THE_END = "_the_end";
|
||||
private static String serverVersion = null;
|
||||
private static BentoBox plugin = BentoBox.getInstance();
|
||||
private static PasteHandler pasteHandler = null;
|
||||
private static WorldRegenerator regenerator = null;
|
||||
|
||||
private Util() {}
|
||||
@ -722,6 +725,25 @@ public class Util {
|
||||
return regenerator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the paste handler the plugin will use
|
||||
* @param pasteHandler the NMS paster
|
||||
*/
|
||||
public static void setPasteHandler(PasteHandler pasteHandler) {
|
||||
Util.pasteHandler = pasteHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the paste handler the plugin will use
|
||||
* @return an NMS accelerated class for this server
|
||||
*/
|
||||
public static PasteHandler getPasteHandler() {
|
||||
if (pasteHandler == null) {
|
||||
setPasteHandler(new world.bentobox.bentobox.nms.fallback.PasteHandlerImpl());
|
||||
}
|
||||
return pasteHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcast a localized message to all players with the permission {@link Server#BROADCAST_CHANNEL_USERS}
|
||||
*
|
||||
|
Loading…
Reference in New Issue
Block a user