mirror of
https://github.com/plexusorg/Plex-FAWE.git
synced 2024-07-05 16:40:04 +00:00
481 lines
19 KiB
Java
481 lines
19 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.boydti.fawe.bukkit.adapter;
|
|
|
|
import static com.google.common.base.Preconditions.checkNotNull;
|
|
|
|
import com.google.common.collect.ImmutableList;
|
|
import com.google.common.collect.Maps;
|
|
import com.sk89q.jnbt.ByteArrayTag;
|
|
import com.sk89q.jnbt.ByteTag;
|
|
import com.sk89q.jnbt.CompoundTag;
|
|
import com.sk89q.jnbt.DoubleTag;
|
|
import com.sk89q.jnbt.EndTag;
|
|
import com.sk89q.jnbt.FloatTag;
|
|
import com.sk89q.jnbt.IntArrayTag;
|
|
import com.sk89q.jnbt.IntTag;
|
|
import com.sk89q.jnbt.ListTag;
|
|
import com.sk89q.jnbt.LongTag;
|
|
import com.sk89q.jnbt.NBTConstants;
|
|
import com.sk89q.jnbt.ShortTag;
|
|
import com.sk89q.jnbt.StringTag;
|
|
import com.sk89q.jnbt.Tag;
|
|
import com.sk89q.worldedit.blocks.BaseBlock;
|
|
import com.sk89q.worldedit.world.block.BlockState;
|
|
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
|
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
|
|
import com.sk89q.worldedit.entity.BaseEntity;
|
|
import com.sk89q.worldedit.internal.Constants;
|
|
import com.sk89q.worldedit.registry.state.BooleanProperty;
|
|
import com.sk89q.worldedit.registry.state.DirectionalProperty;
|
|
import com.sk89q.worldedit.registry.state.EnumProperty;
|
|
import com.sk89q.worldedit.registry.state.IntegerProperty;
|
|
import com.sk89q.worldedit.registry.state.Property;
|
|
import com.sk89q.worldedit.util.Direction;
|
|
import com.sk89q.worldedit.world.block.BlockStateHolder;
|
|
import com.sk89q.worldedit.world.block.BlockType;
|
|
import net.minecraft.server.v1_13_R1.BiomeBase;
|
|
import net.minecraft.server.v1_13_R1.Block;
|
|
import net.minecraft.server.v1_13_R1.BlockPosition;
|
|
import net.minecraft.server.v1_13_R1.BlockStateBoolean;
|
|
import net.minecraft.server.v1_13_R1.BlockStateDirection;
|
|
import net.minecraft.server.v1_13_R1.BlockStateEnum;
|
|
import net.minecraft.server.v1_13_R1.BlockStateInteger;
|
|
import net.minecraft.server.v1_13_R1.BlockStateList;
|
|
import net.minecraft.server.v1_13_R1.Entity;
|
|
import net.minecraft.server.v1_13_R1.EntityTypes;
|
|
import net.minecraft.server.v1_13_R1.IBlockData;
|
|
import net.minecraft.server.v1_13_R1.IBlockState;
|
|
import net.minecraft.server.v1_13_R1.INamable;
|
|
import net.minecraft.server.v1_13_R1.MinecraftKey;
|
|
import net.minecraft.server.v1_13_R1.NBTBase;
|
|
import net.minecraft.server.v1_13_R1.NBTTagByte;
|
|
import net.minecraft.server.v1_13_R1.NBTTagByteArray;
|
|
import net.minecraft.server.v1_13_R1.NBTTagCompound;
|
|
import net.minecraft.server.v1_13_R1.NBTTagDouble;
|
|
import net.minecraft.server.v1_13_R1.NBTTagEnd;
|
|
import net.minecraft.server.v1_13_R1.NBTTagFloat;
|
|
import net.minecraft.server.v1_13_R1.NBTTagInt;
|
|
import net.minecraft.server.v1_13_R1.NBTTagIntArray;
|
|
import net.minecraft.server.v1_13_R1.NBTTagList;
|
|
import net.minecraft.server.v1_13_R1.NBTTagLong;
|
|
import net.minecraft.server.v1_13_R1.NBTTagShort;
|
|
import net.minecraft.server.v1_13_R1.NBTTagString;
|
|
import net.minecraft.server.v1_13_R1.TileEntity;
|
|
import net.minecraft.server.v1_13_R1.World;
|
|
import net.minecraft.server.v1_13_R1.WorldServer;
|
|
import org.bukkit.Bukkit;
|
|
import org.bukkit.Location;
|
|
import org.bukkit.block.Biome;
|
|
import org.bukkit.craftbukkit.v1_13_R1.CraftServer;
|
|
import org.bukkit.craftbukkit.v1_13_R1.CraftWorld;
|
|
import org.bukkit.craftbukkit.v1_13_R1.block.CraftBlock;
|
|
import org.bukkit.craftbukkit.v1_13_R1.entity.CraftEntity;
|
|
import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason;
|
|
|
|
import java.lang.reflect.Field;
|
|
import java.lang.reflect.Method;
|
|
import java.util.*;
|
|
import java.util.logging.Level;
|
|
import java.util.logging.Logger;
|
|
import java.util.stream.Collectors;
|
|
|
|
import javax.annotation.Nullable;
|
|
|
|
public final class Spigot_v1_13_R1 implements BukkitImplAdapter<NBTBase> {
|
|
|
|
private final Logger logger = Logger.getLogger(getClass().getCanonicalName());
|
|
|
|
private final Field nbtListTagListField;
|
|
private final Method nbtCreateTagMethod;
|
|
|
|
// ------------------------------------------------------------------------
|
|
// Code that may break between versions of Minecraft
|
|
// ------------------------------------------------------------------------
|
|
|
|
public Spigot_v1_13_R1() throws NoSuchFieldException, NoSuchMethodException {
|
|
// A simple test
|
|
if (!Bukkit.getServer().getClass().getName().endsWith("DummyServer")) CraftServer.class.cast(Bukkit.getServer());
|
|
// test between 1.12 and 1.12.1 since md_5 didn't update revision numbers
|
|
TileEntity.class.getDeclaredMethod("load", NBTTagCompound.class);
|
|
|
|
// The list of tags on an NBTTagList
|
|
nbtListTagListField = NBTTagList.class.getDeclaredField("list");
|
|
nbtListTagListField.setAccessible(true);
|
|
|
|
// The method to create an NBTBase tag given its type ID
|
|
nbtCreateTagMethod = NBTBase.class.getDeclaredMethod("createTag", byte.class);
|
|
nbtCreateTagMethod.setAccessible(true);
|
|
}
|
|
|
|
/**
|
|
* Read the given NBT data into the given tile entity.
|
|
*
|
|
* @param tileEntity the tile entity
|
|
* @param tag the tag
|
|
*/
|
|
private static void readTagIntoTileEntity(NBTTagCompound tag, TileEntity tileEntity) {
|
|
tileEntity.load(tag);
|
|
}
|
|
|
|
/**
|
|
* Write the tile entity's NBT data to the given tag.
|
|
*
|
|
* @param tileEntity the tile entity
|
|
* @param tag the tag
|
|
*/
|
|
private static void readTileEntityIntoTag(TileEntity tileEntity, NBTTagCompound tag) {
|
|
tileEntity.save(tag);
|
|
}
|
|
|
|
/**
|
|
* Get the ID string of the given entity.
|
|
*
|
|
* @param entity the entity
|
|
* @return the entity ID or null if one is not known
|
|
*/
|
|
@Nullable
|
|
private static String getEntityId(Entity entity) {
|
|
MinecraftKey minecraftkey = EntityTypes.getName(entity.getBukkitEntity().getHandle().P());
|
|
return minecraftkey == null ? null : minecraftkey.toString();
|
|
}
|
|
|
|
/**
|
|
* Create an entity using the given entity ID.
|
|
*
|
|
* @param id the entity ID
|
|
* @param world the world
|
|
* @return an entity or null
|
|
*/
|
|
@Nullable
|
|
private static Entity createEntityFromId(String id, World world) {
|
|
return EntityTypes.a(world, new MinecraftKey(id));
|
|
}
|
|
|
|
/**
|
|
* Write the given NBT data into the given entity.
|
|
*
|
|
* @param entity the entity
|
|
* @param tag the tag
|
|
*/
|
|
private static void readTagIntoEntity(NBTTagCompound tag, Entity entity) {
|
|
entity.f(tag);
|
|
}
|
|
|
|
/**
|
|
* Write the entity's NBT data to the given tag.
|
|
*
|
|
* @param entity the entity
|
|
* @param tag the tag
|
|
*/
|
|
private static void readEntityIntoTag(Entity entity, NBTTagCompound tag) {
|
|
entity.save(tag);
|
|
}
|
|
|
|
// ------------------------------------------------------------------------
|
|
// Code that is less likely to break
|
|
// ------------------------------------------------------------------------
|
|
|
|
@Override
|
|
public int getBiomeId(Biome biome) {
|
|
BiomeBase mcBiome = CraftBlock.biomeToBiomeBase(biome);
|
|
return mcBiome != null ? BiomeBase.a(mcBiome) : 0;
|
|
}
|
|
|
|
@Override
|
|
public Biome getBiome(int id) {
|
|
BiomeBase mcBiome = BiomeBase.getBiome(id);
|
|
return CraftBlock.biomeBaseToBiome(mcBiome); // Defaults to ocean if it's an invalid ID
|
|
}
|
|
|
|
@SuppressWarnings("deprecation")
|
|
@Override
|
|
public BlockState getBlock(Location location) {
|
|
checkNotNull(location);
|
|
|
|
CraftWorld craftWorld = ((CraftWorld) location.getWorld());
|
|
int x = location.getBlockX();
|
|
int y = location.getBlockY();
|
|
int z = location.getBlockZ();
|
|
|
|
org.bukkit.block.Block bukkitBlock = location.getBlock();
|
|
BlockState state = BukkitAdapter.adapt(bukkitBlock.getBlockData());
|
|
if (state.getBlockType().getMaterial().hasContainer()) {
|
|
//Read the NBT data
|
|
TileEntity te = craftWorld.getHandle().getTileEntity(new BlockPosition(x, y, z));
|
|
if (te != null) {
|
|
NBTTagCompound tag = new NBTTagCompound();
|
|
readTileEntityIntoTag(te, tag); // Load data
|
|
return new BaseBlock(state, (CompoundTag) toNative(tag));
|
|
}
|
|
}
|
|
|
|
return state;
|
|
}
|
|
|
|
@Override
|
|
public boolean setBlock(Location location, BlockStateHolder state, boolean notifyAndLight) {
|
|
checkNotNull(location);
|
|
checkNotNull(state);
|
|
|
|
CraftWorld craftWorld = ((CraftWorld) location.getWorld());
|
|
int x = location.getBlockX();
|
|
int y = location.getBlockY();
|
|
int z = location.getBlockZ();
|
|
|
|
// Two pass update:
|
|
// Note, this will notify blocks BEFORE the tile entity is set
|
|
location.getBlock().setBlockData(BukkitAdapter.adapt(state), false);
|
|
|
|
// Copy NBT data for the block
|
|
CompoundTag nativeTag = state.getNbtData();
|
|
if (nativeTag != null) {
|
|
// We will assume that the tile entity was created for us,
|
|
// though we do not do this on the Forge version
|
|
TileEntity tileEntity = craftWorld.getHandle().getTileEntity(new BlockPosition(x, y, z));
|
|
if (tileEntity != null) {
|
|
NBTTagCompound tag = (NBTTagCompound) fromNative(nativeTag);
|
|
tag.set("x", new NBTTagInt(x));
|
|
tag.set("y", new NBTTagInt(y));
|
|
tag.set("z", new NBTTagInt(z));
|
|
readTagIntoTileEntity(tag, tileEntity); // Load data
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public BaseEntity getEntity(org.bukkit.entity.Entity entity) {
|
|
checkNotNull(entity);
|
|
|
|
CraftEntity craftEntity = ((CraftEntity) entity);
|
|
Entity mcEntity = craftEntity.getHandle();
|
|
|
|
String id = getEntityId(mcEntity);
|
|
|
|
if (id != null) {
|
|
NBTTagCompound tag = new NBTTagCompound();
|
|
readEntityIntoTag(mcEntity, tag);
|
|
return new BaseEntity(com.sk89q.worldedit.world.entity.EntityTypes.get(id), (CompoundTag) toNative(tag));
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
@Nullable
|
|
@Override
|
|
public org.bukkit.entity.Entity createEntity(Location location, BaseEntity state) {
|
|
checkNotNull(location);
|
|
checkNotNull(state);
|
|
|
|
CraftWorld craftWorld = ((CraftWorld) location.getWorld());
|
|
WorldServer worldServer = craftWorld.getHandle();
|
|
|
|
Entity createdEntity = createEntityFromId(state.getType().getId(), craftWorld.getHandle());
|
|
|
|
if (createdEntity != null) {
|
|
CompoundTag nativeTag = state.getNbtData();
|
|
if (nativeTag != null) {
|
|
NBTTagCompound tag = (NBTTagCompound) fromNative(nativeTag);
|
|
for (String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) {
|
|
tag.remove(name);
|
|
}
|
|
readTagIntoEntity(tag, createdEntity);
|
|
}
|
|
|
|
createdEntity.setLocation(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch());
|
|
|
|
worldServer.addEntity(createdEntity, SpawnReason.CUSTOM);
|
|
return createdEntity.getBukkitEntity();
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
@SuppressWarnings("unchecked")
|
|
@Override
|
|
public Map<String, ? extends Property> getProperties(BlockType blockType) {
|
|
Block block;
|
|
try {
|
|
block = Block.getByName(blockType.getId());
|
|
} catch (Throwable e) {
|
|
return Collections.emptyMap();
|
|
}
|
|
if (block == null) {
|
|
logger.warning("Failed to find properties for " + blockType.getId());
|
|
return Collections.emptyMap();
|
|
}
|
|
Map<String, Property> properties = Maps.newLinkedHashMap();
|
|
BlockStateList<Block, IBlockData> blockStateList = block.getStates();
|
|
for (IBlockState state : blockStateList.d()) {
|
|
Property property;
|
|
if (state instanceof BlockStateBoolean) {
|
|
property = new BooleanProperty(state.a(), ImmutableList.copyOf(state.d()));
|
|
} else if (state instanceof BlockStateDirection) {
|
|
property = new DirectionalProperty(state.a(),
|
|
(List<Direction>) state.d().stream().map(e -> Direction.valueOf(((INamable) e).getName().toUpperCase())).collect(Collectors.toList()));
|
|
} else if (state instanceof BlockStateEnum) {
|
|
property = new EnumProperty(state.a(),
|
|
(List<String>) state.d().stream().map(e -> ((INamable) e).getName()).collect(Collectors.toList()));
|
|
} else if (state instanceof BlockStateInteger) {
|
|
property = new IntegerProperty(state.a(), ImmutableList.copyOf(state.d()));
|
|
} else {
|
|
throw new IllegalArgumentException("WorldEdit needs an update to support " + state.getClass().getSimpleName());
|
|
}
|
|
|
|
properties.put(property.getName(), property);
|
|
}
|
|
return properties;
|
|
}
|
|
|
|
/**
|
|
* Converts from a non-native NMS NBT structure to a native WorldEdit NBT
|
|
* structure.
|
|
*
|
|
* @param foreign non-native NMS NBT structure
|
|
* @return native WorldEdit NBT structure
|
|
*/
|
|
@SuppressWarnings("unchecked")
|
|
@Override
|
|
public Tag toNative(NBTBase foreign) {
|
|
if (foreign == null) {
|
|
return null;
|
|
}
|
|
if (foreign instanceof NBTTagCompound) {
|
|
Map<String, Tag> values = new HashMap<>();
|
|
Set<String> foreignKeys = ((NBTTagCompound) foreign).getKeys(); // map.keySet
|
|
|
|
for (String str : foreignKeys) {
|
|
NBTBase base = ((NBTTagCompound) foreign).get(str);
|
|
values.put(str, toNative(base));
|
|
}
|
|
return new CompoundTag(values);
|
|
} else if (foreign instanceof NBTTagByte) {
|
|
return new ByteTag(((NBTTagByte) foreign).g()); // getByte
|
|
} else if (foreign instanceof NBTTagByteArray) {
|
|
return new ByteArrayTag(((NBTTagByteArray) foreign).c()); // data
|
|
} else if (foreign instanceof NBTTagDouble) {
|
|
return new DoubleTag(((NBTTagDouble) foreign).asDouble()); // getDouble
|
|
} else if (foreign instanceof NBTTagFloat) {
|
|
return new FloatTag(((NBTTagFloat) foreign).i()); // getFloat
|
|
} else if (foreign instanceof NBTTagInt) {
|
|
return new IntTag(((NBTTagInt) foreign).e()); // getInt
|
|
} else if (foreign instanceof NBTTagIntArray) {
|
|
return new IntArrayTag(((NBTTagIntArray) foreign).d()); // data
|
|
} else if (foreign instanceof NBTTagList) {
|
|
try {
|
|
return toNativeList((NBTTagList) foreign);
|
|
} catch (Throwable e) {
|
|
logger.log(Level.WARNING, "Failed to convert NBTTagList", e);
|
|
return new ListTag(ByteTag.class, new ArrayList<ByteTag>());
|
|
}
|
|
} else if (foreign instanceof NBTTagLong) {
|
|
return new LongTag(((NBTTagLong) foreign).d()); // getLong
|
|
} else if (foreign instanceof NBTTagShort) {
|
|
return new ShortTag(((NBTTagShort) foreign).f()); // getShort
|
|
} else if (foreign instanceof NBTTagString) {
|
|
return new StringTag(foreign.b_()); // data
|
|
} else if (foreign instanceof NBTTagEnd) {
|
|
return new EndTag();
|
|
} else {
|
|
throw new IllegalArgumentException("Don't know how to make native " + foreign.getClass().getCanonicalName());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Convert a foreign NBT list tag into a native WorldEdit one.
|
|
*
|
|
* @param foreign the foreign tag
|
|
* @return the converted tag
|
|
* @throws NoSuchFieldException on error
|
|
* @throws SecurityException on error
|
|
* @throws IllegalArgumentException on error
|
|
* @throws IllegalAccessException on error
|
|
*/
|
|
public ListTag toNativeList(NBTTagList foreign) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
|
|
List<Tag> values = new ArrayList<>();
|
|
int type = foreign.getTypeId();
|
|
|
|
List foreignList;
|
|
foreignList = (List) nbtListTagListField.get(foreign);
|
|
for (int i = 0; i < foreign.size(); i++) {
|
|
NBTBase element = (NBTBase) foreignList.get(i);
|
|
values.add(toNative(element)); // List elements shouldn't have names
|
|
}
|
|
|
|
Class<? extends Tag> cls = NBTConstants.getClassFromType(type);
|
|
return new ListTag(cls, values);
|
|
}
|
|
|
|
/**
|
|
* Converts a WorldEdit-native NBT structure to a NMS structure.
|
|
*
|
|
* @param foreign structure to convert
|
|
* @return non-native structure
|
|
*/
|
|
@Override
|
|
public NBTBase fromNative(Tag foreign) {
|
|
if (foreign == null) {
|
|
return null;
|
|
}
|
|
if (foreign instanceof CompoundTag) {
|
|
NBTTagCompound tag = new NBTTagCompound();
|
|
for (Map.Entry<String, Tag> entry : ((CompoundTag) foreign)
|
|
.getValue().entrySet()) {
|
|
tag.set(entry.getKey(), fromNative(entry.getValue()));
|
|
}
|
|
return tag;
|
|
} else if (foreign instanceof ByteTag) {
|
|
return new NBTTagByte(((ByteTag) foreign).getValue());
|
|
} else if (foreign instanceof ByteArrayTag) {
|
|
return new NBTTagByteArray(((ByteArrayTag) foreign).getValue());
|
|
} else if (foreign instanceof DoubleTag) {
|
|
return new NBTTagDouble(((DoubleTag) foreign).getValue());
|
|
} else if (foreign instanceof FloatTag) {
|
|
return new NBTTagFloat(((FloatTag) foreign).getValue());
|
|
} else if (foreign instanceof IntTag) {
|
|
return new NBTTagInt(((IntTag) foreign).getValue());
|
|
} else if (foreign instanceof IntArrayTag) {
|
|
return new NBTTagIntArray(((IntArrayTag) foreign).getValue());
|
|
} else if (foreign instanceof ListTag) {
|
|
NBTTagList tag = new NBTTagList();
|
|
ListTag<?> foreignList = (ListTag) foreign;
|
|
for (Tag t : foreignList.getValue()) {
|
|
tag.add(fromNative(t));
|
|
}
|
|
return tag;
|
|
} else if (foreign instanceof LongTag) {
|
|
return new NBTTagLong(((LongTag) foreign).getValue());
|
|
} else if (foreign instanceof ShortTag) {
|
|
return new NBTTagShort(((ShortTag) foreign).getValue());
|
|
} else if (foreign instanceof StringTag) {
|
|
return new NBTTagString(((StringTag) foreign).getValue());
|
|
} else if (foreign instanceof EndTag) {
|
|
try {
|
|
return (NBTBase) nbtCreateTagMethod.invoke(null, (byte) 0);
|
|
} catch (Exception e) {
|
|
return null;
|
|
}
|
|
} else {
|
|
throw new IllegalArgumentException("Don't know how to make NMS " + foreign.getClass().getCanonicalName());
|
|
}
|
|
}
|
|
|
|
} |