package jk_5.nailed.plugins.worldedit; import com.sk89q.jnbt.CompoundTag; import com.sk89q.worldedit.*; import com.sk89q.worldedit.blocks.BaseBlock; import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.blocks.LazyBlock; import com.sk89q.worldedit.entity.BaseEntity; import com.sk89q.worldedit.entity.Entity; import com.sk89q.worldedit.internal.Constants; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.util.TreeGenerator.TreeType; import com.sk89q.worldedit.world.AbstractWorld; import com.sk89q.worldedit.world.biome.BaseBiome; import com.sk89q.worldedit.world.registry.WorldData; import net.minecraft.block.Block; import net.minecraft.block.state.IBlockState; import net.minecraft.entity.EntityList; import net.minecraft.entity.item.EntityItem; import net.minecraft.inventory.IInventory; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.BlockPos; import net.minecraft.util.LongHashMap; import net.minecraft.world.ChunkCoordIntPair; import net.minecraft.world.World; import net.minecraft.world.chunk.Chunk; import net.minecraft.world.chunk.IChunkProvider; import net.minecraft.world.gen.ChunkProviderServer; import javax.annotation.Nullable; import java.lang.ref.WeakReference; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import static com.google.common.base.Preconditions.checkNotNull; /** * An adapter to Minecraft worlds for WorldEdit. */ public class WorldEditWorld extends AbstractWorld { private static final int UPDATE = 1, NOTIFY = 2, NOTIFY_CLIENT = 4; private static final Logger logger = Logger.getLogger(WorldEditWorld.class.getCanonicalName()); private final WeakReference<World> worldRef; /** * Construct a new world. * * @param world the world */ WorldEditWorld(World world) { checkNotNull(world); this.worldRef = new WeakReference<World>(world); } /** * Get the underlying handle to the world. * * @return the world * @throws WorldEditException thrown if a reference to the world was lost (i.e. world was unloaded) */ public World getWorldChecked() throws WorldEditException { World world = worldRef.get(); if (world != null) { return world; } else { throw new WorldReferenceLostException("The reference to the world was lost (i.e. the world may have been unloaded)"); } } /** * Get the underlying handle to the world. * * @return the world * @throws RuntimeException thrown if a reference to the world was lost (i.e. world was unloaded) */ public World getWorld() { World world = worldRef.get(); if (world != null) { return world; } else { throw new RuntimeException("The reference to the world was lost (i.e. the world may have been unloaded)"); } } @Override public String getName() { return getWorld().getWorldInfo().getWorldName(); } @Override public boolean setBlock(Vector position, BaseBlock block, boolean notifyAndLight) throws WorldEditException { checkNotNull(position); checkNotNull(block); World world = getWorldChecked(); int x = position.getBlockX(); int y = position.getBlockY(); int z = position.getBlockZ(); // First set the block Chunk chunk = world.getChunkFromChunkCoords(x >> 4, z >> 4); BlockPos pos = new BlockPos(x, y, z); IBlockState old = chunk.getBlockState(pos); IBlockState newState = Block.getBlockById(block.getId()).getStateFromMeta(block.getData()); IBlockState successState = chunk.setBlockState(pos, newState); boolean successful = successState != null; // Create the TileEntity if (successful) { if (block.hasNbtData()) { // Kill the old TileEntity world.removeTileEntity(pos); NBTTagCompound nativeTag = NBTConverter.toNative(block.getNbtData()); nativeTag.setString("id", block.getNbtId()); TileEntityUtils.setTileEntity(world, position, nativeTag); } } if(notifyAndLight){ if(!successful){ newState = old; } world.checkLight(pos); //// world.markBlockForUpdate(pos); world.notifyBlockOfStateChange(pos, newState.getBlock()); if(newState.getBlock().hasComparatorInputOverride()){ world.updateComparatorOutputLevel(pos, newState.getBlock()); } //world.markAndNotifyBlock(pos, chunk, old, newState, UPDATE | NOTIFY); } return successful; } @Override public int getBlockLightLevel(Vector position) { checkNotNull(position); return getWorld().getLight(new BlockPos(position.getBlockX(), position.getBlockY(), position.getBlockZ())); } @Override public boolean clearContainerBlockContents(Vector position) { checkNotNull(position); TileEntity tile = getWorld().getTileEntity(new BlockPos(position.getBlockX(), position.getBlockY(), position.getBlockZ())); if ((tile instanceof IInventory)) { IInventory inv = (IInventory) tile; int size = inv.getSizeInventory(); for (int i = 0; i < size; i++) { inv.setInventorySlotContents(i, null); } return true; } return false; } @Override public BaseBiome getBiome(Vector2D position) { checkNotNull(position); return new BaseBiome(getWorld().getBiomeGenForCoords(new BlockPos(position.getBlockX(), 0, position.getBlockZ())).biomeID); } @Override public boolean setBiome(Vector2D position, BaseBiome biome) { checkNotNull(position); checkNotNull(biome); Chunk chunk = getWorld().getChunkFromBlockCoords(new BlockPos(position.getBlockX(), 0, position.getBlockZ())); if ((chunk != null) && (chunk.isLoaded())) { chunk.getBiomeArray()[((position.getBlockZ() & 0xF) << 4 | position.getBlockX() & 0xF)] = (byte) biome.getId(); return true; } return false; } @Override public void dropItem(Vector position, BaseItemStack item) { checkNotNull(position); checkNotNull(item); if (item.getType() == 0) { return; } EntityItem entity = new EntityItem(getWorld(), position.getX(), position.getY(), position.getZ(), NailedWorldEditPlatform.toVanilla(item)); entity.setPickupDelay(10); getWorld().spawnEntityInWorld(entity); } @Override public boolean regenerate(Region region, EditSession editSession) { BaseBlock[] history = new BaseBlock[256 * (getMaxY() + 1)]; for (Vector2D chunk : region.getChunks()) { Vector min = new Vector(chunk.getBlockX() * 16, 0, chunk.getBlockZ() * 16); for (int x = 0; x < 16; x++) { for (int y = 0; y < getMaxY() + 1; y++) { for (int z = 0; z < 16; z++) { Vector pt = min.add(x, y, z); int index = y * 16 * 16 + z * 16 + x; history[index] = editSession.getBlock(pt); } } } try { Set<Vector2D> chunks = region.getChunks(); IChunkProvider provider = getWorld().getChunkProvider(); if (!(provider instanceof ChunkProviderServer)) { return false; } ChunkProviderServer chunkServer = (ChunkProviderServer) provider; Field u; try { u = ChunkProviderServer.class.getDeclaredField("field_73248_b"); // chunksToUnload } catch(NoSuchFieldException e) { u = ChunkProviderServer.class.getDeclaredField("chunksToUnload"); } u.setAccessible(true); Set<?> unloadQueue = (Set<?>) u.get(chunkServer); Field m; try { m = ChunkProviderServer.class.getDeclaredField("field_73244_f"); // loadedChunkHashMap } catch(NoSuchFieldException e) { m = ChunkProviderServer.class.getDeclaredField("loadedChunkHashMap"); } m.setAccessible(true); LongHashMap loadedMap = (LongHashMap) m.get(chunkServer); Field lc; try { lc = ChunkProviderServer.class.getDeclaredField("field_73245_g"); // loadedChunkHashMap } catch(NoSuchFieldException e) { lc = ChunkProviderServer.class.getDeclaredField("loadedChunks"); } lc.setAccessible(true); @SuppressWarnings("unchecked") List<Chunk> loaded = (List<Chunk>) lc.get(chunkServer); Field p; try { p = ChunkProviderServer.class.getDeclaredField("field_73246_d"); // currentChunkProvider } catch(NoSuchFieldException e) { p = ChunkProviderServer.class.getDeclaredField("currentChunkProvider"); } p.setAccessible(true); IChunkProvider chunkProvider = (IChunkProvider) p.get(chunkServer); for (Vector2D coord : chunks) { long pos = ChunkCoordIntPair.chunkXZ2Int(coord.getBlockX(), coord.getBlockZ()); Chunk mcChunk; if (chunkServer.chunkExists(coord.getBlockX(), coord.getBlockZ())) { mcChunk = chunkServer.loadChunk(coord.getBlockX(), coord.getBlockZ()); mcChunk.onChunkUnload(); } unloadQueue.remove(pos); loadedMap.remove(pos); mcChunk = chunkProvider.provideChunk(coord.getBlockX(), coord.getBlockZ()); loadedMap.add(pos, mcChunk); loaded.add(mcChunk); if (mcChunk != null) { mcChunk.onChunkLoad(); mcChunk.populateChunk(chunkProvider, chunkProvider, coord.getBlockX(), coord.getBlockZ()); } } } catch (Throwable t) { logger.log(Level.WARNING, "Failed to generate chunk", t); return false; } for (int x = 0; x < 16; x++) { for (int y = 0; y < getMaxY() + 1; y++) { for (int z = 0; z < 16; z++) { Vector pt = min.add(x, y, z); int index = y * 16 * 16 + z * 16 + x; if (!region.contains(pt)) editSession.smartSetBlock(pt, history[index]); else { editSession.rememberChange(pt, history[index], editSession.rawGetBlock(pt)); } } } } } return false; } @Override public boolean generateTree(TreeType type, EditSession editSession, Vector position) throws MaxChangedBlocksException { return false; } @Override public WorldData getWorldData() { return WorldEditWorldData.instance(); } @Override public boolean isValidBlockType(int id) { return (id == 0) || (net.minecraft.block.Block.getBlockById(id) != null); } @Override public BaseBlock getBlock(Vector position) { World world = getWorld(); BlockPos pos = new BlockPos(position.getBlockX(), position.getBlockY(), position.getBlockZ()); IBlockState state = world.getBlockState(pos); TileEntity tile = getWorld().getTileEntity(pos); if (tile != null) { return new TileEntityBaseBlock(Block.getIdFromBlock(state.getBlock()), state.getBlock().getMetaFromState(state), tile); } else { return new BaseBlock(Block.getIdFromBlock(state.getBlock()), state.getBlock().getMetaFromState(state)); } } @Override public BaseBlock getLazyBlock(Vector position) { World world = getWorld(); BlockPos pos = new BlockPos(position.getBlockX(), position.getBlockY(), position.getBlockZ()); IBlockState state = world.getBlockState(pos); return new LazyBlock(Block.getIdFromBlock(state.getBlock()), state.getBlock().getMetaFromState(state), this, position); } @Override public int hashCode() { return getWorld().hashCode(); } @Override public boolean equals(Object o) { if (o == null) { return false; } else if ((o instanceof WorldEditWorld)) { WorldEditWorld other = ((WorldEditWorld) o); World otherWorld = other.worldRef.get(); World thisWorld = worldRef.get(); return otherWorld != null && thisWorld != null && otherWorld.equals(thisWorld); } else if (o instanceof com.sk89q.worldedit.world.World) { return ((com.sk89q.worldedit.world.World) o).getName().equals(getName()); } else { return false; } } @Override @SuppressWarnings("unchecked") public List<? extends Entity> getEntities(Region region) { List<Entity> entities = new ArrayList<Entity>(); World world = getWorld(); List<net.minecraft.entity.Entity> ents = world.loadedEntityList; for (net.minecraft.entity.Entity entity : ents) { if (region.contains(new Vector(entity.posX, entity.posY, entity.posZ))) { entities.add(new WorldEditEntity(entity)); } } return entities; } @Override public List<? extends Entity> getEntities() { List<Entity> entities = new ArrayList<Entity>(); for (Object entity : getWorld().loadedEntityList) { entities.add(new WorldEditEntity((net.minecraft.entity.Entity) entity)); } return entities; } @Nullable @Override public Entity createEntity(Location location, BaseEntity entity) { World world = getWorld(); net.minecraft.entity.Entity createdEntity = EntityList.createEntityByName(entity.getTypeId(), world); if (createdEntity != null) { CompoundTag nativeTag = entity.getNbtData(); if (nativeTag != null) { NBTTagCompound tag = NBTConverter.toNative(entity.getNbtData()); for (String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) { tag.removeTag(name); } createdEntity.readFromNBT(tag); } createdEntity.setLocationAndAngles(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch()); world.spawnEntityInWorld(createdEntity); return new WorldEditEntity(createdEntity); } else { return null; } } /** * Thrown when the reference to the world is lost. */ private static class WorldReferenceLostException extends WorldEditException { private WorldReferenceLostException(String message) { super(message); } } public static WorldEditWorld wrap(net.minecraft.world.World world) { return new WorldEditWorld(world); } }