From b9cefc19360e45b384b3dd090c49fea67ab7580d Mon Sep 17 00:00:00 2001 From: Jesse Boyd Date: Thu, 11 Apr 2019 00:48:18 +1000 Subject: [PATCH] wip 1.13 nms --- v1_13/BukkitChunk_1_13.java | 517 +++++++++++++++++++ v1_13/BukkitChunk_1_13_Copy.java | 81 +++ v1_13/BukkitQueue_1_13.java | 855 +++++++++++++++++++++++++++++++ 3 files changed, 1453 insertions(+) create mode 100644 v1_13/BukkitChunk_1_13.java create mode 100644 v1_13/BukkitChunk_1_13_Copy.java create mode 100644 v1_13/BukkitQueue_1_13.java diff --git a/v1_13/BukkitChunk_1_13.java b/v1_13/BukkitChunk_1_13.java new file mode 100644 index 000000000..9147ffb1f --- /dev/null +++ b/v1_13/BukkitChunk_1_13.java @@ -0,0 +1,517 @@ +package com.boydti.fawe.bukkit.v1_13; + +import com.boydti.fawe.Fawe; +import com.boydti.fawe.FaweCache; +import com.boydti.fawe.bukkit.adapter.v1_13_1.BlockMaterial_1_13; +import com.boydti.fawe.bukkit.v0.BukkitQueue_0; +import com.boydti.fawe.config.Settings; +import com.boydti.fawe.example.IntFaweChunk; +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.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 com.sk89q.worldedit.world.biome.BiomeType; +import com.sk89q.worldedit.world.block.BlockType; +import com.sk89q.worldedit.world.block.BlockTypes; +import net.minecraft.server.v1_13_R2.BiomeBase; +import net.minecraft.server.v1_13_R2.Block; +import net.minecraft.server.v1_13_R2.BlockPosition; +import net.minecraft.server.v1_13_R2.Blocks; +import net.minecraft.server.v1_13_R2.ChunkSection; +import net.minecraft.server.v1_13_R2.DataBits; +import net.minecraft.server.v1_13_R2.DataPalette; +import net.minecraft.server.v1_13_R2.DataPaletteBlock; +import net.minecraft.server.v1_13_R2.DataPaletteGlobal; +import net.minecraft.server.v1_13_R2.Entity; +import net.minecraft.server.v1_13_R2.EntityPlayer; +import net.minecraft.server.v1_13_R2.EntityTypes; +import net.minecraft.server.v1_13_R2.GameProfileSerializer; +import net.minecraft.server.v1_13_R2.IBlockData; +import net.minecraft.server.v1_13_R2.MinecraftKey; +import net.minecraft.server.v1_13_R2.NBTTagCompound; +import net.minecraft.server.v1_13_R2.NBTTagInt; +import net.minecraft.server.v1_13_R2.TileEntity; +import org.bukkit.Chunk; +import org.bukkit.World; +import org.bukkit.craftbukkit.v1_13_R2.CraftChunk; +import org.bukkit.event.entity.CreatureSpawnEvent; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; + +public class BukkitChunk_1_13 extends IntFaweChunk { + + 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_13(FaweQueue parent, int x, int z) { + super(parent, x, z); + } + + public BukkitChunk_1_13(FaweQueue parent, int x, int z, int[][] ids, short[] count, short[] air, byte[] heightMap) { + super(parent, x, z, ids, count, air, heightMap); + } + + public void storeBiomes(BiomeType[] 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]; + EntityTypes type = ent.P(); + MinecraftKey id = EntityTypes.getName(type); + 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.toString())); + setEntity(nativeTag); + return true; + } else { + return false; + } + } + + @Override + public IntFaweChunk copy(boolean shallow) { + BukkitChunk_1_13 copy; + if (shallow) { + copy = new BukkitChunk_1_13(getParent(), getX(), getZ(), ids, count, air, heightMap); + copy.biomes = biomes; + copy.chunk = chunk; + } else { + copy = new BukkitChunk_1_13(getParent(), getX(), getZ(), (int[][]) 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_13.fieldPalette.get(current); + if (!(currentPalette instanceof DataPaletteGlobal)) { + // TODO support non global palette + BukkitQueue_1_13.methodResize.invoke(current, 128); + currentPalette = (DataPalette) BukkitQueue_1_13.fieldPalette.get(current); + if (!(currentPalette instanceof DataPaletteGlobal)) { + throw new RuntimeException("Palette must be global!"); + } + } + DataPaletteBlock paletteBlock = newDataPaletteBlock(); + BukkitQueue_1_13.fieldPalette.set(paletteBlock, currentPalette); + // Clone size + BukkitQueue_1_13.fieldSize.set(paletteBlock, BukkitQueue_1_13.fieldSize.get(current)); + // Clone palette + DataBits currentBits = (DataBits) BukkitQueue_1_13.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_13.fieldBits.set(paletteBlock, newBits); + copy.sectionPalettes[i] = paletteBlock; + } + } catch (Throwable e) { + MainUtil.handleError(e); + } + } + return copy; + } + + private DataPaletteBlock newDataPaletteBlock() { + return new DataPaletteBlock<>(ChunkSection.GLOBAL_PALETTE, Block.REGISTRY_ID, GameProfileSerializer::d, GameProfileSerializer::a, Blocks.AIR.getBlockData()); + } + + @Override + public Chunk getNewChunk() { + return ((BukkitQueue_1_13) getParent()).getWorld().getChunkAt(getX(), getZ()); + } + + public void optimize() { + if (sectionPalettes != null) { + return; + } + int[][] arrays = getCombinedIdArrays(); + 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(); + int[] blocks = getIdArray(layer); + for (int y = 0; y < 16; y++) { + for (int z = 0; z < 16; z++) { + for (int x = 0; x < 16; x++) { + int combinedId = blocks[FaweCache.CACHE_J[y][z][x]]; + if (combinedId != 0) { + BlockType state = BlockTypes.getFromStateId(combinedId); + IBlockData blockData = ((BlockMaterial_1_13) state.getMaterial()).getState(); + palette.setBlock(x, y, z, blockData); + } + } + } + } + } + } + } + + @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_13_Copy copy = getParent().getChangeTask() != null ? new BukkitChunk_1_13_Copy(getParent(), getX(), getZ()) : null; + final Chunk chunk = this.getChunk(); + final World world = chunk.getWorld(); + Settings settings = getParent().getSettings(); + int bx = this.getX() << 4; + int bz = this.getZ() << 4; + final boolean flag = world.getEnvironment() == World.Environment.NORMAL; + net.minecraft.server.v1_13_R2.Chunk nmsChunk = ((CraftChunk) chunk).getHandle(); + nmsChunk.f(true); // Set Modified + nmsChunk.mustSave = true; + net.minecraft.server.v1_13_R2.World nmsWorld = nmsChunk.world; + ChunkSection[] sections = nmsChunk.getSections(); + List[] entities = nmsChunk.getEntitySlices(); + 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 || settings.EXPERIMENTAL.KEEP_ENTITIES_IN_BLOCKS) { + continue; + } else if (count >= 4096) { + Collection ents = entities[i]; + if (!ents.isEmpty()) { + synchronized (BukkitQueue_0.class) { + Iterator iter = ents.iterator(); + while (iter.hasNext()) { + Entity entity = iter.next(); + if (entity instanceof EntityPlayer) { + continue; + } + iter.remove(); + if (copy != null) { + copy.storeEntity(entity); + } + removeEntity(entity); + } + } + } + } else { + Collection ents = entities[i]; + if (!ents.isEmpty()) { + int layerYStart = i << 4; + int layerYEnd = layerYStart + 15; + int[] 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(); + 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(); + Entity entity = EntityTypes.a(nmsWorld, new MinecraftKey(id)); + 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_13.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 int[] 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); // TODO set data + 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++) { + int combinedId = array[i2[x]]; + switch (combinedId) { + case 0: + continue; + case 1: + case 2: + case 3: + existing = nibble.a(x, y, z); + if (!existing.isAir()) { + if (existing.e() > 0) { + getParent().getRelighter().addLightUpdate(bx + x, by + y, bz + z); + } + nonEmptyBlockCount--; + } + nibble.setBlock(x, y, z, BukkitQueue_1_13.air); + continue; + default: + existing = nibble.a(x, y, z); + if (!existing.isAir()) { + if (existing.e() > 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]; + int[] 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()); + } + BiomeBase[] currentBiomes = nmsChunk.getBiomeIndex(); + for (int i = 0 ; i < this.biomes.length; i++) { + BiomeType biome = this.biomes[i]; + if (biome != null) { + 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_13.fromNative(nativeTag); + tag.set("x", new NBTTagInt(x)); + tag.set("y", new NBTTagInt(y)); + tag.set("z", new NBTTagInt(z)); + tileEntity.load(tag); + } + } + } + } + // Change task + if (copy != null) { + getParent().getChangeTask().run(copy, this); + } + } catch (Throwable e) { + MainUtil.handleError(e); + } + return this; + } +} diff --git a/v1_13/BukkitChunk_1_13_Copy.java b/v1_13/BukkitChunk_1_13_Copy.java new file mode 100644 index 000000000..73b7f3c36 --- /dev/null +++ b/v1_13/BukkitChunk_1_13_Copy.java @@ -0,0 +1,81 @@ +package com.boydti.fawe.bukkit.v1_13; + +import com.boydti.fawe.object.FaweQueue; +import net.minecraft.server.v1_13_R2.ChunkSection; +import net.minecraft.server.v1_13_R2.DataPaletteBlock; +import net.minecraft.server.v1_13_R2.IBlockData; +import net.minecraft.server.v1_13_R2.NibbleArray; + +public class BukkitChunk_1_13_Copy extends BukkitChunk_1_13 { + public BukkitChunk_1_13_Copy(FaweQueue parent, int x, int z) { + super(parent, x, z); + } + + public void set(int i, byte[] ids, byte[] data) { + this.dataInts[i] = data; + } + + public boolean storeSection(ChunkSection section, int layer) throws IllegalAccessException { + if (section == null) { + return false; + } + DataPaletteBlock blocks = section.getBlocks(); + NibbleArray data = new NibbleArray(); + for (int y = 0; y < 16; y++) { + for (int z = 0; z < 16; z++) { + for (int x = 0; x < 16; x++) { + IBlockData block = blocks.a(x, y, z); + } + } + } + blocks.set(ids, data); + set(layer, ids, data.asBytes()); + short solid = (short) getParent().fieldNonEmptyBlockCount.getInt(section); + count[layer] = solid; + air[layer] = (short) (4096 - solid); + return true; + } + + @Override + public int[][] getCombinedIdArrays() { + for (int i = 0; i < ids.length; i++) { + getIdArray(i); + } + return super.getCombinedIdArrays(); + } + + @Override + public int[] getIdArray(int i) { + int[] combined = this.ids[i]; + if (combined != null) { + return combined; + } + byte[] idsBytesArray = idsBytes[i]; + if (idsBytesArray == null) { + return null; + } + byte[] datasBytesArray = datasBytes[i]; + + idsBytes[i] = null; + datasBytes[i] = null; + + this.ids[i] = combined = new char[4096]; + for (int j = 0, k = 0; j < 2048; j++, k += 2) { + combined[k] = (char) (((idsBytesArray[k] & 0xFF) << 4) + (datasBytesArray[j] & 15)); + } + for (int j = 0, k = 1; j < 2048; j++, k += 2) { + combined[k] = (char) (((idsBytesArray[k] & 0xFF) << 4) + ((datasBytesArray[j] >> 4) & 15)); + } + return combined; + } + + @Override + public void setBlock(int x, int y, int z, int id) { + throw new UnsupportedOperationException("This chunk is an immutable copy"); + } + + @Override + public void setBlock(int x, int y, int z, int id, int data) { + throw new UnsupportedOperationException("This chunk is an immutable copy"); + } +} diff --git a/v1_13/BukkitQueue_1_13.java b/v1_13/BukkitQueue_1_13.java new file mode 100644 index 000000000..52bb00bd5 --- /dev/null +++ b/v1_13/BukkitQueue_1_13.java @@ -0,0 +1,855 @@ +package com.boydti.fawe.bukkit.v1_13; + +import com.boydti.fawe.Fawe; +import com.boydti.fawe.FaweCache; +import com.boydti.fawe.bukkit.BukkitPlayer; +import com.boydti.fawe.bukkit.v0.BukkitQueue_0; +import com.boydti.fawe.bukkit.v1_13.packet.FaweChunkPacket; +import com.boydti.fawe.bukkit.v1_13.packet.MCAChunkPacket; +import com.boydti.fawe.example.IntFaweChunk; +import com.boydti.fawe.jnbt.anvil.MCAChunk; +import com.boydti.fawe.object.FaweChunk; +import com.boydti.fawe.object.FawePlayer; +import com.boydti.fawe.object.RegionWrapper; +import com.boydti.fawe.object.RunnableVal; +import com.boydti.fawe.object.brush.visualization.VisualChunk; +import com.boydti.fawe.object.queue.LazyFaweChunk; +import com.boydti.fawe.object.visitor.FaweChunkVisitor; +import com.boydti.fawe.util.*; +import com.comphenix.protocol.PacketType; +import com.comphenix.protocol.ProtocolLibrary; +import com.comphenix.protocol.ProtocolManager; +import com.comphenix.protocol.injector.netty.WirePacket; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.jnbt.StringTag; +import com.sk89q.jnbt.Tag; +import com.sk89q.worldedit.bukkit.BukkitAdapter; +import com.sk89q.worldedit.world.biome.BaseBiome; +import com.sk89q.worldedit.world.block.BlockTypes; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufAllocator; +import net.minecraft.server.v1_13_R2.BiomeBase; +import net.minecraft.server.v1_13_R2.BiomeCache; +import net.minecraft.server.v1_13_R2.Block; +import net.minecraft.server.v1_13_R2.BlockPosition; +import net.minecraft.server.v1_13_R2.ChunkProviderGenerate; +import net.minecraft.server.v1_13_R2.ChunkProviderServer; +import net.minecraft.server.v1_13_R2.ChunkSection; +import net.minecraft.server.v1_13_R2.DataPaletteBlock; +import net.minecraft.server.v1_13_R2.Entity; +import net.minecraft.server.v1_13_R2.EntityPlayer; +import net.minecraft.server.v1_13_R2.EntityTracker; +import net.minecraft.server.v1_13_R2.EntityTypes; +import net.minecraft.server.v1_13_R2.EnumDifficulty; +import net.minecraft.server.v1_13_R2.EnumGamemode; +import net.minecraft.server.v1_13_R2.EnumSkyBlock; +import net.minecraft.server.v1_13_R2.IBlockData; +import net.minecraft.server.v1_13_R2.IDataManager; +import net.minecraft.server.v1_13_R2.MinecraftServer; +import net.minecraft.server.v1_13_R2.NBTTagCompound; +import net.minecraft.server.v1_13_R2.NibbleArray; +import net.minecraft.server.v1_13_R2.PacketDataSerializer; +import net.minecraft.server.v1_13_R2.PacketPlayOutMapChunk; +import net.minecraft.server.v1_13_R2.PacketPlayOutMultiBlockChange; +import net.minecraft.server.v1_13_R2.PlayerChunk; +import net.minecraft.server.v1_13_R2.PlayerChunkMap; +import net.minecraft.server.v1_13_R2.RegionFile; +import net.minecraft.server.v1_13_R2.RegionFileCache; +import net.minecraft.server.v1_13_R2.ServerNBTManager; +import net.minecraft.server.v1_13_R2.TileEntity; +import net.minecraft.server.v1_13_R2.WorldChunkManager; +import net.minecraft.server.v1_13_R2.WorldData; +import net.minecraft.server.v1_13_R2.WorldManager; +import net.minecraft.server.v1_13_R2.WorldServer; +import net.minecraft.server.v1_13_R2.WorldSettings; +import net.minecraft.server.v1_13_R2.WorldType; +import org.bukkit.Bukkit; +import org.bukkit.Chunk; +import org.bukkit.World; +import org.bukkit.WorldCreator; +import org.bukkit.block.Biome; +import org.bukkit.block.data.BlockData; +import org.bukkit.craftbukkit.v1_13_R2.CraftChunk; +import org.bukkit.craftbukkit.v1_13_R2.CraftServer; +import org.bukkit.craftbukkit.v1_13_R2.CraftWorld; +import org.bukkit.craftbukkit.v1_13_R2.entity.CraftPlayer; +import org.bukkit.event.world.WorldInitEvent; +import org.bukkit.event.world.WorldLoadEvent; +import org.bukkit.generator.ChunkGenerator; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.*; +import java.util.concurrent.atomic.LongAdder; + +public class BukkitQueue_1_13 extends BukkitQueue_0 { + + protected final static Field fieldBits; + protected final static Field fieldPalette; + protected final static Field fieldSize; + protected final static Field fieldTickingBlockCount; + protected final static Field fieldNonEmptyBlockCount; + protected final static Field fieldSection; + protected final static Field fieldBiomes; + protected final static Field fieldChunkGenerator; + protected final static Field fieldSeed; + protected final static Field fieldBiomeCache; + protected final static Field fieldBiomes2; + protected final static Field fieldGenLayer1; + protected final static Field fieldGenLayer2; + protected final static Field fieldSave; +// protected final static MutableGenLayer genLayer; + protected final static ChunkSection emptySection; + protected final static Field fieldRegistry; + protected final static Field fieldNbtMap; + protected final static Field fieldIbdMap; + + protected static final Method methodResize; + + static { + try { + emptySection = new ChunkSection(0, true); + Arrays.fill(emptySection.getSkyLightArray().asBytes(), (byte) 255); + fieldSection = ChunkSection.class.getDeclaredField("blockIds"); + fieldTickingBlockCount = ChunkSection.class.getDeclaredField("tickingBlockCount"); + fieldNonEmptyBlockCount = ChunkSection.class.getDeclaredField("nonEmptyBlockCount"); + fieldSection.setAccessible(true); + fieldTickingBlockCount.setAccessible(true); + fieldNonEmptyBlockCount.setAccessible(true); + + fieldBiomes = ChunkProviderGenerate.class.getDeclaredField("D"); // * + fieldBiomes.setAccessible(true); + fieldChunkGenerator = ChunkProviderServer.class.getDeclaredField("chunkGenerator"); + fieldChunkGenerator.setAccessible(true); + fieldSeed = WorldData.class.getDeclaredField("e"); + fieldSeed.setAccessible(true); + fieldBiomeCache = WorldChunkManager.class.getDeclaredField("d"); // * + fieldBiomeCache.setAccessible(true); + fieldBiomes2 = WorldChunkManager.class.getDeclaredField("e"); // * + fieldBiomes2.setAccessible(true); + fieldGenLayer1 = WorldChunkManager.class.getDeclaredField("b") ; + fieldGenLayer2 = WorldChunkManager.class.getDeclaredField("c") ; + fieldGenLayer1.setAccessible(true); + fieldGenLayer2.setAccessible(true); + + fieldSave = ReflectionUtils.setAccessible(net.minecraft.server.v1_13_R2.Chunk.class.getDeclaredField("s")); //* + + fieldPalette = DataPaletteBlock.class.getDeclaredField("c"); + fieldPalette.setAccessible(true); + + methodResize = DataPaletteBlock.class.getDeclaredMethod("b", int.class); + methodResize.setAccessible(true); + + /// + + fieldRegistry = DataPaletteBlock.class.getDeclaredField("d"); + fieldRegistry.setAccessible(true); + + fieldNbtMap = DataPaletteBlock.class.getDeclaredField("e"); + fieldNbtMap.setAccessible(true); + + fieldIbdMap = DataPaletteBlock.class.getDeclaredField("f"); + fieldIbdMap.setAccessible(true); + + fieldSize = DataPaletteBlock.class.getDeclaredField("i"); + fieldSize.setAccessible(true); + + fieldBits = DataPaletteBlock.class.getDeclaredField("a"); + fieldBits.setAccessible(true); + + Fawe.debug("Using adapter: " + getAdapter()); + Fawe.debug("========================================="); + } catch (Throwable e) { + throw new RuntimeException(e); + } + } + + public BukkitQueue_1_13(final com.sk89q.worldedit.world.World world) { + super(world); + getImpWorld(); + } + + public BukkitQueue_1_13(final String world) { + super(world); + getImpWorld(); + } + + private boolean save(net.minecraft.server.v1_13_R2.Chunk chunk, ChunkProviderServer cps) { + cps.saveChunk(chunk, false); + chunk.a(false); + return true; + } + + @Override + public ChunkSection[] getSections(net.minecraft.server.v1_13_R2.Chunk chunk) { + return chunk.getSections(); + } + + @Override + public net.minecraft.server.v1_13_R2.Chunk loadChunk(World world, int x, int z, boolean generate) { + ChunkProviderServer provider = ((CraftWorld) world).getHandle().getChunkProvider(); + if (generate) { + return provider.getChunkAt(x, z, true, true); + } else { + return provider.getChunkAt(x, z, true, false); + } + } + + @Override + public ChunkSection[] getCachedSections(World world, int cx, int cz) { + net.minecraft.server.v1_13_R2.Chunk chunk = ((CraftWorld) world).getHandle().getChunkProvider().getChunkAt(cx, cz, false, false); + if (chunk != null) { + return chunk.getSections(); + } + return null; + } + + @Override + public net.minecraft.server.v1_13_R2.Chunk getCachedChunk(World world, int cx, int cz) { + return ((CraftWorld) world).getHandle().getChunkProvider().getChunkAt(cx, cz, false, false); + } + + @Override + public ChunkSection getCachedSection(ChunkSection[] chunkSections, int cy) { + return chunkSections[cy]; + } + + @Override + public void saveChunk(net.minecraft.server.v1_13_R2.Chunk chunk) { + chunk.f(true); // Set Modified + chunk.mustSave = true; + } + + @Override + public boolean regenerateChunk(World world, int x, int z, BaseBiome biome, Long seed) { + if (biome != null) { + try { + if (seed == null) { + seed = world.getSeed(); + } + nmsWorld.worldData.getSeed(); + boolean result; + ChunkProviderGenerate generator = new ChunkProviderGenerate(nmsWorld, seed, false, ""); + Biome bukkitBiome = getAdapter().getBiome(biome.getId()); + BiomeBase base = BiomeBase.getBiome(biome.getId()); + fieldBiomes.set(generator, new BiomeBase[]{base}); + boolean cold = base.getTemperature() <= 1; + net.minecraft.server.v1_13_R2.ChunkGenerator existingGenerator = nmsWorld.getChunkProvider().chunkGenerator; + long existingSeed = world.getSeed(); + { + if (genLayer == null) genLayer = new MutableGenLayer(seed); + genLayer.set(biome.getId()); + Object existingGenLayer1 = fieldGenLayer1.get(nmsWorld.getWorldChunkManager()); + Object existingGenLayer2 = fieldGenLayer2.get(nmsWorld.getWorldChunkManager()); + fieldGenLayer1.set(nmsWorld.getWorldChunkManager(), genLayer); + fieldGenLayer2.set(nmsWorld.getWorldChunkManager(), genLayer); + + fieldSeed.set(nmsWorld.worldData, seed); + + ReflectionUtils.setFailsafeFieldValue(fieldBiomeCache, this.nmsWorld.getWorldChunkManager(), new BiomeCache(this.nmsWorld.getWorldChunkManager())); + + ReflectionUtils.setFailsafeFieldValue(fieldChunkGenerator, this.nmsWorld.getChunkProvider(), generator); + + keepLoaded.remove(MathMan.pairInt(x, z)); + result = getWorld().regenerateChunk(x, z); + net.minecraft.server.v1_13_R2.Chunk nmsChunk = getCachedChunk(world, x, z); + if (nmsChunk != null) { + nmsChunk.f(true); // Set Modified + nmsChunk.mustSave = true; + } + + ReflectionUtils.setFailsafeFieldValue(fieldChunkGenerator, this.nmsWorld.getChunkProvider(), existingGenerator); + + fieldSeed.set(nmsWorld.worldData, existingSeed); + + fieldGenLayer1.set(nmsWorld.getWorldChunkManager(), existingGenLayer1); + fieldGenLayer2.set(nmsWorld.getWorldChunkManager(), existingGenLayer2); + } + return result; + } catch (Throwable e) { + e.printStackTrace(); + } + } + return super.regenerateChunk(world, x, z, biome, seed); + } + + @Override + public boolean setMCA(final int mcaX, final int mcaZ, final RegionWrapper allowed, final Runnable whileLocked, final boolean saveChunks, final boolean load) { + TaskManager.IMP.sync(new RunnableVal() { + @Override + public void run(Boolean value) { + long start = System.currentTimeMillis(); + long last = start; + synchronized (RegionFileCache.class) { + World world = getWorld(); + if (world.getKeepSpawnInMemory()) world.setKeepSpawnInMemory(false); + ChunkProviderServer provider = nmsWorld.getChunkProvider(); + + boolean mustSave = false; + boolean[][] chunksUnloaded = null; + { // Unload chunks + Iterator iter = provider.a().iterator(); + while (iter.hasNext()) { + net.minecraft.server.v1_13_R2.Chunk chunk = iter.next(); + if (chunk.locX >> 5 == mcaX && chunk.locZ >> 5 == mcaZ) { + boolean isIn = allowed.isInChunk(chunk.locX, chunk.locZ); + if (isIn) { + if (!load) { + mustSave |= saveChunks && save(chunk, provider); + continue; + } + iter.remove(); + boolean save = saveChunks && chunk.a(false); + mustSave |= save; + provider.unloadChunk(chunk, save); + if (chunksUnloaded == null) { + chunksUnloaded = new boolean[32][]; + } + int relX = chunk.locX & 31; + boolean[] arr = chunksUnloaded[relX]; + if (arr == null) { + arr = chunksUnloaded[relX] = new boolean[32]; + } + arr[chunk.locZ & 31] = true; + } + } + } + } + if (mustSave) { + provider.c(); // TODO only the necessary chunks + } + + File unloadedRegion = null; + if (load && !RegionFileCache.a.isEmpty()) { + Map map = RegionFileCache.a; + Iterator> iter = map.entrySet().iterator(); + String requiredPath = world.getName() + File.separator + "region"; + while (iter.hasNext()) { + Map.Entry entry = iter.next(); + File file = entry.getKey(); + int[] regPos = MainUtil.regionNameToCoords(file.getPath()); + if (regPos[0] == mcaX && regPos[1] == mcaZ && file.getPath().contains(requiredPath)) { + if (file.exists()) { + unloadedRegion = file; + RegionFile regionFile = entry.getValue(); + iter.remove(); + try { + regionFile.c(); + } catch (IOException e) { + e.printStackTrace(); + } + } + break; + } + } + } + + long now = System.currentTimeMillis(); + if (whileLocked != null) whileLocked.run(); + if (!load) return; + + { // Load the region again + if (unloadedRegion != null && chunksUnloaded != null && unloadedRegion.exists()) { + final boolean[][] finalChunksUnloaded = chunksUnloaded; + TaskManager.IMP.async(() -> { + int bx = mcaX << 5; + int bz = mcaZ << 5; + for (int x = 0; x < finalChunksUnloaded.length; x++) { + boolean[] arr = finalChunksUnloaded[x]; + if (arr != null) { + for (int z = 0; z < arr.length; z++) { + if (arr[z]) { + int cx = bx + x; + int cz = bz + z; + SetQueue.IMP.addTask(new Runnable() { + @Override + public void run() { + net.minecraft.server.v1_13_R2.Chunk chunk = provider.getChunkAt(cx, cz, null, false); + if (chunk != null) { + PlayerChunk pc = getPlayerChunk(nmsWorld, cx, cz); + if (pc != null) { + sendChunk(pc, chunk, 0); + } + } + } + }); + } + } + } + } + }); + } + } + } + } + }); + return true; + } + + @Override + public void setHeightMap(FaweChunk chunk, byte[] heightMap) { + CraftChunk craftChunk = (CraftChunk) chunk.getChunk(); + if (craftChunk != null) { + int[] otherMap = craftChunk.getHandle().heightMap; + for (int i = 0; i < heightMap.length; i++) { + int newHeight = heightMap[i] & 0xFF; + int currentHeight = otherMap[i]; + if (newHeight > currentHeight) { + otherMap[i] = newHeight; + } + } + } + } + + @Override + public boolean next(int amount, long time) { + return super.next(amount, time); + } + + @Override + public void setSkyLight(ChunkSection section, int x, int y, int z, int value) { + section.getSkyLightArray().a(x & 15, y & 15, z & 15, value); + } + + @Override + public void setBlockLight(ChunkSection section, int x, int y, int z, int value) { + section.getEmittedLightArray().a(x & 15, y & 15, z & 15, value); + } + + @Override + public World createWorld(final WorldCreator creator) { + final String name = creator.name(); + ChunkGenerator generator = creator.generator(); + final CraftServer server = (CraftServer) Bukkit.getServer(); + final MinecraftServer console = server.getServer(); + final File folder = new File(server.getWorldContainer(), name); + final World world = server.getWorld(name); + final WorldType type = WorldType.getType(creator.type().getName()); + final boolean generateStructures = creator.generateStructures(); + if (world != null) { + return world; + } + if (folder.exists() && !folder.isDirectory()) { + throw new IllegalArgumentException("File exists with the name '" + name + "' and isn't a folder"); + } + TaskManager.IMP.sync(new RunnableVal() { + @Override + public void run(Object value) { + try { + Field field = CraftServer.class.getDeclaredField("worlds"); + field.setAccessible(true); + Map existing = (Map) field.get(server); + if (!existing.getClass().getName().contains("SynchronizedMap")) { + field.set(server, Collections.synchronizedMap(existing)); + } + } catch (Throwable e) { + e.printStackTrace(); + } + } + }); + if (generator == null) { + generator = server.getGenerator(name); + } + int dimension = 10 + console.worlds.size(); + boolean used = false; + do { + for (final WorldServer ws : console.worlds) { + used = (ws.dimension == dimension); + if (used) { + ++dimension; + break; + } + } + } while (used); + final boolean hardcore = false; + final IDataManager sdm = new ServerNBTManager(server.getWorldContainer(), name, true, server.getHandle().getServer().dataConverterManager); + WorldData worlddata = sdm.getWorldData(); + final WorldSettings worldSettings; + if (worlddata == null) { + worldSettings = new WorldSettings(creator.seed(), EnumGamemode.getById(server.getDefaultGameMode().getValue()), generateStructures, hardcore, type); + worldSettings.setGeneratorSettings(creator.generatorSettings()); + worlddata = new WorldData(worldSettings, name); + } else { + worldSettings = null; + } + worlddata.checkName(name); + final WorldServer internal = (WorldServer)new WorldServer(console, sdm, worlddata, dimension, console.methodProfiler, creator.environment(), generator).b(); + startSet(true); // Temporarily allow async chunk load since the world isn't added yet + if (worldSettings != null) { + internal.a(worldSettings); + } + endSet(true); + internal.scoreboard = server.getScoreboardManager().getMainScoreboard().getHandle(); + internal.tracker = new EntityTracker(internal); + internal.addIWorldAccess(new WorldManager(console, internal)); + internal.worldData.setDifficulty(EnumDifficulty.EASY); + internal.setSpawnFlags(true, true); + if (generator != null) { + internal.getWorld().getPopulators().addAll(generator.getDefaultPopulators(internal.getWorld())); + } + // Add the world + return TaskManager.IMP.sync(new RunnableVal() { + @Override + public void run(World value) { + console.worlds.add(internal); + server.getPluginManager().callEvent(new WorldInitEvent(internal.getWorld())); + server.getPluginManager().callEvent(new WorldLoadEvent(internal.getWorld())); + this.value = internal.getWorld(); + } + }); + } + + @Override + public int getCombinedId4Data(ChunkSection lastSection, int x, int y, int z) { + DataPaletteBlock dataPalette = lastSection.getBlocks(); + IBlockData ibd = dataPalette.a(x & 15, y & 15, z & 15); + int id = Block.REGISTRY_ID.getId(ibd); + return BlockTypes.states[idbToStateOrdinal[id]]; + } + + @Override + public int getBiome(net.minecraft.server.v1_13_R2.Chunk chunk, int x, int z) { + return chunk.getBiomeIndex()[((z & 15) << 4) + (x & 15)]; + } + + @Override + public int getOpacity(ChunkSection section, int x, int y, int z) { + DataPaletteBlock dataPalette = section.getBlocks(); + IBlockData ibd = dataPalette.a(x & 15, y & 15, z & 15); + return ibd.c(); + } + + @Override + public int getBrightness(ChunkSection section, int x, int y, int z) { + DataPaletteBlock dataPalette = section.getBlocks(); + IBlockData ibd = dataPalette.a(x & 15, y & 15, z & 15); + return ibd.d(); + } + + @Override + public int getOpacityBrightnessPair(ChunkSection section, int x, int y, int z) { + DataPaletteBlock dataPalette = section.getBlocks(); + IBlockData ibd = dataPalette.a(x & 15, y & 15, z & 15); + return MathMan.pair16(ibd.c(), ibd.d()); + } + + @Override + public void sendChunk(int x, int z, int bitMask) { + net.minecraft.server.v1_13_R2.Chunk chunk = getCachedChunk(getWorld(), x, z); + if (chunk != null) { + sendChunk(getPlayerChunk((WorldServer) chunk.getWorld(), chunk.locX, chunk.locZ), chunk, bitMask); + } + } + + @Override + public void sendChunkUpdatePLIB(FaweChunk chunk, FawePlayer... players) { + PlayerChunkMap playerManager = ((CraftWorld) getWorld()).getHandle().getPlayerChunkMap(); + ProtocolManager manager = ProtocolLibrary.getProtocolManager(); + WirePacket packet = null; + try { + for (int i = 0; i < players.length; i++) { + CraftPlayer bukkitPlayer = ((CraftPlayer) ((BukkitPlayer) players[i]).parent); + EntityPlayer player = bukkitPlayer.getHandle(); + + if (playerManager.a(player, chunk.getX(), chunk.getZ())) { + if (packet == null) { + byte[] data; + byte[] buffer = new byte[8192]; + if (chunk instanceof LazyFaweChunk) { + chunk = (FaweChunk) chunk.getChunk(); + } + if (chunk instanceof MCAChunk) { + data = new MCAChunkPacket((MCAChunk) chunk, true, true, hasSky()).apply(buffer); + } else { + data = new FaweChunkPacket(chunk, true, true, hasSky()).apply(buffer); + } + packet = new WirePacket(PacketType.Play.Server.MAP_CHUNK, data); + } + manager.sendWirePacket(bukkitPlayer, packet); + } + } + } catch (InvocationTargetException e) { + throw new RuntimeException(e); + } + } + + @Override + public void sendBlockUpdate(FaweChunk chunk, FawePlayer... players) { + try { + PlayerChunkMap playerManager = ((CraftWorld) getWorld()).getHandle().getPlayerChunkMap(); + boolean watching = false; + boolean[] watchingArr = new boolean[players.length]; + for (int i = 0; i < players.length; i++) { + EntityPlayer player = ((CraftPlayer) ((BukkitPlayer) players[i]).parent).getHandle(); + if (playerManager.a(player, chunk.getX(), chunk.getZ())) { + watchingArr[i] = true; + watching = true; + } + } + if (!watching) return; + final LongAdder size = new LongAdder(); + if (chunk instanceof VisualChunk) { + size.add(((VisualChunk) chunk).size()); + } else if (chunk instanceof IntFaweChunk) { + size.add(((IntFaweChunk) chunk).getTotalCount()); + } else { + chunk.forEachQueuedBlock(new FaweChunkVisitor() { + @Override + public void run(int localX, int y, int localZ, int combined) { + size.add(1); + } + }); + } + if (size.intValue() == 0) return; + PacketPlayOutMultiBlockChange packet = new PacketPlayOutMultiBlockChange(); + ByteBuf byteBuf = ByteBufAllocator.DEFAULT.buffer(); + final PacketDataSerializer buffer = new PacketDataSerializer(byteBuf); + buffer.writeInt(chunk.getX()); + buffer.writeInt(chunk.getZ()); + buffer.d(size.intValue()); + chunk.forEachQueuedBlock(new FaweChunkVisitor() { + @Override + public void run(int localX, int y, int localZ, int combined) { + short index = (short) (localX << 12 | localZ << 8 | y); + if (combined < 16) combined = 0; + buffer.writeShort(index); + buffer.d(combined); + } + }); + packet.a(buffer); + for (int i = 0; i < players.length; i++) { + if (watchingArr[i]) ((CraftPlayer) ((BukkitPlayer) players[i]).parent).getHandle().playerConnection.sendPacket(packet); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + + @Override + public void refreshChunk(FaweChunk fc) { + sendChunk(fc.getX(), fc.getZ(), fc.getBitMask()); + } + + public void sendPacket(int cx, int cz, Packet packet) { + PlayerChunk chunk = getPlayerChunk(nmsWorld, cx, cz); + if (chunk != null) { + for (EntityPlayer player : chunk.c) { + player.playerConnection.sendPacket(packet); + } + } + } + + private PlayerChunk getPlayerChunk(WorldServer w, int cx, int cz) { + PlayerChunkMap chunkMap = w.getPlayerChunkMap(); + PlayerChunk playerChunk = chunkMap.getChunk(cx, cz); + if (playerChunk == null) { + return null; + } + if (playerChunk.c.isEmpty()) { + return null; + } + return playerChunk; + } + + public boolean sendChunk(PlayerChunk playerChunk, net.minecraft.server.v1_13_R2.Chunk nmsChunk, int mask) { + WorldServer w = (WorldServer) nmsChunk.getWorld(); + if (playerChunk == null) { + return false; + } + if (mask == 0) { + PacketPlayOutMapChunk packet = new PacketPlayOutMapChunk(nmsChunk, 65535); + for (EntityPlayer player : playerChunk.c) { + player.playerConnection.sendPacket(packet); + } + return true; + } + // Send chunks + boolean empty = false; + ChunkSection[] sections = nmsChunk.getSections(); + for (int i = 0; i < sections.length; i++) { + if (sections[i] == null) { + sections[i] = emptySection; + empty = true; + } + } + if (mask == 0 || mask == 65535 && hasEntities(nmsChunk)) { + PacketPlayOutMapChunk packet = new PacketPlayOutMapChunk(nmsChunk, 65280); + for (EntityPlayer player : playerChunk.c) { + player.playerConnection.sendPacket(packet); + } + mask = 255; + } + PacketPlayOutMapChunk packet = new PacketPlayOutMapChunk(nmsChunk, mask); + for (EntityPlayer player : playerChunk.c) { + player.playerConnection.sendPacket(packet); + } + if (empty) { + for (int i = 0; i < sections.length; i++) { + if (sections[i] == emptySection) { + sections[i] = null; + } + } + } + return true; + } + + public boolean hasEntities(net.minecraft.server.v1_13_R2.Chunk nmsChunk) { + try { + final Collection[] entities = (Collection[]) getEntitySlices.invoke(nmsChunk); + for (int i = 0; i < entities.length; i++) { + Collection slice = entities[i]; + if (slice != null && !slice.isEmpty()) { + return true; + } + } + } catch (Throwable ignore) {} + return false; + } + + @Override + public boolean removeSectionLighting(ChunkSection section, int layer, boolean sky) { + if (section != null) { + Arrays.fill(section.getEmittedLightArray().asBytes(), (byte) 0); + if (sky) { + byte[] light = section.getSkyLightArray().asBytes(); + if (light != null) { + Arrays.fill(light, (byte) 0); + } + } + return true; + } + return false; + } + + @Override + public void setFullbright(ChunkSection[] sections) { + for (int i = 0; i < sections.length; i++) { + ChunkSection section = sections[i]; + if (section != null) { + byte[] bytes = section.getSkyLightArray().asBytes(); + Arrays.fill(bytes, (byte) 255); + } + } + } + + @Override + public int getSkyLight(ChunkSection section, int x, int y, int z) { + return section.b(x & 15, y & 15, z & 15); + } + + @Override + public int getEmmittedLight(ChunkSection section, int x, int y, int z) { + return section.c(x & 15, y & 15, z & 15); + } + + @Override + public void relightBlock(int x, int y, int z) { + pos.c(x, y, z); + nmsWorld.c(EnumSkyBlock.BLOCK, pos); + } + + @Override + public void relightSky(int x, int y, int z) { + pos.c(x, y, z); + nmsWorld.c(EnumSkyBlock.SKY, pos); + } + + @Override + public void relight(int x, int y, int z) { + pos.c(x, y, z); + nmsWorld.w(pos); + } + + protected WorldServer nmsWorld; + + @Override + public World getImpWorld() { + World world = super.getImpWorld(); + if (world != null) { + this.nmsWorld = ((CraftWorld) world).getHandle(); + return super.getImpWorld(); + } else { + return null; + } + } + + public void setCount(int tickingBlockCount, int nonEmptyBlockCount, ChunkSection section) throws NoSuchFieldException, IllegalAccessException { + fieldTickingBlockCount.set(section, tickingBlockCount); + fieldNonEmptyBlockCount.set(section, nonEmptyBlockCount); + } + + public int getNonEmptyBlockCount(ChunkSection section) throws IllegalAccessException { + return (int) fieldNonEmptyBlockCount.get(section); + } + + public void setPalette(ChunkSection section, DataPaletteBlock palette) throws NoSuchFieldException, IllegalAccessException { + fieldSection.set(section, palette); + Arrays.fill(section.getEmittedLightArray().asBytes(), (byte) 0); + } + + public ChunkSection newChunkSection(int y2, boolean flag, int[] array) { + try { + if (array == null) { + return new ChunkSection(y2, flag); + } else { + ChunkSection section = new ChunkSection(y2, flag); + for (int x = 0; x < 16; x++) { + + } + array; // set 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); + } + } + } + + protected BlockPosition.MutableBlockPosition pos = new BlockPosition.MutableBlockPosition(0, 0, 0); + + @Override + public CompoundTag getTileEntity(net.minecraft.server.v1_13_R2.Chunk chunk, int x, int y, int z) { + Map tiles = chunk.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) toNative(tag); + } catch (Exception e) { + MainUtil.handleError(e); + return null; + } + } + + @Deprecated + public boolean unloadChunk(final String world, final Chunk chunk) { + net.minecraft.server.v1_13_R2.Chunk c = ((CraftChunk) chunk).getHandle(); + c.mustSave = false; + if (chunk.isLoaded()) { + chunk.unload(false, false); + } + return true; + } + + @Override + public BukkitChunk_1_13 getFaweChunk(int x, int z) { + return new BukkitChunk_1_13(this, x, z); + } +}