Plex-FAWE/worldedit-bukkit/src/main/java/com/sk89q/worldedit/bukkit/BukkitWorld.java

519 lines
18 KiB
Java

/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.bukkit;
import static com.google.common.base.Preconditions.checkNotNull;
import com.boydti.fawe.beta.IChunkGet;
import com.boydti.fawe.bukkit.adapter.mc1_14.BukkitGetBlocks_1_14;
import com.boydti.fawe.config.Settings;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.blocks.BaseItem;
import com.sk89q.worldedit.blocks.BaseItemStack;
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
import com.sk89q.worldedit.entity.BaseEntity;
import com.sk89q.worldedit.history.change.BlockChange;
import com.sk89q.worldedit.math.BlockVector2;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.math.Vector3;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.util.Direction;
import com.sk89q.worldedit.util.TreeGenerator;
import com.sk89q.worldedit.world.AbstractWorld;
import com.sk89q.worldedit.world.biome.BiomeType;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import com.sk89q.worldedit.world.weather.WeatherType;
import com.sk89q.worldedit.world.weather.WeatherTypes;
import java.lang.ref.WeakReference;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import javax.annotation.Nullable;
import org.bukkit.Effect;
import org.bukkit.TreeType;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.BlockState;
import org.bukkit.block.Chest;
import org.bukkit.entity.Entity;
import org.bukkit.inventory.DoubleChestInventory;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
import org.slf4j.Logger;
public class BukkitWorld extends AbstractWorld {
private static final Logger logger = WorldEdit.logger;
private static final Map<Integer, Effect> effects = new HashMap<>();
static {
for (Effect effect : Effect.values()) {
effects.put(effect.getId(), effect);
}
}
private final WeakReference<World> worldRef;
/**
* Construct the object.
*
* @param world the world
*/
public BukkitWorld(World world) {
this.worldRef = new WeakReference<>(world);
}
@Override
public List<com.sk89q.worldedit.entity.Entity> getEntities(Region region) {
World world = getWorld();
List<Entity> ents = world.getEntities();
List<com.sk89q.worldedit.entity.Entity> entities = new ArrayList<>();
for (Entity ent : ents) {
if (region.contains(BukkitAdapter.asBlockVector(ent.getLocation()))) {
entities.add(BukkitAdapter.adapt(ent));
}
}
return entities;
}
@Override
public List<com.sk89q.worldedit.entity.Entity> getEntities() {
List<com.sk89q.worldedit.entity.Entity> list = new ArrayList<>();
for (Entity entity : getWorld().getEntities()) {
list.add(BukkitAdapter.adapt(entity));
}
return list;
}
@Nullable
@Override
public com.sk89q.worldedit.entity.Entity createEntity(com.sk89q.worldedit.util.Location location, BaseEntity entity) {
BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter();
if (adapter != null) {
try {
Entity createdEntity = adapter.createEntity(BukkitAdapter.adapt(getWorld(), location), entity);
if (createdEntity != null) {
return new BukkitEntity(createdEntity);
} else {
return null;
}
} catch (Exception e) {
logger.warn("Corrupt entity found when creating: " + entity.getType().getId());
if (entity.getNbtData() != null) {
logger.warn(entity.getNbtData().toString());
}
e.printStackTrace();
return null;
}
} else {
return null;
}
}
/**
* Get the world handle.
*
* @return the world
*/
public World getWorld() {
return checkNotNull(worldRef.get(), "The world was unloaded and the reference is unavailable");
}
/**
* Get the world handle.
*
* @return the world
*/
protected World getWorldChecked() throws WorldEditException {
World world = worldRef.get();
if (world == null) {
throw new WorldUnloadedException();
}
return world;
}
@Override
public String getName() {
return getWorld().getName();
}
@Override
public String getId() {
return getWorld().getName().replace(" ", "_").toLowerCase(Locale.ROOT);
}
@Override
public Path getStoragePath() {
return getWorld().getWorldFolder().toPath();
}
@Override
public int getBlockLightLevel(BlockVector3 pt) {
return getWorld().getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()).getLightLevel();
}
@Override
public boolean regenerate(Region region, EditSession editSession) {
BaseBlock[] history = new BaseBlock[16 * 16 * (getMaxY() + 1)];
for (BlockVector2 chunk : region.getChunks()) {
BlockVector3 min = BlockVector3.at(chunk.getBlockX() * 16, 0, chunk.getBlockZ() * 16);
// First save all the blocks inside
for (int x = 0; x < 16; ++x) {
for (int y = 0; y < (getMaxY() + 1); ++y) {
for (int z = 0; z < 16; ++z) {
BlockVector3 pt = min.add(x, y, z);
int index = y * 16 * 16 + z * 16 + x;
history[index] = editSession.getFullBlock(pt);
}
}
}
try {
getWorld().regenerateChunk(chunk.getBlockX(), chunk.getBlockZ());
} catch (Throwable t) {
logger.warn("Chunk generation via Bukkit raised an error", t);
}
// Then restore
for (int x = 0; x < 16; ++x) {
for (int y = 0; y < (getMaxY() + 1); ++y) {
for (int z = 0; z < 16; ++z) {
BlockVector3 pt = min.add(x, y, z);
int index = y * 16 * 16 + z * 16 + x;
// We have to restore the block if it was outside
if (!region.contains(pt)) {
editSession.smartSetBlock(pt, history[index]);
} else { // Otherwise fool with history
editSession.getChangeSet().add(new BlockChange(pt, history[index], editSession.getFullBlock(pt)));
}
}
}
}
}
return true;
}
/**
* Gets the single block inventory for a potentially double chest.
* Handles people who have an old version of Bukkit.
* This should be replaced with {@link org.bukkit.block.Chest#getBlockInventory()}
* in a few months (now = March 2012) // note from future dev - lol
*
* @param chest The chest to get a single block inventory for
* @return The chest's inventory
*/
private Inventory getBlockInventory(Chest chest) {
try {
return chest.getBlockInventory();
} catch (Throwable t) {
if (chest.getInventory() instanceof DoubleChestInventory) {
DoubleChestInventory inven = (DoubleChestInventory) chest.getInventory();
if (inven.getLeftSide().getHolder().equals(chest)) {
return inven.getLeftSide();
} else if (inven.getRightSide().getHolder().equals(chest)) {
return inven.getRightSide();
} else {
return inven;
}
} else {
return chest.getInventory();
}
}
}
@Override
public boolean clearContainerBlockContents(BlockVector3 pt) {
Block block = getWorld().getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ());
BlockState state = block.getState();
if (!(state instanceof InventoryHolder)) {
return false;
}
InventoryHolder chest = (InventoryHolder) state;
Inventory inven = chest.getInventory();
if (chest instanceof Chest) {
inven = getBlockInventory((Chest) chest);
}
inven.clear();
return true;
}
/**
* An EnumMap that stores which WorldEdit TreeTypes apply to which Bukkit TreeTypes
*/
private static final EnumMap<TreeGenerator.TreeType, TreeType> treeTypeMapping =
new EnumMap<>(TreeGenerator.TreeType.class);
static {
for (TreeGenerator.TreeType type : TreeGenerator.TreeType.values()) {
try {
TreeType bukkitType = TreeType.valueOf(type.name());
treeTypeMapping.put(type, bukkitType);
} catch (IllegalArgumentException e) {
// Unhandled TreeType
}
}
// Other mappings for WE-specific values
treeTypeMapping.put(TreeGenerator.TreeType.SHORT_JUNGLE, TreeType.SMALL_JUNGLE);
treeTypeMapping.put(TreeGenerator.TreeType.RANDOM, TreeType.BROWN_MUSHROOM);
treeTypeMapping.put(TreeGenerator.TreeType.RANDOM_REDWOOD, TreeType.REDWOOD);
treeTypeMapping.put(TreeGenerator.TreeType.PINE, TreeType.REDWOOD);
treeTypeMapping.put(TreeGenerator.TreeType.RANDOM_BIRCH, TreeType.BIRCH);
treeTypeMapping.put(TreeGenerator.TreeType.RANDOM_JUNGLE, TreeType.JUNGLE);
treeTypeMapping.put(TreeGenerator.TreeType.RANDOM_MUSHROOM, TreeType.BROWN_MUSHROOM);
for (TreeGenerator.TreeType type : TreeGenerator.TreeType.values()) {
if (treeTypeMapping.get(type) == null) {
WorldEdit.logger.error("No TreeType mapping for TreeGenerator.TreeType." + type);
}
}
}
public static TreeType toBukkitTreeType(TreeGenerator.TreeType type) {
return treeTypeMapping.get(type);
}
@Override
public boolean generateTree(TreeGenerator.TreeType type, EditSession editSession, BlockVector3 pt) {
World world = getWorld();
TreeType bukkitType = toBukkitTreeType(type);
return type != null && world.generateTree(BukkitAdapter.adapt(world, pt), bukkitType,
new EditSessionBlockChangeDelegate(editSession));
}
@Override
public void dropItem(Vector3 pt, BaseItemStack item) {
World world = getWorld();
world.dropItemNaturally(BukkitAdapter.adapt(world, pt), BukkitAdapter.adapt(item));
}
@Override
public void checkLoadedChunk(BlockVector3 pt) {
World world = getWorld();
world.getChunkAt(pt.getBlockX() >> 4, pt.getBlockZ() >> 4);
}
@Override
public boolean equals(Object other) {
final World ref = worldRef.get();
if (ref == null) {
return false;
} else if (other == null) {
return false;
} else if ((other instanceof BukkitWorld)) {
World otherWorld = ((BukkitWorld) other).worldRef.get();
return ref.equals(otherWorld);
} else if (other instanceof com.sk89q.worldedit.world.World) {
return ((com.sk89q.worldedit.world.World) other).getName().equals(ref.getName());
} else {
return false;
}
}
@Override
public int hashCode() {
return getWorld().hashCode();
}
@Override
public int getMaxY() {
return getWorld().getMaxHeight() - 1;
}
@Override
public void fixAfterFastMode(Iterable<BlockVector2> chunks) {
World world = getWorld();
for (BlockVector2 chunkPos : chunks) {
world.refreshChunk(chunkPos.getBlockX(), chunkPos.getBlockZ());
}
}
@Override
public boolean playEffect(Vector3 position, int type, int data) {
World world = getWorld();
final Effect effect = effects.get(type);
if (effect == null) {
return false;
}
world.playEffect(BukkitAdapter.adapt(world, position), effect, data);
return true;
}
@Override
public WeatherType getWeather() {
if (getWorld().isThundering()) {
return WeatherTypes.THUNDER_STORM;
} else if (getWorld().hasStorm()) {
return WeatherTypes.RAIN;
}
return WeatherTypes.CLEAR;
}
@Override
public long getRemainingWeatherDuration() {
return getWorld().getWeatherDuration();
}
@Override
public void setWeather(WeatherType weatherType) {
if (weatherType == WeatherTypes.THUNDER_STORM) {
getWorld().setThundering(true);
} else if (weatherType == WeatherTypes.RAIN) {
getWorld().setStorm(true);
} else {
getWorld().setStorm(false);
getWorld().setThundering(false);
}
}
@Override
public void setWeather(WeatherType weatherType, long duration) {
// Who named these methods...
if (weatherType == WeatherTypes.THUNDER_STORM) {
getWorld().setThundering(true);
getWorld().setThunderDuration((int) duration);
getWorld().setWeatherDuration((int) duration);
} else if (weatherType == WeatherTypes.RAIN) {
getWorld().setStorm(true);
getWorld().setWeatherDuration((int) duration);
} else {
getWorld().setStorm(false);
getWorld().setThundering(false);
getWorld().setWeatherDuration((int) duration);
}
}
@Override
public BlockVector3 getSpawnPosition() {
return BukkitAdapter.asBlockVector(getWorld().getSpawnLocation());
}
@Override
public void simulateBlockMine(BlockVector3 pt) {
getWorld().getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()).breakNaturally();
}
private static volatile boolean hasWarnedImplError = false;
@Override
public com.sk89q.worldedit.world.block.BlockState getBlock(BlockVector3 position) {
BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter();
if (adapter != null) {
try {
return adapter.getBlock(BukkitAdapter.adapt(getWorld(), position)).toImmutableState();
} catch (Exception e) {
if (!hasWarnedImplError) {
hasWarnedImplError = true;
logger.warn("Unable to retrieve block via impl adapter", e);
}
}
}
Block bukkitBlock = getWorld().getBlockAt(position.getBlockX(), position.getBlockY(), position.getBlockZ());
return BukkitAdapter.adapt(bukkitBlock.getBlockData());
}
@Override
public <B extends BlockStateHolder<B>> boolean setBlock(BlockVector3 position, B block, boolean notifyAndLight) throws WorldEditException {
BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter();
if (adapter != null) {
try {
return adapter.setBlock(BukkitAdapter.adapt(getWorld(), position), block, notifyAndLight);
} catch (Exception e) {
if (block instanceof BaseBlock && ((BaseBlock) block).getNbtData() != null) {
logger.warn("Tried to set a corrupt tile entity at " + position.toString());
logger.warn(((BaseBlock) block).getNbtData().toString());
}
e.printStackTrace();
Block bukkitBlock = getWorld().getBlockAt(position.getBlockX(), position.getBlockY(), position.getBlockZ());
bukkitBlock.setBlockData(BukkitAdapter.adapt(block), notifyAndLight);
return true;
}
} else {
Block bukkitBlock = getWorld().getBlockAt(position.getBlockX(), position.getBlockY(), position.getBlockZ());
bukkitBlock.setBlockData(BukkitAdapter.adapt(block), false);
return true;
}
}
@Override
public BaseBlock getFullBlock(BlockVector3 position) {
BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter();
if (adapter != null) {
return adapter.getBlock(BukkitAdapter.adapt(getWorld(), position));
} else {
return getBlock(position).toBaseBlock();
}
}
@Override
public boolean notifyAndLightBlock(BlockVector3 position, com.sk89q.worldedit.world.block.BlockState previousType) throws WorldEditException {
BukkitImplAdapter adapter = WorldEditPlugin.getInstance().getBukkitImplAdapter();
if (adapter != null) {
adapter.notifyAndLightBlock(BukkitAdapter.adapt(getWorld(), position), previousType);
return true;
}
return false;
}
@Override
public BiomeType getBiome(BlockVector2 position) {
return BukkitAdapter.adapt(getWorld().getBiome(position.getBlockX(), position.getBlockZ()));
}
@Override
public boolean setTile(int x, int y, int z, CompoundTag tile) throws WorldEditException {
return false;
}
@Override
public boolean setBiome(BlockVector2 position, BiomeType biome) {
getWorld().setBiome(position.getBlockX(), position.getBlockZ(), BukkitAdapter.adapt(biome));
return true;
}
@Override
public void sendChunk(int X, int Z, int mask) {
}
@Override
public IChunkGet get(int chunkX, int chunkZ) {
return new BukkitGetBlocks_1_14(getWorldChecked(), chunkX, chunkZ, Settings.IMP.QUEUE.POOL);
}
}