diff --git a/bukkit110/build.gradle b/bukkit110/build.gradle new file mode 100644 index 00000000..d2a3998f --- /dev/null +++ b/bukkit110/build.gradle @@ -0,0 +1,32 @@ +dependencies { + compile project(':bukkit0') + compile 'org.bukkit.craftbukkitv1_10:craftbukkitv1_10:1.10' +} + +processResources { + from('src/main/resources') { + include 'plugin.yml' + expand( + name: project.parent.name, + version: project.parent.version + ) + } +} + +apply plugin: 'com.github.johnrengelman.shadow' +// We only want the shadow jar produced +jar.enabled = false +shadowJar { + dependencies { + include(dependency(':bukkit0')) + include(dependency(':core')) + } + archiveName = "${parent.name}-${project.name}-${parent.version}.jar" + destinationDir = file '../target' +} +shadowJar.doLast { + task -> + ant.checksum file: task.archivePath +} + +build.dependsOn(shadowJar); \ No newline at end of file diff --git a/bukkit110/src/main/java/com/boydti/fawe/bukkit/v1_10/BukkitChunk_1_10.java b/bukkit110/src/main/java/com/boydti/fawe/bukkit/v1_10/BukkitChunk_1_10.java new file mode 100644 index 00000000..4341d34e --- /dev/null +++ b/bukkit110/src/main/java/com/boydti/fawe/bukkit/v1_10/BukkitChunk_1_10.java @@ -0,0 +1,128 @@ +package com.boydti.fawe.bukkit.v1_10; + +import com.boydti.fawe.FaweCache; +import com.boydti.fawe.example.CharFaweChunk; +import com.boydti.fawe.object.FaweQueue; +import com.boydti.fawe.util.MainUtil; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import net.minecraft.server.v1_10_R1.Block; +import net.minecraft.server.v1_10_R1.DataBits; +import net.minecraft.server.v1_10_R1.DataPalette; +import net.minecraft.server.v1_10_R1.DataPaletteBlock; +import net.minecraft.server.v1_10_R1.DataPaletteGlobal; +import net.minecraft.server.v1_10_R1.IBlockData; +import org.bukkit.Chunk; + +public class BukkitChunk_1_10 extends CharFaweChunk { + + public DataPaletteBlock[] sectionPalettes; + + /** + * A FaweSections object represents a chunk and the blocks that you wish to change in it. + * + * @param parent + * @param x + * @param z + */ + public BukkitChunk_1_10(FaweQueue parent, int x, int z) { + super(parent, x, z); + } + + @Override + public Chunk getNewChunk() { + return ((com.boydti.fawe.bukkit.v1_10.BukkitQueue_1_10) getParent()).getWorld().getChunkAt(getX(), getZ()); + } + + @Override + public CharFaweChunk copy(boolean shallow) { + BukkitChunk_1_10 value = (BukkitChunk_1_10) super.copy(shallow); + if (sectionPalettes != null) { + value.sectionPalettes = new DataPaletteBlock[16]; + try { + Field fieldBits = DataPaletteBlock.class.getDeclaredField("b"); + fieldBits.setAccessible(true); + Field fieldPalette = DataPaletteBlock.class.getDeclaredField("c"); + fieldPalette.setAccessible(true); + Field fieldSize = DataPaletteBlock.class.getDeclaredField("e"); + fieldSize.setAccessible(true); + for (int i = 0; i < sectionPalettes.length; i++) { + DataPaletteBlock current = sectionPalettes[i]; + if (current == null) { + continue; + } + // Clone palette + DataPalette currentPalette = (DataPalette) fieldPalette.get(current); + if (!(currentPalette instanceof DataPaletteGlobal)) { + current.a(128, null); + } + DataPaletteBlock paletteBlock = newDataPaletteBlock(); + currentPalette = (DataPalette) fieldPalette.get(current); + if (!(currentPalette instanceof DataPaletteGlobal)) { + throw new RuntimeException("Palette must be global!"); + } + fieldPalette.set(paletteBlock, currentPalette); + // Clone size + fieldSize.set(paletteBlock, fieldSize.get(current)); + // Clone palette + DataBits currentBits = (DataBits) fieldBits.get(current); + DataBits newBits = new DataBits(1, 0); + for (Field field : DataBits.class.getDeclaredFields()) { + field.setAccessible(true); + Object currentValue = field.get(currentBits); + if (currentValue instanceof long[]) { + currentValue = ((long[]) currentValue).clone(); + } + field.set(newBits, currentValue); + } + fieldBits.set(paletteBlock, newBits); + value.sectionPalettes[i] = paletteBlock; + } + } catch (Throwable e) { + MainUtil.handleError(e); + } + } + return value; + } + + public DataPaletteBlock newDataPaletteBlock() { + try { + return new DataPaletteBlock(); + } catch (Throwable e) { + try { + Constructor constructor = DataPaletteBlock.class.getDeclaredConstructor(IBlockData[].class); + return constructor.newInstance((Object) null); + } catch (Throwable e2) { + throw new RuntimeException(e2); + } + } + } + + public void optimize() { + if (sectionPalettes != null) { + return; + } + char[][] arrays = getCombinedIdArrays(); + IBlockData lastBlock = null; + char lastChar = Character.MAX_VALUE; + for (int layer = 0; layer < 16; layer++) { + if (getCount(layer) > 0) { + if (sectionPalettes == null) { + sectionPalettes = new DataPaletteBlock[16]; + } + DataPaletteBlock palette = newDataPaletteBlock(); + char[] blocks = getIdArray(layer); + for (int y = 0; y < 16; y++) { + for (int z = 0; z < 16; z++) { + for (int x = 0; x < 16; x++) { + char combinedId = blocks[FaweCache.CACHE_J[y][x][z]]; + if (combinedId > 1) { + palette.setBlock(x, y, z, Block.getById(combinedId >> 4).fromLegacyData(combinedId & 0xF)); + } + } + } + } + } + } + } +} diff --git a/bukkit110/src/main/java/com/boydti/fawe/bukkit/v1_10/BukkitMain_110.java b/bukkit110/src/main/java/com/boydti/fawe/bukkit/v1_10/BukkitMain_110.java new file mode 100644 index 00000000..fbdd9759 --- /dev/null +++ b/bukkit110/src/main/java/com/boydti/fawe/bukkit/v1_10/BukkitMain_110.java @@ -0,0 +1,19 @@ +package com.boydti.fawe.bukkit.v1_10; + +import com.boydti.fawe.bukkit.ABukkitMain; +import com.boydti.fawe.bukkit.v0.BukkitEditSessionWrapper_0; +import com.boydti.fawe.bukkit.v0.BukkitQueue_0; +import com.boydti.fawe.object.EditSessionWrapper; +import com.sk89q.worldedit.EditSession; + +public class BukkitMain_110 extends ABukkitMain { + @Override + public BukkitQueue_0 getQueue(String world) { + return new com.boydti.fawe.bukkit.v1_10.BukkitQueue_1_10(world); + } + + @Override + public EditSessionWrapper getEditSessionWrapper(EditSession session) { + return new BukkitEditSessionWrapper_0(session); + } +} diff --git a/bukkit110/src/main/java/com/boydti/fawe/bukkit/v1_10/BukkitQueue_1_10.java b/bukkit110/src/main/java/com/boydti/fawe/bukkit/v1_10/BukkitQueue_1_10.java new file mode 100644 index 00000000..983b9d2e --- /dev/null +++ b/bukkit110/src/main/java/com/boydti/fawe/bukkit/v1_10/BukkitQueue_1_10.java @@ -0,0 +1,747 @@ +package com.boydti.fawe.bukkit.v1_10; + +import com.boydti.fawe.Fawe; +import com.boydti.fawe.FaweCache; +import com.boydti.fawe.bukkit.v0.BukkitQueue_0; +import com.boydti.fawe.example.CharFaweChunk; +import com.boydti.fawe.object.BytePair; +import com.boydti.fawe.object.FaweChunk; +import com.boydti.fawe.object.PseudoRandom; +import com.boydti.fawe.object.RunnableVal; +import com.boydti.fawe.util.MainUtil; +import com.boydti.fawe.util.MathMan; +import com.boydti.fawe.util.ReflectionUtils; +import com.boydti.fawe.util.TaskManager; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.jnbt.ListTag; +import com.sk89q.jnbt.LongTag; +import com.sk89q.jnbt.StringTag; +import com.sk89q.jnbt.Tag; +import com.sk89q.worldedit.internal.Constants; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.UUID; +import net.minecraft.server.v1_10_R1.Block; +import net.minecraft.server.v1_10_R1.BlockPosition; +import net.minecraft.server.v1_10_R1.Blocks; +import net.minecraft.server.v1_10_R1.ChunkCoordIntPair; +import net.minecraft.server.v1_10_R1.ChunkSection; +import net.minecraft.server.v1_10_R1.DataBits; +import net.minecraft.server.v1_10_R1.DataPalette; +import net.minecraft.server.v1_10_R1.DataPaletteBlock; +import net.minecraft.server.v1_10_R1.Entity; +import net.minecraft.server.v1_10_R1.EntityHuman; +import net.minecraft.server.v1_10_R1.EntityPlayer; +import net.minecraft.server.v1_10_R1.EntityTracker; +import net.minecraft.server.v1_10_R1.EntityTrackerEntry; +import net.minecraft.server.v1_10_R1.EntityTypes; +import net.minecraft.server.v1_10_R1.IBlockData; +import net.minecraft.server.v1_10_R1.NBTTagCompound; +import net.minecraft.server.v1_10_R1.NibbleArray; +import net.minecraft.server.v1_10_R1.PacketPlayOutEntityDestroy; +import net.minecraft.server.v1_10_R1.PacketPlayOutMapChunk; +import net.minecraft.server.v1_10_R1.PlayerChunk; +import net.minecraft.server.v1_10_R1.PlayerChunkMap; +import net.minecraft.server.v1_10_R1.TileEntity; +import net.minecraft.server.v1_10_R1.WorldServer; +import org.bukkit.Chunk; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.World.Environment; +import org.bukkit.block.Biome; +import org.bukkit.craftbukkit.v1_10_R1.CraftChunk; +import org.bukkit.event.entity.CreatureSpawnEvent; + +public class BukkitQueue_1_10 extends BukkitQueue_0 { + + private IBlockData air; + + public BukkitQueue_1_10(final String world) { + super(world); + checkVersion("v1_10_R1"); + try { + Field fieldAir = DataPaletteBlock.class.getDeclaredField("a"); + fieldAir.setAccessible(true); + air = (IBlockData) fieldAir.get(null); + if (adapter == null) { + setupAdapter(new com.boydti.fawe.bukkit.v1_10.FaweAdapter_1_10()); + Fawe.debug("Using adapter: " + adapter); + Fawe.debug("========================================="); + } + } catch (Throwable e) { + throw new RuntimeException(e); + } + } + + @Override + public ChunkSection[] getCachedSections(World world, int cx, int cz) { + CraftChunk chunk = (CraftChunk) world.getChunkAt(cx, cz); + return chunk.getHandle().getSections(); + } + + @Override + public DataPaletteBlock getCachedSection(ChunkSection[] chunkSections, int cy) { + ChunkSection nibble = chunkSections[cy]; + return nibble != null ? nibble.getBlocks() : null; + } + + @Override + public int getCombinedId4Data(DataPaletteBlock lastSection, int x, int y, int z) { + IBlockData ibd = lastSection.a(x & 15, y & 15, z & 15); + Block block = ibd.getBlock(); + int id = Block.getId(block); + if (FaweCache.hasData(id)) { + return (id << 4) + block.toLegacyData(ibd); + } else { + return id << 4; + } + } + + @Override + public void refreshChunk(World world, Chunk chunk) { + if (!chunk.isLoaded()) { + return; + } + net.minecraft.server.v1_10_R1.Chunk nmsChunk = ((CraftChunk) chunk).getHandle(); + ChunkCoordIntPair pos = nmsChunk.k(); // getPosition() + WorldServer w = (WorldServer) nmsChunk.getWorld(); + PlayerChunkMap chunkMap = w.getPlayerChunkMap(); + PlayerChunk playerChunk = chunkMap.getChunk(pos.x, pos.z); + if (playerChunk == null) { + return; + } + HashSet set = new HashSet(playerChunk.c); + EntityTracker tracker = w.getTracker(); + // Get players + HashSet players = new HashSet<>(); + for (EntityHuman human : w.players) { + if (set.contains(human)) { + players.add((EntityPlayer) human); + } + } + if (players.size() == 0) { + return; + } + HashSet entities = new HashSet<>(); + List[] entitieSlices = playerChunk.chunk.getEntitySlices(); + for (List slice : entitieSlices) { + if (slice == null) { + continue; + } + for (Entity ent : slice) { + EntityTrackerEntry entry = tracker.trackedEntities.get(ent.getId()); + if (entry == null) { + continue; + } + entities.add(entry); + PacketPlayOutEntityDestroy packet = new PacketPlayOutEntityDestroy(ent.getId()); + for (EntityPlayer player : players) { + player.playerConnection.sendPacket(packet); + } + } + } + for (EntityPlayer player : players) { + player.playerConnection.networkManager.a(); + } + // Send chunks + PacketPlayOutMapChunk packet = new PacketPlayOutMapChunk(playerChunk.chunk, 65535); + for (EntityPlayer player : players) { + player.playerConnection.sendPacket(packet); + } + // send ents + for (List slice : entitieSlices) { + if (slice == null) { + continue; + } + for (Entity ent : slice) { + EntityTrackerEntry entry = tracker.trackedEntities.get(ent.getId()); + if (entry == null) { + continue; + } + try { + TaskManager.IMP.later(new Runnable() { + @Override + public void run() { + for (EntityPlayer player : players) { + boolean result = entry.trackedPlayers.remove(player); + if (result && ent != player) { + entry.updatePlayer(player); + } + } + } + }, 2); + } catch (Throwable e) { + MainUtil.handleError(e); + } + } + } + } + + @Override + public boolean fixLighting(final FaweChunk pc, RelightMode mode) { + if (mode == RelightMode.NONE) { + return true; + } + try { + CharFaweChunk bc = (CharFaweChunk) pc; + Chunk chunk = (Chunk) bc.getChunk(); + if (!chunk.isLoaded()) { + if (Fawe.get().getMainThread() != Thread.currentThread()) { + return false; + } + chunk.load(false); + } + net.minecraft.server.v1_10_R1.Chunk c = ((CraftChunk) chunk).getHandle(); + final boolean flag = chunk.getWorld().getEnvironment() == Environment.NORMAL; + ChunkSection[] sections = c.getSections(); + if (mode == RelightMode.ALL) { + for (int i = 0; i < sections.length; i++) { + ChunkSection section = sections[i]; + if (section != null) { + section.a(new NibbleArray()); + if (flag) { + section.b(new NibbleArray()); + } + } + } + } + if (flag) { + if (mode == RelightMode.ALL) { + c.initLighting(); + } else { + int i = c.g(); + for (int x = 0; x < 16; ++x) { + for (int z = 0; z < 16; ++z) { + int l = 15; + int y = i + 16 - 1; + do { + int opacity = c.a(x, y, z).c(); + if (opacity == 0 && l != 15) { + opacity = 1; + } + l -= opacity; + if (l > 0) { + ChunkSection section = sections[y >> 4]; + if (section != null) { + section.a(x, y & 15, z, l); + } + } + --y; + } while (y > 0 && l > 0); + } + } + } + } + if (((bc.getTotalRelight() == 0) && mode == RelightMode.MINIMAL)) { + return true; + } + if (mode == RelightMode.ALL) { + bc = getPrevious(bc, c.getSections(), null, null, null, true); + } + int total = bc.getTotalCount(); + net.minecraft.server.v1_10_R1.World w = c.world; + final int X = chunk.getX() << 4; + final int Z = chunk.getZ() << 4; + for (int j = sections.length - 1; j >= 0; j--) { + final Object section = sections[j]; + if (section == null) { + continue; + } + if (((bc.getRelight(j) == 0) && mode == RelightMode.MINIMAL) || (bc.getCount(j) == 0 && mode != RelightMode.ALL) || ((bc.getCount(j) >= 4096) && (bc.getAir(j) == 0)) || bc.getAir(j) == 4096) { + continue; + } + final char[] array = bc.getIdArray(j); + if (array == null) { + continue; + } + if (mode == RelightMode.ALL) { + for (int k = array.length - 1; k >= 0; k--) { + final int x = FaweCache.CACHE_X[j][k]; + final int y = FaweCache.CACHE_Y[j][k]; + final int z = FaweCache.CACHE_Z[j][k]; + if (this.isSurrounded(bc.getCombinedIdArrays(), x, y, z)) { + continue; + } + pos.c(X + x, y, Z + z); + w.w(pos); + } + continue; + } + for (int k = array.length - 1; k >= 0; k--) { + final int i = array[k]; + final short id = (short) (i >> 4); + switch (id) { // Lighting + case 0: + continue; + default: + if (mode == RelightMode.MINIMAL) { + continue; + } + if (PseudoRandom.random.random(3) != 0) { + continue; + } + case 10: + case 11: + case 39: + case 40: + case 50: + case 51: + case 62: + case 74: + case 76: + case 89: + case 122: + case 124: + case 130: + case 138: + case 169: + final int x = FaweCache.CACHE_X[j][k]; + final int y = FaweCache.CACHE_Y[j][k]; + final int z = FaweCache.CACHE_Z[j][k]; + if (this.isSurrounded(bc.getCombinedIdArrays(), x, y, z)) { + continue; + } + pos.c(X + x, y, Z + z); + w.w(pos); + } + } + } + return true; + } catch (Throwable e) { + if (Thread.currentThread() == Fawe.get().getMainThread()) { + MainUtil.handleError(e); + } + } + return false; + } + + public boolean isSurrounded(final char[][] sections, final int x, final int y, final int z) { + return this.isSolid(this.getId(sections, x, y + 1, z)) + && this.isSolid(this.getId(sections, x + 1, y - 1, z)) + && this.isSolid(this.getId(sections, x - 1, y, z)) + && this.isSolid(this.getId(sections, x, y, z + 1)) + && this.isSolid(this.getId(sections, x, y, z - 1)); + } + + public boolean isSolid(final int i) { + if (i != 0) { + final Material material = Material.getMaterial(i); + return (material != null) && Material.getMaterial(i).isOccluding(); + } + return false; + } + + public int getId(final char[][] sections, final int x, final int y, final int z) { + if ((x < 0) || (x > 15) || (z < 0) || (z > 15)) { + return 1; + } + if ((y < 0) || (y > 255)) { + return 1; + } + final int i = FaweCache.CACHE_I[y][x][z]; + final char[] section = sections[i]; + if (section == null) { + return 0; + } + final int j = FaweCache.CACHE_J[y][x][z]; + return section[j] >> 4; + } + + public void setCount(int tickingBlockCount, int nonEmptyBlockCount, ChunkSection section) throws NoSuchFieldException, IllegalAccessException { + Class clazz = section.getClass(); + Field fieldTickingBlockCount = clazz.getDeclaredField("tickingBlockCount"); + Field fieldNonEmptyBlockCount = clazz.getDeclaredField("nonEmptyBlockCount"); + fieldTickingBlockCount.setAccessible(true); + fieldNonEmptyBlockCount.setAccessible(true); + fieldTickingBlockCount.set(section, tickingBlockCount); + fieldNonEmptyBlockCount.set(section, nonEmptyBlockCount); + } + + public void setPalette(ChunkSection section, DataPaletteBlock palette) throws NoSuchFieldException, IllegalAccessException { + Field fieldSection = ChunkSection.class.getDeclaredField("blockIds"); + fieldSection.setAccessible(true); + fieldSection.set(section, palette); + } + + public ChunkSection newChunkSection(int y2, boolean flag, char[] array) { + try { + if (array == null) { + return new ChunkSection(y2, flag); + } else { + return new ChunkSection(y2, flag, array); + } + } catch (Throwable e) { + try { + if (array == null) { + Constructor constructor = ChunkSection.class.getDeclaredConstructor(int.class, boolean.class, IBlockData[].class); + return constructor.newInstance(y2, flag, (IBlockData[]) null); + } else { + Constructor constructor = ChunkSection.class.getDeclaredConstructor(int.class, boolean.class, char[].class, IBlockData[].class); + return constructor.newInstance(y2, flag, array, (IBlockData[]) null); + } + } catch (Throwable e2) { + throw new RuntimeException(e2); + } + } + } + + @Override + public CharFaweChunk getPrevious(CharFaweChunk fs, ChunkSection[] sections, Map tilesGeneric, Collection[] entitiesGeneric, Set createdEntities, boolean all) throws Exception { + Map tiles = (Map) tilesGeneric; + Collection[] entities = (Collection[]) entitiesGeneric; + CharFaweChunk previous = (CharFaweChunk) getFaweChunk(fs.getX(), fs.getZ()); + // Copy blocks + char[][] idPrevious = new char[16][]; + for (int layer = 0; layer < sections.length; layer++) { + if (fs.getCount(layer) != 0 || all) { + ChunkSection section = sections[layer]; + if (section != null) { + short solid = 0; + char[] previousLayer = idPrevious[layer] = new char[4096]; + DataPaletteBlock blocks = section.getBlocks(); + for (int j = 0; j < 4096; j++) { + int x = FaweCache.CACHE_X[0][j]; + int y = FaweCache.CACHE_Y[0][j]; + int z = FaweCache.CACHE_Z[0][j]; + IBlockData ibd = blocks.a(x, y, z); + Block block = ibd.getBlock(); + int combined = Block.getId(block); + if (FaweCache.hasData(combined)) { + combined = (combined << 4) + block.toLegacyData(ibd); + } else { + combined = combined << 4; + } + if (combined > 1) { + solid++; + } + previousLayer[j] = (char) combined; + } + previous.count[layer] = solid; + previous.air[layer] = (short) (4096 - solid); + } + } + } + previous.ids = idPrevious; + // Copy tiles + if (tiles != null) { + for (Map.Entry entry : tiles.entrySet()) { + TileEntity tile = entry.getValue(); + NBTTagCompound tag = new NBTTagCompound(); + BlockPosition pos = entry.getKey(); + CompoundTag nativeTag = getTag(tile); + previous.setTile(pos.getX() & 15, pos.getY(), pos.getZ() & 15, nativeTag); + } + } + // Copy entities + if (entities != null) { + for (Collection entityList : entities) { + for (Entity ent : entityList) { + if (ent instanceof EntityPlayer || (!createdEntities.isEmpty() && createdEntities.contains(ent.getUniqueID()))) { + continue; + } + int x = ((int) Math.round(ent.locX) & 15); + int z = ((int) Math.round(ent.locZ) & 15); + int y = (int) Math.round(ent.locY); + int i = FaweCache.CACHE_I[y][x][z]; + char[] array = fs.getIdArray(i); + if (array == null) { + continue; + } + int j = FaweCache.CACHE_J[y][x][z]; + if (array[j] != 0) { + String id = EntityTypes.b(ent); + if (id != null) { + NBTTagCompound tag = new NBTTagCompound(); + ent.e(tag); // readEntityIntoTag + CompoundTag nativeTag = (CompoundTag) methodToNative.invoke(adapter, tag); + Map map = ReflectionUtils.getMap(nativeTag.getValue()); + map.put("Id", new StringTag(id)); + previous.setEntity(nativeTag); + } + } + } + } + } + return previous; + } + + private BlockPosition.MutableBlockPosition pos = new BlockPosition.MutableBlockPosition(0, 0, 0); + + @Override + public CompoundTag getTileEntity(Chunk chunk, int x, int y, int z) { + Map tiles = ((CraftChunk) chunk).getHandle().getTileEntities(); + pos.c(x, y, z); + TileEntity tile = tiles.get(pos); + return tile != null ? getTag(tile) : null; + } + + public CompoundTag getTag(TileEntity tile) { + try { + NBTTagCompound tag = new NBTTagCompound(); + tile.save(tag); // readTagIntoEntity + return (CompoundTag) methodToNative.invoke(adapter, tag); + } catch (Exception e) { + MainUtil.handleError(e); + return null; + } + } + + @Override + public Chunk getChunk(World world, int x, int z) { + return world.getChunkAt(x, z); + } + + @Override + public boolean setComponents(final FaweChunk fc, RunnableVal changeTask) { + final com.boydti.fawe.bukkit.v1_10.BukkitChunk_1_10 fs = (com.boydti.fawe.bukkit.v1_10.BukkitChunk_1_10) fc; + final Chunk chunk = (Chunk) fs.getChunk(); + final World world = chunk.getWorld(); + chunk.load(true); + try { + final boolean flag = world.getEnvironment() == Environment.NORMAL; + net.minecraft.server.v1_10_R1.Chunk nmsChunk = ((CraftChunk) chunk).getHandle(); + net.minecraft.server.v1_10_R1.World nmsWorld = nmsChunk.world; + ChunkSection[] sections = nmsChunk.getSections(); + Class clazzChunk = nmsChunk.getClass(); + final Field ef = clazzChunk.getDeclaredField("entitySlices"); + final Collection[] entities = (Collection[]) ef.get(nmsChunk); + Map tiles = nmsChunk.getTileEntities(); + // Remove entities + for (int i = 0; i < entities.length; i++) { + int count = fs.getCount(i); + if (count == 0) { + continue; + } else if (count >= 4096) { + entities[i].clear(); + } else { + char[] array = fs.getIdArray(i); + Collection ents = new ArrayList<>(entities[i]); + for (Entity entity : ents) { + if (entity instanceof EntityPlayer) { + continue; + } + int x = ((int) Math.round(entity.locX) & 15); + int z = ((int) Math.round(entity.locZ) & 15); + int y = (int) Math.round(entity.locY); + if (array == null || y < 0 || y > 255) { + continue; + } + if (y < 0 || y > 255 || array[FaweCache.CACHE_J[y][x][z]] != 0) { + nmsWorld.removeEntity(entity); + } + } + } + } + HashSet entsToRemove = fs.getEntityRemoves(); + if (entsToRemove.size() > 0) { + for (int i = 0; i < entities.length; i++) { + Collection ents = new ArrayList<>(entities[i]); + for (Entity entity : ents) { + if (entsToRemove.contains(entity.getUniqueID())) { + nmsWorld.removeEntity(entity); + } + } + } + } + // Set entities + Set createdEntities = new HashSet<>(); + Set entitiesToSpawn = fs.getEntities(); + for (CompoundTag nativeTag : entitiesToSpawn) { + Map entityTagMap = ReflectionUtils.getMap(nativeTag.getValue()); + StringTag idTag = (StringTag) entityTagMap.get("Id"); + ListTag posTag = (ListTag) entityTagMap.get("Pos"); + ListTag rotTag = (ListTag) entityTagMap.get("Rotation"); + if (idTag == null || posTag == null || rotTag == null) { + Fawe.debug("Unknown entity tag: " + nativeTag); + continue; + } + double x = posTag.getDouble(0); + double y = posTag.getDouble(1); + double z = posTag.getDouble(2); + float yaw = rotTag.getFloat(0); + float pitch = rotTag.getFloat(1); + String id = idTag.getValue(); + Entity entity = EntityTypes.createEntityByName(id, nmsWorld); + if (entity != null) { + UUID uuid = entity.getUniqueID(); + entityTagMap.put("UUIDMost", new LongTag(uuid.getMostSignificantBits())); + entityTagMap.put("UUIDLeast", new LongTag(uuid.getLeastSignificantBits())); + if (nativeTag != null) { + NBTTagCompound tag = (NBTTagCompound) methodFromNative.invoke(adapter, nativeTag); + for (String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) { + tag.remove(name); + } + entity.f(tag); + } + entity.setLocation(x, y, z, yaw, pitch); + nmsWorld.addEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM); + createdEntities.add(entity.getUniqueID()); + } + } + // Change task? + if (changeTask != null) { + CharFaweChunk previous = getPrevious(fs, sections, tiles, entities, createdEntities, false); + changeTask.run(previous); + } + // Trim tiles + Set> entryset = tiles.entrySet(); + Iterator> iterator = entryset.iterator(); + while (iterator.hasNext()) { + Map.Entry tile = iterator.next(); + BlockPosition pos = tile.getKey(); + int lx = pos.getX() & 15; + int ly = pos.getY(); + int lz = pos.getZ() & 15; + int j = FaweCache.CACHE_I[ly][lx][lz]; + char[] array = fs.getIdArray(j); + if (array == null) { + continue; + } + int k = FaweCache.CACHE_J[ly][lx][lz]; + if (array[k] != 0) { + tile.getValue().invalidateBlockCache(); + iterator.remove(); + } + } + // Set blocks + for (int j = 0; j < sections.length; j++) { + int count = fs.getCount(j); + if (count == 0) { + continue; + } + final char[] array = fs.getIdArray(j); + if (array == null) { + continue; + } + ChunkSection section = sections[j]; + if (section == null) { + if (fs.sectionPalettes != null && fs.sectionPalettes[j] != null) { + section = sections[j] = newChunkSection(j << 4, flag, null); + setPalette(section, fs.sectionPalettes[j]); + setCount(0, count - fs.getAir(j), section); + continue; + } else { + sections[j] = newChunkSection(j << 4, flag, array); + } + continue; + } else if (count >= 4096) { + if (fs.sectionPalettes != null && fs.sectionPalettes[j] != null) { + setPalette(section, fs.sectionPalettes[j]); + setCount(0, count - fs.getAir(j), section); + continue; + } else { + sections[j] = newChunkSection(j << 4, flag, array); + } + continue; + } + DataPaletteBlock nibble = section.getBlocks(); + Field fieldBits = nibble.getClass().getDeclaredField("b"); + fieldBits.setAccessible(true); + DataBits bits = (DataBits) fieldBits.get(nibble); + + Field fieldPalette = nibble.getClass().getDeclaredField("c"); + fieldPalette.setAccessible(true); + DataPalette palette = (DataPalette) fieldPalette.get(nibble); + int nonEmptyBlockCount = 0; + for (int y = 0; y < 16; y++) { + for (int z = 0; z < 16; z++) { + for (int x = 0; x < 16; x++) { + char combinedId = array[FaweCache.CACHE_J[y][x][z]]; + switch (combinedId) { + case 0: + IBlockData existing = nibble.a(x, y, z); + if (existing != air) { + nonEmptyBlockCount++; + } + continue; + case 1: + nibble.setBlock(x, y, z, Blocks.AIR.getBlockData()); + continue; + default: + nonEmptyBlockCount++; + nibble.setBlock(x, y, z, Block.getById(combinedId >> 4).fromLegacyData(combinedId & 0xF)); + } + } + } + } + setCount(0, nonEmptyBlockCount, section); + } + // Set biomes + int[][] biomes = fs.biomes; + if (biomes != null) { + for (int x = 0; x < 16; x++) { + int[] array = biomes[x]; + if (array == null) { + continue; + } + for (int z = 0; z < 16; z++) { + int biome = array[z]; + if (biome == 0) { + continue; + } + nmsChunk.getBiomeIndex()[((z & 0xF) << 4 | x & 0xF)] = (byte) biome; + } + } + } + // Set tiles + Map tilesToSpawn = fs.getTiles(); + int bx = fs.getX() << 4; + int bz = fs.getZ() << 4; + + for (Map.Entry entry : tilesToSpawn.entrySet()) { + CompoundTag nativeTag = entry.getValue(); + BytePair pair = entry.getKey(); + BlockPosition pos = new BlockPosition(MathMan.unpair16x(pair.pair[0]) + bx, pair.pair[1] & 0xFF, MathMan.unpair16y(pair.pair[0]) + bz); // Set pos + TileEntity tileEntity = nmsWorld.getTileEntity(pos); + if (tileEntity != null) { + NBTTagCompound tag = (NBTTagCompound) methodFromNative.invoke(adapter, nativeTag); + tileEntity.a(tag); // ReadTagIntoTile + } + } + } catch (Throwable e) { + MainUtil.handleError(e); + } + final int[][] biomes = fs.getBiomeArray(); + final Biome[] values = Biome.values(); + if (biomes != null) { + for (int x = 0; x < 16; x++) { + final int[] array = biomes[x]; + if (array == null) { + continue; + } + for (int z = 0; z < 16; z++) { + final int biome = array[z]; + if (biome == 0) { + continue; + } + chunk.getBlock(x, 0, z).setBiome(values[biome]); + } + } + } + sendChunk(fs, null); + return true; + } + + @Deprecated + public boolean unloadChunk(final String world, final Chunk chunk) { + net.minecraft.server.v1_10_R1.Chunk c = ((CraftChunk) chunk).getHandle(); + c.mustSave = false; + if (chunk.isLoaded()) { + chunk.unload(false, false); + } + return true; + } + + @Override + public FaweChunk getFaweChunk(int x, int z) { + return new com.boydti.fawe.bukkit.v1_10.BukkitChunk_1_10(this, x, z); + } +} diff --git a/bukkit110/src/main/java/com/boydti/fawe/bukkit/v1_10/FaweAdapter_1_10.java b/bukkit110/src/main/java/com/boydti/fawe/bukkit/v1_10/FaweAdapter_1_10.java new file mode 100644 index 00000000..11da9e86 --- /dev/null +++ b/bukkit110/src/main/java/com/boydti/fawe/bukkit/v1_10/FaweAdapter_1_10.java @@ -0,0 +1,373 @@ +package com.boydti.fawe.bukkit.v1_10; + +import com.google.common.base.Preconditions; +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.bukkit.adapter.BukkitImplAdapter; +import com.sk89q.worldedit.entity.BaseEntity; +import com.sk89q.worldedit.internal.Constants; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.annotation.Nullable; +import net.minecraft.server.v1_10_R1.BiomeBase; +import net.minecraft.server.v1_10_R1.BlockPosition; +import net.minecraft.server.v1_10_R1.EntityTypes; +import net.minecraft.server.v1_10_R1.NBTBase; +import net.minecraft.server.v1_10_R1.NBTTagByte; +import net.minecraft.server.v1_10_R1.NBTTagByteArray; +import net.minecraft.server.v1_10_R1.NBTTagCompound; +import net.minecraft.server.v1_10_R1.NBTTagDouble; +import net.minecraft.server.v1_10_R1.NBTTagEnd; +import net.minecraft.server.v1_10_R1.NBTTagFloat; +import net.minecraft.server.v1_10_R1.NBTTagInt; +import net.minecraft.server.v1_10_R1.NBTTagIntArray; +import net.minecraft.server.v1_10_R1.NBTTagList; +import net.minecraft.server.v1_10_R1.NBTTagLong; +import net.minecraft.server.v1_10_R1.NBTTagShort; +import net.minecraft.server.v1_10_R1.NBTTagString; +import net.minecraft.server.v1_10_R1.TileEntity; +import net.minecraft.server.v1_10_R1.World; +import net.minecraft.server.v1_10_R1.WorldServer; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.block.Biome; +import org.bukkit.block.Block; +import org.bukkit.craftbukkit.v1_10_R1.CraftServer; +import org.bukkit.craftbukkit.v1_10_R1.CraftWorld; +import org.bukkit.craftbukkit.v1_10_R1.block.CraftBlock; +import org.bukkit.craftbukkit.v1_10_R1.entity.CraftEntity; +import org.bukkit.event.entity.CreatureSpawnEvent; + +public final class FaweAdapter_1_10 implements BukkitImplAdapter +{ + private final Logger logger = Logger.getLogger(getClass().getCanonicalName()); + private final Field nbtListTagListField; + private final Method nbtCreateTagMethod; + + public FaweAdapter_1_10() + throws NoSuchFieldException, NoSuchMethodException + { + CraftServer.class.cast(Bukkit.getServer()); + + this.nbtListTagListField = NBTTagList.class.getDeclaredField("list"); + this.nbtListTagListField.setAccessible(true); + + this.nbtCreateTagMethod = NBTBase.class.getDeclaredMethod("createTag", new Class[] { Byte.TYPE }); + this.nbtCreateTagMethod.setAccessible(true); + } + + private static void readTagIntoTileEntity(NBTTagCompound tag, TileEntity tileEntity) + { + tileEntity.a(tag); + } + + private static void readTileEntityIntoTag(TileEntity tileEntity, NBTTagCompound tag) + { + tileEntity.save(tag); + } + + @Nullable + private static String getEntityId(net.minecraft.server.v1_10_R1.Entity entity) + { + return EntityTypes.b(entity); + } + + @Nullable + private static net.minecraft.server.v1_10_R1.Entity createEntityFromId(String id, World world) + { + return EntityTypes.createEntityByName(id, world); + } + + private static void readTagIntoEntity(NBTTagCompound tag, net.minecraft.server.v1_10_R1.Entity entity) + { + entity.f(tag); + } + + private static void readEntityIntoTag(net.minecraft.server.v1_10_R1.Entity entity, NBTTagCompound tag) + { + entity.e(tag); + } + + public int getBlockId(Material material) + { + return material.getId(); + } + + public Material getMaterial(int id) + { + return Material.getMaterial(id); + } + + public int getBiomeId(Biome biome) + { + BiomeBase mcBiome = CraftBlock.biomeToBiomeBase(biome); + return mcBiome != null ? BiomeBase.a(mcBiome) : 0; + } + + public Biome getBiome(int id) + { + BiomeBase mcBiome = BiomeBase.getBiome(id); + return CraftBlock.biomeBaseToBiome(mcBiome); + } + + public BaseBlock getBlock(Location location) + { + Preconditions.checkNotNull(location); + + CraftWorld craftWorld = (CraftWorld)location.getWorld(); + int x = location.getBlockX(); + int y = location.getBlockY(); + int z = location.getBlockZ(); + + Block bukkitBlock = location.getBlock(); + BaseBlock block = new BaseBlock(bukkitBlock.getTypeId(), bukkitBlock.getData()); + + TileEntity te = craftWorld.getHandle().getTileEntity(new BlockPosition(x, y, z)); + if (te != null) + { + NBTTagCompound tag = new NBTTagCompound(); + readTileEntityIntoTag(te, tag); + block.setNbtData((CompoundTag)toNative(tag)); + } + return block; + } + + public boolean setBlock(Location location, BaseBlock block, boolean notifyAndLight) + { + Preconditions.checkNotNull(location); + Preconditions.checkNotNull(block); + + CraftWorld craftWorld = (CraftWorld)location.getWorld(); + int x = location.getBlockX(); + int y = location.getBlockY(); + int z = location.getBlockZ(); + + boolean changed = location.getBlock().setTypeIdAndData(block.getId(), (byte)block.getData(), notifyAndLight); + + CompoundTag nativeTag = block.getNbtData(); + if (nativeTag != null) + { + 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); + } + } + return changed; + } + + public BaseEntity getEntity(org.bukkit.entity.Entity entity) + { + Preconditions.checkNotNull(entity); + + CraftEntity craftEntity = (CraftEntity)entity; + net.minecraft.server.v1_10_R1.Entity mcEntity = craftEntity.getHandle(); + + String id = getEntityId(mcEntity); + if (id != null) + { + NBTTagCompound tag = new NBTTagCompound(); + readEntityIntoTag(mcEntity, tag); + return new BaseEntity(id, (CompoundTag)toNative(tag)); + } + return null; + } + + @Nullable + public org.bukkit.entity.Entity createEntity(Location location, BaseEntity state) + { + Preconditions.checkNotNull(location); + Preconditions.checkNotNull(state); + + CraftWorld craftWorld = (CraftWorld)location.getWorld(); + WorldServer worldServer = craftWorld.getHandle(); + + net.minecraft.server.v1_10_R1.Entity createdEntity = createEntityFromId(state.getTypeId(), 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, CreatureSpawnEvent.SpawnReason.CUSTOM); + return createdEntity.getBukkitEntity(); + } + return null; + } + + private Tag toNative(NBTBase foreign) + { + if (foreign == null) { + return null; + } + if ((foreign instanceof NBTTagCompound)) + { + Map values = new HashMap(); + Set foreignKeys = ((NBTTagCompound)foreign).c(); + for (String str : foreignKeys) + { + NBTBase base = ((NBTTagCompound)foreign).get(str); + values.put(str, toNative(base)); + } + return new CompoundTag(values); + } + if ((foreign instanceof NBTTagByte)) { + return new ByteTag(((NBTTagByte)foreign).g()); + } + if ((foreign instanceof NBTTagByteArray)) { + return new ByteArrayTag(((NBTTagByteArray)foreign).c()); + } + if ((foreign instanceof NBTTagDouble)) { + return new DoubleTag(((NBTTagDouble)foreign).h()); + } + if ((foreign instanceof NBTTagFloat)) { + return new FloatTag(((NBTTagFloat)foreign).i()); + } + if ((foreign instanceof NBTTagInt)) { + return new IntTag(((NBTTagInt)foreign).e()); + } + if ((foreign instanceof NBTTagIntArray)) { + return new IntArrayTag(((NBTTagIntArray)foreign).d()); + } + if ((foreign instanceof NBTTagList)) { + try + { + return toNativeList((NBTTagList)foreign); + } + catch (Throwable e) + { + this.logger.log(Level.WARNING, "Failed to convert NBTTagList", e); + return new ListTag(ByteTag.class, new ArrayList()); + } + } + if ((foreign instanceof NBTTagLong)) { + return new LongTag(((NBTTagLong)foreign).d()); + } + if ((foreign instanceof NBTTagShort)) { + return new ShortTag(((NBTTagShort)foreign).f()); + } + if ((foreign instanceof NBTTagString)) { + return new StringTag(((NBTTagString)foreign).c_()); + } + if ((foreign instanceof NBTTagEnd)) { + return new EndTag(); + } + throw new IllegalArgumentException("Don't know how to make native " + foreign.getClass().getCanonicalName()); + } + + private ListTag toNativeList(NBTTagList foreign) + throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException + { + List values = new ArrayList(); + int type = foreign.getTypeId(); + + List foreignList = (List)this.nbtListTagListField.get(foreign); + for (int i = 0; i < foreign.size(); i++) + { + NBTBase element = (NBTBase)foreignList.get(i); + values.add(toNative(element)); + } + Class cls = NBTConstants.getClassFromType(type); + return new ListTag(cls, values); + } + + private NBTBase fromNative(Tag foreign) + { + if (foreign == null) { + return null; + } + Map.Entry entry; + if ((foreign instanceof CompoundTag)) + { + NBTTagCompound tag = new NBTTagCompound(); + for (Iterator localIterator = ((CompoundTag)foreign) + .getValue().entrySet().iterator(); localIterator.hasNext();) + { + entry = (Map.Entry)localIterator.next(); + + tag.set((String)entry.getKey(), fromNative((Tag)entry.getValue())); + } + return tag; + } + if ((foreign instanceof ByteTag)) { + return new NBTTagByte(((ByteTag)foreign).getValue().byteValue()); + } + if ((foreign instanceof ByteArrayTag)) { + return new NBTTagByteArray(((ByteArrayTag)foreign).getValue()); + } + if ((foreign instanceof DoubleTag)) { + return new NBTTagDouble(((DoubleTag)foreign).getValue().doubleValue()); + } + if ((foreign instanceof FloatTag)) { + return new NBTTagFloat(((FloatTag)foreign).getValue().floatValue()); + } + if ((foreign instanceof IntTag)) { + return new NBTTagInt(((IntTag)foreign).getValue().intValue()); + } + if ((foreign instanceof IntArrayTag)) { + return new NBTTagIntArray(((IntArrayTag)foreign).getValue()); + } + if ((foreign instanceof ListTag)) + { + NBTTagList tag = new NBTTagList(); + ListTag foreignList = (ListTag)foreign; + for (Tag t : foreignList.getValue()) { + tag.add(fromNative(t)); + } + return tag; + } + if ((foreign instanceof LongTag)) { + return new NBTTagLong(((LongTag)foreign).getValue().longValue()); + } + if ((foreign instanceof ShortTag)) { + return new NBTTagShort(((ShortTag)foreign).getValue().shortValue()); + } + if ((foreign instanceof StringTag)) { + return new NBTTagString(((StringTag)foreign).getValue()); + } + if ((foreign instanceof EndTag)) { + try + { + return (NBTBase)this.nbtCreateTagMethod.invoke(null, new Object[] { Byte.valueOf((byte) 0) }); + } + catch (Exception e) + { + return null; + } + } + throw new IllegalArgumentException("Don't know how to make NMS " + foreign.getClass().getCanonicalName()); + } +} diff --git a/bukkit110/src/main/resources/plugin.yml b/bukkit110/src/main/resources/plugin.yml new file mode 100644 index 00000000..d3f40bf5 --- /dev/null +++ b/bukkit110/src/main/resources/plugin.yml @@ -0,0 +1,49 @@ +name: ${name} +main: com.boydti.fawe.bukkit.v1_10.BukkitMain_110 +version: ${version} +description: Fast Async WorldEdit plugin +authors: [Empire92] +loadbefore: [WorldEdit] +load: STARTUP +database: false +#softdepend: [WorldGuard, PlotSquared, MCore, Factions, GriefPrevention, Residence, Towny, PlotMe, PreciousStones] +commands: + wea: + description: (FAWE) Bypass WorldEdit processing and area restrictions + aliases: [weanywhere,worldeditanywhere,/wea,/weanywhere,/worldeditanywhere] + usage: "Vault is required for the toggle. Optionally, you can set the permission fawe.bypass" + fixlighting: + description: (FAWE) Fix the lighting in your current chunk + aliases: [/fixlighting] + stream: + description: (FAWE) Stream a schematic into the world + aliases: [/stream] + fawe: + description: (FAWE) Reload the plugin + aliases: [/fawe,/fawereload] + select: + description: (FAWE) Select your current WorldEdit Region. + aliases: [/select,wer,/wer,worldeditregion,/worldeditregion,/region] + frb: + description: (FAWE) Rollback an edit + aliases: [fawerollback,fawerb,/uu,/rb,/frb,/fawerollback,/fawerb] + fcancel: + description: (FAWE) Cancel your edit + aliases: [fawecancel,/fcancel,/cancel,/fawecancel] + '/p': + description: VoxelSniper perform command + aliases: [perform,/perform] + '/d': + description: VoxelSniper default command + aliases: [default,/default] +permissions: + fawe.bypass: + default: false + fawe.admin: + default: false + fawe.stream: + default: false + fawe.fixlighting: + default: false + fawe.reload: + default: false diff --git a/core/src/main/java/com/boydti/fawe/util/SetQueue.java b/core/src/main/java/com/boydti/fawe/util/SetQueue.java index 89a6019c..120d9f39 100644 --- a/core/src/main/java/com/boydti/fawe/util/SetQueue.java +++ b/core/src/main/java/com/boydti/fawe/util/SetQueue.java @@ -18,7 +18,7 @@ public class SetQueue { */ public static final SetQueue IMP = new SetQueue(); - public static enum QueueStage { + public enum QueueStage { INACTIVE, ACTIVE, NONE; } diff --git a/settings.gradle b/settings.gradle index 8b7727f8..4291e29c 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,3 +1,3 @@ rootProject.name = 'FastAsyncWorldEdit' -include 'core', 'bukkit0', 'bukkit19', 'bukkit18', 'forge189', 'forge1710', 'sponge' +include 'core', 'bukkit0', 'bukkit19', 'bukkit110', 'bukkit18', 'forge189', 'forge1710', 'sponge'