set blocks

This commit is contained in:
Jesse Boyd 2019-05-02 04:19:15 +10:00
parent f96760b36c
commit adb2c37a02
No known key found for this signature in database
GPG Key ID: 59F1DE6293AF6E1F
7 changed files with 221 additions and 75 deletions

View File

@ -7,11 +7,14 @@ import com.boydti.fawe.beta.IQueueExtent;
import com.boydti.fawe.beta.implementation.blocks.CharSetBlocks; import com.boydti.fawe.beta.implementation.blocks.CharSetBlocks;
import com.boydti.fawe.beta.implementation.holder.ChunkHolder; import com.boydti.fawe.beta.implementation.holder.ChunkHolder;
import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.Futures;
import net.jpountz.util.UnsafeUtils;
import net.minecraft.server.v1_13_R2.Chunk; import net.minecraft.server.v1_13_R2.Chunk;
import net.minecraft.server.v1_13_R2.ChunkSection; import net.minecraft.server.v1_13_R2.ChunkSection;
import org.bukkit.World; import org.bukkit.World;
import java.util.Arrays;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicReferenceArray;
public class BukkitChunkHolder<T extends Future<T>> extends ChunkHolder { public class BukkitChunkHolder<T extends Future<T>> extends ChunkHolder {
@Override @Override
@ -33,20 +36,73 @@ public class BukkitChunkHolder<T extends Future<T>> extends ChunkHolder {
int X = getX(); int X = getX();
int Z = getZ(); int Z = getZ();
Chunk currentNmsChunk = extent.ensureLoaded(X, Z); Chunk nmsChunk = extent.ensureLoaded(X, Z);
ChunkSection[] sections = currentNmsChunk.getSections(); try {
World world = extent.getBukkitWorld(); synchronized (nmsChunk) {
boolean hasSky = world.getEnvironment() == World.Environment.NORMAL; ChunkSection[] sections = nmsChunk.getSections();
World world = extent.getBukkitWorld();
boolean hasSky = world.getEnvironment() == World.Environment.NORMAL;
for (int layer = 0; layer < 16; layer++) { for (int layer = 0; layer < 16; layer++) {
if (!set.hasSection(layer)) continue; if (!set.hasSection(layer)) continue;
char[] arr = set.blocks[layer]; char[] setArr = set.blocks[layer];
ChunkSection newSection = extent.newChunkSection(layer, hasSky, arr); ChunkSection newSection;
sections[layer] = newSection; ChunkSection existingSection = sections[layer];
if (existingSection == null) {
newSection = extent.newChunkSection(layer, hasSky, setArr);
if (BukkitQueue.setSectionAtomic(sections, null, newSection, layer)) {
continue;
} else {
existingSection = sections[layer];
if (existingSection == null) {
System.out.println("Skipping invalid null section. chunk:" + X + "," + Z + " layer: " + layer);
continue;
}
}
}
DelegateLock lock = BukkitQueue.applyLock(existingSection);
synchronized (lock) {
if (lock.isLocked()) {
lock.lock();
lock.unlock();
}
synchronized (get) {
ChunkSection getSection;
if (get.nmsChunk != nmsChunk) {
get.nmsChunk = nmsChunk;
get.sections = null;
get.reset();
System.out.println("chunk doesn't match");
} else {
getSection = get.getSections()[layer];
if (getSection != existingSection) {
get.sections[layer] = existingSection;
get.reset();
System.out.println("Section doesn't match");
} else if (lock.isModified()) {
System.out.println("lock is outdated");
get.reset(layer);
}
}
char[] getArr = get.load(layer);
for (int i = 0; i < 4096; i++) {
char value = setArr[i];
if (value != 0) {
getArr[i] = value;
}
}
newSection = extent.newChunkSection(layer, hasSky, getArr);
if (!BukkitQueue.setSectionAtomic(sections, existingSection, newSection, layer)) {
System.out.println("Failed to set chunk section:" + X + "," + Z + " layer: " + layer);
continue;
}
}
}
}
}
} finally {
extent.returnToPool(this);
} }
/* /*
- getBlocks - getBlocks

View File

@ -62,85 +62,97 @@ public class BukkitGetBlocks extends CharGetBlocks {
if (data == null || data == FaweCache.EMPTY_CHAR_4096) { if (data == null || data == FaweCache.EMPTY_CHAR_4096) {
data = new char[4096]; data = new char[4096];
} }
DelegateLock lock = BukkitQueue.applyLock(section);
synchronized (lock) {
if (lock.isLocked()) {
lock.lock();
lock.unlock();
}
lock.setModified(false);
// Efficiently convert ChunkSection to raw data
try {
final DataPaletteBlock<IBlockData> blocks = section.getBlocks();
final DataBits bits = (DataBits) BukkitQueue_1_13.fieldBits.get(blocks);
final DataPalette<IBlockData> palette = (DataPalette<IBlockData>) BukkitQueue_1_13.fieldPalette.get(blocks);
final int bitsPerEntry = bits.c();
// Efficiently convert ChunkSection to raw data final long[] blockStates = bits.a();
try { new BitArray4096(blockStates, bitsPerEntry).toRaw(data);
final DataPaletteBlock<IBlockData> blocks = section.getBlocks();
final DataBits bits = (DataBits) BukkitQueue_1_13.fieldBits.get(blocks);
final DataPalette<IBlockData> palette = (DataPalette<IBlockData>) BukkitQueue_1_13.fieldPalette.get(blocks);
final int bitsPerEntry = bits.c();
final long[] blockStates = bits.a(); int num_palette;
new BitArray4096(blockStates, bitsPerEntry).toRaw(data); if (palette instanceof DataPaletteLinear) {
num_palette = ((DataPaletteLinear<IBlockData>) palette).b();
} else if (palette instanceof DataPaletteHash) {
num_palette = ((DataPaletteHash<IBlockData>) palette).b();
} else {
num_palette = 0;
int[] paletteToBlockInts = FaweCache.PALETTE_TO_BLOCK.get();
char[] paletteToBlockChars = FaweCache.PALETTE_TO_BLOCK_CHAR.get();
try {
for (int i = 0; i < 4096; i++) {
char paletteVal = data[i];
char ordinal = paletteToBlockChars[paletteVal];
if (ordinal == Character.MAX_VALUE) {
paletteToBlockInts[num_palette++] = paletteVal;
IBlockData ibd = palette.a(data[i]);
if (ibd == null) {
ordinal = BlockTypes.AIR.getDefaultState().getOrdinalChar();
} else {
ordinal = ((Spigot_v1_13_R2) getAdapter()).adaptToChar(ibd);
}
paletteToBlockChars[paletteVal] = ordinal;
}
data[i] = ordinal;
}
} finally {
for (int i = 0; i < num_palette; i++) {
int paletteVal = paletteToBlockInts[i];
paletteToBlockChars[paletteVal] = Character.MAX_VALUE;
}
}
return data;
}
int num_palette;
if (palette instanceof DataPaletteLinear) {
num_palette = ((DataPaletteLinear<IBlockData>) palette).b();
} else if (palette instanceof DataPaletteHash) {
num_palette = ((DataPaletteHash<IBlockData>) palette).b();
} else {
num_palette = 0;
int[] paletteToBlockInts = FaweCache.PALETTE_TO_BLOCK.get();
char[] paletteToBlockChars = FaweCache.PALETTE_TO_BLOCK_CHAR.get(); char[] paletteToBlockChars = FaweCache.PALETTE_TO_BLOCK_CHAR.get();
try { try {
for (int i = 0; i < num_palette; i++) {
IBlockData ibd = palette.a(i);
char ordinal;
if (ibd == null) {
ordinal = BlockTypes.AIR.getDefaultState().getOrdinalChar();
System.out.println("Invalid palette");
} else {
ordinal = ((Spigot_v1_13_R2) getAdapter()).adaptToChar(ibd);
}
paletteToBlockChars[i] = ordinal;
}
for (int i = 0; i < 4096; i++) { for (int i = 0; i < 4096; i++) {
char paletteVal = data[i]; char paletteVal = data[i];
char ordinal = paletteToBlockChars[paletteVal]; data[i] = paletteToBlockChars[paletteVal];
if (ordinal == Character.MAX_VALUE) {
paletteToBlockInts[num_palette++] = paletteVal;
IBlockData ibd = palette.a(data[i]);
if (ibd == null) {
ordinal = BlockTypes.AIR.getDefaultState().getOrdinalChar();
} else {
ordinal = ((Spigot_v1_13_R2) getAdapter()).adaptToChar(ibd);
}
paletteToBlockChars[paletteVal] = ordinal;
}
data[i] = ordinal;
} }
} finally { } finally {
for (int i = 0; i < num_palette; i++) { for (int i = 0; i < num_palette; i++) {
int paletteVal = paletteToBlockInts[i]; paletteToBlockChars[i] = Character.MAX_VALUE;
paletteToBlockChars[paletteVal] = Character.MAX_VALUE;
} }
} }
return data; } catch (IllegalAccessException e) {
e.printStackTrace();
throw new RuntimeException(e);
} }
return data;
char[] paletteToBlockChars = FaweCache.PALETTE_TO_BLOCK_CHAR.get();
try {
for (int i = 0; i < num_palette; i++) {
IBlockData ibd = palette.a(i);
char ordinal;
if (ibd == null) {
ordinal = BlockTypes.AIR.getDefaultState().getOrdinalChar();
System.out.println("Invalid palette");
} else {
ordinal = ((Spigot_v1_13_R2) getAdapter()).adaptToChar(ibd);
}
paletteToBlockChars[i] = ordinal;
}
for (int i = 0; i < 4096; i++) {
char paletteVal = data[i];
data[i] = paletteToBlockChars[paletteVal];
}
} finally {
for (int i = 0; i < num_palette; i++) {
paletteToBlockChars[i] = Character.MAX_VALUE;
}
}
} catch (IllegalAccessException e) {
e.printStackTrace();
throw new RuntimeException(e);
} }
return data;
} }
public ChunkSection[] getSections() { public ChunkSection[] getSections() {
ChunkSection[] tmp = sections; ChunkSection[] tmp = sections;
if (tmp == null) { if (tmp == null) {
Chunk chunk = getChunk(); synchronized (this) {
sections = tmp = chunk.getSections(); tmp = sections;
if (tmp == null) {
Chunk chunk = getChunk();
sections = tmp = chunk.getSections().clone();
}
}
} }
return tmp; return tmp;
} }
@ -148,7 +160,12 @@ public class BukkitGetBlocks extends CharGetBlocks {
public Chunk getChunk() { public Chunk getChunk() {
Chunk tmp = nmsChunk; Chunk tmp = nmsChunk;
if (tmp == null) { if (tmp == null) {
nmsChunk = tmp = BukkitQueue.ensureLoaded(nmsWorld, X, Z); synchronized (this) {
tmp = nmsChunk;
if (tmp == null) {
nmsChunk = tmp = BukkitQueue.ensureLoaded(nmsWorld, X, Z);
}
}
} }
return tmp; return tmp;
} }

View File

@ -17,6 +17,7 @@ import com.sk89q.worldedit.world.World;
import com.sk89q.worldedit.world.block.BlockID; import com.sk89q.worldedit.world.block.BlockID;
import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockTypes; import com.sk89q.worldedit.world.block.BlockTypes;
import net.jpountz.util.UnsafeUtils;
import net.minecraft.server.v1_13_R2.Block; import net.minecraft.server.v1_13_R2.Block;
import net.minecraft.server.v1_13_R2.Chunk; import net.minecraft.server.v1_13_R2.Chunk;
import net.minecraft.server.v1_13_R2.ChunkCoordIntPair; import net.minecraft.server.v1_13_R2.ChunkCoordIntPair;
@ -34,9 +35,13 @@ import org.bukkit.craftbukkit.v1_13_R2.CraftChunk;
import org.bukkit.craftbukkit.v1_13_R2.CraftWorld; import org.bukkit.craftbukkit.v1_13_R2.CraftWorld;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Arrays; import java.util.Arrays;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.locks.ReentrantLock;
import sun.misc.Unsafe;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
@ -99,6 +104,11 @@ public class BukkitQueue extends SimpleCharQueueExtent {
public final static Field fieldTickingBlockCount; public final static Field fieldTickingBlockCount;
public final static Field fieldNonEmptyBlockCount; public final static Field fieldNonEmptyBlockCount;
private static final int CHUNKSECTION_BASE;
private static final int CHUNKSECTION_SHIFT;
private static final Field fieldLock;
static { static {
try { try {
fieldSize = DataPaletteBlock.class.getDeclaredField("i"); fieldSize = DataPaletteBlock.class.getDeclaredField("i");
@ -114,6 +124,20 @@ public class BukkitQueue extends SimpleCharQueueExtent {
fieldTickingBlockCount.setAccessible(true); fieldTickingBlockCount.setAccessible(true);
fieldNonEmptyBlockCount = ChunkSection.class.getDeclaredField("nonEmptyBlockCount"); fieldNonEmptyBlockCount = ChunkSection.class.getDeclaredField("nonEmptyBlockCount");
fieldNonEmptyBlockCount.setAccessible(true); fieldNonEmptyBlockCount.setAccessible(true);
fieldLock = DataPaletteBlock.class.getDeclaredField("j");
fieldLock.setAccessible(true);
Field modifiersField = Field.class.getDeclaredField("modifiers");
int modifiers = modifiersField.getInt(fieldLock);
modifiers &= ~Modifier.FINAL;
modifiersField.setInt(fieldLock, modifiers);
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) { } catch (RuntimeException e) {
throw e; throw e;
} catch (Throwable rethrow) { } catch (Throwable rethrow) {
@ -122,6 +146,32 @@ public class BukkitQueue extends SimpleCharQueueExtent {
} }
} }
public 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;
}
public static DelegateLock applyLock(ChunkSection section) {
try {
synchronized (section) {
DataPaletteBlock<IBlockData> blocks = section.getBlocks();
ReentrantLock currentLock = (ReentrantLock) 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);
}
}
private static boolean PAPER = true; private static boolean PAPER = true;
public Chunk ensureLoaded(int X, int Z) { public Chunk ensureLoaded(int X, int Z) {

View File

@ -13,6 +13,14 @@ public class DelegateLock extends ReentrantLock {
this.parent = parent; this.parent = parent;
} }
public boolean isModified() {
return modified;
}
public void setModified(boolean modified) {
this.modified = modified;
}
@Override @Override
public void lock() { public void lock() {
modified = true; modified = true;
@ -35,12 +43,16 @@ public class DelegateLock extends ReentrantLock {
} }
@Override @Override
public synchronized void unlock() { public void unlock() {
modified = true; modified = true;
parent.unlock(); parent.unlock();
this.notifyAll(); this.notifyAll();
} }
public ReentrantLock getParent() {
return parent;
}
@Override @Override
public synchronized Condition newCondition() { public synchronized Condition newCondition() {
return parent.newCondition(); return parent.newCondition();

View File

@ -78,6 +78,10 @@ public abstract class SingleThreadQueueExtent implements IQueueExtent {
// Pool discarded chunks for reuse (can safely be cleared by another thread) // Pool discarded chunks for reuse (can safely be cleared by another thread)
private static final ConcurrentLinkedQueue<IChunk> CHUNK_POOL = new ConcurrentLinkedQueue<>(); private static final ConcurrentLinkedQueue<IChunk> CHUNK_POOL = new ConcurrentLinkedQueue<>();
public void returnToPool(IChunk chunk) {
CHUNK_POOL.add(chunk);
}
@Override @Override
public <T extends Future<T>> T submit(final IChunk<T> chunk) { public <T extends Future<T>> T submit(final IChunk<T> chunk) {
if (chunk.isEmpty()) { if (chunk.isEmpty()) {
@ -123,7 +127,6 @@ public abstract class SingleThreadQueueExtent implements IQueueExtent {
private IChunk poolOrCreate(final int X, final int Z) { private IChunk poolOrCreate(final int X, final int Z) {
IChunk next = CHUNK_POOL.poll(); IChunk next = CHUNK_POOL.poll();
if (next == null) { if (next == null) {
System.out.println("Create");
next = create(false); next = create(false);
} }
next.init(this, X, Z); next.init(this, X, Z);

View File

@ -30,6 +30,10 @@ public class CharBlocks implements IBlocks {
for (int i = 0; i < 16; i++) sections[i] = NULL; for (int i = 0; i < 16; i++) sections[i] = NULL;
} }
public void reset(int layer) {
sections[layer] = NULL;
}
protected char[] load(int layer) { protected char[] load(int layer) {
return new char[4096]; return new char[4096];
} }

View File

@ -144,4 +144,8 @@ public enum UnsafeUtils {
public static void writeShort(short[] dest, int destOff, int value) { public static void writeShort(short[] dest, int destOff, int value) {
UNSAFE.putShort(dest, SHORT_ARRAY_OFFSET + SHORT_ARRAY_SCALE * destOff, (short) value); UNSAFE.putShort(dest, SHORT_ARRAY_OFFSET + SHORT_ARRAY_SCALE * destOff, (short) value);
} }
public static Unsafe getUNSAFE() {
return UNSAFE;
}
} }