mirror of
https://github.com/plexusorg/Plex-FAWE.git
synced 2024-12-23 01:37:37 +00:00
This commit is contained in:
commit
9d489791ee
1
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
1
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@ -32,7 +32,6 @@ body:
|
|||||||
- '1.19.4'
|
- '1.19.4'
|
||||||
- '1.18.2'
|
- '1.18.2'
|
||||||
- '1.17.1'
|
- '1.17.1'
|
||||||
- '1.16.5'
|
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
|
|
||||||
|
2
.github/renovate.json
vendored
2
.github/renovate.json
vendored
@ -19,8 +19,6 @@
|
|||||||
"net.fabricmc:fabric-loader",
|
"net.fabricmc:fabric-loader",
|
||||||
"net.fabricmc.fabric-api:fabric-api",
|
"net.fabricmc.fabric-api:fabric-api",
|
||||||
"com.github.luben:zstd-jni",
|
"com.github.luben:zstd-jni",
|
||||||
"net.kyori",
|
|
||||||
"net.kyori:adventure-nbt",
|
|
||||||
"org.jetbrains.kotlin.jvm",
|
"org.jetbrains.kotlin.jvm",
|
||||||
"log4j"
|
"log4j"
|
||||||
],
|
],
|
||||||
|
@ -34,7 +34,7 @@ logger.lifecycle("""
|
|||||||
*******************************************
|
*******************************************
|
||||||
""")
|
""")
|
||||||
|
|
||||||
var rootVersion by extra("2.6.5")
|
var rootVersion by extra("2.7.1")
|
||||||
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("")
|
||||||
@ -83,7 +83,7 @@ allprojects {
|
|||||||
}
|
}
|
||||||
|
|
||||||
applyCommonConfiguration()
|
applyCommonConfiguration()
|
||||||
val supportedVersions = listOf("1.16.5", "1.17.1", "1.18.2", "1.19.4", "1.20", "1.20.1")
|
val supportedVersions = listOf("1.17.1", "1.18.2", "1.19.4", "1.20", "1.20.1")
|
||||||
|
|
||||||
tasks {
|
tasks {
|
||||||
supportedVersions.forEach {
|
supportedVersions.forEach {
|
||||||
|
@ -15,7 +15,7 @@ fun Project.applyPaperweightAdapterConfiguration() {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
"implementation"(project(":worldedit-bukkit"))
|
"implementation"(project(":worldedit-bukkit"))
|
||||||
"implementation"(platform("com.intellectualsites.bom:bom-1.18.x:1.9"))
|
"implementation"(platform("com.intellectualsites.bom:bom-newest:1.33"))
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.named("assemble") {
|
tasks.named("assemble") {
|
||||||
|
@ -45,7 +45,7 @@ fun Project.applyCommonJavaConfiguration(sourcesJar: Boolean, banSlf4j: Boolean
|
|||||||
"testImplementation"("org.mockito:mockito-core:5.1.1")
|
"testImplementation"("org.mockito:mockito-core:5.1.1")
|
||||||
"testImplementation"("org.mockito:mockito-junit-jupiter:5.1.1")
|
"testImplementation"("org.mockito:mockito-junit-jupiter:5.1.1")
|
||||||
"testRuntimeOnly"("org.junit.jupiter:junit-jupiter-engine:5.9.2")
|
"testRuntimeOnly"("org.junit.jupiter:junit-jupiter-engine:5.9.2")
|
||||||
"implementation"(platform("com.intellectualsites.bom:bom-1.18.x:1.9"))
|
"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
|
||||||
|
@ -11,13 +11,13 @@ 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.2.5"
|
towny = "0.99.5.4"
|
||||||
|
|
||||||
# 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.9.3"
|
adventure = "4.14.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"
|
||||||
@ -30,7 +30,6 @@ jchronic = "0.2.4a"
|
|||||||
lz4-java = "1.8.0"
|
lz4-java = "1.8.0"
|
||||||
lz4-stream = "1.0.0"
|
lz4-stream = "1.0.0"
|
||||||
## Internal
|
## Internal
|
||||||
adventure-text-minimessage = "4.2.0-SNAPSHOT"
|
|
||||||
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"
|
||||||
@ -39,7 +38,7 @@ piston = "0.5.7"
|
|||||||
mockito = "5.4.0"
|
mockito = "5.4.0"
|
||||||
|
|
||||||
# Gradle plugins
|
# Gradle plugins
|
||||||
pluginyml = "0.5.3"
|
pluginyml = "0.6.0"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
# Minecraft expectations
|
# Minecraft expectations
|
||||||
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
3
gradle/wrapper/gradle-wrapper.properties
vendored
3
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,6 +1,7 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-bin.zip
|
||||||
networkTimeout=10000
|
networkTimeout=10000
|
||||||
|
validateDistributionUrl=true
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
5
gradlew
vendored
5
gradlew
vendored
@ -130,10 +130,13 @@ location of your Java installation."
|
|||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
JAVACMD=java
|
JAVACMD=java
|
||||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
if ! command -v java >/dev/null 2>&1
|
||||||
|
then
|
||||||
|
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
|
||||||
Please set the JAVA_HOME variable in your environment to match the
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
location of your Java installation."
|
location of your Java installation."
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Increase the maximum file descriptors if we can.
|
# Increase the maximum file descriptors if we can.
|
||||||
|
@ -445,7 +445,11 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
|
|
||||||
bitMask |= 1 << layer;
|
bitMask |= 1 << layer;
|
||||||
|
|
||||||
char[] setArr = set.load(layerNo);
|
// setArr is modified by PaperweightPlatformAdapter#newChunkSection. This is in order to write changes to
|
||||||
|
// this chunk GET when #updateGet is called. Future dords, please listen this time.
|
||||||
|
char[] tmp = set.load(layerNo);
|
||||||
|
char[] setArr = new char[tmp.length];
|
||||||
|
System.arraycopy(tmp, 0, setArr, 0, tmp.length);
|
||||||
|
|
||||||
// synchronise on internal section to avoid circular locking with a continuing edit if the chunk was
|
// synchronise on internal section to avoid circular locking with a continuing edit if the chunk was
|
||||||
// submitted to keep loaded internal chunks to queue target size.
|
// submitted to keep loaded internal chunks to queue target size.
|
||||||
|
@ -12,6 +12,7 @@ import com.fastasyncworldedit.core.util.ReflectionUtils;
|
|||||||
import com.fastasyncworldedit.core.util.TaskManager;
|
import com.fastasyncworldedit.core.util.TaskManager;
|
||||||
import com.mojang.datafixers.util.Either;
|
import com.mojang.datafixers.util.Either;
|
||||||
import com.sk89q.worldedit.bukkit.adapter.Refraction;
|
import com.sk89q.worldedit.bukkit.adapter.Refraction;
|
||||||
|
import com.sk89q.worldedit.internal.util.LogManagerCompat;
|
||||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||||
import com.sk89q.worldedit.world.biome.BiomeTypes;
|
import com.sk89q.worldedit.world.biome.BiomeTypes;
|
||||||
import com.sk89q.worldedit.world.block.BlockState;
|
import com.sk89q.worldedit.world.block.BlockState;
|
||||||
@ -48,6 +49,8 @@ import net.minecraft.world.level.chunk.Palette;
|
|||||||
import net.minecraft.world.level.chunk.PalettedContainer;
|
import net.minecraft.world.level.chunk.PalettedContainer;
|
||||||
import net.minecraft.world.level.gameevent.GameEventDispatcher;
|
import net.minecraft.world.level.gameevent.GameEventDispatcher;
|
||||||
import net.minecraft.world.level.gameevent.GameEventListener;
|
import net.minecraft.world.level.gameevent.GameEventListener;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.craftbukkit.v1_17_R1.CraftChunk;
|
import org.bukkit.craftbukkit.v1_17_R1.CraftChunk;
|
||||||
import sun.misc.Unsafe;
|
import sun.misc.Unsafe;
|
||||||
|
|
||||||
@ -61,6 +64,8 @@ import java.util.Locale;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.Semaphore;
|
import java.util.concurrent.Semaphore;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
@ -91,6 +96,8 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
|||||||
|
|
||||||
private static final Field fieldRemove;
|
private static final Field fieldRemove;
|
||||||
|
|
||||||
|
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
||||||
|
|
||||||
static {
|
static {
|
||||||
try {
|
try {
|
||||||
fieldBits = PalettedContainer.class.getDeclaredField(Refraction.pickName("bits", "l"));
|
fieldBits = PalettedContainer.class.getDeclaredField(Refraction.pickName("bits", "l"));
|
||||||
@ -225,7 +232,21 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
|||||||
}
|
}
|
||||||
CompletableFuture<org.bukkit.Chunk> future = serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true);
|
CompletableFuture<org.bukkit.Chunk> future = serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true);
|
||||||
try {
|
try {
|
||||||
CraftChunk chunk = (CraftChunk) future.get();
|
CraftChunk chunk;
|
||||||
|
try {
|
||||||
|
chunk = (CraftChunk) future.get(10, TimeUnit.SECONDS);
|
||||||
|
} catch (TimeoutException e) {
|
||||||
|
String world = serverLevel.getWorld().getName();
|
||||||
|
// We've already taken 10 seconds we can afford to wait a little here.
|
||||||
|
boolean loaded = TaskManager.taskManager().sync(() -> Bukkit.getWorld(world) != null);
|
||||||
|
if (loaded) {
|
||||||
|
LOGGER.warn("Chunk {},{} failed to load in 10 seconds in world {}. Retrying...", chunkX, chunkZ, world);
|
||||||
|
// Retry chunk load
|
||||||
|
chunk = (CraftChunk) serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true).get();
|
||||||
|
} else {
|
||||||
|
throw new UnsupportedOperationException("Cannot load chunk from unloaded world " + world + "!");
|
||||||
|
}
|
||||||
|
}
|
||||||
return chunk.getHandle();
|
return chunk.getHandle();
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
@ -491,7 +491,11 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
|
|
||||||
bitMask |= 1 << getSectionIndex;
|
bitMask |= 1 << getSectionIndex;
|
||||||
|
|
||||||
char[] setArr = set.load(layerNo);
|
// setArr is modified by PaperweightPlatformAdapter#newChunkSection. This is in order to write changes to
|
||||||
|
// this chunk GET when #updateGet is called. Future dords, please listen this time.
|
||||||
|
char[] tmp = set.load(layerNo);
|
||||||
|
char[] setArr = new char[tmp.length];
|
||||||
|
System.arraycopy(tmp, 0, setArr, 0, tmp.length);
|
||||||
|
|
||||||
// synchronise on internal section to avoid circular locking with a continuing edit if the chunk was
|
// synchronise on internal section to avoid circular locking with a continuing edit if the chunk was
|
||||||
// submitted to keep loaded internal chunks to queue target size.
|
// submitted to keep loaded internal chunks to queue target size.
|
||||||
|
@ -13,17 +13,16 @@ import com.mojang.datafixers.util.Either;
|
|||||||
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
|
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
|
||||||
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
|
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
|
||||||
import com.sk89q.worldedit.bukkit.adapter.Refraction;
|
import com.sk89q.worldedit.bukkit.adapter.Refraction;
|
||||||
|
import com.sk89q.worldedit.internal.util.LogManagerCompat;
|
||||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||||
import com.sk89q.worldedit.world.biome.BiomeTypes;
|
import com.sk89q.worldedit.world.biome.BiomeTypes;
|
||||||
import com.sk89q.worldedit.world.block.BlockState;
|
import com.sk89q.worldedit.world.block.BlockState;
|
||||||
import com.sk89q.worldedit.world.block.BlockTypesCache;
|
import com.sk89q.worldedit.world.block.BlockTypesCache;
|
||||||
import io.papermc.lib.PaperLib;
|
import io.papermc.lib.PaperLib;
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.core.Holder;
|
import net.minecraft.core.Holder;
|
||||||
import net.minecraft.core.IdMap;
|
import net.minecraft.core.IdMap;
|
||||||
import net.minecraft.core.Registry;
|
import net.minecraft.core.Registry;
|
||||||
import net.minecraft.core.SectionPos;
|
|
||||||
import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
|
import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
|
||||||
import net.minecraft.server.level.ChunkHolder;
|
import net.minecraft.server.level.ChunkHolder;
|
||||||
import net.minecraft.server.level.ChunkMap;
|
import net.minecraft.server.level.ChunkMap;
|
||||||
@ -41,7 +40,6 @@ import net.minecraft.world.level.LevelAccessor;
|
|||||||
import net.minecraft.world.level.biome.Biome;
|
import net.minecraft.world.level.biome.Biome;
|
||||||
import net.minecraft.world.level.block.Block;
|
import net.minecraft.world.level.block.Block;
|
||||||
import net.minecraft.world.level.block.Blocks;
|
import net.minecraft.world.level.block.Blocks;
|
||||||
import net.minecraft.world.level.block.EntityBlock;
|
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
import net.minecraft.world.level.chunk.GlobalPalette;
|
import net.minecraft.world.level.chunk.GlobalPalette;
|
||||||
import net.minecraft.world.level.chunk.HashMapPalette;
|
import net.minecraft.world.level.chunk.HashMapPalette;
|
||||||
@ -51,8 +49,8 @@ import net.minecraft.world.level.chunk.LinearPalette;
|
|||||||
import net.minecraft.world.level.chunk.Palette;
|
import net.minecraft.world.level.chunk.Palette;
|
||||||
import net.minecraft.world.level.chunk.PalettedContainer;
|
import net.minecraft.world.level.chunk.PalettedContainer;
|
||||||
import net.minecraft.world.level.chunk.SingleValuePalette;
|
import net.minecraft.world.level.chunk.SingleValuePalette;
|
||||||
import net.minecraft.world.level.gameevent.GameEventDispatcher;
|
import org.apache.logging.log4j.Logger;
|
||||||
import net.minecraft.world.level.gameevent.GameEventListener;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.craftbukkit.v1_18_R2.CraftChunk;
|
import org.bukkit.craftbukkit.v1_18_R2.CraftChunk;
|
||||||
import sun.misc.Unsafe;
|
import sun.misc.Unsafe;
|
||||||
|
|
||||||
@ -75,6 +73,8 @@ import java.util.Map;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.Semaphore;
|
import java.util.concurrent.Semaphore;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
public final class PaperweightPlatformAdapter extends NMSAdapter {
|
public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||||
@ -106,6 +106,8 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
|||||||
|
|
||||||
private static final Field fieldRemove;
|
private static final Field fieldRemove;
|
||||||
|
|
||||||
|
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
||||||
|
|
||||||
static {
|
static {
|
||||||
try {
|
try {
|
||||||
fieldData = PalettedContainer.class.getDeclaredField(Refraction.pickName("data", "d"));
|
fieldData = PalettedContainer.class.getDeclaredField(Refraction.pickName("data", "d"));
|
||||||
@ -253,7 +255,21 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
|||||||
}
|
}
|
||||||
CompletableFuture<org.bukkit.Chunk> future = serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true);
|
CompletableFuture<org.bukkit.Chunk> future = serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true);
|
||||||
try {
|
try {
|
||||||
CraftChunk chunk = (CraftChunk) future.get();
|
CraftChunk chunk;
|
||||||
|
try {
|
||||||
|
chunk = (CraftChunk) future.get(10, TimeUnit.SECONDS);
|
||||||
|
} catch (TimeoutException e) {
|
||||||
|
String world = serverLevel.getWorld().getName();
|
||||||
|
// We've already taken 10 seconds we can afford to wait a little here.
|
||||||
|
boolean loaded = TaskManager.taskManager().sync(() -> Bukkit.getWorld(world) != null);
|
||||||
|
if (loaded) {
|
||||||
|
LOGGER.warn("Chunk {},{} failed to load in 10 seconds in world {}. Retrying...", chunkX, chunkZ, world);
|
||||||
|
// Retry chunk load
|
||||||
|
chunk = (CraftChunk) serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true).get();
|
||||||
|
} else {
|
||||||
|
throw new UnsupportedOperationException("Cannot load chunk from unloaded world " + world + "!");
|
||||||
|
}
|
||||||
|
}
|
||||||
return chunk.getHandle();
|
return chunk.getHandle();
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
@ -332,6 +332,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<CompoundTag> getEntities() {
|
public Set<CompoundTag> getEntities() {
|
||||||
|
ensureLoaded(serverLevel, chunkX, chunkZ);
|
||||||
List<Entity> entities = PaperweightPlatformAdapter.getEntities(getChunk());
|
List<Entity> entities = PaperweightPlatformAdapter.getEntities(getChunk());
|
||||||
if (entities.isEmpty()) {
|
if (entities.isEmpty()) {
|
||||||
return Collections.emptySet();
|
return Collections.emptySet();
|
||||||
@ -490,7 +491,11 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
|
|
||||||
bitMask |= 1 << getSectionIndex;
|
bitMask |= 1 << getSectionIndex;
|
||||||
|
|
||||||
char[] setArr = set.load(layerNo);
|
// setArr is modified by PaperweightPlatformAdapter#newChunkSection. This is in order to write changes to
|
||||||
|
// this chunk GET when #updateGet is called. Future dords, please listen this time.
|
||||||
|
char[] tmp = set.load(layerNo);
|
||||||
|
char[] setArr = new char[tmp.length];
|
||||||
|
System.arraycopy(tmp, 0, setArr, 0, tmp.length);
|
||||||
|
|
||||||
// synchronise on internal section to avoid circular locking with a continuing edit if the chunk was
|
// synchronise on internal section to avoid circular locking with a continuing edit if the chunk was
|
||||||
// submitted to keep loaded internal chunks to queue target size.
|
// submitted to keep loaded internal chunks to queue target size.
|
||||||
|
@ -14,6 +14,7 @@ import com.mojang.datafixers.util.Either;
|
|||||||
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
|
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
|
||||||
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
|
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
|
||||||
import com.sk89q.worldedit.bukkit.adapter.Refraction;
|
import com.sk89q.worldedit.bukkit.adapter.Refraction;
|
||||||
|
import com.sk89q.worldedit.internal.util.LogManagerCompat;
|
||||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||||
import com.sk89q.worldedit.world.biome.BiomeTypes;
|
import com.sk89q.worldedit.world.biome.BiomeTypes;
|
||||||
import com.sk89q.worldedit.world.block.BlockState;
|
import com.sk89q.worldedit.world.block.BlockState;
|
||||||
@ -43,7 +44,6 @@ import net.minecraft.world.level.biome.Biome;
|
|||||||
import net.minecraft.world.level.block.Block;
|
import net.minecraft.world.level.block.Block;
|
||||||
import net.minecraft.world.level.block.Blocks;
|
import net.minecraft.world.level.block.Blocks;
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
|
||||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||||
import net.minecraft.world.level.chunk.GlobalPalette;
|
import net.minecraft.world.level.chunk.GlobalPalette;
|
||||||
import net.minecraft.world.level.chunk.HashMapPalette;
|
import net.minecraft.world.level.chunk.HashMapPalette;
|
||||||
@ -54,6 +54,8 @@ import net.minecraft.world.level.chunk.Palette;
|
|||||||
import net.minecraft.world.level.chunk.PalettedContainer;
|
import net.minecraft.world.level.chunk.PalettedContainer;
|
||||||
import net.minecraft.world.level.chunk.SingleValuePalette;
|
import net.minecraft.world.level.chunk.SingleValuePalette;
|
||||||
import net.minecraft.world.level.entity.PersistentEntitySectionManager;
|
import net.minecraft.world.level.entity.PersistentEntitySectionManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.craftbukkit.v1_19_R3.CraftChunk;
|
import org.bukkit.craftbukkit.v1_19_R3.CraftChunk;
|
||||||
import sun.misc.Unsafe;
|
import sun.misc.Unsafe;
|
||||||
|
|
||||||
@ -61,7 +63,6 @@ import javax.annotation.Nonnull;
|
|||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.lang.invoke.MethodHandle;
|
import java.lang.invoke.MethodHandle;
|
||||||
import java.lang.invoke.MethodHandles;
|
import java.lang.invoke.MethodHandles;
|
||||||
import java.lang.invoke.MethodType;
|
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
@ -77,9 +78,10 @@ import java.util.Map;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.Semaphore;
|
import java.util.concurrent.Semaphore;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
import static java.lang.invoke.MethodType.methodType;
|
|
||||||
import static net.minecraft.core.registries.Registries.BIOME;
|
import static net.minecraft.core.registries.Registries.BIOME;
|
||||||
|
|
||||||
public final class PaperweightPlatformAdapter extends NMSAdapter {
|
public final class PaperweightPlatformAdapter extends NMSAdapter {
|
||||||
@ -111,6 +113,8 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
|||||||
|
|
||||||
private static final Field fieldRemove;
|
private static final Field fieldRemove;
|
||||||
|
|
||||||
|
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
||||||
|
|
||||||
static final boolean POST_CHUNK_REWRITE;
|
static final boolean POST_CHUNK_REWRITE;
|
||||||
private static Method PAPER_CHUNK_GEN_ALL_ENTITIES;
|
private static Method PAPER_CHUNK_GEN_ALL_ENTITIES;
|
||||||
private static Field LEVEL_CHUNK_ENTITIES;
|
private static Field LEVEL_CHUNK_ENTITIES;
|
||||||
@ -287,7 +291,21 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
|||||||
}
|
}
|
||||||
CompletableFuture<org.bukkit.Chunk> future = serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true);
|
CompletableFuture<org.bukkit.Chunk> future = serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true);
|
||||||
try {
|
try {
|
||||||
CraftChunk chunk = (CraftChunk) future.get();
|
CraftChunk chunk;
|
||||||
|
try {
|
||||||
|
chunk = (CraftChunk) future.get(10, TimeUnit.SECONDS);
|
||||||
|
} catch (TimeoutException e) {
|
||||||
|
String world = serverLevel.getWorld().getName();
|
||||||
|
// We've already taken 10 seconds we can afford to wait a little here.
|
||||||
|
boolean loaded = TaskManager.taskManager().sync(() -> Bukkit.getWorld(world) != null);
|
||||||
|
if (loaded) {
|
||||||
|
LOGGER.warn("Chunk {},{} failed to load in 10 seconds in world {}. Retrying...", chunkX, chunkZ, world);
|
||||||
|
// Retry chunk load
|
||||||
|
chunk = (CraftChunk) serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true).get();
|
||||||
|
} else {
|
||||||
|
throw new UnsupportedOperationException("Cannot load chunk from unloaded world " + world + "!");
|
||||||
|
}
|
||||||
|
}
|
||||||
addTicket(serverLevel, chunkX, chunkZ);
|
addTicket(serverLevel, chunkX, chunkZ);
|
||||||
return (LevelChunk) chunk.getHandle(ChunkStatus.FULL);
|
return (LevelChunk) chunk.getHandle(ChunkStatus.FULL);
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
|
@ -310,6 +310,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<CompoundTag> getEntities() {
|
public Set<CompoundTag> getEntities() {
|
||||||
|
ensureLoaded(serverLevel, chunkX, chunkZ);
|
||||||
List<Entity> entities = PaperweightPlatformAdapter.getEntities(getChunk());
|
List<Entity> entities = PaperweightPlatformAdapter.getEntities(getChunk());
|
||||||
if (entities.isEmpty()) {
|
if (entities.isEmpty()) {
|
||||||
return Collections.emptySet();
|
return Collections.emptySet();
|
||||||
@ -468,7 +469,11 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
|
|
||||||
bitMask |= 1 << getSectionIndex;
|
bitMask |= 1 << getSectionIndex;
|
||||||
|
|
||||||
char[] setArr = set.load(layerNo);
|
// setArr is modified by PaperweightPlatformAdapter#newChunkSection. This is in order to write changes to
|
||||||
|
// this chunk GET when #updateGet is called. Future dords, please listen this time.
|
||||||
|
char[] tmp = set.load(layerNo);
|
||||||
|
char[] setArr = new char[tmp.length];
|
||||||
|
System.arraycopy(tmp, 0, setArr, 0, tmp.length);
|
||||||
|
|
||||||
// synchronise on internal section to avoid circular locking with a continuing edit if the chunk was
|
// synchronise on internal section to avoid circular locking with a continuing edit if the chunk was
|
||||||
// submitted to keep loaded internal chunks to queue target size.
|
// submitted to keep loaded internal chunks to queue target size.
|
||||||
|
@ -14,6 +14,7 @@ import com.mojang.datafixers.util.Either;
|
|||||||
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
|
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
|
||||||
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
|
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
|
||||||
import com.sk89q.worldedit.bukkit.adapter.Refraction;
|
import com.sk89q.worldedit.bukkit.adapter.Refraction;
|
||||||
|
import com.sk89q.worldedit.internal.util.LogManagerCompat;
|
||||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||||
import com.sk89q.worldedit.world.biome.BiomeTypes;
|
import com.sk89q.worldedit.world.biome.BiomeTypes;
|
||||||
import com.sk89q.worldedit.world.block.BlockState;
|
import com.sk89q.worldedit.world.block.BlockState;
|
||||||
@ -54,6 +55,8 @@ import net.minecraft.world.level.chunk.Palette;
|
|||||||
import net.minecraft.world.level.chunk.PalettedContainer;
|
import net.minecraft.world.level.chunk.PalettedContainer;
|
||||||
import net.minecraft.world.level.chunk.SingleValuePalette;
|
import net.minecraft.world.level.chunk.SingleValuePalette;
|
||||||
import net.minecraft.world.level.entity.PersistentEntitySectionManager;
|
import net.minecraft.world.level.entity.PersistentEntitySectionManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.craftbukkit.v1_20_R1.CraftChunk;
|
import org.bukkit.craftbukkit.v1_20_R1.CraftChunk;
|
||||||
import sun.misc.Unsafe;
|
import sun.misc.Unsafe;
|
||||||
|
|
||||||
@ -77,6 +80,8 @@ import java.util.Map;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.Semaphore;
|
import java.util.concurrent.Semaphore;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
import static java.lang.invoke.MethodType.methodType;
|
import static java.lang.invoke.MethodType.methodType;
|
||||||
@ -117,6 +122,8 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
|||||||
|
|
||||||
private static final Field fieldRemove;
|
private static final Field fieldRemove;
|
||||||
|
|
||||||
|
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
||||||
|
|
||||||
static final boolean POST_CHUNK_REWRITE;
|
static final boolean POST_CHUNK_REWRITE;
|
||||||
private static Method PAPER_CHUNK_GEN_ALL_ENTITIES;
|
private static Method PAPER_CHUNK_GEN_ALL_ENTITIES;
|
||||||
private static Field LEVEL_CHUNK_ENTITIES;
|
private static Field LEVEL_CHUNK_ENTITIES;
|
||||||
@ -307,7 +314,21 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
|||||||
}
|
}
|
||||||
CompletableFuture<org.bukkit.Chunk> future = serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true);
|
CompletableFuture<org.bukkit.Chunk> future = serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true);
|
||||||
try {
|
try {
|
||||||
CraftChunk chunk = (CraftChunk) future.get();
|
CraftChunk chunk;
|
||||||
|
try {
|
||||||
|
chunk = (CraftChunk) future.get(10, TimeUnit.SECONDS);
|
||||||
|
} catch (TimeoutException e) {
|
||||||
|
String world = serverLevel.getWorld().getName();
|
||||||
|
// We've already taken 10 seconds we can afford to wait a little here.
|
||||||
|
boolean loaded = TaskManager.taskManager().sync(() -> Bukkit.getWorld(world) != null);
|
||||||
|
if (loaded) {
|
||||||
|
LOGGER.warn("Chunk {},{} failed to load in 10 seconds in world {}. Retrying...", chunkX, chunkZ, world);
|
||||||
|
// Retry chunk load
|
||||||
|
chunk = (CraftChunk) serverLevel.getWorld().getChunkAtAsync(chunkX, chunkZ, true, true).get();
|
||||||
|
} else {
|
||||||
|
throw new UnsupportedOperationException("Cannot load chunk from unloaded world " + world + "!");
|
||||||
|
}
|
||||||
|
}
|
||||||
addTicket(serverLevel, chunkX, chunkZ);
|
addTicket(serverLevel, chunkX, chunkZ);
|
||||||
return (LevelChunk) CRAFT_CHUNK_GET_HANDLE.invoke(chunk);
|
return (LevelChunk) CRAFT_CHUNK_GET_HANDLE.invoke(chunk);
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
|
@ -3,7 +3,7 @@ import io.papermc.paperweight.userdev.attribute.Obfuscation
|
|||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
`java-library`
|
`java-library`
|
||||||
id("com.modrinth.minotaur") version "2.8.1"
|
id("com.modrinth.minotaur") version "2.8.2"
|
||||||
}
|
}
|
||||||
|
|
||||||
project.description = "Bukkit"
|
project.description = "Bukkit"
|
||||||
@ -101,8 +101,8 @@ 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.plotsquared:PlotSquared-Bukkit") { isTransitive = false }
|
compileOnly("com.intellectualsites.plotsquared:plotsquared-bukkit") { isTransitive = false }
|
||||||
compileOnly("com.plotsquared:PlotSquared-Core") { isTransitive = false }
|
compileOnly("com.intellectualsites.plotsquared:plotsquared-core") { isTransitive = false }
|
||||||
|
|
||||||
// Third party
|
// Third party
|
||||||
implementation("io.papermc:paperlib")
|
implementation("io.papermc:paperlib")
|
||||||
@ -183,7 +183,7 @@ tasks.named<ShadowJar>("shadowJar") {
|
|||||||
include(dependency("org.lz4:lz4-java:1.8.0"))
|
include(dependency("org.lz4:lz4-java:1.8.0"))
|
||||||
}
|
}
|
||||||
relocate("net.kyori", "com.fastasyncworldedit.core.adventure") {
|
relocate("net.kyori", "com.fastasyncworldedit.core.adventure") {
|
||||||
include(dependency("net.kyori:adventure-nbt:4.9.3"))
|
include(dependency("net.kyori:adventure-nbt:4.14.0"))
|
||||||
}
|
}
|
||||||
relocate("com.zaxxer", "com.fastasyncworldedit.core.math") {
|
relocate("com.zaxxer", "com.fastasyncworldedit.core.math") {
|
||||||
include(dependency("com.zaxxer:SparseBitSet:1.2"))
|
include(dependency("com.zaxxer:SparseBitSet:1.2"))
|
||||||
|
@ -108,6 +108,14 @@ public class FaweBukkit implements IFawe, Listener {
|
|||||||
if (version.isEqualOrHigherThan(MinecraftVersion.CAVES_18) && Settings.settings().HISTORY.SMALL_EDITS) {
|
if (version.isEqualOrHigherThan(MinecraftVersion.CAVES_18) && Settings.settings().HISTORY.SMALL_EDITS) {
|
||||||
LOGGER.warn("Small-edits enabled (maximum y range of 0 -> 256) with 1.18 world heights. Are you sure?");
|
LOGGER.warn("Small-edits enabled (maximum y range of 0 -> 256) with 1.18 world heights. Are you sure?");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (version.isEqualOrLowerThan(MinecraftVersion.ONE_DOT_SIXTEEN_EOL)) {
|
||||||
|
LOGGER.warn("You are running Minecraft 1.16.5. This version has been released over two years ago (January 2021).");
|
||||||
|
LOGGER.warn("FastAsyncWorldEdit will stop operating on this version in the near future.");
|
||||||
|
LOGGER.warn("Neither Mojang, nor Spigot or other software vendors support this version anymore." +
|
||||||
|
"Please update your server to a newer version of Minecraft (1.20+) to continue receiving updates and " +
|
||||||
|
"support.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -317,18 +325,9 @@ public class FaweBukkit implements IFawe, Listener {
|
|||||||
if (plotSquared == null) {
|
if (plotSquared == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (PlotSquared.get().getVersion().version[0] == 6) {
|
if (PlotSquared.get().getVersion().version[0] == 7) {
|
||||||
WEManager.weManager().addManager(new com.fastasyncworldedit.bukkit.regions.plotsquared.PlotSquaredFeature());
|
WEManager.weManager().addManager(new com.fastasyncworldedit.bukkit.regions.plotsquared.PlotSquaredFeature());
|
||||||
LOGGER.info("Plugin 'PlotSquared' v6 found. Using it now.");
|
LOGGER.info("Plugin 'PlotSquared' v7 found. Using it now.");
|
||||||
} else if (PlotSquared.get().getVersion().version[0] == 7) {
|
|
||||||
WEManager.weManager().addManager(new com.fastasyncworldedit.bukkit.regions.plotsquared.PlotSquaredFeature());
|
|
||||||
LOGGER.error("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
|
|
||||||
LOGGER.error("!! !!");
|
|
||||||
LOGGER.error("!! ERROR: PlotSquared v7 found. This FAWE version does not support PlotSquared V7 !!");
|
|
||||||
LOGGER.error("!! Follow the instructions when notified of v7 release candidates and use FAWE from !!");
|
|
||||||
LOGGER.error("!! https://ci.athion.net/job/FastAsyncWorldEdit-Pull-Requests/view/change-requests/job/PR-2075/ !!");
|
|
||||||
LOGGER.error("!! !!");
|
|
||||||
LOGGER.error("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
|
|
||||||
} else {
|
} else {
|
||||||
LOGGER.error("Incompatible version of PlotSquared found. Please use PlotSquared v6.");
|
LOGGER.error("Incompatible version of PlotSquared found. Please use PlotSquared v6.");
|
||||||
LOGGER.info("https://www.spigotmc.org/resources/77506/");
|
LOGGER.info("https://www.spigotmc.org/resources/77506/");
|
||||||
|
@ -30,8 +30,7 @@ public class NMSAdapter implements FAWEPlatformAdapterImpl {
|
|||||||
ordinal = BlockTypesCache.ReservedIDs.AIR;
|
ordinal = BlockTypesCache.ReservedIDs.AIR;
|
||||||
nonAir--;
|
nonAir--;
|
||||||
}
|
}
|
||||||
case BlockTypesCache.ReservedIDs.AIR, BlockTypesCache.ReservedIDs.CAVE_AIR, BlockTypesCache.ReservedIDs.VOID_AIR ->
|
case BlockTypesCache.ReservedIDs.AIR, BlockTypesCache.ReservedIDs.CAVE_AIR, BlockTypesCache.ReservedIDs.VOID_AIR -> nonAir--;
|
||||||
nonAir--;
|
|
||||||
}
|
}
|
||||||
int palette = blockToPalette[ordinal];
|
int palette = blockToPalette[ordinal];
|
||||||
if (palette == Integer.MAX_VALUE) {
|
if (palette == Integer.MAX_VALUE) {
|
||||||
@ -74,8 +73,6 @@ public class NMSAdapter implements FAWEPlatformAdapterImpl {
|
|||||||
CachedBukkitAdapter adapter,
|
CachedBukkitAdapter adapter,
|
||||||
short[] nonEmptyBlockCount
|
short[] nonEmptyBlockCount
|
||||||
) {
|
) {
|
||||||
// Write to new array to avoid editing SET array
|
|
||||||
char[] copy = new char[set.length];
|
|
||||||
short nonAir = 4096;
|
short nonAir = 4096;
|
||||||
int num_palette = 0;
|
int num_palette = 0;
|
||||||
char[] getArr = null;
|
char[] getArr = null;
|
||||||
@ -86,19 +83,23 @@ public class NMSAdapter implements FAWEPlatformAdapterImpl {
|
|||||||
if (getArr == null) {
|
if (getArr == null) {
|
||||||
getArr = get.apply(layer);
|
getArr = get.apply(layer);
|
||||||
}
|
}
|
||||||
switch (ordinal = getArr[i]) {
|
// write to set array as this should be a copied array, and will be important when the changes are written
|
||||||
|
// to the GET chunk cached by FAWE. Future dords, actually read this comment please.
|
||||||
|
set[i] = switch (ordinal = getArr[i]) {
|
||||||
case BlockTypesCache.ReservedIDs.__RESERVED__ -> {
|
case BlockTypesCache.ReservedIDs.__RESERVED__ -> {
|
||||||
nonAir--;
|
nonAir--;
|
||||||
ordinal = BlockTypesCache.ReservedIDs.AIR;
|
yield (ordinal = BlockTypesCache.ReservedIDs.AIR);
|
||||||
}
|
}
|
||||||
case BlockTypesCache.ReservedIDs.AIR, BlockTypesCache.ReservedIDs.CAVE_AIR, BlockTypesCache.ReservedIDs.VOID_AIR ->
|
case BlockTypesCache.ReservedIDs.AIR, BlockTypesCache.ReservedIDs.CAVE_AIR,
|
||||||
|
BlockTypesCache.ReservedIDs.VOID_AIR -> {
|
||||||
nonAir--;
|
nonAir--;
|
||||||
|
yield ordinal;
|
||||||
}
|
}
|
||||||
|
default -> ordinal;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
case BlockTypesCache.ReservedIDs.AIR, BlockTypesCache.ReservedIDs.CAVE_AIR, BlockTypesCache.ReservedIDs.VOID_AIR ->
|
case BlockTypesCache.ReservedIDs.AIR, BlockTypesCache.ReservedIDs.CAVE_AIR, BlockTypesCache.ReservedIDs.VOID_AIR -> nonAir--;
|
||||||
nonAir--;
|
|
||||||
}
|
}
|
||||||
copy[i] = ordinal;
|
|
||||||
int palette = blockToPalette[ordinal];
|
int palette = blockToPalette[ordinal];
|
||||||
if (palette == Integer.MAX_VALUE) {
|
if (palette == Integer.MAX_VALUE) {
|
||||||
blockToPalette[ordinal] = num_palette;
|
blockToPalette[ordinal] = num_palette;
|
||||||
@ -116,7 +117,7 @@ public class NMSAdapter implements FAWEPlatformAdapterImpl {
|
|||||||
System.arraycopy(adapter.getOrdinalToIbdID(), 0, blockToPalette, 0, adapter.getOrdinalToIbdID().length);
|
System.arraycopy(adapter.getOrdinalToIbdID(), 0, blockToPalette, 0, adapter.getOrdinalToIbdID().length);
|
||||||
}
|
}
|
||||||
for (int i = 0; i < 4096; i++) {
|
for (int i = 0; i < 4096; i++) {
|
||||||
char ordinal = copy[i];
|
char ordinal = set[i];
|
||||||
if (ordinal == BlockTypesCache.ReservedIDs.__RESERVED__) {
|
if (ordinal == BlockTypesCache.ReservedIDs.__RESERVED__) {
|
||||||
LOGGER.error("Empty (__RESERVED__) ordinal given where not expected, default to air.");
|
LOGGER.error("Empty (__RESERVED__) ordinal given where not expected, default to air.");
|
||||||
ordinal = BlockTypesCache.ReservedIDs.AIR;
|
ordinal = BlockTypesCache.ReservedIDs.AIR;
|
||||||
|
@ -171,18 +171,17 @@ public class FaweDelegateRegionManager {
|
|||||||
.limitUnlimited()
|
.limitUnlimited()
|
||||||
.changeSetNull()
|
.changeSetNull()
|
||||||
.build();
|
.build();
|
||||||
File schematicFile = new File(hybridPlotWorld.getRoot(), "plot.schem");
|
File schematicFile = new File(hybridPlotWorld.getSchematicRoot(), "plot.schem");
|
||||||
if (!schematicFile.exists()) {
|
if (!schematicFile.exists()) {
|
||||||
schematicFile = new File(hybridPlotWorld.getRoot(), "plot.schematic");
|
schematicFile = new File(hybridPlotWorld.getSchematicRoot(), "plot.schematic");
|
||||||
}
|
}
|
||||||
BlockVector3 to = plot.getBottomAbs().getBlockVector3().withY(Settings.Schematics.PASTE_ON_TOP
|
BlockVector3 to = plot.getBottomAbs().getBlockVector3().withY(hybridPlotWorld.getPlotYStart());
|
||||||
? hybridPlotWorld.SCHEM_Y
|
|
||||||
: hybridPlotWorld.getMinBuildHeight());
|
|
||||||
try {
|
try {
|
||||||
Clipboard clip = ClipboardFormats
|
Clipboard clip = ClipboardFormats
|
||||||
.findByFile(schematicFile)
|
.findByFile(schematicFile)
|
||||||
.getReader(new FileInputStream(schematicFile))
|
.getReader(new FileInputStream(schematicFile))
|
||||||
.read();
|
.read();
|
||||||
|
clip.setOrigin(clip.getRegion().getMinimumPoint());
|
||||||
clip.paste(scheditsession, to, true, true, true);
|
clip.paste(scheditsession, to, true, true, true);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
@ -214,7 +213,7 @@ public class FaweDelegateRegionManager {
|
|||||||
) {
|
) {
|
||||||
TaskManager.taskManager().async(() -> {
|
TaskManager.taskManager().async(() -> {
|
||||||
synchronized (FaweDelegateRegionManager.class) {
|
synchronized (FaweDelegateRegionManager.class) {
|
||||||
//todo because of the following code this should proably be in the Bukkit module
|
//todo because of the following code this should probably be in the Bukkit module
|
||||||
World pos1World = BukkitAdapter.adapt(getWorld(pos1.getWorldName()));
|
World pos1World = BukkitAdapter.adapt(getWorld(pos1.getWorldName()));
|
||||||
World pos3World = BukkitAdapter.adapt(getWorld(swapPos.getWorldName()));
|
World pos3World = BukkitAdapter.adapt(getWorld(swapPos.getWorldName()));
|
||||||
EditSession sessionA = WorldEdit.getInstance().newEditSessionBuilder().world(pos1World)
|
EditSession sessionA = WorldEdit.getInstance().newEditSessionBuilder().world(pos1World)
|
||||||
|
@ -152,6 +152,7 @@ public class FaweDelegateSchematicHandler {
|
|||||||
final BlockVector3 to = BlockVector3
|
final BlockVector3 to = BlockVector3
|
||||||
.at(region.getMinimumPoint().getX() + xOffset, y_offset_actual, region.getMinimumPoint().getZ() + zOffset);
|
.at(region.getMinimumPoint().getX() + xOffset, y_offset_actual, region.getMinimumPoint().getZ() + zOffset);
|
||||||
final Clipboard clipboard = schematic.getClipboard();
|
final Clipboard clipboard = schematic.getClipboard();
|
||||||
|
clipboard.setOrigin(clipboard.getRegion().getMinimumPoint());
|
||||||
clipboard.paste(editSession, to, true, false, true);
|
clipboard.paste(editSession, to, true, false, true);
|
||||||
if (whenDone != null) {
|
if (whenDone != null) {
|
||||||
whenDone.value = true;
|
whenDone.value = true;
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
package com.fastasyncworldedit.bukkit.regions.plotsquared;
|
package com.fastasyncworldedit.bukkit.regions.plotsquared;
|
||||||
|
|
||||||
import com.fastasyncworldedit.core.util.TaskManager;
|
import com.fastasyncworldedit.core.util.TaskManager;
|
||||||
|
import net.kyori.adventure.text.Component;
|
||||||
|
import net.kyori.adventure.text.minimessage.tag.Tag;
|
||||||
|
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
|
||||||
import com.plotsquared.core.PlotSquared;
|
import com.plotsquared.core.PlotSquared;
|
||||||
import com.plotsquared.core.command.CommandCategory;
|
import com.plotsquared.core.command.CommandCategory;
|
||||||
import com.plotsquared.core.command.CommandDeclaration;
|
import com.plotsquared.core.command.CommandDeclaration;
|
||||||
import com.plotsquared.core.command.RequiredType;
|
import com.plotsquared.core.command.RequiredType;
|
||||||
import com.plotsquared.core.command.SubCommand;
|
import com.plotsquared.core.command.SubCommand;
|
||||||
import com.plotsquared.core.configuration.caption.StaticCaption;
|
import com.plotsquared.core.configuration.caption.StaticCaption;
|
||||||
import com.plotsquared.core.configuration.caption.Templates;
|
|
||||||
import com.plotsquared.core.configuration.caption.TranslatableCaption;
|
import com.plotsquared.core.configuration.caption.TranslatableCaption;
|
||||||
import com.plotsquared.core.player.PlotPlayer;
|
import com.plotsquared.core.player.PlotPlayer;
|
||||||
|
|
||||||
@ -33,7 +35,7 @@ public class FaweTrim extends SubCommand {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!PlotSquared.platform().worldUtil().isWorld(strings[0])) {
|
if (!PlotSquared.platform().worldUtil().isWorld(strings[0])) {
|
||||||
plotPlayer.sendMessage(TranslatableCaption.of("errors.not_valid_plot_world"), Templates.of("value", strings[0]));
|
plotPlayer.sendMessage(TranslatableCaption.of("errors.not_valid_plot_world"), TagResolver.resolver("value", Tag.inserting(Component.text(strings[0]))));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
ran = true;
|
ran = true;
|
||||||
|
@ -6,11 +6,9 @@ import com.plotsquared.core.command.CommandCategory;
|
|||||||
import com.plotsquared.core.command.CommandDeclaration;
|
import com.plotsquared.core.command.CommandDeclaration;
|
||||||
import com.plotsquared.core.command.MainCommand;
|
import com.plotsquared.core.command.MainCommand;
|
||||||
import com.plotsquared.core.command.RequiredType;
|
import com.plotsquared.core.command.RequiredType;
|
||||||
import com.plotsquared.core.configuration.caption.Templates;
|
|
||||||
import com.plotsquared.core.configuration.caption.TranslatableCaption;
|
import com.plotsquared.core.configuration.caption.TranslatableCaption;
|
||||||
import com.plotsquared.core.player.PlotPlayer;
|
import com.plotsquared.core.player.PlotPlayer;
|
||||||
import com.plotsquared.core.plot.Plot;
|
import com.plotsquared.core.plot.Plot;
|
||||||
import com.plotsquared.core.util.Permissions;
|
|
||||||
import com.plotsquared.core.util.StringMan;
|
import com.plotsquared.core.util.StringMan;
|
||||||
import com.plotsquared.core.util.task.RunnableVal2;
|
import com.plotsquared.core.util.task.RunnableVal2;
|
||||||
import com.plotsquared.core.util.task.RunnableVal3;
|
import com.plotsquared.core.util.task.RunnableVal3;
|
||||||
@ -24,6 +22,9 @@ import com.sk89q.worldedit.world.biome.BiomeType;
|
|||||||
import com.sk89q.worldedit.world.biome.BiomeTypes;
|
import com.sk89q.worldedit.world.biome.BiomeTypes;
|
||||||
import com.sk89q.worldedit.world.biome.Biomes;
|
import com.sk89q.worldedit.world.biome.Biomes;
|
||||||
import com.sk89q.worldedit.world.registry.BiomeRegistry;
|
import com.sk89q.worldedit.world.registry.BiomeRegistry;
|
||||||
|
import net.kyori.adventure.text.Component;
|
||||||
|
import net.kyori.adventure.text.minimessage.tag.Tag;
|
||||||
|
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@ -56,7 +57,7 @@ public class PlotSetBiome extends Command {
|
|||||||
) throws CommandException {
|
) throws CommandException {
|
||||||
final Plot plot = check(player.getCurrentPlot(), TranslatableCaption.of("errors.not_in_plot"));
|
final Plot plot = check(player.getCurrentPlot(), TranslatableCaption.of("errors.not_in_plot"));
|
||||||
checkTrue(
|
checkTrue(
|
||||||
plot.isOwner(player.getUUID()) || Permissions.hasPermission(player, "plots.admin.command.generatebiome"),
|
plot.isOwner(player.getUUID()) || player.hasPermission("plots.admin.command.generatebiome"),
|
||||||
TranslatableCaption.of("permission.no_plot_perms")
|
TranslatableCaption.of("permission.no_plot_perms")
|
||||||
);
|
);
|
||||||
if (plot.getRunning() != 0) {
|
if (plot.getRunning() != 0) {
|
||||||
@ -64,7 +65,7 @@ public class PlotSetBiome extends Command {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
checkTrue(args.length == 1, TranslatableCaption.of("commandconfig.command_syntax"),
|
checkTrue(args.length == 1, TranslatableCaption.of("commandconfig.command_syntax"),
|
||||||
Templates.of("value", getUsage())
|
TagResolver.resolver("value", Tag.inserting(Component.text(getUsage())))
|
||||||
);
|
);
|
||||||
final Set<CuboidRegion> regions = plot.getRegions();
|
final Set<CuboidRegion> regions = plot.getRegions();
|
||||||
BiomeRegistry biomeRegistry =
|
BiomeRegistry biomeRegistry =
|
||||||
@ -80,7 +81,7 @@ public class PlotSetBiome extends Command {
|
|||||||
player.sendMessage(TranslatableCaption.of("biome.need_biome"));
|
player.sendMessage(TranslatableCaption.of("biome.need_biome"));
|
||||||
player.sendMessage(
|
player.sendMessage(
|
||||||
TranslatableCaption.of("commandconfig.subcommand_set_options_header"),
|
TranslatableCaption.of("commandconfig.subcommand_set_options_header"),
|
||||||
Templates.of("values", biomes)
|
TagResolver.resolver("value", Tag.inserting(Component.text(biomes)))
|
||||||
);
|
);
|
||||||
return CompletableFuture.completedFuture(false);
|
return CompletableFuture.completedFuture(false);
|
||||||
}
|
}
|
||||||
|
@ -23,9 +23,11 @@ import com.sk89q.worldedit.world.World;
|
|||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@ -40,7 +42,7 @@ public class PlotSquaredFeature extends FaweMaskManager {
|
|||||||
if (Settings.FAWE_Components.FAWE_HOOK) {
|
if (Settings.FAWE_Components.FAWE_HOOK) {
|
||||||
Settings.Enabled_Components.WORLDEDIT_RESTRICTIONS = false;
|
Settings.Enabled_Components.WORLDEDIT_RESTRICTIONS = false;
|
||||||
if (Settings.PLATFORM.toLowerCase(Locale.ROOT).startsWith("bukkit")) {
|
if (Settings.PLATFORM.toLowerCase(Locale.ROOT).startsWith("bukkit")) {
|
||||||
new FaweTrim();
|
// new FaweTrim();
|
||||||
}
|
}
|
||||||
// TODO: revisit this later on
|
// TODO: revisit this later on
|
||||||
/*
|
/*
|
||||||
@ -192,6 +194,10 @@ public class PlotSquaredFeature extends FaweMaskManager {
|
|||||||
maskedRegion = new RegionIntersection(world, weRegions);
|
maskedRegion = new RegionIntersection(world, weRegions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (plot == null) {
|
||||||
|
return new FaweMask(maskedRegion);
|
||||||
|
}
|
||||||
|
|
||||||
return new PlotSquaredMask(maskedRegion, finalPlot);
|
return new PlotSquaredMask(maskedRegion, finalPlot);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -201,9 +207,9 @@ public class PlotSquaredFeature extends FaweMaskManager {
|
|||||||
private final WeakReference<Set<Plot>> connectedPlots;
|
private final WeakReference<Set<Plot>> connectedPlots;
|
||||||
private final boolean singlePlot;
|
private final boolean singlePlot;
|
||||||
|
|
||||||
private PlotSquaredMask(Region region, Plot plot) {
|
private PlotSquaredMask(@Nonnull Region region, @Nonnull Plot plot) {
|
||||||
super(region);
|
super(region);
|
||||||
this.plot = plot;
|
this.plot = Objects.requireNonNull(plot);
|
||||||
Set<Plot> connected = plot.getConnectedPlots();
|
Set<Plot> connected = plot.getConnectedPlots();
|
||||||
connectedPlots = new WeakReference<>(connected);
|
connectedPlots = new WeakReference<>(connected);
|
||||||
singlePlot = connected.size() == 1;
|
singlePlot = connected.size() == 1;
|
||||||
@ -211,8 +217,9 @@ public class PlotSquaredFeature extends FaweMaskManager {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isValid(Player player, MaskType type, boolean notify) {
|
public boolean isValid(Player player, MaskType type, boolean notify) {
|
||||||
if ((!connectedPlots.refersTo(plot.getConnectedPlots()) && !singlePlot) || (Settings.Done.RESTRICT_BUILDING && DoneFlag.isDone(
|
if ((!connectedPlots.refersTo(plot.getConnectedPlots()) && (!singlePlot || plot
|
||||||
plot))) {
|
.getConnectedPlots()
|
||||||
|
.size() > 1)) || (Settings.Done.RESTRICT_BUILDING && DoneFlag.isDone(plot))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return isAllowed(player, plot, type, notify);
|
return isAllowed(player, plot, type, notify);
|
||||||
|
@ -13,6 +13,7 @@ import java.util.regex.Pattern;
|
|||||||
public class MinecraftVersion implements Comparable<MinecraftVersion> {
|
public class MinecraftVersion implements Comparable<MinecraftVersion> {
|
||||||
|
|
||||||
public static final MinecraftVersion NETHER = new MinecraftVersion(1, 16);
|
public static final MinecraftVersion NETHER = new MinecraftVersion(1, 16);
|
||||||
|
public static final MinecraftVersion ONE_DOT_SIXTEEN_EOL = new MinecraftVersion(1, 16, 5);
|
||||||
public static final MinecraftVersion CAVES_17 = new MinecraftVersion(1, 17);
|
public static final MinecraftVersion CAVES_17 = new MinecraftVersion(1, 17);
|
||||||
public static final MinecraftVersion CAVES_18 = new MinecraftVersion(1, 18);
|
public static final MinecraftVersion CAVES_18 = new MinecraftVersion(1, 18);
|
||||||
private static MinecraftVersion current = null;
|
private static MinecraftVersion current = null;
|
||||||
|
@ -19,6 +19,8 @@
|
|||||||
|
|
||||||
package com.sk89q.wepif;
|
package com.sk89q.wepif;
|
||||||
|
|
||||||
|
import com.destroystokyo.paper.profile.PlayerProfile;
|
||||||
|
import org.bukkit.BanEntry;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
import org.bukkit.OfflinePlayer;
|
import org.bukkit.OfflinePlayer;
|
||||||
@ -30,10 +32,11 @@ import org.bukkit.permissions.Permission;
|
|||||||
import org.bukkit.permissions.PermissionAttachment;
|
import org.bukkit.permissions.PermissionAttachment;
|
||||||
import org.bukkit.permissions.PermissionAttachmentInfo;
|
import org.bukkit.permissions.PermissionAttachmentInfo;
|
||||||
import org.bukkit.plugin.Plugin;
|
import org.bukkit.plugin.Plugin;
|
||||||
import org.bukkit.profile.PlayerProfile;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
@ -158,6 +161,15 @@ public class TestOfflinePermissible implements OfflinePlayer, Permissible {
|
|||||||
throw new UnsupportedOperationException("Not supported yet.");
|
throw new UnsupportedOperationException("Not supported yet.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable BanEntry<org.bukkit.profile.PlayerProfile> ban(
|
||||||
|
@Nullable final String reason,
|
||||||
|
@Nullable final Date expires,
|
||||||
|
@Nullable final String source
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isWhitelisted() {
|
public boolean isWhitelisted() {
|
||||||
throw new UnsupportedOperationException("Not supported yet.");
|
throw new UnsupportedOperationException("Not supported yet.");
|
||||||
@ -323,4 +335,9 @@ public class TestOfflinePermissible implements OfflinePlayer, Permissible {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable Location getLastDeathLocation() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -28,14 +28,13 @@ dependencies {
|
|||||||
implementation("com.google.code.gson:gson")
|
implementation("com.google.code.gson:gson")
|
||||||
|
|
||||||
// Platform expectations
|
// Platform expectations
|
||||||
// TODO update bom-newest
|
implementation("org.yaml:snakeyaml")
|
||||||
implementation("org.yaml:snakeyaml:2.0")
|
|
||||||
|
|
||||||
// Logging
|
// Logging
|
||||||
implementation("org.apache.logging.log4j:log4j-api")
|
implementation("org.apache.logging.log4j:log4j-api")
|
||||||
|
|
||||||
// Plugins
|
// Plugins
|
||||||
compileOnly("com.plotsquared:PlotSquared-Core") { isTransitive = false }
|
compileOnly("com.intellectualsites.plotsquared:plotsquared-core") { 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)
|
||||||
|
@ -134,15 +134,29 @@ public class MobSpawnerBlock extends BaseBlock {
|
|||||||
values.put("MaxNearbyEntities", new ShortTag(maxNearbyEntities));
|
values.put("MaxNearbyEntities", new ShortTag(maxNearbyEntities));
|
||||||
values.put("RequiredPlayerRange", new ShortTag(requiredPlayerRange));
|
values.put("RequiredPlayerRange", new ShortTag(requiredPlayerRange));
|
||||||
if (spawnData == null) {
|
if (spawnData == null) {
|
||||||
values.put("SpawnData", new CompoundTag(ImmutableMap.of("id", new StringTag(mobType))));
|
values.put(
|
||||||
|
"SpawnData",
|
||||||
|
new CompoundTag(ImmutableMap.of("entity", new CompoundTag(ImmutableMap.of("id", new StringTag(mobType)))))
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
values.put("SpawnData", new CompoundTag(spawnData.getValue()));
|
values.put("SpawnData", new CompoundTag(spawnData.getValue()));
|
||||||
}
|
}
|
||||||
if (spawnPotentials == null) {
|
if (spawnPotentials == null) {
|
||||||
values.put("SpawnPotentials", new ListTag(CompoundTag.class, ImmutableList.of(
|
values.put(
|
||||||
new CompoundTag(ImmutableMap.of("Weight", new IntTag(1), "Entity",
|
"SpawnPotentials",
|
||||||
|
new ListTag(
|
||||||
|
CompoundTag.class,
|
||||||
|
ImmutableList.of(new CompoundTag(ImmutableMap.of(
|
||||||
|
"weight",
|
||||||
|
new IntTag(1),
|
||||||
|
"data",
|
||||||
|
new CompoundTag(ImmutableMap.of(
|
||||||
|
"entity",
|
||||||
new CompoundTag(ImmutableMap.of("id", new StringTag(mobType)))
|
new CompoundTag(ImmutableMap.of("id", new StringTag(mobType)))
|
||||||
)))));
|
))
|
||||||
|
)))
|
||||||
|
)
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
values.put("SpawnPotentials", new ListTag(CompoundTag.class, spawnPotentials.getValue()));
|
values.put("SpawnPotentials", new ListTag(CompoundTag.class, spawnPotentials.getValue()));
|
||||||
}
|
}
|
||||||
|
@ -449,7 +449,6 @@ public class Fawe {
|
|||||||
* @return Executor used for clipboard IO if clipboard on disk is enabled or null
|
* @return Executor used for clipboard IO if clipboard on disk is enabled or null
|
||||||
* @since 2.6.2
|
* @since 2.6.2
|
||||||
*/
|
*/
|
||||||
@Nullable
|
|
||||||
public KeyQueuedExecutorService<UUID> getClipboardExecutor() {
|
public KeyQueuedExecutorService<UUID> getClipboardExecutor() {
|
||||||
return this.clipboardExecutor;
|
return this.clipboardExecutor;
|
||||||
}
|
}
|
||||||
|
@ -22,9 +22,27 @@ import java.util.List;
|
|||||||
public class CommandBrush implements Brush {
|
public class CommandBrush implements Brush {
|
||||||
|
|
||||||
private final String command;
|
private final String command;
|
||||||
|
private final boolean print;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* New instance
|
||||||
|
*
|
||||||
|
* @deprecated Use {@link CommandBrush#CommandBrush(String, boolean)}
|
||||||
|
*/
|
||||||
|
@Deprecated(forRemoval = true)
|
||||||
public CommandBrush(String command) {
|
public CommandBrush(String command) {
|
||||||
this.command = command.charAt(0) == '/' ? "/" + command : command;
|
this(command, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* New instance
|
||||||
|
*
|
||||||
|
* @param command command to run, or commands split by ';'
|
||||||
|
* @param print if output should be printed to the actor for the run commands
|
||||||
|
*/
|
||||||
|
public CommandBrush(String command, boolean print) {
|
||||||
|
this.command = command;
|
||||||
|
this.print = print;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -36,7 +54,7 @@ public class CommandBrush implements Brush {
|
|||||||
position.subtract(radius, radius, radius),
|
position.subtract(radius, radius, radius),
|
||||||
position.add(radius, radius, radius)
|
position.add(radius, radius, radius)
|
||||||
);
|
);
|
||||||
String replaced = command.replace("{x}", position.getBlockX() + "")
|
String replaced = command.replace("{x}", Integer.toString(position.getBlockX()))
|
||||||
.replace("{y}", Integer.toString(position.getBlockY()))
|
.replace("{y}", Integer.toString(position.getBlockY()))
|
||||||
.replace("{z}", Integer.toString(position.getBlockZ()))
|
.replace("{z}", Integer.toString(position.getBlockZ()))
|
||||||
.replace("{world}", editSession.getWorld().getName())
|
.replace("{world}", editSession.getWorld().getName())
|
||||||
@ -46,21 +64,22 @@ public class CommandBrush implements Brush {
|
|||||||
if (!(actor instanceof Player player)) {
|
if (!(actor instanceof Player player)) {
|
||||||
throw FaweCache.PLAYER_ONLY;
|
throw FaweCache.PLAYER_ONLY;
|
||||||
}
|
}
|
||||||
//Use max world height to allow full coverage of the world height
|
|
||||||
Location face = player.getBlockTraceFace(editSession.getWorld().getMaxY(), true);
|
|
||||||
if (face == null) {
|
|
||||||
position = position.add(0, 1, 1);
|
|
||||||
} else {
|
|
||||||
position = position.add(face.getDirection().toBlockPoint());
|
|
||||||
}
|
|
||||||
player.setSelection(selector);
|
player.setSelection(selector);
|
||||||
AsyncPlayer wePlayer = new SilentPlayerWrapper(new LocationMaskedPlayerWrapper(
|
AsyncPlayer wePlayer = new LocationMaskedPlayerWrapper(
|
||||||
player,
|
player,
|
||||||
new Location(player.getExtent(), position.toVector3())
|
new Location(player.getExtent(), position.toVector3())
|
||||||
));
|
);
|
||||||
|
if (!print) {
|
||||||
|
wePlayer = new SilentPlayerWrapper(wePlayer);
|
||||||
|
}
|
||||||
List<String> cmds = StringMan.split(replaced, ';');
|
List<String> cmds = StringMan.split(replaced, ';');
|
||||||
for (String cmd : cmds) {
|
for (String cmd : cmds) {
|
||||||
CommandEvent event = new CommandEvent(wePlayer, cmd);
|
if (cmd.isBlank()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
cmd = cmd.charAt(0) != '/' ? "/" + cmd : cmd;
|
||||||
|
cmd = cmd.length() >1 && cmd.charAt(1) == '/' ? cmd.substring(1) : cmd;
|
||||||
|
CommandEvent event = new CommandEvent(wePlayer, cmd, editSession);
|
||||||
PlatformCommandManager.getInstance().handleCommandOnCurrentThread(event);
|
PlatformCommandManager.getInstance().handleCommandOnCurrentThread(event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,9 @@ package com.fastasyncworldedit.core.command.tool.brush;
|
|||||||
import com.fastasyncworldedit.core.FaweCache;
|
import com.fastasyncworldedit.core.FaweCache;
|
||||||
import com.fastasyncworldedit.core.math.LocalBlockVectorSet;
|
import com.fastasyncworldedit.core.math.LocalBlockVectorSet;
|
||||||
import com.fastasyncworldedit.core.util.StringMan;
|
import com.fastasyncworldedit.core.util.StringMan;
|
||||||
|
import com.fastasyncworldedit.core.wrappers.AsyncPlayer;
|
||||||
import com.fastasyncworldedit.core.wrappers.LocationMaskedPlayerWrapper;
|
import com.fastasyncworldedit.core.wrappers.LocationMaskedPlayerWrapper;
|
||||||
|
import com.fastasyncworldedit.core.wrappers.SilentPlayerWrapper;
|
||||||
import com.sk89q.worldedit.EditSession;
|
import com.sk89q.worldedit.EditSession;
|
||||||
import com.sk89q.worldedit.MaxChangedBlocksException;
|
import com.sk89q.worldedit.MaxChangedBlocksException;
|
||||||
import com.sk89q.worldedit.entity.Player;
|
import com.sk89q.worldedit.entity.Player;
|
||||||
@ -13,7 +15,7 @@ import com.sk89q.worldedit.extension.platform.PlatformCommandManager;
|
|||||||
import com.sk89q.worldedit.function.pattern.Pattern;
|
import com.sk89q.worldedit.function.pattern.Pattern;
|
||||||
import com.sk89q.worldedit.math.BlockVector3;
|
import com.sk89q.worldedit.math.BlockVector3;
|
||||||
import com.sk89q.worldedit.regions.selector.CuboidRegionSelector;
|
import com.sk89q.worldedit.regions.selector.CuboidRegionSelector;
|
||||||
import com.sk89q.worldedit.util.formatting.text.Component;
|
import com.sk89q.worldedit.util.Location;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -43,7 +45,7 @@ public class ScatterCommand extends ScatterBrush {
|
|||||||
position.subtract(radius, radius, radius),
|
position.subtract(radius, radius, radius),
|
||||||
position.add(radius, radius, radius)
|
position.add(radius, radius, radius)
|
||||||
);
|
);
|
||||||
String replaced = command.replace("{x}", position.getBlockX() + "")
|
String replaced = command.replace("{x}", Integer.toString(position.getBlockX()))
|
||||||
.replace("{y}", Integer.toString(position.getBlockY()))
|
.replace("{y}", Integer.toString(position.getBlockY()))
|
||||||
.replace("{z}", Integer.toString(position.getBlockZ()))
|
.replace("{z}", Integer.toString(position.getBlockZ()))
|
||||||
.replace("{world}", editSession.getWorld().getName())
|
.replace("{world}", editSession.getWorld().getName())
|
||||||
@ -55,41 +57,22 @@ public class ScatterCommand extends ScatterBrush {
|
|||||||
}
|
}
|
||||||
player.setSelection(selector);
|
player.setSelection(selector);
|
||||||
List<String> cmds = StringMan.split(replaced, ';');
|
List<String> cmds = StringMan.split(replaced, ';');
|
||||||
|
AsyncPlayer wePlayer = new LocationMaskedPlayerWrapper(
|
||||||
|
player,
|
||||||
|
new Location(player.getExtent(), position.toVector3())
|
||||||
|
);
|
||||||
|
if (!print) {
|
||||||
|
wePlayer = new SilentPlayerWrapper(wePlayer);
|
||||||
|
}
|
||||||
for (String cmd : cmds) {
|
for (String cmd : cmds) {
|
||||||
Player p = print ?
|
if (cmd.isBlank()) {
|
||||||
new LocationMaskedPlayerWrapper(player, player.getLocation().setPosition(position.toVector3()), false) :
|
continue;
|
||||||
new ScatterCommandPlayerWrapper(player, position);
|
}
|
||||||
CommandEvent event = new CommandEvent(p, cmd, editSession);
|
cmd = cmd.charAt(0) != '/' ? "/" + cmd : cmd;
|
||||||
|
cmd = cmd.length() >1 && cmd.charAt(1) == '/' ? cmd.substring(1) : cmd;
|
||||||
|
CommandEvent event = new CommandEvent(wePlayer, cmd, editSession);
|
||||||
PlatformCommandManager.getInstance().handleCommandOnCurrentThread(event);
|
PlatformCommandManager.getInstance().handleCommandOnCurrentThread(event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class ScatterCommandPlayerWrapper extends LocationMaskedPlayerWrapper {
|
|
||||||
|
|
||||||
ScatterCommandPlayerWrapper(Player player, BlockVector3 position) {
|
|
||||||
super(player, player.getLocation().setPosition(position.toVector3()), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void print(String msg) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void print(Component component) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void printDebug(String msg) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void printError(String msg) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void printRaw(String msg) {
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,119 +0,0 @@
|
|||||||
package com.fastasyncworldedit.core.concurrent;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.concurrent.locks.Condition;
|
|
||||||
import java.util.concurrent.locks.Lock;
|
|
||||||
import java.util.concurrent.locks.StampedLock;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Allows for reentrant behaviour of a wrapped {@link StampedLock}. Will not count the number of times it is re-entered.
|
|
||||||
*
|
|
||||||
* @since 2.3.0
|
|
||||||
*/
|
|
||||||
public class ReentrantWrappedStampedLock implements Lock {
|
|
||||||
|
|
||||||
private final StampedLock parent = new StampedLock();
|
|
||||||
private volatile Thread owner;
|
|
||||||
private volatile long stamp = 0;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void lock() {
|
|
||||||
if (Thread.currentThread() == owner) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
stamp = parent.writeLock();
|
|
||||||
owner = Thread.currentThread();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void lockInterruptibly() throws InterruptedException {
|
|
||||||
if (Thread.currentThread() == owner) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
stamp = parent.writeLockInterruptibly();
|
|
||||||
owner = Thread.currentThread();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean tryLock() {
|
|
||||||
if (Thread.currentThread() == owner) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (parent.isWriteLocked()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
stamp = parent.writeLock();
|
|
||||||
owner = Thread.currentThread();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean tryLock(final long time, @NotNull final TimeUnit unit) throws InterruptedException {
|
|
||||||
if (Thread.currentThread() == owner) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (!parent.isWriteLocked()) {
|
|
||||||
stamp = parent.writeLock();
|
|
||||||
owner = Thread.currentThread();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
stamp = parent.tryWriteLock(time, unit);
|
|
||||||
owner = Thread.currentThread();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void unlock() {
|
|
||||||
if (owner != Thread.currentThread()) {
|
|
||||||
throw new IllegalCallerException("The lock should only be unlocked by the owning thread when a stamp is not supplied");
|
|
||||||
}
|
|
||||||
unlock(stamp);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public Condition newCondition() {
|
|
||||||
throw new UnsupportedOperationException("Conditions are not supported by StampedLock");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves the stamp associated with the current lock. 0 if the wrapped {@link StampedLock} is not write-locked. This method is
|
|
||||||
* thread-checking.
|
|
||||||
*
|
|
||||||
* @return lock stam[ or 0 if not locked.
|
|
||||||
* @throws IllegalCallerException if the {@link StampedLock} is write-locked and the calling thread is not the lock owner
|
|
||||||
* @since 2.3.0
|
|
||||||
*/
|
|
||||||
public long getStampChecked() {
|
|
||||||
if (stamp != 0 && owner != Thread.currentThread()) {
|
|
||||||
throw new IllegalCallerException("The stamp should be be acquired by a thread that does not own the lock");
|
|
||||||
}
|
|
||||||
return stamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unlock the wrapped {@link StampedLock} using the given stamp. This can be called by any thread.
|
|
||||||
*
|
|
||||||
* @param stamp Stamp to unlock with
|
|
||||||
* @throws IllegalMonitorStateException if the given stamp does not match the lock's stamp
|
|
||||||
* @since 2.3.0
|
|
||||||
*/
|
|
||||||
public void unlock(final long stamp) {
|
|
||||||
parent.unlockWrite(stamp);
|
|
||||||
this.stamp = 0;
|
|
||||||
owner = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the lock is currently held.
|
|
||||||
*
|
|
||||||
* @return true if the lock is currently held.
|
|
||||||
* @since 2.3.0
|
|
||||||
*/
|
|
||||||
public boolean isLocked() {
|
|
||||||
return owner == null && this.stamp == 0 && parent.isWriteLocked(); // Be verbose
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -468,24 +468,6 @@ public class Settings extends Config {
|
|||||||
})
|
})
|
||||||
public int BUFFER_SIZE = 531441;
|
public int BUFFER_SIZE = 531441;
|
||||||
|
|
||||||
|
|
||||||
@Comment({
|
|
||||||
"The maximum time in milliseconds to wait for a chunk to load for an edit.",
|
|
||||||
" (50ms = 1 server tick, 0 = Fastest).",
|
|
||||||
" The default value of 100 should be safe for most cases.",
|
|
||||||
"",
|
|
||||||
"Actions which require loaded chunks (e.g. copy) which do not load in time",
|
|
||||||
" will use the last chunk as filler, which may appear as bands of duplicated blocks.",
|
|
||||||
"Actions usually wait about 25-50ms for the chunk to load, more if the server is lagging.",
|
|
||||||
"A value of 100ms does not force it to wait 100ms if the chunk loads in 10ms.",
|
|
||||||
"",
|
|
||||||
"This value is a timeout in case a chunk is never going to load (for whatever odd reason).",
|
|
||||||
"If the action times out, the operation continues by using the previous chunk as filler,",
|
|
||||||
" and displaying an error message. In this case, either copy a smaller section,",
|
|
||||||
" or increase chunk-wait-ms.",
|
|
||||||
"A value of 0 is faster simply because it doesn't bother loading the chunks or waiting.",
|
|
||||||
})
|
|
||||||
public int CHUNK_WAIT_MS = 1000;
|
|
||||||
@Comment("Delete history on disk after a number of days")
|
@Comment("Delete history on disk after a number of days")
|
||||||
public int DELETE_AFTER_DAYS = 7;
|
public int DELETE_AFTER_DAYS = 7;
|
||||||
@Comment("Delete history in memory on logout (does not effect disk)")
|
@Comment("Delete history in memory on logout (does not effect disk)")
|
||||||
@ -493,6 +475,7 @@ public class Settings extends Config {
|
|||||||
@Comment({
|
@Comment({
|
||||||
"If history should be enabled by default for plugins using WorldEdit:",
|
"If history should be enabled by default for plugins using WorldEdit:",
|
||||||
" - It is faster to have disabled",
|
" - It is faster to have disabled",
|
||||||
|
" - It is faster to have disabled",
|
||||||
" - Use of the FAWE API will not be effected"
|
" - Use of the FAWE API will not be effected"
|
||||||
})
|
})
|
||||||
public boolean ENABLE_FOR_CONSOLE = true;
|
public boolean ENABLE_FOR_CONSOLE = true;
|
||||||
@ -515,10 +498,12 @@ public class Settings extends Config {
|
|||||||
|
|
||||||
@Create
|
@Create
|
||||||
public static PROGRESS PROGRESS;
|
public static PROGRESS PROGRESS;
|
||||||
|
|
||||||
@Comment({
|
@Comment({
|
||||||
"This should equal the number of processors you have",
|
"This should equal the number of processors you have",
|
||||||
})
|
})
|
||||||
public int PARALLEL_THREADS = Math.max(1, Runtime.getRuntime().availableProcessors());
|
public int PARALLEL_THREADS = Math.max(1, Runtime.getRuntime().availableProcessors());
|
||||||
|
|
||||||
@Comment({
|
@Comment({
|
||||||
"When doing edits that effect more than this many chunks:",
|
"When doing edits that effect more than this many chunks:",
|
||||||
" - FAWE will start placing before all calculations are finished",
|
" - FAWE will start placing before all calculations are finished",
|
||||||
@ -530,14 +515,6 @@ public class Settings extends Config {
|
|||||||
|
|
||||||
})
|
})
|
||||||
public int TARGET_SIZE = 8 * Runtime.getRuntime().availableProcessors();
|
public int TARGET_SIZE = 8 * Runtime.getRuntime().availableProcessors();
|
||||||
@Comment({
|
|
||||||
"Force FAWE to start placing chunks regardless of whether an edit is finished processing",
|
|
||||||
" - A larger value will use slightly less CPU time",
|
|
||||||
" - A smaller value will reduce memory usage",
|
|
||||||
" - A value too small may break some operations (deform?)"
|
|
||||||
})
|
|
||||||
//TODO Find out where this was used and why the usage was removed
|
|
||||||
public int MAX_WAIT_MS = 1000;
|
|
||||||
|
|
||||||
@Comment({
|
@Comment({
|
||||||
"Increase or decrease queue intensity (ms) [-50,50]:",
|
"Increase or decrease queue intensity (ms) [-50,50]:",
|
||||||
@ -566,13 +543,6 @@ public class Settings extends Config {
|
|||||||
})
|
})
|
||||||
public boolean POOL = true;
|
public boolean POOL = true;
|
||||||
|
|
||||||
@Comment({
|
|
||||||
"Discard edits which have been idle for a certain amount of time (ms)",
|
|
||||||
" - E.g. A plugin creates an EditSession but never does anything with it",
|
|
||||||
" - This only applies to plugins improperly using WorldEdit's legacy API"
|
|
||||||
})
|
|
||||||
public int DISCARD_AFTER_MS = 60000;
|
|
||||||
|
|
||||||
@Comment({
|
@Comment({
|
||||||
"When using fastmode do not bother to tick existing/placed blocks/fluids",
|
"When using fastmode do not bother to tick existing/placed blocks/fluids",
|
||||||
"Only works in versions up to 1.17.1"
|
"Only works in versions up to 1.17.1"
|
||||||
@ -639,7 +609,7 @@ public class Settings extends Config {
|
|||||||
public boolean REMOVE_ENTITY_FROM_WORLD_ON_CHUNK_FAIL = true;
|
public boolean REMOVE_ENTITY_FROM_WORLD_ON_CHUNK_FAIL = true;
|
||||||
|
|
||||||
@Comment({
|
@Comment({
|
||||||
"Other experimental features"
|
"Increased debug logging for brush actions and processor setup"
|
||||||
})
|
})
|
||||||
public boolean OTHER = false;
|
public boolean OTHER = false;
|
||||||
|
|
||||||
|
@ -117,7 +117,7 @@ public class RichPatternParser extends FaweParser<Pattern> {
|
|||||||
if (addBrackets) {
|
if (addBrackets) {
|
||||||
value += "[";
|
value += "[";
|
||||||
}
|
}
|
||||||
value += StringMan.join(entry.getValue(), " ");
|
value += StringMan.join(entry.getValue(), "][");
|
||||||
if (addBrackets) {
|
if (addBrackets) {
|
||||||
value += "]";
|
value += "]";
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,54 @@
|
|||||||
|
package com.fastasyncworldedit.core.extension.factory.parser.pattern;
|
||||||
|
|
||||||
|
import com.fastasyncworldedit.core.configuration.Caption;
|
||||||
|
import com.fastasyncworldedit.core.extension.factory.parser.RichParser;
|
||||||
|
import com.fastasyncworldedit.core.function.pattern.TypeSwapPattern;
|
||||||
|
import com.fastasyncworldedit.core.util.Permission;
|
||||||
|
import com.sk89q.worldedit.WorldEdit;
|
||||||
|
import com.sk89q.worldedit.extension.input.InputParseException;
|
||||||
|
import com.sk89q.worldedit.extension.input.ParserContext;
|
||||||
|
import com.sk89q.worldedit.function.pattern.Pattern;
|
||||||
|
import com.sk89q.worldedit.util.formatting.text.TextComponent;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
public class TypeSwapPatternParser extends RichParser<Pattern> {
|
||||||
|
|
||||||
|
private static final List<String> SUGGESTIONS = List.of("oak", "spruce", "stone", "sandstone");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new rich parser with a defined prefix for the result, e.g. {@code #simplex}.
|
||||||
|
*
|
||||||
|
* @param worldEdit the worldedit instance.
|
||||||
|
*/
|
||||||
|
public TypeSwapPatternParser(WorldEdit worldEdit) {
|
||||||
|
super(worldEdit, "#typeswap", "#ts", "#swaptype");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Stream<String> getSuggestions(String argumentInput, int index) {
|
||||||
|
if (index > 2) {
|
||||||
|
return Stream.empty();
|
||||||
|
}
|
||||||
|
return SUGGESTIONS.stream();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Pattern parseFromInput(@Nonnull String[] input, ParserContext context) throws InputParseException {
|
||||||
|
if (input.length != 2) {
|
||||||
|
throw new InputParseException(Caption.of(
|
||||||
|
"fawe.error.command.syntax",
|
||||||
|
TextComponent.of(getPrefix() + "[input][output] (e.g. " + getPrefix() + "[spruce][oak])")
|
||||||
|
));
|
||||||
|
}
|
||||||
|
return new TypeSwapPattern(
|
||||||
|
context.requireExtent(),
|
||||||
|
input[0],
|
||||||
|
input[1],
|
||||||
|
Permission.hasPermission(context.requireActor(), "fawe.pattern.typeswap.regex")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -39,6 +39,8 @@ import java.net.URI;
|
|||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.MappedByteBuffer;
|
import java.nio.MappedByteBuffer;
|
||||||
import java.nio.channels.FileChannel;
|
import java.nio.channels.FileChannel;
|
||||||
|
import java.nio.channels.FileLock;
|
||||||
|
import java.nio.channels.OverlappingFileLockException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@ -46,6 +48,7 @@ import java.util.Iterator;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A clipboard with disk backed storage. (lower memory + loads on crash)
|
* A clipboard with disk backed storage. (lower memory + loads on crash)
|
||||||
@ -59,6 +62,7 @@ public class DiskOptimizedClipboard extends LinearClipboard {
|
|||||||
private static final int HEADER_SIZE = 27; // Current header size
|
private static final int HEADER_SIZE = 27; // Current header size
|
||||||
private static final int VERSION_1_HEADER_SIZE = 22; // Header size of "version 1"
|
private static final int VERSION_1_HEADER_SIZE = 22; // Header size of "version 1"
|
||||||
private static final int VERSION_2_HEADER_SIZE = 27; // Header size of "version 2" i.e. when NBT/entities could be saved
|
private static final int VERSION_2_HEADER_SIZE = 27; // Header size of "version 2" i.e. when NBT/entities could be saved
|
||||||
|
private static final Map<String, LockHolder> LOCK_HOLDER_CACHE = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
private final HashMap<IntTriple, CompoundTag> nbtMap;
|
private final HashMap<IntTriple, CompoundTag> nbtMap;
|
||||||
private final File file;
|
private final File file;
|
||||||
@ -301,7 +305,23 @@ public class DiskOptimizedClipboard extends LinearClipboard {
|
|||||||
private void init() throws IOException {
|
private void init() throws IOException {
|
||||||
if (this.fileChannel == null) {
|
if (this.fileChannel == null) {
|
||||||
this.fileChannel = braf.getChannel();
|
this.fileChannel = braf.getChannel();
|
||||||
this.fileChannel.lock();
|
try {
|
||||||
|
FileLock lock = this.fileChannel.lock();
|
||||||
|
LOCK_HOLDER_CACHE.put(file.getName(), new LockHolder(lock));
|
||||||
|
} catch (OverlappingFileLockException e) {
|
||||||
|
LockHolder existing = LOCK_HOLDER_CACHE.get(file.getName());
|
||||||
|
if (existing != null) {
|
||||||
|
long ms = System.currentTimeMillis() - existing.lockHeldSince;
|
||||||
|
LOGGER.error(
|
||||||
|
"Cannot lock clipboard file {} acquired by thread {}, {}ms ago",
|
||||||
|
file.getName(),
|
||||||
|
existing.thread,
|
||||||
|
ms
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// Rethrow to prevent clipboard access
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
this.byteBuffer = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0, braf.length());
|
this.byteBuffer = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0, braf.length());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -737,4 +757,18 @@ public class DiskOptimizedClipboard extends LinearClipboard {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class LockHolder {
|
||||||
|
|
||||||
|
final FileLock lock;
|
||||||
|
final long lockHeldSince;
|
||||||
|
final String thread;
|
||||||
|
|
||||||
|
LockHolder(FileLock lock) {
|
||||||
|
this.lock = lock;
|
||||||
|
lockHeldSince = System.currentTimeMillis();
|
||||||
|
this.thread = Thread.currentThread().getName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -6,19 +6,24 @@ import com.fastasyncworldedit.core.queue.IChunkGet;
|
|||||||
import com.fastasyncworldedit.core.queue.IChunkSet;
|
import com.fastasyncworldedit.core.queue.IChunkSet;
|
||||||
import com.sk89q.jnbt.CompoundTag;
|
import com.sk89q.jnbt.CompoundTag;
|
||||||
import com.sk89q.worldedit.extent.Extent;
|
import com.sk89q.worldedit.extent.Extent;
|
||||||
|
import com.sk89q.worldedit.internal.util.LogManagerCompat;
|
||||||
import com.sk89q.worldedit.math.BlockVector3;
|
import com.sk89q.worldedit.math.BlockVector3;
|
||||||
import com.sk89q.worldedit.world.block.BlockTypes;
|
import com.sk89q.worldedit.world.block.BlockTypes;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Processor that removes existing entities that would not be in air after the edit
|
* Processor that removes existing entities that would not be in air after the edit
|
||||||
*
|
*
|
||||||
* @since TODO
|
* @since 2.7.0
|
||||||
*/
|
*/
|
||||||
public class EntityInBlockRemovingProcessor implements IBatchProcessor {
|
public class EntityInBlockRemovingProcessor implements IBatchProcessor {
|
||||||
|
|
||||||
|
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IChunkSet processSet(final IChunk chunk, final IChunkGet get, final IChunkSet set) {
|
public IChunkSet processSet(final IChunk chunk, final IChunkGet get, final IChunkSet set) {
|
||||||
|
try {
|
||||||
for (CompoundTag tag : get.getEntities()) {
|
for (CompoundTag tag : get.getEntities()) {
|
||||||
// Empty tags for seemingly non-existent entities can exist?
|
// Empty tags for seemingly non-existent entities can exist?
|
||||||
if (tag.getList("Pos").size() == 0) {
|
if (tag.getList("Pos").size() == 0) {
|
||||||
@ -39,6 +44,9 @@ public class EntityInBlockRemovingProcessor implements IBatchProcessor {
|
|||||||
set.removeEntity(tag.getUUID());
|
set.removeEntity(tag.getUUID());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOGGER.warn("Could not remove entities in blocks in chunk {},{}", chunk.getX(), chunk.getZ(), e);
|
||||||
|
}
|
||||||
return set;
|
return set;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,9 +5,11 @@ import com.sk89q.worldedit.function.mask.AbstractExtentMask;
|
|||||||
import com.sk89q.worldedit.function.mask.Mask;
|
import com.sk89q.worldedit.function.mask.Mask;
|
||||||
import com.sk89q.worldedit.math.BlockVector3;
|
import com.sk89q.worldedit.math.BlockVector3;
|
||||||
|
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
public class IdMask extends AbstractExtentMask implements ResettableMask {
|
public class IdMask extends AbstractExtentMask implements ResettableMask {
|
||||||
|
|
||||||
private transient int id = -1;
|
private final AtomicInteger id = new AtomicInteger(-1);
|
||||||
|
|
||||||
public IdMask(Extent extent) {
|
public IdMask(Extent extent) {
|
||||||
super(extent);
|
super(extent);
|
||||||
@ -15,12 +17,9 @@ public class IdMask extends AbstractExtentMask implements ResettableMask {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean test(Extent extent, BlockVector3 vector) {
|
public boolean test(Extent extent, BlockVector3 vector) {
|
||||||
if (id != -1) {
|
int blockID = extent.getBlock(vector).getInternalBlockTypeId();
|
||||||
return extent.getBlock(vector).getInternalBlockTypeId() == id;
|
int testId = id.compareAndExchange(-1, blockID);
|
||||||
} else {
|
return blockID == testId || testId == -1;
|
||||||
id = extent.getBlock(vector).getInternalBlockTypeId();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -30,12 +29,12 @@ public class IdMask extends AbstractExtentMask implements ResettableMask {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void reset() {
|
public void reset() {
|
||||||
this.id = -1;
|
this.id.set(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mask copy() {
|
public Mask copy() {
|
||||||
return new IdMask(getExtent());
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -0,0 +1,103 @@
|
|||||||
|
package com.fastasyncworldedit.core.function.pattern;
|
||||||
|
|
||||||
|
import com.fastasyncworldedit.core.extent.filter.block.FilterBlock;
|
||||||
|
import com.fastasyncworldedit.core.util.StringMan;
|
||||||
|
import com.sk89q.worldedit.WorldEditException;
|
||||||
|
import com.sk89q.worldedit.extent.Extent;
|
||||||
|
import com.sk89q.worldedit.math.BlockVector3;
|
||||||
|
import com.sk89q.worldedit.world.block.BaseBlock;
|
||||||
|
import com.sk89q.worldedit.world.block.BlockState;
|
||||||
|
import com.sk89q.worldedit.world.block.BlockType;
|
||||||
|
import com.sk89q.worldedit.world.block.BlockTypes;
|
||||||
|
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pattern that replaces blocks based on their ID, matching for an "input" and replacing with an "output" string. The "input"
|
||||||
|
* string may be regex. Keeps as much of the block state as possible, excluding NBT data.
|
||||||
|
*
|
||||||
|
* @since 2.7.0
|
||||||
|
*/
|
||||||
|
public class TypeSwapPattern extends AbstractExtentPattern {
|
||||||
|
|
||||||
|
private static final Pattern SPLITTER = Pattern.compile("[|,]");
|
||||||
|
|
||||||
|
private final String inputString;
|
||||||
|
private final String outputString;
|
||||||
|
private final String[] inputs;
|
||||||
|
private Pattern inputPattern = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new instance
|
||||||
|
*
|
||||||
|
* @param extent extent to use
|
||||||
|
* @param inputString string to replace. May be regex.
|
||||||
|
* @param outputString string to replace with
|
||||||
|
* @param allowRegex if regex should be allowed for input string matching
|
||||||
|
* @since 2.7.0
|
||||||
|
*/
|
||||||
|
public TypeSwapPattern(Extent extent, String inputString, String outputString, boolean allowRegex) {
|
||||||
|
super(extent);
|
||||||
|
this.inputString = inputString;
|
||||||
|
this.outputString = outputString;
|
||||||
|
if (!StringMan.isAlphanumericUnd(inputString)) {
|
||||||
|
if (allowRegex) {
|
||||||
|
this.inputPattern = Pattern.compile(inputString.replace(",", "|"));
|
||||||
|
inputs = null;
|
||||||
|
} else {
|
||||||
|
inputs = SPLITTER.split(inputString);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
inputs = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean apply(Extent extent, BlockVector3 get, BlockVector3 set) throws WorldEditException {
|
||||||
|
BlockState existing = get.getBlock(extent);
|
||||||
|
BlockState newBlock = getNewBlock(existing);
|
||||||
|
if (newBlock == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return set.setBlock(extent, newBlock);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void applyBlock(final FilterBlock block) {
|
||||||
|
BlockState existing = block.getBlock();
|
||||||
|
BlockState newState = getNewBlock(existing);
|
||||||
|
if (newState != null) {
|
||||||
|
block.setBlock(newState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BaseBlock applyBlock(final BlockVector3 position) {
|
||||||
|
BaseBlock existing = position.getFullBlock(getExtent());
|
||||||
|
BlockState newState = getNewBlock(existing.toBlockState());
|
||||||
|
return newState == null ? existing : newState.toBaseBlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
private BlockState getNewBlock(BlockState existing) {
|
||||||
|
String oldId = existing.getBlockType().getId();
|
||||||
|
String newId = oldId;
|
||||||
|
if (inputPattern != null) {
|
||||||
|
newId = inputPattern.matcher(oldId).replaceAll(outputString);
|
||||||
|
} else if (inputs != null && inputs.length > 0) {
|
||||||
|
for (String input : inputs) {
|
||||||
|
newId = newId.replace(input, outputString);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
newId = oldId.replace(inputString, outputString);
|
||||||
|
}
|
||||||
|
if (newId.equals(oldId)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
BlockType newType = BlockTypes.get(newId);
|
||||||
|
if (newType == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return newType.getDefaultState().withProperties(existing);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -41,7 +41,7 @@ public class FaweException extends RuntimeException {
|
|||||||
* New instance of a given {@link FaweException.Type}
|
* New instance of a given {@link FaweException.Type}
|
||||||
*
|
*
|
||||||
* @param ignorable if an edit can continue if this exception is caught, e.g. by {@link com.fastasyncworldedit.core.extent.LimitExtent}
|
* @param ignorable if an edit can continue if this exception is caught, e.g. by {@link com.fastasyncworldedit.core.extent.LimitExtent}
|
||||||
* @since TODO
|
* @since 2.7.0
|
||||||
*/
|
*/
|
||||||
public FaweException(Component reason, Type type, boolean ignorable) {
|
public FaweException(Component reason, Type type, boolean ignorable) {
|
||||||
this.message = reason;
|
this.message = reason;
|
||||||
@ -70,7 +70,7 @@ public class FaweException extends RuntimeException {
|
|||||||
/**
|
/**
|
||||||
* If an edit can continue if this exception is caught, e.g. by {@link com.fastasyncworldedit.core.extent.LimitExtent}
|
* If an edit can continue if this exception is caught, e.g. by {@link com.fastasyncworldedit.core.extent.LimitExtent}
|
||||||
*
|
*
|
||||||
* @since TODO
|
* @since 2.7.0
|
||||||
*/
|
*/
|
||||||
public boolean ignorable() {
|
public boolean ignorable() {
|
||||||
return ignorable;
|
return ignorable;
|
||||||
|
@ -85,7 +85,7 @@ public interface IBatchProcessor {
|
|||||||
}
|
}
|
||||||
for (int layer = maxLayer; layer < set.getMaxSectionPosition(); layer++) {
|
for (int layer = maxLayer; layer < set.getMaxSectionPosition(); layer++) {
|
||||||
if (set.hasSection(layer)) {
|
if (set.hasSection(layer)) {
|
||||||
if (layer == minLayer) {
|
if (layer == maxLayer) {
|
||||||
char[] arr = set.loadIfPresent(layer);
|
char[] arr = set.loadIfPresent(layer);
|
||||||
if (arr != null) {
|
if (arr != null) {
|
||||||
int index = ((maxY + 1) & 15) << 8;
|
int index = ((maxY + 1) & 15) << 8;
|
||||||
|
@ -2,9 +2,7 @@ package com.fastasyncworldedit.core.queue;
|
|||||||
|
|
||||||
import com.sk89q.jnbt.CompoundTag;
|
import com.sk89q.jnbt.CompoundTag;
|
||||||
import com.sk89q.jnbt.DoubleTag;
|
import com.sk89q.jnbt.DoubleTag;
|
||||||
import com.sk89q.jnbt.IntArrayTag;
|
|
||||||
import com.sk89q.jnbt.ListTag;
|
import com.sk89q.jnbt.ListTag;
|
||||||
import com.sk89q.jnbt.LongTag;
|
|
||||||
import com.sk89q.jnbt.NBTUtils;
|
import com.sk89q.jnbt.NBTUtils;
|
||||||
import com.sk89q.jnbt.StringTag;
|
import com.sk89q.jnbt.StringTag;
|
||||||
import com.sk89q.jnbt.Tag;
|
import com.sk89q.jnbt.Tag;
|
||||||
|
@ -27,6 +27,7 @@ import java.util.Queue;
|
|||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.ForkJoinPool;
|
import java.util.concurrent.ForkJoinPool;
|
||||||
import java.util.concurrent.ForkJoinTask;
|
import java.util.concurrent.ForkJoinTask;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
@ -52,6 +53,7 @@ public abstract class QueueHandler implements Trimable, Runnable {
|
|||||||
null,
|
null,
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Secondary queue should be used for "cleanup" tasks that are likely to be shorter in life than those submitted to the
|
* Secondary queue should be used for "cleanup" tasks that are likely to be shorter in life than those submitted to the
|
||||||
* primary queue. They may be IO-bound tasks.
|
* primary queue. They may be IO-bound tasks.
|
||||||
@ -508,4 +510,28 @@ public abstract class QueueHandler implements Trimable, Runnable {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Primary queue should be used for tasks that are unlikely to wait on other tasks, IO, etc. (i.e. spend most of their
|
||||||
|
* time utilising CPU.
|
||||||
|
* <p>
|
||||||
|
* Internal API usage only.
|
||||||
|
*
|
||||||
|
* @since 2.7.0
|
||||||
|
*/
|
||||||
|
public ExecutorService getForkJoinPoolPrimary() {
|
||||||
|
return forkJoinPoolPrimary;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Secondary queue should be used for "cleanup" tasks that are likely to be shorter in life than those submitted to the
|
||||||
|
* primary queue. They may be IO-bound tasks.
|
||||||
|
* <p>
|
||||||
|
* Internal API usage only.
|
||||||
|
*
|
||||||
|
* @since 2.7.0
|
||||||
|
*/
|
||||||
|
public ExecutorService getForkJoinPoolSecondary() {
|
||||||
|
return forkJoinPoolSecondary;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -83,17 +83,6 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implemen
|
|||||||
this.maxY = maxY;
|
this.maxY = maxY;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Safety check to ensure that the thread being used matches the one being initialized on. - Can
|
|
||||||
* be removed later
|
|
||||||
*/
|
|
||||||
private void checkThread() {
|
|
||||||
if (Thread.currentThread() != currentThread && currentThread != null) {
|
|
||||||
throw new UnsupportedOperationException(
|
|
||||||
"This class must be used from a single thread. Use multiple queues for concurrent operations");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void enableQueue() {
|
public void enableQueue() {
|
||||||
enabledQueue = true;
|
enabledQueue = true;
|
||||||
@ -154,10 +143,10 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implemen
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!this.chunks.isEmpty()) {
|
if (!this.chunks.isEmpty()) {
|
||||||
|
getChunkLock.lock();
|
||||||
for (IChunk chunk : this.chunks.values()) {
|
for (IChunk chunk : this.chunks.values()) {
|
||||||
chunk.recycle();
|
chunk.recycle();
|
||||||
}
|
}
|
||||||
getChunkLock.lock();
|
|
||||||
this.chunks.clear();
|
this.chunks.clear();
|
||||||
getChunkLock.unlock();
|
getChunkLock.unlock();
|
||||||
}
|
}
|
||||||
@ -232,11 +221,23 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implemen
|
|||||||
* Submit without first checking that it has been removed from the chunk map
|
* Submit without first checking that it has been removed from the chunk map
|
||||||
*/
|
*/
|
||||||
private <V extends Future<V>> V submitUnchecked(IQueueChunk chunk) {
|
private <V extends Future<V>> V submitUnchecked(IQueueChunk chunk) {
|
||||||
|
if (chunk.isEmpty()) {
|
||||||
|
if (chunk instanceof ChunkHolder<?> holder) {
|
||||||
|
long age = holder.initAge();
|
||||||
|
// Ensure we've given time for the chunk to be used - it was likely used for a reason!
|
||||||
|
if (age < 5) {
|
||||||
|
try {
|
||||||
|
Thread.sleep(5 - age);
|
||||||
|
} catch (InterruptedException ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if (chunk.isEmpty()) {
|
if (chunk.isEmpty()) {
|
||||||
chunk.recycle();
|
chunk.recycle();
|
||||||
Future result = Futures.immediateFuture(null);
|
Future result = Futures.immediateFuture(null);
|
||||||
return (V) result;
|
return (V) result;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (Fawe.isMainThread()) {
|
if (Fawe.isMainThread()) {
|
||||||
V result = (V) chunk.call();
|
V result = (V) chunk.call();
|
||||||
@ -451,6 +452,7 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implemen
|
|||||||
@Override
|
@Override
|
||||||
public synchronized void flush() {
|
public synchronized void flush() {
|
||||||
if (!chunks.isEmpty()) {
|
if (!chunks.isEmpty()) {
|
||||||
|
getChunkLock.lock();
|
||||||
if (MemUtil.isMemoryLimited()) {
|
if (MemUtil.isMemoryLimited()) {
|
||||||
for (IQueueChunk chunk : chunks.values()) {
|
for (IQueueChunk chunk : chunks.values()) {
|
||||||
final Future future = submitUnchecked(chunk);
|
final Future future = submitUnchecked(chunk);
|
||||||
@ -467,7 +469,6 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implemen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
getChunkLock.lock();
|
|
||||||
chunks.clear();
|
chunks.clear();
|
||||||
getChunkLock.unlock();
|
getChunkLock.unlock();
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package com.fastasyncworldedit.core.queue.implementation.chunk;
|
package com.fastasyncworldedit.core.queue.implementation.chunk;
|
||||||
|
|
||||||
import com.fastasyncworldedit.core.FaweCache;
|
import com.fastasyncworldedit.core.FaweCache;
|
||||||
import com.fastasyncworldedit.core.concurrent.ReentrantWrappedStampedLock;
|
|
||||||
import com.fastasyncworldedit.core.configuration.Settings;
|
import com.fastasyncworldedit.core.configuration.Settings;
|
||||||
import com.fastasyncworldedit.core.extent.filter.block.ChunkFilterBlock;
|
import com.fastasyncworldedit.core.extent.filter.block.ChunkFilterBlock;
|
||||||
import com.fastasyncworldedit.core.extent.processor.EmptyBatchProcessor;
|
import com.fastasyncworldedit.core.extent.processor.EmptyBatchProcessor;
|
||||||
@ -26,6 +25,8 @@ import java.util.Map;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
|
import java.util.concurrent.locks.Lock;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An abstract {@link IChunk} class that implements basic get/set blocks.
|
* An abstract {@link IChunk} class that implements basic get/set blocks.
|
||||||
@ -43,7 +44,7 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
|
|||||||
return POOL.poll();
|
return POOL.poll();
|
||||||
}
|
}
|
||||||
|
|
||||||
private final ReentrantWrappedStampedLock calledLock = new ReentrantWrappedStampedLock();
|
private final Lock calledLock = new ReentrantLock();
|
||||||
|
|
||||||
private volatile IChunkGet chunkExisting; // The existing chunk (e.g. a clipboard, or the world, before changes)
|
private volatile IChunkGet chunkExisting; // The existing chunk (e.g. a clipboard, or the world, before changes)
|
||||||
private volatile IChunkSet chunkSet; // The blocks to be set to the chunkExisting
|
private volatile IChunkSet chunkSet; // The blocks to be set to the chunkExisting
|
||||||
@ -55,6 +56,7 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
|
|||||||
private int bitMask = -1; // Allow forceful setting of bitmask (for lighting)
|
private int bitMask = -1; // Allow forceful setting of bitmask (for lighting)
|
||||||
private boolean isInit = false; // Lighting handles queue differently. It relies on the chunk cache and not doing init.
|
private boolean isInit = false; // Lighting handles queue differently. It relies on the chunk cache and not doing init.
|
||||||
private boolean createCopy = false;
|
private boolean createCopy = false;
|
||||||
|
private long initTime = -1L;
|
||||||
|
|
||||||
private ChunkHolder() {
|
private ChunkHolder() {
|
||||||
this.delegate = NULL;
|
this.delegate = NULL;
|
||||||
@ -66,6 +68,7 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized void recycle() {
|
public synchronized void recycle() {
|
||||||
|
calledLock.lock();
|
||||||
delegate = NULL;
|
delegate = NULL;
|
||||||
if (chunkSet != null) {
|
if (chunkSet != null) {
|
||||||
chunkSet.recycle();
|
chunkSet.recycle();
|
||||||
@ -74,6 +77,11 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
|
|||||||
chunkExisting = null;
|
chunkExisting = null;
|
||||||
extent = null;
|
extent = null;
|
||||||
POOL.offer(this);
|
POOL.offer(this);
|
||||||
|
calledLock.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
public long initAge() {
|
||||||
|
return System.currentTimeMillis() - initTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized IBlockDelegate getDelegate() {
|
public synchronized IBlockDelegate getDelegate() {
|
||||||
@ -84,10 +92,10 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
|
|||||||
* If the chunk is currently being "called", this method will block until completed.
|
* If the chunk is currently being "called", this method will block until completed.
|
||||||
*/
|
*/
|
||||||
private void checkAndWaitOnCalledLock() {
|
private void checkAndWaitOnCalledLock() {
|
||||||
if (calledLock.isLocked()) {
|
if (!calledLock.tryLock()) {
|
||||||
calledLock.lock();
|
calledLock.lock();
|
||||||
calledLock.unlock();
|
|
||||||
}
|
}
|
||||||
|
calledLock.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -1024,6 +1032,7 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized <V extends IChunk> void init(IQueueExtent<V> extent, int chunkX, int chunkZ) {
|
public synchronized <V extends IChunk> void init(IQueueExtent<V> extent, int chunkX, int chunkZ) {
|
||||||
|
this.initTime = System.currentTimeMillis();
|
||||||
this.extent = extent;
|
this.extent = extent;
|
||||||
this.chunkX = chunkX;
|
this.chunkX = chunkX;
|
||||||
this.chunkZ = chunkZ;
|
this.chunkZ = chunkZ;
|
||||||
@ -1040,14 +1049,15 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
|
|||||||
@Override
|
@Override
|
||||||
public synchronized T call() {
|
public synchronized T call() {
|
||||||
calledLock.lock();
|
calledLock.lock();
|
||||||
final long stamp = calledLock.getStampChecked();
|
|
||||||
if (chunkSet != null && !chunkSet.isEmpty()) {
|
if (chunkSet != null && !chunkSet.isEmpty()) {
|
||||||
this.delegate = GET;
|
this.delegate = GET;
|
||||||
chunkSet.setBitMask(bitMask);
|
chunkSet.setBitMask(bitMask);
|
||||||
try {
|
try {
|
||||||
IChunkSet copy = chunkSet.createCopy();
|
IChunkSet copy = chunkSet.createCopy();
|
||||||
chunkSet = null;
|
chunkSet = null;
|
||||||
return this.call(copy, () -> calledLock.unlock(stamp));
|
return this.call(copy, () -> {
|
||||||
|
// Do nothing
|
||||||
|
});
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
calledLock.unlock();
|
calledLock.unlock();
|
||||||
throw t;
|
throw t;
|
||||||
@ -1072,6 +1082,7 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
|
|||||||
} else {
|
} else {
|
||||||
finalizer = finalize;
|
finalizer = finalize;
|
||||||
}
|
}
|
||||||
|
calledLock.unlock();
|
||||||
return get.call(set, finalizer);
|
return get.call(set, finalizer);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
@ -4,12 +4,15 @@ import com.fastasyncworldedit.core.extent.processor.ProcessorScope;
|
|||||||
import com.sk89q.worldedit.entity.Player;
|
import com.sk89q.worldedit.entity.Player;
|
||||||
import com.sk89q.worldedit.regions.Region;
|
import com.sk89q.worldedit.regions.Region;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
public class FaweMask implements IDelegateRegion {
|
public class FaweMask implements IDelegateRegion {
|
||||||
|
|
||||||
private final Region region;
|
private final Region region;
|
||||||
|
|
||||||
public FaweMask(Region region) {
|
public FaweMask(@Nonnull Region region) {
|
||||||
this.region = region;
|
this.region = Objects.requireNonNull(region);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -35,7 +38,7 @@ public class FaweMask implements IDelegateRegion {
|
|||||||
* @param type type of mask
|
* @param type type of mask
|
||||||
* @param notify if the player should be notified
|
* @param notify if the player should be notified
|
||||||
* @return if still valid
|
* @return if still valid
|
||||||
* @since TODO
|
* @since 2.7.0
|
||||||
*/
|
*/
|
||||||
public boolean isValid(Player player, FaweMaskManager.MaskType type, boolean notify) {
|
public boolean isValid(Player player, FaweMaskManager.MaskType type, boolean notify) {
|
||||||
return isValid(player, type);
|
return isValid(player, type);
|
||||||
|
@ -30,7 +30,7 @@ public abstract class FaweMaskManager {
|
|||||||
/**
|
/**
|
||||||
* Get a {@link FaweMask} for the given player and {@link MaskType}. If isWhitelist is false, will return a "blacklist" mask.
|
* Get a {@link FaweMask} for the given player and {@link MaskType}. If isWhitelist is false, will return a "blacklist" mask.
|
||||||
*
|
*
|
||||||
* @since TODO
|
* @since 2.7.0
|
||||||
*/
|
*/
|
||||||
public FaweMask getMask(final Player player, MaskType type, boolean isWhitelist, boolean notify) {
|
public FaweMask getMask(final Player player, MaskType type, boolean isWhitelist, boolean notify) {
|
||||||
return getMask(player, type, isWhitelist);
|
return getMask(player, type, isWhitelist);
|
||||||
|
@ -4,6 +4,7 @@ import com.sk89q.worldedit.extent.AbstractDelegateExtent;
|
|||||||
import com.sk89q.worldedit.extent.Extent;
|
import com.sk89q.worldedit.extent.Extent;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
|
|
||||||
public class ExtentTraverser<T extends Extent> {
|
public class ExtentTraverser<T extends Extent> {
|
||||||
@ -24,6 +25,7 @@ public class ExtentTraverser<T extends Extent> {
|
|||||||
return root != null;
|
return root != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
public T get() {
|
public T get() {
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
@ -50,6 +52,7 @@ public class ExtentTraverser<T extends Extent> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
|
@Nullable
|
||||||
public <U> U findAndGet(Class<U> clazz) {
|
public <U> U findAndGet(Class<U> clazz) {
|
||||||
ExtentTraverser<Extent> traverser = find(clazz);
|
ExtentTraverser<Extent> traverser = find(clazz);
|
||||||
return (traverser != null) ? (U) traverser.get() : null;
|
return (traverser != null) ? (U) traverser.get() : null;
|
||||||
|
@ -0,0 +1,44 @@
|
|||||||
|
package com.fastasyncworldedit.core.util;
|
||||||
|
|
||||||
|
import com.fastasyncworldedit.core.extent.processor.BatchProcessorHolder;
|
||||||
|
import com.fastasyncworldedit.core.extent.processor.MultiBatchProcessor;
|
||||||
|
import com.fastasyncworldedit.core.queue.IBatchProcessor;
|
||||||
|
import com.sk89q.worldedit.internal.util.LogManagerCompat;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.ArrayDeque;
|
||||||
|
import java.util.Queue;
|
||||||
|
|
||||||
|
public class ProcessorTraverser<T extends IBatchProcessor> {
|
||||||
|
|
||||||
|
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
||||||
|
|
||||||
|
private final T root;
|
||||||
|
|
||||||
|
public ProcessorTraverser(@Nonnull T root) {
|
||||||
|
this.root = root;
|
||||||
|
}
|
||||||
|
|
||||||
|
public <U extends IBatchProcessor> @Nullable U find(Class<U> clazz) {
|
||||||
|
try {
|
||||||
|
Queue<IBatchProcessor> processors = new ArrayDeque<>();
|
||||||
|
IBatchProcessor processor = root;
|
||||||
|
do {
|
||||||
|
if (clazz.isAssignableFrom(processor.getClass())) {
|
||||||
|
return clazz.cast(processor);
|
||||||
|
} else if (processor instanceof MultiBatchProcessor multiProcessor) {
|
||||||
|
processors.addAll(multiProcessor.getBatchProcessors());
|
||||||
|
} else if (processor instanceof BatchProcessorHolder holder) {
|
||||||
|
processors.add(holder.getProcessor());
|
||||||
|
}
|
||||||
|
} while ((processor = processors.poll()) != null);
|
||||||
|
return null;
|
||||||
|
} catch (Throwable e) {
|
||||||
|
LOGGER.error("Error traversing processors", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -88,7 +88,10 @@ public abstract class TaskManager {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Run a bunch of tasks in parallel using the shared thread pool.
|
* Run a bunch of tasks in parallel using the shared thread pool.
|
||||||
|
*
|
||||||
|
* @deprecated Deprecated without replacement as unused internally, and poor implementation of what it's designed to do.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(forRemoval = true, since = "2.7.0")
|
||||||
public void parallel(Collection<Runnable> runables) {
|
public void parallel(Collection<Runnable> runables) {
|
||||||
for (Runnable run : runables) {
|
for (Runnable run : runables) {
|
||||||
pool.submit(run);
|
pool.submit(run);
|
||||||
@ -101,8 +104,9 @@ public abstract class TaskManager {
|
|||||||
*
|
*
|
||||||
* @param runnables the tasks to run
|
* @param runnables the tasks to run
|
||||||
* @param numThreads number of threads (null = config.yml parallel threads)
|
* @param numThreads number of threads (null = config.yml parallel threads)
|
||||||
|
* @deprecated Deprecated without replacement as unused internally, and poor implementation of what it's designed to do.
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated(forRemoval = true, since = "2.7.0")
|
||||||
public void parallel(Collection<Runnable> runnables, @Nullable Integer numThreads) {
|
public void parallel(Collection<Runnable> runnables, @Nullable Integer numThreads) {
|
||||||
if (runnables == null) {
|
if (runnables == null) {
|
||||||
return;
|
return;
|
||||||
@ -271,13 +275,17 @@ public abstract class TaskManager {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Deprecated without replacement as unused internally, and poor implementation of what it's designed to do.
|
||||||
|
*/
|
||||||
|
@Deprecated(forRemoval = true, since = "2.7.0")
|
||||||
public void wait(AtomicBoolean running, int timeout) {
|
public void wait(AtomicBoolean running, int timeout) {
|
||||||
try {
|
try {
|
||||||
long start = System.currentTimeMillis();
|
long start = System.currentTimeMillis();
|
||||||
synchronized (running) {
|
synchronized (running) {
|
||||||
while (running.get()) {
|
while (running.get()) {
|
||||||
running.wait(timeout);
|
running.wait(timeout);
|
||||||
if (running.get() && System.currentTimeMillis() - start > Settings.settings().QUEUE.DISCARD_AFTER_MS) {
|
if (running.get() && System.currentTimeMillis() - start > 60000) {
|
||||||
new RuntimeException("FAWE is taking a long time to execute a task (might just be a symptom): ").printStackTrace();
|
new RuntimeException("FAWE is taking a long time to execute a task (might just be a symptom): ").printStackTrace();
|
||||||
LOGGER.info("For full debug information use: /fawe threads");
|
LOGGER.info("For full debug information use: /fawe threads");
|
||||||
}
|
}
|
||||||
@ -288,6 +296,10 @@ public abstract class TaskManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Deprecated without replacement as unused internally, and poor implementation of what it's designed to do.
|
||||||
|
*/
|
||||||
|
@Deprecated(forRemoval = true, since = "2.7.0")
|
||||||
public void notify(AtomicBoolean running) {
|
public void notify(AtomicBoolean running) {
|
||||||
running.set(false);
|
running.set(false);
|
||||||
synchronized (running) {
|
synchronized (running) {
|
||||||
|
@ -14,7 +14,13 @@ import org.w3c.dom.Document;
|
|||||||
import javax.xml.XMLConstants;
|
import javax.xml.XMLConstants;
|
||||||
import javax.xml.parsers.DocumentBuilder;
|
import javax.xml.parsers.DocumentBuilder;
|
||||||
import javax.xml.parsers.DocumentBuilderFactory;
|
import javax.xml.parsers.DocumentBuilderFactory;
|
||||||
import java.net.URL;
|
import java.io.InputStream;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.http.HttpClient;
|
||||||
|
import java.net.http.HttpRequest;
|
||||||
|
import java.net.http.HttpResponse;
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.time.temporal.ChronoUnit;
|
||||||
|
|
||||||
public class UpdateNotification {
|
public class UpdateNotification {
|
||||||
|
|
||||||
@ -28,11 +34,29 @@ public class UpdateNotification {
|
|||||||
*/
|
*/
|
||||||
public static void doUpdateCheck() {
|
public static void doUpdateCheck() {
|
||||||
if (Settings.settings().ENABLED_COMPONENTS.UPDATE_NOTIFICATIONS) {
|
if (Settings.settings().ENABLED_COMPONENTS.UPDATE_NOTIFICATIONS) {
|
||||||
|
final HttpRequest request = HttpRequest
|
||||||
|
.newBuilder()
|
||||||
|
.uri(URI.create("https://ci.athion.net/job/FastAsyncWorldEdit/api/xml/"))
|
||||||
|
.timeout(Duration.of(10L, ChronoUnit.SECONDS))
|
||||||
|
.build();
|
||||||
|
HttpClient.newHttpClient()
|
||||||
|
.sendAsync(request, HttpResponse.BodyHandlers.ofInputStream())
|
||||||
|
.whenComplete((response, thrown) -> {
|
||||||
|
if (thrown != null) {
|
||||||
|
LOGGER.error("Update check failed: {} ", thrown.getMessage());
|
||||||
|
}
|
||||||
|
processResponseBody(response.body());
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void processResponseBody(InputStream body) {
|
||||||
try {
|
try {
|
||||||
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
|
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
|
||||||
dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
|
dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
|
||||||
DocumentBuilder db = dbf.newDocumentBuilder();
|
DocumentBuilder db = dbf.newDocumentBuilder();
|
||||||
Document doc = db.parse(new URL("https://ci.plex.us.org/job/Plex-FAWE/api/xml/").openStream());
|
Document doc = db.parse(body);
|
||||||
faweVersion = doc.getElementsByTagName("lastSuccessfulBuild").item(0).getFirstChild().getTextContent();
|
faweVersion = doc.getElementsByTagName("lastSuccessfulBuild").item(0).getFirstChild().getTextContent();
|
||||||
FaweVersion faweVersion = Fawe.instance().getVersion();
|
FaweVersion faweVersion = Fawe.instance().getVersion();
|
||||||
if (faweVersion.build == 0) {
|
if (faweVersion.build == 0) {
|
||||||
@ -47,17 +71,15 @@ public class UpdateNotification {
|
|||||||
"""
|
"""
|
||||||
An update for FastAsyncWorldEdit is available. You are {} build(s) out of date.
|
An update for FastAsyncWorldEdit is available. You are {} build(s) out of date.
|
||||||
You are running build {}, the latest version is build {}.
|
You are running build {}, the latest version is build {}.
|
||||||
Update at https://ci.plex.us.org/job/Plex-FAWE/""",
|
Update at https://www.spigotmc.org/resources/13932/""",
|
||||||
versionDifference,
|
versionDifference,
|
||||||
faweVersion.build,
|
faweVersion.build,
|
||||||
UpdateNotification.faweVersion
|
UpdateNotification.faweVersion
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception ignored) {
|
||||||
LOGGER.error("Unable to check for updates. Skipping.");
|
LOGGER.error("Unable to check for updates. Skipping.");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -76,8 +98,8 @@ public class UpdateNotification {
|
|||||||
faweVersion.build,
|
faweVersion.build,
|
||||||
UpdateNotification.faweVersion,
|
UpdateNotification.faweVersion,
|
||||||
TextComponent
|
TextComponent
|
||||||
.of("https://ci.plex.us.org/job/Plex-FAWE/")
|
.of("https://www.spigotmc.org/resources/13932/")
|
||||||
.clickEvent(ClickEvent.openUrl("https://ci.plex.us.org/job/Plex-FAWE/"))
|
.clickEvent(ClickEvent.openUrl("https://www.spigotmc.org/resources/13932/"))
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,81 @@
|
|||||||
|
package com.fastasyncworldedit.core.util.task;
|
||||||
|
|
||||||
|
import com.fastasyncworldedit.core.configuration.Settings;
|
||||||
|
|
||||||
|
import java.io.Closeable;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
import java.util.concurrent.ForkJoinPool;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* async queue that accepts a {@link Thread.UncaughtExceptionHandler} for exception handling per instance, delegating to a
|
||||||
|
* parent {@link KeyQueuedExecutorService}.
|
||||||
|
*
|
||||||
|
* @since 2.7.0
|
||||||
|
*/
|
||||||
|
public class AsyncNotifyKeyedQueue implements Closeable {
|
||||||
|
|
||||||
|
private static final KeyQueuedExecutorService<UUID> QUEUE_SUBMISSIONS = new KeyQueuedExecutorService<>(new ForkJoinPool(
|
||||||
|
Settings.settings().QUEUE.PARALLEL_THREADS,
|
||||||
|
new FaweForkJoinWorkerThreadFactory("AsyncNotifyKeyedQueue - %s"),
|
||||||
|
null,
|
||||||
|
false
|
||||||
|
));
|
||||||
|
|
||||||
|
private final Thread.UncaughtExceptionHandler handler;
|
||||||
|
private final Supplier<UUID> key;
|
||||||
|
private volatile boolean closed;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* New instance
|
||||||
|
*
|
||||||
|
* @param handler exception handler
|
||||||
|
* @param key supplier of UUID key
|
||||||
|
*/
|
||||||
|
public AsyncNotifyKeyedQueue(Thread.UncaughtExceptionHandler handler, Supplier<UUID> key) {
|
||||||
|
this.handler = handler;
|
||||||
|
this.key = key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Thread.UncaughtExceptionHandler getHandler() {
|
||||||
|
return handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> Future<T> run(Runnable task) {
|
||||||
|
return call(() -> {
|
||||||
|
task.run();
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> Future<T> call(Callable<T> task) {
|
||||||
|
Future[] self = new Future[1];
|
||||||
|
Callable<T> wrapped = () -> {
|
||||||
|
if (!closed) {
|
||||||
|
try {
|
||||||
|
return task.call();
|
||||||
|
} catch (Throwable e) {
|
||||||
|
handler.uncaughtException(Thread.currentThread(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (self[0] != null) {
|
||||||
|
self[0].cancel(true);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
self[0] = QUEUE_SUBMISSIONS.submit(key.get(), wrapped);
|
||||||
|
return self[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
closed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isClosed() {
|
||||||
|
return closed;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,13 +1,9 @@
|
|||||||
package com.fastasyncworldedit.core.util.task;
|
package com.fastasyncworldedit.core.util.task;
|
||||||
|
|
||||||
import com.fastasyncworldedit.core.Fawe;
|
import com.fastasyncworldedit.core.Fawe;
|
||||||
import com.fastasyncworldedit.core.configuration.Settings;
|
|
||||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
|
||||||
|
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
import java.util.concurrent.ForkJoinPool;
|
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
import java.util.concurrent.locks.Lock;
|
import java.util.concurrent.locks.Lock;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
@ -15,13 +11,6 @@ import java.util.function.Supplier;
|
|||||||
|
|
||||||
public class AsyncNotifyQueue implements Closeable {
|
public class AsyncNotifyQueue implements Closeable {
|
||||||
|
|
||||||
private static final ForkJoinPool QUEUE_SUBMISSIONS = new ForkJoinPool(
|
|
||||||
Settings.settings().QUEUE.PARALLEL_THREADS,
|
|
||||||
new FaweForkJoinWorkerThreadFactory("AsyncNotifyQueue - %s"),
|
|
||||||
null,
|
|
||||||
false
|
|
||||||
);
|
|
||||||
|
|
||||||
private final Lock lock = new ReentrantLock(true);
|
private final Lock lock = new ReentrantLock(true);
|
||||||
private final Thread.UncaughtExceptionHandler handler;
|
private final Thread.UncaughtExceptionHandler handler;
|
||||||
private boolean closed;
|
private boolean closed;
|
||||||
@ -56,9 +45,6 @@ public class AsyncNotifyQueue implements Closeable {
|
|||||||
return task.call();
|
return task.call();
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
handler.uncaughtException(Thread.currentThread(), e);
|
handler.uncaughtException(Thread.currentThread(), e);
|
||||||
if (self[0] != null) {
|
|
||||||
self[0].cancel(true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
@ -70,7 +56,7 @@ public class AsyncNotifyQueue implements Closeable {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
self[0] = QUEUE_SUBMISSIONS.submit(wrapped);
|
self[0] = Fawe.instance().getQueueHandler().async(wrapped);
|
||||||
return self[0];
|
return self[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,6 +30,7 @@ import com.fastasyncworldedit.core.extent.ResettableExtent;
|
|||||||
import com.fastasyncworldedit.core.extent.SingleRegionExtent;
|
import com.fastasyncworldedit.core.extent.SingleRegionExtent;
|
||||||
import com.fastasyncworldedit.core.extent.SourceMaskExtent;
|
import com.fastasyncworldedit.core.extent.SourceMaskExtent;
|
||||||
import com.fastasyncworldedit.core.extent.clipboard.WorldCopyClipboard;
|
import com.fastasyncworldedit.core.extent.clipboard.WorldCopyClipboard;
|
||||||
|
import com.fastasyncworldedit.core.extent.processor.ExtentBatchProcessorHolder;
|
||||||
import com.fastasyncworldedit.core.extent.processor.lighting.NullRelighter;
|
import com.fastasyncworldedit.core.extent.processor.lighting.NullRelighter;
|
||||||
import com.fastasyncworldedit.core.extent.processor.lighting.Relighter;
|
import com.fastasyncworldedit.core.extent.processor.lighting.Relighter;
|
||||||
import com.fastasyncworldedit.core.function.SurfaceRegionFunction;
|
import com.fastasyncworldedit.core.function.SurfaceRegionFunction;
|
||||||
@ -55,6 +56,7 @@ import com.fastasyncworldedit.core.queue.implementation.preloader.Preloader;
|
|||||||
import com.fastasyncworldedit.core.util.ExtentTraverser;
|
import com.fastasyncworldedit.core.util.ExtentTraverser;
|
||||||
import com.fastasyncworldedit.core.util.MaskTraverser;
|
import com.fastasyncworldedit.core.util.MaskTraverser;
|
||||||
import com.fastasyncworldedit.core.util.MathMan;
|
import com.fastasyncworldedit.core.util.MathMan;
|
||||||
|
import com.fastasyncworldedit.core.util.ProcessorTraverser;
|
||||||
import com.fastasyncworldedit.core.util.TaskManager;
|
import com.fastasyncworldedit.core.util.TaskManager;
|
||||||
import com.fastasyncworldedit.core.util.collection.BlockVector3Set;
|
import com.fastasyncworldedit.core.util.collection.BlockVector3Set;
|
||||||
import com.fastasyncworldedit.core.util.task.RunnableVal;
|
import com.fastasyncworldedit.core.util.task.RunnableVal;
|
||||||
@ -524,9 +526,17 @@ public class EditSession extends PassthroughExtent implements AutoCloseable {
|
|||||||
* @return mask, may be null
|
* @return mask, may be null
|
||||||
*/
|
*/
|
||||||
public Mask getMask() {
|
public Mask getMask() {
|
||||||
//FAWE start - ExtendTraverser & MaskingExtents
|
//FAWE start - ExtentTraverser & MaskingExtents
|
||||||
ExtentTraverser<MaskingExtent> maskingExtent = new ExtentTraverser<>(getExtent()).find(MaskingExtent.class);
|
MaskingExtent maskingExtent = new ExtentTraverser<>(getExtent()).findAndGet(MaskingExtent.class);
|
||||||
return maskingExtent != null ? maskingExtent.get().getMask() : null;
|
if (maskingExtent == null) {
|
||||||
|
ExtentBatchProcessorHolder processorExtent =
|
||||||
|
new ExtentTraverser<>(getExtent()).findAndGet(ExtentBatchProcessorHolder.class);
|
||||||
|
if (processorExtent != null) {
|
||||||
|
maskingExtent =
|
||||||
|
new ProcessorTraverser<>(processorExtent.getProcessor()).find(MaskingExtent.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return maskingExtent != null ? maskingExtent.getMask() : null;
|
||||||
//FAWE end
|
//FAWE end
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -609,23 +619,31 @@ public class EditSession extends PassthroughExtent implements AutoCloseable {
|
|||||||
//FAWE start - use MaskingExtent & ExtentTraverser
|
//FAWE start - use MaskingExtent & ExtentTraverser
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set a mask.
|
* Set a mask. Combines with any existing masks, set null to clear existing masks.
|
||||||
*
|
*
|
||||||
* @param mask mask or null
|
* @param mask mask or null
|
||||||
*/
|
*/
|
||||||
public void setMask(Mask mask) {
|
public void setMask(@Nullable Mask mask) {
|
||||||
if (mask == null) {
|
if (mask == null) {
|
||||||
mask = Masks.alwaysTrue();
|
mask = Masks.alwaysTrue();
|
||||||
} else {
|
} else {
|
||||||
new MaskTraverser(mask).reset(this);
|
new MaskTraverser(mask).reset(this);
|
||||||
}
|
}
|
||||||
ExtentTraverser<MaskingExtent> maskingExtent = new ExtentTraverser<>(getExtent()).find(MaskingExtent.class);
|
MaskingExtent maskingExtent = new ExtentTraverser<>(getExtent()).findAndGet(MaskingExtent.class);
|
||||||
if (maskingExtent != null && maskingExtent.get() != null) {
|
if (maskingExtent == null && mask != Masks.alwaysTrue()) {
|
||||||
Mask oldMask = maskingExtent.get().getMask();
|
ExtentBatchProcessorHolder processorExtent =
|
||||||
|
new ExtentTraverser<>(getExtent()).findAndGet(ExtentBatchProcessorHolder.class);
|
||||||
|
if (processorExtent != null) {
|
||||||
|
maskingExtent =
|
||||||
|
new ProcessorTraverser<>(processorExtent.getProcessor()).find(MaskingExtent.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (maskingExtent != null) {
|
||||||
|
Mask oldMask = maskingExtent.getMask();
|
||||||
if (oldMask instanceof ResettableMask) {
|
if (oldMask instanceof ResettableMask) {
|
||||||
((ResettableMask) oldMask).reset();
|
((ResettableMask) oldMask).reset();
|
||||||
}
|
}
|
||||||
maskingExtent.get().setMask(mask);
|
maskingExtent.setMask(mask);
|
||||||
} else if (mask != Masks.alwaysTrue()) {
|
} else if (mask != Masks.alwaysTrue()) {
|
||||||
addProcessor(new MaskingExtent(getExtent(), mask));
|
addProcessor(new MaskingExtent(getExtent(), mask));
|
||||||
}
|
}
|
||||||
@ -2270,6 +2288,90 @@ public class EditSession extends PassthroughExtent implements AutoCloseable {
|
|||||||
//FAWE end
|
//FAWE end
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes a cone.
|
||||||
|
*
|
||||||
|
* @param pos Center of the cone
|
||||||
|
* @param block The block pattern to use
|
||||||
|
* @param radiusX The cone's largest north/south extent
|
||||||
|
* @param radiusZ The cone's largest east/west extent
|
||||||
|
* @param height The cone's up/down extent. If negative, extend downward.
|
||||||
|
* @param filled If false, only a shell will be generated.
|
||||||
|
* @param thickness The cone's wall thickness, if it's hollow.
|
||||||
|
* @return number of blocks changed
|
||||||
|
* @throws MaxChangedBlocksException thrown if too many blocks are changed
|
||||||
|
*/
|
||||||
|
public int makeCone(
|
||||||
|
BlockVector3 pos,
|
||||||
|
Pattern block,
|
||||||
|
double radiusX,
|
||||||
|
double radiusZ,
|
||||||
|
int height,
|
||||||
|
boolean filled,
|
||||||
|
double thickness
|
||||||
|
) throws MaxChangedBlocksException {
|
||||||
|
int affected = 0;
|
||||||
|
|
||||||
|
final int ceilRadiusX = (int) Math.ceil(radiusX);
|
||||||
|
final int ceilRadiusZ = (int) Math.ceil(radiusZ);
|
||||||
|
|
||||||
|
double rx2 = Math.pow(radiusX, 2);
|
||||||
|
double ry2 = Math.pow(height, 2);
|
||||||
|
double rz2 = Math.pow(radiusZ, 2);
|
||||||
|
|
||||||
|
int cx = pos.getX();
|
||||||
|
int cy = pos.getY();
|
||||||
|
int cz = pos.getZ();
|
||||||
|
|
||||||
|
for (int y = 0; y < height; ++y) {
|
||||||
|
double ySquaredMinusHeightOverHeightSquared = Math.pow(y - height, 2) / ry2;
|
||||||
|
int yy = cy + y;
|
||||||
|
forX:
|
||||||
|
for (int x = 0; x <= ceilRadiusX; ++x) {
|
||||||
|
double xSquaredOverRadiusX = Math.pow(x, 2) / rx2;
|
||||||
|
int xx = cx + x;
|
||||||
|
forZ:
|
||||||
|
for (int z = 0; z <= ceilRadiusZ; ++z) {
|
||||||
|
int zz = cz + z;
|
||||||
|
double zSquaredOverRadiusZ = Math.pow(z, 2) / rz2;
|
||||||
|
double distanceFromOriginMinusHeightSquared = xSquaredOverRadiusX + zSquaredOverRadiusZ - ySquaredMinusHeightOverHeightSquared;
|
||||||
|
|
||||||
|
if (distanceFromOriginMinusHeightSquared > 1) {
|
||||||
|
if (z == 0) {
|
||||||
|
break forX;
|
||||||
|
}
|
||||||
|
break forZ;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!filled) {
|
||||||
|
double xNext = Math.pow(x + thickness, 2) / rx2 + zSquaredOverRadiusZ - ySquaredMinusHeightOverHeightSquared;
|
||||||
|
double yNext = xSquaredOverRadiusX + zSquaredOverRadiusZ - Math.pow(y + thickness - height, 2) / ry2;
|
||||||
|
double zNext = xSquaredOverRadiusX + Math.pow(z + thickness, 2) / rz2 - ySquaredMinusHeightOverHeightSquared;
|
||||||
|
if (xNext <= 0 && zNext <= 0 && (yNext <= 0 && y + thickness != height)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (distanceFromOriginMinusHeightSquared <= 0) {
|
||||||
|
if (setBlock(xx, yy, zz, block)) {
|
||||||
|
++affected;
|
||||||
|
}
|
||||||
|
if (setBlock(xx, yy, zz, block)) {
|
||||||
|
++affected;
|
||||||
|
}
|
||||||
|
if (setBlock(xx, yy, zz, block)) {
|
||||||
|
++affected;
|
||||||
|
}
|
||||||
|
if (setBlock(xx, yy, zz, block)) {
|
||||||
|
++affected;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return affected;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Move the blocks in a region a certain direction.
|
* Move the blocks in a region a certain direction.
|
||||||
*
|
*
|
||||||
@ -2991,9 +3093,10 @@ public class EditSession extends PassthroughExtent implements AutoCloseable {
|
|||||||
} catch (ExpressionTimeoutException e) {
|
} catch (ExpressionTimeoutException e) {
|
||||||
timedOut[0] = timedOut[0] + 1;
|
timedOut[0] = timedOut[0] + 1;
|
||||||
return null;
|
return null;
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
throw e;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
LOGGER.warn("Failed to create shape", e);
|
throw new RuntimeException(e);
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -23,8 +23,10 @@ import com.fastasyncworldedit.core.Fawe;
|
|||||||
import com.fastasyncworldedit.core.configuration.Caption;
|
import com.fastasyncworldedit.core.configuration.Caption;
|
||||||
import com.fastasyncworldedit.core.configuration.Settings;
|
import com.fastasyncworldedit.core.configuration.Settings;
|
||||||
import com.fastasyncworldedit.core.extent.ResettableExtent;
|
import com.fastasyncworldedit.core.extent.ResettableExtent;
|
||||||
|
import com.fastasyncworldedit.core.extent.clipboard.DiskOptimizedClipboard;
|
||||||
import com.fastasyncworldedit.core.extent.clipboard.MultiClipboardHolder;
|
import com.fastasyncworldedit.core.extent.clipboard.MultiClipboardHolder;
|
||||||
import com.fastasyncworldedit.core.history.DiskStorageHistory;
|
import com.fastasyncworldedit.core.history.DiskStorageHistory;
|
||||||
|
import com.fastasyncworldedit.core.internal.exception.FaweClipboardVersionMismatchException;
|
||||||
import com.fastasyncworldedit.core.internal.io.FaweInputStream;
|
import com.fastasyncworldedit.core.internal.io.FaweInputStream;
|
||||||
import com.fastasyncworldedit.core.internal.io.FaweOutputStream;
|
import com.fastasyncworldedit.core.internal.io.FaweOutputStream;
|
||||||
import com.fastasyncworldedit.core.limit.FaweLimit;
|
import com.fastasyncworldedit.core.limit.FaweLimit;
|
||||||
@ -50,6 +52,8 @@ import com.sk89q.worldedit.command.tool.Tool;
|
|||||||
import com.sk89q.worldedit.entity.Player;
|
import com.sk89q.worldedit.entity.Player;
|
||||||
import com.sk89q.worldedit.extension.platform.Actor;
|
import com.sk89q.worldedit.extension.platform.Actor;
|
||||||
import com.sk89q.worldedit.extension.platform.Locatable;
|
import com.sk89q.worldedit.extension.platform.Locatable;
|
||||||
|
import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard;
|
||||||
|
import com.sk89q.worldedit.extent.clipboard.Clipboard;
|
||||||
import com.sk89q.worldedit.extent.inventory.BlockBag;
|
import com.sk89q.worldedit.extent.inventory.BlockBag;
|
||||||
import com.sk89q.worldedit.function.mask.Mask;
|
import com.sk89q.worldedit.function.mask.Mask;
|
||||||
import com.sk89q.worldedit.function.operation.ChangeSetExecutor;
|
import com.sk89q.worldedit.function.operation.ChangeSetExecutor;
|
||||||
@ -93,6 +97,7 @@ import java.util.ListIterator;
|
|||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.concurrent.locks.Lock;
|
import java.util.concurrent.locks.Lock;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
@ -105,8 +110,8 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
|||||||
*/
|
*/
|
||||||
public class LocalSession implements TextureHolder {
|
public class LocalSession implements TextureHolder {
|
||||||
|
|
||||||
private static final transient int CUI_VERSION_UNINITIALIZED = -1;
|
public static int MAX_HISTORY_SIZE = 15;
|
||||||
public static transient int MAX_HISTORY_SIZE = 15;
|
private static final int CUI_VERSION_UNINITIALIZED = -1;
|
||||||
|
|
||||||
// Non-session related fields
|
// Non-session related fields
|
||||||
private transient LocalConfiguration config;
|
private transient LocalConfiguration config;
|
||||||
@ -877,6 +882,58 @@ public class LocalSession implements TextureHolder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load a clipboard from disk and into this localsession. Synchronises with other clipboard setting/getting to and from
|
||||||
|
* this session
|
||||||
|
*
|
||||||
|
* @param file Clipboard file to load
|
||||||
|
* @throws FaweClipboardVersionMismatchException in clipboard version mismatch (between saved and internal, expected, version)
|
||||||
|
* @throws ExecutionException if the computation threw an exception
|
||||||
|
* @throws InterruptedException if the current thread was interrupted while waiting
|
||||||
|
*/
|
||||||
|
public void loadClipboardFromDisk(File file) throws FaweClipboardVersionMismatchException, ExecutionException,
|
||||||
|
InterruptedException {
|
||||||
|
synchronized (clipboardLock) {
|
||||||
|
if (file.exists() && file.length() > 5) {
|
||||||
|
try {
|
||||||
|
if (getClipboard() != null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (EmptyClipboardException ignored) {
|
||||||
|
}
|
||||||
|
DiskOptimizedClipboard doc = Fawe.instance().getClipboardExecutor().submit(
|
||||||
|
uuid,
|
||||||
|
() -> DiskOptimizedClipboard.loadFromFile(file)
|
||||||
|
).get();
|
||||||
|
Clipboard clip = doc.toClipboard();
|
||||||
|
ClipboardHolder holder = new ClipboardHolder(clip);
|
||||||
|
setClipboard(holder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteClipboardOnDisk() {
|
||||||
|
synchronized (clipboardLock) {
|
||||||
|
ClipboardHolder holder = getExistingClipboard();
|
||||||
|
if (holder != null) {
|
||||||
|
for (Clipboard clipboard : holder.getClipboards()) {
|
||||||
|
DiskOptimizedClipboard doc;
|
||||||
|
if (clipboard instanceof DiskOptimizedClipboard) {
|
||||||
|
doc = (DiskOptimizedClipboard) clipboard;
|
||||||
|
} else if (clipboard instanceof BlockArrayClipboard && ((BlockArrayClipboard) clipboard).getParent() instanceof DiskOptimizedClipboard) {
|
||||||
|
doc = (DiskOptimizedClipboard) ((BlockArrayClipboard) clipboard).getParent();
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Fawe.instance().getClipboardExecutor().submit(uuid, () -> {
|
||||||
|
doc.close(); // Ensure closed before deletion
|
||||||
|
doc.getFile().delete();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
//FAWE end
|
//FAWE end
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -929,11 +929,13 @@ public class BrushCommands {
|
|||||||
@Arg(desc = "Expression")
|
@Arg(desc = "Expression")
|
||||||
Expression radius,
|
Expression radius,
|
||||||
@Arg(desc = "Command to run")
|
@Arg(desc = "Command to run")
|
||||||
List<String> input
|
List<String> input,
|
||||||
|
@Switch(name = 'p', desc = "Show any printed output")
|
||||||
|
boolean print
|
||||||
) throws WorldEditException {
|
) throws WorldEditException {
|
||||||
worldEdit.checkMaxBrushRadius(radius);
|
worldEdit.checkMaxBrushRadius(radius);
|
||||||
String cmd = StringMan.join(input, " ");
|
String cmd = StringMan.join(input, " ");
|
||||||
set(context, new CommandBrush(cmd), "worldedit.brush.command").setSize(radius);
|
set(context, new CommandBrush(cmd, print), "worldedit.brush.command").setSize(radius);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Command(
|
@Command(
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
package com.sk89q.worldedit.command;
|
package com.sk89q.worldedit.command;
|
||||||
|
|
||||||
|
import com.fastasyncworldedit.core.Fawe;
|
||||||
import com.fastasyncworldedit.core.FaweAPI;
|
import com.fastasyncworldedit.core.FaweAPI;
|
||||||
import com.fastasyncworldedit.core.FaweCache;
|
import com.fastasyncworldedit.core.FaweCache;
|
||||||
import com.fastasyncworldedit.core.configuration.Caption;
|
import com.fastasyncworldedit.core.configuration.Caption;
|
||||||
@ -28,6 +29,7 @@ import com.fastasyncworldedit.core.extent.clipboard.DiskOptimizedClipboard;
|
|||||||
import com.fastasyncworldedit.core.extent.clipboard.MultiClipboardHolder;
|
import com.fastasyncworldedit.core.extent.clipboard.MultiClipboardHolder;
|
||||||
import com.fastasyncworldedit.core.extent.clipboard.ReadOnlyClipboard;
|
import com.fastasyncworldedit.core.extent.clipboard.ReadOnlyClipboard;
|
||||||
import com.fastasyncworldedit.core.extent.clipboard.URIClipboardHolder;
|
import com.fastasyncworldedit.core.extent.clipboard.URIClipboardHolder;
|
||||||
|
import com.fastasyncworldedit.core.internal.exception.FaweException;
|
||||||
import com.fastasyncworldedit.core.internal.io.FastByteArrayOutputStream;
|
import com.fastasyncworldedit.core.internal.io.FastByteArrayOutputStream;
|
||||||
import com.fastasyncworldedit.core.limit.FaweLimit;
|
import com.fastasyncworldedit.core.limit.FaweLimit;
|
||||||
import com.fastasyncworldedit.core.util.ImgurUtility;
|
import com.fastasyncworldedit.core.util.ImgurUtility;
|
||||||
@ -160,7 +162,7 @@ public class ClipboardCommands {
|
|||||||
session.getPlacementPosition(actor));
|
session.getPlacementPosition(actor));
|
||||||
ForwardExtentCopy copy = new ForwardExtentCopy(editSession, region, clipboard, region.getMinimumPoint());
|
ForwardExtentCopy copy = new ForwardExtentCopy(editSession, region, clipboard, region.getMinimumPoint());
|
||||||
copy.setCopyingEntities(copyEntities);
|
copy.setCopyingEntities(copyEntities);
|
||||||
createCopy(session, editSession, copyBiomes, mask, clipboard, copy);
|
createCopy(actor, session, editSession, copyBiomes, mask, clipboard, copy);
|
||||||
|
|
||||||
copy.getStatusMessages().forEach(actor::print);
|
copy.getStatusMessages().forEach(actor::print);
|
||||||
//FAWE end
|
//FAWE end
|
||||||
@ -271,7 +273,7 @@ public class ClipboardCommands {
|
|||||||
copy.setSourceFunction(new BlockReplace(editSession, leavePattern));
|
copy.setSourceFunction(new BlockReplace(editSession, leavePattern));
|
||||||
copy.setCopyingEntities(copyEntities);
|
copy.setCopyingEntities(copyEntities);
|
||||||
copy.setRemovingEntities(true);
|
copy.setRemovingEntities(true);
|
||||||
createCopy(session, editSession, copyBiomes, mask, clipboard, copy);
|
createCopy(actor, session, editSession, copyBiomes, mask, clipboard, copy);
|
||||||
|
|
||||||
if (!actor.hasPermission("fawe.tips")) {
|
if (!actor.hasPermission("fawe.tips")) {
|
||||||
actor.print(Caption.of("fawe.tips.tip.lazycut"));
|
actor.print(Caption.of("fawe.tips.tip.lazycut"));
|
||||||
@ -281,6 +283,7 @@ public class ClipboardCommands {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void createCopy(
|
private void createCopy(
|
||||||
|
final Actor actor,
|
||||||
final LocalSession session,
|
final LocalSession session,
|
||||||
final EditSession editSession,
|
final EditSession editSession,
|
||||||
final boolean copyBiomes,
|
final boolean copyBiomes,
|
||||||
@ -311,9 +314,22 @@ public class ClipboardCommands {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
Operations.completeLegacy(copy);
|
Operations.completeLegacy(copy);
|
||||||
} finally {
|
} catch (Exception e) {
|
||||||
clipboard.flush();
|
DiskOptimizedClipboard doc;
|
||||||
|
if (clipboard instanceof DiskOptimizedClipboard) {
|
||||||
|
doc = (DiskOptimizedClipboard) clipboard;
|
||||||
|
} else if (clipboard instanceof BlockArrayClipboard && ((BlockArrayClipboard) clipboard).getParent() instanceof DiskOptimizedClipboard) {
|
||||||
|
doc = (DiskOptimizedClipboard) ((BlockArrayClipboard) clipboard).getParent();
|
||||||
|
} else {
|
||||||
|
throw e;
|
||||||
}
|
}
|
||||||
|
Fawe.instance().getClipboardExecutor().submit(actor.getUniqueId(), () -> {
|
||||||
|
clipboard.close();
|
||||||
|
doc.getFile().delete();
|
||||||
|
});
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
clipboard.flush();
|
||||||
session.setClipboard(new ClipboardHolder(clipboard));
|
session.setClipboard(new ClipboardHolder(clipboard));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,6 +188,49 @@ public class GenerationCommands {
|
|||||||
return affected;
|
return affected;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Command(
|
||||||
|
name = "/cone",
|
||||||
|
desc = "Generates a cone."
|
||||||
|
)
|
||||||
|
@CommandPermissions("worldedit.generation.cone")
|
||||||
|
@Logging(PLACEMENT)
|
||||||
|
public int cone(Actor actor, LocalSession session, EditSession editSession,
|
||||||
|
@Arg(desc = "The pattern of blocks to generate")
|
||||||
|
Pattern pattern,
|
||||||
|
@Arg(desc = "The radii of the cone. 1st is N/S, 2nd is E/W")
|
||||||
|
@Radii(2)
|
||||||
|
List<Double> radii,
|
||||||
|
@Arg(desc = "The height of the cone", def = "1")
|
||||||
|
int height,
|
||||||
|
@Switch(name = 'h', desc = "Make a hollow cone")
|
||||||
|
boolean hollow,
|
||||||
|
@Arg(desc = "Thickness of the hollow cone", def = "1")
|
||||||
|
double thickness
|
||||||
|
) throws WorldEditException {
|
||||||
|
double radiusX;
|
||||||
|
double radiusZ;
|
||||||
|
switch (radii.size()) {
|
||||||
|
case 1 -> radiusX = radiusZ = Math.max(1, radii.get(0));
|
||||||
|
case 2 -> {
|
||||||
|
radiusX = Math.max(1, radii.get(0));
|
||||||
|
radiusZ = Math.max(1, radii.get(1));
|
||||||
|
}
|
||||||
|
default -> {
|
||||||
|
actor.printError(Caption.of("worldedit.cone.invalid-radius"));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
worldEdit.checkMaxRadius(radiusX);
|
||||||
|
worldEdit.checkMaxRadius(radiusZ);
|
||||||
|
worldEdit.checkMaxRadius(height);
|
||||||
|
|
||||||
|
BlockVector3 pos = session.getPlacementPosition(actor);
|
||||||
|
int affected = editSession.makeCone(pos, pattern, radiusX, radiusZ, height, !hollow, thickness);
|
||||||
|
actor.printInfo(Caption.of("worldedit.cone.created", TextComponent.of(affected)));
|
||||||
|
return affected;
|
||||||
|
}
|
||||||
|
|
||||||
@Command(
|
@Command(
|
||||||
name = "/hsphere",
|
name = "/hsphere",
|
||||||
desc = "Generates a hollow sphere."
|
desc = "Generates a hollow sphere."
|
||||||
|
@ -50,12 +50,19 @@ import com.sk89q.worldedit.extension.platform.Actor;
|
|||||||
import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat;
|
import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat;
|
||||||
import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormats;
|
import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormats;
|
||||||
import com.sk89q.worldedit.function.EntityFunction;
|
import com.sk89q.worldedit.function.EntityFunction;
|
||||||
|
import com.sk89q.worldedit.function.block.BlockReplace;
|
||||||
import com.sk89q.worldedit.function.mask.BlockTypeMask;
|
import com.sk89q.worldedit.function.mask.BlockTypeMask;
|
||||||
|
import com.sk89q.worldedit.function.mask.BoundedHeightMask;
|
||||||
import com.sk89q.worldedit.function.mask.ExistingBlockMask;
|
import com.sk89q.worldedit.function.mask.ExistingBlockMask;
|
||||||
import com.sk89q.worldedit.function.mask.Mask;
|
import com.sk89q.worldedit.function.mask.Mask;
|
||||||
|
import com.sk89q.worldedit.function.mask.MaskIntersection;
|
||||||
|
import com.sk89q.worldedit.function.mask.Masks;
|
||||||
|
import com.sk89q.worldedit.function.mask.RegionMask;
|
||||||
import com.sk89q.worldedit.function.operation.Operations;
|
import com.sk89q.worldedit.function.operation.Operations;
|
||||||
import com.sk89q.worldedit.function.pattern.Pattern;
|
import com.sk89q.worldedit.function.pattern.Pattern;
|
||||||
|
import com.sk89q.worldedit.function.visitor.DownwardVisitor;
|
||||||
import com.sk89q.worldedit.function.visitor.EntityVisitor;
|
import com.sk89q.worldedit.function.visitor.EntityVisitor;
|
||||||
|
import com.sk89q.worldedit.function.visitor.RecursiveVisitor;
|
||||||
import com.sk89q.worldedit.internal.annotation.Direction;
|
import com.sk89q.worldedit.internal.annotation.Direction;
|
||||||
import com.sk89q.worldedit.internal.annotation.VertHeight;
|
import com.sk89q.worldedit.internal.annotation.VertHeight;
|
||||||
import com.sk89q.worldedit.internal.expression.EvaluationException;
|
import com.sk89q.worldedit.internal.expression.EvaluationException;
|
||||||
@ -63,8 +70,10 @@ import com.sk89q.worldedit.internal.expression.Expression;
|
|||||||
import com.sk89q.worldedit.internal.expression.ExpressionException;
|
import com.sk89q.worldedit.internal.expression.ExpressionException;
|
||||||
import com.sk89q.worldedit.math.BlockVector3;
|
import com.sk89q.worldedit.math.BlockVector3;
|
||||||
import com.sk89q.worldedit.math.Vector2;
|
import com.sk89q.worldedit.math.Vector2;
|
||||||
|
import com.sk89q.worldedit.math.Vector3;
|
||||||
import com.sk89q.worldedit.regions.CuboidRegion;
|
import com.sk89q.worldedit.regions.CuboidRegion;
|
||||||
import com.sk89q.worldedit.regions.CylinderRegion;
|
import com.sk89q.worldedit.regions.CylinderRegion;
|
||||||
|
import com.sk89q.worldedit.regions.EllipsoidRegion;
|
||||||
import com.sk89q.worldedit.regions.Region;
|
import com.sk89q.worldedit.regions.Region;
|
||||||
import com.sk89q.worldedit.util.formatting.component.SubtleFormat;
|
import com.sk89q.worldedit.util.formatting.component.SubtleFormat;
|
||||||
import com.sk89q.worldedit.util.formatting.text.Component;
|
import com.sk89q.worldedit.util.formatting.text.Component;
|
||||||
@ -836,6 +845,55 @@ public class UtilityCommands {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Command(
|
||||||
|
// name = "/hollowr",
|
||||||
|
// desc = "Hollow out a space recursively with a pattern"
|
||||||
|
// )
|
||||||
|
// @CommandPermissions("worldedit.hollowr")
|
||||||
|
// @Logging(PLACEMENT)
|
||||||
|
// public int hollowr(
|
||||||
|
// Actor actor,
|
||||||
|
// LocalSession session,
|
||||||
|
// EditSession editSession,
|
||||||
|
// @Arg(desc = "The radius to hollow out") Expression radiusExp,
|
||||||
|
// @ArgFlag(name = 'p', desc = "The blocks to fill with") Pattern pattern,
|
||||||
|
// @ArgFlag(name = 'm', desc = "The blocks remove", def = "") Mask mask
|
||||||
|
// ) throws WorldEditException {
|
||||||
|
// //FAWE start
|
||||||
|
// double radius = radiusExp.evaluate();
|
||||||
|
// //FAWE end
|
||||||
|
// radius = Math.max(1, radius);
|
||||||
|
// we.checkMaxRadius(radius);
|
||||||
|
// if (mask == null) {
|
||||||
|
// Mask mask = new MaskIntersection(
|
||||||
|
// new RegionMask(new EllipsoidRegion(null, origin, Vector3.at(radius, radius, radius))),
|
||||||
|
// new BoundedHeightMask(
|
||||||
|
// Math.max(lowerBound, minY),
|
||||||
|
// Math.min(maxY, origin.getBlockY())
|
||||||
|
// ),
|
||||||
|
// Masks.negate(new ExistingBlockMask(this))
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Want to replace blocks
|
||||||
|
// BlockReplace replace = new BlockReplace(this, pattern);
|
||||||
|
//
|
||||||
|
// // Pick how we're going to visit blocks
|
||||||
|
// RecursiveVisitor visitor;
|
||||||
|
// //FAWE start - provide extent for preloading, min/max y
|
||||||
|
// if (recursive) {
|
||||||
|
// visitor = new RecursiveVisitor(mask, replace, (int) (radius * 2 + 1), minY, maxY, this);
|
||||||
|
// } else {
|
||||||
|
// visitor = new DownwardVisitor(mask, replace, origin.getBlockY(), (int) (radius * 2 + 1), minY, maxY, this);
|
||||||
|
// }
|
||||||
|
// //FAWE end
|
||||||
|
//
|
||||||
|
// BlockVector3 pos = session.getPlacementPosition(actor);
|
||||||
|
// int affected = editSession.res(pos, pattern, radius, depth, true);
|
||||||
|
// actor.print(Caption.of("worldedit.fillr.created", TextComponent.of(affected)));
|
||||||
|
// return affected;
|
||||||
|
// }
|
||||||
|
|
||||||
public static List<Map.Entry<URI, String>> filesToEntry(final File root, final List<File> files, final UUID uuid) {
|
public static List<Map.Entry<URI, String>> filesToEntry(final File root, final List<File> files, final UUID uuid) {
|
||||||
return files.stream()
|
return files.stream()
|
||||||
.map(input -> { // Keep this functional, as transform is evaluated lazily
|
.map(input -> { // Keep this functional, as transform is evaluated lazily
|
||||||
|
@ -426,23 +426,7 @@ public interface Player extends Entity, Actor {
|
|||||||
cancel(true);
|
cancel(true);
|
||||||
LocalSession session = getSession();
|
LocalSession session = getSession();
|
||||||
if (Settings.settings().CLIPBOARD.USE_DISK && Settings.settings().CLIPBOARD.DELETE_ON_LOGOUT) {
|
if (Settings.settings().CLIPBOARD.USE_DISK && Settings.settings().CLIPBOARD.DELETE_ON_LOGOUT) {
|
||||||
ClipboardHolder holder = session.getExistingClipboard();
|
session.deleteClipboardOnDisk();
|
||||||
if (holder != null) {
|
|
||||||
for (Clipboard clipboard : holder.getClipboards()) {
|
|
||||||
DiskOptimizedClipboard doc;
|
|
||||||
if (clipboard instanceof DiskOptimizedClipboard) {
|
|
||||||
doc = (DiskOptimizedClipboard) clipboard;
|
|
||||||
} else if (clipboard instanceof BlockArrayClipboard && ((BlockArrayClipboard) clipboard).getParent() instanceof DiskOptimizedClipboard) {
|
|
||||||
doc = (DiskOptimizedClipboard) ((BlockArrayClipboard) clipboard).getParent();
|
|
||||||
} else {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
Fawe.instance().getClipboardExecutor().submit(getUniqueId(), () -> {
|
|
||||||
doc.close(); // Ensure closed before deletion
|
|
||||||
doc.getFile().delete();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (Settings.settings().CLIPBOARD.USE_DISK) {
|
} else if (Settings.settings().CLIPBOARD.USE_DISK) {
|
||||||
Fawe.instance().getClipboardExecutor().submit(getUniqueId(), () -> session.setClipboard(null));
|
Fawe.instance().getClipboardExecutor().submit(getUniqueId(), () -> session.setClipboard(null));
|
||||||
} else if (Settings.settings().CLIPBOARD.DELETE_ON_LOGOUT) {
|
} else if (Settings.settings().CLIPBOARD.DELETE_ON_LOGOUT) {
|
||||||
@ -464,22 +448,7 @@ public interface Player extends Entity, Actor {
|
|||||||
Settings.settings().PATHS.CLIPBOARD + File.separator + getUniqueId() + ".bd"
|
Settings.settings().PATHS.CLIPBOARD + File.separator + getUniqueId() + ".bd"
|
||||||
);
|
);
|
||||||
try {
|
try {
|
||||||
if (file.exists() && file.length() > 5) {
|
getSession().loadClipboardFromDisk(file);
|
||||||
LocalSession session = getSession();
|
|
||||||
try {
|
|
||||||
if (session.getClipboard() != null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} catch (EmptyClipboardException ignored) {
|
|
||||||
}
|
|
||||||
DiskOptimizedClipboard doc = Fawe.instance().getClipboardExecutor().submit(
|
|
||||||
getUniqueId(),
|
|
||||||
() -> DiskOptimizedClipboard.loadFromFile(file)
|
|
||||||
).get();
|
|
||||||
Clipboard clip = doc.toClipboard();
|
|
||||||
ClipboardHolder holder = new ClipboardHolder(clip);
|
|
||||||
session.setClipboard(holder);
|
|
||||||
}
|
|
||||||
} catch (FaweClipboardVersionMismatchException e) {
|
} catch (FaweClipboardVersionMismatchException e) {
|
||||||
print(e.getComponent());
|
print(e.getComponent());
|
||||||
} catch (RuntimeException e) {
|
} catch (RuntimeException e) {
|
||||||
|
@ -42,6 +42,7 @@ import com.fastasyncworldedit.core.extension.factory.parser.pattern.OffsetPatter
|
|||||||
import com.fastasyncworldedit.core.extension.factory.parser.pattern.PerlinPatternParser;
|
import com.fastasyncworldedit.core.extension.factory.parser.pattern.PerlinPatternParser;
|
||||||
import com.fastasyncworldedit.core.extension.factory.parser.pattern.RandomFullClipboardPatternParser;
|
import com.fastasyncworldedit.core.extension.factory.parser.pattern.RandomFullClipboardPatternParser;
|
||||||
import com.fastasyncworldedit.core.extension.factory.parser.pattern.RandomOffsetPatternParser;
|
import com.fastasyncworldedit.core.extension.factory.parser.pattern.RandomOffsetPatternParser;
|
||||||
|
import com.fastasyncworldedit.core.extension.factory.parser.pattern.TypeSwapPatternParser;
|
||||||
import com.sk89q.worldedit.extension.factory.parser.pattern.RandomPatternParser;
|
import com.sk89q.worldedit.extension.factory.parser.pattern.RandomPatternParser;
|
||||||
import com.fastasyncworldedit.core.extension.factory.parser.pattern.RelativePatternParser;
|
import com.fastasyncworldedit.core.extension.factory.parser.pattern.RelativePatternParser;
|
||||||
import com.fastasyncworldedit.core.extension.factory.parser.pattern.RichPatternParser;
|
import com.fastasyncworldedit.core.extension.factory.parser.pattern.RichPatternParser;
|
||||||
@ -133,6 +134,7 @@ public final class PatternFactory extends AbstractFactory<Pattern> {
|
|||||||
register(new SimplexPatternParser(worldEdit));
|
register(new SimplexPatternParser(worldEdit));
|
||||||
register(new SolidRandomOffsetPatternParser(worldEdit));
|
register(new SolidRandomOffsetPatternParser(worldEdit));
|
||||||
register(new SurfaceRandomOffsetPatternParser(worldEdit));
|
register(new SurfaceRandomOffsetPatternParser(worldEdit));
|
||||||
|
register(new TypeSwapPatternParser(worldEdit));
|
||||||
register(new VoronoiPatternParser(worldEdit));
|
register(new VoronoiPatternParser(worldEdit));
|
||||||
//FAWE end
|
//FAWE end
|
||||||
}
|
}
|
||||||
|
@ -19,10 +19,9 @@
|
|||||||
|
|
||||||
package com.sk89q.worldedit.extension.platform;
|
package com.sk89q.worldedit.extension.platform;
|
||||||
|
|
||||||
import com.fastasyncworldedit.core.configuration.Caption;
|
|
||||||
import com.fastasyncworldedit.core.internal.exception.FaweException;
|
import com.fastasyncworldedit.core.internal.exception.FaweException;
|
||||||
import com.fastasyncworldedit.core.util.TaskManager;
|
import com.fastasyncworldedit.core.util.TaskManager;
|
||||||
import com.fastasyncworldedit.core.util.task.AsyncNotifyQueue;
|
import com.fastasyncworldedit.core.util.task.AsyncNotifyKeyedQueue;
|
||||||
import com.sk89q.worldedit.WorldEditException;
|
import com.sk89q.worldedit.WorldEditException;
|
||||||
import com.sk89q.worldedit.internal.cui.CUIEvent;
|
import com.sk89q.worldedit.internal.cui.CUIEvent;
|
||||||
import com.sk89q.worldedit.util.formatting.text.TextComponent;
|
import com.sk89q.worldedit.util.formatting.text.TextComponent;
|
||||||
@ -68,7 +67,7 @@ public abstract class AbstractNonPlayerActor implements Actor {
|
|||||||
|
|
||||||
// Queue for async tasks
|
// Queue for async tasks
|
||||||
private final AtomicInteger runningCount = new AtomicInteger();
|
private final AtomicInteger runningCount = new AtomicInteger();
|
||||||
private final AsyncNotifyQueue asyncNotifyQueue = new AsyncNotifyQueue((thread, throwable) -> {
|
private final AsyncNotifyKeyedQueue asyncNotifyQueue = new AsyncNotifyKeyedQueue((thread, throwable) -> {
|
||||||
while (throwable.getCause() != null) {
|
while (throwable.getCause() != null) {
|
||||||
throwable = throwable.getCause();
|
throwable = throwable.getCause();
|
||||||
}
|
}
|
||||||
@ -82,7 +81,7 @@ public abstract class AbstractNonPlayerActor implements Actor {
|
|||||||
throwable.printStackTrace();
|
throwable.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}, this::getUniqueId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Run a task either async, or on the current thread.
|
* Run a task either async, or on the current thread.
|
||||||
|
@ -25,7 +25,7 @@ import com.fastasyncworldedit.core.math.MutableBlockVector3;
|
|||||||
import com.fastasyncworldedit.core.regions.FaweMaskManager;
|
import com.fastasyncworldedit.core.regions.FaweMaskManager;
|
||||||
import com.fastasyncworldedit.core.util.TaskManager;
|
import com.fastasyncworldedit.core.util.TaskManager;
|
||||||
import com.fastasyncworldedit.core.util.WEManager;
|
import com.fastasyncworldedit.core.util.WEManager;
|
||||||
import com.fastasyncworldedit.core.util.task.AsyncNotifyQueue;
|
import com.fastasyncworldedit.core.util.task.AsyncNotifyKeyedQueue;
|
||||||
import com.sk89q.worldedit.EditSession;
|
import com.sk89q.worldedit.EditSession;
|
||||||
import com.sk89q.worldedit.MaxChangedBlocksException;
|
import com.sk89q.worldedit.MaxChangedBlocksException;
|
||||||
import com.sk89q.worldedit.WorldEdit;
|
import com.sk89q.worldedit.WorldEdit;
|
||||||
@ -81,7 +81,7 @@ public abstract class AbstractPlayerActor implements Actor, Player, Cloneable {
|
|||||||
|
|
||||||
// Queue for async tasks
|
// Queue for async tasks
|
||||||
private final AtomicInteger runningCount = new AtomicInteger();
|
private final AtomicInteger runningCount = new AtomicInteger();
|
||||||
private final AsyncNotifyQueue asyncNotifyQueue = new AsyncNotifyQueue(
|
private final AsyncNotifyKeyedQueue asyncNotifyQueue = new AsyncNotifyKeyedQueue(
|
||||||
(thread, throwable) -> {
|
(thread, throwable) -> {
|
||||||
while (throwable.getCause() != null) {
|
while (throwable.getCause() != null) {
|
||||||
throwable = throwable.getCause();
|
throwable = throwable.getCause();
|
||||||
@ -96,7 +96,7 @@ public abstract class AbstractPlayerActor implements Actor, Player, Cloneable {
|
|||||||
throwable.printStackTrace();
|
throwable.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}, this::getUniqueId);
|
||||||
|
|
||||||
public AbstractPlayerActor(Map<String, Object> meta) {
|
public AbstractPlayerActor(Map<String, Object> meta) {
|
||||||
this.meta = meta;
|
this.meta = meta;
|
||||||
@ -709,7 +709,7 @@ public abstract class AbstractPlayerActor implements Actor, Player, Cloneable {
|
|||||||
@Override
|
@Override
|
||||||
public void checkPermission(String permission) throws AuthorizationException {
|
public void checkPermission(String permission) throws AuthorizationException {
|
||||||
if (!hasPermission(permission)) {
|
if (!hasPermission(permission)) {
|
||||||
throw new AuthorizationException();
|
throw new AuthorizationException(Caption.of("fawe.error.no-perm", permission));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,8 +45,8 @@ import com.sk89q.worldedit.world.item.ItemTypes;
|
|||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Path;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -354,7 +354,7 @@ public class SessionManager {
|
|||||||
@Subscribe
|
@Subscribe
|
||||||
public void onConfigurationLoad(ConfigurationLoadEvent event) {
|
public void onConfigurationLoad(ConfigurationLoadEvent event) {
|
||||||
LocalConfiguration config = event.getConfiguration();
|
LocalConfiguration config = event.getConfiguration();
|
||||||
File dir = new File(config.getWorkingDirectoryPath().toFile(), "sessions");
|
Path dir = config.getWorkingDirectoryPath().resolve("sessions");
|
||||||
store = new JsonFileSessionStore(dir);
|
store = new JsonFileSessionStore(dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,6 +36,10 @@ import java.io.FileNotFoundException;
|
|||||||
import java.io.FileReader;
|
import java.io.FileReader;
|
||||||
import java.io.FileWriter;
|
import java.io.FileWriter;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.NoSuchFileException;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.StandardCopyOption;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
@ -49,20 +53,31 @@ public class JsonFileSessionStore implements SessionStore {
|
|||||||
|
|
||||||
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
||||||
private final Gson gson;
|
private final Gson gson;
|
||||||
private final File dir;
|
private final Path dir;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new session store.
|
||||||
|
*
|
||||||
|
* @param dir the directory
|
||||||
|
* @deprecated Use {@link #JsonFileSessionStore(Path)}
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public JsonFileSessionStore(File dir) {
|
||||||
|
this(dir.toPath());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new session store.
|
* Create a new session store.
|
||||||
*
|
*
|
||||||
* @param dir the directory
|
* @param dir the directory
|
||||||
*/
|
*/
|
||||||
public JsonFileSessionStore(File dir) {
|
public JsonFileSessionStore(Path dir) {
|
||||||
checkNotNull(dir);
|
checkNotNull(dir);
|
||||||
|
|
||||||
if (!dir.isDirectory()) {
|
try {
|
||||||
if (!dir.mkdirs()) {
|
Files.createDirectories(dir);
|
||||||
LOGGER.warn("Failed to create directory '" + dir.getPath() + "' for sessions");
|
} catch (IOException e) {
|
||||||
}
|
LOGGER.warn("Failed to create directory '" + dir + "' for sessions", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.dir = dir;
|
this.dir = dir;
|
||||||
@ -77,21 +92,19 @@ public class JsonFileSessionStore implements SessionStore {
|
|||||||
* @param id the ID
|
* @param id the ID
|
||||||
* @return the file
|
* @return the file
|
||||||
*/
|
*/
|
||||||
private File getPath(UUID id) {
|
private Path getPath(UUID id) {
|
||||||
checkNotNull(id);
|
checkNotNull(id);
|
||||||
return new File(dir, id + ".json");
|
return dir.resolve(id + ".json");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LocalSession load(UUID id) throws IOException {
|
public LocalSession load(UUID id) throws IOException {
|
||||||
File file = getPath(id);
|
Path file = getPath(id);
|
||||||
try (Closer closer = Closer.create()) {
|
try (var reader = Files.newBufferedReader(file)) {
|
||||||
FileReader fr = closer.register(new FileReader(file));
|
LocalSession session = gson.fromJson(reader, LocalSession.class);
|
||||||
BufferedReader br = closer.register(new BufferedReader(fr));
|
|
||||||
LocalSession session = gson.fromJson(br, LocalSession.class);
|
|
||||||
if (session == null) {
|
if (session == null) {
|
||||||
LOGGER.warn("Loaded a null session from {}, creating new session", file);
|
LOGGER.warn("Loaded a null session from {}, creating new session", file);
|
||||||
if (!file.delete()) {
|
if (!Files.deleteIfExists(file)) {
|
||||||
LOGGER.warn("Failed to delete corrupted session {}", file);
|
LOGGER.warn("Failed to delete corrupted session {}", file);
|
||||||
}
|
}
|
||||||
session = new LocalSession();
|
session = new LocalSession();
|
||||||
@ -99,7 +112,7 @@ public class JsonFileSessionStore implements SessionStore {
|
|||||||
return session;
|
return session;
|
||||||
} catch (JsonParseException e) {
|
} catch (JsonParseException e) {
|
||||||
throw new IOException(e);
|
throw new IOException(e);
|
||||||
} catch (FileNotFoundException e) {
|
} catch (NoSuchFileException e) {
|
||||||
return new LocalSession();
|
return new LocalSession();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -107,29 +120,26 @@ public class JsonFileSessionStore implements SessionStore {
|
|||||||
@Override
|
@Override
|
||||||
public void save(UUID id, LocalSession session) throws IOException {
|
public void save(UUID id, LocalSession session) throws IOException {
|
||||||
checkNotNull(session);
|
checkNotNull(session);
|
||||||
File finalFile = getPath(id);
|
Path finalFile = getPath(id);
|
||||||
File tempFile = new File(finalFile.getParentFile(), finalFile.getName() + ".tmp");
|
Path tempFile = finalFile.getParent().resolve(finalFile.getFileName() + ".tmp");
|
||||||
|
|
||||||
try (Closer closer = Closer.create()) {
|
try (var writer = Files.newBufferedWriter(tempFile)) {
|
||||||
FileWriter fr = closer.register(new FileWriter(tempFile));
|
gson.toJson(session, writer);
|
||||||
BufferedWriter bw = closer.register(new BufferedWriter(fr));
|
|
||||||
gson.toJson(session, bw);
|
|
||||||
} catch (JsonIOException e) {
|
} catch (JsonIOException e) {
|
||||||
throw new IOException(e);
|
throw new IOException(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (finalFile.exists()) {
|
if (Files.size(tempFile) == 0) {
|
||||||
if (!finalFile.delete()) {
|
|
||||||
LOGGER.warn("Failed to delete " + finalFile.getPath() + " so the .tmp file can replace it");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tempFile.length() == 0) {
|
|
||||||
throw new IllegalStateException("Gson wrote zero bytes");
|
throw new IllegalStateException("Gson wrote zero bytes");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!tempFile.renameTo(finalFile)) {
|
try {
|
||||||
LOGGER.warn("Failed to rename temporary session file to " + finalFile.getPath());
|
Files.move(
|
||||||
|
tempFile, finalFile,
|
||||||
|
StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING
|
||||||
|
);
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOGGER.warn("Failed to rename temporary session file to " + finalFile.toAbsolutePath(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,7 +86,7 @@ public class BlockType implements Keyed, Pattern {
|
|||||||
* a specific requirement to actually create new block types, please contact the FAWE devs to discuss. Use
|
* a specific requirement to actually create new block types, please contact the FAWE devs to discuss. Use
|
||||||
* {@link BlockTypes#get(String)} instead.
|
* {@link BlockTypes#get(String)} instead.
|
||||||
*/
|
*/
|
||||||
@Deprecated(since = "TODO")
|
@Deprecated(since = "2.7.0")
|
||||||
//FAWE end
|
//FAWE end
|
||||||
public BlockType(String id) {
|
public BlockType(String id) {
|
||||||
this(id, null);
|
this(id, null);
|
||||||
@ -98,7 +98,7 @@ public class BlockType implements Keyed, Pattern {
|
|||||||
* a specific requirement to actually create new block types, please contact the FAWE devs to discuss. Use
|
* a specific requirement to actually create new block types, please contact the FAWE devs to discuss. Use
|
||||||
* {@link BlockTypes#get(String)} instead.
|
* {@link BlockTypes#get(String)} instead.
|
||||||
*/
|
*/
|
||||||
@Deprecated(since = "TODO")
|
@Deprecated(since = "2.7.0")
|
||||||
//FAWE end
|
//FAWE end
|
||||||
public BlockType(String id, Function<BlockState, BlockState> values) {
|
public BlockType(String id, Function<BlockState, BlockState> values) {
|
||||||
// If it has no namespace, assume minecraft.
|
// If it has no namespace, assume minecraft.
|
||||||
|
@ -43,7 +43,7 @@ public class ItemType implements RegistryItem, Keyed {
|
|||||||
|
|
||||||
private final String id;
|
private final String id;
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
private final LazyReference<String> name = LazyReference.from(() -> {
|
private transient final LazyReference<String> name = LazyReference.from(() -> {
|
||||||
String name = GuavaUtil.firstNonNull(
|
String name = GuavaUtil.firstNonNull(
|
||||||
WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.GAME_HOOKS)
|
WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.GAME_HOOKS)
|
||||||
.getRegistries().getItemRegistry().getName(this),
|
.getRegistries().getItemRegistry().getName(this),
|
||||||
@ -51,18 +51,18 @@ public class ItemType implements RegistryItem, Keyed {
|
|||||||
);
|
);
|
||||||
return name.isEmpty() ? getId() : name;
|
return name.isEmpty() ? getId() : name;
|
||||||
});
|
});
|
||||||
private final LazyReference<Component> richName = LazyReference.from(() ->
|
private transient final LazyReference<Component> richName = LazyReference.from(() ->
|
||||||
WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.GAME_HOOKS)
|
WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.GAME_HOOKS)
|
||||||
.getRegistries().getItemRegistry().getRichName(this)
|
.getRegistries().getItemRegistry().getRichName(this)
|
||||||
);
|
);
|
||||||
private final LazyReference<ItemMaterial> itemMaterial = LazyReference.from(() ->
|
private transient final LazyReference<ItemMaterial> itemMaterial = LazyReference.from(() ->
|
||||||
WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.GAME_HOOKS)
|
WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.GAME_HOOKS)
|
||||||
.getRegistries().getItemRegistry().getMaterial(this)
|
.getRegistries().getItemRegistry().getMaterial(this)
|
||||||
);
|
);
|
||||||
//FAWE start
|
//FAWE start
|
||||||
private BlockType blockType;
|
private transient BlockType blockType;
|
||||||
private boolean initBlockType;
|
private transient boolean initBlockType;
|
||||||
private BaseItem defaultState;
|
private transient BaseItem defaultState;
|
||||||
//FAWE end
|
//FAWE end
|
||||||
|
|
||||||
public ItemType(String id) {
|
public ItemType(String id) {
|
||||||
|
@ -489,6 +489,8 @@
|
|||||||
"worldedit.jumpto.none": "No block in sight (or too far away)!",
|
"worldedit.jumpto.none": "No block in sight (or too far away)!",
|
||||||
"worldedit.up.obstructed": "You would hit something above you.",
|
"worldedit.up.obstructed": "You would hit something above you.",
|
||||||
"worldedit.up.moved": "Woosh!",
|
"worldedit.up.moved": "Woosh!",
|
||||||
|
"worldedit.cone.invalid-radius": "You must either specify 1 or 2 radius values.",
|
||||||
|
"worldedit.cone.created": "{0} blocks have been created.",
|
||||||
"worldedit.cyl.invalid-radius": "You must either specify 1 or 2 radius values.",
|
"worldedit.cyl.invalid-radius": "You must either specify 1 or 2 radius values.",
|
||||||
"worldedit.cyl.created": "{0} blocks have been created.",
|
"worldedit.cyl.created": "{0} blocks have been created.",
|
||||||
"worldedit.hcyl.thickness-too-large": "Thickness cannot be larger than x or z radii.",
|
"worldedit.hcyl.thickness-too-large": "Thickness cannot be larger than x or z radii.",
|
||||||
|
Loading…
Reference in New Issue
Block a user