Mirror von
https://github.com/IntellectualSites/FastAsyncWorldEdit.git
synchronisiert 2024-12-25 02:20:07 +01:00
set blocks
Dieser Commit ist enthalten in:
Ursprung
f96760b36c
Commit
adb2c37a02
@ -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 {
|
||||||
|
synchronized (nmsChunk) {
|
||||||
|
ChunkSection[] sections = nmsChunk.getSections();
|
||||||
World world = extent.getBukkitWorld();
|
World world = extent.getBukkitWorld();
|
||||||
boolean hasSky = world.getEnvironment() == World.Environment.NORMAL;
|
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
|
||||||
|
@ -62,7 +62,13 @@ 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
|
// Efficiently convert ChunkSection to raw data
|
||||||
try {
|
try {
|
||||||
final DataPaletteBlock<IBlockData> blocks = section.getBlocks();
|
final DataPaletteBlock<IBlockData> blocks = section.getBlocks();
|
||||||
@ -135,21 +141,32 @@ public class BukkitGetBlocks extends CharGetBlocks {
|
|||||||
}
|
}
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public ChunkSection[] getSections() {
|
public ChunkSection[] getSections() {
|
||||||
ChunkSection[] tmp = sections;
|
ChunkSection[] tmp = sections;
|
||||||
|
if (tmp == null) {
|
||||||
|
synchronized (this) {
|
||||||
|
tmp = sections;
|
||||||
if (tmp == null) {
|
if (tmp == null) {
|
||||||
Chunk chunk = getChunk();
|
Chunk chunk = getChunk();
|
||||||
sections = tmp = chunk.getSections();
|
sections = tmp = chunk.getSections().clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return tmp;
|
return tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Chunk getChunk() {
|
public Chunk getChunk() {
|
||||||
Chunk tmp = nmsChunk;
|
Chunk tmp = nmsChunk;
|
||||||
|
if (tmp == null) {
|
||||||
|
synchronized (this) {
|
||||||
|
tmp = nmsChunk;
|
||||||
if (tmp == null) {
|
if (tmp == null) {
|
||||||
nmsChunk = tmp = BukkitQueue.ensureLoaded(nmsWorld, X, Z);
|
nmsChunk = tmp = BukkitQueue.ensureLoaded(nmsWorld, X, Z);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return tmp;
|
return tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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) {
|
||||||
|
@ -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();
|
||||||
|
@ -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);
|
||||||
|
@ -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];
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren