Telesphoreo 2023-09-27 15:25:07 -05:00
commit 1a6b10c10f
36 changed files with 387 additions and 319 deletions

View File

@ -20,7 +20,14 @@
"net.fabricmc.fabric-api:fabric-api", "net.fabricmc.fabric-api:fabric-api",
"com.github.luben:zstd-jni", "com.github.luben:zstd-jni",
"org.jetbrains.kotlin.jvm", "org.jetbrains.kotlin.jvm",
"log4j" "log4j",
"org.apache.logging.log4j:log4j-api",
"org.apache.logging.log4j:log4j-bom",
"org.apache.logging.log4j:log4j-slf4j-impl",
"org.apache.logging.log4j:log4j-core",
"org.bstats:bstats-sponge",
"org.spongepowered:spongeapi",
"org.yaml:snakeyaml"
], ],
"labels": ["Renovate"], "labels": ["Renovate"],
"rebaseWhen": "conflicted", "rebaseWhen": "conflicted",

View File

@ -34,7 +34,7 @@ logger.lifecycle("""
******************************************* *******************************************
""") """)
var rootVersion by extra("2.7.1") var rootVersion by extra("2.7.2")
var snapshot by extra("SNAPSHOT") var snapshot by extra("SNAPSHOT")
var revision: String by extra("") var revision: String by extra("")
var buildNumber by extra("") var buildNumber by extra("")

View File

@ -15,7 +15,6 @@ fun Project.applyPaperweightAdapterConfiguration() {
dependencies { dependencies {
"implementation"(project(":worldedit-bukkit")) "implementation"(project(":worldedit-bukkit"))
"implementation"(platform("com.intellectualsites.bom:bom-newest:1.33"))
} }
tasks.named("assemble") { tasks.named("assemble") {

View File

@ -40,12 +40,11 @@ fun Project.applyCommonJavaConfiguration(sourcesJar: Boolean, banSlf4j: Boolean
dependencies { dependencies {
"compileOnly"("com.google.code.findbugs:jsr305:3.0.2") "compileOnly"("com.google.code.findbugs:jsr305:3.0.2")
"testImplementation"("org.junit.jupiter:junit-jupiter-api:5.9.2") "testImplementation"("org.junit.jupiter:junit-jupiter-api:5.10.0")
"testImplementation"("org.junit.jupiter:junit-jupiter-params:5.9.2") "testImplementation"("org.junit.jupiter:junit-jupiter-params:5.10.0")
"testImplementation"("org.mockito:mockito-core:5.1.1") "testImplementation"("org.mockito:mockito-core:5.4.0")
"testImplementation"("org.mockito:mockito-junit-jupiter:5.1.1") "testImplementation"("org.mockito:mockito-junit-jupiter:5.4.0")
"testRuntimeOnly"("org.junit.jupiter:junit-jupiter-engine:5.9.2") "testRuntimeOnly"("org.junit.jupiter:junit-jupiter-engine:5.10.0")
"implementation"(platform("com.intellectualsites.bom:bom-newest:1.33"))
} }
// Java 8 turns on doclint which we fail // Java 8 turns on doclint which we fail

View File

@ -1,8 +1,11 @@
[versions] [versions]
# Minecraft expectations # Minecraft expectations
paper = "1.20.1-R0.1-SNAPSHOT"
fastutil = "8.5.9" fastutil = "8.5.9"
guava = "31.1-jre" guava = "31.1-jre"
log4j = "2.19.0" log4j = "2.19.0"
gson = "2.10"
snakeyaml = "2.0"
# Plugins # Plugins
dummypermscompat = "1.10" dummypermscompat = "1.10"
@ -11,13 +14,16 @@ mapmanager = "1.8.0-SNAPSHOT"
griefprevention = "16.18.1" griefprevention = "16.18.1"
griefdefender = "2.1.0-SNAPSHOT" griefdefender = "2.1.0-SNAPSHOT"
residence = "4.5._13.1" residence = "4.5._13.1"
towny = "0.99.5.7" towny = "0.99.5.16"
plotsquared = "7.0.0"
# Third party # Third party
bstats = "3.0.2" bstats = "3.0.2"
sparsebitset = "1.2" sparsebitset = "1.2"
parallelgzip = "1.0.5" parallelgzip = "1.0.5"
adventure = "4.14.0" adventure = "4.14.0"
adventure-bukkit = "4.3.0"
checkerqual = "3.38.0"
truezip = "6.8.4" truezip = "6.8.4"
auto-value = "1.10.2" auto-value = "1.10.2"
findbugs = "3.0.2" findbugs = "3.0.2"
@ -29,22 +35,32 @@ jlibnoise = "1.0.0"
jchronic = "0.2.4a" jchronic = "0.2.4a"
lz4-java = "1.8.0" lz4-java = "1.8.0"
lz4-stream = "1.0.0" lz4-stream = "1.0.0"
commons-cli = "1.5.0"
paperlib = "1.0.8"
paster = "1.1.5"
vault = "1.7.1"
serverlib = "2.3.4"
## Internal ## Internal
text-adapter = "3.0.6" text-adapter = "3.0.6"
text = "3.0.4" text = "3.0.4"
piston = "0.5.7" piston = "0.5.7"
# Tests # Tests
mockito = "5.4.0" mockito = "5.5.0"
# Gradle plugins # Gradle plugins
pluginyml = "0.6.0" pluginyml = "0.6.0"
minotaur = "2.8.4"
[libraries] [libraries]
# Minecraft expectations # Minecraft expectations
paper = { group = "io.papermc.paper", name = "paper-api", version.ref = "paper" }
fastutil = { group = "it.unimi.dsi", name = "fastutil", version.ref = "fastutil" } fastutil = { group = "it.unimi.dsi", name = "fastutil", version.ref = "fastutil" }
log4jBom = { group = "org.apache.logging.log4j", name = "log4j-bom", version.ref = "log4j" } log4jBom = { group = "org.apache.logging.log4j", name = "log4j-bom", version.ref = "log4j" }
log4jApi = { group = "org.apache.logging.log4j", name = "log4j-api", version.ref = "log4j" }
guava = { group = "com.google.guava", name = "guava", version.ref = "guava" } guava = { group = "com.google.guava", name = "guava", version.ref = "guava" }
gson = { group = "com.google.code.gson", name = "gson", version.ref = "gson" }
snakeyaml = { group = "org.yaml", name = "snakeyaml", version.ref = "snakeyaml" }
# Plugins # Plugins
dummypermscompat = { group = "com.sk89q", name = "dummypermscompat", version.ref = "dummypermscompat" } dummypermscompat = { group = "com.sk89q", name = "dummypermscompat", version.ref = "dummypermscompat" }
@ -54,9 +70,12 @@ griefprevention = { group = "com.github.TechFortress", name = "GriefPrevention",
griefdefender = { group = "com.griefdefender", name = "api", version.ref = "griefdefender" } griefdefender = { group = "com.griefdefender", name = "api", version.ref = "griefdefender" }
residence = { group = "com.bekvon.bukkit.residence", name = "Residence", version.ref = "residence" } residence = { group = "com.bekvon.bukkit.residence", name = "Residence", version.ref = "residence" }
towny = { group = "com.palmergames.bukkit.towny", name = "towny", version.ref = "towny" } towny = { group = "com.palmergames.bukkit.towny", name = "towny", version.ref = "towny" }
plotSquaredCore = { group = "com.intellectualsites.plotsquared", name = "plotsquared-core", version.ref = "plotsquared" }
plotSquaredBukkit = { group = "com.intellectualsites.plotsquared", name = "plotsquared-bukkit", version.ref = "plotsquared" }
# Third Party # Third Party
bstatsBase = { group = "org.bstats", name = "bstats-base", version.ref = "bstats" } bstatsBase = { group = "org.bstats", name = "bstats-base", version.ref = "bstats" }
bstatsBukkit = { group = "org.bstats", name = "bstats-bukkit", version.ref = "bstats" }
sparsebitset = { group = "com.zaxxer", name = "SparseBitSet", version.ref = "sparsebitset" } sparsebitset = { group = "com.zaxxer", name = "SparseBitSet", version.ref = "sparsebitset" }
parallelgzip = { group = "org.anarres", name = "parallelgzip", version.ref = "parallelgzip" } parallelgzip = { group = "org.anarres", name = "parallelgzip", version.ref = "parallelgzip" }
adventureNbt = { group = "net.kyori", name = "adventure-nbt", version.ref = "adventure" } adventureNbt = { group = "net.kyori", name = "adventure-nbt", version.ref = "adventure" }
@ -73,6 +92,15 @@ jlibnoise = { group = "com.sk89q.lib", name = "jlibnoise", version.ref = "jlibno
jchronic = { group = "com.sk89q", name = "jchronic", version.ref = "jchronic" } jchronic = { group = "com.sk89q", name = "jchronic", version.ref = "jchronic" }
lz4Java = { group = "org.lz4", name = "lz4-java", version.ref = "lz4-java" } lz4Java = { group = "org.lz4", name = "lz4-java", version.ref = "lz4-java" }
lz4JavaStream = { group = "net.jpountz", name = "lz4-java-stream", version.ref = "lz4-stream" } lz4JavaStream = { group = "net.jpountz", name = "lz4-java-stream", version.ref = "lz4-stream" }
commonsCli = { group = "commons-cli", name = "commons-cli", version.ref = "commons-cli" }
paperlib = { group = "io.papermc", name = "paperlib", version.ref = "paperlib" }
adventureApi = { group = "net.kyori", name = "adventure-api", version.ref = "adventure" }
adventureMiniMessage = { group = "net.kyori", name = "adventure-text-minimessage", version.ref = "adventure" }
adventureBukkit = { group = "net.kyori", name = "adventure-platform-bukkit", version.ref = "adventure-bukkit" }
paster = { group = "com.intellectualsites.paster", name = "Paster", version.ref = "paster" }
vault = { group = "com.github.MilkBowl", name = "VaultAPI", version.ref = "vault" }
serverlib = { group = "dev.notmyfault.serverlib", name = "ServerLib", version.ref = "serverlib" }
checkerqual = { group = "org.checkerframework", name = "checker-qual", version.ref = "checkerqual" }
# Internal # Internal
## Text ## Text
@ -94,3 +122,4 @@ log4jCore = { group = "org.apache.logging.log4j", name = "log4j-core", version.r
[plugins] [plugins]
pluginyml = { id = "net.minecrell.plugin-yml.bukkit", version.ref = "pluginyml" } pluginyml = { id = "net.minecrell.plugin-yml.bukkit", version.ref = "pluginyml" }
minotaur = { id = "com.modrinth.minotaur", version.ref = "minotaur" }

Binary file not shown.

View File

@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip
networkTimeout=10000 networkTimeout=10000
validateDistributionUrl=true validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME

3
gradlew vendored
View File

@ -83,7 +83,8 @@ done
# This is normally unused # This is normally unused
# shellcheck disable=SC2034 # shellcheck disable=SC2034
APP_BASE_NAME=${0##*/} APP_BASE_NAME=${0##*/}
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value. # Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum MAX_FD=maximum

View File

@ -22,5 +22,5 @@ configurations.all {
dependencies { dependencies {
the<PaperweightUserDependenciesExtension>().paperDevBundle("1.17.1-R0.1-20220414.034903-210") the<PaperweightUserDependenciesExtension>().paperDevBundle("1.17.1-R0.1-20220414.034903-210")
compileOnly("io.papermc:paperlib") compileOnly(libs.paperlib)
} }

View File

@ -85,11 +85,7 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
private static final MethodHandle methodGetVisibleChunk; 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 Field fieldLock;
private static final long fieldLockOffset;
private static final Field fieldGameEventDispatcherSections; private static final Field fieldGameEventDispatcherSections;
private static final MethodHandle methodremoveBlockEntityTicker; private static final MethodHandle methodremoveBlockEntityTicker;
@ -127,15 +123,12 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
getVisibleChunkIfPresent.setAccessible(true); getVisibleChunkIfPresent.setAccessible(true);
methodGetVisibleChunk = MethodHandles.lookup().unreflect(getVisibleChunkIfPresent); methodGetVisibleChunk = MethodHandles.lookup().unreflect(getVisibleChunkIfPresent);
Unsafe unsafe = ReflectionUtils.getUnsafe();
if (!PaperLib.isPaper()) { if (!PaperLib.isPaper()) {
fieldLock = PalettedContainer.class.getDeclaredField(Refraction.pickName("lock", "m")); fieldLock = PalettedContainer.class.getDeclaredField(Refraction.pickName("lock", "m"));
fieldLockOffset = unsafe.objectFieldOffset(fieldLock); fieldLock.setAccessible(true);
} else { } else {
// in paper, the used methods are synchronized properly // in paper, the used methods are synchronized properly
fieldLock = null; fieldLock = null;
fieldLockOffset = -1;
} }
fieldGameEventDispatcherSections = LevelChunk.class.getDeclaredField(Refraction.pickName( 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 = BlockEntity.class.getDeclaredField(Refraction.pickName("remove", "p"));
fieldRemove.setAccessible(true); 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) { } catch (RuntimeException e) {
throw e; throw e;
} catch (Throwable rethrow) { } catch (Throwable rethrow) {
@ -173,9 +159,8 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
LevelChunkSection value, LevelChunkSection value,
int layer int layer
) { ) {
long offset = ((long) layer << CHUNKSECTION_SHIFT) + CHUNKSECTION_BASE;
if (layer >= 0 && layer < sections.length) { if (layer >= 0 && layer < sections.length) {
return ReflectionUtils.getUnsafe().compareAndSwapObject(sections, offset, expected, value); return ReflectionUtils.compareAndSet(sections, expected, value, layer);
} }
return false; return false;
} }
@ -190,14 +175,13 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
} }
try { try {
synchronized (section) { synchronized (section) {
Unsafe unsafe = ReflectionUtils.getUnsafe();
PalettedContainer<net.minecraft.world.level.block.state.BlockState> blocks = section.getStates(); 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) { if (currentLock instanceof DelegateSemaphore delegateSemaphore) {
return delegateSemaphore; return delegateSemaphore;
} }
DelegateSemaphore newLock = new DelegateSemaphore(1, currentLock); DelegateSemaphore newLock = new DelegateSemaphore(1, currentLock);
unsafe.putObject(blocks, fieldLockOffset, newLock); fieldLock.set(blocks, newLock);
return newLock; return newLock;
} }
} catch (Throwable e) { } catch (Throwable e) {

View File

@ -184,9 +184,6 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
protected boolean prepare() { protected boolean prepare() {
this.originalServerWorld = ((CraftWorld) originalBukkitWorld).getHandle(); this.originalServerWorld = ((CraftWorld) originalBukkitWorld).getHandle();
originalChunkProvider = originalServerWorld.getChunkSource(); originalChunkProvider = originalServerWorld.getChunkSource();
if (!(originalChunkProvider instanceof ServerChunkCache)) {
return false;
}
//flat bedrock? (only on paper) //flat bedrock? (only on paper)
if (paperConfigField != null) { if (paperConfigField != null) {
@ -197,7 +194,7 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
} }
seed = options.getSeed().orElse(originalServerWorld.getSeed()); 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; 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 //let's start then
structureManager = server.getStructureManager(); structureManager = server.getStructureManager();
threadedLevelLightEngine = freshChunkProvider.getLightEngine(); threadedLevelLightEngine = freshChunkProvider.getLightEngine();
@ -680,7 +677,7 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
} }
@Override @Override
public CompletableFuture<?> processChunk(Long xz, List<ChunkAccess> accessibleChunks) { public CompletableFuture<?> processChunk(List<ChunkAccess> accessibleChunks) {
return chunkStatus.generate( return chunkStatus.generate(
Runnable::run, // TODO revisit, we might profit from this somehow? Runnable::run, // TODO revisit, we might profit from this somehow?
freshWorld, freshWorld,

View File

@ -13,5 +13,5 @@ repositories {
dependencies { dependencies {
// https://papermc.io/repo/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/ // 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") the<PaperweightUserDependenciesExtension>().paperDevBundle("1.18.2-R0.1-20220920.010157-167")
compileOnly("io.papermc:paperlib") compileOnly(libs.paperlib)
} }

View File

@ -92,14 +92,8 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
private static final MethodHandle methodGetVisibleChunk; 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 Field fieldThreadingDetector;
private static final long fieldThreadingDetectorOffset;
private static final Field fieldLock; private static final Field fieldLock;
private static final long fieldLockOffset;
private static final MethodHandle methodRemoveGameEventListener; private static final MethodHandle methodRemoveGameEventListener;
private static final MethodHandle methodremoveTickingBlockEntity; private static final MethodHandle methodremoveTickingBlockEntity;
@ -136,20 +130,15 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
getVisibleChunkIfPresent.setAccessible(true); getVisibleChunkIfPresent.setAccessible(true);
methodGetVisibleChunk = MethodHandles.lookup().unreflect(getVisibleChunkIfPresent); methodGetVisibleChunk = MethodHandles.lookup().unreflect(getVisibleChunkIfPresent);
Unsafe unsafe = ReflectionUtils.getUnsafe();
if (!PaperLib.isPaper()) { if (!PaperLib.isPaper()) {
fieldThreadingDetector = PalettedContainer.class.getDeclaredField(Refraction.pickName("threadingDetector", "f")); fieldThreadingDetector = PalettedContainer.class.getDeclaredField(Refraction.pickName("threadingDetector", "f"));
fieldThreadingDetectorOffset = unsafe.objectFieldOffset(fieldThreadingDetector); fieldThreadingDetector.setAccessible(true);
fieldLock = ThreadingDetector.class.getDeclaredField(Refraction.pickName("lock", "c")); fieldLock = ThreadingDetector.class.getDeclaredField(Refraction.pickName("lock", "c"));
fieldLockOffset = unsafe.objectFieldOffset(fieldLock); fieldLock.setAccessible(true);
} else { } else {
// in paper, the used methods are synchronized properly // in paper, the used methods are synchronized properly
fieldThreadingDetector = null; fieldThreadingDetector = null;
fieldThreadingDetectorOffset = -1;
fieldLock = null; fieldLock = null;
fieldLockOffset = -1;
} }
Method removeGameEventListener = LevelChunk.class.getDeclaredMethod( Method removeGameEventListener = LevelChunk.class.getDeclaredMethod(
@ -169,13 +158,6 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
fieldRemove = BlockEntity.class.getDeclaredField(Refraction.pickName("remove", "p")); fieldRemove = BlockEntity.class.getDeclaredField(Refraction.pickName("remove", "p"));
fieldRemove.setAccessible(true); 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) { } catch (RuntimeException e) {
throw e; throw e;
} catch (Throwable rethrow) { } catch (Throwable rethrow) {
@ -190,9 +172,8 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
LevelChunkSection value, LevelChunkSection value,
int layer int layer
) { ) {
long offset = ((long) layer << CHUNKSECTION_SHIFT) + CHUNKSECTION_BASE;
if (layer >= 0 && layer < sections.length) { if (layer >= 0 && layer < sections.length) {
return ReflectionUtils.getUnsafe().compareAndSwapObject(sections, offset, expected, value); return ReflectionUtils.compareAndSet(sections, expected, value, layer);
} }
return false; return false;
} }
@ -207,19 +188,15 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
} }
try { try {
synchronized (section) { synchronized (section) {
Unsafe unsafe = ReflectionUtils.getUnsafe();
PalettedContainer<net.minecraft.world.level.block.state.BlockState> blocks = section.getStates(); PalettedContainer<net.minecraft.world.level.block.state.BlockState> blocks = section.getStates();
ThreadingDetector currentThreadingDetector = (ThreadingDetector) unsafe.getObject( ThreadingDetector currentThreadingDetector = (ThreadingDetector) fieldThreadingDetector.get(blocks);
blocks,
fieldThreadingDetectorOffset
);
synchronized (currentThreadingDetector) { synchronized (currentThreadingDetector) {
Semaphore currentLock = (Semaphore) unsafe.getObject(currentThreadingDetector, fieldLockOffset); Semaphore currentLock = (Semaphore) fieldLock.get(currentThreadingDetector);
if (currentLock instanceof DelegateSemaphore delegateSemaphore) { if (currentLock instanceof DelegateSemaphore delegateSemaphore) {
return delegateSemaphore; return delegateSemaphore;
} }
DelegateSemaphore newLock = new DelegateSemaphore(1, currentLock); DelegateSemaphore newLock = new DelegateSemaphore(1, currentLock);
unsafe.putObject(currentThreadingDetector, fieldLockOffset, newLock); fieldLock.set(currentThreadingDetector, newLock);
return newLock; return newLock;
} }
} }

View File

@ -178,9 +178,6 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
protected boolean prepare() { protected boolean prepare() {
this.originalServerWorld = ((CraftWorld) originalBukkitWorld).getHandle(); this.originalServerWorld = ((CraftWorld) originalBukkitWorld).getHandle();
originalChunkProvider = originalServerWorld.getChunkSource(); originalChunkProvider = originalServerWorld.getChunkSource();
if (!(originalChunkProvider instanceof ServerChunkCache)) {
return false;
}
//flat bedrock? (only on paper) //flat bedrock? (only on paper)
if (paperConfigField != null) { if (paperConfigField != null) {
@ -191,7 +188,7 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
} }
seed = options.getSeed().orElse(originalServerWorld.getSeed()); 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; 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 //let's start then
structureManager = server.getStructureManager(); structureManager = server.getStructureManager();
threadedLevelLightEngine = freshChunkProvider.getLightEngine(); threadedLevelLightEngine = freshChunkProvider.getLightEngine();
@ -523,7 +520,7 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
} }
@Override @Override
public CompletableFuture<?> processChunk(Long xz, List<ChunkAccess> accessibleChunks) { public CompletableFuture<?> processChunk(List<ChunkAccess> accessibleChunks) {
return chunkStatus.generate( return chunkStatus.generate(
Runnable::run, // TODO revisit, we might profit from this somehow? Runnable::run, // TODO revisit, we might profit from this somehow?
freshWorld, freshWorld,

View File

@ -12,5 +12,5 @@ repositories {
dependencies { dependencies {
the<PaperweightUserDependenciesExtension>().paperDevBundle("1.19.4-R0.1-20230608.201059-104") the<PaperweightUserDependenciesExtension>().paperDevBundle("1.19.4-R0.1-20230608.201059-104")
compileOnly("io.papermc:paperlib") compileOnly(libs.paperlib)
} }

View File

@ -99,14 +99,8 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
private static final MethodHandle methodGetVisibleChunk; 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 Field fieldThreadingDetector;
private static final long fieldThreadingDetectorOffset;
private static final Field fieldLock; private static final Field fieldLock;
private static final long fieldLockOffset;
private static final MethodHandle methodRemoveGameEventListener; private static final MethodHandle methodRemoveGameEventListener;
private static final MethodHandle methodremoveTickingBlockEntity; private static final MethodHandle methodremoveTickingBlockEntity;
@ -149,20 +143,15 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
getVisibleChunkIfPresent.setAccessible(true); getVisibleChunkIfPresent.setAccessible(true);
methodGetVisibleChunk = lookup.unreflect(getVisibleChunkIfPresent); methodGetVisibleChunk = lookup.unreflect(getVisibleChunkIfPresent);
Unsafe unsafe = ReflectionUtils.getUnsafe();
if (!PaperLib.isPaper()) { if (!PaperLib.isPaper()) {
fieldThreadingDetector = PalettedContainer.class.getDeclaredField(Refraction.pickName("threadingDetector", "f")); fieldThreadingDetector = PalettedContainer.class.getDeclaredField(Refraction.pickName("threadingDetector", "f"));
fieldThreadingDetectorOffset = unsafe.objectFieldOffset(fieldThreadingDetector); fieldThreadingDetector.setAccessible(true);
fieldLock = ThreadingDetector.class.getDeclaredField(Refraction.pickName("lock", "c")); fieldLock = ThreadingDetector.class.getDeclaredField(Refraction.pickName("lock", "c"));
fieldLockOffset = unsafe.objectFieldOffset(fieldLock); fieldLock.setAccessible(true);
} else { } else {
// in paper, the used methods are synchronized properly // in paper, the used methods are synchronized properly
fieldThreadingDetector = null; fieldThreadingDetector = null;
fieldThreadingDetectorOffset = -1;
fieldLock = null; fieldLock = null;
fieldLockOffset = -1;
} }
Method removeGameEventListener = LevelChunk.class.getDeclaredMethod( Method removeGameEventListener = LevelChunk.class.getDeclaredMethod(
@ -185,12 +174,6 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
fieldRemove = BlockEntity.class.getDeclaredField(Refraction.pickName("remove", "q")); fieldRemove = BlockEntity.class.getDeclaredField(Refraction.pickName("remove", "q"));
fieldRemove.setAccessible(true); 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; boolean chunkRewrite;
try { try {
ServerLevel.class.getDeclaredMethod("getEntityLookup"); ServerLevel.class.getDeclaredMethod("getEntityLookup");
@ -226,9 +209,8 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
LevelChunkSection value, LevelChunkSection value,
int layer int layer
) { ) {
long offset = ((long) layer << CHUNKSECTION_SHIFT) + CHUNKSECTION_BASE;
if (layer >= 0 && layer < sections.length) { if (layer >= 0 && layer < sections.length) {
return ReflectionUtils.getUnsafe().compareAndSwapObject(sections, offset, expected, value); return ReflectionUtils.compareAndSet(sections, expected, value, layer);
} }
return false; return false;
} }
@ -243,19 +225,15 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
} }
try { try {
synchronized (section) { synchronized (section) {
Unsafe unsafe = ReflectionUtils.getUnsafe();
PalettedContainer<net.minecraft.world.level.block.state.BlockState> blocks = section.getStates(); PalettedContainer<net.minecraft.world.level.block.state.BlockState> blocks = section.getStates();
ThreadingDetector currentThreadingDetector = (ThreadingDetector) unsafe.getObject( ThreadingDetector currentThreadingDetector = (ThreadingDetector) fieldThreadingDetector.get(blocks);
blocks,
fieldThreadingDetectorOffset
);
synchronized (currentThreadingDetector) { synchronized (currentThreadingDetector) {
Semaphore currentLock = (Semaphore) unsafe.getObject(currentThreadingDetector, fieldLockOffset); Semaphore currentLock = (Semaphore) fieldLock.get(currentThreadingDetector);
if (currentLock instanceof DelegateSemaphore delegateSemaphore) { if (currentLock instanceof DelegateSemaphore delegateSemaphore) {
return delegateSemaphore; return delegateSemaphore;
} }
DelegateSemaphore newLock = new DelegateSemaphore(1, currentLock); DelegateSemaphore newLock = new DelegateSemaphore(1, currentLock);
unsafe.putObject(currentThreadingDetector, fieldLockOffset, newLock); fieldLock.set(currentThreadingDetector, newLock);
return newLock; return newLock;
} }
} }

View File

@ -192,9 +192,6 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
protected boolean prepare() { protected boolean prepare() {
this.originalServerWorld = ((CraftWorld) originalBukkitWorld).getHandle(); this.originalServerWorld = ((CraftWorld) originalBukkitWorld).getHandle();
originalChunkProvider = originalServerWorld.getChunkSource(); originalChunkProvider = originalServerWorld.getChunkSource();
if (!(originalChunkProvider instanceof ServerChunkCache)) {
return false;
}
//flat bedrock? (only on paper) //flat bedrock? (only on paper)
if (paperConfigField != null) { if (paperConfigField != null) {
@ -205,7 +202,7 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
} }
seed = options.getSeed().orElse(originalServerWorld.getSeed()); 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; 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 //let's start then
structureTemplateManager = server.getStructureManager(); structureTemplateManager = server.getStructureManager();
threadedLevelLightEngine = new NoOpLightEngine(freshChunkProvider); threadedLevelLightEngine = new NoOpLightEngine(freshChunkProvider);
@ -554,7 +551,7 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
} }
@Override @Override
public CompletableFuture<?> processChunk(Long xz, List<ChunkAccess> accessibleChunks) { public CompletableFuture<?> processChunk(List<ChunkAccess> accessibleChunks) {
return chunkStatus.generate( return chunkStatus.generate(
Runnable::run, // TODO revisit, we might profit from this somehow? Runnable::run, // TODO revisit, we might profit from this somehow?
freshWorld, freshWorld,

View File

@ -12,6 +12,6 @@ repositories {
dependencies { dependencies {
// https://repo.papermc.io/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/ // 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") the<PaperweightUserDependenciesExtension>().paperDevBundle("1.20.1-R0.1-20230916.212543-167")
compileOnly("io.papermc:paperlib") compileOnly(libs.paperlib)
} }

View File

@ -637,7 +637,7 @@ public final class PaperweightAdapter implements BukkitImplAdapter<net.minecraft
final net.minecraft.core.Direction enumFacing = adapt(face); final net.minecraft.core.Direction enumFacing = adapt(face);
BlockHitResult rayTrace = new BlockHitResult(blockVec, enumFacing, blockPos, false); BlockHitResult rayTrace = new BlockHitResult(blockVec, enumFacing, blockPos, false);
UseOnContext context = new UseOnContext(fakePlayer, InteractionHand.MAIN_HAND, rayTrace); 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 (result != InteractionResult.SUCCESS) {
if (worldServer.getBlockState(blockPos).use(worldServer, fakePlayer, InteractionHand.MAIN_HAND, rayTrace).consumesAction()) { if (worldServer.getBlockState(blockPos).use(worldServer, fakePlayer, InteractionHand.MAIN_HAND, rayTrace).consumesAction()) {
result = InteractionResult.SUCCESS; result = InteractionResult.SUCCESS;

View File

@ -102,14 +102,8 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
private static final MethodHandle methodGetVisibleChunk; 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 Field fieldThreadingDetector;
private static final long fieldThreadingDetectorOffset;
private static final Field fieldLock; private static final Field fieldLock;
private static final long fieldLockOffset;
private static final MethodHandle methodRemoveGameEventListener; private static final MethodHandle methodRemoveGameEventListener;
private static final MethodHandle methodremoveTickingBlockEntity; private static final MethodHandle methodremoveTickingBlockEntity;
@ -158,20 +152,15 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
getVisibleChunkIfPresent.setAccessible(true); getVisibleChunkIfPresent.setAccessible(true);
methodGetVisibleChunk = lookup.unreflect(getVisibleChunkIfPresent); methodGetVisibleChunk = lookup.unreflect(getVisibleChunkIfPresent);
Unsafe unsafe = ReflectionUtils.getUnsafe();
if (!PaperLib.isPaper()) { if (!PaperLib.isPaper()) {
fieldThreadingDetector = PalettedContainer.class.getDeclaredField(Refraction.pickName("threadingDetector", "f")); fieldThreadingDetector = PalettedContainer.class.getDeclaredField(Refraction.pickName("threadingDetector", "f"));
fieldThreadingDetectorOffset = unsafe.objectFieldOffset(fieldThreadingDetector); fieldThreadingDetector.setAccessible(true);
fieldLock = ThreadingDetector.class.getDeclaredField(Refraction.pickName("lock", "c")); fieldLock = ThreadingDetector.class.getDeclaredField(Refraction.pickName("lock", "c"));
fieldLockOffset = unsafe.objectFieldOffset(fieldLock); fieldLock.setAccessible(true);
} else { } else {
// in paper, the used methods are synchronized properly // in paper, the used methods are synchronized properly
fieldThreadingDetector = null; fieldThreadingDetector = null;
fieldThreadingDetectorOffset = -1;
fieldLock = null; fieldLock = null;
fieldLockOffset = -1;
} }
Method removeGameEventListener = LevelChunk.class.getDeclaredMethod( Method removeGameEventListener = LevelChunk.class.getDeclaredMethod(
@ -194,12 +183,6 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
fieldRemove = BlockEntity.class.getDeclaredField(Refraction.pickName("remove", "q")); fieldRemove = BlockEntity.class.getDeclaredField(Refraction.pickName("remove", "q"));
fieldRemove.setAccessible(true); 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; boolean chunkRewrite;
try { try {
ServerLevel.class.getDeclaredMethod("getEntityLookup"); ServerLevel.class.getDeclaredMethod("getEntityLookup");
@ -249,9 +232,8 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
LevelChunkSection value, LevelChunkSection value,
int layer int layer
) { ) {
long offset = ((long) layer << CHUNKSECTION_SHIFT) + CHUNKSECTION_BASE;
if (layer >= 0 && layer < sections.length) { if (layer >= 0 && layer < sections.length) {
return ReflectionUtils.getUnsafe().compareAndSwapObject(sections, offset, expected, value); return ReflectionUtils.compareAndSet(sections, expected, value, layer);
} }
return false; return false;
} }
@ -266,19 +248,15 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
} }
try { try {
synchronized (section) { synchronized (section) {
Unsafe unsafe = ReflectionUtils.getUnsafe();
PalettedContainer<net.minecraft.world.level.block.state.BlockState> blocks = section.getStates(); PalettedContainer<net.minecraft.world.level.block.state.BlockState> blocks = section.getStates();
ThreadingDetector currentThreadingDetector = (ThreadingDetector) unsafe.getObject( ThreadingDetector currentThreadingDetector = (ThreadingDetector) fieldThreadingDetector.get(blocks);
blocks,
fieldThreadingDetectorOffset
);
synchronized (currentThreadingDetector) { synchronized (currentThreadingDetector) {
Semaphore currentLock = (Semaphore) unsafe.getObject(currentThreadingDetector, fieldLockOffset); Semaphore currentLock = (Semaphore) fieldLock.get(currentThreadingDetector);
if (currentLock instanceof DelegateSemaphore delegateSemaphore) { if (currentLock instanceof DelegateSemaphore delegateSemaphore) {
return delegateSemaphore; return delegateSemaphore;
} }
DelegateSemaphore newLock = new DelegateSemaphore(1, currentLock); DelegateSemaphore newLock = new DelegateSemaphore(1, currentLock);
unsafe.putObject(currentThreadingDetector, fieldLockOffset, newLock); fieldLock.set(currentThreadingDetector, newLock);
return newLock; return newLock;
} }
} }

View File

@ -192,9 +192,6 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
protected boolean prepare() { protected boolean prepare() {
this.originalServerWorld = ((CraftWorld) originalBukkitWorld).getHandle(); this.originalServerWorld = ((CraftWorld) originalBukkitWorld).getHandle();
originalChunkProvider = originalServerWorld.getChunkSource(); originalChunkProvider = originalServerWorld.getChunkSource();
if (!(originalChunkProvider instanceof ServerChunkCache)) {
return false;
}
//flat bedrock? (only on paper) //flat bedrock? (only on paper)
if (paperConfigField != null) { if (paperConfigField != null) {
@ -205,7 +202,7 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
} }
seed = options.getSeed().orElse(originalServerWorld.getSeed()); 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; 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 //let's start then
structureTemplateManager = server.getStructureManager(); structureTemplateManager = server.getStructureManager();
threadedLevelLightEngine = new NoOpLightEngine(freshChunkProvider); threadedLevelLightEngine = new NoOpLightEngine(freshChunkProvider);
@ -555,7 +552,7 @@ public class PaperweightRegen extends Regenerator<ChunkAccess, ProtoChunk, Level
} }
@Override @Override
public CompletableFuture<?> processChunk(Long xz, List<ChunkAccess> accessibleChunks) { public CompletableFuture<?> processChunk(List<ChunkAccess> accessibleChunks) {
return chunkStatus.generate( return chunkStatus.generate(
Runnable::run, // TODO revisit, we might profit from this somehow? Runnable::run, // TODO revisit, we might profit from this somehow?
freshWorld, freshWorld,

View File

@ -3,7 +3,7 @@ import io.papermc.paperweight.userdev.attribute.Obfuscation
plugins { plugins {
`java-library` `java-library`
id("com.modrinth.minotaur") version "2.8.3" alias(libs.plugins.minotaur)
} }
project.description = "Bukkit" project.description = "Bukkit"
@ -74,19 +74,19 @@ dependencies {
implementation(libs.fastutil) implementation(libs.fastutil)
// Platform expectations // Platform expectations
compileOnly("io.papermc.paper:paper-api") { compileOnly(libs.paper) {
exclude("junit", "junit") exclude("junit", "junit")
exclude(group = "org.slf4j", module = "slf4j-api") exclude(group = "org.slf4j", module = "slf4j-api")
} }
// Logging // Logging
localImplementation("org.apache.logging.log4j:log4j-api") localImplementation(libs.log4jApi)
localImplementation(libs.log4jBom) { localImplementation(libs.log4jBom) {
because("Spigot provides Log4J (sort of, not in API, implicitly part of server)") because("Spigot provides Log4J (sort of, not in API, implicitly part of server)")
} }
// Plugins // Plugins
compileOnly("com.github.MilkBowl:VaultAPI") { isTransitive = false } compileOnly(libs.vault) { isTransitive = false }
compileOnly(libs.dummypermscompat) { compileOnly(libs.dummypermscompat) {
exclude("com.github.MilkBowl", "VaultAPI") exclude("com.github.MilkBowl", "VaultAPI")
} }
@ -101,26 +101,26 @@ dependencies {
compileOnly(libs.griefdefender) { isTransitive = false } compileOnly(libs.griefdefender) { isTransitive = false }
compileOnly(libs.residence) { isTransitive = false } compileOnly(libs.residence) { isTransitive = false }
compileOnly(libs.towny) { isTransitive = false } compileOnly(libs.towny) { isTransitive = false }
compileOnly("com.intellectualsites.plotsquared:plotsquared-bukkit") { isTransitive = false } compileOnly(libs.plotSquaredBukkit) { isTransitive = false }
compileOnly("com.intellectualsites.plotsquared:plotsquared-core") { isTransitive = false } compileOnly(libs.plotSquaredCore) { isTransitive = false }
// Third party // Third party
implementation("io.papermc:paperlib") implementation(libs.paperlib)
implementation("org.bstats:bstats-bukkit") { isTransitive = false } implementation(libs.bstatsBukkit) { isTransitive = false }
implementation(libs.bstatsBase) { isTransitive = false } implementation(libs.bstatsBase) { isTransitive = false }
implementation("dev.notmyfault.serverlib:ServerLib") implementation(libs.serverlib)
implementation("com.intellectualsites.paster:Paster") { isTransitive = false } implementation(libs.paster) { isTransitive = false }
api(libs.lz4Java) { isTransitive = false } api(libs.lz4Java) { isTransitive = false }
api(libs.sparsebitset) { isTransitive = false } api(libs.sparsebitset) { isTransitive = false }
api(libs.parallelgzip) { isTransitive = false } api(libs.parallelgzip) { isTransitive = false }
compileOnly("net.kyori:adventure-api") compileOnly(libs.adventureApi)
compileOnlyApi("org.checkerframework:checker-qual") compileOnlyApi(libs.checkerqual)
// Tests // Tests
testImplementation(libs.mockito) testImplementation(libs.mockito)
testImplementation("net.kyori:adventure-api") testImplementation(libs.adventureApi)
testImplementation("org.checkerframework:checker-qual") testImplementation(libs.checkerqual)
testImplementation("io.papermc.paper:paper-api") { isTransitive = true } testImplementation(libs.paper) { isTransitive = true }
} }
tasks.named<Copy>("processResources") { tasks.named<Copy>("processResources") {
@ -174,7 +174,7 @@ tasks.named<ShadowJar>("shadowJar") {
include(dependency("it.unimi.dsi:fastutil")) include(dependency("it.unimi.dsi:fastutil"))
} }
relocate("org.incendo.serverlib", "com.fastasyncworldedit.serverlib") { 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") { relocate("com.intellectualsites.paster", "com.fastasyncworldedit.paster") {
include(dependency("com.intellectualsites.paster:Paster")) include(dependency("com.intellectualsites.paster:Paster"))

View File

@ -22,14 +22,14 @@ import com.sk89q.worldedit.world.block.BaseBlock;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap; import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; 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.apache.logging.log4j.Logger;
import org.bukkit.World;
import org.bukkit.generator.BiomeProvider; import org.bukkit.generator.BiomeProvider;
import org.bukkit.generator.BlockPopulator; import org.bukkit.generator.BlockPopulator;
import org.bukkit.generator.WorldInfo; import org.bukkit.generator.WorldInfo;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.Iterator; import java.util.Iterator;
@ -42,7 +42,6 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors;
/** /**
* Represents an abstract regeneration handler. * Represents an abstract regeneration handler.
@ -62,7 +61,7 @@ public abstract class Regenerator<IChunkAccess, ProtoChunk extends IChunkAccess,
protected final RegenOptions options; protected final RegenOptions options;
//runtime //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 Long2ObjectLinkedOpenHashMap<ProtoChunk> protoChunks = new Long2ObjectLinkedOpenHashMap<>();
private final Long2ObjectOpenHashMap<Chunk> chunks = new Long2ObjectOpenHashMap<>(); private final Long2ObjectOpenHashMap<Chunk> chunks = new Long2ObjectOpenHashMap<>();
protected boolean generateConcurrent = true; protected boolean generateConcurrent = true;
@ -85,19 +84,19 @@ public abstract class Regenerator<IChunkAccess, ProtoChunk extends IChunkAccess,
this.options = options; 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 random = new Random();
random.setSeed(worldseed); random.setSeed(worldSeed);
long xRand = random.nextLong() / 2L * 2L + 1L; long xRand = random.nextLong() / 2L * 2L + 1L;
long zRand = 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; return random;
} }
/** /**
* Regenerates the selected {@code Region}. * 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 * @throws Exception when something goes terribly wrong
*/ */
public boolean regenerate() throws Exception { 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 //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 //generate chunk coords lists with a certain radius
Int2ObjectOpenHashMap<List<Long>> chunkCoordsForRadius = new Int2ObjectOpenHashMap<>(); Int2ObjectOpenHashMap<long[]> chunkCoordsForRadius = new Int2ObjectOpenHashMap<>();
chunkStati.keySet().stream().map(ChunkStatusWrapper::requiredNeighborChunkRadius0).distinct().forEach(radius -> { chunkStatuses.keySet().stream().mapToInt(ChunkStatusWrapper::requiredNeighborChunkRadius0).distinct().forEach(radius -> {
if (radius == -1) { //ignore ChunkStatus.EMPTY if (radius == -1) { //ignore ChunkStatus.EMPTY
return; return;
} }
@ -186,19 +185,19 @@ public abstract class Regenerator<IChunkAccess, ProtoChunk extends IChunkAccess,
}); });
//create chunks //create chunks
for (Long xz : chunkCoordsForRadius.get(0)) { for (long xz : chunkCoordsForRadius.get(0)) {
ProtoChunk chunk = createProtoChunk(MathMan.unpairIntX(xz), MathMan.unpairIntY(xz)); ProtoChunk chunk = createProtoChunk(MathMan.unpairIntX(xz), MathMan.unpairIntY(xz));
protoChunks.put(xz, chunk); 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 //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<>(); Int2ObjectOpenHashMap<Long2ObjectOpenHashMap<List<IChunkAccess>>> worldLimits = new Int2ObjectOpenHashMap<>();
chunkStati.keySet().stream().map(ChunkStatusWrapper::requiredNeighborChunkRadius0).distinct().forEach(radius -> { chunkStatuses.keySet().stream().mapToInt(ChunkStatusWrapper::requiredNeighborChunkRadius0).distinct().forEach(radius -> {
if (radius == -1) { //ignore ChunkStatus.EMPTY if (radius == -1) { //ignore ChunkStatus.EMPTY
return; return;
} }
Long2ObjectOpenHashMap<List<IChunkAccess>> map = new Long2ObjectOpenHashMap<>(); Long2ObjectOpenHashMap<List<IChunkAccess>> map = new Long2ObjectOpenHashMap<>();
for (Long xz : chunkCoordsForRadius.get(radius)) { for (long xz : chunkCoordsForRadius.get(radius)) {
int x = MathMan.unpairIntX(xz); int x = MathMan.unpairIntX(xz);
int z = MathMan.unpairIntY(xz); int z = MathMan.unpairIntY(xz);
List<IChunkAccess> l = new ArrayList<>((radius + 1 + radius) * (radius + 1 + radius)); 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); map.put(xz, l);
} }
worldlimits.put(radius, map); worldLimits.put(radius, map);
}); });
//run generation tasks excluding FULL chunk status //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(); ChunkStatus chunkStatus = entry.getKey();
int radius = chunkStatus.requiredNeighborChunkRadius0(); 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) { if (this.generateConcurrent && entry.getValue() == Concurrency.RADIUS) {
SequentialTasks<ConcurrentTasks<SequentialTasks<Long>>> tasks = getChunkStatusTaskRows(coords, radius); SequentialTasks<ConcurrentTasks<LongList>> tasks = getChunkStatusTaskRows(coords, radius);
for (ConcurrentTasks<SequentialTasks<Long>> para : tasks) { for (ConcurrentTasks<LongList> para : tasks) {
List<Runnable> scheduled = new ArrayList<>(tasks.size()); List<Runnable> scheduled = new ArrayList<>(tasks.size());
for (SequentialTasks<Long> row : para) { for (LongList row : para) {
scheduled.add(() -> { scheduled.add(() -> {
for (Long xz : row) { for (long xz : row) {
chunkStatus.processChunkSave(xz, worldlimits.get(radius).get(xz)); chunkStatus.processChunkSave(xz, limitsForRadius.get(xz));
} }
}); });
} }
try { runAndWait(scheduled);
List<Future<?>> futures = new ArrayList<>();
scheduled.forEach(task -> futures.add(executor.submit(task)));
for (Future<?> future : futures) {
future.get();
}
} catch (Exception e) {
e.printStackTrace();
}
} }
} else if (this.generateConcurrent && entry.getValue() == Concurrency.FULL) { } else if (this.generateConcurrent && entry.getValue() == Concurrency.FULL) {
// every chunk can be processed individually // every chunk can be processed individually
List<Runnable> scheduled = new ArrayList<>(coords.size()); List<Runnable> scheduled = new ArrayList<>(coords.length);
for (long xz : coords) { for (long xz : coords) {
scheduled.add(() -> { scheduled.add(() -> chunkStatus.processChunkSave(xz, limitsForRadius.get(xz)));
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();
} }
runAndWait(scheduled);
} else { // Concurrency.NONE or generateConcurrent == false } else { // Concurrency.NONE or generateConcurrent == false
// run sequential but submit to different thread // run sequential but submit to different thread
// running regen on the main thread otherwise triggers async-only events on the main thread // running regen on the main thread otherwise triggers async-only events on the main thread
executor.submit(() -> { executor.submit(() -> {
for (long xz : coords) { for (long xz : coords) {
chunkStatus.processChunkSave(xz, worldlimits.get(radius).get(xz)); chunkStatus.processChunkSave(xz, limitsForRadius.get(xz));
} }
}).get(); // wait until finished this step }).get(); // wait until finished this step
} }
} }
//convert to proper chunks //convert to proper chunks
for (Long xz : chunkCoordsForRadius.get(0)) { for (long xz : chunkCoordsForRadius.get(0)) {
ProtoChunk proto = protoChunks.get(xz); ProtoChunk proto = protoChunks.get(xz);
chunks.put(xz, createChunk(proto)); chunks.put(xz, createChunk(proto));
} }
//final chunkstatus //final chunkstatus
ChunkStatus FULL = getFullChunkStatus(); 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); Chunk chunk = chunks.get(xz);
FULL.processChunkSave(xz, Arrays.asList(chunk)); FULL.processChunkSave(xz, List.of(chunk));
} }
//populate //populate
List<BlockPopulator> populators = getBlockPopulators(); List<BlockPopulator> populators = getBlockPopulators();
for (Long xz : chunkCoordsForRadius.get(0)) { for (long xz : chunkCoordsForRadius.get(0)) {
int x = MathMan.unpairIntX(xz); int x = MathMan.unpairIntX(xz);
int z = MathMan.unpairIntY(xz); int z = MathMan.unpairIntY(xz);
@ -302,6 +284,18 @@ public abstract class Regenerator<IChunkAccess, ProtoChunk extends IChunkAccess,
return true; 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() { private void copyToWorld() {
//Setting Blocks //Setting Blocks
boolean genbiomes = options.shouldRegenBiomes(); boolean genbiomes = options.shouldRegenBiomes();
@ -437,7 +431,7 @@ public abstract class Regenerator<IChunkAccess, ProtoChunk extends IChunkAccess,
protected abstract IChunkCache<IChunkGet> initSourceQueueCache(); protected abstract IChunkCache<IChunkGet> initSourceQueueCache();
//algorithms //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 oldMin = region.getMinimumPoint();
BlockVector3 newMin = BlockVector3.at( BlockVector3 newMin = BlockVector3.at(
(oldMin.getX() >> 4 << 4) - border * 16, (oldMin.getX() >> 4 << 4) - border * 16,
@ -455,76 +449,79 @@ public abstract class Regenerator<IChunkAccess, ProtoChunk extends IChunkAccess,
.sorted(Comparator .sorted(Comparator
.comparingInt(BlockVector2::getZ) .comparingInt(BlockVector2::getZ)
.thenComparingInt(BlockVector2::getX)) //needed for RegionLimitedWorldAccess .thenComparingInt(BlockVector2::getX)) //needed for RegionLimitedWorldAccess
.map(c -> MathMan.pairInt(c.getX(), c.getZ())) .mapToLong(c -> MathMan.pairInt(c.getX(), c.getZ()))
.collect(Collectors.toList()); .toArray();
} }
/** /**
* Creates a list of chunkcoord rows that may be executed concurrently * 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 * @param requiredNeighborChunkRadius the radius of neighbor chunks that may not be written to concurrently (ChunkStatus
* .requiredNeighborRadius) * .requiredNeighborRadius)
* @return a list of chunkcoords rows that may be executed concurrently * @return a list of chunkcoords rows that may be executed concurrently
*/ */
private SequentialTasks<ConcurrentTasks<SequentialTasks<Long>>> getChunkStatusTaskRows( private SequentialTasks<ConcurrentTasks<LongList>> getChunkStatusTaskRows(
List<Long> allcoords, long[] allCoords,
int requiredNeighborChunkRadius int requiredNeighborChunkRadius
) { ) {
int requiredneighbors = Math.max(0, requiredNeighborChunkRadius); int requiredNeighbors = Math.max(0, requiredNeighborChunkRadius);
int minx = allcoords.isEmpty() ? 0 : MathMan.unpairIntX(allcoords.get(0)); final int coordsCount = allCoords.length;
int maxx = allcoords.isEmpty() ? 0 : MathMan.unpairIntX(allcoords.get(allcoords.size() - 1)); long first = coordsCount == 0 ? 0 : allCoords[0];
int minz = allcoords.isEmpty() ? 0 : MathMan.unpairIntY(allcoords.get(0)); long last = coordsCount == 0 ? 0 : allCoords[coordsCount - 1];
int maxz = allcoords.isEmpty() ? 0 : MathMan.unpairIntY(allcoords.get(allcoords.size() - 1)); int minX = MathMan.unpairIntX(first);
SequentialTasks<ConcurrentTasks<SequentialTasks<Long>>> tasks; int maxX = MathMan.unpairIntX(last);
if (maxz - minz > maxx - minx) { int minZ = MathMan.unpairIntY(first);
int numlists = Math.min(requiredneighbors * 2 + 1, maxx - minx + 1); 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(); Int2ObjectOpenHashMap<LongList> byX = new Int2ObjectOpenHashMap<>();
int expectedListLength = (allcoords.size() + 1) / (maxx - minx); int expectedListLength = (coordsCount + 1) / (maxX - minX);
//init lists //init lists
for (int i = minx; i <= maxx; i++) { for (int i = minX; i <= maxX; i++) {
byx.put(i, new SequentialTasks(expectedListLength)); byX.put(i, new LongArrayList(expectedListLength));
} }
//sort into lists by x coord //sort into lists by x coord
for (Long xz : allcoords) { for (long allCoord : allCoords) {
byx.get(MathMan.unpairIntX(xz)).add(xz); byX.get(MathMan.unpairIntX(allCoord)).add(allCoord);
} }
//create parallel tasks //create parallel tasks
tasks = new SequentialTasks(numlists); tasks = new SequentialTasks<>(numlists);
for (int offset = 0; offset < numlists; offset++) { for (int offset = 0; offset < numlists; offset++) {
ConcurrentTasks<SequentialTasks<Long>> para = new ConcurrentTasks((maxz - minz + 1) / numlists + 1); ConcurrentTasks<LongList> para = new ConcurrentTasks<>((maxZ - minZ + 1) / numlists + 1);
for (int i = 0; minx + i * numlists + offset <= maxx; i++) { for (int i = 0; minX + i * numlists + offset <= maxX; i++) {
para.add(byx.get(minx + i * numlists + offset)); para.add(byX.get(minX + i * numlists + offset));
} }
tasks.add(para); tasks.add(para);
} }
} else { } 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(); Int2ObjectOpenHashMap<LongList> byZ = new Int2ObjectOpenHashMap<>();
int expectedListLength = (allcoords.size() + 1) / (maxz - minz); int expectedListLength = (coordsCount + 1) / (maxZ - minZ);
//init lists //init lists
for (int i = minz; i <= maxz; i++) { for (int i = minZ; i <= maxZ; i++) {
byz.put(i, new SequentialTasks(expectedListLength)); byZ.put(i, new LongArrayList(expectedListLength));
} }
//sort into lists by x coord //sort into lists by x coord
for (Long xz : allcoords) { for (long allCoord : allCoords) {
byz.get(MathMan.unpairIntY(xz)).add(xz); byZ.get(MathMan.unpairIntY(allCoord)).add(allCoord);
} }
//create parallel tasks //create parallel tasks
tasks = new SequentialTasks(numlists); tasks = new SequentialTasks<>(numlists);
for (int offset = 0; offset < numlists; offset++) { for (int offset = 0; offset < numlists; offset++) {
ConcurrentTasks<SequentialTasks<Long>> para = new ConcurrentTasks((maxx - minx + 1) / numlists + 1); ConcurrentTasks<LongList> para = new ConcurrentTasks<>((maxX - minX + 1) / numlists + 1);
for (int i = 0; minz + i * numlists + offset <= maxz; i++) { for (int i = 0; minZ + i * numlists + offset <= maxZ; i++) {
para.add(byz.get(minz + i * numlists + offset)); para.add(byZ.get(minZ + i * numlists + offset));
} }
tasks.add(para); tasks.add(para);
} }
@ -576,15 +573,14 @@ public abstract class Regenerator<IChunkAccess, ProtoChunk extends IChunkAccess,
/** /**
* Return the name of the wrapped {@code ChunkStatus}. * 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}. * @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. * 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 { try {
processChunk(xz, accessibleChunks).get(); processChunk(accessibleChunks).get();
} catch (Exception e) { } catch (Exception e) {
LOGGER.error( LOGGER.error(
"Error while running " + name() + " on chunk " + MathMan.unpairIntX(xz) + "/" + MathMan.unpairIntY(xz), "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 static class SequentialTasks<T> extends Tasks<T> {
public SequentialTasks(int expectedsize) { public SequentialTasks(int expectedSize) {
super(expectedsize); super(expectedSize);
} }
} }
public static class ConcurrentTasks<T> extends Tasks<T> { public static class ConcurrentTasks<T> extends Tasks<T> {
public ConcurrentTasks(int expectedsize) { public ConcurrentTasks(int expectedSize) {
super(expectedsize); super(expectedSize);
} }
} }
@ -615,8 +611,8 @@ public abstract class Regenerator<IChunkAccess, ProtoChunk extends IChunkAccess,
private final List<T> tasks; private final List<T> tasks;
public Tasks(int expectedsize) { public Tasks(int expectedSize) {
tasks = new ArrayList(expectedsize); tasks = new ArrayList<>(expectedSize);
} }
public void add(T task) { public void add(T task) {

View File

@ -25,7 +25,6 @@ import com.sk89q.worldedit.util.YAMLConfiguration;
import com.sk89q.worldedit.util.report.Unreported; import com.sk89q.worldedit.util.report.Unreported;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import java.io.File;
import java.nio.file.Path; import java.nio.file.Path;
/** /**

View File

@ -42,6 +42,7 @@ import com.sk89q.worldedit.util.SideEffect;
import com.sk89q.worldedit.util.lifecycle.Lifecycled; import com.sk89q.worldedit.util.lifecycle.Lifecycled;
import com.sk89q.worldedit.world.DataFixer; import com.sk89q.worldedit.world.DataFixer;
import com.sk89q.worldedit.world.registry.Registries; import com.sk89q.worldedit.world.registry.Registries;
import io.papermc.lib.PaperLib;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Server; import org.bukkit.Server;
@ -258,6 +259,14 @@ public class BukkitServerInterface extends AbstractPlatform implements MultiUser
return SUPPORTED_SIDE_EFFECTS; return SUPPORTED_SIDE_EFFECTS;
} }
@Override
public long getTickCount() {
if (PaperLib.isPaper()) {
return Bukkit.getCurrentTick();
}
return super.getTickCount();
}
public void unregisterCommands() { public void unregisterCommands() {
dynamicCommands.unregisterCommands(); dynamicCommands.unregisterCommands();
} }

View File

@ -27,6 +27,7 @@ import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.entity.Player; import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.event.platform.SessionIdleEvent; import com.sk89q.worldedit.event.platform.SessionIdleEvent;
import com.sk89q.worldedit.extension.platform.Actor; 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.Direction;
import com.sk89q.worldedit.util.Location; import com.sk89q.worldedit.util.Location;
import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.World;
@ -55,6 +56,7 @@ import java.util.Optional;
public class WorldEditListener implements Listener { public class WorldEditListener implements Listener {
private final WorldEditPlugin plugin; private final WorldEditPlugin plugin;
private final InteractionDebouncer debouncer;
/** /**
* Construct the object. * Construct the object.
@ -63,6 +65,7 @@ public class WorldEditListener implements Listener {
*/ */
public WorldEditListener(WorldEditPlugin plugin) { public WorldEditListener(WorldEditPlugin plugin) {
this.plugin = plugin; this.plugin = plugin;
debouncer = new InteractionDebouncer(plugin.getInternalPlatform());
} }
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
@ -128,62 +131,58 @@ public class WorldEditListener implements Listener {
*/ */
@EventHandler @EventHandler
public void onPlayerInteract(PlayerInteractEvent event) { public void onPlayerInteract(PlayerInteractEvent event) {
if (!plugin.getInternalPlatform().isHookingEvents()) { if (!plugin.getInternalPlatform().isHookingEvents()
return; || event.useItemInHand() == Result.DENY
} || event.getHand() == EquipmentSlot.OFF_HAND
|| event.getAction() == Action.PHYSICAL) {
if (event.useItemInHand() == Result.DENY) {
return;
}
if (event.getHand() == EquipmentSlot.OFF_HAND) {
return; return;
} }
final Player player = plugin.wrapPlayer(event.getPlayer()); 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 World world = player.getWorld();
final WorldEdit we = plugin.getWorldEdit(); final WorldEdit we = plugin.getWorldEdit();
final Direction direction = BukkitAdapter.adapt(event.getBlockFace()); 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(); boolean result = false;
if (action == Action.LEFT_CLICK_BLOCK) { switch (event.getAction()) {
final Block clickedBlock = event.getClickedBlock(); case LEFT_CLICK_BLOCK:
final Location pos = new Location(world, clickedBlock.getX(), clickedBlock.getY(), clickedBlock.getZ()); result = we.handleBlockLeftClick(player, pos, direction) || we.handleArmSwing(player);
break;
if (we.handleBlockLeftClick(player, pos, direction)) { case LEFT_CLICK_AIR:
event.setCancelled(true); result = we.handleArmSwing(player);
} break;
case RIGHT_CLICK_BLOCK:
if (we.handleArmSwing(player)) { result = we.handleBlockRightClick(player, pos, direction) || we.handleRightClick(player);
event.setCancelled(true); break;
} case RIGHT_CLICK_AIR:
result = we.handleRightClick(player);
} else if (action == Action.LEFT_CLICK_AIR) { break;
default:
if (we.handleArmSwing(player)) { break;
event.setCancelled(true); }
} debouncer.setLastInteraction(player, result);
if (result) {
} else if (action == Action.RIGHT_CLICK_BLOCK) { event.setCancelled(true);
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);
}
} }
} }
@EventHandler @EventHandler
public void onPlayerQuit(PlayerQuitEvent event) { public void onPlayerQuit(PlayerQuitEvent event) {
debouncer.clearInteraction(plugin.wrapPlayer(event.getPlayer()));
plugin.getWorldEdit().getEventBus().post(new SessionIdleEvent(new BukkitPlayer.SessionKeyImpl(event.getPlayer()))); plugin.getWorldEdit().getEventBus().post(new SessionIdleEvent(new BukkitPlayer.SessionKeyImpl(event.getPlayer())));
} }

View File

@ -36,6 +36,8 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.time.Duration;
import java.time.Instant;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
@ -142,6 +144,11 @@ public class TestOfflinePermissible implements OfflinePlayer, Permissible {
return false; return false;
} }
@Override
public boolean isConnected() {
return false;
}
@Override @Override
public String getName() { public String getName() {
return "Tester"; return "Tester";
@ -161,6 +168,24 @@ public class TestOfflinePermissible implements OfflinePlayer, Permissible {
throw new UnsupportedOperationException("Not supported yet."); 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 @Override
public @Nullable BanEntry<org.bukkit.profile.PlayerProfile> ban( public @Nullable BanEntry<org.bukkit.profile.PlayerProfile> ban(
@Nullable final String reason, @Nullable final String reason,

View File

@ -27,16 +27,16 @@ dependencies {
// Minecraft expectations // Minecraft expectations
annotationProcessor(libs.guava) annotationProcessor(libs.guava)
implementation("com.google.guava:guava") implementation(libs.guava)
implementation("com.google.code.gson:gson") implementation(libs.gson)
// Logging // Logging
implementation(libs.log4jBom) { implementation(libs.log4jBom) {
because("We control Log4J on this platform") because("We control Log4J on this platform")
} }
implementation("org.apache.logging.log4j:log4j-api") implementation(libs.log4jApi)
implementation(libs.log4jCore) implementation(libs.log4jCore)
implementation("commons-cli:commons-cli:1.5.0") implementation(libs.commonsCli)
api(libs.parallelgzip) { isTransitive = false } api(libs.parallelgzip) { isTransitive = false }
api(libs.lz4Java) api(libs.lz4Java)
} }

View File

@ -11,7 +11,7 @@ applyPlatformAndCoreConfiguration()
dependencies { dependencies {
constraints { constraints {
implementation("org.yaml:snakeyaml") { implementation(libs.snakeyaml) {
version { strictly("2.0") } version { strictly("2.0") }
because("Bukkit provides SnakeYaml") because("Bukkit provides SnakeYaml")
} }
@ -24,17 +24,17 @@ dependencies {
// Minecraft expectations // Minecraft expectations
implementation(libs.fastutil) implementation(libs.fastutil)
implementation("com.google.guava:guava") implementation(libs.guava)
implementation("com.google.code.gson:gson") implementation(libs.gson)
// Platform expectations // Platform expectations
implementation("org.yaml:snakeyaml") implementation(libs.snakeyaml)
// Logging // Logging
implementation("org.apache.logging.log4j:log4j-api") implementation(libs.log4jApi)
// Plugins // Plugins
compileOnly("com.intellectualsites.plotsquared:plotsquared-core") { isTransitive = false } compileOnly(libs.plotSquaredCore) { isTransitive = false }
// ensure this is on the classpath for the AP // ensure this is on the classpath for the AP
annotationProcessor(libs.guava) annotationProcessor(libs.guava)
@ -45,11 +45,11 @@ dependencies {
compileOnly(libs.truezip) compileOnly(libs.truezip)
implementation(libs.findbugs) implementation(libs.findbugs)
implementation(libs.rhino) implementation(libs.rhino)
compileOnly("net.kyori:adventure-api") compileOnly(libs.adventureApi)
compileOnlyApi(libs.adventureNbt) compileOnlyApi(libs.adventureNbt)
compileOnlyApi("net.kyori:adventure-text-minimessage") compileOnlyApi(libs.adventureMiniMessage)
implementation(libs.zstd) { isTransitive = false } implementation(libs.zstd) { isTransitive = false }
compileOnly("com.intellectualsites.paster:Paster") compileOnly(libs.paster)
compileOnly(libs.lz4Java) { isTransitive = false } compileOnly(libs.lz4Java) { isTransitive = false }
compileOnly(libs.sparsebitset) compileOnly(libs.sparsebitset)
compileOnly(libs.parallelgzip) { isTransitive = false } compileOnly(libs.parallelgzip) { isTransitive = false }

View File

@ -400,6 +400,7 @@ public class Settings extends Config {
"of a waterlogged fence). For blocking/remapping of all occurrences of a property like waterlogged, see", "of a waterlogged fence). For blocking/remapping of all occurrences of a property like waterlogged, see",
"remap-properties below.", "remap-properties below.",
"To generate a blank list, substitute the default content with a set of square brackets [] instead.", "To generate a blank list, substitute the default content with a set of square brackets [] instead.",
"The 'worldedit.anyblock' permission is not considered here.",
"Example block property blocking:", "Example block property blocking:",
" - \"minecraft:conduit[waterlogged=true]\"", " - \"minecraft:conduit[waterlogged=true]\"",
" - \"minecraft:piston[extended=false,facing=west]\"", " - \"minecraft:piston[extended=false,facing=west]\"",

View File

@ -4,19 +4,19 @@ import sun.misc.Unsafe;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.lang.reflect.AccessibleObject; import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.Comparator;
/** /**
* This is an internal class not meant to be used outside the FAWE internals. * This is an internal class not meant to be used outside the FAWE internals.
*/ */
public class ReflectionUtils { public class ReflectionUtils {
private static final VarHandle REFERENCE_ARRAY_HANDLE = MethodHandles.arrayElementVarHandle(Object[].class);
private static Unsafe UNSAFE; private static Unsafe UNSAFE;
static { static {
@ -33,6 +33,21 @@ public class ReflectionUtils {
return t.isInstance(o) ? t.cast(o) : null; return t.isInstance(o) ? t.cast(o) : null;
} }
/**
* Performs CAS on the array element at the given index.
*
* @param array the array in which to compare and set the value
* @param expectedValue the value expected to be at the index
* @param newValue the new value to be set at the index if the expected value matches
* @param index the index at which to compare and set the value
* @param <T> the type of elements in the array
* @return true if the value at the index was successfully updated to the new value, false otherwise
* @see VarHandle#compareAndSet(Object...)
*/
public static <T> boolean compareAndSet(T[] array, T expectedValue, T newValue, int index) {
return REFERENCE_ARRAY_HANDLE.compareAndSet(array, index, expectedValue, newValue);
}
public static void setAccessibleNonFinal(Field field) { public static void setAccessibleNonFinal(Field field) {
// let's make the field accessible // let's make the field accessible
field.setAccessible(true); field.setAccessible(true);

View File

@ -98,6 +98,8 @@ public class YAMLProcessor extends YAMLNode {
LoaderOptions loaderOptions = new LoaderOptions(); LoaderOptions loaderOptions = new LoaderOptions();
try { try {
int yamlAliasLimit = Integer.getInteger("worldedit.yaml.aliasLimit", 50);
loaderOptions.setMaxAliasesForCollections(yamlAliasLimit);
// 64 MB default // 64 MB default
int yamlCodePointLimit = Integer.getInteger("worldedit.yaml.codePointLimit", 64 * 1024 * 1024); int yamlCodePointLimit = Integer.getInteger("worldedit.yaml.codePointLimit", 64 * 1024 * 1024);
loaderOptions.setCodePointLimit(yamlCodePointLimit); loaderOptions.setCodePointLimit(yamlCodePointLimit);
@ -105,7 +107,7 @@ public class YAMLProcessor extends YAMLNode {
// pre-1.32 snakeyaml // pre-1.32 snakeyaml
} }
yaml = new Yaml(new SafeConstructor(new LoaderOptions()), representer, dumperOptions, loaderOptions); yaml = new Yaml(new SafeConstructor(loaderOptions), representer, dumperOptions, loaderOptions);
this.file = file; this.file = file;
} }

View File

@ -55,4 +55,9 @@ public abstract class AbstractPlatform implements Platform {
return null; return null;
} }
@Override
public long getTickCount() {
return System.nanoTime() / 50_000_000;
}
} }

View File

@ -216,6 +216,14 @@ public interface Platform extends Keyed {
*/ */
Set<SideEffect> getSupportedSideEffects(); Set<SideEffect> getSupportedSideEffects();
/**
* Get the number of ticks since the server started.
* On some platforms this value may be an approximation based on the JVM run time.
*
* @return The number of ticks since the server started.
*/
long getTickCount();
//FAWE start //FAWE start
/** /**

View File

@ -0,0 +1,69 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.internal.event;
import com.sk89q.worldedit.extension.platform.Platform;
import com.sk89q.worldedit.util.Identifiable;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
public class InteractionDebouncer {
private final Platform platform;
private final Map<UUID, Interaction> lastInteractions = new HashMap<>();
public InteractionDebouncer(Platform platform) {
this.platform = platform;
}
public void clearInteraction(Identifiable player) {
lastInteractions.remove(player.getUniqueId());
}
public void setLastInteraction(Identifiable player, boolean result) {
lastInteractions.put(player.getUniqueId(), new Interaction(platform.getTickCount(), result));
}
public Optional<Boolean> getDuplicateInteractionResult(Identifiable player) {
Interaction last = lastInteractions.get(player.getUniqueId());
if (last == null) {
return Optional.empty();
}
long now = platform.getTickCount();
if (now - last.tick <= 1) {
return Optional.of(last.result);
}
return Optional.empty();
}
private static class Interaction {
public final long tick;
public final boolean result;
public Interaction(long tick, boolean result) {
this.tick = tick;
this.result = result;
}
}
}

View File

@ -28,7 +28,7 @@ dependencies {
}) })
api("org.apache.logging.log4j:log4j-api") api("org.apache.logging.log4j:log4j-api")
api("org.bstats:bstats-sponge:1.7") api("org.bstats:bstats-sponge:1.7")
testImplementation("org.mockito:mockito-core:5.4.0") testImplementation("org.mockito:mockito-core:5.5.0")
} }
<<<<<<< HEAD <<<<<<< HEAD