mirror of
https://github.com/plexusorg/Plex-FAWE.git
synced 2025-07-16 03:24:03 +00:00
1.15 (wip)
Adapters and nms needs updating still, alongside some other fixes and changes, but I'm too tired for that now
This commit is contained in:
@@ -0,0 +1,266 @@
|
||||
package com.boydti.fawe.bukkit.adapter.mc1_15;
|
||||
|
||||
import com.boydti.fawe.Fawe;
|
||||
import com.boydti.fawe.FaweCache;
|
||||
import com.boydti.fawe.bukkit.adapter.DelegateLock;
|
||||
import com.boydti.fawe.bukkit.adapter.NMSAdapter;
|
||||
import com.boydti.fawe.config.Settings;
|
||||
import com.boydti.fawe.object.collection.BitArray4096;
|
||||
import com.boydti.fawe.util.MathMan;
|
||||
import com.boydti.fawe.util.TaskManager;
|
||||
import com.sk89q.worldedit.world.block.BlockState;
|
||||
import com.sk89q.worldedit.world.block.BlockTypesCache;
|
||||
import io.papermc.lib.PaperLib;
|
||||
import net.jpountz.util.UnsafeUtils;
|
||||
import net.minecraft.server.v1_15_R1.*;
|
||||
import org.bukkit.craftbukkit.v1_15_R1.CraftChunk;
|
||||
import org.bukkit.craftbukkit.v1_15_R1.CraftWorld;
|
||||
import sun.misc.Unsafe;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.function.Function;
|
||||
|
||||
public final class BukkitAdapter_1_15 extends NMSAdapter {
|
||||
/*
|
||||
NMS fields
|
||||
*/
|
||||
public final static Field fieldBits;
|
||||
public final static Field fieldPalette;
|
||||
public final static Field fieldSize;
|
||||
|
||||
public final static Field fieldFluidCount;
|
||||
public final static Field fieldTickingBlockCount;
|
||||
public final static Field fieldNonEmptyBlockCount;
|
||||
|
||||
private final static Field fieldDirtyCount;
|
||||
private final static Field fieldDirtyBits;
|
||||
|
||||
private static final int CHUNKSECTION_BASE;
|
||||
private static final int CHUNKSECTION_SHIFT;
|
||||
|
||||
private static final Field fieldLock;
|
||||
|
||||
static {
|
||||
try {
|
||||
fieldSize = DataPaletteBlock.class.getDeclaredField("i");
|
||||
fieldSize.setAccessible(true);
|
||||
fieldBits = DataPaletteBlock.class.getDeclaredField("a");
|
||||
fieldBits.setAccessible(true);
|
||||
fieldPalette = DataPaletteBlock.class.getDeclaredField("h");
|
||||
fieldPalette.setAccessible(true);
|
||||
|
||||
fieldFluidCount = ChunkSection.class.getDeclaredField("e");
|
||||
fieldFluidCount.setAccessible(true);
|
||||
fieldTickingBlockCount = ChunkSection.class.getDeclaredField("tickingBlockCount");
|
||||
fieldTickingBlockCount.setAccessible(true);
|
||||
fieldNonEmptyBlockCount = ChunkSection.class.getDeclaredField("nonEmptyBlockCount");
|
||||
fieldNonEmptyBlockCount.setAccessible(true);
|
||||
|
||||
fieldDirtyCount = PlayerChunk.class.getDeclaredField("dirtyCount");
|
||||
fieldDirtyCount.setAccessible(true);
|
||||
fieldDirtyBits = PlayerChunk.class.getDeclaredField("r");
|
||||
fieldDirtyBits.setAccessible(true);
|
||||
|
||||
{
|
||||
Field tmp;
|
||||
try {
|
||||
tmp = DataPaletteBlock.class.getDeclaredField("writeLock");
|
||||
} catch (NoSuchFieldException paper) {
|
||||
tmp = DataPaletteBlock.class.getDeclaredField("j");
|
||||
}
|
||||
Field modifiersField = Field.class.getDeclaredField("modifiers");
|
||||
modifiersField.setAccessible(true);
|
||||
int modifiers = modifiersField.getInt(tmp);
|
||||
int newModifiers = modifiers & (~Modifier.FINAL);
|
||||
if (newModifiers != modifiers) modifiersField.setInt(tmp, newModifiers);
|
||||
fieldLock = tmp;
|
||||
fieldLock.setAccessible(true);
|
||||
}
|
||||
|
||||
Unsafe unsafe = UnsafeUtils.getUNSAFE();
|
||||
CHUNKSECTION_BASE = unsafe.arrayBaseOffset(ChunkSection[].class);
|
||||
int scale = unsafe.arrayIndexScale(ChunkSection[].class);
|
||||
if ((scale & (scale - 1)) != 0)
|
||||
throw new Error("data type scale not a power of two");
|
||||
CHUNKSECTION_SHIFT = 31 - Integer.numberOfLeadingZeros(scale);
|
||||
} catch (RuntimeException e) {
|
||||
throw e;
|
||||
} catch (Throwable rethrow) {
|
||||
rethrow.printStackTrace();
|
||||
throw new RuntimeException(rethrow);
|
||||
}
|
||||
}
|
||||
|
||||
protected static boolean setSectionAtomic(ChunkSection[] sections, ChunkSection expected, ChunkSection value, int layer) {
|
||||
long offset = ((long) layer << CHUNKSECTION_SHIFT) + CHUNKSECTION_BASE;
|
||||
if (layer >= 0 && layer < sections.length) {
|
||||
return UnsafeUtils.getUNSAFE().compareAndSwapObject(sections, offset, expected, value);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected static DelegateLock applyLock(ChunkSection section) {
|
||||
try {
|
||||
synchronized (section) {
|
||||
DataPaletteBlock<IBlockData> blocks = section.getBlocks();
|
||||
Lock currentLock = (Lock) fieldLock.get(blocks);
|
||||
if (currentLock instanceof DelegateLock) {
|
||||
return (DelegateLock) currentLock;
|
||||
}
|
||||
DelegateLock newLock = new DelegateLock(currentLock);
|
||||
fieldLock.set(blocks, newLock);
|
||||
return newLock;
|
||||
}
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static Chunk ensureLoaded(net.minecraft.server.v1_15_R1.World nmsWorld, int X, int Z) {
|
||||
Chunk nmsChunk = nmsWorld.getChunkIfLoaded(X, Z);
|
||||
if (nmsChunk != null) {
|
||||
return nmsChunk;
|
||||
}
|
||||
if (Fawe.isMainThread()) {
|
||||
return nmsWorld.getChunkAt(X, Z);
|
||||
}
|
||||
if (PaperLib.isPaper()) {
|
||||
CraftWorld craftWorld = nmsWorld.getWorld();
|
||||
CompletableFuture<org.bukkit.Chunk> future = craftWorld.getChunkAtAsync(X, Z, true);
|
||||
try {
|
||||
CraftChunk chunk = (CraftChunk) future.get();
|
||||
return chunk.getHandle();
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
// TODO optimize
|
||||
return TaskManager.IMP.sync(() -> nmsWorld.getChunkAt(X, Z));
|
||||
}
|
||||
|
||||
public static PlayerChunk getPlayerChunk(net.minecraft.server.v1_15_R1.WorldServer nmsWorld, final int cx, final int cz) {
|
||||
PlayerChunkMap chunkMap = nmsWorld.getChunkProvider().playerChunkMap;
|
||||
PlayerChunk playerChunk = chunkMap.visibleChunks.get(ChunkCoordIntPair.pair(cx, cz));
|
||||
return playerChunk;
|
||||
}
|
||||
|
||||
public static void sendChunk(net.minecraft.server.v1_15_R1.WorldServer nmsWorld, int X, int Z, int mask) {
|
||||
PlayerChunk playerChunk = getPlayerChunk(nmsWorld, X, Z);
|
||||
if (playerChunk == null) {
|
||||
return;
|
||||
}
|
||||
if (playerChunk.hasBeenLoaded()) {
|
||||
TaskManager.IMP.sync(() -> {
|
||||
try {
|
||||
int dirtyBits = fieldDirtyBits.getInt(playerChunk);
|
||||
if (dirtyBits == 0) {
|
||||
nmsWorld.getChunkProvider().playerChunkMap.a(playerChunk);
|
||||
}
|
||||
if (mask == 0) {
|
||||
dirtyBits = 65535;
|
||||
} else {
|
||||
dirtyBits |= mask;
|
||||
}
|
||||
|
||||
fieldDirtyBits.set(playerChunk, dirtyBits);
|
||||
fieldDirtyCount.set(playerChunk, 64);
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
});
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
NMS conversion
|
||||
*/
|
||||
public static ChunkSection newChunkSection(final int layer, final char[] blocks) {
|
||||
return newChunkSection(layer, null, blocks);
|
||||
}
|
||||
|
||||
public static ChunkSection newChunkSection(final int layer, final Function<Integer, char[]> get, char[] set) {
|
||||
if (set == null) {
|
||||
return newChunkSection(layer);
|
||||
}
|
||||
final int[] blockToPalette = FaweCache.IMP.BLOCK_TO_PALETTE.get();
|
||||
final int[] paletteToBlock = FaweCache.IMP.PALETTE_TO_BLOCK.get();
|
||||
final long[] blockStates = FaweCache.IMP.BLOCK_STATES.get();
|
||||
final int[] blocksCopy = FaweCache.IMP.SECTION_BLOCKS.get();
|
||||
try {
|
||||
int[] num_palette_buffer = new int[1];
|
||||
int air;
|
||||
if (get == null) {
|
||||
air = createPalette(blockToPalette, paletteToBlock, blocksCopy, num_palette_buffer, set);
|
||||
} else {
|
||||
air = createPalette(layer, blockToPalette, paletteToBlock, blocksCopy, num_palette_buffer, get, set);
|
||||
}
|
||||
int num_palette = num_palette_buffer[0];
|
||||
// BlockStates
|
||||
int bitsPerEntry = MathMan.log2nlz(num_palette - 1);
|
||||
if (Settings.IMP.PROTOCOL_SUPPORT_FIX || num_palette != 1) {
|
||||
bitsPerEntry = Math.max(bitsPerEntry, 4); // Protocol support breaks <4 bits per entry
|
||||
} else {
|
||||
bitsPerEntry = Math.max(bitsPerEntry, 1); // For some reason minecraft needs 4096 bits to store 0 entries
|
||||
}
|
||||
|
||||
final int blockBitArrayEnd = (bitsPerEntry * 4096) >> 6;
|
||||
if (num_palette == 1) {
|
||||
for (int i = 0; i < blockBitArrayEnd; i++) blockStates[i] = 0;
|
||||
} else {
|
||||
final BitArray4096 bitArray = new BitArray4096(blockStates, bitsPerEntry);
|
||||
bitArray.fromRaw(blocksCopy);
|
||||
}
|
||||
|
||||
ChunkSection section = newChunkSection(layer);
|
||||
// set palette & data bits
|
||||
final DataPaletteBlock<IBlockData> dataPaletteBlocks = section.getBlocks();
|
||||
// private DataPalette<T> h;
|
||||
// protected DataBits a;
|
||||
final long[] bits = Arrays.copyOfRange(blockStates, 0, blockBitArrayEnd);
|
||||
final DataBits nmsBits = new DataBits(bitsPerEntry, 4096, bits);
|
||||
final DataPalette<IBlockData> palette;
|
||||
// palette = new DataPaletteHash<>(Block.REGISTRY_ID, bitsPerEntry, dataPaletteBlocks, GameProfileSerializer::d, GameProfileSerializer::a);
|
||||
palette = new DataPaletteLinear<>(Block.REGISTRY_ID, bitsPerEntry, dataPaletteBlocks, GameProfileSerializer::d);
|
||||
|
||||
// set palette
|
||||
for (int i = 0; i < num_palette; i++) {
|
||||
final int ordinal = paletteToBlock[i];
|
||||
blockToPalette[ordinal] = Integer.MAX_VALUE;
|
||||
final BlockState state = BlockTypesCache.states[ordinal];
|
||||
final IBlockData ibd = ((BlockMaterial_1_15) state.getMaterial()).getState();
|
||||
palette.a(ibd);
|
||||
}
|
||||
try {
|
||||
fieldBits.set(dataPaletteBlocks, nmsBits);
|
||||
fieldPalette.set(dataPaletteBlocks, palette);
|
||||
fieldSize.set(dataPaletteBlocks, bitsPerEntry);
|
||||
setCount(0, 4096 - air, section);
|
||||
} catch (final IllegalAccessException | NoSuchFieldException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
return section;
|
||||
} catch (final Throwable e) {
|
||||
Arrays.fill(blockToPalette, Integer.MAX_VALUE);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private static ChunkSection newChunkSection(int layer) {
|
||||
return new ChunkSection(layer << 4);
|
||||
}
|
||||
|
||||
public static void setCount(final int tickingBlockCount, final int nonEmptyBlockCount, final ChunkSection section) throws NoSuchFieldException, IllegalAccessException {
|
||||
fieldFluidCount.setShort(section, (short) 0); // TODO FIXME
|
||||
fieldTickingBlockCount.setShort(section, (short) tickingBlockCount);
|
||||
fieldNonEmptyBlockCount.setShort(section, (short) nonEmptyBlockCount);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user