package com.boydti.fawe.bukkit.v1_12; 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.FaweChunk; import com.boydti.fawe.object.FaweQueue; import com.boydti.fawe.util.MainUtil; import com.boydti.fawe.util.MathMan; import com.boydti.fawe.util.ReflectionUtils; import com.sk89q.jnbt.*; import com.sk89q.worldedit.internal.Constants; import net.minecraft.server.v1_12_R1.*; import org.bukkit.Chunk; import org.bukkit.World; import org.bukkit.craftbukkit.v1_12_R1.CraftChunk; import org.bukkit.event.entity.CreatureSpawnEvent; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.util.*; public class BukkitChunk_1_12 extends CharFaweChunk { public DataPaletteBlock[] sectionPalettes; public static Map> entityKeys; /** * 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_12(FaweQueue parent, int x, int z) { super(parent, x, z); } public BukkitChunk_1_12(FaweQueue parent, int x, int z, char[][] ids, short[] count, short[] air, byte[] heightMap) { super(parent, x, z, ids, count, air, heightMap); } public void storeBiomes(byte[] biomes) { this.biomes = Arrays.copyOf(biomes, biomes.length); } public boolean storeTile(TileEntity tile, BlockPosition pos) { NBTTagCompound tag = new NBTTagCompound(); CompoundTag nativeTag = getParent().getTag(tile); setTile(pos.getX() & 15, pos.getY(), pos.getZ() & 15, nativeTag); return true; } public boolean storeEntity(Entity ent) throws InvocationTargetException, IllegalAccessException { if (ent instanceof EntityPlayer || BukkitQueue_0.getAdapter() == null) { return false; } int x = (MathMan.roundInt(ent.locX) & 15); int z = (MathMan.roundInt(ent.locZ) & 15); int y = (MathMan.roundInt(ent.locY) & 0xFF); int i = FaweCache.CACHE_I[y][z][x]; int j = FaweCache.CACHE_J[y][z][x]; String id = EntityTypes.b(ent); if (id != null) { NBTTagCompound tag = new NBTTagCompound(); ent.save(tag); // readEntityIntoTag CompoundTag nativeTag = (CompoundTag) BukkitQueue_0.toNative(tag); Map map = ReflectionUtils.getMap(nativeTag.getValue()); map.put("Id", new StringTag(id)); setEntity(nativeTag); return true; } else { return false; } } @Override public CharFaweChunk copy(boolean shallow) { BukkitChunk_1_12 copy; if (shallow) { copy = new BukkitChunk_1_12(getParent(), getX(), getZ(), ids, count, air, heightMap); copy.biomes = biomes; copy.chunk = chunk; } else { copy = new BukkitChunk_1_12(getParent(), getX(), getZ(), (char[][]) MainUtil.copyNd(ids), count.clone(), air.clone(), heightMap.clone()); copy.biomes = biomes != null ? biomes.clone() : null; copy.chunk = chunk; } if (sectionPalettes != null) { copy.sectionPalettes = new DataPaletteBlock[16]; try { for (int i = 0; i < sectionPalettes.length; i++) { DataPaletteBlock current = sectionPalettes[i]; if (current == null) { continue; } // Clone palette DataPalette currentPalette = (DataPalette) BukkitQueue_1_12.fieldPalette.get(current); if (!(currentPalette instanceof DataPaletteGlobal)) { current.a(128, null); } DataPaletteBlock paletteBlock = newDataPaletteBlock(); currentPalette = (DataPalette) BukkitQueue_1_12.fieldPalette.get(current); if (!(currentPalette instanceof DataPaletteGlobal)) { throw new RuntimeException("Palette must be global!"); } BukkitQueue_1_12.fieldPalette.set(paletteBlock, currentPalette); // Clone size BukkitQueue_1_12.fieldSize.set(paletteBlock, BukkitQueue_1_12.fieldSize.get(current)); // Clone palette DataBits currentBits = (DataBits) BukkitQueue_1_12.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); } BukkitQueue_1_12.fieldBits.set(paletteBlock, newBits); copy.sectionPalettes[i] = paletteBlock; } } catch (Throwable e) { MainUtil.handleError(e); } } return copy; } @Override public Chunk getNewChunk() { return ((BukkitQueue_1_12) getParent()).getWorld().getChunkAt(getX(), getZ()); } 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][z][x]]; if (combinedId > 1) { palette.setBlock(x, y, z, Block.getById(combinedId >> 4).fromLegacyData(combinedId & 0xF)); } } } } } } } @Override public void start() { getChunk().load(true); } private void removeEntity(Entity entity) { entity.b(false); entity.die(); entity.valid = false; } @Override public FaweChunk call() { try { BukkitChunk_1_12_Copy copy = getParent().getChangeTask() != null ? new BukkitChunk_1_12_Copy(getParent(), getX(), getZ()) : null; final Chunk chunk = this.getChunk(); final World world = chunk.getWorld(); int bx = this.getX() << 4; int bz = this.getZ() << 4; final boolean flag = world.getEnvironment() == World.Environment.NORMAL; net.minecraft.server.v1_12_R1.Chunk nmsChunk = ((CraftChunk) chunk).getHandle(); nmsChunk.f(true); // Set Modified nmsChunk.mustSave = true; net.minecraft.server.v1_12_R1.World nmsWorld = nmsChunk.world; ChunkSection[] sections = nmsChunk.getSections(); final Collection[] entities = (Collection[]) getParent().getEntitySlices.invoke(nmsChunk); Map tiles = nmsChunk.getTileEntities(); // Set heightmap getParent().setHeightMap(this, heightMap); // Remove entities HashSet entsToRemove = this.getEntityRemoves(); if (!entsToRemove.isEmpty()) { for (int i = 0; i < entities.length; i++) { Collection ents = entities[i]; if (!ents.isEmpty()) { Iterator iter = ents.iterator(); while (iter.hasNext()) { Entity entity = iter.next(); if (entsToRemove.contains(entity.getUniqueID())) { if (copy != null) { copy.storeEntity(entity); } iter.remove(); synchronized (BukkitQueue_0.class) { removeEntity(entity); } } } } } } for (int i = 0; i < entities.length; i++) { int count = this.getCount(i); if (count == 0) { continue; } else if (count >= 4096) { Collection ents = entities[i]; if (!ents.isEmpty()) { if (copy != null) { for (Entity entity : ents) { copy.storeEntity(entity); } } synchronized (BukkitQueue_0.class) { ents.clear(); } } } else if (!getParent().getSettings().EXPERIMENTAL.KEEP_ENTITIES_IN_BLOCKS) { Collection ents = entities[i]; if (!ents.isEmpty()) { int layerYStart = i << 4; int layerYEnd = layerYStart + 15; char[] array = this.getIdArray(i); if (array == null) continue; Iterator iter = ents.iterator(); while (iter.hasNext()) { Entity entity = iter.next(); if (entity instanceof EntityPlayer) { continue; } int y = MathMan.roundInt(entity.locY); if (y > layerYEnd || y < layerYStart) continue; int x = (MathMan.roundInt(entity.locX) & 15); int z = (MathMan.roundInt(entity.locZ) & 15); if (array[FaweCache.CACHE_J[y][z][x]] != 0) { if (copy != null) { copy.storeEntity(entity); } iter.remove(); synchronized (BukkitQueue_0.class) { removeEntity(entity); } } } } } } // Set entities Set entitiesToSpawn = this.getEntities(); // Set createdEntities = new HashSet<>(); if (!entitiesToSpawn.isEmpty()) { synchronized (BukkitQueue_0.class) { 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(); if (entityKeys == null) { entityKeys = new HashMap<>(); for (MinecraftKey key : EntityTypes.a()) { String currentId = EntityTypes.a(key); Class clazz = EntityTypes.b.get(key); entityKeys.putIfAbsent(currentId, clazz); entityKeys.putIfAbsent(key.getKey(), clazz); entityKeys.put(key.b() + ":" + key.getKey(), clazz); } } Class clazz = entityKeys.get(id); if (clazz != null) { Entity entity = EntityTypes.a(clazz, 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) BukkitQueue_1_12.fromNative(nativeTag); for (String name : Constants.NO_COPY_ENTITY_NBT_FIELDS) { tag.remove(name); } entity.f(tag); } entity.setLocation(x, y, z, yaw, pitch); synchronized (BukkitQueue_0.class) { nmsWorld.addEntity(entity, CreatureSpawnEvent.SpawnReason.CUSTOM); } // createdEntities.add(entity.getUniqueID()); } } } } } // Set blocks for (int j = 0; j < sections.length; j++) { int count = this.getCount(j); if (count == 0) { continue; } int countAir = this.getAir(j); final char[] array = this.getIdArray(j); if (array == null) { continue; } ChunkSection section = sections[j]; if (copy != null) { copy.storeSection(section, j); } if (section == null) { if (count == countAir) { continue; } if (this.sectionPalettes != null && this.sectionPalettes[j] != null) { section = sections[j] = getParent().newChunkSection(j << 4, flag, null); getParent().setPalette(section, this.sectionPalettes[j]); getParent().setCount(0, count - this.getAir(j), section); continue; } else { sections[j] = getParent().newChunkSection(j << 4, flag, array); continue; } } else if (count >= 4096) { if (countAir >= 4096) { sections[j] = null; continue; } if (this.sectionPalettes != null && this.sectionPalettes[j] != null) { getParent().setPalette(section, this.sectionPalettes[j]); getParent().setCount(0, count - this.getAir(j), section); continue; } else { sections[j] = getParent().newChunkSection(j << 4, flag, array); continue; } } int by = j << 4; DataPaletteBlock nibble = section.getBlocks(); int nonEmptyBlockCount = 0; IBlockData existing; for (int y = 0; y < 16; y++) { short[][] i1 = FaweCache.CACHE_J[y]; for (int z = 0; z < 16; z++) { short[] i2 = i1[z]; for (int x= 0; x < 16; x++) { char combinedId = array[i2[x]]; switch (combinedId) { case 0: continue; case 1: existing = nibble.a(x, y, z); if (existing != BukkitQueue_1_12.air) { if (existing.d() > 0) { getParent().getRelighter().addLightUpdate(bx + x, by + y, bz + z); } nonEmptyBlockCount--; } nibble.setBlock(x, y, z, BukkitQueue_1_12.air); continue; default: existing = nibble.a(x, y, z); if (existing != BukkitQueue_1_12.air) { if (existing.d() > 0) { getParent().getRelighter().addLightUpdate(bx + x, by + y, bz + z); } } else { nonEmptyBlockCount++; } nibble.setBlock(x, y, z, getParent().IBD_CACHE[(int) combinedId]); } } } } getParent().setCount(0, getParent().getNonEmptyBlockCount(section) + nonEmptyBlockCount, section); } // Trim tiles HashMap toRemove = null; if (!tiles.isEmpty()) { Iterator> iterator = tiles.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][lz][lx]; char[] array = this.getIdArray(j); if (array == null) { continue; } int k = FaweCache.CACHE_J[ly][lz][lx]; if (array[k] != 0) { if (toRemove == null) { toRemove = new HashMap<>(); } if (copy != null) { copy.storeTile(tile.getValue(), tile.getKey()); } toRemove.put(tile.getKey(), tile.getValue()); } } if (toRemove != null) { synchronized (BukkitQueue_0.class) { for (Map.Entry entry : toRemove.entrySet()) { BlockPosition bp = entry.getKey(); TileEntity tile = entry.getValue(); nmsWorld.s(bp); tiles.remove(bp); tile.z(); tile.invalidateBlockCache(); } } } } // Set biomes if (this.biomes != null) { if (copy != null) { copy.storeBiomes(nmsChunk.getBiomeIndex()); } byte[] currentBiomes = nmsChunk.getBiomeIndex(); for (int i = 0 ; i < this.biomes.length; i++) { byte biome = this.biomes[i]; if (biome != 0) { if (biome == -1) biome = 0; currentBiomes[i] = biome; } } } // Set tiles Map tilesToSpawn = this.getTiles(); if (!tilesToSpawn.isEmpty()) { for (Map.Entry entry : tilesToSpawn.entrySet()) { CompoundTag nativeTag = entry.getValue(); short blockHash = entry.getKey(); int x = (blockHash >> 12 & 0xF) + bx; int y = (blockHash & 0xFF); int z = (blockHash >> 8 & 0xF) + bz; BlockPosition pos = new BlockPosition(x, y, z); // Set pos synchronized (BukkitQueue_0.class) { TileEntity tileEntity = nmsWorld.getTileEntity(pos); if (tileEntity != null) { NBTTagCompound tag = (NBTTagCompound) BukkitQueue_1_12.fromNative(nativeTag); tag.set("x", new NBTTagInt(x)); tag.set("y", new NBTTagInt(y)); tag.set("z", new NBTTagInt(z)); if (BukkitQueue_1_12.methodTileEntityLoad != null) { BukkitQueue_1_12.methodTileEntityLoad.invoke(tileEntity, tag); // ReadTagIntoTile } else { tileEntity.load(tag); } } } } } // Change task if (copy != null) { getParent().getChangeTask().run(copy, this); } } catch (Throwable e) { MainUtil.handleError(e); } return this; } }