mirror of
https://github.com/plexusorg/Plex-FAWE.git
synced 2024-12-22 17:27:38 +00:00
This commit is contained in:
commit
80ffba7a63
@ -13,7 +13,7 @@ jobs:
|
|||||||
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
|
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
|
||||||
DISCORD_USERNAME: FastAsyncWorldEdit Release
|
DISCORD_USERNAME: FastAsyncWorldEdit Release
|
||||||
DISCORD_AVATAR: https://raw.githubusercontent.com/IntellectualSites/Assets/main/plugins/FastAsyncWorldEdit/FastAsyncWorldEdit.png
|
DISCORD_AVATAR: https://raw.githubusercontent.com/IntellectualSites/Assets/main/plugins/FastAsyncWorldEdit/FastAsyncWorldEdit.png
|
||||||
uses: Ilshidur/action-discord@0c4b27844ba47cb1c7bee539c8eead5284ce9fa9 # ratchet:Ilshidur/action-discord@0.3.2
|
uses: Ilshidur/action-discord@0.3.2
|
||||||
with:
|
with:
|
||||||
args: |
|
args: |
|
||||||
"<@&525015715300900875> <@&706463154804097105> <@&671372968462516240>"
|
"<@&525015715300900875> <@&706463154804097105> <@&671372968462516240>"
|
||||||
|
8
.github/workflows/build-pr.yml
vendored
8
.github/workflows/build-pr.yml
vendored
@ -16,6 +16,12 @@ jobs:
|
|||||||
uses: actions/setup-java@v3
|
uses: actions/setup-java@v3
|
||||||
with:
|
with:
|
||||||
distribution: temurin
|
distribution: temurin
|
||||||
|
cache: gradle
|
||||||
java-version: 17
|
java-version: 17
|
||||||
- name: Build on ${{ matrix.os }}
|
- name: Build on ${{ matrix.os }}
|
||||||
run: ./gradlew clean build
|
run: ./gradlew build -s
|
||||||
|
- name: Archive artifacts
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: FastAsyncWorldEdit-SNAPSHOT
|
||||||
|
path: worldedit-bukkit/build/libs/FastAsyncWorldEdit-Bukkit-*.jar
|
||||||
|
8
.github/workflows/codeql.yml
vendored
8
.github/workflows/codeql.yml
vendored
@ -1,5 +1,7 @@
|
|||||||
name: "CodeQL"
|
name: "CodeQL"
|
||||||
on:
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
pull_request:
|
pull_request:
|
||||||
# The branches below must be a subset of the branches above
|
# The branches below must be a subset of the branches above
|
||||||
branches: [main]
|
branches: [main]
|
||||||
@ -18,6 +20,12 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
- name: Setup Java
|
||||||
|
uses: actions/setup-java@v3
|
||||||
|
with:
|
||||||
|
distribution: temurin
|
||||||
|
cache: gradle
|
||||||
|
java-version: 17
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@v2
|
uses: github/codeql-action/init@v2
|
||||||
with:
|
with:
|
||||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -33,3 +33,5 @@ worldedit-core/src/main/resources/lang/*
|
|||||||
/worldedit-core/.factorypath
|
/worldedit-core/.factorypath
|
||||||
|
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
### Run server ignore
|
||||||
|
run-*
|
||||||
|
11
Jenkinsfile
vendored
11
Jenkinsfile
vendored
@ -4,14 +4,13 @@ pipeline {
|
|||||||
disableConcurrentBuilds()
|
disableConcurrentBuilds()
|
||||||
}
|
}
|
||||||
stages {
|
stages {
|
||||||
stage('Set JDK 17') {
|
|
||||||
steps {
|
|
||||||
tool name: 'Temurin-17.0.6+10', type: 'jdk'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
stage('Build') {
|
stage('Build') {
|
||||||
steps {
|
steps {
|
||||||
sh './gradlew clean build'
|
withEnv([
|
||||||
|
"PATH+JAVA=${tool 'Temurin-17.0.7_7'}/bin"
|
||||||
|
]) {
|
||||||
|
sh './gradlew clean build'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stage('Archive artifacts') {
|
stage('Archive artifacts') {
|
||||||
|
@ -7,7 +7,7 @@ import xyz.jpenilla.runpaper.task.RunServer
|
|||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id("io.github.gradle-nexus.publish-plugin") version "1.3.0"
|
id("io.github.gradle-nexus.publish-plugin") version "1.3.0"
|
||||||
id("xyz.jpenilla.run-paper") version "2.0.1"
|
id("xyz.jpenilla.run-paper") version "2.1.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!File("$rootDir/.git").exists()) {
|
if (!File("$rootDir/.git").exists()) {
|
||||||
@ -34,7 +34,7 @@ logger.lifecycle("""
|
|||||||
*******************************************
|
*******************************************
|
||||||
""")
|
""")
|
||||||
|
|
||||||
var rootVersion by extra("2.6.0")
|
var rootVersion by extra("2.6.3")
|
||||||
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("")
|
||||||
@ -105,7 +105,7 @@ tasks {
|
|||||||
}
|
}
|
||||||
|
|
||||||
nexusPublishing {
|
nexusPublishing {
|
||||||
repositories {
|
this.repositories {
|
||||||
sonatype {
|
sonatype {
|
||||||
nexusUrl.set(URI.create("https://s01.oss.sonatype.org/service/local/"))
|
nexusUrl.set(URI.create("https://s01.oss.sonatype.org/service/local/"))
|
||||||
snapshotRepositoryUrl.set(URI.create("https://s01.oss.sonatype.org/content/repositories/snapshots/"))
|
snapshotRepositoryUrl.set(URI.create("https://s01.oss.sonatype.org/content/repositories/snapshots/"))
|
||||||
|
@ -22,9 +22,9 @@ val properties = Properties().also { props ->
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(gradleApi())
|
implementation(gradleApi())
|
||||||
implementation("org.ajoberstar.grgit:grgit-gradle:5.0.0")
|
implementation("org.ajoberstar.grgit:grgit-gradle:5.2.0")
|
||||||
implementation("gradle.plugin.com.github.johnrengelman:shadow:7.1.2")
|
implementation("com.github.johnrengelman:shadow:8.1.1")
|
||||||
implementation("io.papermc.paperweight.userdev:io.papermc.paperweight.userdev.gradle.plugin:1.5.3")
|
implementation("io.papermc.paperweight.userdev:io.papermc.paperweight.userdev.gradle.plugin:1.5.5")
|
||||||
}
|
}
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
|
@ -6,12 +6,12 @@ log4j = "2.19.0"
|
|||||||
|
|
||||||
# Plugins
|
# Plugins
|
||||||
dummypermscompat = "1.10"
|
dummypermscompat = "1.10"
|
||||||
worldguard-bukkit = "7.0.7"
|
worldguard-bukkit = "7.0.8"
|
||||||
mapmanager = "1.8.0-SNAPSHOT"
|
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.98.4.18"
|
towny = "0.99.1.2"
|
||||||
|
|
||||||
# Third party
|
# Third party
|
||||||
bstats = "3.0.2"
|
bstats = "3.0.2"
|
||||||
@ -23,7 +23,7 @@ auto-value = "1.10.1"
|
|||||||
findbugs = "3.0.2"
|
findbugs = "3.0.2"
|
||||||
rhino-runtime = "1.7.14"
|
rhino-runtime = "1.7.14"
|
||||||
zstd-jni = "1.4.8-1" # Not latest as it can be difficult to obtain latest ZSTD libs
|
zstd-jni = "1.4.8-1" # Not latest as it can be difficult to obtain latest ZSTD libs
|
||||||
antlr4 = "4.12.0"
|
antlr4 = "4.13.0"
|
||||||
json-simple = "1.1.1"
|
json-simple = "1.1.1"
|
||||||
jlibnoise = "1.0.0"
|
jlibnoise = "1.0.0"
|
||||||
jchronic = "0.2.4a"
|
jchronic = "0.2.4a"
|
||||||
@ -36,7 +36,7 @@ text = "3.0.4"
|
|||||||
piston = "0.5.7"
|
piston = "0.5.7"
|
||||||
|
|
||||||
# Tests
|
# Tests
|
||||||
mockito = "5.2.0"
|
mockito = "5.3.1"
|
||||||
|
|
||||||
# Gradle plugins
|
# Gradle plugins
|
||||||
pluginyml = "0.5.3"
|
pluginyml = "0.5.3"
|
||||||
@ -54,7 +54,7 @@ mapmanager = { group = "com.github.InventivetalentDev", name = "MapManager", ver
|
|||||||
griefprevention = { group = "com.github.TechFortress", name = "GriefPrevention", version.ref = "griefprevention" }
|
griefprevention = { group = "com.github.TechFortress", name = "GriefPrevention", version.ref = "griefprevention" }
|
||||||
griefdefender = { group = "com.griefdefender", name = "api", version.ref = "griefdefender" }
|
griefdefender = { group = "com.griefdefender", name = "api", version.ref = "griefdefender" }
|
||||||
residence = { group = "com.bekvon.bukkit.residence", name = "Residence", version.ref = "residence" }
|
residence = { group = "com.bekvon.bukkit.residence", name = "Residence", version.ref = "residence" }
|
||||||
towny = { group = "com.github.TownyAdvanced", name = "Towny", version.ref = "towny" }
|
towny = { group = "com.palmergames.bukkit.towny", name = "towny", version.ref = "towny" }
|
||||||
|
|
||||||
# Third Party
|
# Third Party
|
||||||
bstatsBase = { group = "org.bstats", name = "bstats-base", version.ref = "bstats" }
|
bstatsBase = { group = "org.bstats", name = "bstats-base", version.ref = "bstats" }
|
||||||
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,6 +1,6 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip
|
||||||
networkTimeout=10000
|
networkTimeout=10000
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
7
gradlew
vendored
7
gradlew
vendored
@ -85,9 +85,6 @@ done
|
|||||||
APP_BASE_NAME=${0##*/}
|
APP_BASE_NAME=${0##*/}
|
||||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||||
|
|
||||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
|
||||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
|
||||||
|
|
||||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
MAX_FD=maximum
|
MAX_FD=maximum
|
||||||
|
|
||||||
@ -197,6 +194,10 @@ if "$cygwin" || "$msys" ; then
|
|||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
|
|
||||||
# Collect all arguments for the java command;
|
# Collect all arguments for the java command;
|
||||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||||
# shell script including quotes and variable substitutions, so put them in
|
# shell script including quotes and variable substitutions, so put them in
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import io.papermc.paperweight.userdev.PaperweightUserDependenciesExtension
|
||||||
|
|
||||||
applyPaperweightAdapterConfiguration()
|
applyPaperweightAdapterConfiguration()
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
@ -19,6 +21,6 @@ configurations.all {
|
|||||||
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
paperDevBundle("1.17.1-R0.1-20220414.034903-210")
|
the<PaperweightUserDependenciesExtension>().paperDevBundle("1.17.1-R0.1-20220414.034903-210")
|
||||||
compileOnly("io.papermc:paperlib")
|
compileOnly("io.papermc:paperlib")
|
||||||
}
|
}
|
||||||
|
@ -103,6 +103,7 @@ import java.util.HashMap;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.OptionalInt;
|
import java.util.OptionalInt;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
@ -658,10 +659,17 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
|||||||
.registryAccess()
|
.registryAccess()
|
||||||
.ownedRegistryOrThrow(
|
.ownedRegistryOrThrow(
|
||||||
Registry.BIOME_REGISTRY);
|
Registry.BIOME_REGISTRY);
|
||||||
return biomeRegistry.stream()
|
List<ResourceLocation> keys = biomeRegistry.stream()
|
||||||
.map(biomeRegistry::getKey)
|
.map(biomeRegistry::getKey).filter(Objects::nonNull).toList();
|
||||||
.map(CraftNamespacedKey::fromMinecraft)
|
List<NamespacedKey> namespacedKeys = new ArrayList<>();
|
||||||
.collect(Collectors.toList());
|
for (ResourceLocation key : keys) {
|
||||||
|
try {
|
||||||
|
namespacedKeys.add(CraftNamespacedKey.fromMinecraft(key));
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
LOGGER.error("Error converting biome key {}", key.toString(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return namespacedKeys;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -445,10 +445,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
|
|
||||||
bitMask |= 1 << layer;
|
bitMask |= 1 << layer;
|
||||||
|
|
||||||
// Changes may still be written to chunk SET
|
char[] setArr = set.load(layerNo);
|
||||||
char[] tmp = set.load(layerNo);
|
|
||||||
char[] setArr = new char[4096];
|
|
||||||
System.arraycopy(tmp, 0, setArr, 0, 4096);
|
|
||||||
|
|
||||||
// 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.
|
||||||
|
@ -29,7 +29,9 @@ import net.minecraft.server.level.ChunkHolder;
|
|||||||
import net.minecraft.server.level.ChunkMap;
|
import net.minecraft.server.level.ChunkMap;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
import net.minecraft.server.level.ServerPlayer;
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
|
import net.minecraft.server.level.TicketType;
|
||||||
import net.minecraft.util.BitStorage;
|
import net.minecraft.util.BitStorage;
|
||||||
|
import net.minecraft.util.Unit;
|
||||||
import net.minecraft.world.entity.Entity;
|
import net.minecraft.world.entity.Entity;
|
||||||
import net.minecraft.world.level.ChunkPos;
|
import net.minecraft.world.level.ChunkPos;
|
||||||
import net.minecraft.world.level.LevelAccessor;
|
import net.minecraft.world.level.LevelAccessor;
|
||||||
@ -209,10 +211,12 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
|||||||
} else {
|
} else {
|
||||||
LevelChunk nmsChunk = serverLevel.getChunkSource().getChunkAtIfCachedImmediately(chunkX, chunkZ);
|
LevelChunk nmsChunk = serverLevel.getChunkSource().getChunkAtIfCachedImmediately(chunkX, chunkZ);
|
||||||
if (nmsChunk != null) {
|
if (nmsChunk != null) {
|
||||||
|
addTicket(serverLevel, chunkX, chunkZ);
|
||||||
return nmsChunk;
|
return nmsChunk;
|
||||||
}
|
}
|
||||||
nmsChunk = serverLevel.getChunkSource().getChunkAtIfLoadedImmediately(chunkX, chunkZ);
|
nmsChunk = serverLevel.getChunkSource().getChunkAtIfLoadedImmediately(chunkX, chunkZ);
|
||||||
if (nmsChunk != null) {
|
if (nmsChunk != null) {
|
||||||
|
addTicket(serverLevel, chunkX, chunkZ);
|
||||||
return nmsChunk;
|
return nmsChunk;
|
||||||
}
|
}
|
||||||
// Avoid "async" methods from the main thread.
|
// Avoid "async" methods from the main thread.
|
||||||
@ -230,6 +234,13 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
|||||||
return TaskManager.taskManager().sync(() -> serverLevel.getChunk(chunkX, chunkZ));
|
return TaskManager.taskManager().sync(() -> serverLevel.getChunk(chunkX, chunkZ));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void addTicket(ServerLevel serverLevel, int chunkX, int chunkZ) {
|
||||||
|
// Ensure chunk is definitely loaded before applying a ticket
|
||||||
|
net.minecraft.server.MCUtil.MAIN_EXECUTOR.execute(() -> serverLevel
|
||||||
|
.getChunkSource()
|
||||||
|
.addRegionTicket(TicketType.PLUGIN, new ChunkPos(chunkX, chunkZ), 0, Unit.INSTANCE));
|
||||||
|
}
|
||||||
|
|
||||||
public static ChunkHolder getPlayerChunk(ServerLevel nmsWorld, final int chunkX, final int chunkZ) {
|
public static ChunkHolder getPlayerChunk(ServerLevel nmsWorld, final int chunkX, final int chunkZ) {
|
||||||
ChunkMap chunkMap = nmsWorld.getChunkSource().chunkMap;
|
ChunkMap chunkMap = nmsWorld.getChunkSource().chunkMap;
|
||||||
try {
|
try {
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import io.papermc.paperweight.userdev.PaperweightUserDependenciesExtension
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
java
|
java
|
||||||
}
|
}
|
||||||
@ -10,6 +12,6 @@ repositories {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
// https://papermc.io/repo/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/
|
// https://papermc.io/repo/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/
|
||||||
paperDevBundle("1.18.2-R0.1-20220920.010157-167")
|
the<PaperweightUserDependenciesExtension>().paperDevBundle("1.18.2-R0.1-20220920.010157-167")
|
||||||
compileOnly("io.papermc:paperlib")
|
compileOnly("io.papermc:paperlib")
|
||||||
}
|
}
|
||||||
|
@ -656,10 +656,17 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
|||||||
.registryAccess()
|
.registryAccess()
|
||||||
.ownedRegistryOrThrow(
|
.ownedRegistryOrThrow(
|
||||||
Registry.BIOME_REGISTRY);
|
Registry.BIOME_REGISTRY);
|
||||||
return biomeRegistry.stream()
|
List<ResourceLocation> keys = biomeRegistry.stream()
|
||||||
.map(biomeRegistry::getKey).filter(Objects::nonNull)
|
.map(biomeRegistry::getKey).filter(Objects::nonNull).toList();
|
||||||
.map(CraftNamespacedKey::fromMinecraft)
|
List<NamespacedKey> namespacedKeys = new ArrayList<>();
|
||||||
.collect(Collectors.toList());
|
for (ResourceLocation key : keys) {
|
||||||
|
try {
|
||||||
|
namespacedKeys.add(CraftNamespacedKey.fromMinecraft(key));
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
LOGGER.error("Error converting biome key {}", key.toString(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return namespacedKeys;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -491,9 +491,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
|
|
||||||
bitMask |= 1 << getSectionIndex;
|
bitMask |= 1 << getSectionIndex;
|
||||||
|
|
||||||
char[] tmp = set.load(layerNo);
|
char[] setArr = set.load(layerNo);
|
||||||
char[] setArr = new char[4096];
|
|
||||||
System.arraycopy(tmp, 0, setArr, 0, 4096);
|
|
||||||
|
|
||||||
// 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.
|
||||||
|
@ -29,9 +29,11 @@ import net.minecraft.server.level.ChunkHolder;
|
|||||||
import net.minecraft.server.level.ChunkMap;
|
import net.minecraft.server.level.ChunkMap;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
import net.minecraft.server.level.ServerPlayer;
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
|
import net.minecraft.server.level.TicketType;
|
||||||
import net.minecraft.util.BitStorage;
|
import net.minecraft.util.BitStorage;
|
||||||
import net.minecraft.util.SimpleBitStorage;
|
import net.minecraft.util.SimpleBitStorage;
|
||||||
import net.minecraft.util.ThreadingDetector;
|
import net.minecraft.util.ThreadingDetector;
|
||||||
|
import net.minecraft.util.Unit;
|
||||||
import net.minecraft.util.ZeroBitStorage;
|
import net.minecraft.util.ZeroBitStorage;
|
||||||
import net.minecraft.world.entity.Entity;
|
import net.minecraft.world.entity.Entity;
|
||||||
import net.minecraft.world.level.ChunkPos;
|
import net.minecraft.world.level.ChunkPos;
|
||||||
@ -237,10 +239,12 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
|||||||
} else {
|
} else {
|
||||||
LevelChunk nmsChunk = serverLevel.getChunkSource().getChunkAtIfCachedImmediately(chunkX, chunkZ);
|
LevelChunk nmsChunk = serverLevel.getChunkSource().getChunkAtIfCachedImmediately(chunkX, chunkZ);
|
||||||
if (nmsChunk != null) {
|
if (nmsChunk != null) {
|
||||||
|
addTicket(serverLevel, chunkX, chunkZ);
|
||||||
return nmsChunk;
|
return nmsChunk;
|
||||||
}
|
}
|
||||||
nmsChunk = serverLevel.getChunkSource().getChunkAtIfLoadedImmediately(chunkX, chunkZ);
|
nmsChunk = serverLevel.getChunkSource().getChunkAtIfLoadedImmediately(chunkX, chunkZ);
|
||||||
if (nmsChunk != null) {
|
if (nmsChunk != null) {
|
||||||
|
addTicket(serverLevel, chunkX, chunkZ);
|
||||||
return nmsChunk;
|
return nmsChunk;
|
||||||
}
|
}
|
||||||
// Avoid "async" methods from the main thread.
|
// Avoid "async" methods from the main thread.
|
||||||
@ -258,6 +262,13 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
|||||||
return TaskManager.taskManager().sync(() -> serverLevel.getChunk(chunkX, chunkZ));
|
return TaskManager.taskManager().sync(() -> serverLevel.getChunk(chunkX, chunkZ));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void addTicket(ServerLevel serverLevel, int chunkX, int chunkZ) {
|
||||||
|
// Ensure chunk is definitely loaded before applying a ticket
|
||||||
|
net.minecraft.server.MCUtil.MAIN_EXECUTOR.execute(() -> serverLevel
|
||||||
|
.getChunkSource()
|
||||||
|
.addRegionTicket(TicketType.PLUGIN, new ChunkPos(chunkX, chunkZ), 0, Unit.INSTANCE));
|
||||||
|
}
|
||||||
|
|
||||||
public static ChunkHolder getPlayerChunk(ServerLevel nmsWorld, final int chunkX, final int chunkZ) {
|
public static ChunkHolder getPlayerChunk(ServerLevel nmsWorld, final int chunkX, final int chunkZ) {
|
||||||
ChunkMap chunkMap = nmsWorld.getChunkSource().chunkMap;
|
ChunkMap chunkMap = nmsWorld.getChunkSource().chunkMap;
|
||||||
try {
|
try {
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import io.papermc.paperweight.userdev.PaperweightUserDependenciesExtension
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
java
|
java
|
||||||
}
|
}
|
||||||
@ -9,6 +11,6 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
paperDevBundle("1.19.2-R0.1-20221206.184705-189")
|
the<PaperweightUserDependenciesExtension>().paperDevBundle("1.19.2-R0.1-20221206.184705-189")
|
||||||
compileOnly("io.papermc:paperlib")
|
compileOnly("io.papermc:paperlib")
|
||||||
}
|
}
|
||||||
|
@ -645,10 +645,17 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
|||||||
.registryAccess()
|
.registryAccess()
|
||||||
.ownedRegistryOrThrow(
|
.ownedRegistryOrThrow(
|
||||||
Registry.BIOME_REGISTRY);
|
Registry.BIOME_REGISTRY);
|
||||||
return biomeRegistry.stream()
|
List<ResourceLocation> keys = biomeRegistry.stream()
|
||||||
.map(biomeRegistry::getKey).filter(Objects::nonNull)
|
.map(biomeRegistry::getKey).filter(Objects::nonNull).toList();
|
||||||
.map(CraftNamespacedKey::fromMinecraft)
|
List<NamespacedKey> namespacedKeys = new ArrayList<>();
|
||||||
.collect(Collectors.toList());
|
for (ResourceLocation key : keys) {
|
||||||
|
try {
|
||||||
|
namespacedKeys.add(CraftNamespacedKey.fromMinecraft(key));
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
LOGGER.error("Error converting biome key {}", key.toString(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return namespacedKeys;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -488,9 +488,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
|
|
||||||
bitMask |= 1 << getSectionIndex;
|
bitMask |= 1 << getSectionIndex;
|
||||||
|
|
||||||
char[] tmp = set.load(layerNo);
|
char[] setArr = set.load(layerNo);
|
||||||
char[] setArr = new char[4096];
|
|
||||||
System.arraycopy(tmp, 0, setArr, 0, 4096);
|
|
||||||
|
|
||||||
// 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.
|
||||||
|
@ -29,10 +29,12 @@ import net.minecraft.server.level.ChunkHolder;
|
|||||||
import net.minecraft.server.level.ChunkMap;
|
import net.minecraft.server.level.ChunkMap;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
import net.minecraft.server.level.ServerPlayer;
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
|
import net.minecraft.server.level.TicketType;
|
||||||
import net.minecraft.util.BitStorage;
|
import net.minecraft.util.BitStorage;
|
||||||
import net.minecraft.util.ExceptionCollector;
|
import net.minecraft.util.ExceptionCollector;
|
||||||
import net.minecraft.util.SimpleBitStorage;
|
import net.minecraft.util.SimpleBitStorage;
|
||||||
import net.minecraft.util.ThreadingDetector;
|
import net.minecraft.util.ThreadingDetector;
|
||||||
|
import net.minecraft.util.Unit;
|
||||||
import net.minecraft.util.ZeroBitStorage;
|
import net.minecraft.util.ZeroBitStorage;
|
||||||
import net.minecraft.world.entity.Entity;
|
import net.minecraft.world.entity.Entity;
|
||||||
import net.minecraft.world.level.ChunkPos;
|
import net.minecraft.world.level.ChunkPos;
|
||||||
@ -270,10 +272,12 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
|||||||
} else {
|
} else {
|
||||||
LevelChunk nmsChunk = serverLevel.getChunkSource().getChunkAtIfCachedImmediately(chunkX, chunkZ);
|
LevelChunk nmsChunk = serverLevel.getChunkSource().getChunkAtIfCachedImmediately(chunkX, chunkZ);
|
||||||
if (nmsChunk != null) {
|
if (nmsChunk != null) {
|
||||||
|
addTicket(serverLevel, chunkX, chunkZ);
|
||||||
return nmsChunk;
|
return nmsChunk;
|
||||||
}
|
}
|
||||||
nmsChunk = serverLevel.getChunkSource().getChunkAtIfLoadedImmediately(chunkX, chunkZ);
|
nmsChunk = serverLevel.getChunkSource().getChunkAtIfLoadedImmediately(chunkX, chunkZ);
|
||||||
if (nmsChunk != null) {
|
if (nmsChunk != null) {
|
||||||
|
addTicket(serverLevel, chunkX, chunkZ);
|
||||||
return nmsChunk;
|
return nmsChunk;
|
||||||
}
|
}
|
||||||
// Avoid "async" methods from the main thread.
|
// Avoid "async" methods from the main thread.
|
||||||
@ -291,6 +295,13 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
|||||||
return TaskManager.taskManager().sync(() -> serverLevel.getChunk(chunkX, chunkZ));
|
return TaskManager.taskManager().sync(() -> serverLevel.getChunk(chunkX, chunkZ));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void addTicket(ServerLevel serverLevel, int chunkX, int chunkZ) {
|
||||||
|
// Ensure chunk is definitely loaded before applying a ticket
|
||||||
|
io.papermc.paper.util.MCUtil.MAIN_EXECUTOR.execute(() -> serverLevel
|
||||||
|
.getChunkSource()
|
||||||
|
.addRegionTicket(TicketType.PLUGIN, new ChunkPos(chunkX, chunkZ), 0, Unit.INSTANCE));
|
||||||
|
}
|
||||||
|
|
||||||
public static ChunkHolder getPlayerChunk(ServerLevel nmsWorld, final int chunkX, final int chunkZ) {
|
public static ChunkHolder getPlayerChunk(ServerLevel nmsWorld, final int chunkX, final int chunkZ) {
|
||||||
ChunkMap chunkMap = nmsWorld.getChunkSource().chunkMap;
|
ChunkMap chunkMap = nmsWorld.getChunkSource().chunkMap;
|
||||||
try {
|
try {
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import io.papermc.paperweight.userdev.PaperweightUserDependenciesExtension
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
java
|
java
|
||||||
}
|
}
|
||||||
@ -10,6 +12,6 @@ repositories {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
// https://papermc.io/repo/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/
|
// https://papermc.io/repo/service/rest/repository/browse/maven-public/io/papermc/paper/dev-bundle/
|
||||||
paperDevBundle("1.19.3-R0.1-20230312.180621-141")
|
the<PaperweightUserDependenciesExtension>().paperDevBundle("1.19.3-R0.1-20230312.180621-141")
|
||||||
compileOnly("io.papermc:paperlib")
|
compileOnly("io.papermc:paperlib")
|
||||||
}
|
}
|
||||||
|
@ -650,10 +650,17 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
|||||||
.getServer()
|
.getServer()
|
||||||
.registryAccess()
|
.registryAccess()
|
||||||
.registryOrThrow(BIOME);
|
.registryOrThrow(BIOME);
|
||||||
return biomeRegistry.stream()
|
List<ResourceLocation> keys = biomeRegistry.stream()
|
||||||
.map(biomeRegistry::getKey).filter(Objects::nonNull)
|
.map(biomeRegistry::getKey).filter(Objects::nonNull).toList();
|
||||||
.map(CraftNamespacedKey::fromMinecraft)
|
List<NamespacedKey> namespacedKeys = new ArrayList<>();
|
||||||
.collect(Collectors.toList());
|
for (ResourceLocation key : keys) {
|
||||||
|
try {
|
||||||
|
namespacedKeys.add(CraftNamespacedKey.fromMinecraft(key));
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
LOGGER.error("Error converting biome key {}", key.toString(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return namespacedKeys;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -490,9 +490,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
|
|
||||||
bitMask |= 1 << getSectionIndex;
|
bitMask |= 1 << getSectionIndex;
|
||||||
|
|
||||||
char[] tmp = set.load(layerNo);
|
char[] setArr = set.load(layerNo);
|
||||||
char[] setArr = new char[4096];
|
|
||||||
System.arraycopy(tmp, 0, setArr, 0, 4096);
|
|
||||||
|
|
||||||
// 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.
|
||||||
|
@ -29,10 +29,12 @@ import net.minecraft.server.level.ChunkHolder;
|
|||||||
import net.minecraft.server.level.ChunkMap;
|
import net.minecraft.server.level.ChunkMap;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
import net.minecraft.server.level.ServerPlayer;
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
|
import net.minecraft.server.level.TicketType;
|
||||||
import net.minecraft.util.BitStorage;
|
import net.minecraft.util.BitStorage;
|
||||||
import net.minecraft.util.ExceptionCollector;
|
import net.minecraft.util.ExceptionCollector;
|
||||||
import net.minecraft.util.SimpleBitStorage;
|
import net.minecraft.util.SimpleBitStorage;
|
||||||
import net.minecraft.util.ThreadingDetector;
|
import net.minecraft.util.ThreadingDetector;
|
||||||
|
import net.minecraft.util.Unit;
|
||||||
import net.minecraft.util.ZeroBitStorage;
|
import net.minecraft.util.ZeroBitStorage;
|
||||||
import net.minecraft.world.entity.Entity;
|
import net.minecraft.world.entity.Entity;
|
||||||
import net.minecraft.world.level.ChunkPos;
|
import net.minecraft.world.level.ChunkPos;
|
||||||
@ -267,10 +269,12 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
|||||||
} else {
|
} else {
|
||||||
LevelChunk nmsChunk = serverLevel.getChunkSource().getChunkAtIfCachedImmediately(chunkX, chunkZ);
|
LevelChunk nmsChunk = serverLevel.getChunkSource().getChunkAtIfCachedImmediately(chunkX, chunkZ);
|
||||||
if (nmsChunk != null) {
|
if (nmsChunk != null) {
|
||||||
|
addTicket(serverLevel, chunkX, chunkZ);
|
||||||
return nmsChunk;
|
return nmsChunk;
|
||||||
}
|
}
|
||||||
nmsChunk = serverLevel.getChunkSource().getChunkAtIfLoadedImmediately(chunkX, chunkZ);
|
nmsChunk = serverLevel.getChunkSource().getChunkAtIfLoadedImmediately(chunkX, chunkZ);
|
||||||
if (nmsChunk != null) {
|
if (nmsChunk != null) {
|
||||||
|
addTicket(serverLevel, chunkX, chunkZ);
|
||||||
return nmsChunk;
|
return nmsChunk;
|
||||||
}
|
}
|
||||||
// Avoid "async" methods from the main thread.
|
// Avoid "async" methods from the main thread.
|
||||||
@ -288,6 +292,13 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
|||||||
return TaskManager.taskManager().sync(() -> serverLevel.getChunk(chunkX, chunkZ));
|
return TaskManager.taskManager().sync(() -> serverLevel.getChunk(chunkX, chunkZ));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void addTicket(ServerLevel serverLevel, int chunkX, int chunkZ) {
|
||||||
|
// Ensure chunk is definitely loaded before applying a ticket
|
||||||
|
io.papermc.paper.util.MCUtil.MAIN_EXECUTOR.execute(() -> serverLevel
|
||||||
|
.getChunkSource()
|
||||||
|
.addRegionTicket(TicketType.PLUGIN, new ChunkPos(chunkX, chunkZ), 0, Unit.INSTANCE));
|
||||||
|
}
|
||||||
|
|
||||||
public static ChunkHolder getPlayerChunk(ServerLevel nmsWorld, final int chunkX, final int chunkZ) {
|
public static ChunkHolder getPlayerChunk(ServerLevel nmsWorld, final int chunkX, final int chunkZ) {
|
||||||
ChunkMap chunkMap = nmsWorld.getChunkSource().chunkMap;
|
ChunkMap chunkMap = nmsWorld.getChunkSource().chunkMap;
|
||||||
try {
|
try {
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import io.papermc.paperweight.userdev.PaperweightUserDependenciesExtension
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
java
|
java
|
||||||
}
|
}
|
||||||
@ -9,6 +11,6 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
paperDevBundle("1.19.4-R0.1-20230412.010331-64")
|
the<PaperweightUserDependenciesExtension>().paperDevBundle("1.19.4-R0.1-20230601.025018-99")
|
||||||
compileOnly("io.papermc:paperlib")
|
compileOnly("io.papermc:paperlib")
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,6 @@ import com.google.common.collect.ImmutableMap;
|
|||||||
import com.sk89q.jnbt.Tag;
|
import com.sk89q.jnbt.Tag;
|
||||||
import com.sk89q.worldedit.EditSession;
|
import com.sk89q.worldedit.EditSession;
|
||||||
import com.sk89q.worldedit.blocks.BaseItemStack;
|
import com.sk89q.worldedit.blocks.BaseItemStack;
|
||||||
import com.sk89q.worldedit.blocks.TileEntityBlock;
|
|
||||||
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
import com.sk89q.worldedit.bukkit.BukkitAdapter;
|
||||||
import com.sk89q.worldedit.bukkit.BukkitWorld;
|
import com.sk89q.worldedit.bukkit.BukkitWorld;
|
||||||
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
|
import com.sk89q.worldedit.bukkit.adapter.BukkitImplAdapter;
|
||||||
@ -50,7 +49,6 @@ import com.sk89q.worldedit.world.block.BaseBlock;
|
|||||||
import com.sk89q.worldedit.world.block.BlockState;
|
import com.sk89q.worldedit.world.block.BlockState;
|
||||||
import com.sk89q.worldedit.world.block.BlockStateHolder;
|
import com.sk89q.worldedit.world.block.BlockStateHolder;
|
||||||
import com.sk89q.worldedit.world.block.BlockType;
|
import com.sk89q.worldedit.world.block.BlockType;
|
||||||
import com.sk89q.worldedit.world.block.BlockTypes;
|
|
||||||
import com.sk89q.worldedit.world.block.BlockTypesCache;
|
import com.sk89q.worldedit.world.block.BlockTypesCache;
|
||||||
import com.sk89q.worldedit.world.entity.EntityType;
|
import com.sk89q.worldedit.world.entity.EntityType;
|
||||||
import com.sk89q.worldedit.world.item.ItemType;
|
import com.sk89q.worldedit.world.item.ItemType;
|
||||||
@ -60,7 +58,6 @@ import net.minecraft.core.BlockPos;
|
|||||||
import net.minecraft.core.Registry;
|
import net.minecraft.core.Registry;
|
||||||
import net.minecraft.core.WritableRegistry;
|
import net.minecraft.core.WritableRegistry;
|
||||||
import net.minecraft.core.registries.Registries;
|
import net.minecraft.core.registries.Registries;
|
||||||
import net.minecraft.nbt.IntTag;
|
|
||||||
import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
|
import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.server.MinecraftServer;
|
import net.minecraft.server.MinecraftServer;
|
||||||
@ -71,14 +68,12 @@ import net.minecraft.server.level.ServerPlayer;
|
|||||||
import net.minecraft.util.StringRepresentable;
|
import net.minecraft.util.StringRepresentable;
|
||||||
import net.minecraft.world.entity.Entity;
|
import net.minecraft.world.entity.Entity;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
import net.minecraft.world.level.Level;
|
|
||||||
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.entity.BlockEntity;
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
|
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
|
||||||
import net.minecraft.world.level.block.state.properties.DirectionProperty;
|
import net.minecraft.world.level.block.state.properties.DirectionProperty;
|
||||||
import net.minecraft.world.level.chunk.LevelChunk;
|
import net.minecraft.world.level.chunk.LevelChunk;
|
||||||
import net.minecraft.world.level.chunk.LevelChunkSection;
|
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
@ -86,7 +81,6 @@ import org.bukkit.Material;
|
|||||||
import org.bukkit.NamespacedKey;
|
import org.bukkit.NamespacedKey;
|
||||||
import org.bukkit.TreeType;
|
import org.bukkit.TreeType;
|
||||||
import org.bukkit.block.data.BlockData;
|
import org.bukkit.block.data.BlockData;
|
||||||
import org.bukkit.craftbukkit.v1_19_R3.CraftChunk;
|
|
||||||
import org.bukkit.craftbukkit.v1_19_R3.CraftServer;
|
import org.bukkit.craftbukkit.v1_19_R3.CraftServer;
|
||||||
import org.bukkit.craftbukkit.v1_19_R3.CraftWorld;
|
import org.bukkit.craftbukkit.v1_19_R3.CraftWorld;
|
||||||
import org.bukkit.craftbukkit.v1_19_R3.block.CraftBlockState;
|
import org.bukkit.craftbukkit.v1_19_R3.block.CraftBlockState;
|
||||||
@ -602,10 +596,17 @@ public final class PaperweightFaweAdapter extends CachedBukkitAdapter implements
|
|||||||
.getServer()
|
.getServer()
|
||||||
.registryAccess()
|
.registryAccess()
|
||||||
.registryOrThrow(BIOME);
|
.registryOrThrow(BIOME);
|
||||||
return biomeRegistry.stream()
|
List<ResourceLocation> keys = biomeRegistry.stream()
|
||||||
.map(biomeRegistry::getKey).filter(Objects::nonNull)
|
.map(biomeRegistry::getKey).filter(Objects::nonNull).toList();
|
||||||
.map(CraftNamespacedKey::fromMinecraft)
|
List<NamespacedKey> namespacedKeys = new ArrayList<>();
|
||||||
.collect(Collectors.toList());
|
for (ResourceLocation key : keys) {
|
||||||
|
try {
|
||||||
|
namespacedKeys.add(CraftNamespacedKey.fromMinecraft(key));
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
LOGGER.error("Error converting biome key {}", key.toString(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return namespacedKeys;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -490,9 +490,7 @@ public class PaperweightGetBlocks extends CharGetBlocks implements BukkitGetBloc
|
|||||||
|
|
||||||
bitMask |= 1 << getSectionIndex;
|
bitMask |= 1 << getSectionIndex;
|
||||||
|
|
||||||
char[] tmp = set.load(layerNo);
|
char[] setArr = set.load(layerNo);
|
||||||
char[] setArr = new char[4096];
|
|
||||||
System.arraycopy(tmp, 0, setArr, 0, 4096);
|
|
||||||
|
|
||||||
// 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.
|
||||||
|
@ -29,10 +29,12 @@ import net.minecraft.server.level.ChunkHolder;
|
|||||||
import net.minecraft.server.level.ChunkMap;
|
import net.minecraft.server.level.ChunkMap;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
import net.minecraft.server.level.ServerPlayer;
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
|
import net.minecraft.server.level.TicketType;
|
||||||
import net.minecraft.util.BitStorage;
|
import net.minecraft.util.BitStorage;
|
||||||
import net.minecraft.util.ExceptionCollector;
|
import net.minecraft.util.ExceptionCollector;
|
||||||
import net.minecraft.util.SimpleBitStorage;
|
import net.minecraft.util.SimpleBitStorage;
|
||||||
import net.minecraft.util.ThreadingDetector;
|
import net.minecraft.util.ThreadingDetector;
|
||||||
|
import net.minecraft.util.Unit;
|
||||||
import net.minecraft.util.ZeroBitStorage;
|
import net.minecraft.util.ZeroBitStorage;
|
||||||
import net.minecraft.world.entity.Entity;
|
import net.minecraft.world.entity.Entity;
|
||||||
import net.minecraft.world.level.ChunkPos;
|
import net.minecraft.world.level.ChunkPos;
|
||||||
@ -182,7 +184,7 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
|||||||
removeBlockEntityTicker.setAccessible(true);
|
removeBlockEntityTicker.setAccessible(true);
|
||||||
methodremoveTickingBlockEntity = lookup.unreflect(removeBlockEntityTicker);
|
methodremoveTickingBlockEntity = lookup.unreflect(removeBlockEntityTicker);
|
||||||
|
|
||||||
fieldRemove = BlockEntity.class.getDeclaredField(Refraction.pickName("remove", "p"));
|
fieldRemove = BlockEntity.class.getDeclaredField(Refraction.pickName("remove", "q"));
|
||||||
fieldRemove.setAccessible(true);
|
fieldRemove.setAccessible(true);
|
||||||
|
|
||||||
CHUNKSECTION_BASE = unsafe.arrayBaseOffset(LevelChunkSection[].class);
|
CHUNKSECTION_BASE = unsafe.arrayBaseOffset(LevelChunkSection[].class);
|
||||||
@ -292,10 +294,12 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
|||||||
} else {
|
} else {
|
||||||
LevelChunk nmsChunk = serverLevel.getChunkSource().getChunkAtIfCachedImmediately(chunkX, chunkZ);
|
LevelChunk nmsChunk = serverLevel.getChunkSource().getChunkAtIfCachedImmediately(chunkX, chunkZ);
|
||||||
if (nmsChunk != null) {
|
if (nmsChunk != null) {
|
||||||
|
addTicket(serverLevel, chunkX, chunkZ);
|
||||||
return nmsChunk;
|
return nmsChunk;
|
||||||
}
|
}
|
||||||
nmsChunk = serverLevel.getChunkSource().getChunkAtIfLoadedImmediately(chunkX, chunkZ);
|
nmsChunk = serverLevel.getChunkSource().getChunkAtIfLoadedImmediately(chunkX, chunkZ);
|
||||||
if (nmsChunk != null) {
|
if (nmsChunk != null) {
|
||||||
|
addTicket(serverLevel, chunkX, chunkZ);
|
||||||
return nmsChunk;
|
return nmsChunk;
|
||||||
}
|
}
|
||||||
// Avoid "async" methods from the main thread.
|
// Avoid "async" methods from the main thread.
|
||||||
@ -305,6 +309,7 @@ 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 = (CraftChunk) future.get();
|
||||||
|
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) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
@ -313,6 +318,13 @@ public final class PaperweightPlatformAdapter extends NMSAdapter {
|
|||||||
return TaskManager.taskManager().sync(() -> serverLevel.getChunk(chunkX, chunkZ));
|
return TaskManager.taskManager().sync(() -> serverLevel.getChunk(chunkX, chunkZ));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void addTicket(ServerLevel serverLevel, int chunkX, int chunkZ) {
|
||||||
|
// Ensure chunk is definitely loaded before applying a ticket
|
||||||
|
io.papermc.paper.util.MCUtil.MAIN_EXECUTOR.execute(() -> serverLevel
|
||||||
|
.getChunkSource()
|
||||||
|
.addRegionTicket(TicketType.UNLOAD_COOLDOWN, new ChunkPos(chunkX, chunkZ), 0, Unit.INSTANCE));
|
||||||
|
}
|
||||||
|
|
||||||
public static ChunkHolder getPlayerChunk(ServerLevel nmsWorld, final int chunkX, final int chunkZ) {
|
public static ChunkHolder getPlayerChunk(ServerLevel nmsWorld, final int chunkX, final int chunkZ) {
|
||||||
ChunkMap chunkMap = nmsWorld.getChunkSource().chunkMap;
|
ChunkMap chunkMap = nmsWorld.getChunkSource().chunkMap;
|
||||||
try {
|
try {
|
||||||
|
@ -3,7 +3,7 @@ import io.papermc.paperweight.userdev.attribute.Obfuscation
|
|||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
`java-library`
|
`java-library`
|
||||||
id("com.modrinth.minotaur") version "2.+"
|
id("com.modrinth.minotaur") version "2.7.5"
|
||||||
}
|
}
|
||||||
|
|
||||||
project.description = "Bukkit"
|
project.description = "Bukkit"
|
||||||
@ -32,6 +32,10 @@ repositories {
|
|||||||
name = "OSS Sonatype Snapshots"
|
name = "OSS Sonatype Snapshots"
|
||||||
url = uri("https://oss.sonatype.org/content/repositories/snapshots/")
|
url = uri("https://oss.sonatype.org/content/repositories/snapshots/")
|
||||||
}
|
}
|
||||||
|
maven {
|
||||||
|
name = "Glaremasters"
|
||||||
|
url = uri("https://repo.glaremasters.me/repository/towny/")
|
||||||
|
}
|
||||||
flatDir { dir(File("src/main/resources")) }
|
flatDir { dir(File("src/main/resources")) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,7 +51,13 @@ val adapters = configurations.create("adapters") {
|
|||||||
isCanBeResolved = true
|
isCanBeResolved = true
|
||||||
shouldResolveConsistentlyWith(configurations["runtimeClasspath"])
|
shouldResolveConsistentlyWith(configurations["runtimeClasspath"])
|
||||||
attributes {
|
attributes {
|
||||||
attribute(Obfuscation.OBFUSCATION_ATTRIBUTE, objects.named(Obfuscation.OBFUSCATED))
|
attribute(Obfuscation.OBFUSCATION_ATTRIBUTE,
|
||||||
|
if ((project.findProperty("enginehub.obf.none") as String?).toBoolean()) {
|
||||||
|
objects.named(Obfuscation.NONE)
|
||||||
|
} else {
|
||||||
|
objects.named(Obfuscation.OBFUSCATED)
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,8 +3,6 @@ package com.fastasyncworldedit.bukkit.adapter;
|
|||||||
import co.aikar.timings.Timings;
|
import co.aikar.timings.Timings;
|
||||||
import com.fastasyncworldedit.bukkit.listener.ChunkListener;
|
import com.fastasyncworldedit.bukkit.listener.ChunkListener;
|
||||||
import com.fastasyncworldedit.core.queue.implementation.QueueHandler;
|
import com.fastasyncworldedit.core.queue.implementation.QueueHandler;
|
||||||
import com.sk89q.worldedit.internal.util.LogManagerCompat;
|
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
@ -31,7 +29,7 @@ public class BukkitQueueHandler extends QueueHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void startSet(boolean parallel) {
|
public void startUnsafe(boolean parallel) {
|
||||||
ChunkListener.physicsFreeze = true;
|
ChunkListener.physicsFreeze = true;
|
||||||
if (parallel) {
|
if (parallel) {
|
||||||
try {
|
try {
|
||||||
@ -51,7 +49,7 @@ public class BukkitQueueHandler extends QueueHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void endSet(boolean parallel) {
|
public void endUnsafe(boolean parallel) {
|
||||||
ChunkListener.physicsFreeze = false;
|
ChunkListener.physicsFreeze = false;
|
||||||
if (parallel) {
|
if (parallel) {
|
||||||
try {
|
try {
|
||||||
|
@ -165,7 +165,11 @@ public abstract class Regenerator<IChunkAccess, ProtoChunk extends IChunkAccess,
|
|||||||
.setNameFormat("fawe-regen-%d")
|
.setNameFormat("fawe-regen-%d")
|
||||||
.build()
|
.build()
|
||||||
);
|
);
|
||||||
} // else using sequential chunk generation, concurrent not supported
|
} else { // else using sequential chunk generation, concurrent not supported
|
||||||
|
executor = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder()
|
||||||
|
.setNameFormat("fawe-regen-%d")
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
|
||||||
//TODO: can we get that required radius down without affecting chunk generation (e.g. strucures, features, ...)?
|
//TODO: can we get that required radius down without affecting chunk generation (e.g. strucures, features, ...)?
|
||||||
//for now it is working well and fast, if we are bored in the future we could do the research (a lot of it) to reduce the border radius
|
//for now it is working well and fast, if we are bored in the future we could do the research (a lot of it) to reduce the border radius
|
||||||
@ -253,10 +257,13 @@ public abstract class Regenerator<IChunkAccess, ProtoChunk extends IChunkAccess,
|
|||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
} else { // Concurrency.NONE or generateConcurrent == false
|
} else { // Concurrency.NONE or generateConcurrent == false
|
||||||
// run sequential
|
// run sequential but submit to different thread
|
||||||
for (long xz : coords) {
|
// running regen on the main thread otherwise triggers async-only events on the main thread
|
||||||
chunkStatus.processChunkSave(xz, worldlimits.get(radius).get(xz));
|
executor.submit(() -> {
|
||||||
}
|
for (long xz : coords) {
|
||||||
|
chunkStatus.processChunkSave(xz, worldlimits.get(radius).get(xz));
|
||||||
|
}
|
||||||
|
}).get(); // wait until finished this step
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -371,48 +371,6 @@ public abstract class ChunkListener implements Listener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Prevent firework from loading chunks.
|
|
||||||
*/
|
|
||||||
@EventHandler(priority = EventPriority.LOWEST)
|
|
||||||
public void onChunkLoad(ChunkLoadEvent event) {
|
|
||||||
if (!Settings.settings().TICK_LIMITER.FIREWORKS_LOAD_CHUNKS) {
|
|
||||||
Chunk chunk = event.getChunk();
|
|
||||||
Entity[] entities = chunk.getEntities();
|
|
||||||
World world = chunk.getWorld();
|
|
||||||
|
|
||||||
Exception e = new Exception();
|
|
||||||
int start = 14;
|
|
||||||
int end = 22;
|
|
||||||
int depth = Math.min(end, getDepth(e));
|
|
||||||
|
|
||||||
for (int frame = start; frame < depth; frame++) {
|
|
||||||
StackTraceElement elem = getElement(e, frame);
|
|
||||||
if (elem == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
String className = elem.getClassName();
|
|
||||||
int len = className.length();
|
|
||||||
if (len > 15 && className.charAt(len - 15) == 'E' && className
|
|
||||||
.endsWith("EntityFireworks")) {
|
|
||||||
for (Entity ent : world.getEntities()) {
|
|
||||||
if (ent.getType() == EntityType.FIREWORK) {
|
|
||||||
Vector velocity = ent.getVelocity();
|
|
||||||
double vertical = Math.abs(velocity.getY());
|
|
||||||
if (Math.abs(velocity.getX()) > vertical
|
|
||||||
|| Math.abs(velocity.getZ()) > vertical) {
|
|
||||||
LOGGER.warn(
|
|
||||||
"[FAWE `tick-limiter`] Detected and cancelled rogue FireWork at {}",
|
|
||||||
ent.getLocation());
|
|
||||||
ent.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.LOWEST)
|
@EventHandler(priority = EventPriority.LOWEST)
|
||||||
public void onItemSpawn(ItemSpawnEvent event) {
|
public void onItemSpawn(ItemSpawnEvent event) {
|
||||||
if (physicsFreeze) {
|
if (physicsFreeze) {
|
||||||
|
@ -112,11 +112,15 @@ public class WorldEditPlugin extends JavaPlugin {
|
|||||||
private BukkitServerInterface platform;
|
private BukkitServerInterface platform;
|
||||||
private BukkitConfiguration config;
|
private BukkitConfiguration config;
|
||||||
private BukkitPermissionAttachmentManager permissionAttachmentManager;
|
private BukkitPermissionAttachmentManager permissionAttachmentManager;
|
||||||
|
// Fawe start
|
||||||
|
private BukkitCommandSender bukkitConsoleCommandSender;
|
||||||
|
// Fawe end
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onLoad() {
|
public void onLoad() {
|
||||||
|
|
||||||
//FAWE start
|
//FAWE start
|
||||||
|
this.bukkitConsoleCommandSender = new BukkitCommandSender(this, Bukkit.getConsoleSender());
|
||||||
// This is already covered by Spigot, however, a more pesky warning with a proper explanation over "Ambiguous plugin name..." can't hurt.
|
// This is already covered by Spigot, however, a more pesky warning with a proper explanation over "Ambiguous plugin name..." can't hurt.
|
||||||
Plugin[] plugins = Bukkit.getServer().getPluginManager().getPlugins();
|
Plugin[] plugins = Bukkit.getServer().getPluginManager().getPlugins();
|
||||||
for (Plugin p : plugins) {
|
for (Plugin p : plugins) {
|
||||||
@ -594,7 +598,7 @@ public class WorldEditPlugin extends JavaPlugin {
|
|||||||
return new BukkitBlockCommandSender(this, (BlockCommandSender) sender);
|
return new BukkitBlockCommandSender(this, (BlockCommandSender) sender);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new BukkitCommandSender(this, sender);
|
return bukkitConsoleCommandSender;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BukkitServerInterface getInternalPlatform() {
|
public BukkitServerInterface getInternalPlatform() {
|
||||||
|
@ -23,6 +23,7 @@ import com.sk89q.worldedit.LocalSession;
|
|||||||
import com.sk89q.worldedit.WorldEdit;
|
import com.sk89q.worldedit.WorldEdit;
|
||||||
import com.sk89q.worldedit.extension.platform.Actor;
|
import com.sk89q.worldedit.extension.platform.Actor;
|
||||||
import com.sk89q.worldedit.regions.selector.CuboidRegionSelector;
|
import com.sk89q.worldedit.regions.selector.CuboidRegionSelector;
|
||||||
|
import com.sk89q.worldedit.regions.selector.ExtendingCuboidRegionSelector;
|
||||||
import com.sk89q.worldedit.util.formatting.text.TextComponent;
|
import com.sk89q.worldedit.util.formatting.text.TextComponent;
|
||||||
import com.sk89q.worldedit.util.task.Task;
|
import com.sk89q.worldedit.util.task.Task;
|
||||||
import com.sk89q.worldedit.world.World;
|
import com.sk89q.worldedit.world.World;
|
||||||
@ -39,9 +40,12 @@ public class CLIExtraCommands {
|
|||||||
desc = "Select the entire world"
|
desc = "Select the entire world"
|
||||||
)
|
)
|
||||||
public void selectWorld(Actor actor, World world, LocalSession session) {
|
public void selectWorld(Actor actor, World world, LocalSession session) {
|
||||||
session.setRegionSelector(world, new CuboidRegionSelector(
|
final CuboidRegionSelector selector;
|
||||||
world, world.getMinimumPoint(), world.getMaximumPoint()
|
if (session.getRegionSelector(world) instanceof ExtendingCuboidRegionSelector) {
|
||||||
));
|
selector = new ExtendingCuboidRegionSelector(world, world.getMinimumPoint(), world.getMaximumPoint());
|
||||||
|
} else {
|
||||||
|
selector = new CuboidRegionSelector(world, world.getMinimumPoint(), world.getMaximumPoint());
|
||||||
|
}
|
||||||
actor.printInfo(TextComponent.of("Selected the entire world."));
|
actor.printInfo(TextComponent.of("Selected the entire world."));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
kotlin("jvm") version "1.5.30"
|
kotlin("jvm") version "1.8.20"
|
||||||
application
|
application
|
||||||
}
|
}
|
||||||
|
|
||||||
applyCommonConfiguration()
|
applyCommonConfiguration()
|
||||||
|
|
||||||
tasks.withType<KotlinCompile> {
|
tasks.withType<KotlinCompile> {
|
||||||
kotlinOptions.jvmTarget = "11"
|
kotlinOptions.jvmTarget = "17"
|
||||||
}
|
}
|
||||||
|
|
||||||
application.mainClass.set("com.sk89q.worldedit.internal.util.DocumentationPrinter")
|
application.mainClass.set("com.sk89q.worldedit.internal.util.DocumentationPrinter")
|
||||||
|
@ -12,7 +12,9 @@ import com.fastasyncworldedit.core.util.RandomTextureUtil;
|
|||||||
import com.fastasyncworldedit.core.util.TaskManager;
|
import com.fastasyncworldedit.core.util.TaskManager;
|
||||||
import com.fastasyncworldedit.core.util.TextureUtil;
|
import com.fastasyncworldedit.core.util.TextureUtil;
|
||||||
import com.fastasyncworldedit.core.util.WEManager;
|
import com.fastasyncworldedit.core.util.WEManager;
|
||||||
|
import com.fastasyncworldedit.core.util.task.KeyQueuedExecutorService;
|
||||||
import com.github.luben.zstd.Zstd;
|
import com.github.luben.zstd.Zstd;
|
||||||
|
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||||
import com.sk89q.worldedit.WorldEdit;
|
import com.sk89q.worldedit.WorldEdit;
|
||||||
import com.sk89q.worldedit.internal.util.LogManagerCompat;
|
import com.sk89q.worldedit.internal.util.LogManagerCompat;
|
||||||
import net.jpountz.lz4.LZ4Factory;
|
import net.jpountz.lz4.LZ4Factory;
|
||||||
@ -33,6 +35,9 @@ import java.lang.management.MemoryPoolMXBean;
|
|||||||
import java.lang.management.MemoryUsage;
|
import java.lang.management.MemoryUsage;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
|
import java.util.concurrent.ThreadPoolExecutor;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -86,6 +91,7 @@ public class Fawe {
|
|||||||
* The platform specific implementation.
|
* The platform specific implementation.
|
||||||
*/
|
*/
|
||||||
private final IFawe implementation;
|
private final IFawe implementation;
|
||||||
|
private final KeyQueuedExecutorService<UUID> clipboardExecutor;
|
||||||
private FaweVersion version;
|
private FaweVersion version;
|
||||||
private TextureUtil textures;
|
private TextureUtil textures;
|
||||||
private QueueHandler queueHandler;
|
private QueueHandler queueHandler;
|
||||||
@ -131,6 +137,15 @@ public class Fawe {
|
|||||||
}, 0);
|
}, 0);
|
||||||
|
|
||||||
TaskManager.taskManager().repeat(timer, 1);
|
TaskManager.taskManager().repeat(timer, 1);
|
||||||
|
|
||||||
|
clipboardExecutor = new KeyQueuedExecutorService<>(new ThreadPoolExecutor(
|
||||||
|
1,
|
||||||
|
Settings.settings().QUEUE.PARALLEL_THREADS,
|
||||||
|
0L,
|
||||||
|
TimeUnit.MILLISECONDS,
|
||||||
|
new LinkedBlockingQueue<>(),
|
||||||
|
new ThreadFactoryBuilder().setNameFormat("fawe-clipboard-%d").build()
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -339,9 +354,10 @@ public class Fawe {
|
|||||||
Settings.settings().QUEUE.TARGET_SIZE,
|
Settings.settings().QUEUE.TARGET_SIZE,
|
||||||
Settings.settings().QUEUE.PARALLEL_THREADS
|
Settings.settings().QUEUE.PARALLEL_THREADS
|
||||||
);
|
);
|
||||||
if (Settings.settings().QUEUE.TARGET_SIZE < 2 * Settings.settings().QUEUE.PARALLEL_THREADS) {
|
if (Settings.settings().QUEUE.TARGET_SIZE < 4 * Settings.settings().QUEUE.PARALLEL_THREADS) {
|
||||||
LOGGER.error(
|
LOGGER.error(
|
||||||
"queue.target_size is {}, and queue.parallel_threads is {}. It is HIGHLY recommended that queue" + ".target_size be at least twice queue.parallel_threads or higher.",
|
"queue.target_size is {}, and queue.parallel_threads is {}. It is HIGHLY recommended that queue" +
|
||||||
|
".target_size be at least four times queue.parallel_threads or greater.",
|
||||||
Settings.settings().QUEUE.TARGET_SIZE,
|
Settings.settings().QUEUE.TARGET_SIZE,
|
||||||
Settings.settings().QUEUE.PARALLEL_THREADS
|
Settings.settings().QUEUE.PARALLEL_THREADS
|
||||||
);
|
);
|
||||||
@ -427,4 +443,15 @@ public class Fawe {
|
|||||||
return this.thread = Thread.currentThread();
|
return this.thread = Thread.currentThread();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the 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
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public KeyQueuedExecutorService<UUID> getClipboardExecutor() {
|
||||||
|
return this.clipboardExecutor;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package com.fastasyncworldedit.core.command;
|
package com.fastasyncworldedit.core.command;
|
||||||
|
|
||||||
|
import com.fastasyncworldedit.core.configuration.Caption;
|
||||||
import com.sk89q.worldedit.extension.input.InputParseException;
|
import com.sk89q.worldedit.extension.input.InputParseException;
|
||||||
|
import com.sk89q.worldedit.util.formatting.text.Component;
|
||||||
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -13,33 +15,81 @@ public class SuggestInputParseException extends InputParseException {
|
|||||||
|
|
||||||
private final InputParseException cause;
|
private final InputParseException cause;
|
||||||
private final Supplier<List<String>> getSuggestions;
|
private final Supplier<List<String>> getSuggestions;
|
||||||
private String prefix;
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Use {@link SuggestInputParseException#SuggestInputParseException(Component, Supplier)}
|
||||||
|
*/
|
||||||
|
@Deprecated(forRemoval = true, since = "2.6.2")
|
||||||
public SuggestInputParseException(String msg, String prefix, Supplier<List<String>> getSuggestions) {
|
public SuggestInputParseException(String msg, String prefix, Supplier<List<String>> getSuggestions) {
|
||||||
this(new InputParseException(msg), prefix, getSuggestions);
|
this(new InputParseException(msg), getSuggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Use {@link SuggestInputParseException#of(Throwable, Supplier)}
|
||||||
|
*/
|
||||||
|
@Deprecated(forRemoval = true, since = "2.6.2")
|
||||||
public static SuggestInputParseException of(Throwable other, String prefix, Supplier<List<String>> getSuggestions) {
|
public static SuggestInputParseException of(Throwable other, String prefix, Supplier<List<String>> getSuggestions) {
|
||||||
if (other instanceof InputParseException) {
|
if (other instanceof InputParseException) {
|
||||||
return of((InputParseException) other, prefix, getSuggestions);
|
return of((InputParseException) other, getSuggestions);
|
||||||
}
|
}
|
||||||
return of(new InputParseException(other.getMessage()), prefix, getSuggestions);
|
return of(new InputParseException(other.getMessage()), getSuggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Use {@link SuggestInputParseException#of(InputParseException, Supplier)}
|
||||||
|
*/
|
||||||
|
@Deprecated(forRemoval = true, since = "2.6.2")
|
||||||
public static SuggestInputParseException of(InputParseException other, String prefix, Supplier<List<String>> getSuggestions) {
|
public static SuggestInputParseException of(InputParseException other, String prefix, Supplier<List<String>> getSuggestions) {
|
||||||
if (other instanceof SuggestInputParseException) {
|
if (other instanceof SuggestInputParseException) {
|
||||||
return (SuggestInputParseException) other;
|
return (SuggestInputParseException) other;
|
||||||
}
|
}
|
||||||
return new SuggestInputParseException(other, prefix, getSuggestions);
|
return new SuggestInputParseException(other, getSuggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Use {@link SuggestInputParseException#SuggestInputParseException(InputParseException, Supplier)}
|
||||||
|
*/
|
||||||
|
@Deprecated(forRemoval = true, since = "2.6.2")
|
||||||
public SuggestInputParseException(InputParseException other, String prefix, Supplier<List<String>> getSuggestions) {
|
public SuggestInputParseException(InputParseException other, String prefix, Supplier<List<String>> getSuggestions) {
|
||||||
super(other.getMessage());
|
super(other.getRichMessage());
|
||||||
|
checkNotNull(getSuggestions);
|
||||||
|
checkNotNull(other);
|
||||||
|
this.cause = other;
|
||||||
|
this.getSuggestions = getSuggestions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new SuggestInputParseException instance
|
||||||
|
*
|
||||||
|
* @param message Message to send
|
||||||
|
* @param getSuggestions Supplier of list of suggestions to give to user
|
||||||
|
* @since 2.6.2
|
||||||
|
*/
|
||||||
|
public SuggestInputParseException(Component message, Supplier<List<String>> getSuggestions) {
|
||||||
|
this(new InputParseException(message), getSuggestions);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SuggestInputParseException of(Throwable other, Supplier<List<String>> getSuggestions) {
|
||||||
|
if (other instanceof InputParseException) {
|
||||||
|
return of((InputParseException) other, getSuggestions);
|
||||||
|
}
|
||||||
|
//noinspection deprecation
|
||||||
|
return of(new InputParseException(other.getMessage()), getSuggestions);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SuggestInputParseException of(InputParseException other, Supplier<List<String>> getSuggestions) {
|
||||||
|
if (other instanceof SuggestInputParseException) {
|
||||||
|
return (SuggestInputParseException) other;
|
||||||
|
}
|
||||||
|
return new SuggestInputParseException(other, getSuggestions);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SuggestInputParseException(InputParseException other, Supplier<List<String>> getSuggestions) {
|
||||||
|
super(other.getRichMessage());
|
||||||
checkNotNull(getSuggestions);
|
checkNotNull(getSuggestions);
|
||||||
checkNotNull(other);
|
checkNotNull(other);
|
||||||
this.cause = other;
|
this.cause = other;
|
||||||
this.getSuggestions = getSuggestions;
|
this.getSuggestions = getSuggestions;
|
||||||
this.prefix = prefix;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SuggestInputParseException get(InvocationTargetException e) {
|
public static SuggestInputParseException get(InvocationTargetException e) {
|
||||||
@ -54,7 +104,7 @@ public class SuggestInputParseException extends InputParseException {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static SuggestInputParseException of(String input, List<Object> values) {
|
public static SuggestInputParseException of(String input, List<Object> values) {
|
||||||
throw new SuggestInputParseException("No value: " + input, input, () ->
|
throw new SuggestInputParseException(Caption.of("fawe.error.no-value-for-input", input), () ->
|
||||||
values.stream()
|
values.stream()
|
||||||
.map(Object::toString)
|
.map(Object::toString)
|
||||||
.filter(v -> v.startsWith(input))
|
.filter(v -> v.startsWith(input))
|
||||||
@ -76,8 +126,12 @@ public class SuggestInputParseException extends InputParseException {
|
|||||||
return getSuggestions.get();
|
return getSuggestions.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Unused
|
||||||
|
*/
|
||||||
|
@Deprecated(forRemoval = true, since = "2.6.2")
|
||||||
public SuggestInputParseException prepend(String input) {
|
public SuggestInputParseException prepend(String input) {
|
||||||
this.prefix = input + prefix;
|
// Do nothing
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,11 +102,10 @@ public class Settings extends Config {
|
|||||||
|
|
||||||
public FaweLimit getLimit(Actor actor) {
|
public FaweLimit getLimit(Actor actor) {
|
||||||
FaweLimit limit;
|
FaweLimit limit;
|
||||||
if (actor.hasPermission("fawe.limit.*") || actor.hasPermission("fawe.bypass")) {
|
if (actor.hasPermission("fawe.bypass") || actor.hasPermission("fawe.limit.unlimited")) {
|
||||||
limit = FaweLimit.MAX.copy();
|
return FaweLimit.MAX.copy();
|
||||||
} else {
|
|
||||||
limit = new FaweLimit();
|
|
||||||
}
|
}
|
||||||
|
limit = new FaweLimit();
|
||||||
ArrayList<String> keys = new ArrayList<>(LIMITS.getSections());
|
ArrayList<String> keys = new ArrayList<>(LIMITS.getSections());
|
||||||
if (keys.remove("default")) {
|
if (keys.remove("default")) {
|
||||||
keys.add("default");
|
keys.add("default");
|
||||||
@ -394,6 +393,7 @@ public class Settings extends Config {
|
|||||||
"Where block properties are specified, any blockstate with the property will be disallowed (e.g. all directions",
|
"Where block properties are specified, any blockstate with the property will be disallowed (e.g. all directions",
|
||||||
"of a waterlogged fence). For blocking/remapping of all occurrences of a property like waterlogged, see",
|
"of a waterlogged fence). For blocking/remapping of all occurrences of a property like waterlogged, see",
|
||||||
"remap-properties below.",
|
"remap-properties below.",
|
||||||
|
"To generate a blank list, substitute the default content with a set of square brackets [] instead.",
|
||||||
"Example block property blocking:",
|
"Example block property blocking:",
|
||||||
" - \"minecraft:conduit[waterlogged=true]\"",
|
" - \"minecraft:conduit[waterlogged=true]\"",
|
||||||
" - \"minecraft:piston[extended=false,facing=west]\"",
|
" - \"minecraft:piston[extended=false,facing=west]\"",
|
||||||
@ -520,10 +520,10 @@ public class Settings extends Config {
|
|||||||
" - A smaller value will reduce memory usage",
|
" - A smaller value will reduce memory usage",
|
||||||
" - A value too small may break some operations (deform?)",
|
" - A value too small may break some operations (deform?)",
|
||||||
" - Values smaller than the configurated parallel-threads are not accepted",
|
" - Values smaller than the configurated parallel-threads are not accepted",
|
||||||
" - It is recommended this option be at least 2x greater than parallel-threads"
|
" - It is recommended this option be at least 4x greater than parallel-threads"
|
||||||
|
|
||||||
})
|
})
|
||||||
public int TARGET_SIZE = 64;
|
public int TARGET_SIZE = 8 * Runtime.getRuntime().availableProcessors();
|
||||||
@Comment({
|
@Comment({
|
||||||
"Force FAWE to start placing chunks regardless of whether an edit is finished processing",
|
"Force FAWE to start placing chunks regardless of whether an edit is finished processing",
|
||||||
" - A larger value will use slightly less CPU time",
|
" - A larger value will use slightly less CPU time",
|
||||||
@ -671,6 +671,14 @@ public class Settings extends Config {
|
|||||||
})
|
})
|
||||||
public int MAX_IMAGE_SIZE = 8294400;
|
public int MAX_IMAGE_SIZE = 8294400;
|
||||||
|
|
||||||
|
@Comment({
|
||||||
|
"Whitelist of hostnames to allow images to be downloaded from",
|
||||||
|
" - Adding '*' to the list will allow any host, but this is NOT adviseable",
|
||||||
|
" - Crash exploits exist with malformed images",
|
||||||
|
" - See: https://medium.com/chargebee-engineering/perils-of-parsing-pixel-flood-attack-on-java-imageio-a97aeb06637d"
|
||||||
|
})
|
||||||
|
public List<String> ALLOWED_IMAGE_HOSTS = new ArrayList<>(Collections.singleton(("i.imgur.com")));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class EXTENT {
|
public static class EXTENT {
|
||||||
@ -694,7 +702,7 @@ public class Settings extends Config {
|
|||||||
public static class TICK_LIMITER {
|
public static class TICK_LIMITER {
|
||||||
|
|
||||||
@Comment("Enable the limiter")
|
@Comment("Enable the limiter")
|
||||||
public boolean ENABLED = true;
|
public boolean ENABLED = false;
|
||||||
@Comment("The interval in ticks")
|
@Comment("The interval in ticks")
|
||||||
public int INTERVAL = 20;
|
public int INTERVAL = 20;
|
||||||
@Comment("Max falling blocks per interval (per chunk)")
|
@Comment("Max falling blocks per interval (per chunk)")
|
||||||
@ -703,12 +711,6 @@ public class Settings extends Config {
|
|||||||
public int PHYSICS_MS = 10;
|
public int PHYSICS_MS = 10;
|
||||||
@Comment("Max item spawns per interval (per chunk)")
|
@Comment("Max item spawns per interval (per chunk)")
|
||||||
public int ITEMS = 256;
|
public int ITEMS = 256;
|
||||||
@Comment({
|
|
||||||
"Whether fireworks can load chunks",
|
|
||||||
" - Fireworks usually travel vertically so do not load any chunks",
|
|
||||||
" - Horizontal fireworks can be hacked in to crash a server"
|
|
||||||
})
|
|
||||||
public boolean FIREWORKS_LOAD_CHUNKS = false;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@ public class RichMaskParser extends FaweParser<Mask> {
|
|||||||
@Override
|
@Override
|
||||||
public Mask parseFromInput(String input, ParserContext context) throws InputParseException {
|
public Mask parseFromInput(String input, ParserContext context) throws InputParseException {
|
||||||
if (input.isEmpty()) {
|
if (input.isEmpty()) {
|
||||||
throw new SuggestInputParseException("No input provided", "", () -> Stream
|
throw new SuggestInputParseException(Caption.of("fawe.error.no-input-provided"), () -> Stream
|
||||||
.of("#", ",", "&")
|
.of("#", ",", "&")
|
||||||
.map(n -> n + ":")
|
.map(n -> n + ":")
|
||||||
.collect(Collectors.toList())
|
.collect(Collectors.toList())
|
||||||
@ -95,7 +95,6 @@ public class RichMaskParser extends FaweParser<Mask> {
|
|||||||
"https://intellectualsites.github.io/fastasyncworldedit-documentation/patterns/patterns"
|
"https://intellectualsites.github.io/fastasyncworldedit-documentation/patterns/patterns"
|
||||||
))
|
))
|
||||||
)),
|
)),
|
||||||
full,
|
|
||||||
() -> {
|
() -> {
|
||||||
if (full.length() == 1) {
|
if (full.length() == 1) {
|
||||||
return new ArrayList<>(worldEdit.getMaskFactory().getSuggestions(""));
|
return new ArrayList<>(worldEdit.getMaskFactory().getSuggestions(""));
|
||||||
@ -148,6 +147,7 @@ public class RichMaskParser extends FaweParser<Mask> {
|
|||||||
try {
|
try {
|
||||||
builder.addRegex(full);
|
builder.addRegex(full);
|
||||||
} catch (InputParseException ignored) {
|
} catch (InputParseException ignored) {
|
||||||
|
builder.clear();
|
||||||
context.setPreferringWildcard(false);
|
context.setPreferringWildcard(false);
|
||||||
context.setRestricted(false);
|
context.setRestricted(false);
|
||||||
BaseBlock block = worldEdit.getBlockFactory().parseFromInput(full, context);
|
BaseBlock block = worldEdit.getBlockFactory().parseFromInput(full, context);
|
||||||
@ -162,7 +162,6 @@ public class RichMaskParser extends FaweParser<Mask> {
|
|||||||
"https://intellectualsites.github.io/fastasyncworldedit-documentation/masks/masks"
|
"https://intellectualsites.github.io/fastasyncworldedit-documentation/masks/masks"
|
||||||
))
|
))
|
||||||
)),
|
)),
|
||||||
full,
|
|
||||||
() -> {
|
() -> {
|
||||||
if (full.length() == 1) {
|
if (full.length() == 1) {
|
||||||
return new ArrayList<>(worldEdit.getMaskFactory().getSuggestions(""));
|
return new ArrayList<>(worldEdit.getMaskFactory().getSuggestions(""));
|
||||||
|
@ -47,8 +47,7 @@ public class RichPatternParser extends FaweParser<Pattern> {
|
|||||||
public Pattern parseFromInput(String input, ParserContext context) throws InputParseException {
|
public Pattern parseFromInput(String input, ParserContext context) throws InputParseException {
|
||||||
if (input.isEmpty()) {
|
if (input.isEmpty()) {
|
||||||
throw new SuggestInputParseException(
|
throw new SuggestInputParseException(
|
||||||
"No input provided",
|
Caption.of("fawe.error.no-input-provided"),
|
||||||
"",
|
|
||||||
() -> Stream
|
() -> Stream
|
||||||
.concat(Stream.of("#", ",", "&"), BlockTypes.getNameSpaces().stream().map(n -> n + ":"))
|
.concat(Stream.of("#", ",", "&"), BlockTypes.getNameSpaces().stream().map(n -> n + ":"))
|
||||||
.collect(Collectors.toList())
|
.collect(Collectors.toList())
|
||||||
@ -88,7 +87,6 @@ public class RichPatternParser extends FaweParser<Pattern> {
|
|||||||
"https://intellectualsites.github.io/fastasyncworldedit-documentation/patterns/patterns"
|
"https://intellectualsites.github.io/fastasyncworldedit-documentation/patterns/patterns"
|
||||||
))
|
))
|
||||||
)),
|
)),
|
||||||
full,
|
|
||||||
() -> {
|
() -> {
|
||||||
if (full.length() == 1) {
|
if (full.length() == 1) {
|
||||||
return new ArrayList<>(worldEdit.getPatternFactory().getSuggestions(""));
|
return new ArrayList<>(worldEdit.getPatternFactory().getSuggestions(""));
|
||||||
|
@ -141,7 +141,7 @@ public class DisallowedBlocksExtent extends AbstractDelegateExtent implements IB
|
|||||||
BlockState state = BlockTypesCache.states[block];
|
BlockState state = BlockTypesCache.states[block];
|
||||||
if (blockedBlocks != null) {
|
if (blockedBlocks != null) {
|
||||||
if (blockedBlocks.contains(state.getBlockType().getId())) {
|
if (blockedBlocks.contains(state.getBlockType().getId())) {
|
||||||
blocks[i] = 0;
|
blocks[i] = BlockTypesCache.ReservedIDs.__RESERVED__;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -150,7 +150,7 @@ public class DisallowedBlocksExtent extends AbstractDelegateExtent implements IB
|
|||||||
}
|
}
|
||||||
for (FuzzyBlockState fuzzy : blockedStates) {
|
for (FuzzyBlockState fuzzy : blockedStates) {
|
||||||
if (fuzzy.equalsFuzzy(state)) {
|
if (fuzzy.equalsFuzzy(state)) {
|
||||||
blocks[i] = 0;
|
blocks[i] = BlockTypesCache.ReservedIDs.__RESERVED__;
|
||||||
continue it;
|
continue it;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -178,7 +178,7 @@ public class DisallowedBlocksExtent extends AbstractDelegateExtent implements IB
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ProcessorScope getScope() {
|
public ProcessorScope getScope() {
|
||||||
return ProcessorScope.CHANGING_BLOCKS;
|
return ProcessorScope.REMOVING_BLOCKS;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -168,7 +168,7 @@ public abstract class FaweRegionExtent extends ResettableExtent implements IBatc
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ProcessorScope getScope() {
|
public ProcessorScope getScope() {
|
||||||
return ProcessorScope.READING_SET_BLOCKS;
|
return ProcessorScope.REMOVING_BLOCKS;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -172,7 +172,7 @@ public class MultiRegionExtent extends FaweRegionExtent {
|
|||||||
set = intersection.processSet(chunk, get, set);
|
set = intersection.processSet(chunk, get, set);
|
||||||
}
|
}
|
||||||
if (disallowedIntersection != null) {
|
if (disallowedIntersection != null) {
|
||||||
intersection.processSet(chunk, get, set);
|
set = disallowedIntersection.processSet(chunk, get, set, true);
|
||||||
}
|
}
|
||||||
return set;
|
return set;
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ import com.sk89q.worldedit.world.biome.BiomeType;
|
|||||||
import com.sk89q.worldedit.world.block.BaseBlock;
|
import com.sk89q.worldedit.world.block.BaseBlock;
|
||||||
import com.sk89q.worldedit.world.block.BlockState;
|
import com.sk89q.worldedit.world.block.BlockState;
|
||||||
import com.sk89q.worldedit.world.block.BlockStateHolder;
|
import com.sk89q.worldedit.world.block.BlockStateHolder;
|
||||||
|
import com.sk89q.worldedit.world.block.BlockTypesCache;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@ -191,8 +192,8 @@ public class CPUOptimizedClipboard extends LinearClipboard {
|
|||||||
@Override
|
@Override
|
||||||
public <B extends BlockStateHolder<B>> boolean setBlock(int index, B block) {
|
public <B extends BlockStateHolder<B>> boolean setBlock(int index, B block) {
|
||||||
char ordinal = block.getOrdinalChar();
|
char ordinal = block.getOrdinalChar();
|
||||||
if (ordinal == 0) {
|
if (ordinal == BlockTypesCache.ReservedIDs.__RESERVED__) {
|
||||||
ordinal = 1;
|
ordinal = BlockTypesCache.ReservedIDs.AIR;
|
||||||
}
|
}
|
||||||
states[index] = ordinal;
|
states[index] = ordinal;
|
||||||
boolean hasNbt = block instanceof BaseBlock && block.hasNbtData();
|
boolean hasNbt = block instanceof BaseBlock && block.hasNbtData();
|
||||||
|
@ -26,6 +26,7 @@ import com.sk89q.worldedit.world.block.BaseBlock;
|
|||||||
import com.sk89q.worldedit.world.block.BlockState;
|
import com.sk89q.worldedit.world.block.BlockState;
|
||||||
import com.sk89q.worldedit.world.block.BlockStateHolder;
|
import com.sk89q.worldedit.world.block.BlockStateHolder;
|
||||||
import com.sk89q.worldedit.world.block.BlockTypes;
|
import com.sk89q.worldedit.world.block.BlockTypes;
|
||||||
|
import com.sk89q.worldedit.world.block.BlockTypesCache;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
@ -155,7 +156,9 @@ public class DiskOptimizedClipboard extends LinearClipboard {
|
|||||||
/**
|
/**
|
||||||
* Load an existing file as a DiskOptimizedClipboard. The file MUST exist and MUST be created as a DiskOptimizedClipboard
|
* Load an existing file as a DiskOptimizedClipboard. The file MUST exist and MUST be created as a DiskOptimizedClipboard
|
||||||
* with data written to it.
|
* with data written to it.
|
||||||
|
* @deprecated Will be made private, use {@link DiskOptimizedClipboard#loadFromFile(File)}
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(forRemoval = true, since = "2.6.2")
|
||||||
public DiskOptimizedClipboard(File file) {
|
public DiskOptimizedClipboard(File file) {
|
||||||
this(file, VERSION);
|
this(file, VERSION);
|
||||||
}
|
}
|
||||||
@ -166,14 +169,15 @@ public class DiskOptimizedClipboard extends LinearClipboard {
|
|||||||
*
|
*
|
||||||
* @param file File to read from
|
* @param file File to read from
|
||||||
* @param versionOverride An override version to allow loading of older clipboards if required
|
* @param versionOverride An override version to allow loading of older clipboards if required
|
||||||
|
* @deprecated Will be made private, use {@link DiskOptimizedClipboard#loadFromFile(File)}
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(forRemoval = true, since = "2.6.2")
|
||||||
public DiskOptimizedClipboard(File file, int versionOverride) {
|
public DiskOptimizedClipboard(File file, int versionOverride) {
|
||||||
super(readSize(file, versionOverride), BlockVector3.ZERO);
|
super(readSize(file, versionOverride), BlockVector3.ZERO);
|
||||||
headerSize = getHeaderSizeOverrideFromVersion(versionOverride);
|
headerSize = getHeaderSizeOverrideFromVersion(versionOverride);
|
||||||
nbtMap = new HashMap<>();
|
nbtMap = new HashMap<>();
|
||||||
try {
|
try {
|
||||||
this.file = file;
|
this.file = file;
|
||||||
checkFileLength(file);
|
|
||||||
this.braf = new RandomAccessFile(file, "rw");
|
this.braf = new RandomAccessFile(file, "rw");
|
||||||
braf.setLength(file.length());
|
braf.setLength(file.length());
|
||||||
this.nbtBytesRemaining = Integer.MAX_VALUE - (int) file.length();
|
this.nbtBytesRemaining = Integer.MAX_VALUE - (int) file.length();
|
||||||
@ -202,32 +206,6 @@ public class DiskOptimizedClipboard extends LinearClipboard {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkFileLength(File file) throws IOException {
|
|
||||||
long expectedFileSize = headerSize + ((long) getVolume() << 1);
|
|
||||||
if (file.length() > Integer.MAX_VALUE) {
|
|
||||||
if (expectedFileSize >= Integer.MAX_VALUE) {
|
|
||||||
throw new IOException(String.format(
|
|
||||||
"Cannot load clipboard of file size: %d > 2147483647 bytes (2.147 GiB), " + "volume: %d blocks",
|
|
||||||
file.length(),
|
|
||||||
getVolume()
|
|
||||||
));
|
|
||||||
} else {
|
|
||||||
throw new IOException(String.format(
|
|
||||||
"Cannot load clipboard of file size > 2147483647 bytes (2.147 GiB). Possible corrupt file? Mismatch" +
|
|
||||||
" between volume `%d` and file length `%d`!",
|
|
||||||
file.length(),
|
|
||||||
getVolume()
|
|
||||||
));
|
|
||||||
}
|
|
||||||
} else if (expectedFileSize != file.length()) {
|
|
||||||
throw new IOException(String.format(
|
|
||||||
"Possible corrupt clipboard file? Mismatch between expected file size `%d` and actual file size `%d`!",
|
|
||||||
expectedFileSize,
|
|
||||||
file.length()
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempt to load a file into a new {@link DiskOptimizedClipboard} instance. Will attempt to recover on version mismatch
|
* Attempt to load a file into a new {@link DiskOptimizedClipboard} instance. Will attempt to recover on version mismatch
|
||||||
* failure.
|
* failure.
|
||||||
@ -723,8 +701,8 @@ public class DiskOptimizedClipboard extends LinearClipboard {
|
|||||||
try {
|
try {
|
||||||
int index = headerSize + (getIndex(x, y, z) << 1);
|
int index = headerSize + (getIndex(x, y, z) << 1);
|
||||||
char ordinal = block.getOrdinalChar();
|
char ordinal = block.getOrdinalChar();
|
||||||
if (ordinal == 0) {
|
if (ordinal == BlockTypesCache.ReservedIDs.__RESERVED__) {
|
||||||
ordinal = 1;
|
ordinal = BlockTypesCache.ReservedIDs.AIR;
|
||||||
}
|
}
|
||||||
byteBuffer.putChar(index, ordinal);
|
byteBuffer.putChar(index, ordinal);
|
||||||
boolean hasNbt = block instanceof BaseBlock && block.hasNbtData();
|
boolean hasNbt = block instanceof BaseBlock && block.hasNbtData();
|
||||||
|
@ -16,6 +16,7 @@ import com.sk89q.worldedit.world.block.BlockState;
|
|||||||
import com.sk89q.worldedit.world.block.BlockStateHolder;
|
import com.sk89q.worldedit.world.block.BlockStateHolder;
|
||||||
import com.sk89q.worldedit.world.block.BlockType;
|
import com.sk89q.worldedit.world.block.BlockType;
|
||||||
import com.sk89q.worldedit.world.block.BlockTypes;
|
import com.sk89q.worldedit.world.block.BlockTypes;
|
||||||
|
import com.sk89q.worldedit.world.block.BlockTypesCache;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@ -269,8 +270,8 @@ public class MemoryOptimizedClipboard extends LinearClipboard {
|
|||||||
@Override
|
@Override
|
||||||
public <B extends BlockStateHolder<B>> boolean setBlock(int index, B block) {
|
public <B extends BlockStateHolder<B>> boolean setBlock(int index, B block) {
|
||||||
int ordinal = block.getOrdinal();
|
int ordinal = block.getOrdinal();
|
||||||
if (ordinal == 0) {
|
if (ordinal == BlockTypesCache.ReservedIDs.__RESERVED__) {
|
||||||
ordinal = 1;
|
ordinal = BlockTypesCache.ReservedIDs.AIR;
|
||||||
}
|
}
|
||||||
setOrdinal(index, ordinal);
|
setOrdinal(index, ordinal);
|
||||||
boolean hasNbt = block instanceof BaseBlock && block.hasNbtData();
|
boolean hasNbt = block instanceof BaseBlock && block.hasNbtData();
|
||||||
|
@ -172,8 +172,8 @@ public class FastSchematicWriter implements ClipboardWriter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int ordinal = block.getOrdinal();
|
int ordinal = block.getOrdinal();
|
||||||
if (ordinal == 0) {
|
if (ordinal == BlockTypesCache.ReservedIDs.__RESERVED__) {
|
||||||
ordinal = 1;
|
ordinal = BlockTypesCache.ReservedIDs.AIR;
|
||||||
}
|
}
|
||||||
char value = palette[ordinal];
|
char value = palette[ordinal];
|
||||||
if (value == Character.MAX_VALUE) {
|
if (value == Character.MAX_VALUE) {
|
||||||
|
@ -41,8 +41,8 @@ public class DistrFilter extends ForkedFilter<DistrFilter> {
|
|||||||
@Override
|
@Override
|
||||||
public final void applyBlock(FilterBlock block) {
|
public final void applyBlock(FilterBlock block) {
|
||||||
int ordinal = block.getOrdinal();
|
int ordinal = block.getOrdinal();
|
||||||
if (ordinal == 0) {
|
if (ordinal == BlockTypesCache.ReservedIDs.__RESERVED__) {
|
||||||
ordinal = 1;
|
ordinal = BlockTypesCache.ReservedIDs.AIR;
|
||||||
}
|
}
|
||||||
counter[ordinal]++;
|
counter[ordinal]++;
|
||||||
}
|
}
|
||||||
|
@ -257,7 +257,7 @@ public class MultiBatchProcessor implements IBatchProcessor {
|
|||||||
for (IBatchProcessor processor : processors) {
|
for (IBatchProcessor processor : processors) {
|
||||||
scope = Math.max(scope, processor.getScope().intValue());
|
scope = Math.max(scope, processor.getScope().intValue());
|
||||||
}
|
}
|
||||||
return ProcessorScope.valueOf(0);
|
return ProcessorScope.valueOf(scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -3,9 +3,9 @@ package com.fastasyncworldedit.core.extent.processor;
|
|||||||
/**
|
/**
|
||||||
* The scope of a processor.
|
* The scope of a processor.
|
||||||
* Order in which processors are executed:
|
* Order in which processors are executed:
|
||||||
* - ADDING_BLOCKS (processors that strictly ADD blocks to an edit ONLY)
|
* - ADDING_BLOCKS (processors that may ADD blocks to an edit ONLY)
|
||||||
* - CHANGING_BLOCKS (processors that strictly ADD or CHANGE blocks being set)
|
* - CHANGING_BLOCKS (processors that may ADD or CHANGE blocks being set)
|
||||||
* - REMOVING_BLOCKS (processors that string ADD, CHANGE or REMOVE blocks being set)
|
* - REMOVING_BLOCKS (processors that may ADD, CHANGE or REMOVE blocks being set)
|
||||||
* - CUSTOM (processors that do not specify a SCOPE)
|
* - CUSTOM (processors that do not specify a SCOPE)
|
||||||
* - READING_SET_BLOCKS (processors that do not alter blocks at all, and read the blocks that are actually going to set, e.g. history processors)
|
* - READING_SET_BLOCKS (processors that do not alter blocks at all, and read the blocks that are actually going to set, e.g. history processors)
|
||||||
*/
|
*/
|
||||||
|
@ -73,11 +73,11 @@ public class HeightmapProcessor implements IBatchProcessor {
|
|||||||
for (int y = 15; y >= 0; y--) {
|
for (int y = 15; y >= 0; y--) {
|
||||||
// We don't need to actually iterate over x and z as we're both reading and writing an index
|
// We don't need to actually iterate over x and z as we're both reading and writing an index
|
||||||
for (int j = 0; j < BLOCKS_PER_Y; j++) {
|
for (int j = 0; j < BLOCKS_PER_Y; j++) {
|
||||||
char ordinal = 0;
|
char ordinal = BlockTypesCache.ReservedIDs.__RESERVED__;
|
||||||
if (hasSectionSet) {
|
if (hasSectionSet) {
|
||||||
ordinal = setSection[index(y, j)];
|
ordinal = setSection[index(y, j)];
|
||||||
}
|
}
|
||||||
if (ordinal == 0) {
|
if (ordinal == BlockTypesCache.ReservedIDs.__RESERVED__) {
|
||||||
if (!hasSectionGet) {
|
if (!hasSectionGet) {
|
||||||
if (!hasSectionSet) {
|
if (!hasSectionSet) {
|
||||||
continue layer;
|
continue layer;
|
||||||
|
@ -178,8 +178,7 @@ public class BlockMaskBuilder {
|
|||||||
}
|
}
|
||||||
if (operator == null) {
|
if (operator == null) {
|
||||||
throw new SuggestInputParseException(
|
throw new SuggestInputParseException(
|
||||||
"No operator for " + input,
|
Caption.of("fawe.error.no-operator-for-input", input),
|
||||||
"",
|
|
||||||
() -> Arrays.asList("=", "~", "!", "<", ">", "<=", ">=")
|
() -> Arrays.asList("=", "~", "!", "<", ">", "<=", ">=")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -195,7 +194,7 @@ public class BlockMaskBuilder {
|
|||||||
String value = charSequence.toString();
|
String value = charSequence.toString();
|
||||||
final PropertyKey fKey = key;
|
final PropertyKey fKey = key;
|
||||||
Collection<BlockType> types = type != null ? Collections.singleton(type) : blockTypeList;
|
Collection<BlockType> types = type != null ? Collections.singleton(type) : blockTypeList;
|
||||||
throw new SuggestInputParseException("No value for " + input, input, () -> {
|
throw new SuggestInputParseException(Caption.of("fawe.error.no-value-for-input", input), () -> {
|
||||||
HashSet<String> values = new HashSet<>();
|
HashSet<String> values = new HashSet<>();
|
||||||
types.stream().filter(t -> t.hasProperty(fKey)).forEach(t -> {
|
types.stream().filter(t -> t.hasProperty(fKey)).forEach(t -> {
|
||||||
Property<Object> p = t.getProperty(fKey);
|
Property<Object> p = t.getProperty(fKey);
|
||||||
@ -287,7 +286,7 @@ public class BlockMaskBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void suggest(String input, String property, Collection<BlockType> finalTypes) throws InputParseException {
|
private void suggest(String input, String property, Collection<BlockType> finalTypes) throws InputParseException {
|
||||||
throw new SuggestInputParseException(input + " does not have: " + property, input, () -> {
|
throw new SuggestInputParseException(Caption.of("worldedit.error.parser.unknown-property", property, input), () -> {
|
||||||
Set<PropertyKey> keys = PropertyKeySet.empty();
|
Set<PropertyKey> keys = PropertyKeySet.empty();
|
||||||
finalTypes.forEach(t -> t.getProperties().forEach(p -> keys.add(p.getKey())));
|
finalTypes.forEach(t -> t.getProperties().forEach(p -> keys.add(p.getKey())));
|
||||||
return keys.stream().map(PropertyKey::getName)
|
return keys.stream().map(PropertyKey::getName)
|
||||||
|
@ -5,6 +5,7 @@ import com.sk89q.worldedit.WorldEditException;
|
|||||||
import com.sk89q.worldedit.history.UndoContext;
|
import com.sk89q.worldedit.history.UndoContext;
|
||||||
import com.sk89q.worldedit.history.change.Change;
|
import com.sk89q.worldedit.history.change.Change;
|
||||||
import com.sk89q.worldedit.world.biome.BiomeTypes;
|
import com.sk89q.worldedit.world.biome.BiomeTypes;
|
||||||
|
import com.sk89q.worldedit.world.block.BlockTypesCache;
|
||||||
|
|
||||||
public class MutableBiomeChange implements Change {
|
public class MutableBiomeChange implements Change {
|
||||||
|
|
||||||
@ -13,8 +14,8 @@ public class MutableBiomeChange implements Change {
|
|||||||
private int to;
|
private int to;
|
||||||
|
|
||||||
public MutableBiomeChange() {
|
public MutableBiomeChange() {
|
||||||
this.from = 0;
|
this.from = BlockTypesCache.ReservedIDs.__RESERVED__;
|
||||||
this.to = 0;
|
this.to = BlockTypesCache.ReservedIDs.__RESERVED__;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setBiome(int x, int y, int z, int from, int to) {
|
public void setBiome(int x, int y, int z, int from, int to) {
|
||||||
|
@ -6,6 +6,7 @@ import com.sk89q.worldedit.extent.inventory.BlockBagException;
|
|||||||
import com.sk89q.worldedit.history.UndoContext;
|
import com.sk89q.worldedit.history.UndoContext;
|
||||||
import com.sk89q.worldedit.history.change.Change;
|
import com.sk89q.worldedit.history.change.Change;
|
||||||
import com.sk89q.worldedit.world.block.BlockState;
|
import com.sk89q.worldedit.world.block.BlockState;
|
||||||
|
import com.sk89q.worldedit.world.block.BlockTypesCache;
|
||||||
|
|
||||||
public class MutableFullBlockChange implements Change {
|
public class MutableFullBlockChange implements Change {
|
||||||
|
|
||||||
@ -39,14 +40,14 @@ public class MutableFullBlockChange implements Change {
|
|||||||
if (blockBag != null) {
|
if (blockBag != null) {
|
||||||
BlockState toState = BlockState.getFromOrdinal(to);
|
BlockState toState = BlockState.getFromOrdinal(to);
|
||||||
if (fromState != toState) {
|
if (fromState != toState) {
|
||||||
if (allowFetch && from != 0) {
|
if (allowFetch && from != BlockTypesCache.ReservedIDs.__RESERVED__) {
|
||||||
try {
|
try {
|
||||||
blockBag.fetchPlacedBlock(fromState);
|
blockBag.fetchPlacedBlock(fromState);
|
||||||
} catch (BlockBagException e) {
|
} catch (BlockBagException e) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (allowStore && to != 0) {
|
if (allowStore && to != BlockTypesCache.ReservedIDs.__RESERVED__) {
|
||||||
try {
|
try {
|
||||||
blockBag.storeDroppedBlock(toState);
|
blockBag.storeDroppedBlock(toState);
|
||||||
} catch (BlockBagException ignored) {
|
} catch (BlockBagException ignored) {
|
||||||
|
@ -39,19 +39,27 @@ import java.util.Map;
|
|||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
|
import java.util.concurrent.Semaphore;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This batch processor writes changes to a concrete implementation.
|
||||||
|
* {@link #processSet(IChunk, IChunkGet, IChunkSet)} is synchronized to guarantee consistency.
|
||||||
|
* To avoid many blocking threads on this method, changes are enqueued in {@link #queue}.
|
||||||
|
* This allows to keep other threads free for other work.
|
||||||
|
*/
|
||||||
public abstract class AbstractChangeSet implements ChangeSet, IBatchProcessor {
|
public abstract class AbstractChangeSet implements ChangeSet, IBatchProcessor {
|
||||||
|
|
||||||
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
||||||
|
|
||||||
private final World world;
|
private final World world;
|
||||||
private final AtomicInteger lastException = new AtomicInteger();
|
private final AtomicInteger lastException = new AtomicInteger();
|
||||||
protected AtomicInteger waitingCombined = new AtomicInteger(0);
|
private final Semaphore workerSemaphore = new Semaphore(1, false);
|
||||||
protected AtomicInteger waitingAsync = new AtomicInteger(0);
|
private final ConcurrentLinkedQueue<Runnable> queue = new ConcurrentLinkedQueue<>();
|
||||||
|
protected volatile boolean closed;
|
||||||
protected boolean closed;
|
|
||||||
|
|
||||||
public AbstractChangeSet(World world) {
|
public AbstractChangeSet(World world) {
|
||||||
this.world = world;
|
this.world = world;
|
||||||
@ -65,16 +73,11 @@ public abstract class AbstractChangeSet implements ChangeSet, IBatchProcessor {
|
|||||||
if (closed) {
|
if (closed) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
waitingAsync.incrementAndGet();
|
|
||||||
TaskManager.taskManager().async(() -> {
|
TaskManager.taskManager().async(() -> {
|
||||||
waitingAsync.decrementAndGet();
|
|
||||||
synchronized (waitingAsync) {
|
|
||||||
waitingAsync.notifyAll();
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
close();
|
close();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
LOGGER.catching(e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -82,20 +85,10 @@ public abstract class AbstractChangeSet implements ChangeSet, IBatchProcessor {
|
|||||||
@Override
|
@Override
|
||||||
public void flush() {
|
public void flush() {
|
||||||
try {
|
try {
|
||||||
if (!Fawe.isMainThread()) {
|
// drain with this thread too
|
||||||
while (waitingAsync.get() > 0) {
|
drainQueue(true);
|
||||||
synchronized (waitingAsync) {
|
} catch (Exception e) {
|
||||||
waitingAsync.wait(1000);
|
LOGGER.catching(e);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
while (waitingCombined.get() > 0) {
|
|
||||||
synchronized (waitingCombined) {
|
|
||||||
waitingCombined.wait(1000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,7 +118,7 @@ public abstract class AbstractChangeSet implements ChangeSet, IBatchProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set) {
|
public final synchronized IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set) {
|
||||||
int bx = chunk.getX() << 4;
|
int bx = chunk.getX() << 4;
|
||||||
int bz = chunk.getZ() << 4;
|
int bz = chunk.getZ() << 4;
|
||||||
|
|
||||||
@ -193,7 +186,7 @@ public abstract class AbstractChangeSet implements ChangeSet, IBatchProcessor {
|
|||||||
}
|
}
|
||||||
final int combinedFrom = from;
|
final int combinedFrom = from;
|
||||||
final int combinedTo = blocksSet[index];
|
final int combinedTo = blocksSet[index];
|
||||||
if (combinedTo != 0) {
|
if (combinedTo != BlockTypesCache.ReservedIDs.__RESERVED__) {
|
||||||
add(xx, yy, zz, combinedFrom, combinedTo);
|
add(xx, yy, zz, combinedFrom, combinedTo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -306,12 +299,12 @@ public abstract class AbstractChangeSet implements ChangeSet, IBatchProcessor {
|
|||||||
BaseBlock to = change.getCurrent();
|
BaseBlock to = change.getCurrent();
|
||||||
add(loc, from, to);
|
add(loc, from, to);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
LOGGER.catching(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isEmpty() {
|
public boolean isEmpty() {
|
||||||
return waitingCombined.get() == 0 && waitingAsync.get() == 0 && size() == 0;
|
return queue.isEmpty() && workerSemaphore.availablePermits() == 1 && size() == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void add(BlockVector3 loc, BaseBlock from, BaseBlock to) {
|
public void add(BlockVector3 loc, BaseBlock from, BaseBlock to) {
|
||||||
@ -353,7 +346,7 @@ public abstract class AbstractChangeSet implements ChangeSet, IBatchProcessor {
|
|||||||
add(x, y, z, combinedFrom, combinedTo);
|
add(x, y, z, combinedFrom, combinedTo);
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
LOGGER.catching(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -362,7 +355,6 @@ public abstract class AbstractChangeSet implements ChangeSet, IBatchProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Future<?> addWriteTask(final Runnable writeTask, final boolean completeNow) {
|
public Future<?> addWriteTask(final Runnable writeTask, final boolean completeNow) {
|
||||||
AbstractChangeSet.this.waitingCombined.incrementAndGet();
|
|
||||||
Runnable wrappedTask = () -> {
|
Runnable wrappedTask = () -> {
|
||||||
try {
|
try {
|
||||||
writeTask.run();
|
writeTask.run();
|
||||||
@ -372,25 +364,55 @@ public abstract class AbstractChangeSet implements ChangeSet, IBatchProcessor {
|
|||||||
} else {
|
} else {
|
||||||
int hash = t.getMessage().hashCode();
|
int hash = t.getMessage().hashCode();
|
||||||
if (lastException.getAndSet(hash) != hash) {
|
if (lastException.getAndSet(hash) != hash) {
|
||||||
t.printStackTrace();
|
LOGGER.catching(t);
|
||||||
}
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
if (AbstractChangeSet.this.waitingCombined.decrementAndGet() <= 0) {
|
|
||||||
synchronized (AbstractChangeSet.this.waitingAsync) {
|
|
||||||
AbstractChangeSet.this.waitingAsync.notifyAll();
|
|
||||||
}
|
|
||||||
synchronized (AbstractChangeSet.this.waitingCombined) {
|
|
||||||
AbstractChangeSet.this.waitingCombined.notifyAll();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if (completeNow) {
|
if (completeNow) {
|
||||||
wrappedTask.run();
|
wrappedTask.run();
|
||||||
return Futures.immediateCancelledFuture();
|
return Futures.immediateVoidFuture();
|
||||||
} else {
|
} else {
|
||||||
return Fawe.instance().getQueueHandler().submit(wrappedTask);
|
CompletableFuture<?> task = new CompletableFuture<>();
|
||||||
|
queue.add(() -> {
|
||||||
|
wrappedTask.run();
|
||||||
|
task.complete(null);
|
||||||
|
});
|
||||||
|
// make sure changes are processed
|
||||||
|
triggerWorker();
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void triggerWorker() {
|
||||||
|
if (workerSemaphore.availablePermits() == 0) {
|
||||||
|
return; // fast path to avoid additional tasks: a worker is already draining the queue
|
||||||
|
}
|
||||||
|
// create a new worker to drain the current queue
|
||||||
|
Fawe.instance().getQueueHandler().async(() -> drainQueue(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void drainQueue(boolean ignoreRunningState) {
|
||||||
|
if (!workerSemaphore.tryAcquire()) {
|
||||||
|
if (ignoreRunningState) {
|
||||||
|
// ignoreRunningState means we want to block
|
||||||
|
// even if another thread is already draining
|
||||||
|
try {
|
||||||
|
workerSemaphore.acquire();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return; // another thread is draining the queue already, ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Runnable next;
|
||||||
|
while ((next = queue.poll()) != null) { // process all tasks in the queue
|
||||||
|
next.run();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
workerSemaphore.release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,8 +25,6 @@ public class AbstractDelegateChangeSet extends AbstractChangeSet {
|
|||||||
public AbstractDelegateChangeSet(AbstractChangeSet parent) {
|
public AbstractDelegateChangeSet(AbstractChangeSet parent) {
|
||||||
super(parent.getWorld());
|
super(parent.getWorld());
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
this.waitingCombined = parent.waitingCombined;
|
|
||||||
this.waitingAsync = parent.waitingAsync;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public final AbstractChangeSet getParent() {
|
public final AbstractChangeSet getParent() {
|
||||||
|
@ -258,7 +258,7 @@ public abstract class FaweStreamChangeSet extends AbstractChangeSet {
|
|||||||
if (blockSize > 0) {
|
if (blockSize > 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (waitingCombined.get() != 0 || waitingAsync.get() != 0) {
|
if (!super.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
flush();
|
flush();
|
||||||
|
@ -2,6 +2,7 @@ package com.fastasyncworldedit.core.limit;
|
|||||||
|
|
||||||
import com.fastasyncworldedit.core.FaweCache;
|
import com.fastasyncworldedit.core.FaweCache;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
public class FaweLimit {
|
public class FaweLimit {
|
||||||
@ -114,10 +115,10 @@ public class FaweLimit {
|
|||||||
MAX.FAST_PLACEMENT = true;
|
MAX.FAST_PLACEMENT = true;
|
||||||
MAX.CONFIRM_LARGE = true;
|
MAX.CONFIRM_LARGE = true;
|
||||||
MAX.RESTRICT_HISTORY_TO_REGIONS = false;
|
MAX.RESTRICT_HISTORY_TO_REGIONS = false;
|
||||||
MAX.STRIP_NBT = null;
|
MAX.STRIP_NBT = Collections.emptySet();
|
||||||
MAX.UNIVERSAL_DISALLOWED_BLOCKS = false;
|
MAX.UNIVERSAL_DISALLOWED_BLOCKS = false;
|
||||||
MAX.DISALLOWED_BLOCKS = null;
|
MAX.DISALLOWED_BLOCKS = Collections.emptySet();
|
||||||
MAX.REMAP_PROPERTIES = null;
|
MAX.REMAP_PROPERTIES = Collections.emptySet();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean MAX_CHANGES() {
|
public boolean MAX_CHANGES() {
|
||||||
@ -241,7 +242,7 @@ public class FaweLimit {
|
|||||||
&& FAST_PLACEMENT
|
&& FAST_PLACEMENT
|
||||||
&& !RESTRICT_HISTORY_TO_REGIONS
|
&& !RESTRICT_HISTORY_TO_REGIONS
|
||||||
&& (STRIP_NBT == null || STRIP_NBT.isEmpty())
|
&& (STRIP_NBT == null || STRIP_NBT.isEmpty())
|
||||||
&& !UNIVERSAL_DISALLOWED_BLOCKS
|
// && !UNIVERSAL_DISALLOWED_BLOCKS --> do not include this, it effectively has no relevance
|
||||||
&& (DISALLOWED_BLOCKS == null || DISALLOWED_BLOCKS.isEmpty())
|
&& (DISALLOWED_BLOCKS == null || DISALLOWED_BLOCKS.isEmpty())
|
||||||
&& (REMAP_PROPERTIES == null || REMAP_PROPERTIES.isEmpty());
|
&& (REMAP_PROPERTIES == null || REMAP_PROPERTIES.isEmpty());
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,20 @@ import java.util.Map;
|
|||||||
|
|
||||||
public class BlockVector3ChunkMap<T> implements IAdaptedMap<BlockVector3, T, Integer, T> {
|
public class BlockVector3ChunkMap<T> implements IAdaptedMap<BlockVector3, T, Integer, T> {
|
||||||
|
|
||||||
private final Int2ObjectArrayMap<T> map = new Int2ObjectArrayMap<>();
|
private final Int2ObjectArrayMap<T> map;
|
||||||
|
|
||||||
|
public BlockVector3ChunkMap() {
|
||||||
|
map = new Int2ObjectArrayMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new instance that is a copy of an existing map
|
||||||
|
*
|
||||||
|
* @param map existing map to copy
|
||||||
|
*/
|
||||||
|
public BlockVector3ChunkMap(BlockVector3ChunkMap<T> map) {
|
||||||
|
this.map = new Int2ObjectArrayMap<>(map.getParent());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<Integer, T> getParent() {
|
public Map<Integer, T> getParent() {
|
||||||
|
@ -7,6 +7,7 @@ import com.sk89q.jnbt.CompoundTag;
|
|||||||
import com.sk89q.worldedit.WorldEdit;
|
import com.sk89q.worldedit.WorldEdit;
|
||||||
import com.sk89q.worldedit.extent.Extent;
|
import com.sk89q.worldedit.extent.Extent;
|
||||||
import com.sk89q.worldedit.math.BlockVector3;
|
import com.sk89q.worldedit.math.BlockVector3;
|
||||||
|
import com.sk89q.worldedit.world.block.BlockTypesCache;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -71,7 +72,7 @@ public interface IBatchProcessor {
|
|||||||
if (arr != null) {
|
if (arr != null) {
|
||||||
int index = (minY & 15) << 8;
|
int index = (minY & 15) << 8;
|
||||||
for (int i = 0; i < index; i++) {
|
for (int i = 0; i < index; i++) {
|
||||||
arr[i] = 0;
|
arr[i] = BlockTypesCache.ReservedIDs.__RESERVED__;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
arr = new char[4096];
|
arr = new char[4096];
|
||||||
@ -89,7 +90,7 @@ public interface IBatchProcessor {
|
|||||||
if (arr != null) {
|
if (arr != null) {
|
||||||
int index = ((maxY + 1) & 15) << 8;
|
int index = ((maxY + 1) & 15) << 8;
|
||||||
for (int i = index; i < arr.length; i++) {
|
for (int i = index; i < arr.length; i++) {
|
||||||
arr[i] = 0;
|
arr[i] = BlockTypesCache.ReservedIDs.__RESERVED__;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
arr = new char[4096];
|
arr = new char[4096];
|
||||||
@ -130,7 +131,7 @@ public interface IBatchProcessor {
|
|||||||
if (arr != null) {
|
if (arr != null) {
|
||||||
int index = (minY & 15) << 8;
|
int index = (minY & 15) << 8;
|
||||||
for (int i = index; i < 4096; i++) {
|
for (int i = index; i < 4096; i++) {
|
||||||
arr[i] = 0;
|
arr[i] = BlockTypesCache.ReservedIDs.__RESERVED__;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
set.setBlocks(layer, arr);
|
set.setBlocks(layer, arr);
|
||||||
@ -139,7 +140,7 @@ public interface IBatchProcessor {
|
|||||||
if (arr != null) {
|
if (arr != null) {
|
||||||
int index = ((maxY + 1) & 15) << 8;
|
int index = ((maxY + 1) & 15) << 8;
|
||||||
for (int i = 0; i < index; i++) {
|
for (int i = 0; i < index; i++) {
|
||||||
arr[i] = 0;
|
arr[i] = BlockTypesCache.ReservedIDs.__RESERVED__;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
set.setBlocks(layer, arr);
|
set.setBlocks(layer, arr);
|
||||||
|
@ -8,8 +8,9 @@ import com.sk89q.worldedit.math.BlockVector3;
|
|||||||
import com.sk89q.worldedit.world.biome.BiomeType;
|
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||||
import com.sk89q.worldedit.world.block.BlockStateHolder;
|
import com.sk89q.worldedit.world.block.BlockStateHolder;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.util.HashMap;
|
import java.util.EnumMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
@ -95,7 +96,7 @@ public interface IChunkSet extends IBlocks, OutputExtent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
default Map<HeightMapType, int[]> getHeightMaps() {
|
default Map<HeightMapType, int[]> getHeightMaps() {
|
||||||
return new HashMap<>();
|
return new EnumMap<>(HeightMapType.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -115,4 +116,15 @@ public interface IChunkSet extends IBlocks, OutputExtent {
|
|||||||
*/
|
*/
|
||||||
boolean hasBiomes(int layer);
|
boolean hasBiomes(int layer);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an entirely distinct copy of this SET instance. All mutable data must be copied to sufficiently prevent leakage
|
||||||
|
* between the copy and the original.
|
||||||
|
*
|
||||||
|
* @return distinct new {@link IChunkSet instance}
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
default IChunkSet createCopy() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ import com.fastasyncworldedit.core.queue.implementation.chunk.ChunkCache;
|
|||||||
import com.fastasyncworldedit.core.util.MemUtil;
|
import com.fastasyncworldedit.core.util.MemUtil;
|
||||||
import com.fastasyncworldedit.core.util.TaskManager;
|
import com.fastasyncworldedit.core.util.TaskManager;
|
||||||
import com.fastasyncworldedit.core.util.collection.CleanableThreadLocal;
|
import com.fastasyncworldedit.core.util.collection.CleanableThreadLocal;
|
||||||
|
import com.fastasyncworldedit.core.util.task.FaweForkJoinWorkerThreadFactory;
|
||||||
import com.fastasyncworldedit.core.wrappers.WorldWrapper;
|
import com.fastasyncworldedit.core.wrappers.WorldWrapper;
|
||||||
import com.google.common.util.concurrent.Futures;
|
import com.google.common.util.concurrent.Futures;
|
||||||
import com.sk89q.worldedit.world.World;
|
import com.sk89q.worldedit.world.World;
|
||||||
@ -39,10 +40,41 @@ import java.util.function.Supplier;
|
|||||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||||
public abstract class QueueHandler implements Trimable, Runnable {
|
public abstract class QueueHandler implements Trimable, Runnable {
|
||||||
|
|
||||||
private final ForkJoinPool forkJoinPoolPrimary = new ForkJoinPool();
|
private static final int PROCESSORS = Runtime.getRuntime().availableProcessors();
|
||||||
private final ForkJoinPool forkJoinPoolSecondary = new ForkJoinPool();
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
private final ForkJoinPool forkJoinPoolPrimary = new ForkJoinPool(
|
||||||
|
PROCESSORS,
|
||||||
|
new FaweForkJoinWorkerThreadFactory("FAWE Fork Join Pool Primary - %s"),
|
||||||
|
null,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
private final ForkJoinPool forkJoinPoolSecondary = new ForkJoinPool(
|
||||||
|
PROCESSORS,
|
||||||
|
new FaweForkJoinWorkerThreadFactory("FAWE Fork Join Pool Secondary - %s"),
|
||||||
|
null,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
/**
|
||||||
|
* Main "work-horse" queue for FAWE. Handles chunk submission (and chunk submission alone). Blocking in order to forcibly
|
||||||
|
* prevent overworking/over-submission of chunk process tasks.
|
||||||
|
*/
|
||||||
private final ThreadPoolExecutor blockingExecutor = FaweCache.INSTANCE.newBlockingExecutor();
|
private final ThreadPoolExecutor blockingExecutor = FaweCache.INSTANCE.newBlockingExecutor();
|
||||||
|
/**
|
||||||
|
* Queue for tasks to be completed on the main thread. These take priority of tasks submitted to syncWhenFree queue
|
||||||
|
*/
|
||||||
private final ConcurrentLinkedQueue<FutureTask> syncTasks = new ConcurrentLinkedQueue<>();
|
private final ConcurrentLinkedQueue<FutureTask> syncTasks = new ConcurrentLinkedQueue<>();
|
||||||
|
/**
|
||||||
|
* Queue for tasks to be completed on the main thread. These are completed only if and when there is time left in a tick
|
||||||
|
* after completing all tasks in the syncTasks queue
|
||||||
|
*/
|
||||||
private final ConcurrentLinkedQueue<FutureTask> syncWhenFree = new ConcurrentLinkedQueue<>();
|
private final ConcurrentLinkedQueue<FutureTask> syncWhenFree = new ConcurrentLinkedQueue<>();
|
||||||
|
|
||||||
private final Map<World, WeakReference<IChunkCache<IChunkGet>>> chunkGetCache = new HashMap<>();
|
private final Map<World, WeakReference<IChunkCache<IChunkGet>>> chunkGetCache = new HashMap<>();
|
||||||
@ -53,9 +85,8 @@ public abstract class QueueHandler implements Trimable, Runnable {
|
|||||||
*/
|
*/
|
||||||
private long last;
|
private long last;
|
||||||
private long allocate = 50;
|
private long allocate = 50;
|
||||||
private double targetTPS = 18;
|
|
||||||
|
|
||||||
public QueueHandler() {
|
protected QueueHandler() {
|
||||||
TaskManager.taskManager().repeat(this, 1);
|
TaskManager.taskManager().repeat(this, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,13 +112,19 @@ public abstract class QueueHandler implements Trimable, Runnable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get if the {@code blockingExecutor} is saturated with tasks or not. Under-utilisation implies the queue has space for
|
||||||
|
* more submissions.
|
||||||
|
*
|
||||||
|
* @return true if {@code blockingExecutor} is not saturated with tasks
|
||||||
|
*/
|
||||||
public boolean isUnderutilized() {
|
public boolean isUnderutilized() {
|
||||||
return blockingExecutor.getActiveCount() < blockingExecutor.getMaximumPoolSize();
|
return blockingExecutor.getActiveCount() < blockingExecutor.getMaximumPoolSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
private long getAllocate() {
|
private long getAllocate() {
|
||||||
long now = System.currentTimeMillis();
|
long now = System.currentTimeMillis();
|
||||||
targetTPS = 18 - Math.max(Settings.settings().QUEUE.EXTRA_TIME_MS * 0.05, 0);
|
double targetTPS = 18 - Math.max(Settings.settings().QUEUE.EXTRA_TIME_MS * 0.05, 0);
|
||||||
long diff = 50 + this.last - (this.last = now);
|
long diff = 50 + this.last - (this.last = now);
|
||||||
long absDiff = Math.abs(diff);
|
long absDiff = Math.abs(diff);
|
||||||
if (diff == 0) {
|
if (diff == 0) {
|
||||||
@ -126,6 +163,10 @@ public abstract class QueueHandler implements Trimable, Runnable {
|
|||||||
} while (System.currentTimeMillis() - start < currentAllocate);
|
} while (System.currentTimeMillis() - start < currentAllocate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated For removal without replacement.
|
||||||
|
*/
|
||||||
|
@Deprecated(forRemoval = true, since = "2.6.2")
|
||||||
public <T extends Future<T>> void complete(Future<T> task) {
|
public <T extends Future<T>> void complete(Future<T> task) {
|
||||||
try {
|
try {
|
||||||
while (task != null) {
|
while (task != null) {
|
||||||
@ -136,49 +177,140 @@ public abstract class QueueHandler implements Trimable, Runnable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Complete a task in the {@code forkJoinPoolSecondary} queue. 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.
|
||||||
|
*
|
||||||
|
* @param run Runnable to run
|
||||||
|
* @param value Value to return when done
|
||||||
|
* @param <T> Value type
|
||||||
|
* @return Future for submitted task
|
||||||
|
*/
|
||||||
public <T> Future<T> async(Runnable run, T value) {
|
public <T> Future<T> async(Runnable run, T value) {
|
||||||
return forkJoinPoolSecondary.submit(run, value);
|
return forkJoinPoolSecondary.submit(run, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Complete a task in the {@code forkJoinPoolSecondary} queue. 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.
|
||||||
|
*
|
||||||
|
* @param run Runnable to run
|
||||||
|
* @return Future for submitted task
|
||||||
|
*/
|
||||||
public Future<?> async(Runnable run) {
|
public Future<?> async(Runnable run) {
|
||||||
return forkJoinPoolSecondary.submit(run);
|
return forkJoinPoolSecondary.submit(run);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Complete a task in the {@code forkJoinPoolSecondary} queue. 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.
|
||||||
|
*
|
||||||
|
* @param call Callable to run
|
||||||
|
* @param <T> Return value type
|
||||||
|
* @return Future for submitted task
|
||||||
|
*/
|
||||||
public <T> Future<T> async(Callable<T> call) {
|
public <T> Future<T> async(Callable<T> call) {
|
||||||
return forkJoinPoolSecondary.submit(call);
|
return forkJoinPoolSecondary.submit(call);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ForkJoinTask submit(Runnable call) {
|
/**
|
||||||
return forkJoinPoolPrimary.submit(call);
|
* Complete a task in the {@code forkJoinPoolPrimary} queue. 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.
|
||||||
|
*
|
||||||
|
* @param run Task to run
|
||||||
|
* @return {@link ForkJoinTask} representing task being run
|
||||||
|
*/
|
||||||
|
public ForkJoinTask submit(Runnable run) {
|
||||||
|
return forkJoinPoolPrimary.submit(run);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Submit a task to be run on the main thread. Does not guarantee to be run on the next tick as FAWE will only operate to
|
||||||
|
* maintain approx. 18 tps.
|
||||||
|
*
|
||||||
|
* @param run Task to run
|
||||||
|
* @param <T> Value type
|
||||||
|
* @return Future representing task
|
||||||
|
*/
|
||||||
public <T> Future<T> sync(Runnable run) {
|
public <T> Future<T> sync(Runnable run) {
|
||||||
return sync(run, syncTasks);
|
return sync(run, syncTasks);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Submit a task to be run on the main thread. Does not guarantee to be run on the next tick as FAWE will only operate to
|
||||||
|
* maintain approx. 18 tps.
|
||||||
|
*
|
||||||
|
* @param call Task to run
|
||||||
|
* @param <T> Value type
|
||||||
|
* @return Future representing task
|
||||||
|
*/
|
||||||
public <T> Future<T> sync(Callable<T> call) throws Exception {
|
public <T> Future<T> sync(Callable<T> call) throws Exception {
|
||||||
return sync(call, syncTasks);
|
return sync(call, syncTasks);
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T> Future<T> sync(Supplier<T> call) {
|
/**
|
||||||
return sync(call, syncTasks);
|
* Submit a task to be run on the main thread. Does not guarantee to be run on the next tick as FAWE will only operate to
|
||||||
|
* maintain approx. 18 tps.
|
||||||
|
*
|
||||||
|
* @param supplier Task to run
|
||||||
|
* @param <T> Value type
|
||||||
|
* @return Future representing task
|
||||||
|
*/
|
||||||
|
public <T> Future<T> sync(Supplier<T> supplier) {
|
||||||
|
return sync(supplier, syncTasks);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lower priority sync task (runs only when there are no other tasks)
|
/**
|
||||||
|
* Submit a task to be run on the main thread. Does not guarantee to be run on the next tick as FAWE will only operate to
|
||||||
|
* maintain approx. 18 tps. Takes lower priority than tasks submitted via any {@code QueueHandler#sync} method. Completed
|
||||||
|
* only if and when there is time left in a tick after completing all sync tasks submitted using the aforementioned methods.
|
||||||
|
*
|
||||||
|
* @param run Task to run
|
||||||
|
* @param value Value to return when done
|
||||||
|
* @param <T> Value type
|
||||||
|
* @return Future representing task
|
||||||
|
*/
|
||||||
public <T> Future<T> syncWhenFree(Runnable run, T value) {
|
public <T> Future<T> syncWhenFree(Runnable run, T value) {
|
||||||
return sync(run, value, syncWhenFree);
|
return sync(run, value, syncWhenFree);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Submit a task to be run on the main thread. Does not guarantee to be run on the next tick as FAWE will only operate to
|
||||||
|
* maintain approx. 18 tps. Takes lower priority than tasks submitted via any {@code QueueHandler#sync} method. Completed
|
||||||
|
* only if and when there is time left in a tick after completing all sync tasks submitted using the aforementioned methods.
|
||||||
|
*
|
||||||
|
* @param run Task to run
|
||||||
|
* @param <T> Value type
|
||||||
|
* @return Future representing task
|
||||||
|
*/
|
||||||
public <T> Future<T> syncWhenFree(Runnable run) {
|
public <T> Future<T> syncWhenFree(Runnable run) {
|
||||||
return sync(run, syncWhenFree);
|
return sync(run, syncWhenFree);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Submit a task to be run on the main thread. Does not guarantee to be run on the next tick as FAWE will only operate to
|
||||||
|
* maintain approx. 18 tps. Takes lower priority than tasks submitted via any {@code QueueHandler#sync} method. Completed
|
||||||
|
* only if and when there is time left in a tick after completing all sync tasks submitted using the aforementioned methods.
|
||||||
|
*
|
||||||
|
* @param call Task to run
|
||||||
|
* @param <T> Value type
|
||||||
|
* @return Future representing task
|
||||||
|
*/
|
||||||
public <T> Future<T> syncWhenFree(Callable<T> call) throws Exception {
|
public <T> Future<T> syncWhenFree(Callable<T> call) throws Exception {
|
||||||
return sync(call, syncWhenFree);
|
return sync(call, syncWhenFree);
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T> Future<T> syncWhenFree(Supplier<T> call) {
|
/**
|
||||||
return sync(call, syncWhenFree);
|
* Submit a task to be run on the main thread. Does not guarantee to be run on the next tick as FAWE will only operate to
|
||||||
|
* maintain approx. 18 tps. Takes lower priority than tasks submitted via any {@code QueueHandler#sync} method. Completed
|
||||||
|
* only if and when there is time left in a tick after completing all sync tasks submitted using the aforementioned methods.
|
||||||
|
*
|
||||||
|
* @param supplier Task to run
|
||||||
|
* @param <T> Value type
|
||||||
|
* @return Future representing task
|
||||||
|
*/
|
||||||
|
public <T> Future<T> syncWhenFree(Supplier<T> supplier) {
|
||||||
|
return sync(supplier, syncWhenFree);
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T> Future<T> sync(Runnable run, T value, Queue<FutureTask> queue) {
|
private <T> Future<T> sync(Runnable run, T value, Queue<FutureTask> queue) {
|
||||||
@ -229,6 +361,15 @@ public abstract class QueueHandler implements Trimable, Runnable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal use only. Specifically for submitting {@link IQueueChunk} for "processing" an edit. Submits to the blocking
|
||||||
|
* executor, the main "work-horse" queue for FAWE. Handles chunk submission (and chunk submission alone). Blocking in order
|
||||||
|
* to forcibly prevent overworking/over-submission of chunk process tasks.
|
||||||
|
*
|
||||||
|
* @param chunk chunk
|
||||||
|
* @param <T>
|
||||||
|
* @return Future representing task
|
||||||
|
*/
|
||||||
public <T extends Future<T>> T submit(IQueueChunk<T> chunk) {
|
public <T extends Future<T>> T submit(IQueueChunk<T> chunk) {
|
||||||
// if (MemUtil.isMemoryFree()) { TODO NOT IMPLEMENTED - optimize this
|
// if (MemUtil.isMemoryFree()) { TODO NOT IMPLEMENTED - optimize this
|
||||||
// return (T) forkJoinPoolSecondary.submit(chunk);
|
// return (T) forkJoinPoolSecondary.submit(chunk);
|
||||||
@ -260,6 +401,9 @@ public abstract class QueueHandler implements Trimable, Runnable {
|
|||||||
return new SingleThreadQueueExtent();
|
return new SingleThreadQueueExtent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the current thread's {@link IQueueExtent} instance in the queue pool to null.
|
||||||
|
*/
|
||||||
public void unCache() {
|
public void unCache() {
|
||||||
queuePool.set(null);
|
queuePool.set(null);
|
||||||
}
|
}
|
||||||
@ -272,14 +416,58 @@ public abstract class QueueHandler implements Trimable, Runnable {
|
|||||||
return queue;
|
return queue;
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract void startSet(boolean parallel);
|
/**
|
||||||
|
* Indicate a "set" task is being started.
|
||||||
|
*
|
||||||
|
* @param parallel if the "set" being started is parallel/async
|
||||||
|
* @deprecated To be replaced by better-named {@link QueueHandler#startUnsafe(boolean)} )}
|
||||||
|
*/
|
||||||
|
@Deprecated(forRemoval = true, since = "2.6.2")
|
||||||
|
public void startSet(boolean parallel) {
|
||||||
|
startUnsafe(parallel);
|
||||||
|
}
|
||||||
|
|
||||||
public abstract void endSet(boolean parallel);
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicate a "set" task is ending.
|
||||||
|
*
|
||||||
|
* @param parallel if the "set" being started is parallel/async
|
||||||
|
* @deprecated To be replaced by better-named {@link QueueHandler#endUnsafe(boolean)} )}
|
||||||
|
*/
|
||||||
|
@Deprecated(forRemoval = true, since = "2.6.2")
|
||||||
|
public void endSet(boolean parallel) {
|
||||||
|
startUnsafe(parallel);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicate an unsafe task is starting. Physics are frozen, async catchers disabled, etc. for the duration of the task
|
||||||
|
*
|
||||||
|
* @param parallel If the task is being run async and/or in parallel
|
||||||
|
*/
|
||||||
|
public abstract void startUnsafe(boolean parallel);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicate a/the unsafe task submitted after a {@link QueueHandler#startUnsafe(boolean)} call has ended.
|
||||||
|
*
|
||||||
|
* @param parallel If the task was being run async and/or in parallel
|
||||||
|
*/
|
||||||
|
public abstract void endUnsafe(boolean parallel);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new queue for a given world.
|
||||||
|
*/
|
||||||
public IQueueExtent<IQueueChunk> getQueue(World world) {
|
public IQueueExtent<IQueueChunk> getQueue(World world) {
|
||||||
return getQueue(world, null, null);
|
return getQueue(world, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new queue for a given world.
|
||||||
|
*
|
||||||
|
* @param world World to create queue for
|
||||||
|
* @param processor existing processor to set to queue or null
|
||||||
|
* @param postProcessor existing post-processor to set to queue or null
|
||||||
|
* @return New queue for given world
|
||||||
|
*/
|
||||||
public IQueueExtent<IQueueChunk> getQueue(World world, IBatchProcessor processor, IBatchProcessor postProcessor) {
|
public IQueueExtent<IQueueChunk> getQueue(World world, IBatchProcessor processor, IBatchProcessor postProcessor) {
|
||||||
final IQueueExtent<IQueueChunk> queue = pool();
|
final IQueueExtent<IQueueChunk> queue = pool();
|
||||||
IChunkCache<IChunkGet> cacheGet = getOrCreateWorldCache(world);
|
IChunkCache<IChunkGet> cacheGet = getOrCreateWorldCache(world);
|
||||||
@ -294,6 +482,12 @@ public abstract class QueueHandler implements Trimable, Runnable {
|
|||||||
return queue;
|
return queue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trims each chunk GET cache
|
||||||
|
*
|
||||||
|
* @param aggressive if each chunk GET cache should be trimmed aggressively
|
||||||
|
* @return true if all chunk GET caches could be trimmed
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean trim(boolean aggressive) {
|
public boolean trim(boolean aggressive) {
|
||||||
boolean result = true;
|
boolean result = true;
|
||||||
|
@ -275,8 +275,8 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implemen
|
|||||||
* Get a new IChunk from either the pool, or create a new one<br> + Initialize it at the
|
* Get a new IChunk from either the pool, or create a new one<br> + Initialize it at the
|
||||||
* coordinates
|
* coordinates
|
||||||
*
|
*
|
||||||
* @param chunkX
|
* @param chunkX X chunk coordinate
|
||||||
* @param chunkZ
|
* @param chunkZ Z chunk coordinate
|
||||||
* @return IChunk
|
* @return IChunk
|
||||||
*/
|
*/
|
||||||
private ChunkHolder poolOrCreate(int chunkX, int chunkZ) {
|
private ChunkHolder poolOrCreate(int chunkX, int chunkZ) {
|
||||||
@ -309,19 +309,11 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implemen
|
|||||||
// If queueing is enabled AND either of the following
|
// If queueing is enabled AND either of the following
|
||||||
// - memory is low & queue size > num threads + 8
|
// - memory is low & queue size > num threads + 8
|
||||||
// - queue size > target size and primary queue has less than num threads submissions
|
// - queue size > target size and primary queue has less than num threads submissions
|
||||||
if (enabledQueue && ((lowMem && size > Settings.settings().QUEUE.PARALLEL_THREADS + 8) || (size > Settings.settings().QUEUE.TARGET_SIZE && Fawe
|
int targetSize = lowMem ? Settings.settings().QUEUE.PARALLEL_THREADS + 8 : Settings.settings().QUEUE.TARGET_SIZE;
|
||||||
.instance()
|
if (enabledQueue && size > targetSize && (lowMem || Fawe.instance().getQueueHandler().isUnderutilized())) {
|
||||||
.getQueueHandler()
|
|
||||||
.isUnderutilized()))) {
|
|
||||||
chunk = chunks.removeFirst();
|
chunk = chunks.removeFirst();
|
||||||
final Future future = submitUnchecked(chunk);
|
final Future future = submitUnchecked(chunk);
|
||||||
if (future != null && !future.isDone()) {
|
if (future != null && !future.isDone()) {
|
||||||
final int targetSize;
|
|
||||||
if (lowMem) {
|
|
||||||
targetSize = Settings.settings().QUEUE.PARALLEL_THREADS + 8;
|
|
||||||
} else {
|
|
||||||
targetSize = Settings.settings().QUEUE.TARGET_SIZE;
|
|
||||||
}
|
|
||||||
pollSubmissions(targetSize, lowMem);
|
pollSubmissions(targetSize, lowMem);
|
||||||
submissions.add(future);
|
submissions.add(future);
|
||||||
}
|
}
|
||||||
|
@ -15,12 +15,11 @@ import com.sk89q.worldedit.world.block.BlockTypesCache;
|
|||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.EnumMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.stream.IntStream;
|
|
||||||
|
|
||||||
public class CharSetBlocks extends CharBlocks implements IChunkSet {
|
public class CharSetBlocks extends CharBlocks implements IChunkSet {
|
||||||
|
|
||||||
@ -40,7 +39,7 @@ public class CharSetBlocks extends CharBlocks implements IChunkSet {
|
|||||||
public BlockVector3ChunkMap<CompoundTag> tiles;
|
public BlockVector3ChunkMap<CompoundTag> tiles;
|
||||||
public HashSet<CompoundTag> entities;
|
public HashSet<CompoundTag> entities;
|
||||||
public HashSet<UUID> entityRemoves;
|
public HashSet<UUID> entityRemoves;
|
||||||
public Map<HeightMapType, int[]> heightMaps;
|
public EnumMap<HeightMapType, int[]> heightMaps;
|
||||||
private boolean fastMode = false;
|
private boolean fastMode = false;
|
||||||
private int bitMask = -1;
|
private int bitMask = -1;
|
||||||
|
|
||||||
@ -93,7 +92,7 @@ public class CharSetBlocks extends CharBlocks implements IChunkSet {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<HeightMapType, int[]> getHeightMaps() {
|
public Map<HeightMapType, int[]> getHeightMaps() {
|
||||||
return heightMaps == null ? new HashMap<>() : heightMaps;
|
return heightMaps == null ? new EnumMap<>(HeightMapType.class) : heightMaps;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -177,7 +176,7 @@ public class CharSetBlocks extends CharBlocks implements IChunkSet {
|
|||||||
@Override
|
@Override
|
||||||
public void setHeightMap(HeightMapType type, int[] heightMap) {
|
public void setHeightMap(HeightMapType type, int[] heightMap) {
|
||||||
if (heightMaps == null) {
|
if (heightMaps == null) {
|
||||||
heightMaps = new HashMap<>();
|
heightMaps = new EnumMap<>(HeightMapType.class);
|
||||||
}
|
}
|
||||||
heightMaps.put(type, heightMap);
|
heightMaps.put(type, heightMap);
|
||||||
}
|
}
|
||||||
@ -306,8 +305,12 @@ public class CharSetBlocks extends CharBlocks implements IChunkSet {
|
|||||||
|| (heightMaps != null && !heightMaps.isEmpty())) {
|
|| (heightMaps != null && !heightMaps.isEmpty())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
//noinspection SimplifyStreamApiCallChains - this is faster than using #noneMatch
|
for (int i = minSectionPosition; i <= maxSectionPosition; i++) {
|
||||||
return !IntStream.range(minSectionPosition, maxSectionPosition + 1).anyMatch(this::hasSection);
|
if (hasSection(i)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -316,6 +319,9 @@ public class CharSetBlocks extends CharBlocks implements IChunkSet {
|
|||||||
tiles = null;
|
tiles = null;
|
||||||
entities = null;
|
entities = null;
|
||||||
entityRemoves = null;
|
entityRemoves = null;
|
||||||
|
light = null;
|
||||||
|
skyLight = null;
|
||||||
|
heightMaps = null;
|
||||||
super.reset();
|
super.reset();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -329,6 +335,62 @@ public class CharSetBlocks extends CharBlocks implements IChunkSet {
|
|||||||
return biomes != null && biomes[layer] != null;
|
return biomes != null && biomes[layer] != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ThreadUnsafeCharBlocks createCopy() {
|
||||||
|
char[][] blocksCopy = new char[sectionCount][];
|
||||||
|
for (int i = 0; i < sectionCount; i++) {
|
||||||
|
if (blocks[i] != null) {
|
||||||
|
blocksCopy[i] = new char[FaweCache.INSTANCE.BLOCKS_PER_LAYER];
|
||||||
|
System.arraycopy(blocks[i], 0, blocksCopy[i], 0, FaweCache.INSTANCE.BLOCKS_PER_LAYER);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BiomeType[][] biomesCopy;
|
||||||
|
if (biomes == null) {
|
||||||
|
biomesCopy = null;
|
||||||
|
} else {
|
||||||
|
biomesCopy = new BiomeType[sectionCount][];
|
||||||
|
for (int i = 0; i < sectionCount; i++) {
|
||||||
|
if (biomes[i] != null) {
|
||||||
|
biomesCopy[i] = new BiomeType[biomes[i].length];
|
||||||
|
System.arraycopy(biomes[i], 0, biomesCopy[i], 0, biomes[i].length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
char[][] lightCopy = createLightCopy(light, sectionCount);
|
||||||
|
char[][] skyLightCopy = createLightCopy(skyLight, sectionCount);
|
||||||
|
return new ThreadUnsafeCharBlocks(
|
||||||
|
blocksCopy,
|
||||||
|
minSectionPosition,
|
||||||
|
maxSectionPosition,
|
||||||
|
biomesCopy,
|
||||||
|
sectionCount,
|
||||||
|
lightCopy,
|
||||||
|
skyLightCopy,
|
||||||
|
tiles != null ? new BlockVector3ChunkMap<>(tiles) : null,
|
||||||
|
entities != null ? new HashSet<>(entities) : null,
|
||||||
|
entityRemoves != null ? new HashSet<>(entityRemoves) : null,
|
||||||
|
heightMaps != null ? new EnumMap<>(heightMaps) : null,
|
||||||
|
defaultOrdinal(),
|
||||||
|
fastMode,
|
||||||
|
bitMask
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char[][] createLightCopy(char[][] lightArr, int sectionCount) {
|
||||||
|
if (lightArr == null) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
char[][] lightCopy = new char[sectionCount][];
|
||||||
|
for (int i = 0; i < sectionCount; i++) {
|
||||||
|
if (lightArr[i] != null) {
|
||||||
|
lightCopy[i] = new char[lightArr[i].length];
|
||||||
|
System.arraycopy(lightArr[i], 0, lightCopy[i], 0, lightArr[i].length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return lightCopy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public char[] load(final int layer) {
|
public char[] load(final int layer) {
|
||||||
updateSectionIndexRange(layer);
|
updateSectionIndexRange(layer);
|
||||||
@ -348,67 +410,47 @@ public class CharSetBlocks extends CharBlocks implements IChunkSet {
|
|||||||
if (layer < minSectionPosition) {
|
if (layer < minSectionPosition) {
|
||||||
int diff = minSectionPosition - layer;
|
int diff = minSectionPosition - layer;
|
||||||
sectionCount += diff;
|
sectionCount += diff;
|
||||||
char[][] tmpBlocks = new char[sectionCount][];
|
|
||||||
Section[] tmpSections = new Section[sectionCount];
|
|
||||||
Object[] tmpSectionLocks = new Object[sectionCount];
|
|
||||||
System.arraycopy(blocks, 0, tmpBlocks, diff, blocks.length);
|
|
||||||
System.arraycopy(sections, 0, tmpSections, diff, sections.length);
|
|
||||||
System.arraycopy(sectionLocks, 0, tmpSectionLocks, diff, sections.length);
|
|
||||||
for (int i = 0; i < diff; i++) {
|
|
||||||
tmpSections[i] = EMPTY;
|
|
||||||
tmpSectionLocks[i] = new Object();
|
|
||||||
}
|
|
||||||
blocks = tmpBlocks;
|
|
||||||
sections = tmpSections;
|
|
||||||
sectionLocks = tmpSectionLocks;
|
|
||||||
minSectionPosition = layer;
|
minSectionPosition = layer;
|
||||||
if (biomes != null) {
|
resizeSectionsArrays(diff, false); // prepend new layer(s)
|
||||||
BiomeType[][] tmpBiomes = new BiomeType[sectionCount][64];
|
|
||||||
System.arraycopy(biomes, 0, tmpBiomes, diff, biomes.length);
|
|
||||||
biomes = tmpBiomes;
|
|
||||||
}
|
|
||||||
if (light != null) {
|
|
||||||
char[][] tmplight = new char[sectionCount][];
|
|
||||||
System.arraycopy(light, 0, tmplight, diff, light.length);
|
|
||||||
light = tmplight;
|
|
||||||
}
|
|
||||||
if (skyLight != null) {
|
|
||||||
char[][] tmplight = new char[sectionCount][];
|
|
||||||
System.arraycopy(skyLight, 0, tmplight, diff, skyLight.length);
|
|
||||||
skyLight = tmplight;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
int diff = layer - maxSectionPosition;
|
int diff = layer - maxSectionPosition;
|
||||||
sectionCount += diff;
|
sectionCount += diff;
|
||||||
char[][] tmpBlocks = new char[sectionCount][];
|
|
||||||
Section[] tmpSections = new Section[sectionCount];
|
|
||||||
Object[] tmpSectionLocks = new Object[sectionCount];
|
|
||||||
System.arraycopy(blocks, 0, tmpBlocks, 0, blocks.length);
|
|
||||||
System.arraycopy(sections, 0, tmpSections, 0, sections.length);
|
|
||||||
System.arraycopy(sectionLocks, 0, tmpSectionLocks, 0, sections.length);
|
|
||||||
for (int i = sectionCount - diff; i < sectionCount; i++) {
|
|
||||||
tmpSections[i] = EMPTY;
|
|
||||||
tmpSectionLocks[i] = new Object();
|
|
||||||
}
|
|
||||||
blocks = tmpBlocks;
|
|
||||||
sections = tmpSections;
|
|
||||||
sectionLocks = tmpSectionLocks;
|
|
||||||
maxSectionPosition = layer;
|
maxSectionPosition = layer;
|
||||||
if (biomes != null) {
|
resizeSectionsArrays(diff, true); // append new layer(s)
|
||||||
BiomeType[][] tmpBiomes = new BiomeType[sectionCount][64];
|
}
|
||||||
System.arraycopy(biomes, 0, tmpBiomes, 0, biomes.length);
|
}
|
||||||
biomes = tmpBiomes;
|
|
||||||
}
|
private void resizeSectionsArrays(int diff, boolean appendNew) {
|
||||||
if (light != null) {
|
char[][] tmpBlocks = new char[sectionCount][];
|
||||||
char[][] tmplight = new char[sectionCount][];
|
Section[] tmpSections = new Section[sectionCount];
|
||||||
System.arraycopy(light, 0, tmplight, 0, light.length);
|
Object[] tmpSectionLocks = new Object[sectionCount];
|
||||||
light = tmplight;
|
int destPos = appendNew ? 0 : diff;
|
||||||
}
|
System.arraycopy(blocks, 0, tmpBlocks, destPos, blocks.length);
|
||||||
if (skyLight != null) {
|
System.arraycopy(sections, 0, tmpSections, destPos, sections.length);
|
||||||
char[][] tmplight = new char[sectionCount][];
|
System.arraycopy(sectionLocks, 0, tmpSectionLocks, destPos, sections.length);
|
||||||
System.arraycopy(skyLight, 0, tmplight, 0, skyLight.length);
|
int toFillFrom = appendNew ? sectionCount - diff : 0;
|
||||||
skyLight = tmplight;
|
int toFillTo = appendNew ? sectionCount : diff;
|
||||||
}
|
for (int i = toFillFrom; i < toFillTo; i++) {
|
||||||
|
tmpSections[i] = EMPTY;
|
||||||
|
tmpSectionLocks[i] = new Object();
|
||||||
|
}
|
||||||
|
blocks = tmpBlocks;
|
||||||
|
sections = tmpSections;
|
||||||
|
sectionLocks = tmpSectionLocks;
|
||||||
|
if (biomes != null) {
|
||||||
|
BiomeType[][] tmpBiomes = new BiomeType[sectionCount][64];
|
||||||
|
System.arraycopy(biomes, 0, tmpBiomes, destPos, biomes.length);
|
||||||
|
biomes = tmpBiomes;
|
||||||
|
}
|
||||||
|
if (light != null) {
|
||||||
|
char[][] tmplight = new char[sectionCount][];
|
||||||
|
System.arraycopy(light, 0, tmplight, destPos, light.length);
|
||||||
|
light = tmplight;
|
||||||
|
}
|
||||||
|
if (skyLight != null) {
|
||||||
|
char[][] tmplight = new char[sectionCount][];
|
||||||
|
System.arraycopy(skyLight, 0, tmplight, destPos, skyLight.length);
|
||||||
|
skyLight = tmplight;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,531 @@
|
|||||||
|
package com.fastasyncworldedit.core.queue.implementation.blocks;
|
||||||
|
|
||||||
|
import com.fastasyncworldedit.core.Fawe;
|
||||||
|
import com.fastasyncworldedit.core.FaweCache;
|
||||||
|
import com.fastasyncworldedit.core.extent.processor.heightmap.HeightMapType;
|
||||||
|
import com.fastasyncworldedit.core.math.BlockVector3ChunkMap;
|
||||||
|
import com.fastasyncworldedit.core.queue.IBlocks;
|
||||||
|
import com.fastasyncworldedit.core.queue.IChunkSet;
|
||||||
|
import com.sk89q.jnbt.CompoundTag;
|
||||||
|
import com.sk89q.worldedit.internal.util.LogManagerCompat;
|
||||||
|
import com.sk89q.worldedit.math.BlockVector3;
|
||||||
|
import com.sk89q.worldedit.world.biome.BiomeType;
|
||||||
|
import com.sk89q.worldedit.world.block.BlockState;
|
||||||
|
import com.sk89q.worldedit.world.block.BlockStateHolder;
|
||||||
|
import com.sk89q.worldedit.world.block.BlockTypesCache;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.EnumMap;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Equivalent to {@link CharSetBlocks} without any attempt to make thread-safe for improved performance.
|
||||||
|
* This is currently only used as a "copy" of {@link CharSetBlocks} to provide to
|
||||||
|
* {@link com.fastasyncworldedit.core.queue.IBatchProcessor} instances for processing without overlapping the continuing edit.
|
||||||
|
*
|
||||||
|
* @since 2.6.2
|
||||||
|
*/
|
||||||
|
public class ThreadUnsafeCharBlocks implements IChunkSet, IBlocks {
|
||||||
|
|
||||||
|
private static final Logger LOGGER = LogManagerCompat.getLogger();
|
||||||
|
|
||||||
|
private final char defaultOrdinal;
|
||||||
|
private char[][] blocks;
|
||||||
|
private int minSectionPosition;
|
||||||
|
private int maxSectionPosition;
|
||||||
|
private int sectionCount;
|
||||||
|
private BiomeType[][] biomes;
|
||||||
|
private char[][] light;
|
||||||
|
private char[][] skyLight;
|
||||||
|
private BlockVector3ChunkMap<CompoundTag> tiles;
|
||||||
|
private HashSet<CompoundTag> entities;
|
||||||
|
private HashSet<UUID> entityRemoves;
|
||||||
|
private Map<HeightMapType, int[]> heightMaps;
|
||||||
|
private boolean fastMode;
|
||||||
|
private int bitMask;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* New instance given the data stored in a {@link CharSetBlocks} instance.
|
||||||
|
*
|
||||||
|
* @since 2.6.2
|
||||||
|
*/
|
||||||
|
ThreadUnsafeCharBlocks(
|
||||||
|
char[][] blocks,
|
||||||
|
int minSectionPosition,
|
||||||
|
int maxSectionPosition,
|
||||||
|
BiomeType[][] biomes,
|
||||||
|
int sectionCount,
|
||||||
|
char[][] light,
|
||||||
|
char[][] skyLight,
|
||||||
|
BlockVector3ChunkMap<CompoundTag> tiles,
|
||||||
|
HashSet<CompoundTag> entities,
|
||||||
|
HashSet<UUID> entityRemoves,
|
||||||
|
Map<HeightMapType, int[]> heightMaps,
|
||||||
|
char defaultOrdinal,
|
||||||
|
boolean fastMode,
|
||||||
|
int bitMask
|
||||||
|
) {
|
||||||
|
this.blocks = blocks;
|
||||||
|
this.minSectionPosition = minSectionPosition;
|
||||||
|
this.maxSectionPosition = maxSectionPosition;
|
||||||
|
this.biomes = biomes;
|
||||||
|
this.sectionCount = sectionCount;
|
||||||
|
this.light = light;
|
||||||
|
this.skyLight = skyLight;
|
||||||
|
this.tiles = tiles;
|
||||||
|
this.entities = entities;
|
||||||
|
this.entityRemoves = entityRemoves;
|
||||||
|
this.heightMaps = heightMaps;
|
||||||
|
this.defaultOrdinal = defaultOrdinal;
|
||||||
|
this.fastMode = fastMode;
|
||||||
|
this.bitMask = bitMask;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasSection(int layer) {
|
||||||
|
layer -= minSectionPosition;
|
||||||
|
return layer >= 0 && layer < blocks.length && blocks[layer] != null && blocks[layer].length == FaweCache.INSTANCE.BLOCKS_PER_LAYER;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public char[] load(int layer) {
|
||||||
|
updateSectionIndexRange(layer);
|
||||||
|
layer -= minSectionPosition;
|
||||||
|
char[] arr = blocks[layer];
|
||||||
|
if (arr == null) {
|
||||||
|
arr = blocks[layer] = new char[FaweCache.INSTANCE.BLOCKS_PER_LAYER];
|
||||||
|
}
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public char[] loadIfPresent(int layer) {
|
||||||
|
if (layer < minSectionPosition || layer > maxSectionPosition) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
layer -= minSectionPosition;
|
||||||
|
return blocks[layer];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<BlockVector3, CompoundTag> getTiles() {
|
||||||
|
return tiles == null ? Collections.emptyMap() : tiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompoundTag getTile(int x, int y, int z) {
|
||||||
|
return tiles.get(x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<CompoundTag> getEntities() {
|
||||||
|
return entities == null ? Collections.emptySet() : entities;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<HeightMapType, int[]> getHeightMaps() {
|
||||||
|
return heightMaps == null ? new HashMap<>() : heightMaps;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeSectionLighting(int layer, boolean sky) {
|
||||||
|
updateSectionIndexRange(layer);
|
||||||
|
layer -= minSectionPosition;
|
||||||
|
if (light == null) {
|
||||||
|
light = new char[sectionCount][];
|
||||||
|
}
|
||||||
|
if (light[layer] == null) {
|
||||||
|
light[layer] = new char[4096];
|
||||||
|
}
|
||||||
|
Arrays.fill(light[layer], (char) 0);
|
||||||
|
if (sky) {
|
||||||
|
if (skyLight == null) {
|
||||||
|
skyLight = new char[sectionCount][];
|
||||||
|
}
|
||||||
|
if (skyLight[layer] == null) {
|
||||||
|
skyLight[layer] = new char[4096];
|
||||||
|
}
|
||||||
|
Arrays.fill(skyLight[layer], (char) 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean trim(boolean aggressive, int layer) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getSectionCount() {
|
||||||
|
return sectionCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getMaxSectionPosition() {
|
||||||
|
return maxSectionPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getMinSectionPosition() {
|
||||||
|
return minSectionPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
public char get(int x, int y, int z) {
|
||||||
|
int layer = (y >> 4);
|
||||||
|
if (!hasSection(layer)) {
|
||||||
|
return defaultOrdinal;
|
||||||
|
}
|
||||||
|
final int index = (y & 15) << 8 | z << 4 | x;
|
||||||
|
return blocks[layer - minSectionPosition][index];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BiomeType getBiomeType(int x, int y, int z) {
|
||||||
|
int layer;
|
||||||
|
if (biomes == null || (y >> 4) < minSectionPosition || (y >> 4) > maxSectionPosition) {
|
||||||
|
return null;
|
||||||
|
} else if (biomes[(layer = (y >> 4) - minSectionPosition)] == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return biomes[layer][(y & 15) >> 2 | (z >> 2) << 2 | x >> 2];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BlockState getBlock(int x, int y, int z) {
|
||||||
|
return BlockTypesCache.states[get(x, y, z)];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean setBiome(int x, int y, int z, BiomeType biome) {
|
||||||
|
updateSectionIndexRange(y >> 4);
|
||||||
|
int layer = (y >> 4) - minSectionPosition;
|
||||||
|
if (biomes == null) {
|
||||||
|
biomes = new BiomeType[sectionCount][];
|
||||||
|
biomes[layer] = new BiomeType[64];
|
||||||
|
} else if (biomes[layer] == null) {
|
||||||
|
biomes[layer] = new BiomeType[64];
|
||||||
|
}
|
||||||
|
biomes[layer][(y & 12) << 2 | (z & 12) | (x & 12) >> 2] = biome;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean setBiome(BlockVector3 position, BiomeType biome) {
|
||||||
|
return setBiome(position.getX(), position.getY(), position.getZ(), biome);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void set(int x, int y, int z, char value) {
|
||||||
|
final int layer = y >> 4;
|
||||||
|
final int index = (y & 15) << 8 | z << 4 | x;
|
||||||
|
try {
|
||||||
|
blocks[layer][index] = value;
|
||||||
|
} catch (ArrayIndexOutOfBoundsException exception) {
|
||||||
|
LOGGER.error("Tried setting block at coordinates (" + x + "," + y + "," + z + ")");
|
||||||
|
assert Fawe.platform() != null;
|
||||||
|
LOGGER.error("Layer variable was = {}", layer, exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T extends BlockStateHolder<T>> boolean setBlock(int x, int y, int z, T holder) {
|
||||||
|
updateSectionIndexRange(y >> 4);
|
||||||
|
set(x, y, z, holder.getOrdinalChar());
|
||||||
|
holder.applyTileEntity(this, x, y, z);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setBlocks(int layer, char[] data) {
|
||||||
|
updateSectionIndexRange(layer);
|
||||||
|
layer -= minSectionPosition;
|
||||||
|
this.blocks[layer] = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty() {
|
||||||
|
if (biomes != null
|
||||||
|
|| light != null
|
||||||
|
|| skyLight != null
|
||||||
|
|| (entities != null && !entities.isEmpty())
|
||||||
|
|| (tiles != null && !tiles.isEmpty())
|
||||||
|
|| (entityRemoves != null && !entityRemoves.isEmpty())
|
||||||
|
|| (heightMaps != null && !heightMaps.isEmpty())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (int i = minSectionPosition; i <= maxSectionPosition; i++) {
|
||||||
|
if (hasSection(i)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean setTile(int x, int y, int z, CompoundTag tile) {
|
||||||
|
updateSectionIndexRange(y >> 4);
|
||||||
|
if (tiles == null) {
|
||||||
|
tiles = new BlockVector3ChunkMap<>();
|
||||||
|
}
|
||||||
|
tiles.put(x, y, z, tile);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setBlockLight(int x, int y, int z, int value) {
|
||||||
|
updateSectionIndexRange(y >> 4);
|
||||||
|
if (light == null) {
|
||||||
|
light = new char[sectionCount][];
|
||||||
|
}
|
||||||
|
final int layer = (y >> 4) - minSectionPosition;
|
||||||
|
if (light[layer] == null) {
|
||||||
|
char[] c = new char[4096];
|
||||||
|
Arrays.fill(c, (char) 16);
|
||||||
|
light[layer] = c;
|
||||||
|
}
|
||||||
|
final int index = (y & 15) << 8 | (z & 15) << 4 | (x & 15);
|
||||||
|
light[layer][index] = (char) value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setSkyLight(int x, int y, int z, int value) {
|
||||||
|
updateSectionIndexRange(y >> 4);
|
||||||
|
if (skyLight == null) {
|
||||||
|
skyLight = new char[sectionCount][];
|
||||||
|
}
|
||||||
|
final int layer = (y >> 4) - minSectionPosition;
|
||||||
|
if (skyLight[layer] == null) {
|
||||||
|
char[] c = new char[4096];
|
||||||
|
Arrays.fill(c, (char) 16);
|
||||||
|
skyLight[layer] = c;
|
||||||
|
}
|
||||||
|
final int index = (y & 15) << 8 | (z & 15) << 4 | (x & 15);
|
||||||
|
skyLight[layer][index] = (char) value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setHeightMap(HeightMapType type, int[] heightMap) {
|
||||||
|
if (heightMaps == null) {
|
||||||
|
heightMaps = new EnumMap<>(HeightMapType.class);
|
||||||
|
}
|
||||||
|
heightMaps.put(type, heightMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setLightLayer(int layer, char[] toSet) {
|
||||||
|
updateSectionIndexRange(layer);
|
||||||
|
if (light == null) {
|
||||||
|
light = new char[sectionCount][];
|
||||||
|
}
|
||||||
|
layer -= minSectionPosition;
|
||||||
|
light[layer] = toSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setSkyLightLayer(int layer, char[] toSet) {
|
||||||
|
updateSectionIndexRange(layer);
|
||||||
|
if (skyLight == null) {
|
||||||
|
skyLight = new char[sectionCount][];
|
||||||
|
}
|
||||||
|
layer -= minSectionPosition;
|
||||||
|
skyLight[layer] = toSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setFullBright(int layer) {
|
||||||
|
updateSectionIndexRange(layer);
|
||||||
|
layer -= minSectionPosition;
|
||||||
|
if (light == null) {
|
||||||
|
light = new char[sectionCount][];
|
||||||
|
}
|
||||||
|
if (light[layer] == null) {
|
||||||
|
light[layer] = new char[4096];
|
||||||
|
}
|
||||||
|
if (skyLight == null) {
|
||||||
|
skyLight = new char[sectionCount][];
|
||||||
|
}
|
||||||
|
if (skyLight[layer] == null) {
|
||||||
|
skyLight[layer] = new char[4096];
|
||||||
|
}
|
||||||
|
Arrays.fill(light[layer], (char) 15);
|
||||||
|
Arrays.fill(skyLight[layer], (char) 15);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setEntity(CompoundTag tag) {
|
||||||
|
if (entities == null) {
|
||||||
|
entities = new HashSet<>();
|
||||||
|
}
|
||||||
|
entities.add(tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeEntity(UUID uuid) {
|
||||||
|
if (entityRemoves == null) {
|
||||||
|
entityRemoves = new HashSet<>();
|
||||||
|
}
|
||||||
|
entityRemoves.add(uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setFastMode(boolean fastMode) {
|
||||||
|
this.fastMode = fastMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isFastMode() {
|
||||||
|
return fastMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setBitMask(int bitMask) {
|
||||||
|
this.bitMask = bitMask;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getBitMask() {
|
||||||
|
return bitMask;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<UUID> getEntityRemoves() {
|
||||||
|
return entityRemoves == null ? Collections.emptySet() : entityRemoves;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BiomeType[][] getBiomes() {
|
||||||
|
return biomes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasBiomes() {
|
||||||
|
return IChunkSet.super.hasBiomes();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public char[][] getLight() {
|
||||||
|
return light;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public char[][] getSkyLight() {
|
||||||
|
return skyLight;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasLight() {
|
||||||
|
return IChunkSet.super.hasLight();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IChunkSet reset() {
|
||||||
|
blocks = new char[sectionCount][];
|
||||||
|
biomes = new BiomeType[sectionCount][];
|
||||||
|
light = new char[sectionCount][];
|
||||||
|
skyLight = new char[sectionCount][];
|
||||||
|
tiles.clear();
|
||||||
|
entities.clear();
|
||||||
|
entityRemoves.clear();
|
||||||
|
heightMaps.clear();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasBiomes(int layer) {
|
||||||
|
layer -= minSectionPosition;
|
||||||
|
return layer >= 0 && layer < biomes.length && biomes[layer] != null && biomes[layer].length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IChunkSet createCopy() {
|
||||||
|
char[][] blocksCopy = new char[sectionCount][];
|
||||||
|
for (int i = 0; i < sectionCount; i++) {
|
||||||
|
blocksCopy[i] = new char[FaweCache.INSTANCE.BLOCKS_PER_LAYER];
|
||||||
|
if (blocks[i] != null) {
|
||||||
|
System.arraycopy(blocks[i], 0, blocksCopy[i], 0, FaweCache.INSTANCE.BLOCKS_PER_LAYER);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BiomeType[][] biomesCopy;
|
||||||
|
if (biomes == null) {
|
||||||
|
biomesCopy = null;
|
||||||
|
} else {
|
||||||
|
biomesCopy = new BiomeType[sectionCount][];
|
||||||
|
for (int i = 0; i < sectionCount; i++) {
|
||||||
|
if (biomes[i] != null) {
|
||||||
|
biomesCopy[i] = new BiomeType[biomes[i].length];
|
||||||
|
System.arraycopy(biomes[i], 0, biomesCopy[i], 0, biomes[i].length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
char[][] lightCopy = CharSetBlocks.createLightCopy(light, sectionCount);
|
||||||
|
char[][] skyLightCopy = CharSetBlocks.createLightCopy(skyLight, sectionCount);
|
||||||
|
return new ThreadUnsafeCharBlocks(
|
||||||
|
blocksCopy,
|
||||||
|
minSectionPosition,
|
||||||
|
maxSectionPosition,
|
||||||
|
biomesCopy,
|
||||||
|
sectionCount,
|
||||||
|
lightCopy,
|
||||||
|
skyLightCopy,
|
||||||
|
tiles != null ? new BlockVector3ChunkMap<>(tiles) : null,
|
||||||
|
entities != null ? new HashSet<>(entities) : null,
|
||||||
|
entityRemoves != null ? new HashSet<>(entityRemoves) : null,
|
||||||
|
heightMaps != null ? new HashMap<>(heightMaps) : null,
|
||||||
|
defaultOrdinal,
|
||||||
|
fastMode,
|
||||||
|
bitMask
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean trim(boolean aggressive) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks and updates the various section arrays against the new layer index
|
||||||
|
private void updateSectionIndexRange(int layer) {
|
||||||
|
if (layer >= minSectionPosition && layer <= maxSectionPosition) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (layer < minSectionPosition) {
|
||||||
|
int diff = minSectionPosition - layer;
|
||||||
|
sectionCount += diff;
|
||||||
|
minSectionPosition = layer;
|
||||||
|
resizeSectionsArrays(layer, diff, false); // prepend new layer(s)
|
||||||
|
} else {
|
||||||
|
int diff = layer - maxSectionPosition;
|
||||||
|
sectionCount += diff;
|
||||||
|
maxSectionPosition = layer;
|
||||||
|
resizeSectionsArrays(layer, diff, true); // append new layer(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void resizeSectionsArrays(int layer, int diff, boolean appendNew) {
|
||||||
|
char[][] tmpBlocks = new char[sectionCount][];
|
||||||
|
int destPos = appendNew ? 0 : diff;
|
||||||
|
System.arraycopy(blocks, 0, tmpBlocks, destPos, blocks.length);
|
||||||
|
blocks = tmpBlocks;
|
||||||
|
if (biomes != null) {
|
||||||
|
BiomeType[][] tmpBiomes = new BiomeType[sectionCount][64];
|
||||||
|
System.arraycopy(biomes, 0, tmpBiomes, destPos, biomes.length);
|
||||||
|
biomes = tmpBiomes;
|
||||||
|
}
|
||||||
|
if (light != null) {
|
||||||
|
char[][] tmplight = new char[sectionCount][];
|
||||||
|
System.arraycopy(light, 0, tmplight, destPos, light.length);
|
||||||
|
light = tmplight;
|
||||||
|
}
|
||||||
|
if (skyLight != null) {
|
||||||
|
char[][] tmplight = new char[sectionCount][];
|
||||||
|
System.arraycopy(skyLight, 0, tmplight, destPos, skyLight.length);
|
||||||
|
skyLight = tmplight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1044,7 +1044,7 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
|
|||||||
if (chunkSet != null && !chunkSet.isEmpty()) {
|
if (chunkSet != null && !chunkSet.isEmpty()) {
|
||||||
chunkSet.setBitMask(bitMask);
|
chunkSet.setBitMask(bitMask);
|
||||||
try {
|
try {
|
||||||
return this.call(chunkSet, () -> {
|
return this.call(chunkSet.createCopy(), () -> {
|
||||||
this.delegate = NULL;
|
this.delegate = NULL;
|
||||||
chunkSet = null;
|
chunkSet = null;
|
||||||
calledLock.unlock(stamp);
|
calledLock.unlock(stamp);
|
||||||
|
@ -52,6 +52,7 @@ import java.io.PrintWriter;
|
|||||||
import java.lang.reflect.Array;
|
import java.lang.reflect.Array;
|
||||||
import java.net.HttpURLConnection;
|
import java.net.HttpURLConnection;
|
||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URI;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.net.URLConnection;
|
import java.net.URLConnection;
|
||||||
@ -68,7 +69,6 @@ import java.nio.file.StandardCopyOption;
|
|||||||
import java.nio.file.attribute.BasicFileAttributes;
|
import java.nio.file.attribute.BasicFileAttributes;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
@ -81,10 +81,8 @@ import java.util.concurrent.atomic.AtomicLong;
|
|||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
import java.util.zip.DataFormatException;
|
|
||||||
import java.util.zip.Deflater;
|
import java.util.zip.Deflater;
|
||||||
import java.util.zip.GZIPInputStream;
|
import java.util.zip.GZIPInputStream;
|
||||||
import java.util.zip.Inflater;
|
|
||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
import java.util.zip.ZipInputStream;
|
import java.util.zip.ZipInputStream;
|
||||||
|
|
||||||
@ -533,6 +531,21 @@ public class MainUtil {
|
|||||||
return readImage(new FileInputStream(file));
|
return readImage(new FileInputStream(file));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void checkImageHost(URI uri) throws IOException {
|
||||||
|
if (Settings.settings().WEB.ALLOWED_IMAGE_HOSTS.contains("*")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String host = uri.getHost();
|
||||||
|
if (Settings.settings().WEB.ALLOWED_IMAGE_HOSTS.stream().anyMatch(host::equalsIgnoreCase)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw new IOException(String.format(
|
||||||
|
"Host `%s` not allowed! Whitelisted image hosts are: %s",
|
||||||
|
host,
|
||||||
|
StringMan.join(Settings.settings().WEB.ALLOWED_IMAGE_HOSTS, ", ")
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
public static BufferedImage toRGB(BufferedImage src) {
|
public static BufferedImage toRGB(BufferedImage src) {
|
||||||
if (src == null) {
|
if (src == null) {
|
||||||
return src;
|
return src;
|
||||||
|
@ -157,13 +157,13 @@ public abstract class TaskManager {
|
|||||||
*/
|
*/
|
||||||
public void runUnsafe(Runnable run) {
|
public void runUnsafe(Runnable run) {
|
||||||
QueueHandler queue = Fawe.instance().getQueueHandler();
|
QueueHandler queue = Fawe.instance().getQueueHandler();
|
||||||
queue.startSet(true);
|
queue.startUnsafe(Fawe.isMainThread());
|
||||||
try {
|
try {
|
||||||
run.run();
|
run.run();
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
queue.endSet(true);
|
queue.endUnsafe(Fawe.isMainThread());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -130,7 +130,9 @@ public class WEManager {
|
|||||||
backupRegions.add(region);
|
backupRegions.add(region);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
player.print(Caption.of("fawe.error.region-mask-invalid", mask.getClass().getSimpleName()));
|
if (Settings.settings().ENABLED_COMPONENTS.DEBUG) {
|
||||||
|
player.printDebug(Caption.of("fawe.error.region-mask-invalid", mask.getClass().getSimpleName()));
|
||||||
|
}
|
||||||
removed = true;
|
removed = true;
|
||||||
iterator.remove();
|
iterator.remove();
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,7 @@ public class SimpleRandomCollection<E> extends RandomCollection<E> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public E next(int x, int y, int z) {
|
public E next(int x, int y, int z) {
|
||||||
return map.ceilingEntry(getRandom().nextDouble(x, y, z)).getValue();
|
return map.ceilingEntry(getRandom().nextDouble(x, y, z) * this.total).getValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -203,6 +203,7 @@ public class ImageUtil {
|
|||||||
arg = "https://i.imgur.com/" + arg.split("imgur.com/")[1] + ".png";
|
arg = "https://i.imgur.com/" + arg.split("imgur.com/")[1] + ".png";
|
||||||
}
|
}
|
||||||
URL url = new URL(arg);
|
URL url = new URL(arg);
|
||||||
|
MainUtil.checkImageHost(url.toURI());
|
||||||
BufferedImage img = MainUtil.readImage(url);
|
BufferedImage img = MainUtil.readImage(url);
|
||||||
if (img == null) {
|
if (img == null) {
|
||||||
throw new IOException("Failed to read " + url + ", please try again later");
|
throw new IOException("Failed to read " + url + ", please try again later");
|
||||||
@ -218,7 +219,7 @@ public class ImageUtil {
|
|||||||
return MainUtil.readImage(file);
|
return MainUtil.readImage(file);
|
||||||
}
|
}
|
||||||
throw new InputParseException(Caption.of("fawe.error.invalid-image", TextComponent.of(arg)));
|
throw new InputParseException(Caption.of("fawe.error.invalid-image", TextComponent.of(arg)));
|
||||||
} catch (IOException e) {
|
} catch (IOException | URISyntaxException e) {
|
||||||
throw new InputParseException(TextComponent.of(e.getMessage()));
|
throw new InputParseException(TextComponent.of(e.getMessage()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -229,7 +230,9 @@ public class ImageUtil {
|
|||||||
if (arg.contains("imgur.com") && !arg.contains("i.imgur.com")) {
|
if (arg.contains("imgur.com") && !arg.contains("i.imgur.com")) {
|
||||||
arg = "https://i.imgur.com/" + arg.split("imgur.com/")[1] + ".png";
|
arg = "https://i.imgur.com/" + arg.split("imgur.com/")[1] + ".png";
|
||||||
}
|
}
|
||||||
return new URL(arg).toURI();
|
URI uri = new URI(arg);
|
||||||
|
MainUtil.checkImageHost(uri);
|
||||||
|
return uri;
|
||||||
}
|
}
|
||||||
if (arg.startsWith("file:/")) {
|
if (arg.startsWith("file:/")) {
|
||||||
arg = arg.replaceFirst("file:/+", "");
|
arg = arg.replaceFirst("file:/+", "");
|
||||||
|
@ -1,9 +1,13 @@
|
|||||||
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;
|
||||||
@ -11,6 +15,13 @@ 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;
|
||||||
@ -59,7 +70,7 @@ public class AsyncNotifyQueue implements Closeable {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
self[0] = Fawe.instance().getQueueHandler().async(wrapped);
|
self[0] = QUEUE_SUBMISSIONS.submit(wrapped);
|
||||||
return self[0];
|
return self[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,21 @@
|
|||||||
|
package com.fastasyncworldedit.core.util.task;
|
||||||
|
|
||||||
|
import java.util.concurrent.ForkJoinPool;
|
||||||
|
import java.util.concurrent.ForkJoinWorkerThread;
|
||||||
|
|
||||||
|
public class FaweForkJoinWorkerThreadFactory implements ForkJoinPool.ForkJoinWorkerThreadFactory {
|
||||||
|
|
||||||
|
private final String nameFormat;
|
||||||
|
|
||||||
|
public FaweForkJoinWorkerThreadFactory(String nameFormat) {
|
||||||
|
this.nameFormat = nameFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ForkJoinWorkerThread newThread(ForkJoinPool pool) {
|
||||||
|
final ForkJoinWorkerThread worker = ForkJoinPool.defaultForkJoinWorkerThreadFactory.newThread(pool);
|
||||||
|
worker.setName(String.format(nameFormat, worker.getPoolIndex()));
|
||||||
|
return worker;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,172 @@
|
|||||||
|
package com.fastasyncworldedit.core.util.task;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Queue;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
import java.util.concurrent.FutureTask;
|
||||||
|
import java.util.concurrent.RejectedExecutionException;
|
||||||
|
import java.util.concurrent.ThreadPoolExecutor;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executor service that queues tasks based on keys, executing tasks on a configurable {@link ThreadPoolExecutor}
|
||||||
|
*
|
||||||
|
* @param <K> Key type
|
||||||
|
* @since 2.6.2
|
||||||
|
*/
|
||||||
|
public class KeyQueuedExecutorService<K> {
|
||||||
|
|
||||||
|
private final ExecutorService parent;
|
||||||
|
private final Map<K, KeyRunner> keyQueue = new HashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new {@link KeyQueuedExecutorService} instance
|
||||||
|
*
|
||||||
|
* @param parent Parent {@link ExecutorService} to use for actual task completion
|
||||||
|
*/
|
||||||
|
public KeyQueuedExecutorService(ExecutorService parent) {
|
||||||
|
this.parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delegates to {@link ThreadPoolExecutor#shutdown()}
|
||||||
|
*/
|
||||||
|
public void shutdown() {
|
||||||
|
parent.shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delegates to {@link ThreadPoolExecutor#shutdownNow()}
|
||||||
|
*/
|
||||||
|
@Nonnull
|
||||||
|
public List<Runnable> shutdownNow() {
|
||||||
|
return parent.shutdownNow();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delegates to {@link ThreadPoolExecutor#isShutdown()}
|
||||||
|
*/
|
||||||
|
public boolean isShutdown() {
|
||||||
|
return parent.isShutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delegates to {@link ThreadPoolExecutor#isTerminated()}
|
||||||
|
*/
|
||||||
|
public boolean isTerminated() {
|
||||||
|
return parent.isTerminated();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delegates to {@link ThreadPoolExecutor#awaitTermination(long, TimeUnit)}
|
||||||
|
*/
|
||||||
|
public boolean awaitTermination(long timeout, @Nonnull TimeUnit unit) throws InterruptedException {
|
||||||
|
return parent.awaitTermination(timeout, unit);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected <T> FutureTask<T> newTaskFor(Runnable runnable, T value) {
|
||||||
|
return new FutureTask<>(runnable, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected <T> FutureTask<T> newTaskFor(Callable<T> callable) {
|
||||||
|
return new FutureTask<>(callable);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
public <T> Future<T> submit(@Nonnull K key, @Nonnull Callable<T> task) {
|
||||||
|
FutureTask<T> ftask = newTaskFor(task);
|
||||||
|
execute(key, ftask);
|
||||||
|
return ftask;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
public <T> Future<T> submit(@Nonnull K key, @Nonnull Runnable task, T result) {
|
||||||
|
FutureTask<T> ftask = newTaskFor(task, result);
|
||||||
|
execute(key, ftask);
|
||||||
|
return ftask;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
public Future<?> submit(@Nonnull K key, @Nonnull Runnable task) {
|
||||||
|
FutureTask<Void> ftask = newTaskFor(task, null);
|
||||||
|
execute(key, ftask);
|
||||||
|
return ftask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void execute(@Nonnull K key, @Nonnull FutureTask<?> command) {
|
||||||
|
synchronized (keyQueue) {
|
||||||
|
boolean triggerRun = false;
|
||||||
|
KeyRunner runner = keyQueue.get(key);
|
||||||
|
if (runner == null) {
|
||||||
|
runner = new KeyRunner(key);
|
||||||
|
keyQueue.put(key, runner);
|
||||||
|
triggerRun = true;
|
||||||
|
}
|
||||||
|
runner.add(command);
|
||||||
|
if (triggerRun) {
|
||||||
|
runner.triggerRun();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final class KeyRunner {
|
||||||
|
|
||||||
|
private final Queue<FutureTask<?>> tasks = new ConcurrentLinkedQueue<>();
|
||||||
|
private final K key;
|
||||||
|
|
||||||
|
private KeyRunner(K key) {
|
||||||
|
this.key = key;
|
||||||
|
}
|
||||||
|
|
||||||
|
void add(FutureTask<?> task) {
|
||||||
|
if (!tasks.add(task)) {
|
||||||
|
throw new RejectedExecutionException(rejection());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void triggerRun() {
|
||||||
|
Runnable task = tasks.poll();
|
||||||
|
if (task == null) {
|
||||||
|
throw new RejectedExecutionException(rejection());
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
run(task);
|
||||||
|
} catch (RejectedExecutionException e) {
|
||||||
|
synchronized (keyQueue) {
|
||||||
|
keyQueue.remove(key);
|
||||||
|
}
|
||||||
|
throw new RejectedExecutionException(rejection(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void run(Runnable task) {
|
||||||
|
parent.execute(() -> {
|
||||||
|
task.run();
|
||||||
|
Runnable next = tasks.poll();
|
||||||
|
if (next == null) {
|
||||||
|
synchronized (keyQueue) {
|
||||||
|
next = tasks.poll();
|
||||||
|
if (next == null) {
|
||||||
|
keyQueue.remove(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (next != null) {
|
||||||
|
run(next);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private String rejection() {
|
||||||
|
return "Task for the key '" + key + "' rejected";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -531,16 +531,14 @@ public final class EditSessionBuilder {
|
|||||||
}
|
}
|
||||||
if (allowedRegions == null && Settings.settings().REGION_RESTRICTIONS) {
|
if (allowedRegions == null && Settings.settings().REGION_RESTRICTIONS) {
|
||||||
if (actor != null && !actor.hasPermission("fawe.bypass.regions")) {
|
if (actor != null && !actor.hasPermission("fawe.bypass.regions")) {
|
||||||
if (actor instanceof Player) {
|
if (actor instanceof Player player) {
|
||||||
Player player = (Player) actor;
|
|
||||||
allowedRegions = player.getAllowedRegions();
|
allowedRegions = player.getAllowedRegions();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (disallowedRegions == null && Settings.settings().REGION_RESTRICTIONS && Settings.settings().REGION_RESTRICTIONS_OPTIONS.ALLOW_BLACKLISTS) {
|
if (disallowedRegions == null && Settings.settings().REGION_RESTRICTIONS && Settings.settings().REGION_RESTRICTIONS_OPTIONS.ALLOW_BLACKLISTS) {
|
||||||
if (actor != null && !actor.hasPermission("fawe.bypass.regions")) {
|
if (actor != null && !actor.hasPermission("fawe.bypass.regions")) {
|
||||||
if (actor instanceof Player) {
|
if (actor instanceof Player player) {
|
||||||
Player player = (Player) actor;
|
|
||||||
disallowedRegions = player.getDisallowedRegions();
|
disallowedRegions = player.getDisallowedRegions();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -561,6 +559,9 @@ public final class EditSessionBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (placeChunks && regionExtent != null) {
|
||||||
|
queue.addProcessor(regionExtent);
|
||||||
|
}
|
||||||
// There's no need to do the below (and it'll also just be a pain to implement) if we're not placing chunks
|
// There's no need to do the below (and it'll also just be a pain to implement) if we're not placing chunks
|
||||||
if (placeChunks) {
|
if (placeChunks) {
|
||||||
if (((relightMode != null && relightMode != RelightMode.NONE) || (relightMode == null && Settings.settings().LIGHTING.MODE > 0))) {
|
if (((relightMode != null && relightMode != RelightMode.NONE) || (relightMode == null && Settings.settings().LIGHTING.MODE > 0))) {
|
||||||
@ -597,15 +598,14 @@ public final class EditSessionBuilder {
|
|||||||
this.extent = regionExtent;
|
this.extent = regionExtent;
|
||||||
}
|
}
|
||||||
if (this.limit != null && this.limit.STRIP_NBT != null && !this.limit.STRIP_NBT.isEmpty()) {
|
if (this.limit != null && this.limit.STRIP_NBT != null && !this.limit.STRIP_NBT.isEmpty()) {
|
||||||
|
this.extent = new StripNBTExtent(this.extent, this.limit.STRIP_NBT);
|
||||||
if (placeChunks) {
|
if (placeChunks) {
|
||||||
queue.addProcessor(new StripNBTExtent(this.extent, this.limit.STRIP_NBT));
|
queue.addProcessor((IBatchProcessor) this.extent);
|
||||||
} else {
|
|
||||||
this.extent = new StripNBTExtent(this.extent, this.limit.STRIP_NBT);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this.limit != null && !this.limit.isUnlimited()) {
|
if (this.limit != null && !this.limit.isUnlimited()) {
|
||||||
Set<String> limitBlocks = new HashSet<>();
|
Set<String> limitBlocks = new HashSet<>();
|
||||||
if ((getActor() == null || getActor().hasPermission("worldedit.anyblock")) && this.limit.UNIVERSAL_DISALLOWED_BLOCKS) {
|
if (getActor() != null && !getActor().hasPermission("worldedit.anyblock") && this.limit.UNIVERSAL_DISALLOWED_BLOCKS) {
|
||||||
limitBlocks.addAll(WorldEdit.getInstance().getConfiguration().disallowedBlocks);
|
limitBlocks.addAll(WorldEdit.getInstance().getConfiguration().disallowedBlocks);
|
||||||
}
|
}
|
||||||
if (this.limit.DISALLOWED_BLOCKS != null && !this.limit.DISALLOWED_BLOCKS.isEmpty()) {
|
if (this.limit.DISALLOWED_BLOCKS != null && !this.limit.DISALLOWED_BLOCKS.isEmpty()) {
|
||||||
@ -613,10 +613,9 @@ public final class EditSessionBuilder {
|
|||||||
}
|
}
|
||||||
Set<PropertyRemap<?>> remaps = this.limit.REMAP_PROPERTIES;
|
Set<PropertyRemap<?>> remaps = this.limit.REMAP_PROPERTIES;
|
||||||
if (!limitBlocks.isEmpty() || (remaps != null && !remaps.isEmpty())) {
|
if (!limitBlocks.isEmpty() || (remaps != null && !remaps.isEmpty())) {
|
||||||
|
this.extent = new DisallowedBlocksExtent(this.extent, limitBlocks, remaps);
|
||||||
if (placeChunks) {
|
if (placeChunks) {
|
||||||
queue.addProcessor(new DisallowedBlocksExtent(this.extent, limitBlocks, remaps));
|
queue.addProcessor((IBatchProcessor) this.extent);
|
||||||
} else {
|
|
||||||
this.extent = new DisallowedBlocksExtent(this.extent, limitBlocks, remaps);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -82,7 +82,10 @@ public class BiomeCommands {
|
|||||||
aliases = {"biomels", "/biomelist", "/listbiomes"},
|
aliases = {"biomels", "/biomelist", "/listbiomes"},
|
||||||
desc = "Gets all biomes available."
|
desc = "Gets all biomes available."
|
||||||
)
|
)
|
||||||
@CommandPermissions("worldedit.biome.list")
|
@CommandPermissions(
|
||||||
|
value = "worldedit.biome.list",
|
||||||
|
queued = false
|
||||||
|
)
|
||||||
public void biomeList(
|
public void biomeList(
|
||||||
Actor actor,
|
Actor actor,
|
||||||
@ArgFlag(name = 'p', desc = "Page number.", def = "1")
|
@ArgFlag(name = 'p', desc = "Page number.", def = "1")
|
||||||
|
@ -134,6 +134,7 @@ import java.io.FileOutputStream;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.nio.file.FileSystems;
|
import java.nio.file.FileSystems;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -310,11 +311,11 @@ public class BrushCommands {
|
|||||||
},
|
},
|
||||||
desc = "Join multiple objects together in a curve",
|
desc = "Join multiple objects together in a curve",
|
||||||
descFooter =
|
descFooter =
|
||||||
"Click to select some objects,click the same block twice to connect the objects.\n"
|
"""
|
||||||
+ "Insufficient brush radius, or clicking the the wrong spot will result in undesired shapes. The shapes must be simple lines or loops.\n"
|
Click to select some objects,click the same block twice to connect the objects.
|
||||||
+ "Pic1: http://i.imgur.com/CeRYAoV.jpg -> http://i.imgur.com/jtM0jA4.png\n"
|
Insufficient brush radius, or clicking the the wrong spot will result in undesired shapes. The shapes must be simple lines or loops.
|
||||||
+ "Pic2: http://i.imgur.com/bUeyc72.png -> http://i.imgur.com/tg6MkcF.png"
|
Pic1: http://i.imgur.com/CeRYAoV.jpg -> http://i.imgur.com/jtM0jA4.png
|
||||||
+ "Tutorial: https://www.planetminecraft.com/blog/fawe-tutorial/"
|
Pic2: http://i.imgur.com/bUeyc72.png -> http://i.imgur.com/tg6MkcF.pngTutorial: https://www.planetminecraft.com/blog/fawe-tutorial/"""
|
||||||
)
|
)
|
||||||
@CommandPermissions("worldedit.brush.spline")
|
@CommandPermissions("worldedit.brush.spline")
|
||||||
public void splineBrush(
|
public void splineBrush(
|
||||||
@ -337,9 +338,10 @@ public class BrushCommands {
|
|||||||
"vaesweep"
|
"vaesweep"
|
||||||
},
|
},
|
||||||
desc = "Sweep your clipboard content along a curve",
|
desc = "Sweep your clipboard content along a curve",
|
||||||
descFooter = "Sweeps your clipboard content along a curve.\n"
|
descFooter = """
|
||||||
+ "Define a curve by selecting the individual points with a brush\n"
|
Sweeps your clipboard content along a curve.
|
||||||
+ "Set [copies] to a value > 0 if you want to have your selection pasted a limited amount of times equally spaced on the curve"
|
Define a curve by selecting the individual points with a brush
|
||||||
|
Set [copies] to a value > 0 if you want to have your selection pasted a limited amount of times equally spaced on the curve"""
|
||||||
)
|
)
|
||||||
@CommandPermissions("worldedit.brush.sweep")
|
@CommandPermissions("worldedit.brush.sweep")
|
||||||
public void sweepBrush(
|
public void sweepBrush(
|
||||||
@ -520,11 +522,9 @@ public class BrushCommands {
|
|||||||
@Switch(name = 'a', desc = "Use image Alpha") boolean alpha,
|
@Switch(name = 'a', desc = "Use image Alpha") boolean alpha,
|
||||||
@Switch(name = 'f', desc = "Blend the image with existing terrain") boolean fadeOut
|
@Switch(name = 'f', desc = "Blend the image with existing terrain") boolean fadeOut
|
||||||
)
|
)
|
||||||
throws WorldEditException, IOException {
|
throws WorldEditException, IOException, URISyntaxException {
|
||||||
URL url = new URL(imageURL);
|
URL url = new URL(imageURL);
|
||||||
if (!url.getHost().equalsIgnoreCase("i.imgur.com")) {
|
MainUtil.checkImageHost(url.toURI());
|
||||||
throw new IOException("Only i.imgur.com links are allowed!");
|
|
||||||
}
|
|
||||||
BufferedImage image = MainUtil.readImage(url);
|
BufferedImage image = MainUtil.readImage(url);
|
||||||
worldEdit.checkMaxBrushRadius(radius);
|
worldEdit.checkMaxBrushRadius(radius);
|
||||||
if (yscale != 1) {
|
if (yscale != 1) {
|
||||||
@ -636,10 +636,10 @@ public class BrushCommands {
|
|||||||
@Command(
|
@Command(
|
||||||
name = "layer",
|
name = "layer",
|
||||||
desc = "Replaces terrain with a layer.",
|
desc = "Replaces terrain with a layer.",
|
||||||
descFooter = "Replaces terrain with a layer.\n"
|
descFooter = """
|
||||||
+ "Example: /br layer 5 oak_planks,orange_stained_glass,magenta_stained_glass,black_wool - Places several " +
|
Replaces terrain with a layer.
|
||||||
"layers on a surface\n"
|
Example: /br layer 5 oak_planks,orange_stained_glass,magenta_stained_glass,black_wool - Places several layers on a surface
|
||||||
+ "Pic: https://i.imgur.com/XV0vYoX.png"
|
Pic: https://i.imgur.com/XV0vYoX.png"""
|
||||||
)
|
)
|
||||||
@CommandPermissions("worldedit.brush.layer")
|
@CommandPermissions("worldedit.brush.layer")
|
||||||
public void surfaceLayer(
|
public void surfaceLayer(
|
||||||
@ -658,11 +658,11 @@ public class BrushCommands {
|
|||||||
@Command(
|
@Command(
|
||||||
name = "splatter",
|
name = "splatter",
|
||||||
desc = "Splatter a pattern on a surface",
|
desc = "Splatter a pattern on a surface",
|
||||||
descFooter = "Sets a bunch of blocks randomly on a surface.\n"
|
descFooter = """
|
||||||
+ "Pic: https://i.imgur.com/hMD29oO.png\n"
|
Sets a bunch of blocks randomly on a surface.
|
||||||
+ "Example: /br splatter stone,dirt 30 15\n"
|
Pic: https://i.imgur.com/hMD29oO.png
|
||||||
+ "Note: The seeds define how many splotches there are, recursion defines how large, "
|
Example: /br splatter stone,dirt 30 15
|
||||||
+ "solid defines whether the pattern is applied per seed, else per block."
|
Note: The seeds define how many splotches there are, recursion defines how large, solid defines whether the pattern is applied per seed, else per block."""
|
||||||
)
|
)
|
||||||
@CommandPermissions("worldedit.brush.splatter")
|
@CommandPermissions("worldedit.brush.splatter")
|
||||||
public void splatterBrush(
|
public void splatterBrush(
|
||||||
@ -691,9 +691,10 @@ public class BrushCommands {
|
|||||||
"scommand"
|
"scommand"
|
||||||
},
|
},
|
||||||
desc = "Run commands at random points on a surface",
|
desc = "Run commands at random points on a surface",
|
||||||
descFooter = "Run commands at random points on a surface\n"
|
descFooter = """
|
||||||
+ " - Your selection will be expanded to the specified size around each point\n"
|
Run commands at random points on a surface
|
||||||
+ " - Placeholders: {x}, {y}, {z}, {world}, {size}"
|
- Your selection will be expanded to the specified size around each point
|
||||||
|
- Placeholders: {x}, {y}, {z}, {world}, {size}"""
|
||||||
)
|
)
|
||||||
@CommandPermissions("worldedit.brush.scattercommand")
|
@CommandPermissions("worldedit.brush.scattercommand")
|
||||||
public void scatterCommandBrush(
|
public void scatterCommandBrush(
|
||||||
@ -723,9 +724,10 @@ public class BrushCommands {
|
|||||||
name = "height",
|
name = "height",
|
||||||
aliases = {"heightmap"},
|
aliases = {"heightmap"},
|
||||||
desc = "Raise or lower terrain using a heightmap",
|
desc = "Raise or lower terrain using a heightmap",
|
||||||
descFooter = "This brush raises and lowers land.\n"
|
descFooter = """
|
||||||
+ "Note: Use a negative yscale to reduce height\n"
|
This brush raises and lowers land.
|
||||||
+ "Snow Pic: https://i.imgur.com/Hrzn0I4.png"
|
Note: Use a negative yscale to reduce height
|
||||||
|
Snow Pic: https://i.imgur.com/Hrzn0I4.png"""
|
||||||
)
|
)
|
||||||
@CommandPermissions("worldedit.brush.height")
|
@CommandPermissions("worldedit.brush.height")
|
||||||
public void heightBrush(
|
public void heightBrush(
|
||||||
@ -890,9 +892,11 @@ public class BrushCommands {
|
|||||||
"copypasta"
|
"copypasta"
|
||||||
},
|
},
|
||||||
desc = "Copy Paste brush",
|
desc = "Copy Paste brush",
|
||||||
descFooter = "Left click the base of an object to copy.\n" + "Right click to paste\n"
|
descFooter = """
|
||||||
+ "Note: Works well with the clipboard scroll action\n"
|
Left click the base of an object to copy.
|
||||||
+ "Video: https://www.youtube.com/watch?v=RPZIaTbqoZw"
|
Right click to paste
|
||||||
|
Note: Works well with the clipboard scroll action
|
||||||
|
Video: https://www.youtube.com/watch?v=RPZIaTbqoZw"""
|
||||||
)
|
)
|
||||||
@CommandPermissions("worldedit.brush.copy")
|
@CommandPermissions("worldedit.brush.copy")
|
||||||
public void copy(
|
public void copy(
|
||||||
@ -914,9 +918,10 @@ public class BrushCommands {
|
|||||||
name = "command",
|
name = "command",
|
||||||
aliases = {"cmd"},
|
aliases = {"cmd"},
|
||||||
desc = "Command brush",
|
desc = "Command brush",
|
||||||
descFooter = "Run the commands at the clicked position.\n"
|
descFooter = """
|
||||||
+ " - Your selection will be expanded to the specified size around each point\n"
|
Run the commands at the clicked position.
|
||||||
+ " - Placeholders: {x}, {y}, {z}, {world}, {size}"
|
- Your selection will be expanded to the specified size around each point
|
||||||
|
- Placeholders: {x}, {y}, {z}, {world}, {size}"""
|
||||||
)
|
)
|
||||||
@CommandPermissions("worldedit.brush.command")
|
@CommandPermissions("worldedit.brush.command")
|
||||||
public void command(
|
public void command(
|
||||||
@ -1036,7 +1041,7 @@ public class BrushCommands {
|
|||||||
)
|
)
|
||||||
throws WorldEditException {
|
throws WorldEditException {
|
||||||
WorldEdit.getInstance().checkMaxBrushRadius(radius);
|
WorldEdit.getInstance().checkMaxBrushRadius(radius);
|
||||||
BrushTool tool = session.getBrushTool(player.getItemInHand(HandSide.MAIN_HAND).getType());
|
BrushTool tool = session.getBrushTool(player);
|
||||||
tool.setSize(radius);
|
tool.setSize(radius);
|
||||||
tool.setFill(null);
|
tool.setFill(null);
|
||||||
tool.setBrush(new OperationFactoryBrush(factory, shape, session), permission);
|
tool.setBrush(new OperationFactoryBrush(factory, shape, session), permission);
|
||||||
@ -1196,17 +1201,12 @@ public class BrushCommands {
|
|||||||
brush = new HollowSphereBrush();
|
brush = new HollowSphereBrush();
|
||||||
} else {
|
} else {
|
||||||
//FAWE start - Suggest different brush material if sand or gravel is used
|
//FAWE start - Suggest different brush material if sand or gravel is used
|
||||||
if (pattern instanceof BlockStateHolder) {
|
if (pattern instanceof BlockStateHolder<?> holder) {
|
||||||
BlockType type = ((BlockStateHolder) pattern).getBlockType();
|
BlockType type = holder.getBlockType();
|
||||||
switch (type.getId()) {
|
if (type == BlockTypes.SAND || type == BlockTypes.GRAVEL) {
|
||||||
case "minecraft:sand":
|
player.print(
|
||||||
case "minecraft:gravel":
|
Caption.of("fawe.worldedit.brush.brush.try.other"));
|
||||||
player.print(
|
falling = true;
|
||||||
Caption.of("fawe.worldedit.brush.brush.try.other"));
|
|
||||||
falling = true;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (falling) {
|
if (falling) {
|
||||||
@ -1258,13 +1258,12 @@ public class BrushCommands {
|
|||||||
|
|
||||||
@Command(
|
@Command(
|
||||||
name = "clipboard",
|
name = "clipboard",
|
||||||
desc = "@Deprecated use instead: `/br copypaste`)",
|
desc = "Paste your clipboard at the brush location. Includes any transforms.",
|
||||||
descFooter = "Choose the clipboard brush.\n"
|
descFooter = "Choose the clipboard brush.\n"
|
||||||
+ "Without the -o flag, the paste will appear centered at the target location. "
|
+ "Without the -o flag, the paste will appear centered at the target location. "
|
||||||
+ "With the flag, then the paste will appear relative to where you had "
|
+ "With the flag, then the paste will appear relative to where you had "
|
||||||
+ "stood relative to the copied area when you copied it."
|
+ "stood relative to the copied area when you copied it."
|
||||||
)
|
)
|
||||||
@Deprecated
|
|
||||||
@CommandPermissions("worldedit.brush.clipboard")
|
@CommandPermissions("worldedit.brush.clipboard")
|
||||||
public void clipboardBrush(
|
public void clipboardBrush(
|
||||||
Player player, LocalSession session,
|
Player player, LocalSession session,
|
||||||
@ -1278,7 +1277,11 @@ public class BrushCommands {
|
|||||||
boolean pasteBiomes,
|
boolean pasteBiomes,
|
||||||
@ArgFlag(name = 'm', desc = "Skip blocks matching this mask in the clipboard")
|
@ArgFlag(name = 'm', desc = "Skip blocks matching this mask in the clipboard")
|
||||||
@ClipboardMask
|
@ClipboardMask
|
||||||
Mask sourceMask, InjectedValueAccess context
|
Mask sourceMask, InjectedValueAccess context,
|
||||||
|
//FAWE start - random rotation
|
||||||
|
@Switch(name = 'r', desc = "Apply random rotation on paste, combines with existing clipboard transforms")
|
||||||
|
boolean randomRotate
|
||||||
|
//FAWE end
|
||||||
) throws WorldEditException {
|
) throws WorldEditException {
|
||||||
ClipboardHolder holder = session.getClipboard();
|
ClipboardHolder holder = session.getClipboard();
|
||||||
|
|
||||||
@ -1294,9 +1297,9 @@ public class BrushCommands {
|
|||||||
|
|
||||||
set(
|
set(
|
||||||
context,
|
context,
|
||||||
new ClipboardBrush(newHolder, ignoreAir, usingOrigin, pasteEntities, pasteBiomes,
|
//FAWE start - random rotation
|
||||||
sourceMask
|
new ClipboardBrush(newHolder, ignoreAir, usingOrigin, pasteEntities, pasteBiomes, sourceMask, randomRotate),
|
||||||
),
|
//FAWE end
|
||||||
"worldedit.brush.clipboard"
|
"worldedit.brush.clipboard"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -1361,7 +1364,7 @@ public class BrushCommands {
|
|||||||
iterations = Math.min(limit.MAX_ITERATIONS, iterations);
|
iterations = Math.min(limit.MAX_ITERATIONS, iterations);
|
||||||
//FAWE end
|
//FAWE end
|
||||||
|
|
||||||
set(context, new SnowSmoothBrush(iterations, mask), "worldedit.brush.snowsmooth").setSize(radius);
|
set(context, new SnowSmoothBrush(iterations, snowBlockCount, mask), "worldedit.brush.snowsmooth").setSize(radius);
|
||||||
player.print(Caption.of(
|
player.print(Caption.of(
|
||||||
"worldedit.brush.smooth.equip",
|
"worldedit.brush.smooth.equip",
|
||||||
radius,
|
radius,
|
||||||
|
@ -79,7 +79,10 @@ public class ChunkCommands {
|
|||||||
aliases = {"/chunkinfo"},
|
aliases = {"/chunkinfo"},
|
||||||
desc = "Get information about the chunk you're inside"
|
desc = "Get information about the chunk you're inside"
|
||||||
)
|
)
|
||||||
@CommandPermissions("worldedit.chunkinfo")
|
@CommandPermissions(
|
||||||
|
value = "worldedit.chunkinfo",
|
||||||
|
queued = false
|
||||||
|
)
|
||||||
public void chunkInfo(Player player) {
|
public void chunkInfo(Player player) {
|
||||||
Location pos = player.getBlockLocation();
|
Location pos = player.getBlockLocation();
|
||||||
int chunkX = (int) Math.floor(pos.getBlockX() / 16.0);
|
int chunkX = (int) Math.floor(pos.getBlockX() / 16.0);
|
||||||
@ -99,7 +102,10 @@ public class ChunkCommands {
|
|||||||
aliases = {"/listchunks"},
|
aliases = {"/listchunks"},
|
||||||
desc = "List chunks that your selection includes"
|
desc = "List chunks that your selection includes"
|
||||||
)
|
)
|
||||||
@CommandPermissions("worldedit.listchunks")
|
@CommandPermissions(
|
||||||
|
value = "worldedit.listchunks",
|
||||||
|
queued = false
|
||||||
|
)
|
||||||
public void listChunks(
|
public void listChunks(
|
||||||
Actor actor, World world, LocalSession session,
|
Actor actor, World world, LocalSession session,
|
||||||
@ArgFlag(name = 'p', desc = "Page number.", def = "1") int page
|
@ArgFlag(name = 'p', desc = "Page number.", def = "1") int page
|
||||||
|
@ -74,6 +74,7 @@ import com.sk89q.worldedit.regions.Region;
|
|||||||
import com.sk89q.worldedit.regions.RegionIntersection;
|
import com.sk89q.worldedit.regions.RegionIntersection;
|
||||||
import com.sk89q.worldedit.regions.RegionSelector;
|
import com.sk89q.worldedit.regions.RegionSelector;
|
||||||
import com.sk89q.worldedit.regions.selector.CuboidRegionSelector;
|
import com.sk89q.worldedit.regions.selector.CuboidRegionSelector;
|
||||||
|
import com.sk89q.worldedit.regions.selector.ExtendingCuboidRegionSelector;
|
||||||
import com.sk89q.worldedit.session.ClipboardHolder;
|
import com.sk89q.worldedit.session.ClipboardHolder;
|
||||||
import com.sk89q.worldedit.util.formatting.text.Component;
|
import com.sk89q.worldedit.util.formatting.text.Component;
|
||||||
import com.sk89q.worldedit.util.formatting.text.TextComponent;
|
import com.sk89q.worldedit.util.formatting.text.TextComponent;
|
||||||
@ -159,35 +160,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);
|
||||||
copy.setCopyingBiomes(copyBiomes);
|
createCopy(session, editSession, copyBiomes, mask, clipboard, copy);
|
||||||
|
|
||||||
Mask sourceMask = editSession.getSourceMask();
|
|
||||||
Region[] regions = editSession.getAllowedRegions();
|
|
||||||
Region allowedRegion;
|
|
||||||
if (regions == null || regions.length == 0) {
|
|
||||||
allowedRegion = new NullRegion();
|
|
||||||
} else {
|
|
||||||
allowedRegion = new RegionIntersection(regions);
|
|
||||||
}
|
|
||||||
final Mask firstSourceMask = mask != null ? mask : sourceMask;
|
|
||||||
final Mask finalMask = MaskIntersection.of(firstSourceMask, new RegionMask(allowedRegion)).optimize();
|
|
||||||
if (finalMask != Masks.alwaysTrue()) {
|
|
||||||
copy.setSourceMask(finalMask);
|
|
||||||
}
|
|
||||||
if (sourceMask != null) {
|
|
||||||
editSession.setSourceMask(null);
|
|
||||||
new MaskTraverser(sourceMask).reset(editSession);
|
|
||||||
editSession.setSourceMask(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
Operations.completeLegacy(copy);
|
|
||||||
} catch (Throwable e) {
|
|
||||||
throw e;
|
|
||||||
} finally {
|
|
||||||
clipboard.flush();
|
|
||||||
}
|
|
||||||
session.setClipboard(new ClipboardHolder(clipboard));
|
|
||||||
|
|
||||||
copy.getStatusMessages().forEach(actor::print);
|
copy.getStatusMessages().forEach(actor::print);
|
||||||
//FAWE end
|
//FAWE end
|
||||||
@ -298,7 +271,25 @@ 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);
|
||||||
|
|
||||||
|
if (!actor.hasPermission("fawe.tips")) {
|
||||||
|
actor.print(Caption.of("fawe.tips.tip.lazycut"));
|
||||||
|
}
|
||||||
|
copy.getStatusMessages().forEach(actor::print);
|
||||||
|
//FAWE end
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createCopy(
|
||||||
|
final LocalSession session,
|
||||||
|
final EditSession editSession,
|
||||||
|
final boolean copyBiomes,
|
||||||
|
final Mask mask,
|
||||||
|
final Clipboard clipboard,
|
||||||
|
final ForwardExtentCopy copy
|
||||||
|
) {
|
||||||
copy.setCopyingBiomes(copyBiomes);
|
copy.setCopyingBiomes(copyBiomes);
|
||||||
|
|
||||||
Mask sourceMask = editSession.getSourceMask();
|
Mask sourceMask = editSession.getSourceMask();
|
||||||
Region[] regions = editSession.getAllowedRegions();
|
Region[] regions = editSession.getAllowedRegions();
|
||||||
Region allowedRegion;
|
Region allowedRegion;
|
||||||
@ -317,20 +308,13 @@ public class ClipboardCommands {
|
|||||||
new MaskTraverser(sourceMask).reset(editSession);
|
new MaskTraverser(sourceMask).reset(editSession);
|
||||||
editSession.setSourceMask(null);
|
editSession.setSourceMask(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Operations.completeLegacy(copy);
|
Operations.completeLegacy(copy);
|
||||||
} catch (Throwable e) {
|
|
||||||
throw e;
|
|
||||||
} finally {
|
} finally {
|
||||||
clipboard.flush();
|
clipboard.flush();
|
||||||
}
|
}
|
||||||
session.setClipboard(new ClipboardHolder(clipboard));
|
session.setClipboard(new ClipboardHolder(clipboard));
|
||||||
|
|
||||||
if (!actor.hasPermission("fawe.tips")) {
|
|
||||||
actor.print(Caption.of("fawe.tips.tip.lazycut"));
|
|
||||||
}
|
|
||||||
copy.getStatusMessages().forEach(actor::print);
|
|
||||||
//FAWE end
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Command(
|
@Command(
|
||||||
@ -444,7 +428,12 @@ public class ClipboardCommands {
|
|||||||
Vector3 max = realTo.add(holder
|
Vector3 max = realTo.add(holder
|
||||||
.getTransform()
|
.getTransform()
|
||||||
.apply(region.getMaximumPoint().subtract(region.getMinimumPoint()).toVector3()));
|
.apply(region.getMaximumPoint().subtract(region.getMinimumPoint()).toVector3()));
|
||||||
RegionSelector selector = new CuboidRegionSelector(world, realTo.toBlockPoint(), max.toBlockPoint());
|
final CuboidRegionSelector selector;
|
||||||
|
if (session.getRegionSelector(world) instanceof ExtendingCuboidRegionSelector) {
|
||||||
|
selector = new ExtendingCuboidRegionSelector(world, realTo.toBlockPoint(), max.toBlockPoint());
|
||||||
|
} else {
|
||||||
|
selector = new CuboidRegionSelector(world, realTo.toBlockPoint(), max.toBlockPoint());
|
||||||
|
}
|
||||||
session.setRegionSelector(world, selector);
|
session.setRegionSelector(world, selector);
|
||||||
selector.learnChanges();
|
selector.learnChanges();
|
||||||
selector.explainRegionAdjust(actor, session);
|
selector.explainRegionAdjust(actor, session);
|
||||||
@ -475,9 +464,10 @@ public class ClipboardCommands {
|
|||||||
@Command(
|
@Command(
|
||||||
name = "/rotate",
|
name = "/rotate",
|
||||||
desc = "Rotate the contents of the clipboard",
|
desc = "Rotate the contents of the clipboard",
|
||||||
descFooter = "Non-destructively rotate the contents of the clipboard.\n"
|
descFooter = """
|
||||||
+ "Angles are provided in degrees and a positive angle will result in a clockwise rotation. "
|
Non-destructively rotate the contents of the clipboard.
|
||||||
+ "Multiple rotations can be stacked. Interpolation is not performed so angles should be a multiple of 90 degrees.\n"
|
Angles are provided in degrees and a positive angle will result in a clockwise rotation. Multiple rotations can be stacked. Interpolation is not performed so angles should be a multiple of 90 degrees.
|
||||||
|
"""
|
||||||
)
|
)
|
||||||
@CommandPermissions("worldedit.clipboard.rotate")
|
@CommandPermissions("worldedit.clipboard.rotate")
|
||||||
public void rotate(
|
public void rotate(
|
||||||
|
@ -149,14 +149,11 @@ public class GeneralCommands {
|
|||||||
String arg0 = args.get(0).toLowerCase(Locale.ENGLISH);
|
String arg0 = args.get(0).toLowerCase(Locale.ENGLISH);
|
||||||
String flipped;
|
String flipped;
|
||||||
switch (arg0) {
|
switch (arg0) {
|
||||||
case "on":
|
case "on" -> flipped = "off";
|
||||||
flipped = "off";
|
case "off" -> flipped = "on";
|
||||||
break;
|
default -> {
|
||||||
case "off":
|
|
||||||
flipped = "on";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return TextComponent.of("There is no replacement for //fast " + arg0);
|
return TextComponent.of("There is no replacement for //fast " + arg0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return CommandUtil.createNewCommandReplacementText("//perf " + flipped);
|
return CommandUtil.createNewCommandReplacementText("//perf " + flipped);
|
||||||
}
|
}
|
||||||
@ -362,7 +359,10 @@ public class GeneralCommands {
|
|||||||
descFooter = "This is dependent on platform implementation. " +
|
descFooter = "This is dependent on platform implementation. " +
|
||||||
"Not all platforms support watchdog hooks, or contain a watchdog."
|
"Not all platforms support watchdog hooks, or contain a watchdog."
|
||||||
)
|
)
|
||||||
@CommandPermissions("worldedit.watchdog")
|
@CommandPermissions(
|
||||||
|
value = "worldedit.watchdog",
|
||||||
|
queued = false
|
||||||
|
)
|
||||||
public void watchdog(
|
public void watchdog(
|
||||||
Actor actor, LocalSession session,
|
Actor actor, LocalSession session,
|
||||||
@Arg(desc = "The mode to set the watchdog hook to", def = "")
|
@Arg(desc = "The mode to set the watchdog hook to", def = "")
|
||||||
@ -424,7 +424,10 @@ public class GeneralCommands {
|
|||||||
aliases = {"/searchitem", "/l", "/search"},
|
aliases = {"/searchitem", "/l", "/search"},
|
||||||
desc = "Search for an item"
|
desc = "Search for an item"
|
||||||
)
|
)
|
||||||
@CommandPermissions("worldedit.searchitem")
|
@CommandPermissions(
|
||||||
|
value = "worldedit.searchitem",
|
||||||
|
queued = false
|
||||||
|
)
|
||||||
public void searchItem(
|
public void searchItem(
|
||||||
Actor actor,
|
Actor actor,
|
||||||
@Switch(name = 'b', desc = "Only search for blocks")
|
@Switch(name = 'b', desc = "Only search for blocks")
|
||||||
@ -573,7 +576,10 @@ public class GeneralCommands {
|
|||||||
aliases = {"tips"},
|
aliases = {"tips"},
|
||||||
desc = "Toggle FAWE tips"
|
desc = "Toggle FAWE tips"
|
||||||
)
|
)
|
||||||
@CommandPermissions("fawe.tips")
|
@CommandPermissions(
|
||||||
|
value = "fawe.tips",
|
||||||
|
queued = false
|
||||||
|
)
|
||||||
public void tips(Actor actor, LocalSession session) throws WorldEditException {
|
public void tips(Actor actor, LocalSession session) throws WorldEditException {
|
||||||
if (actor.togglePermission("fawe.tips")) {
|
if (actor.togglePermission("fawe.tips")) {
|
||||||
actor.print(Caption.of("fawe.info.worldedit.toggle.tips.on"));
|
actor.print(Caption.of("fawe.info.worldedit.toggle.tips.on"));
|
||||||
@ -627,7 +633,6 @@ public class GeneralCommands {
|
|||||||
String command = "/searchitem " + (blocksOnly ? "-b " : "") + (itemsOnly ? "-i " : "") + "-p %page% " + search;
|
String command = "/searchitem " + (blocksOnly ? "-b " : "") + (itemsOnly ? "-i " : "") + "-p %page% " + search;
|
||||||
Map<String, Component> results = new TreeMap<>();
|
Map<String, Component> results = new TreeMap<>();
|
||||||
String idMatch = search.replace(' ', '_');
|
String idMatch = search.replace(' ', '_');
|
||||||
String nameMatch = search.toLowerCase(Locale.ROOT);
|
|
||||||
for (ItemType searchType : ItemType.REGISTRY) {
|
for (ItemType searchType : ItemType.REGISTRY) {
|
||||||
if (blocksOnly && !searchType.hasBlockType()) {
|
if (blocksOnly && !searchType.hasBlockType()) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -65,6 +65,7 @@ import org.jetbrains.annotations.Range;
|
|||||||
import java.awt.RenderingHints;
|
import java.awt.RenderingHints;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
@ -119,18 +120,15 @@ public class GenerationCommands {
|
|||||||
final double radiusX;
|
final double radiusX;
|
||||||
final double radiusZ;
|
final double radiusZ;
|
||||||
switch (radii.size()) {
|
switch (radii.size()) {
|
||||||
case 1:
|
case 1 -> radiusX = radiusZ = Math.max(1, radii.get(0));
|
||||||
radiusX = radiusZ = Math.max(1, radii.get(0));
|
case 2 -> {
|
||||||
break;
|
|
||||||
|
|
||||||
case 2:
|
|
||||||
radiusX = Math.max(1, radii.get(0));
|
radiusX = Math.max(1, radii.get(0));
|
||||||
radiusZ = Math.max(1, radii.get(1));
|
radiusZ = Math.max(1, radii.get(1));
|
||||||
break;
|
}
|
||||||
|
default -> {
|
||||||
default:
|
|
||||||
actor.print(Caption.of("worldedit.cyl.invalid-radius"));
|
actor.print(Caption.of("worldedit.cyl.invalid-radius"));
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
worldEdit.checkMaxRadius(radiusX);
|
worldEdit.checkMaxRadius(radiusX);
|
||||||
worldEdit.checkMaxRadius(radiusZ);
|
worldEdit.checkMaxRadius(radiusZ);
|
||||||
@ -169,18 +167,15 @@ public class GenerationCommands {
|
|||||||
final double radiusX;
|
final double radiusX;
|
||||||
final double radiusZ;
|
final double radiusZ;
|
||||||
switch (radii.size()) {
|
switch (radii.size()) {
|
||||||
case 1:
|
case 1 -> radiusX = radiusZ = Math.max(1, radii.get(0));
|
||||||
radiusX = radiusZ = Math.max(1, radii.get(0));
|
case 2 -> {
|
||||||
break;
|
|
||||||
|
|
||||||
case 2:
|
|
||||||
radiusX = Math.max(1, radii.get(0));
|
radiusX = Math.max(1, radii.get(0));
|
||||||
radiusZ = Math.max(1, radii.get(1));
|
radiusZ = Math.max(1, radii.get(1));
|
||||||
break;
|
}
|
||||||
|
default -> {
|
||||||
default:
|
|
||||||
actor.print(Caption.of("worldedit.cyl.invalid-radius"));
|
actor.print(Caption.of("worldedit.cyl.invalid-radius"));
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
worldEdit.checkMaxRadius(radiusX);
|
worldEdit.checkMaxRadius(radiusX);
|
||||||
@ -234,19 +229,16 @@ public class GenerationCommands {
|
|||||||
final double radiusY;
|
final double radiusY;
|
||||||
final double radiusZ;
|
final double radiusZ;
|
||||||
switch (radii.size()) {
|
switch (radii.size()) {
|
||||||
case 1:
|
case 1 -> radiusX = radiusY = radiusZ = Math.max(0, radii.get(0));
|
||||||
radiusX = radiusY = radiusZ = Math.max(0, radii.get(0));
|
case 3 -> {
|
||||||
break;
|
|
||||||
|
|
||||||
case 3:
|
|
||||||
radiusX = Math.max(0, radii.get(0));
|
radiusX = Math.max(0, radii.get(0));
|
||||||
radiusY = Math.max(0, radii.get(1));
|
radiusY = Math.max(0, radii.get(1));
|
||||||
radiusZ = Math.max(0, radii.get(2));
|
radiusZ = Math.max(0, radii.get(2));
|
||||||
break;
|
}
|
||||||
|
default -> {
|
||||||
default:
|
|
||||||
actor.print(Caption.of("worldedit.sphere.invalid-radius"));
|
actor.print(Caption.of("worldedit.sphere.invalid-radius"));
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
worldEdit.checkMaxRadius(radiusX);
|
worldEdit.checkMaxRadius(radiusX);
|
||||||
@ -437,9 +429,10 @@ public class GenerationCommands {
|
|||||||
name = "/generatebiome",
|
name = "/generatebiome",
|
||||||
aliases = {"/genbiome", "/gb"},
|
aliases = {"/genbiome", "/gb"},
|
||||||
desc = "Sets biome according to a formula.",
|
desc = "Sets biome according to a formula.",
|
||||||
descFooter = "Formula must return positive numbers (true) if the point is inside the shape\n"
|
descFooter = """
|
||||||
+ "Sets the biome of blocks in that shape.\n"
|
Formula must return positive numbers (true) if the point is inside the shape
|
||||||
+ "For details, see https://ehub.to/we/expr"
|
Sets the biome of blocks in that shape.
|
||||||
|
For details, see https://ehub.to/we/expr"""
|
||||||
)
|
)
|
||||||
@CommandPermissions("worldedit.generation.shape.biome")
|
@CommandPermissions("worldedit.generation.shape.biome")
|
||||||
@Logging(ALL)
|
@Logging(ALL)
|
||||||
@ -588,12 +581,10 @@ public class GenerationCommands {
|
|||||||
@Arg(desc = "boolean", def = "true") boolean randomize,
|
@Arg(desc = "boolean", def = "true") boolean randomize,
|
||||||
@Arg(desc = "TODO", def = "100") int threshold,
|
@Arg(desc = "TODO", def = "100") int threshold,
|
||||||
@Arg(desc = "BlockVector2", def = "") BlockVector2 dimensions
|
@Arg(desc = "BlockVector2", def = "") BlockVector2 dimensions
|
||||||
) throws WorldEditException, IOException {
|
) throws WorldEditException, IOException, URISyntaxException {
|
||||||
TextureUtil tu = Fawe.instance().getCachedTextureUtil(randomize, 0, threshold);
|
TextureUtil tu = Fawe.instance().getCachedTextureUtil(randomize, 0, threshold);
|
||||||
URL url = new URL(imageURL);
|
URL url = new URL(imageURL);
|
||||||
if (!url.getHost().equalsIgnoreCase("i.imgur.com")) {
|
MainUtil.checkImageHost(url.toURI());
|
||||||
throw new IOException("Only i.imgur.com links are allowed!");
|
|
||||||
}
|
|
||||||
if (dimensions != null) {
|
if (dimensions != null) {
|
||||||
checkCommandArgument(
|
checkCommandArgument(
|
||||||
(long) dimensions.getX() * dimensions.getZ() <= Settings.settings().WEB.MAX_IMAGE_SIZE,
|
(long) dimensions.getX() * dimensions.getZ() <= Settings.settings().WEB.MAX_IMAGE_SIZE,
|
||||||
@ -626,14 +617,12 @@ public class GenerationCommands {
|
|||||||
BlockVector3 pos1 = session.getPlacementPosition(actor);
|
BlockVector3 pos1 = session.getPlacementPosition(actor);
|
||||||
BlockVector3 pos2 = pos1.add(image.getWidth() - 1, 0, image.getHeight() - 1);
|
BlockVector3 pos2 = pos1.add(image.getWidth() - 1, 0, image.getHeight() - 1);
|
||||||
CuboidRegion region = new CuboidRegion(pos1, pos2);
|
CuboidRegion region = new CuboidRegion(pos1, pos2);
|
||||||
int[] count = new int[1];
|
|
||||||
final BufferedImage finalImage = image;
|
final BufferedImage finalImage = image;
|
||||||
RegionVisitor visitor = new RegionVisitor(region, pos -> {
|
RegionVisitor visitor = new RegionVisitor(region, pos -> {
|
||||||
int x = pos.getBlockX() - pos1.getBlockX();
|
int x = pos.getBlockX() - pos1.getBlockX();
|
||||||
int z = pos.getBlockZ() - pos1.getBlockZ();
|
int z = pos.getBlockZ() - pos1.getBlockZ();
|
||||||
int color = finalImage.getRGB(x, z);
|
int color = finalImage.getRGB(x, z);
|
||||||
BlockType block = tu.getNearestBlock(color);
|
BlockType block = tu.getNearestBlock(color);
|
||||||
count[0]++;
|
|
||||||
if (block != null) {
|
if (block != null) {
|
||||||
return editSession.setBlock(pos, block.getDefaultState());
|
return editSession.setBlock(pos, block.getDefaultState());
|
||||||
}
|
}
|
||||||
|
@ -223,7 +223,10 @@ public class HistorySubCommands {
|
|||||||
aliases = {"summary", "summarize"},
|
aliases = {"summary", "summarize"},
|
||||||
desc = "Summarize an edit"
|
desc = "Summarize an edit"
|
||||||
)
|
)
|
||||||
@CommandPermissions("worldedit.history.info")
|
@CommandPermissions(
|
||||||
|
value = "worldedit.history.info",
|
||||||
|
queued = false
|
||||||
|
)
|
||||||
public synchronized void summary(
|
public synchronized void summary(
|
||||||
Player player, RollbackDatabase database, Arguments arguments,
|
Player player, RollbackDatabase database, Arguments arguments,
|
||||||
@Arg(desc = "Player uuid/name")
|
@Arg(desc = "Player uuid/name")
|
||||||
@ -314,8 +317,7 @@ public class HistorySubCommands {
|
|||||||
public Component apply(@Nullable Supplier<? extends ChangeSet> input) {
|
public Component apply(@Nullable Supplier<? extends ChangeSet> input) {
|
||||||
ChangeSet edit = input.get();
|
ChangeSet edit = input.get();
|
||||||
|
|
||||||
if (edit instanceof RollbackOptimizedHistory) {
|
if (edit instanceof RollbackOptimizedHistory rollback) {
|
||||||
RollbackOptimizedHistory rollback = (RollbackOptimizedHistory) edit;
|
|
||||||
|
|
||||||
UUID uuid = rollback.getUUID();
|
UUID uuid = rollback.getUUID();
|
||||||
int index = rollback.getIndex();
|
int index = rollback.getIndex();
|
||||||
@ -368,7 +370,10 @@ public class HistorySubCommands {
|
|||||||
aliases = {"inspect", "search", "near"},
|
aliases = {"inspect", "search", "near"},
|
||||||
desc = "Find nearby edits"
|
desc = "Find nearby edits"
|
||||||
)
|
)
|
||||||
@CommandPermissions("worldedit.history.find")
|
@CommandPermissions(
|
||||||
|
value = "worldedit.history.find",
|
||||||
|
queued = false
|
||||||
|
)
|
||||||
public synchronized void find(
|
public synchronized void find(
|
||||||
Player player, World world, RollbackDatabase database, Arguments arguments,
|
Player player, World world, RollbackDatabase database, Arguments arguments,
|
||||||
@ArgFlag(name = 'u', def = "", desc = "String user")
|
@ArgFlag(name = 'u', def = "", desc = "String user")
|
||||||
@ -429,7 +434,10 @@ public class HistorySubCommands {
|
|||||||
aliases = {"distribution"},
|
aliases = {"distribution"},
|
||||||
desc = "View block distribution for an edit"
|
desc = "View block distribution for an edit"
|
||||||
)
|
)
|
||||||
@CommandPermissions("worldedit.history.distr")
|
@CommandPermissions(
|
||||||
|
value = "worldedit.history.distr",
|
||||||
|
queued = false
|
||||||
|
)
|
||||||
public void distr(
|
public void distr(
|
||||||
Player player, LocalSession session, RollbackDatabase database, Arguments arguments,
|
Player player, LocalSession session, RollbackDatabase database, Arguments arguments,
|
||||||
@Arg(desc = "Player uuid/name")
|
@Arg(desc = "Player uuid/name")
|
||||||
@ -468,7 +476,10 @@ public class HistorySubCommands {
|
|||||||
name = "list",
|
name = "list",
|
||||||
desc = "List your history"
|
desc = "List your history"
|
||||||
)
|
)
|
||||||
@CommandPermissions("worldedit.history.list")
|
@CommandPermissions(
|
||||||
|
value = "worldedit.history.list",
|
||||||
|
queued = false
|
||||||
|
)
|
||||||
public void list(
|
public void list(
|
||||||
Player player, LocalSession session, RollbackDatabase database, Arguments arguments,
|
Player player, LocalSession session, RollbackDatabase database, Arguments arguments,
|
||||||
@Arg(desc = "Player uuid/name")
|
@Arg(desc = "Player uuid/name")
|
||||||
@ -476,7 +487,6 @@ public class HistorySubCommands {
|
|||||||
@ArgFlag(name = 'p', desc = "Page to view.", def = "")
|
@ArgFlag(name = 'p', desc = "Page to view.", def = "")
|
||||||
Integer page
|
Integer page
|
||||||
) {
|
) {
|
||||||
int index = session.getHistoryIndex();
|
|
||||||
List<Supplier<? extends ChangeSet>> history = Lists.transform(
|
List<Supplier<? extends ChangeSet>> history = Lists.transform(
|
||||||
session.getHistory(),
|
session.getHistory(),
|
||||||
(Function<ChangeSet, Supplier<ChangeSet>>) input -> () -> input
|
(Function<ChangeSet, Supplier<ChangeSet>>) input -> () -> input
|
||||||
|
@ -60,7 +60,10 @@ public class NavigationCommands {
|
|||||||
aliases = {"!", "/unstuck"},
|
aliases = {"!", "/unstuck"},
|
||||||
desc = "Escape from being stuck inside a block"
|
desc = "Escape from being stuck inside a block"
|
||||||
)
|
)
|
||||||
@CommandPermissions("worldedit.navigation.unstuck")
|
@CommandPermissions(
|
||||||
|
value = "worldedit.navigation.unstuck",
|
||||||
|
queued = false
|
||||||
|
)
|
||||||
public void unstuck(Player player) throws WorldEditException {
|
public void unstuck(Player player) throws WorldEditException {
|
||||||
player.findFreePosition();
|
player.findFreePosition();
|
||||||
player.print(Caption.of("worldedit.unstuck.moved"));
|
player.print(Caption.of("worldedit.unstuck.moved"));
|
||||||
@ -71,7 +74,10 @@ public class NavigationCommands {
|
|||||||
aliases = {"asc", "/asc", "/ascend"},
|
aliases = {"asc", "/asc", "/ascend"},
|
||||||
desc = "Go up a floor"
|
desc = "Go up a floor"
|
||||||
)
|
)
|
||||||
@CommandPermissions("worldedit.navigation.ascend")
|
@CommandPermissions(
|
||||||
|
value = "worldedit.navigation.ascend",
|
||||||
|
queued = false
|
||||||
|
)
|
||||||
public void ascend(
|
public void ascend(
|
||||||
Player player,
|
Player player,
|
||||||
@Arg(desc = "# of levels to ascend", def = "1")
|
@Arg(desc = "# of levels to ascend", def = "1")
|
||||||
@ -102,7 +108,10 @@ public class NavigationCommands {
|
|||||||
aliases = {"desc", "/desc", "/descend"},
|
aliases = {"desc", "/desc", "/descend"},
|
||||||
desc = "Go down a floor"
|
desc = "Go down a floor"
|
||||||
)
|
)
|
||||||
@CommandPermissions("worldedit.navigation.descend")
|
@CommandPermissions(
|
||||||
|
value = "worldedit.navigation.descend",
|
||||||
|
queued = false
|
||||||
|
)
|
||||||
public void descend(
|
public void descend(
|
||||||
Player player,
|
Player player,
|
||||||
@Arg(desc = "# of levels to descend", def = "1")
|
@Arg(desc = "# of levels to descend", def = "1")
|
||||||
@ -159,7 +168,10 @@ public class NavigationCommands {
|
|||||||
aliases = {"/thru"},
|
aliases = {"/thru"},
|
||||||
desc = "Pass through walls"
|
desc = "Pass through walls"
|
||||||
)
|
)
|
||||||
@CommandPermissions("worldedit.navigation.thru.command")
|
@CommandPermissions(
|
||||||
|
value = "worldedit.navigation.thru.command",
|
||||||
|
queued = false
|
||||||
|
)
|
||||||
public void thru(Player player) throws WorldEditException {
|
public void thru(Player player) throws WorldEditException {
|
||||||
if (player.passThroughForwardWall(6)) {
|
if (player.passThroughForwardWall(6)) {
|
||||||
player.print(Caption.of("worldedit.thru.moved"));
|
player.print(Caption.of("worldedit.thru.moved"));
|
||||||
@ -173,7 +185,10 @@ public class NavigationCommands {
|
|||||||
aliases = {"j", "/jumpto", "/j"},
|
aliases = {"j", "/jumpto", "/j"},
|
||||||
desc = "Teleport to a location"
|
desc = "Teleport to a location"
|
||||||
)
|
)
|
||||||
@CommandPermissions("worldedit.navigation.jumpto.command")
|
@CommandPermissions(
|
||||||
|
value = "worldedit.navigation.jumpto.command",
|
||||||
|
queued = false
|
||||||
|
)
|
||||||
public void jumpTo(
|
public void jumpTo(
|
||||||
Player player,
|
Player player,
|
||||||
@Arg(desc = "Location to jump to", def = "")
|
@Arg(desc = "Location to jump to", def = "")
|
||||||
|
@ -140,7 +140,10 @@ public class RegionCommands {
|
|||||||
name = "/test",
|
name = "/test",
|
||||||
desc = "test region"
|
desc = "test region"
|
||||||
)
|
)
|
||||||
@CommandPermissions("worldedit.region.test")
|
@CommandPermissions(
|
||||||
|
value = "worldedit.region.test",
|
||||||
|
queued = false
|
||||||
|
)
|
||||||
@Logging(REGION)
|
@Logging(REGION)
|
||||||
public void test(
|
public void test(
|
||||||
Actor actor, EditSession editSession,
|
Actor actor, EditSession editSession,
|
||||||
@ -175,7 +178,10 @@ public class RegionCommands {
|
|||||||
aliases = "/nbt",
|
aliases = "/nbt",
|
||||||
desc = "View nbt info for a block"
|
desc = "View nbt info for a block"
|
||||||
)
|
)
|
||||||
@CommandPermissions("worldedit.nbtinfo")
|
@CommandPermissions(
|
||||||
|
value = "worldedit.nbtinfo",
|
||||||
|
queued = false
|
||||||
|
)
|
||||||
public void nbtinfo(Player player, EditSession editSession) {
|
public void nbtinfo(Player player, EditSession editSession) {
|
||||||
Location pos = player.getBlockTrace(128);
|
Location pos = player.getBlockTrace(128);
|
||||||
if (pos == null) {
|
if (pos == null) {
|
||||||
@ -228,13 +234,12 @@ public class RegionCommands {
|
|||||||
@Switch(name = 'h', desc = "Generate only a shell")
|
@Switch(name = 'h', desc = "Generate only a shell")
|
||||||
boolean shell
|
boolean shell
|
||||||
) throws WorldEditException {
|
) throws WorldEditException {
|
||||||
if (!(region instanceof CuboidRegion)) {
|
if (!(region instanceof CuboidRegion cuboidregion)) {
|
||||||
actor.print(Caption.of("worldedit.line.cuboid-only"));
|
actor.print(Caption.of("worldedit.line.cuboid-only"));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
checkCommandArgument(thickness >= 0, "Thickness must be >= 0");
|
checkCommandArgument(thickness >= 0, "Thickness must be >= 0");
|
||||||
|
|
||||||
CuboidRegion cuboidregion = (CuboidRegion) region;
|
|
||||||
BlockVector3 pos1 = cuboidregion.getPos1();
|
BlockVector3 pos1 = cuboidregion.getPos1();
|
||||||
BlockVector3 pos2 = cuboidregion.getPos2();
|
BlockVector3 pos2 = cuboidregion.getPos2();
|
||||||
int blocksChanged = editSession.drawLine(pattern, pos1, pos2, thickness, !shell);
|
int blocksChanged = editSession.drawLine(pattern, pos1, pos2, thickness, !shell);
|
||||||
@ -261,13 +266,12 @@ public class RegionCommands {
|
|||||||
@Switch(name = 'h', desc = "Generate only a shell")
|
@Switch(name = 'h', desc = "Generate only a shell")
|
||||||
boolean shell
|
boolean shell
|
||||||
) throws WorldEditException {
|
) throws WorldEditException {
|
||||||
if (!(region instanceof ConvexPolyhedralRegion)) {
|
if (!(region instanceof ConvexPolyhedralRegion cpregion)) {
|
||||||
actor.print(Caption.of("worldedit.curve.invalid-type"));
|
actor.print(Caption.of("worldedit.curve.invalid-type"));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
checkCommandArgument(thickness >= 0, "Thickness must be >= 0");
|
checkCommandArgument(thickness >= 0, "Thickness must be >= 0");
|
||||||
|
|
||||||
ConvexPolyhedralRegion cpregion = (ConvexPolyhedralRegion) region;
|
|
||||||
List<BlockVector3> vectors = new ArrayList<>(cpregion.getVertices());
|
List<BlockVector3> vectors = new ArrayList<>(cpregion.getVertices());
|
||||||
|
|
||||||
int blocksChanged = editSession.drawSpline(pattern, vectors, 0, 0, 0, 10, thickness, !shell);
|
int blocksChanged = editSession.drawSpline(pattern, vectors, 0, 0, 0, 10, thickness, !shell);
|
||||||
@ -468,7 +472,10 @@ public class RegionCommands {
|
|||||||
desc = "Bypass region restrictions",
|
desc = "Bypass region restrictions",
|
||||||
descFooter = "Bypass region restrictions"
|
descFooter = "Bypass region restrictions"
|
||||||
)
|
)
|
||||||
@CommandPermissions("fawe.admin")
|
@CommandPermissions(
|
||||||
|
value = "fawe.admin",
|
||||||
|
queued = false
|
||||||
|
)
|
||||||
public void wea(Actor actor) throws WorldEditException {
|
public void wea(Actor actor) throws WorldEditException {
|
||||||
if (actor.togglePermission("fawe.bypass")) {
|
if (actor.togglePermission("fawe.bypass")) {
|
||||||
actor.print(Caption.of("fawe.info.worldedit.bypassed"));
|
actor.print(Caption.of("fawe.info.worldedit.bypassed"));
|
||||||
@ -697,7 +704,7 @@ public class RegionCommands {
|
|||||||
actor.print(Caption.of("fawe.regen.time"));
|
actor.print(Caption.of("fawe.regen.time"));
|
||||||
//FAWE end
|
//FAWE end
|
||||||
RegenOptions options = RegenOptions.builder()
|
RegenOptions options = RegenOptions.builder()
|
||||||
.seed(!randomSeed ? seed : new Long(ThreadLocalRandom.current().nextLong()))
|
.seed(!randomSeed ? seed : Long.valueOf(ThreadLocalRandom.current().nextLong()))
|
||||||
.regenBiomes(regenBiomes)
|
.regenBiomes(regenBiomes)
|
||||||
.biomeType(biomeType)
|
.biomeType(biomeType)
|
||||||
.build();
|
.build();
|
||||||
@ -718,9 +725,10 @@ public class RegionCommands {
|
|||||||
@Command(
|
@Command(
|
||||||
name = "/deform",
|
name = "/deform",
|
||||||
desc = "Deforms a selected region with an expression",
|
desc = "Deforms a selected region with an expression",
|
||||||
descFooter = "The expression is executed for each block and is expected\n"
|
descFooter = """
|
||||||
+ "to modify the variables x, y and z to point to a new block\n"
|
The expression is executed for each block and is expected
|
||||||
+ "to fetch. For details, see https://ehub.to/we/expr"
|
to modify the variables x, y and z to point to a new block
|
||||||
|
to fetch. For details, see https://ehub.to/we/expr"""
|
||||||
)
|
)
|
||||||
@CommandPermissions("worldedit.region.deform")
|
@CommandPermissions("worldedit.region.deform")
|
||||||
@Logging(ALL)
|
@Logging(ALL)
|
||||||
@ -794,9 +802,10 @@ public class RegionCommands {
|
|||||||
@Command(
|
@Command(
|
||||||
name = "/hollow",
|
name = "/hollow",
|
||||||
desc = "Hollows out the object contained in this selection",
|
desc = "Hollows out the object contained in this selection",
|
||||||
descFooter = "Hollows out the object contained in this selection.\n"
|
descFooter = """
|
||||||
+ "Optionally fills the hollowed out part with the given block.\n"
|
Hollows out the object contained in this selection.
|
||||||
+ "Thickness is measured in manhattan distance."
|
Optionally fills the hollowed out part with the given block.
|
||||||
|
Thickness is measured in manhattan distance."""
|
||||||
)
|
)
|
||||||
@CommandPermissions("worldedit.region.hollow")
|
@CommandPermissions("worldedit.region.hollow")
|
||||||
@Logging(REGION)
|
@Logging(REGION)
|
||||||
|
@ -26,7 +26,6 @@ import com.fastasyncworldedit.core.extent.clipboard.MultiClipboardHolder;
|
|||||||
import com.fastasyncworldedit.core.extent.clipboard.URIClipboardHolder;
|
import com.fastasyncworldedit.core.extent.clipboard.URIClipboardHolder;
|
||||||
import com.fastasyncworldedit.core.extent.clipboard.io.schematic.MinecraftStructure;
|
import com.fastasyncworldedit.core.extent.clipboard.io.schematic.MinecraftStructure;
|
||||||
import com.fastasyncworldedit.core.util.MainUtil;
|
import com.fastasyncworldedit.core.util.MainUtil;
|
||||||
import com.google.common.base.Function;
|
|
||||||
import com.google.common.collect.Multimap;
|
import com.google.common.collect.Multimap;
|
||||||
import com.sk89q.worldedit.LocalConfiguration;
|
import com.sk89q.worldedit.LocalConfiguration;
|
||||||
import com.sk89q.worldedit.LocalSession;
|
import com.sk89q.worldedit.LocalSession;
|
||||||
@ -46,6 +45,7 @@ import com.sk89q.worldedit.extent.clipboard.io.ClipboardReader;
|
|||||||
import com.sk89q.worldedit.extent.clipboard.io.ClipboardWriter;
|
import com.sk89q.worldedit.extent.clipboard.io.ClipboardWriter;
|
||||||
import com.sk89q.worldedit.function.operation.Operations;
|
import com.sk89q.worldedit.function.operation.Operations;
|
||||||
import com.sk89q.worldedit.internal.util.LogManagerCompat;
|
import com.sk89q.worldedit.internal.util.LogManagerCompat;
|
||||||
|
import com.sk89q.worldedit.math.transform.AffineTransform;
|
||||||
import com.sk89q.worldedit.math.transform.Transform;
|
import com.sk89q.worldedit.math.transform.Transform;
|
||||||
import com.sk89q.worldedit.session.ClipboardHolder;
|
import com.sk89q.worldedit.session.ClipboardHolder;
|
||||||
import com.sk89q.worldedit.util.formatting.component.ErrorFormat;
|
import com.sk89q.worldedit.util.formatting.component.ErrorFormat;
|
||||||
@ -90,6 +90,8 @@ import java.util.Map;
|
|||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
|
import java.util.concurrent.ThreadLocalRandom;
|
||||||
|
import java.util.function.Function;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import static com.fastasyncworldedit.core.util.ReflectionUtils.as;
|
import static com.fastasyncworldedit.core.util.ReflectionUtils.as;
|
||||||
@ -211,11 +213,9 @@ public class SchematicCommands {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ClipboardHolder clipboard = session.getClipboard();
|
ClipboardHolder clipboard = session.getClipboard();
|
||||||
if (clipboard instanceof URIClipboardHolder) {
|
if (clipboard instanceof URIClipboardHolder identifiable) {
|
||||||
URIClipboardHolder identifiable = (URIClipboardHolder) clipboard;
|
|
||||||
if (identifiable.contains(uri)) {
|
if (identifiable.contains(uri)) {
|
||||||
if (identifiable instanceof MultiClipboardHolder) {
|
if (identifiable instanceof MultiClipboardHolder multi) {
|
||||||
MultiClipboardHolder multi = (MultiClipboardHolder) identifiable;
|
|
||||||
multi.remove(uri);
|
multi.remove(uri);
|
||||||
if (multi.getHolders().isEmpty()) {
|
if (multi.getHolders().isEmpty()) {
|
||||||
session.setClipboard(null);
|
session.setClipboard(null);
|
||||||
@ -317,12 +317,16 @@ public class SchematicCommands {
|
|||||||
@Arg(desc = "File name.")
|
@Arg(desc = "File name.")
|
||||||
String filename,
|
String filename,
|
||||||
@Arg(desc = "Format name.", def = "fast")
|
@Arg(desc = "Format name.", def = "fast")
|
||||||
String formatName
|
String formatName,
|
||||||
|
//FAWE start - random rotation
|
||||||
|
@Switch(name = 'r', desc = "Apply random rotation to the clipboard")
|
||||||
|
boolean randomRotate
|
||||||
|
//FAWE end
|
||||||
) throws FilenameException {
|
) throws FilenameException {
|
||||||
LocalConfiguration config = worldEdit.getConfiguration();
|
LocalConfiguration config = worldEdit.getConfiguration();
|
||||||
|
|
||||||
//FAWE start
|
//FAWE start
|
||||||
ClipboardFormat format = null;
|
ClipboardFormat format;
|
||||||
InputStream in = null;
|
InputStream in = null;
|
||||||
try {
|
try {
|
||||||
URI uri;
|
URI uri;
|
||||||
@ -385,6 +389,12 @@ public class SchematicCommands {
|
|||||||
uri = file.toURI();
|
uri = file.toURI();
|
||||||
|
|
||||||
format.hold(actor, uri, in);
|
format.hold(actor, uri, in);
|
||||||
|
if (randomRotate) {
|
||||||
|
AffineTransform transform = new AffineTransform();
|
||||||
|
int rotate = 90 * ThreadLocalRandom.current().nextInt(4);
|
||||||
|
transform = transform.rotateY(rotate);
|
||||||
|
session.getClipboard().setTransform(transform);
|
||||||
|
}
|
||||||
actor.print(Caption.of("fawe.worldedit.schematic.schematic.loaded", filename));
|
actor.print(Caption.of("fawe.worldedit.schematic.schematic.loaded", filename));
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
actor.print(Caption.of("worldedit.schematic.unknown-filename", TextComponent.of(filename)));
|
actor.print(Caption.of("worldedit.schematic.unknown-filename", TextComponent.of(filename)));
|
||||||
@ -515,7 +525,10 @@ public class SchematicCommands {
|
|||||||
aliases = {"listformats", "f"},
|
aliases = {"listformats", "f"},
|
||||||
desc = "List available formats"
|
desc = "List available formats"
|
||||||
)
|
)
|
||||||
@CommandPermissions("worldedit.schematic.formats")
|
@CommandPermissions(
|
||||||
|
value = "worldedit.schematic.formats",
|
||||||
|
queued = false
|
||||||
|
)
|
||||||
public void formats(Actor actor) {
|
public void formats(Actor actor) {
|
||||||
actor.print(Caption.of("worldedit.schematic.formats.title"));
|
actor.print(Caption.of("worldedit.schematic.formats.title"));
|
||||||
StringBuilder builder;
|
StringBuilder builder;
|
||||||
@ -541,7 +554,10 @@ public class SchematicCommands {
|
|||||||
desc = "List saved schematics",
|
desc = "List saved schematics",
|
||||||
descFooter = "Note: Format is not fully verified until loading."
|
descFooter = "Note: Format is not fully verified until loading."
|
||||||
)
|
)
|
||||||
@CommandPermissions("worldedit.schematic.list")
|
@CommandPermissions(
|
||||||
|
value = "worldedit.schematic.list",
|
||||||
|
queued = false
|
||||||
|
)
|
||||||
public void list(
|
public void list(
|
||||||
Actor actor, LocalSession session,
|
Actor actor, LocalSession session,
|
||||||
@ArgFlag(name = 'p', desc = "Page to view.", def = "1")
|
@ArgFlag(name = 'p', desc = "Page to view.", def = "1")
|
||||||
@ -812,7 +828,6 @@ public class SchematicCommands {
|
|||||||
final String SCHEMATIC_NAME = file.getName();
|
final String SCHEMATIC_NAME = file.getName();
|
||||||
|
|
||||||
double oldKbOverwritten = 0;
|
double oldKbOverwritten = 0;
|
||||||
String overwrittenPath = curFilepath;
|
|
||||||
|
|
||||||
int numFiles = -1;
|
int numFiles = -1;
|
||||||
if (checkFilesize) {
|
if (checkFilesize) {
|
||||||
@ -828,10 +843,10 @@ public class SchematicCommands {
|
|||||||
if (overwrite) {
|
if (overwrite) {
|
||||||
oldKbOverwritten = Files.size(Paths.get(file.getAbsolutePath())) / 1000.0;
|
oldKbOverwritten = Files.size(Paths.get(file.getAbsolutePath())) / 1000.0;
|
||||||
int iter = 1;
|
int iter = 1;
|
||||||
while (new File(overwrittenPath + "." + iter + "." + format.getPrimaryFileExtension()).exists()) {
|
while (new File(curFilepath + "." + iter + "." + format.getPrimaryFileExtension()).exists()) {
|
||||||
iter++;
|
iter++;
|
||||||
}
|
}
|
||||||
file = new File(overwrittenPath + "." + iter + "." + format.getPrimaryFileExtension());
|
file = new File(curFilepath + "." + iter + "." + format.getPrimaryFileExtension());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -314,7 +314,10 @@ public class SelectionCommands {
|
|||||||
name = "/wand",
|
name = "/wand",
|
||||||
desc = "Get the wand object"
|
desc = "Get the wand object"
|
||||||
)
|
)
|
||||||
@CommandPermissions("worldedit.wand")
|
@CommandPermissions(
|
||||||
|
value = "worldedit.wand",
|
||||||
|
queued = false
|
||||||
|
)
|
||||||
public void wand(
|
public void wand(
|
||||||
Player player, LocalSession session,
|
Player player, LocalSession session,
|
||||||
@Switch(name = 'n', desc = "Get a navigation wand") boolean navWand
|
@Switch(name = 'n', desc = "Get a navigation wand") boolean navWand
|
||||||
@ -348,7 +351,10 @@ public class SelectionCommands {
|
|||||||
aliases = {"/toggleeditwand"},
|
aliases = {"/toggleeditwand"},
|
||||||
desc = "Remind the user that the wand is now a tool and can be unbound with /tool none."
|
desc = "Remind the user that the wand is now a tool and can be unbound with /tool none."
|
||||||
)
|
)
|
||||||
@CommandPermissions("worldedit.wand.toggle")
|
@CommandPermissions(
|
||||||
|
value = "worldedit.wand.toggle",
|
||||||
|
queued = false
|
||||||
|
)
|
||||||
public void toggleWand(Player player) {
|
public void toggleWand(Player player) {
|
||||||
player.print(
|
player.print(
|
||||||
Caption.of(
|
Caption.of(
|
||||||
@ -499,7 +505,10 @@ public class SelectionCommands {
|
|||||||
name = "/size",
|
name = "/size",
|
||||||
desc = "Get information about the selection"
|
desc = "Get information about the selection"
|
||||||
)
|
)
|
||||||
@CommandPermissions("worldedit.selection.size")
|
@CommandPermissions(
|
||||||
|
value = "worldedit.selection.size",
|
||||||
|
queued = false
|
||||||
|
)
|
||||||
public void size(
|
public void size(
|
||||||
Actor actor, World world, LocalSession session,
|
Actor actor, World world, LocalSession session,
|
||||||
@Switch(name = 'c', desc = "Get clipboard info instead")
|
@Switch(name = 'c', desc = "Get clipboard info instead")
|
||||||
@ -734,6 +743,7 @@ public class SelectionCommands {
|
|||||||
box.appendCommand("sphere", Caption.of("worldedit.select.sphere.description"), "//sel sphere");
|
box.appendCommand("sphere", Caption.of("worldedit.select.sphere.description"), "//sel sphere");
|
||||||
box.appendCommand("cyl", Caption.of("worldedit.select.cyl.description"), "//sel cyl");
|
box.appendCommand("cyl", Caption.of("worldedit.select.cyl.description"), "//sel cyl");
|
||||||
box.appendCommand("convex", Caption.of("worldedit.select.convex.description"), "//sel convex");
|
box.appendCommand("convex", Caption.of("worldedit.select.convex.description"), "//sel convex");
|
||||||
|
|
||||||
//FAWE start
|
//FAWE start
|
||||||
box.appendCommand("polyhedral", Caption.of("fawe.selection.sel.polyhedral"), "//sel polyhedral");
|
box.appendCommand("polyhedral", Caption.of("fawe.selection.sel.polyhedral"), "//sel polyhedral");
|
||||||
box.appendCommand("fuzzy[=<mask>]", Caption.of("fawe.selection.sel.fuzzy-instruction"), "//sel fuzzy[=<mask>]");
|
box.appendCommand("fuzzy[=<mask>]", Caption.of("fawe.selection.sel.fuzzy-instruction"), "//sel fuzzy[=<mask>]");
|
||||||
|
@ -98,7 +98,10 @@ public class SnapshotCommands {
|
|||||||
name = "list",
|
name = "list",
|
||||||
desc = "List snapshots"
|
desc = "List snapshots"
|
||||||
)
|
)
|
||||||
@CommandPermissions("worldedit.snapshots.list")
|
@CommandPermissions(
|
||||||
|
value = "worldedit.snapshots.list",
|
||||||
|
queued = false
|
||||||
|
)
|
||||||
void list(
|
void list(
|
||||||
Actor actor, World world,
|
Actor actor, World world,
|
||||||
@ArgFlag(name = 'p', desc = "Page of results to return", def = "1")
|
@ArgFlag(name = 'p', desc = "Page of results to return", def = "1")
|
||||||
@ -127,8 +130,7 @@ public class SnapshotCommands {
|
|||||||
TextComponent.of(world.getName())
|
TextComponent.of(world.getName())
|
||||||
));
|
));
|
||||||
|
|
||||||
if (config.snapshotDatabase instanceof FileSystemSnapshotDatabase) {
|
if (config.snapshotDatabase instanceof FileSystemSnapshotDatabase db) {
|
||||||
FileSystemSnapshotDatabase db = (FileSystemSnapshotDatabase) config.snapshotDatabase;
|
|
||||||
Path root = db.getRoot();
|
Path root = db.getRoot();
|
||||||
if (Files.isDirectory(root)) {
|
if (Files.isDirectory(root)) {
|
||||||
WorldEdit.logger.info("No snapshots were found for world '"
|
WorldEdit.logger.info("No snapshots were found for world '"
|
||||||
|
@ -95,7 +95,7 @@ public class SnapshotUtilCommands {
|
|||||||
if (snapshotName != null) {
|
if (snapshotName != null) {
|
||||||
URI uri = resolveSnapshotName(config, snapshotName);
|
URI uri = resolveSnapshotName(config, snapshotName);
|
||||||
Optional<Snapshot> snapOpt = config.snapshotDatabase.getSnapshot(uri);
|
Optional<Snapshot> snapOpt = config.snapshotDatabase.getSnapshot(uri);
|
||||||
if (!snapOpt.isPresent()) {
|
if (snapOpt.isEmpty()) {
|
||||||
actor.print(Caption.of("worldedit.restore.not-available"));
|
actor.print(Caption.of("worldedit.restore.not-available"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -140,7 +140,7 @@ public class ToolUtilCommands {
|
|||||||
@Arg(desc = "The range of the brush")
|
@Arg(desc = "The range of the brush")
|
||||||
int range
|
int range
|
||||||
) throws WorldEditException {
|
) throws WorldEditException {
|
||||||
session.getBrushTool(player.getItemInHand(HandSide.MAIN_HAND).getType()).setRange(range);
|
session.getBrushTool(player).setRange(range);
|
||||||
player.print(Caption.of("worldedit.tool.range.set"));
|
player.print(Caption.of("worldedit.tool.range.set"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,7 +156,7 @@ public class ToolUtilCommands {
|
|||||||
) throws WorldEditException {
|
) throws WorldEditException {
|
||||||
we.checkMaxBrushRadius(size);
|
we.checkMaxBrushRadius(size);
|
||||||
|
|
||||||
session.getBrushTool(player.getItemInHand(HandSide.MAIN_HAND).getType()).setSize(size);
|
session.getBrushTool(player).setSize(size);
|
||||||
player.print(Caption.of("worldedit.tool.size.set"));
|
player.print(Caption.of("worldedit.tool.size.set"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,7 +29,6 @@ import com.fastasyncworldedit.core.util.StringMan;
|
|||||||
import com.fastasyncworldedit.core.util.TaskManager;
|
import com.fastasyncworldedit.core.util.TaskManager;
|
||||||
import com.fastasyncworldedit.core.util.image.ImageUtil;
|
import com.fastasyncworldedit.core.util.image.ImageUtil;
|
||||||
import com.fastasyncworldedit.core.util.task.DelegateConsumer;
|
import com.fastasyncworldedit.core.util.task.DelegateConsumer;
|
||||||
import com.google.common.base.Function;
|
|
||||||
import com.sk89q.worldedit.EditSession;
|
import com.sk89q.worldedit.EditSession;
|
||||||
import com.sk89q.worldedit.IncompleteRegionException;
|
import com.sk89q.worldedit.IncompleteRegionException;
|
||||||
import com.sk89q.worldedit.LocalConfiguration;
|
import com.sk89q.worldedit.LocalConfiguration;
|
||||||
@ -98,6 +97,7 @@ import java.util.Locale;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Function;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@ -130,7 +130,10 @@ public class UtilityCommands {
|
|||||||
aliases = {"/hmi", "hmi"},
|
aliases = {"/hmi", "hmi"},
|
||||||
desc = "Generate the heightmap interface: https://github.com/IntellectualSites/HeightMap"
|
desc = "Generate the heightmap interface: https://github.com/IntellectualSites/HeightMap"
|
||||||
)
|
)
|
||||||
@CommandPermissions("fawe.admin")
|
@CommandPermissions(
|
||||||
|
value = "fawe.admin",
|
||||||
|
queued = false
|
||||||
|
)
|
||||||
public void heightmapInterface(
|
public void heightmapInterface(
|
||||||
Actor actor,
|
Actor actor,
|
||||||
@Arg(name = "min", desc = "int", def = "100") int min,
|
@Arg(name = "min", desc = "int", def = "100") int min,
|
||||||
@ -145,12 +148,9 @@ public class UtilityCommands {
|
|||||||
final int sub = srcFolder.getAbsolutePath().length();
|
final int sub = srcFolder.getAbsolutePath().length();
|
||||||
List<String> images = new ArrayList<>();
|
List<String> images = new ArrayList<>();
|
||||||
MainUtil.iterateFiles(srcFolder, file -> {
|
MainUtil.iterateFiles(srcFolder, file -> {
|
||||||
switch (file.getName().substring(file.getName().lastIndexOf('.')).toLowerCase(Locale.ROOT)) {
|
String s = file.getName().substring(file.getName().lastIndexOf('.')).toLowerCase(Locale.ROOT);
|
||||||
case ".png":
|
if (!s.equals(".png") && !s.equals(".jpeg")) {
|
||||||
case ".jpeg":
|
return;
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
String name = file.getAbsolutePath().substring(sub);
|
String name = file.getAbsolutePath().substring(sub);
|
||||||
@ -187,7 +187,7 @@ public class UtilityCommands {
|
|||||||
StringBuilder config = new StringBuilder();
|
StringBuilder config = new StringBuilder();
|
||||||
config.append("var images = [\n");
|
config.append("var images = [\n");
|
||||||
for (String image : images) {
|
for (String image : images) {
|
||||||
config.append('"' + image.replace(File.separator, "/") + "\",\n");
|
config.append('"').append(image.replace(File.separator, "/")).append("\",\n");
|
||||||
}
|
}
|
||||||
config.append("];\n");
|
config.append("];\n");
|
||||||
config.append("// The low res images (they should all be the same size)\n");
|
config.append("// The low res images (they should all be the same size)\n");
|
||||||
@ -805,7 +805,10 @@ public class UtilityCommands {
|
|||||||
name = "/help",
|
name = "/help",
|
||||||
desc = "Displays help for WorldEdit commands"
|
desc = "Displays help for WorldEdit commands"
|
||||||
)
|
)
|
||||||
@CommandPermissions("worldedit.help")
|
@CommandPermissions(
|
||||||
|
value = "worldedit.help",
|
||||||
|
queued = false
|
||||||
|
)
|
||||||
public void help(
|
public void help(
|
||||||
Actor actor,
|
Actor actor,
|
||||||
@Switch(name = 's', desc = "List sub-commands of the given command, if applicable")
|
@Switch(name = 's', desc = "List sub-commands of the given command, if applicable")
|
||||||
@ -859,7 +862,6 @@ public class UtilityCommands {
|
|||||||
URI uri = input.getKey();
|
URI uri = input.getKey();
|
||||||
String path = input.getValue();
|
String path = input.getValue();
|
||||||
|
|
||||||
boolean url = false;
|
|
||||||
boolean loaded = isLoaded.apply(uri);
|
boolean loaded = isLoaded.apply(uri);
|
||||||
|
|
||||||
URIType type = URIType.FILE;
|
URIType type = URIType.FILE;
|
||||||
@ -959,21 +961,13 @@ public class UtilityCommands {
|
|||||||
if (len > 0) {
|
if (len > 0) {
|
||||||
for (String arg : args) {
|
for (String arg : args) {
|
||||||
switch (arg.toLowerCase(Locale.ROOT)) {
|
switch (arg.toLowerCase(Locale.ROOT)) {
|
||||||
case "me":
|
case "me", "mine", "local", "private" -> listMine = true;
|
||||||
case "mine":
|
case "public", "global" -> listGlobal = true;
|
||||||
case "local":
|
case "all" -> {
|
||||||
case "private":
|
|
||||||
listMine = true;
|
|
||||||
break;
|
|
||||||
case "public":
|
|
||||||
case "global":
|
|
||||||
listGlobal = true;
|
|
||||||
break;
|
|
||||||
case "all":
|
|
||||||
listMine = true;
|
listMine = true;
|
||||||
listGlobal = true;
|
listGlobal = true;
|
||||||
break;
|
}
|
||||||
default:
|
default -> {
|
||||||
if (arg.endsWith("/") || arg.endsWith(File.separator)) {
|
if (arg.endsWith("/") || arg.endsWith(File.separator)) {
|
||||||
arg = arg.replace("/", File.separator);
|
arg = arg.replace("/", File.separator);
|
||||||
String newDirFilter = dirFilter + arg;
|
String newDirFilter = dirFilter + arg;
|
||||||
@ -995,7 +989,7 @@ public class UtilityCommands {
|
|||||||
} else {
|
} else {
|
||||||
filters.add(arg);
|
filters.add(arg);
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1005,7 +999,7 @@ public class UtilityCommands {
|
|||||||
|
|
||||||
List<File> toFilter = new ArrayList<>();
|
List<File> toFilter = new ArrayList<>();
|
||||||
if (!filters.isEmpty()) {
|
if (!filters.isEmpty()) {
|
||||||
forEachFile = new DelegateConsumer<File>(forEachFile) {
|
forEachFile = new DelegateConsumer<>(forEachFile) {
|
||||||
@Override
|
@Override
|
||||||
public void accept(File file) {
|
public void accept(File file) {
|
||||||
toFilter.add(file);
|
toFilter.add(file);
|
||||||
@ -1015,7 +1009,7 @@ public class UtilityCommands {
|
|||||||
|
|
||||||
if (formatName != null) {
|
if (formatName != null) {
|
||||||
final ClipboardFormat cf = ClipboardFormats.findByAlias(formatName);
|
final ClipboardFormat cf = ClipboardFormats.findByAlias(formatName);
|
||||||
forEachFile = new DelegateConsumer<File>(forEachFile) {
|
forEachFile = new DelegateConsumer<>(forEachFile) {
|
||||||
@Override
|
@Override
|
||||||
public void accept(File file) {
|
public void accept(File file) {
|
||||||
if (cf.isFormat(file)) {
|
if (cf.isFormat(file)) {
|
||||||
@ -1024,7 +1018,7 @@ public class UtilityCommands {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
forEachFile = new DelegateConsumer<File>(forEachFile) {
|
forEachFile = new DelegateConsumer<>(forEachFile) {
|
||||||
@Override
|
@Override
|
||||||
public void accept(File file) {
|
public void accept(File file) {
|
||||||
if (!file.toString().endsWith(".cached")) {
|
if (!file.toString().endsWith(".cached")) {
|
||||||
@ -1062,7 +1056,7 @@ public class UtilityCommands {
|
|||||||
}
|
}
|
||||||
if (listGlobal) {
|
if (listGlobal) {
|
||||||
File rel = MainUtil.resolveRelative(new File(dir, dirFilter));
|
File rel = MainUtil.resolveRelative(new File(dir, dirFilter));
|
||||||
forEachFile = new DelegateConsumer<File>(forEachFile) {
|
forEachFile = new DelegateConsumer<>(forEachFile) {
|
||||||
@Override
|
@Override
|
||||||
public void accept(File f) {
|
public void accept(File f) {
|
||||||
try {
|
try {
|
||||||
@ -1172,7 +1166,7 @@ public class UtilityCommands {
|
|||||||
StringBuilder name = new StringBuilder();
|
StringBuilder name = new StringBuilder();
|
||||||
if (relative.isAbsolute()) {
|
if (relative.isAbsolute()) {
|
||||||
relative = root.toURI().relativize(file.toURI());
|
relative = root.toURI().relativize(file.toURI());
|
||||||
name.append(".." + File.separator);
|
name.append("..").append(File.separator);
|
||||||
}
|
}
|
||||||
name.append(relative.getPath());
|
name.append(relative.getPath());
|
||||||
return name.toString();
|
return name.toString();
|
||||||
|
@ -27,9 +27,13 @@ import com.sk89q.worldedit.function.operation.Operation;
|
|||||||
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.math.BlockVector3;
|
import com.sk89q.worldedit.math.BlockVector3;
|
||||||
|
import com.sk89q.worldedit.math.transform.AffineTransform;
|
||||||
|
import com.sk89q.worldedit.math.transform.Transform;
|
||||||
import com.sk89q.worldedit.regions.Region;
|
import com.sk89q.worldedit.regions.Region;
|
||||||
import com.sk89q.worldedit.session.ClipboardHolder;
|
import com.sk89q.worldedit.session.ClipboardHolder;
|
||||||
|
|
||||||
|
import java.util.concurrent.ThreadLocalRandom;
|
||||||
|
|
||||||
public class ClipboardBrush implements Brush {
|
public class ClipboardBrush implements Brush {
|
||||||
|
|
||||||
private final ClipboardHolder holder;
|
private final ClipboardHolder holder;
|
||||||
@ -38,6 +42,9 @@ public class ClipboardBrush implements Brush {
|
|||||||
private final boolean pasteEntities;
|
private final boolean pasteEntities;
|
||||||
private final boolean pasteBiomes;
|
private final boolean pasteBiomes;
|
||||||
private final Mask sourceMask;
|
private final Mask sourceMask;
|
||||||
|
//FAWE start - random rotation
|
||||||
|
private final boolean randomRotate;
|
||||||
|
//FAWE end
|
||||||
|
|
||||||
public ClipboardBrush(ClipboardHolder holder, boolean ignoreAirBlocks, boolean usingOrigin) {
|
public ClipboardBrush(ClipboardHolder holder, boolean ignoreAirBlocks, boolean usingOrigin) {
|
||||||
this.holder = holder;
|
this.holder = holder;
|
||||||
@ -46,23 +53,48 @@ public class ClipboardBrush implements Brush {
|
|||||||
this.pasteBiomes = false;
|
this.pasteBiomes = false;
|
||||||
this.pasteEntities = false;
|
this.pasteEntities = false;
|
||||||
this.sourceMask = null;
|
this.sourceMask = null;
|
||||||
|
//FAWE start - random rotation
|
||||||
|
this.randomRotate = false;
|
||||||
|
//FAWE end
|
||||||
}
|
}
|
||||||
|
|
||||||
public ClipboardBrush(
|
public ClipboardBrush(
|
||||||
ClipboardHolder holder, boolean ignoreAirBlocks, boolean usingOrigin, boolean pasteEntities,
|
ClipboardHolder holder, boolean ignoreAirBlocks, boolean usingOrigin, boolean pasteEntities,
|
||||||
boolean pasteBiomes, Mask sourceMask
|
boolean pasteBiomes, Mask sourceMask
|
||||||
) {
|
) {
|
||||||
|
//FAWE start - random rotation
|
||||||
|
this(holder, ignoreAirBlocks, usingOrigin, pasteEntities, pasteBiomes, sourceMask, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClipboardBrush(
|
||||||
|
ClipboardHolder holder, boolean ignoreAirBlocks, boolean usingOrigin, boolean pasteEntities,
|
||||||
|
boolean pasteBiomes, Mask sourceMask, boolean randomRotate
|
||||||
|
) {
|
||||||
|
//FAWE end
|
||||||
this.holder = holder;
|
this.holder = holder;
|
||||||
this.ignoreAirBlocks = ignoreAirBlocks;
|
this.ignoreAirBlocks = ignoreAirBlocks;
|
||||||
this.usingOrigin = usingOrigin;
|
this.usingOrigin = usingOrigin;
|
||||||
this.pasteEntities = pasteEntities;
|
this.pasteEntities = pasteEntities;
|
||||||
this.pasteBiomes = pasteBiomes;
|
this.pasteBiomes = pasteBiomes;
|
||||||
this.sourceMask = sourceMask;
|
this.sourceMask = sourceMask;
|
||||||
|
this.randomRotate = randomRotate;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void build(EditSession editSession, BlockVector3 position, Pattern pattern, double size) throws
|
public void build(EditSession editSession, BlockVector3 position, Pattern pattern, double size) throws
|
||||||
MaxChangedBlocksException {
|
MaxChangedBlocksException {
|
||||||
|
//FAWE start - random rotation
|
||||||
|
Transform originalTransform = holder.getTransform();
|
||||||
|
Transform transform = new AffineTransform();
|
||||||
|
if (this.randomRotate) {
|
||||||
|
int rotate = 90 * ThreadLocalRandom.current().nextInt(4);
|
||||||
|
transform = ((AffineTransform) transform).rotateY(rotate);
|
||||||
|
if (originalTransform != null) {
|
||||||
|
transform = originalTransform.combine(transform);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
holder.setTransform(transform);
|
||||||
|
//FAWE end
|
||||||
Clipboard clipboard = holder.getClipboard();
|
Clipboard clipboard = holder.getClipboard();
|
||||||
Region region = clipboard.getRegion();
|
Region region = clipboard.getRegion();
|
||||||
BlockVector3 centerOffset = region.getCenter().toBlockPoint().subtract(clipboard.getOrigin());
|
BlockVector3 centerOffset = region.getCenter().toBlockPoint().subtract(clipboard.getOrigin());
|
||||||
@ -77,6 +109,10 @@ public class ClipboardBrush implements Brush {
|
|||||||
.build();
|
.build();
|
||||||
|
|
||||||
Operations.completeLegacy(operation);
|
Operations.completeLegacy(operation);
|
||||||
|
//FAWE start - random rotation
|
||||||
|
// reset transform
|
||||||
|
holder.setTransform(originalTransform);
|
||||||
|
//FAWE end
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ public class ConfirmHandler implements CommandCallListener {
|
|||||||
}
|
}
|
||||||
Optional<Actor> actorOpt = parameters.injectedValue(Key.of(Actor.class));
|
Optional<Actor> actorOpt = parameters.injectedValue(Key.of(Actor.class));
|
||||||
|
|
||||||
if (!actorOpt.isPresent()) {
|
if (actorOpt.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Actor actor = actorOpt.get();
|
Actor actor = actorOpt.get();
|
||||||
|
@ -437,14 +437,16 @@ public interface Player extends Entity, Actor {
|
|||||||
} else {
|
} else {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
WorldEdit.getInstance().getExecutorService().submit(() -> {
|
Fawe.instance().getClipboardExecutor().submit(getUniqueId(), () -> {
|
||||||
doc.close(); // Ensure closed before deletion
|
doc.close(); // Ensure closed before deletion
|
||||||
doc.getFile().delete();
|
doc.getFile().delete();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (Settings.settings().CLIPBOARD.DELETE_ON_LOGOUT || Settings.settings().CLIPBOARD.USE_DISK) {
|
} else if (Settings.settings().CLIPBOARD.USE_DISK) {
|
||||||
WorldEdit.getInstance().getExecutorService().submit(() -> session.setClipboard(null));
|
Fawe.instance().getClipboardExecutor().submit(getUniqueId(), () -> session.setClipboard(null));
|
||||||
|
} else if (Settings.settings().CLIPBOARD.DELETE_ON_LOGOUT) {
|
||||||
|
session.setClipboard(null);
|
||||||
}
|
}
|
||||||
if (Settings.settings().HISTORY.DELETE_ON_LOGOUT) {
|
if (Settings.settings().HISTORY.DELETE_ON_LOGOUT) {
|
||||||
session.clearHistory();
|
session.clearHistory();
|
||||||
@ -470,7 +472,10 @@ public interface Player extends Entity, Actor {
|
|||||||
}
|
}
|
||||||
} catch (EmptyClipboardException ignored) {
|
} catch (EmptyClipboardException ignored) {
|
||||||
}
|
}
|
||||||
DiskOptimizedClipboard doc = DiskOptimizedClipboard.loadFromFile(file);
|
DiskOptimizedClipboard doc = Fawe.instance().getClipboardExecutor().submit(
|
||||||
|
getUniqueId(),
|
||||||
|
() -> DiskOptimizedClipboard.loadFromFile(file)
|
||||||
|
).get();
|
||||||
Clipboard clip = doc.toClipboard();
|
Clipboard clip = doc.toClipboard();
|
||||||
ClipboardHolder holder = new ClipboardHolder(clip);
|
ClipboardHolder holder = new ClipboardHolder(clip);
|
||||||
session.setClipboard(holder);
|
session.setClipboard(holder);
|
||||||
|
@ -321,10 +321,24 @@ public class PlatformManager {
|
|||||||
return queryCapability(Capability.CONFIGURATION).getConfiguration();
|
return queryCapability(Capability.CONFIGURATION).getConfiguration();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current supported {@link SideEffect}s.
|
||||||
|
*
|
||||||
|
* @return the supported side effects
|
||||||
|
* @throws NoCapablePlatformException thrown if no platform is capable
|
||||||
|
*/
|
||||||
public Collection<SideEffect> getSupportedSideEffects() {
|
public Collection<SideEffect> getSupportedSideEffects() {
|
||||||
return queryCapability(Capability.WORLD_EDITING).getSupportedSideEffects();
|
return queryCapability(Capability.WORLD_EDITING).getSupportedSideEffects();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the initialized state of the Platform.
|
||||||
|
* {@return if the platform manager is initialized}
|
||||||
|
*/
|
||||||
|
public boolean isInitialized() {
|
||||||
|
return initialized.get();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* You shouldn't have been calling this anyways, but this is now deprecated. Either don't
|
* You shouldn't have been calling this anyways, but this is now deprecated. Either don't
|
||||||
* fire this event at all, or fire the new event via the event bus if you're a platform.
|
* fire this event at all, or fire the new event via the event bus if you're a platform.
|
||||||
|
@ -266,8 +266,8 @@ public class SpongeSchematicReader extends NBTSchematicReader {
|
|||||||
if (id == null) {
|
if (id == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
values.put("id", id);
|
||||||
//FAWE end
|
//FAWE end
|
||||||
values.put("id", values.get("Id"));
|
|
||||||
values.remove("Id");
|
values.remove("Id");
|
||||||
values.remove("Pos");
|
values.remove("Pos");
|
||||||
if (fixer != null) {
|
if (fixer != null) {
|
||||||
|
@ -75,6 +75,7 @@ public final class Functions {
|
|||||||
);
|
);
|
||||||
handle = handle.asType(handle.type().changeReturnType(Number.class));
|
handle = handle.asType(handle.type().changeReturnType(Number.class));
|
||||||
handle = filterReturnValue(handle, DOUBLE_VALUE);
|
handle = filterReturnValue(handle, DOUBLE_VALUE);
|
||||||
|
handle = handle.asType(handle.type().wrap());
|
||||||
}
|
}
|
||||||
// return vararg-ity
|
// return vararg-ity
|
||||||
if (wasVarargs) {
|
if (wasVarargs) {
|
||||||
|
@ -31,6 +31,7 @@ import com.fastasyncworldedit.core.queue.IChunkSet;
|
|||||||
import com.sk89q.worldedit.math.BlockVector2;
|
import com.sk89q.worldedit.math.BlockVector2;
|
||||||
import com.sk89q.worldedit.math.BlockVector3;
|
import com.sk89q.worldedit.math.BlockVector3;
|
||||||
import com.sk89q.worldedit.world.World;
|
import com.sk89q.worldedit.world.World;
|
||||||
|
import com.sk89q.worldedit.world.block.BlockTypesCache;
|
||||||
import com.sk89q.worldedit.world.storage.ChunkStore;
|
import com.sk89q.worldedit.world.storage.ChunkStore;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
@ -823,14 +824,15 @@ public class CuboidRegion extends AbstractRegion implements FlatRegion {
|
|||||||
boolean trimX = lowerX != 0 || upperX != 15;
|
boolean trimX = lowerX != 0 || upperX != 15;
|
||||||
boolean trimZ = lowerZ != 0 || upperZ != 15;
|
boolean trimZ = lowerZ != 0 || upperZ != 15;
|
||||||
|
|
||||||
|
if (!(trimX || trimZ)) {
|
||||||
|
return set;
|
||||||
|
}
|
||||||
|
|
||||||
for (int layer = get.getMinSectionPosition(); layer < get.getMaxSectionPosition(); layer++) {
|
for (int layer = get.getMinSectionPosition(); layer < get.getMaxSectionPosition(); layer++) {
|
||||||
if (!set.hasSection(layer)) {
|
if (!set.hasSection(layer)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
char[] arr = Objects.requireNonNull(set.loadIfPresent(layer)); // This shouldn't be null if above is true
|
char[] arr = Objects.requireNonNull(set.loadIfPresent(layer)); // This shouldn't be null if above is true
|
||||||
if (!(trimX || trimZ)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
int indexY = 0;
|
int indexY = 0;
|
||||||
for (int y = 0; y < 16; y++, indexY += 256) { // For each y layer within a chunk section
|
for (int y = 0; y < 16; y++, indexY += 256) { // For each y layer within a chunk section
|
||||||
int index;
|
int index;
|
||||||
@ -839,14 +841,14 @@ public class CuboidRegion extends AbstractRegion implements FlatRegion {
|
|||||||
for (int z = 0; z < lowerZ; z++) {
|
for (int z = 0; z < lowerZ; z++) {
|
||||||
// null the z values
|
// null the z values
|
||||||
for (int x = 0; x < 16; x++, index++) {
|
for (int x = 0; x < 16; x++, index++) {
|
||||||
arr[index] = 0;
|
arr[index] = BlockTypesCache.ReservedIDs.__RESERVED__;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
index = indexY + upperZi;
|
index = indexY + upperZi;
|
||||||
for (int z = upperZ + 1; z < 16; z++) {
|
for (int z = upperZ + 1; z < 16; z++) {
|
||||||
// null the z values
|
// null the z values
|
||||||
for (int x = 0; x < 16; x++, index++) {
|
for (int x = 0; x < 16; x++, index++) {
|
||||||
arr[index] = 0;
|
arr[index] = BlockTypesCache.ReservedIDs.__RESERVED__;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -855,11 +857,11 @@ public class CuboidRegion extends AbstractRegion implements FlatRegion {
|
|||||||
for (int z = lowerZ; z <= upperZ; z++, index += 16) {
|
for (int z = lowerZ; z <= upperZ; z++, index += 16) {
|
||||||
for (int x = 0; x < lowerX; x++) {
|
for (int x = 0; x < lowerX; x++) {
|
||||||
// null the x values
|
// null the x values
|
||||||
arr[index + x] = 0;
|
arr[index + x] = BlockTypesCache.ReservedIDs.__RESERVED__;
|
||||||
}
|
}
|
||||||
for (int x = upperX + 1; x < 16; x++) {
|
for (int x = upperX + 1; x < 16; x++) {
|
||||||
// null the x values
|
// null the x values
|
||||||
arr[index + x] = 0;
|
arr[index + x] = BlockTypesCache.ReservedIDs.__RESERVED__;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -925,7 +927,7 @@ public class CuboidRegion extends AbstractRegion implements FlatRegion {
|
|||||||
for (int z = lowerZ; z <= upperZ; z++) {
|
for (int z = lowerZ; z <= upperZ; z++) {
|
||||||
// null the z values
|
// null the z values
|
||||||
for (int x = 0; x < 16; x++, index++) {
|
for (int x = 0; x < 16; x++, index++) {
|
||||||
arr[index] = 0;
|
arr[index] = BlockTypesCache.ReservedIDs.__RESERVED__;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -934,7 +936,7 @@ public class CuboidRegion extends AbstractRegion implements FlatRegion {
|
|||||||
for (int z = lowerZ; z <= upperZ; z++, index += 16) {
|
for (int z = lowerZ; z <= upperZ; z++, index += 16) {
|
||||||
for (int x = lowerX; x <= upperX; x++) {
|
for (int x = lowerX; x <= upperX; x++) {
|
||||||
// null the x values
|
// null the x values
|
||||||
arr[index + x] = 0;
|
arr[index + x] = BlockTypesCache.ReservedIDs.__RESERVED__;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user