mirror of
https://github.com/plexusorg/Plex-FAWE.git
synced 2024-12-22 17:27:38 +00:00
This commit is contained in:
commit
1a6b10c10f
9
.github/renovate.json
vendored
9
.github/renovate.json
vendored
@ -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",
|
||||||
|
@ -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("")
|
||||||
|
@ -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") {
|
||||||
|
@ -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
|
||||||
|
@ -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" }
|
||||||
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@ -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
3
gradlew
vendored
@ -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
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
||||||
|
@ -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,
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
@ -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"))
|
||||||
|
@ -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) {
|
||||||
|
@ -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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -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());
|
||||||
|
|
||||||
Action action = event.getAction();
|
|
||||||
if (action == Action.LEFT_CLICK_BLOCK) {
|
|
||||||
final Block clickedBlock = event.getClickedBlock();
|
final Block clickedBlock = event.getClickedBlock();
|
||||||
final Location pos = new Location(world, clickedBlock.getX(), clickedBlock.getY(), clickedBlock.getZ());
|
final Location pos = clickedBlock == null ? null : new Location(world, clickedBlock.getX(), clickedBlock.getY(), clickedBlock.getZ());
|
||||||
|
|
||||||
if (we.handleBlockLeftClick(player, pos, direction)) {
|
boolean result = false;
|
||||||
event.setCancelled(true);
|
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 (we.handleArmSwing(player)) {
|
if (result) {
|
||||||
event.setCancelled(true);
|
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@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())));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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,
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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 }
|
||||||
|
@ -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]\"",
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -55,4 +55,9 @@ public abstract class AbstractPlatform implements Platform {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getTickCount() {
|
||||||
|
return System.nanoTime() / 50_000_000;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user