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