mirror of
https://github.com/plexusorg/Plex-FAWE.git
synced 2025-07-02 11:26:42 +00:00
This commit is contained in:
@ -22,5 +22,5 @@ configurations.all {
|
||||
|
||||
dependencies {
|
||||
the<PaperweightUserDependenciesExtension>().paperDevBundle("1.17.1-R0.1-20220414.034903-210")
|
||||
compileOnly("io.papermc:paperlib")
|
||||
compileOnly(libs.paperlib)
|
||||
}
|
||||
|
@ -85,11 +85,7 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
|
||||
private static final MethodHandle methodGetVisibleChunk;
|
||||
|
||||
private static final int CHUNKSECTION_BASE;
|
||||
private static final int CHUNKSECTION_SHIFT;
|
||||
|
||||
private static final Field fieldLock;
|
||||
private static final long fieldLockOffset;
|
||||
|
||||
private static final Field fieldGameEventDispatcherSections;
|
||||
private static final MethodHandle methodremoveBlockEntityTicker;
|
||||
@ -127,15 +123,12 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
getVisibleChunkIfPresent.setAccessible(true);
|
||||
methodGetVisibleChunk = MethodHandles.lookup().unreflect(getVisibleChunkIfPresent);
|
||||
|
||||
Unsafe unsafe = ReflectionUtils.getUnsafe();
|
||||
if (!PaperLib.isPaper()) {
|
||||
|
||||
fieldLock = PalettedContainer.class.getDeclaredField(Refraction.pickName("lock", "m"));
|
||||
fieldLockOffset = unsafe.objectFieldOffset(fieldLock);
|
||||
fieldLock.setAccessible(true);
|
||||
} else {
|
||||
// in paper, the used methods are synchronized properly
|
||||
fieldLock = null;
|
||||
fieldLockOffset = -1;
|
||||
}
|
||||
|
||||
fieldGameEventDispatcherSections = LevelChunk.class.getDeclaredField(Refraction.pickName(
|
||||
@ -152,13 +145,6 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
|
||||
fieldRemove = BlockEntity.class.getDeclaredField(Refraction.pickName("remove", "p"));
|
||||
fieldRemove.setAccessible(true);
|
||||
|
||||
CHUNKSECTION_BASE = unsafe.arrayBaseOffset(LevelChunkSection[].class);
|
||||
int scale = unsafe.arrayIndexScale(LevelChunkSection[].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) {
|
||||
@ -173,9 +159,8 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
LevelChunkSection value,
|
||||
int layer
|
||||
) {
|
||||
long offset = ((long) layer << CHUNKSECTION_SHIFT) + CHUNKSECTION_BASE;
|
||||
if (layer >= 0 && layer < sections.length) {
|
||||
return ReflectionUtils.getUnsafe().compareAndSwapObject(sections, offset, expected, value);
|
||||
return ReflectionUtils.compareAndSet(sections, expected, value, layer);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -190,14 +175,13 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
}
|
||||
try {
|
||||
synchronized (section) {
|
||||
Unsafe unsafe = ReflectionUtils.getUnsafe();
|
||||
PalettedContainer<net.minecraft.world.level.block.state.BlockState> blocks = section.getStates();
|
||||
Semaphore currentLock = (Semaphore) unsafe.getObject(blocks, fieldLockOffset);
|
||||
Semaphore currentLock = (Semaphore) fieldLock.get(blocks);
|
||||
if (currentLock instanceof DelegateSemaphore delegateSemaphore) {
|
||||
return delegateSemaphore;
|
||||
}
|
||||
DelegateSemaphore newLock = new DelegateSemaphore(1, currentLock);
|
||||
unsafe.putObject(blocks, fieldLockOffset, newLock);
|
||||
fieldLock.set(blocks, newLock);
|
||||
return newLock;
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
|
@ -184,9 +184,6 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
|
||||
protected boolean prepare() {
|
||||
this.originalServerWorld = ((CraftWorld) originalBukkitWorld).getHandle();
|
||||
originalChunkProvider = originalServerWorld.getChunkSource();
|
||||
if (!(originalChunkProvider instanceof ServerChunkCache)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//flat bedrock? (only on paper)
|
||||
if (paperConfigField != null) {
|
||||
@ -197,7 +194,7 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
|
||||
}
|
||||
|
||||
seed = options.getSeed().orElse(originalServerWorld.getSeed());
|
||||
chunkStati.forEach((s, c) -> super.chunkStati.put(new ChunkStatusWrap(s), c));
|
||||
chunkStati.forEach((s, c) -> super.chunkStatuses.put(new ChunkStatusWrap(s), c));
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -332,7 +329,7 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
|
||||
}
|
||||
};
|
||||
|
||||
ReflectionUtils.unsafeSet(chunkSourceField, freshWorld, freshChunkProvider);
|
||||
chunkSourceField.set(freshWorld, freshChunkProvider);
|
||||
//let's start then
|
||||
structureManager = server.getStructureManager();
|
||||
threadedLevelLightEngine = freshChunkProvider.getLightEngine();
|
||||
@ -680,7 +677,7 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<?> processChunk(Long xz, List<ChunkAccess> accessibleChunks) {
|
||||
public CompletableFuture<?> processChunk(List<ChunkAccess> accessibleChunks) {
|
||||
return chunkStatus.generate(
|
||||
Runnable::run, // TODO revisit, we might profit from this somehow?
|
||||
freshWorld,
|
||||
|
@ -13,5 +13,5 @@ repositories {
|
||||
dependencies {
|
||||
// https://papermc.io/repo/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/
|
||||
the<PaperweightUserDependenciesExtension>().paperDevBundle("1.18.2-R0.1-20220920.010157-167")
|
||||
compileOnly("io.papermc:paperlib")
|
||||
compileOnly(libs.paperlib)
|
||||
}
|
||||
|
@ -92,14 +92,8 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
|
||||
private static final MethodHandle methodGetVisibleChunk;
|
||||
|
||||
private static final int CHUNKSECTION_BASE;
|
||||
private static final int CHUNKSECTION_SHIFT;
|
||||
|
||||
private static final Field fieldThreadingDetector;
|
||||
private static final long fieldThreadingDetectorOffset;
|
||||
|
||||
private static final Field fieldLock;
|
||||
private static final long fieldLockOffset;
|
||||
|
||||
private static final MethodHandle methodRemoveGameEventListener;
|
||||
private static final MethodHandle methodremoveTickingBlockEntity;
|
||||
@ -136,20 +130,15 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
getVisibleChunkIfPresent.setAccessible(true);
|
||||
methodGetVisibleChunk = MethodHandles.lookup().unreflect(getVisibleChunkIfPresent);
|
||||
|
||||
Unsafe unsafe = ReflectionUtils.getUnsafe();
|
||||
if (!PaperLib.isPaper()) {
|
||||
fieldThreadingDetector = PalettedContainer.class.getDeclaredField(Refraction.pickName("threadingDetector", "f"));
|
||||
fieldThreadingDetectorOffset = unsafe.objectFieldOffset(fieldThreadingDetector);
|
||||
|
||||
fieldThreadingDetector.setAccessible(true);
|
||||
fieldLock = ThreadingDetector.class.getDeclaredField(Refraction.pickName("lock", "c"));
|
||||
fieldLockOffset = unsafe.objectFieldOffset(fieldLock);
|
||||
fieldLock.setAccessible(true);
|
||||
} else {
|
||||
// in paper, the used methods are synchronized properly
|
||||
fieldThreadingDetector = null;
|
||||
fieldThreadingDetectorOffset = -1;
|
||||
|
||||
fieldLock = null;
|
||||
fieldLockOffset = -1;
|
||||
}
|
||||
|
||||
Method removeGameEventListener = LevelChunk.class.getDeclaredMethod(
|
||||
@ -169,13 +158,6 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
|
||||
fieldRemove = BlockEntity.class.getDeclaredField(Refraction.pickName("remove", "p"));
|
||||
fieldRemove.setAccessible(true);
|
||||
|
||||
CHUNKSECTION_BASE = unsafe.arrayBaseOffset(LevelChunkSection[].class);
|
||||
int scale = unsafe.arrayIndexScale(LevelChunkSection[].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) {
|
||||
@ -190,9 +172,8 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
LevelChunkSection value,
|
||||
int layer
|
||||
) {
|
||||
long offset = ((long) layer << CHUNKSECTION_SHIFT) + CHUNKSECTION_BASE;
|
||||
if (layer >= 0 && layer < sections.length) {
|
||||
return ReflectionUtils.getUnsafe().compareAndSwapObject(sections, offset, expected, value);
|
||||
return ReflectionUtils.compareAndSet(sections, expected, value, layer);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -207,19 +188,15 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
}
|
||||
try {
|
||||
synchronized (section) {
|
||||
Unsafe unsafe = ReflectionUtils.getUnsafe();
|
||||
PalettedContainer<net.minecraft.world.level.block.state.BlockState> blocks = section.getStates();
|
||||
ThreadingDetector currentThreadingDetector = (ThreadingDetector) unsafe.getObject(
|
||||
blocks,
|
||||
fieldThreadingDetectorOffset
|
||||
);
|
||||
ThreadingDetector currentThreadingDetector = (ThreadingDetector) fieldThreadingDetector.get(blocks);
|
||||
synchronized (currentThreadingDetector) {
|
||||
Semaphore currentLock = (Semaphore) unsafe.getObject(currentThreadingDetector, fieldLockOffset);
|
||||
Semaphore currentLock = (Semaphore) fieldLock.get(currentThreadingDetector);
|
||||
if (currentLock instanceof DelegateSemaphore delegateSemaphore) {
|
||||
return delegateSemaphore;
|
||||
}
|
||||
DelegateSemaphore newLock = new DelegateSemaphore(1, currentLock);
|
||||
unsafe.putObject(currentThreadingDetector, fieldLockOffset, newLock);
|
||||
fieldLock.set(currentThreadingDetector, newLock);
|
||||
return newLock;
|
||||
}
|
||||
}
|
||||
|
@ -178,9 +178,6 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
|
||||
protected boolean prepare() {
|
||||
this.originalServerWorld = ((CraftWorld) originalBukkitWorld).getHandle();
|
||||
originalChunkProvider = originalServerWorld.getChunkSource();
|
||||
if (!(originalChunkProvider instanceof ServerChunkCache)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//flat bedrock? (only on paper)
|
||||
if (paperConfigField != null) {
|
||||
@ -191,7 +188,7 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
|
||||
}
|
||||
|
||||
seed = options.getSeed().orElse(originalServerWorld.getSeed());
|
||||
chunkStati.forEach((s, c) -> super.chunkStati.put(new ChunkStatusWrap(s), c));
|
||||
chunkStati.forEach((s, c) -> super.chunkStatuses.put(new ChunkStatusWrap(s), c));
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -345,7 +342,7 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
|
||||
}
|
||||
};
|
||||
|
||||
ReflectionUtils.unsafeSet(chunkSourceField, freshWorld, freshChunkProvider);
|
||||
chunkSourceField.set(freshWorld, freshChunkProvider);
|
||||
//let's start then
|
||||
structureManager = server.getStructureManager();
|
||||
threadedLevelLightEngine = freshChunkProvider.getLightEngine();
|
||||
@ -523,7 +520,7 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<?> processChunk(Long xz, List<ChunkAccess> accessibleChunks) {
|
||||
public CompletableFuture<?> processChunk(List<ChunkAccess> accessibleChunks) {
|
||||
return chunkStatus.generate(
|
||||
Runnable::run, // TODO revisit, we might profit from this somehow?
|
||||
freshWorld,
|
||||
|
@ -12,5 +12,5 @@ repositories {
|
||||
|
||||
dependencies {
|
||||
the<PaperweightUserDependenciesExtension>().paperDevBundle("1.19.4-R0.1-20230608.201059-104")
|
||||
compileOnly("io.papermc:paperlib")
|
||||
compileOnly(libs.paperlib)
|
||||
}
|
||||
|
@ -99,14 +99,8 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
|
||||
private static final MethodHandle methodGetVisibleChunk;
|
||||
|
||||
private static final int CHUNKSECTION_BASE;
|
||||
private static final int CHUNKSECTION_SHIFT;
|
||||
|
||||
private static final Field fieldThreadingDetector;
|
||||
private static final long fieldThreadingDetectorOffset;
|
||||
|
||||
private static final Field fieldLock;
|
||||
private static final long fieldLockOffset;
|
||||
|
||||
private static final MethodHandle methodRemoveGameEventListener;
|
||||
private static final MethodHandle methodremoveTickingBlockEntity;
|
||||
@ -149,20 +143,15 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
getVisibleChunkIfPresent.setAccessible(true);
|
||||
methodGetVisibleChunk = lookup.unreflect(getVisibleChunkIfPresent);
|
||||
|
||||
Unsafe unsafe = ReflectionUtils.getUnsafe();
|
||||
if (!PaperLib.isPaper()) {
|
||||
fieldThreadingDetector = PalettedContainer.class.getDeclaredField(Refraction.pickName("threadingDetector", "f"));
|
||||
fieldThreadingDetectorOffset = unsafe.objectFieldOffset(fieldThreadingDetector);
|
||||
|
||||
fieldThreadingDetector.setAccessible(true);
|
||||
fieldLock = ThreadingDetector.class.getDeclaredField(Refraction.pickName("lock", "c"));
|
||||
fieldLockOffset = unsafe.objectFieldOffset(fieldLock);
|
||||
fieldLock.setAccessible(true);
|
||||
} else {
|
||||
// in paper, the used methods are synchronized properly
|
||||
fieldThreadingDetector = null;
|
||||
fieldThreadingDetectorOffset = -1;
|
||||
|
||||
fieldLock = null;
|
||||
fieldLockOffset = -1;
|
||||
}
|
||||
|
||||
Method removeGameEventListener = LevelChunk.class.getDeclaredMethod(
|
||||
@ -185,12 +174,6 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
fieldRemove = BlockEntity.class.getDeclaredField(Refraction.pickName("remove", "q"));
|
||||
fieldRemove.setAccessible(true);
|
||||
|
||||
CHUNKSECTION_BASE = unsafe.arrayBaseOffset(LevelChunkSection[].class);
|
||||
int scale = unsafe.arrayIndexScale(LevelChunkSection[].class);
|
||||
if ((scale & (scale - 1)) != 0) {
|
||||
throw new Error("data type scale not a power of two");
|
||||
}
|
||||
CHUNKSECTION_SHIFT = 31 - Integer.numberOfLeadingZeros(scale);
|
||||
boolean chunkRewrite;
|
||||
try {
|
||||
ServerLevel.class.getDeclaredMethod("getEntityLookup");
|
||||
@ -226,9 +209,8 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
LevelChunkSection value,
|
||||
int layer
|
||||
) {
|
||||
long offset = ((long) layer << CHUNKSECTION_SHIFT) + CHUNKSECTION_BASE;
|
||||
if (layer >= 0 && layer < sections.length) {
|
||||
return ReflectionUtils.getUnsafe().compareAndSwapObject(sections, offset, expected, value);
|
||||
return ReflectionUtils.compareAndSet(sections, expected, value, layer);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -243,19 +225,15 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
}
|
||||
try {
|
||||
synchronized (section) {
|
||||
Unsafe unsafe = ReflectionUtils.getUnsafe();
|
||||
PalettedContainer<net.minecraft.world.level.block.state.BlockState> blocks = section.getStates();
|
||||
ThreadingDetector currentThreadingDetector = (ThreadingDetector) unsafe.getObject(
|
||||
blocks,
|
||||
fieldThreadingDetectorOffset
|
||||
);
|
||||
ThreadingDetector currentThreadingDetector = (ThreadingDetector) fieldThreadingDetector.get(blocks);
|
||||
synchronized (currentThreadingDetector) {
|
||||
Semaphore currentLock = (Semaphore) unsafe.getObject(currentThreadingDetector, fieldLockOffset);
|
||||
Semaphore currentLock = (Semaphore) fieldLock.get(currentThreadingDetector);
|
||||
if (currentLock instanceof DelegateSemaphore delegateSemaphore) {
|
||||
return delegateSemaphore;
|
||||
}
|
||||
DelegateSemaphore newLock = new DelegateSemaphore(1, currentLock);
|
||||
unsafe.putObject(currentThreadingDetector, fieldLockOffset, newLock);
|
||||
fieldLock.set(currentThreadingDetector, newLock);
|
||||
return newLock;
|
||||
}
|
||||
}
|
||||
|
@ -192,9 +192,6 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
|
||||
protected boolean prepare() {
|
||||
this.originalServerWorld = ((CraftWorld) originalBukkitWorld).getHandle();
|
||||
originalChunkProvider = originalServerWorld.getChunkSource();
|
||||
if (!(originalChunkProvider instanceof ServerChunkCache)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//flat bedrock? (only on paper)
|
||||
if (paperConfigField != null) {
|
||||
@ -205,7 +202,7 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
|
||||
}
|
||||
|
||||
seed = options.getSeed().orElse(originalServerWorld.getSeed());
|
||||
chunkStati.forEach((s, c) -> super.chunkStati.put(new ChunkStatusWrap(s), c));
|
||||
chunkStati.forEach((s, c) -> super.chunkStatuses.put(new ChunkStatusWrap(s), c));
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -372,7 +369,7 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
|
||||
}
|
||||
|
||||
|
||||
ReflectionUtils.unsafeSet(chunkSourceField, freshWorld, freshChunkProvider);
|
||||
chunkSourceField.set(freshWorld, freshChunkProvider);
|
||||
//let's start then
|
||||
structureTemplateManager = server.getStructureManager();
|
||||
threadedLevelLightEngine = new NoOpLightEngine(freshChunkProvider);
|
||||
@ -554,7 +551,7 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<?> processChunk(Long xz, List<ChunkAccess> accessibleChunks) {
|
||||
public CompletableFuture<?> processChunk(List<ChunkAccess> accessibleChunks) {
|
||||
return chunkStatus.generate(
|
||||
Runnable::run, // TODO revisit, we might profit from this somehow?
|
||||
freshWorld,
|
||||
|
@ -12,6 +12,6 @@ repositories {
|
||||
|
||||
dependencies {
|
||||
// https://repo.papermc.io/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/
|
||||
the<PaperweightUserDependenciesExtension>().paperDevBundle("1.20.1-R0.1-20230623.105806-29")
|
||||
compileOnly("io.papermc:paperlib")
|
||||
the<PaperweightUserDependenciesExtension>().paperDevBundle("1.20.1-R0.1-20230916.212543-167")
|
||||
compileOnly(libs.paperlib)
|
||||
}
|
||||
|
@ -637,7 +637,7 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
|
||||
final net.minecraft.core.Direction enumFacing = adapt(face);
|
||||
BlockHitResult rayTrace = new BlockHitResult(blockVec, enumFacing, blockPos, false);
|
||||
UseOnContext context = new UseOnContext(fakePlayer, InteractionHand.MAIN_HAND, rayTrace);
|
||||
InteractionResult result = stack.useOn(context, InteractionHand.MAIN_HAND);
|
||||
InteractionResult result = stack.useOn(context);
|
||||
if (result != InteractionResult.SUCCESS) {
|
||||
if (worldServer.getBlockState(blockPos).use(worldServer, fakePlayer, InteractionHand.MAIN_HAND, rayTrace).consumesAction()) {
|
||||
result = InteractionResult.SUCCESS;
|
||||
|
@ -102,14 +102,8 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
|
||||
private static final MethodHandle methodGetVisibleChunk;
|
||||
|
||||
private static final int CHUNKSECTION_BASE;
|
||||
private static final int CHUNKSECTION_SHIFT;
|
||||
|
||||
private static final Field fieldThreadingDetector;
|
||||
private static final long fieldThreadingDetectorOffset;
|
||||
|
||||
private static final Field fieldLock;
|
||||
private static final long fieldLockOffset;
|
||||
|
||||
private static final MethodHandle methodRemoveGameEventListener;
|
||||
private static final MethodHandle methodremoveTickingBlockEntity;
|
||||
@ -158,20 +152,15 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
getVisibleChunkIfPresent.setAccessible(true);
|
||||
methodGetVisibleChunk = lookup.unreflect(getVisibleChunkIfPresent);
|
||||
|
||||
Unsafe unsafe = ReflectionUtils.getUnsafe();
|
||||
if (!PaperLib.isPaper()) {
|
||||
fieldThreadingDetector = PalettedContainer.class.getDeclaredField(Refraction.pickName("threadingDetector", "f"));
|
||||
fieldThreadingDetectorOffset = unsafe.objectFieldOffset(fieldThreadingDetector);
|
||||
|
||||
fieldThreadingDetector.setAccessible(true);
|
||||
fieldLock = ThreadingDetector.class.getDeclaredField(Refraction.pickName("lock", "c"));
|
||||
fieldLockOffset = unsafe.objectFieldOffset(fieldLock);
|
||||
fieldLock.setAccessible(true);
|
||||
} else {
|
||||
// in paper, the used methods are synchronized properly
|
||||
fieldThreadingDetector = null;
|
||||
fieldThreadingDetectorOffset = -1;
|
||||
|
||||
fieldLock = null;
|
||||
fieldLockOffset = -1;
|
||||
}
|
||||
|
||||
Method removeGameEventListener = LevelChunk.class.getDeclaredMethod(
|
||||
@ -194,12 +183,6 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
fieldRemove = BlockEntity.class.getDeclaredField(Refraction.pickName("remove", "q"));
|
||||
fieldRemove.setAccessible(true);
|
||||
|
||||
CHUNKSECTION_BASE = unsafe.arrayBaseOffset(LevelChunkSection[].class);
|
||||
int scale = unsafe.arrayIndexScale(LevelChunkSection[].class);
|
||||
if ((scale & (scale - 1)) != 0) {
|
||||
throw new Error("data type scale not a power of two");
|
||||
}
|
||||
CHUNKSECTION_SHIFT = 31 - Integer.numberOfLeadingZeros(scale);
|
||||
boolean chunkRewrite;
|
||||
try {
|
||||
ServerLevel.class.getDeclaredMethod("getEntityLookup");
|
||||
@ -249,9 +232,8 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
LevelChunkSection value,
|
||||
int layer
|
||||
) {
|
||||
long offset = ((long) layer << CHUNKSECTION_SHIFT) + CHUNKSECTION_BASE;
|
||||
if (layer >= 0 && layer < sections.length) {
|
||||
return ReflectionUtils.getUnsafe().compareAndSwapObject(sections, offset, expected, value);
|
||||
return ReflectionUtils.compareAndSet(sections, expected, value, layer);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -266,19 +248,15 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||
}
|
||||
try {
|
||||
synchronized (section) {
|
||||
Unsafe unsafe = ReflectionUtils.getUnsafe();
|
||||
PalettedContainer<net.minecraft.world.level.block.state.BlockState> blocks = section.getStates();
|
||||
ThreadingDetector currentThreadingDetector = (ThreadingDetector) unsafe.getObject(
|
||||
blocks,
|
||||
fieldThreadingDetectorOffset
|
||||
);
|
||||
ThreadingDetector currentThreadingDetector = (ThreadingDetector) fieldThreadingDetector.get(blocks);
|
||||
synchronized (currentThreadingDetector) {
|
||||
Semaphore currentLock = (Semaphore) unsafe.getObject(currentThreadingDetector, fieldLockOffset);
|
||||
Semaphore currentLock = (Semaphore) fieldLock.get(currentThreadingDetector);
|
||||
if (currentLock instanceof DelegateSemaphore delegateSemaphore) {
|
||||
return delegateSemaphore;
|
||||
}
|
||||
DelegateSemaphore newLock = new DelegateSemaphore(1, currentLock);
|
||||
unsafe.putObject(currentThreadingDetector, fieldLockOffset, newLock);
|
||||
fieldLock.set(currentThreadingDetector, newLock);
|
||||
return newLock;
|
||||
}
|
||||
}
|
||||
|
@ -192,9 +192,6 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
|
||||
protected boolean prepare() {
|
||||
this.originalServerWorld = ((CraftWorld) originalBukkitWorld).getHandle();
|
||||
originalChunkProvider = originalServerWorld.getChunkSource();
|
||||
if (!(originalChunkProvider instanceof ServerChunkCache)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//flat bedrock? (only on paper)
|
||||
if (paperConfigField != null) {
|
||||
@ -205,7 +202,7 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
|
||||
}
|
||||
|
||||
seed = options.getSeed().orElse(originalServerWorld.getSeed());
|
||||
chunkStati.forEach((s, c) -> super.chunkStati.put(new ChunkStatusWrap(s), c));
|
||||
chunkStati.forEach((s, c) -> super.chunkStatuses.put(new ChunkStatusWrap(s), c));
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -373,7 +370,7 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
|
||||
}
|
||||
|
||||
|
||||
ReflectionUtils.unsafeSet(chunkSourceField, freshWorld, freshChunkProvider);
|
||||
chunkSourceField.set(freshWorld, freshChunkProvider);
|
||||
//let's start then
|
||||
structureTemplateManager = server.getStructureManager();
|
||||
threadedLevelLightEngine = new NoOpLightEngine(freshChunkProvider);
|
||||
@ -555,7 +552,7 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<?> processChunk(Long xz, List<ChunkAccess> accessibleChunks) {
|
||||
public CompletableFuture<?> processChunk(List<ChunkAccess> accessibleChunks) {
|
||||
return chunkStatus.generate(
|
||||
Runnable::run, // TODO revisit, we might profit from this somehow?
|
||||
freshWorld,
|
||||
|
@ -3,7 +3,7 @@ import io.papermc.paperweight.userdev.attribute.Obfuscation
|
||||
|
||||
plugins {
|
||||
`java-library`
|
||||
id("com.modrinth.minotaur") version "2.8.3"
|
||||
alias(libs.plugins.minotaur)
|
||||
}
|
||||
|
||||
project.description = "Bukkit"
|
||||
@ -74,19 +74,19 @@ dependencies {
|
||||
implementation(libs.fastutil)
|
||||
|
||||
// Platform expectations
|
||||
compileOnly("io.papermc.paper:paper-api") {
|
||||
compileOnly(libs.paper) {
|
||||
exclude("junit", "junit")
|
||||
exclude(group = "org.slf4j", module = "slf4j-api")
|
||||
}
|
||||
|
||||
// Logging
|
||||
localImplementation("org.apache.logging.log4j:log4j-api")
|
||||
localImplementation(libs.log4jApi)
|
||||
localImplementation(libs.log4jBom) {
|
||||
because("Spigot provides Log4J (sort of, not in API, implicitly part of server)")
|
||||
}
|
||||
|
||||
// Plugins
|
||||
compileOnly("com.github.MilkBowl:VaultAPI") { isTransitive = false }
|
||||
compileOnly(libs.vault) { isTransitive = false }
|
||||
compileOnly(libs.dummypermscompat) {
|
||||
exclude("com.github.MilkBowl", "VaultAPI")
|
||||
}
|
||||
@ -101,26 +101,26 @@ dependencies {
|
||||
compileOnly(libs.griefdefender) { isTransitive = false }
|
||||
compileOnly(libs.residence) { isTransitive = false }
|
||||
compileOnly(libs.towny) { isTransitive = false }
|
||||
compileOnly("com.intellectualsites.plotsquared:plotsquared-bukkit") { isTransitive = false }
|
||||
compileOnly("com.intellectualsites.plotsquared:plotsquared-core") { isTransitive = false }
|
||||
compileOnly(libs.plotSquaredBukkit) { isTransitive = false }
|
||||
compileOnly(libs.plotSquaredCore) { isTransitive = false }
|
||||
|
||||
// Third party
|
||||
implementation("io.papermc:paperlib")
|
||||
implementation("org.bstats:bstats-bukkit") { isTransitive = false }
|
||||
implementation(libs.paperlib)
|
||||
implementation(libs.bstatsBukkit) { isTransitive = false }
|
||||
implementation(libs.bstatsBase) { isTransitive = false }
|
||||
implementation("dev.notmyfault.serverlib:ServerLib")
|
||||
implementation("com.intellectualsites.paster:Paster") { isTransitive = false }
|
||||
implementation(libs.serverlib)
|
||||
implementation(libs.paster) { isTransitive = false }
|
||||
api(libs.lz4Java) { isTransitive = false }
|
||||
api(libs.sparsebitset) { isTransitive = false }
|
||||
api(libs.parallelgzip) { isTransitive = false }
|
||||
compileOnly("net.kyori:adventure-api")
|
||||
compileOnlyApi("org.checkerframework:checker-qual")
|
||||
compileOnly(libs.adventureApi)
|
||||
compileOnlyApi(libs.checkerqual)
|
||||
|
||||
// Tests
|
||||
testImplementation(libs.mockito)
|
||||
testImplementation("net.kyori:adventure-api")
|
||||
testImplementation("org.checkerframework:checker-qual")
|
||||
testImplementation("io.papermc.paper:paper-api") { isTransitive = true }
|
||||
testImplementation(libs.adventureApi)
|
||||
testImplementation(libs.checkerqual)
|
||||
testImplementation(libs.paper) { isTransitive = true }
|
||||
}
|
||||
|
||||
tasks.named<Copy>("processResources") {
|
||||
@ -174,7 +174,7 @@ tasks.named<ShadowJar>("shadowJar") {
|
||||
include(dependency("it.unimi.dsi:fastutil"))
|
||||
}
|
||||
relocate("org.incendo.serverlib", "com.fastasyncworldedit.serverlib") {
|
||||
include(dependency("dev.notmyfault.serverlib:ServerLib:2.3.1"))
|
||||
include(dependency("dev.notmyfault.serverlib:ServerLib:2.3.4"))
|
||||
}
|
||||
relocate("com.intellectualsites.paster", "com.fastasyncworldedit.paster") {
|
||||
include(dependency("com.intellectualsites.paster:Paster"))
|
||||
|
@ -22,14 +22,14 @@ import com.sk89q.worldedit.world.block.BaseBlock;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
||||
import it.unimi.dsi.fastutil.longs.LongList;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.generator.BiomeProvider;
|
||||
import org.bukkit.generator.BlockPopulator;
|
||||
import org.bukkit.generator.WorldInfo;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
@ -42,7 +42,6 @@ import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Represents an abstract regeneration handler.
|
||||
@ -62,7 +61,7 @@ public abstract class Regenerator<IChunkAccess, ProtoChunk extends IChunkAccess,
|
||||
protected final RegenOptions options;
|
||||
|
||||
//runtime
|
||||
protected final Map<ChunkStatus, Concurrency> chunkStati = new LinkedHashMap<>();
|
||||
protected final Map<ChunkStatus, Concurrency> chunkStatuses = new LinkedHashMap<>();
|
||||
private final Long2ObjectLinkedOpenHashMap<ProtoChunk> protoChunks = new Long2ObjectLinkedOpenHashMap<>();
|
||||
private final Long2ObjectOpenHashMap<Chunk> chunks = new Long2ObjectOpenHashMap<>();
|
||||
protected boolean generateConcurrent = true;
|
||||
@ -85,19 +84,19 @@ public abstract class Regenerator<IChunkAccess, ProtoChunk extends IChunkAccess,
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
private static Random getChunkRandom(long worldseed, int x, int z) {
|
||||
private static Random getChunkRandom(long worldSeed, int x, int z) {
|
||||
Random random = new Random();
|
||||
random.setSeed(worldseed);
|
||||
random.setSeed(worldSeed);
|
||||
long xRand = random.nextLong() / 2L * 2L + 1L;
|
||||
long zRand = random.nextLong() / 2L * 2L + 1L;
|
||||
random.setSeed((long) x * xRand + (long) z * zRand ^ worldseed);
|
||||
random.setSeed((long) x * xRand + (long) z * zRand ^ worldSeed);
|
||||
return random;
|
||||
}
|
||||
|
||||
/**
|
||||
* Regenerates the selected {@code Region}.
|
||||
*
|
||||
* @return whether or not the regeneration process was successful
|
||||
* @return whether the regeneration process was successful
|
||||
* @throws Exception when something goes terribly wrong
|
||||
*/
|
||||
public boolean regenerate() throws Exception {
|
||||
@ -175,8 +174,8 @@ public abstract class Regenerator<IChunkAccess, ProtoChunk extends IChunkAccess,
|
||||
//for now it is working well and fast, if we are bored in the future we could do the research (a lot of it) to reduce the border radius
|
||||
|
||||
//generate chunk coords lists with a certain radius
|
||||
Int2ObjectOpenHashMap<List<Long>> chunkCoordsForRadius = new Int2ObjectOpenHashMap<>();
|
||||
chunkStati.keySet().stream().map(ChunkStatusWrapper::requiredNeighborChunkRadius0).distinct().forEach(radius -> {
|
||||
Int2ObjectOpenHashMap<long[]> chunkCoordsForRadius = new Int2ObjectOpenHashMap<>();
|
||||
chunkStatuses.keySet().stream().mapToInt(ChunkStatusWrapper::requiredNeighborChunkRadius0).distinct().forEach(radius -> {
|
||||
if (radius == -1) { //ignore ChunkStatus.EMPTY
|
||||
return;
|
||||
}
|
||||
@ -186,19 +185,19 @@ public abstract class Regenerator<IChunkAccess, ProtoChunk extends IChunkAccess,
|
||||
});
|
||||
|
||||
//create chunks
|
||||
for (Long xz : chunkCoordsForRadius.get(0)) {
|
||||
for (long xz : chunkCoordsForRadius.get(0)) {
|
||||
ProtoChunk chunk = createProtoChunk(MathMan.unpairIntX(xz), MathMan.unpairIntY(xz));
|
||||
protoChunks.put(xz, chunk);
|
||||
}
|
||||
|
||||
//generate lists for RegionLimitedWorldAccess, need to be square with odd length (e.g. 17x17), 17 = 1 middle chunk + 8 border chunks * 2
|
||||
Int2ObjectOpenHashMap<Long2ObjectOpenHashMap<List<IChunkAccess>>> worldlimits = new Int2ObjectOpenHashMap<>();
|
||||
chunkStati.keySet().stream().map(ChunkStatusWrapper::requiredNeighborChunkRadius0).distinct().forEach(radius -> {
|
||||
Int2ObjectOpenHashMap<Long2ObjectOpenHashMap<List<IChunkAccess>>> worldLimits = new Int2ObjectOpenHashMap<>();
|
||||
chunkStatuses.keySet().stream().mapToInt(ChunkStatusWrapper::requiredNeighborChunkRadius0).distinct().forEach(radius -> {
|
||||
if (radius == -1) { //ignore ChunkStatus.EMPTY
|
||||
return;
|
||||
}
|
||||
Long2ObjectOpenHashMap<List<IChunkAccess>> map = new Long2ObjectOpenHashMap<>();
|
||||
for (Long xz : chunkCoordsForRadius.get(radius)) {
|
||||
for (long xz : chunkCoordsForRadius.get(radius)) {
|
||||
int x = MathMan.unpairIntX(xz);
|
||||
int z = MathMan.unpairIntY(xz);
|
||||
List<IChunkAccess> l = new ArrayList<>((radius + 1 + radius) * (radius + 1 + radius));
|
||||
@ -209,80 +208,63 @@ public abstract class Regenerator<IChunkAccess, ProtoChunk extends IChunkAccess,
|
||||
}
|
||||
map.put(xz, l);
|
||||
}
|
||||
worldlimits.put(radius, map);
|
||||
worldLimits.put(radius, map);
|
||||
});
|
||||
|
||||
//run generation tasks excluding FULL chunk status
|
||||
for (Map.Entry<ChunkStatus, Concurrency> entry : chunkStati.entrySet()) {
|
||||
for (Map.Entry<ChunkStatus, Concurrency> entry : chunkStatuses.entrySet()) {
|
||||
ChunkStatus chunkStatus = entry.getKey();
|
||||
int radius = chunkStatus.requiredNeighborChunkRadius0();
|
||||
|
||||
List<Long> coords = chunkCoordsForRadius.get(radius);
|
||||
long[] coords = chunkCoordsForRadius.get(radius);
|
||||
Long2ObjectOpenHashMap<List<IChunkAccess>> limitsForRadius = worldLimits.get(radius);
|
||||
if (this.generateConcurrent && entry.getValue() == Concurrency.RADIUS) {
|
||||
SequentialTasks<ConcurrentTasks<SequentialTasks<Long>>> tasks = getChunkStatusTaskRows(coords, radius);
|
||||
for (ConcurrentTasks<SequentialTasks<Long>> para : tasks) {
|
||||
SequentialTasks<ConcurrentTasks<LongList>> tasks = getChunkStatusTaskRows(coords, radius);
|
||||
for (ConcurrentTasks<LongList> para : tasks) {
|
||||
List<Runnable> scheduled = new ArrayList<>(tasks.size());
|
||||
for (SequentialTasks<Long> row : para) {
|
||||
for (LongList row : para) {
|
||||
scheduled.add(() -> {
|
||||
for (Long xz : row) {
|
||||
chunkStatus.processChunkSave(xz, worldlimits.get(radius).get(xz));
|
||||
for (long xz : row) {
|
||||
chunkStatus.processChunkSave(xz, limitsForRadius.get(xz));
|
||||
}
|
||||
});
|
||||
}
|
||||
try {
|
||||
List<Future<?>> futures = new ArrayList<>();
|
||||
scheduled.forEach(task -> futures.add(executor.submit(task)));
|
||||
for (Future<?> future : futures) {
|
||||
future.get();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
runAndWait(scheduled);
|
||||
}
|
||||
} else if (this.generateConcurrent && entry.getValue() == Concurrency.FULL) {
|
||||
// every chunk can be processed individually
|
||||
List<Runnable> scheduled = new ArrayList<>(coords.size());
|
||||
List<Runnable> scheduled = new ArrayList<>(coords.length);
|
||||
for (long xz : coords) {
|
||||
scheduled.add(() -> {
|
||||
chunkStatus.processChunkSave(xz, worldlimits.get(radius).get(xz));
|
||||
});
|
||||
}
|
||||
try {
|
||||
List<Future<?>> futures = new ArrayList<>();
|
||||
scheduled.forEach(task -> futures.add(executor.submit(task)));
|
||||
for (Future<?> future : futures) {
|
||||
future.get();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
scheduled.add(() -> chunkStatus.processChunkSave(xz, limitsForRadius.get(xz)));
|
||||
}
|
||||
runAndWait(scheduled);
|
||||
} else { // Concurrency.NONE or generateConcurrent == false
|
||||
// run sequential but submit to different thread
|
||||
// running regen on the main thread otherwise triggers async-only events on the main thread
|
||||
executor.submit(() -> {
|
||||
for (long xz : coords) {
|
||||
chunkStatus.processChunkSave(xz, worldlimits.get(radius).get(xz));
|
||||
chunkStatus.processChunkSave(xz, limitsForRadius.get(xz));
|
||||
}
|
||||
}).get(); // wait until finished this step
|
||||
}
|
||||
}
|
||||
|
||||
//convert to proper chunks
|
||||
for (Long xz : chunkCoordsForRadius.get(0)) {
|
||||
for (long xz : chunkCoordsForRadius.get(0)) {
|
||||
ProtoChunk proto = protoChunks.get(xz);
|
||||
chunks.put(xz, createChunk(proto));
|
||||
}
|
||||
|
||||
//final chunkstatus
|
||||
ChunkStatus FULL = getFullChunkStatus();
|
||||
for (Long xz : chunkCoordsForRadius.get(0)) { //FULL.requiredNeighbourChunkRadius() == 0!
|
||||
for (long xz : chunkCoordsForRadius.get(0)) { //FULL.requiredNeighbourChunkRadius() == 0!
|
||||
Chunk chunk = chunks.get(xz);
|
||||
FULL.processChunkSave(xz, Arrays.asList(chunk));
|
||||
FULL.processChunkSave(xz, List.of(chunk));
|
||||
}
|
||||
|
||||
//populate
|
||||
List<BlockPopulator> populators = getBlockPopulators();
|
||||
for (Long xz : chunkCoordsForRadius.get(0)) {
|
||||
for (long xz : chunkCoordsForRadius.get(0)) {
|
||||
int x = MathMan.unpairIntX(xz);
|
||||
int z = MathMan.unpairIntY(xz);
|
||||
|
||||
@ -302,6 +284,18 @@ public abstract class Regenerator<IChunkAccess, ProtoChunk extends IChunkAccess,
|
||||
return true;
|
||||
}
|
||||
|
||||
private void runAndWait(final List<Runnable> tasks) {
|
||||
try {
|
||||
List<Future<?>> futures = new ArrayList<>();
|
||||
tasks.forEach(task -> futures.add(executor.submit(task)));
|
||||
for (Future<?> future : futures) {
|
||||
future.get();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOGGER.catching(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void copyToWorld() {
|
||||
//Setting Blocks
|
||||
boolean genbiomes = options.shouldRegenBiomes();
|
||||
@ -437,7 +431,7 @@ public abstract class Regenerator<IChunkAccess, ProtoChunk extends IChunkAccess,
|
||||
protected abstract IChunkCache<IChunkGet> initSourceQueueCache();
|
||||
|
||||
//algorithms
|
||||
private List<Long> getChunkCoordsRegen(Region region, int border) { //needs to be square num of chunks
|
||||
private long[] getChunkCoordsRegen(Region region, int border) { //needs to be square num of chunks
|
||||
BlockVector3 oldMin = region.getMinimumPoint();
|
||||
BlockVector3 newMin = BlockVector3.at(
|
||||
(oldMin.getX() >> 4 << 4) - border * 16,
|
||||
@ -455,76 +449,79 @@ public abstract class Regenerator<IChunkAccess, ProtoChunk extends IChunkAccess,
|
||||
.sorted(Comparator
|
||||
.comparingInt(BlockVector2::getZ)
|
||||
.thenComparingInt(BlockVector2::getX)) //needed for RegionLimitedWorldAccess
|
||||
.map(c -> MathMan.pairInt(c.getX(), c.getZ()))
|
||||
.collect(Collectors.toList());
|
||||
.mapToLong(c -> MathMan.pairInt(c.getX(), c.getZ()))
|
||||
.toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a list of chunkcoord rows that may be executed concurrently
|
||||
*
|
||||
* @param allcoords the coords that should be sorted into rows, must be sorted by z and x
|
||||
* @param allCoords the coords that should be sorted into rows, must be sorted by z and x
|
||||
* @param requiredNeighborChunkRadius the radius of neighbor chunks that may not be written to concurrently (ChunkStatus
|
||||
* .requiredNeighborRadius)
|
||||
* @return a list of chunkcoords rows that may be executed concurrently
|
||||
*/
|
||||
private SequentialTasks<ConcurrentTasks<SequentialTasks<Long>>> getChunkStatusTaskRows(
|
||||
List<Long> allcoords,
|
||||
private SequentialTasks<ConcurrentTasks<LongList>> getChunkStatusTaskRows(
|
||||
long[] allCoords,
|
||||
int requiredNeighborChunkRadius
|
||||
) {
|
||||
int requiredneighbors = Math.max(0, requiredNeighborChunkRadius);
|
||||
int requiredNeighbors = Math.max(0, requiredNeighborChunkRadius);
|
||||
|
||||
int minx = allcoords.isEmpty() ? 0 : MathMan.unpairIntX(allcoords.get(0));
|
||||
int maxx = allcoords.isEmpty() ? 0 : MathMan.unpairIntX(allcoords.get(allcoords.size() - 1));
|
||||
int minz = allcoords.isEmpty() ? 0 : MathMan.unpairIntY(allcoords.get(0));
|
||||
int maxz = allcoords.isEmpty() ? 0 : MathMan.unpairIntY(allcoords.get(allcoords.size() - 1));
|
||||
SequentialTasks<ConcurrentTasks<SequentialTasks<Long>>> tasks;
|
||||
if (maxz - minz > maxx - minx) {
|
||||
int numlists = Math.min(requiredneighbors * 2 + 1, maxx - minx + 1);
|
||||
final int coordsCount = allCoords.length;
|
||||
long first = coordsCount == 0 ? 0 : allCoords[0];
|
||||
long last = coordsCount == 0 ? 0 : allCoords[coordsCount - 1];
|
||||
int minX = MathMan.unpairIntX(first);
|
||||
int maxX = MathMan.unpairIntX(last);
|
||||
int minZ = MathMan.unpairIntY(first);
|
||||
int maxZ = MathMan.unpairIntY(last);
|
||||
SequentialTasks<ConcurrentTasks<LongList>> tasks;
|
||||
if (maxZ - minZ > maxX - minX) {
|
||||
int numlists = Math.min(requiredNeighbors * 2 + 1, maxX - minX + 1);
|
||||
|
||||
Int2ObjectOpenHashMap<SequentialTasks<Long>> byx = new Int2ObjectOpenHashMap();
|
||||
int expectedListLength = (allcoords.size() + 1) / (maxx - minx);
|
||||
Int2ObjectOpenHashMap<LongList> byX = new Int2ObjectOpenHashMap<>();
|
||||
int expectedListLength = (coordsCount + 1) / (maxX - minX);
|
||||
|
||||
//init lists
|
||||
for (int i = minx; i <= maxx; i++) {
|
||||
byx.put(i, new SequentialTasks(expectedListLength));
|
||||
for (int i = minX; i <= maxX; i++) {
|
||||
byX.put(i, new LongArrayList(expectedListLength));
|
||||
}
|
||||
|
||||
//sort into lists by x coord
|
||||
for (Long xz : allcoords) {
|
||||
byx.get(MathMan.unpairIntX(xz)).add(xz);
|
||||
for (long allCoord : allCoords) {
|
||||
byX.get(MathMan.unpairIntX(allCoord)).add(allCoord);
|
||||
}
|
||||
|
||||
//create parallel tasks
|
||||
tasks = new SequentialTasks(numlists);
|
||||
tasks = new SequentialTasks<>(numlists);
|
||||
for (int offset = 0; offset < numlists; offset++) {
|
||||
ConcurrentTasks<SequentialTasks<Long>> para = new ConcurrentTasks((maxz - minz + 1) / numlists + 1);
|
||||
for (int i = 0; minx + i * numlists + offset <= maxx; i++) {
|
||||
para.add(byx.get(minx + i * numlists + offset));
|
||||
ConcurrentTasks<LongList> para = new ConcurrentTasks<>((maxZ - minZ + 1) / numlists + 1);
|
||||
for (int i = 0; minX + i * numlists + offset <= maxX; i++) {
|
||||
para.add(byX.get(minX + i * numlists + offset));
|
||||
}
|
||||
tasks.add(para);
|
||||
}
|
||||
} else {
|
||||
int numlists = Math.min(requiredneighbors * 2 + 1, maxz - minz + 1);
|
||||
int numlists = Math.min(requiredNeighbors * 2 + 1, maxZ - minZ + 1);
|
||||
|
||||
Int2ObjectOpenHashMap<SequentialTasks<Long>> byz = new Int2ObjectOpenHashMap();
|
||||
int expectedListLength = (allcoords.size() + 1) / (maxz - minz);
|
||||
Int2ObjectOpenHashMap<LongList> byZ = new Int2ObjectOpenHashMap<>();
|
||||
int expectedListLength = (coordsCount + 1) / (maxZ - minZ);
|
||||
|
||||
//init lists
|
||||
for (int i = minz; i <= maxz; i++) {
|
||||
byz.put(i, new SequentialTasks(expectedListLength));
|
||||
for (int i = minZ; i <= maxZ; i++) {
|
||||
byZ.put(i, new LongArrayList(expectedListLength));
|
||||
}
|
||||
|
||||
//sort into lists by x coord
|
||||
for (Long xz : allcoords) {
|
||||
byz.get(MathMan.unpairIntY(xz)).add(xz);
|
||||
for (long allCoord : allCoords) {
|
||||
byZ.get(MathMan.unpairIntY(allCoord)).add(allCoord);
|
||||
}
|
||||
|
||||
//create parallel tasks
|
||||
tasks = new SequentialTasks(numlists);
|
||||
tasks = new SequentialTasks<>(numlists);
|
||||
for (int offset = 0; offset < numlists; offset++) {
|
||||
ConcurrentTasks<SequentialTasks<Long>> para = new ConcurrentTasks((maxx - minx + 1) / numlists + 1);
|
||||
for (int i = 0; minz + i * numlists + offset <= maxz; i++) {
|
||||
para.add(byz.get(minz + i * numlists + offset));
|
||||
ConcurrentTasks<LongList> para = new ConcurrentTasks<>((maxX - minX + 1) / numlists + 1);
|
||||
for (int i = 0; minZ + i * numlists + offset <= maxZ; i++) {
|
||||
para.add(byZ.get(minZ + i * numlists + offset));
|
||||
}
|
||||
tasks.add(para);
|
||||
}
|
||||
@ -576,15 +573,14 @@ public abstract class Regenerator<IChunkAccess, ProtoChunk extends IChunkAccess,
|
||||
/**
|
||||
* Return the name of the wrapped {@code ChunkStatus}.
|
||||
*
|
||||
* @param xz represents the chunk coordinates of the chunk to process as denoted by {@code MathMan}
|
||||
* @param accessibleChunks a list of chunks that will be used during the execution of the wrapped {@code ChunkStatus}.
|
||||
* This list is order in the correct order required by the {@code ChunkStatus}, unless Mojang suddenly decides to do things differently.
|
||||
*/
|
||||
public abstract CompletableFuture<?> processChunk(Long xz, List<IChunkAccess> accessibleChunks);
|
||||
public abstract CompletableFuture<?> processChunk(List<IChunkAccess> accessibleChunks);
|
||||
|
||||
void processChunkSave(Long xz, List<IChunkAccess> accessibleChunks) {
|
||||
void processChunkSave(long xz, List<IChunkAccess> accessibleChunks) {
|
||||
try {
|
||||
processChunk(xz, accessibleChunks).get();
|
||||
processChunk(accessibleChunks).get();
|
||||
} catch (Exception e) {
|
||||
LOGGER.error(
|
||||
"Error while running " + name() + " on chunk " + MathMan.unpairIntX(xz) + "/" + MathMan.unpairIntY(xz),
|
||||
@ -597,16 +593,16 @@ public abstract class Regenerator<IChunkAccess, ProtoChunk extends IChunkAccess,
|
||||
|
||||
public static class SequentialTasks<T> extends Tasks<T> {
|
||||
|
||||
public SequentialTasks(int expectedsize) {
|
||||
super(expectedsize);
|
||||
public SequentialTasks(int expectedSize) {
|
||||
super(expectedSize);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class ConcurrentTasks<T> extends Tasks<T> {
|
||||
|
||||
public ConcurrentTasks(int expectedsize) {
|
||||
super(expectedsize);
|
||||
public ConcurrentTasks(int expectedSize) {
|
||||
super(expectedSize);
|
||||
}
|
||||
|
||||
}
|
||||
@ -615,8 +611,8 @@ public abstract class Regenerator<IChunkAccess, ProtoChunk extends IChunkAccess,
|
||||
|
||||
private final List<T> tasks;
|
||||
|
||||
public Tasks(int expectedsize) {
|
||||
tasks = new ArrayList(expectedsize);
|
||||
public Tasks(int expectedSize) {
|
||||
tasks = new ArrayList<>(expectedSize);
|
||||
}
|
||||
|
||||
public void add(T task) {
|
||||
|
@ -25,7 +25,6 @@ import com.sk89q.worldedit.util.YAMLConfiguration;
|
||||
import com.sk89q.worldedit.util.report.Unreported;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
|
||||
/**
|
||||
|
@ -42,6 +42,7 @@ import com.sk89q.worldedit.util.SideEffect;
|
||||
import com.sk89q.worldedit.util.lifecycle.Lifecycled;
|
||||
import com.sk89q.worldedit.world.DataFixer;
|
||||
import com.sk89q.worldedit.world.registry.Registries;
|
||||
import io.papermc.lib.PaperLib;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Server;
|
||||
@ -258,6 +259,14 @@ public class BukkitServerInterface extends AbstractPlatform implements MultiUser
|
||||
return SUPPORTED_SIDE_EFFECTS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTickCount() {
|
||||
if (PaperLib.isPaper()) {
|
||||
return Bukkit.getCurrentTick();
|
||||
}
|
||||
return super.getTickCount();
|
||||
}
|
||||
|
||||
public void unregisterCommands() {
|
||||
dynamicCommands.unregisterCommands();
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ import com.sk89q.worldedit.WorldEdit;
|
||||
import com.sk89q.worldedit.entity.Player;
|
||||
import com.sk89q.worldedit.event.platform.SessionIdleEvent;
|
||||
import com.sk89q.worldedit.extension.platform.Actor;
|
||||
import com.sk89q.worldedit.internal.event.InteractionDebouncer;
|
||||
import com.sk89q.worldedit.util.Direction;
|
||||
import com.sk89q.worldedit.util.Location;
|
||||
import com.sk89q.worldedit.world.World;
|
||||
@ -55,6 +56,7 @@ import java.util.Optional;
|
||||
public class WorldEditListener implements Listener {
|
||||
|
||||
private final WorldEditPlugin plugin;
|
||||
private final InteractionDebouncer debouncer;
|
||||
|
||||
/**
|
||||
* Construct the object.
|
||||
@ -63,6 +65,7 @@ public class WorldEditListener implements Listener {
|
||||
*/
|
||||
public WorldEditListener(WorldEditPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
debouncer = new InteractionDebouncer(plugin.getInternalPlatform());
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
@ -128,62 +131,58 @@ public class WorldEditListener implements Listener {
|
||||
*/
|
||||
@EventHandler
|
||||
public void onPlayerInteract(PlayerInteractEvent event) {
|
||||
if (!plugin.getInternalPlatform().isHookingEvents()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.useItemInHand() == Result.DENY) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.getHand() == EquipmentSlot.OFF_HAND) {
|
||||
if (!plugin.getInternalPlatform().isHookingEvents()
|
||||
|| event.useItemInHand() == Result.DENY
|
||||
|| event.getHand() == EquipmentSlot.OFF_HAND
|
||||
|| event.getAction() == Action.PHYSICAL) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Player player = plugin.wrapPlayer(event.getPlayer());
|
||||
|
||||
if (event.getAction() != Action.LEFT_CLICK_BLOCK) {
|
||||
Optional<Boolean> previousResult = debouncer.getDuplicateInteractionResult(player);
|
||||
if (previousResult.isPresent()) {
|
||||
if (previousResult.get()) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
final World world = player.getWorld();
|
||||
final WorldEdit we = plugin.getWorldEdit();
|
||||
final Direction direction = BukkitAdapter.adapt(event.getBlockFace());
|
||||
final Block clickedBlock = event.getClickedBlock();
|
||||
final Location pos = clickedBlock == null ? null : new Location(world, clickedBlock.getX(), clickedBlock.getY(), clickedBlock.getZ());
|
||||
|
||||
Action action = event.getAction();
|
||||
if (action == Action.LEFT_CLICK_BLOCK) {
|
||||
final Block clickedBlock = event.getClickedBlock();
|
||||
final Location pos = new Location(world, clickedBlock.getX(), clickedBlock.getY(), clickedBlock.getZ());
|
||||
|
||||
if (we.handleBlockLeftClick(player, pos, direction)) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
|
||||
if (we.handleArmSwing(player)) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
|
||||
} else if (action == Action.LEFT_CLICK_AIR) {
|
||||
|
||||
if (we.handleArmSwing(player)) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
|
||||
} else if (action == Action.RIGHT_CLICK_BLOCK) {
|
||||
final Block clickedBlock = event.getClickedBlock();
|
||||
final Location pos = new Location(world, clickedBlock.getX(), clickedBlock.getY(), clickedBlock.getZ());
|
||||
|
||||
if (we.handleBlockRightClick(player, pos, direction)) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
|
||||
if (we.handleRightClick(player)) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
} else if (action == Action.RIGHT_CLICK_AIR) {
|
||||
if (we.handleRightClick(player)) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
boolean result = false;
|
||||
switch (event.getAction()) {
|
||||
case LEFT_CLICK_BLOCK:
|
||||
result = we.handleBlockLeftClick(player, pos, direction) || we.handleArmSwing(player);
|
||||
break;
|
||||
case LEFT_CLICK_AIR:
|
||||
result = we.handleArmSwing(player);
|
||||
break;
|
||||
case RIGHT_CLICK_BLOCK:
|
||||
result = we.handleBlockRightClick(player, pos, direction) || we.handleRightClick(player);
|
||||
break;
|
||||
case RIGHT_CLICK_AIR:
|
||||
result = we.handleRightClick(player);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
debouncer.setLastInteraction(player, result);
|
||||
if (result) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerQuit(PlayerQuitEvent event) {
|
||||
debouncer.clearInteraction(plugin.wrapPlayer(event.getPlayer()));
|
||||
|
||||
plugin.getWorldEdit().getEventBus().post(new SessionIdleEvent(new BukkitPlayer.SessionKeyImpl(event.getPlayer())));
|
||||
}
|
||||
|
||||
|
@ -36,6 +36,8 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
@ -142,6 +144,11 @@ public class TestOfflinePermissible implements OfflinePlayer, Permissible {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConnected() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Tester";
|
||||
@ -161,6 +168,24 @@ public class TestOfflinePermissible implements OfflinePlayer, Permissible {
|
||||
throw new UnsupportedOperationException("Not supported yet.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public <E extends BanEntry<? super PlayerProfile>> @Nullable E ban(
|
||||
@Nullable final String reason,
|
||||
@Nullable final Instant expires,
|
||||
@Nullable final String source
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <E extends BanEntry<? super PlayerProfile>> @Nullable E ban(
|
||||
@Nullable final String reason,
|
||||
@Nullable final Duration duration,
|
||||
@Nullable final String source
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable BanEntry<org.bukkit.profile.PlayerProfile> ban(
|
||||
@Nullable final String reason,
|
||||
|
Reference in New Issue
Block a user